Removing debugfs Files Safely (and What Happens If You Don’t)-Free Linux Device Driver Course Online

Removing debugfs Files Safely (and What Happens If You Don’t)
Free Linux Kernel Programming Course by EmbeddedPathashala — Lecture 14

Creating debugfs files is the easy half of the story. The half that actually separates a well-behaved kernel module from a dangerous one is cleanup — removing those debugfs entries the moment your module is unloaded. Skip this step, and you leave orphaned files sitting on the filesystem that can crash the kernel the instant someone touches them.

In this lecture of our free Linux kernel development course, you’ll learn the correct, modern way to remove debugfs entries, and you’ll build a solid mental model of what a kernel Oops actually is and why forgetting cleanup causes one.

Topics covered in this free Linux device drivers course lecture:
debugfs_remove_recursive Kernel module cleanup Kernel Oops explained Orphaned debugfs entries Linux driver reliability

What You Will Learn

  • Why every debugfs file your module creates must be removed on unload
  • The modern, single-call way to remove an entire debugfs directory tree
  • What an “orphaned” debugfs entry actually is at the filesystem level
  • A clear, safe explanation of what a kernel Oops is and why it happens
  • How to structure your module so cleanup can never be forgotten

Prerequisites

This lecture builds directly on the previous one in this free Linux kernel programming course, where we covered debugfs numeric and boolean helper APIs. You should already know how to create a debugfs parent directory with debugfs_create_dir() and populate it with files.

Why Cleanup Cannot Be Skipped

Every debugfs file your module creates lives inside the kernel’s virtual filesystem tree for as long as the underlying metadata structure — a dentry — exists. That metadata typically points back to memory owned by your module: the variable address you registered, and in the case of a custom file_operations handler, function pointers inside your module’s code.

When your module is unloaded via rmmod, the kernel frees the memory your module occupied. If you never told debugfs to remove its entries first, those debugfs files keep existing on disk-like paths such as /sys/kernel/debug/<your_module>/..., but the data they point to is gone. This mismatch — a filesystem entry referencing memory that no longer belongs to anything — is exactly what “orphaned” means here.

Module lifecycle: correct vs missing cleanup
✔ Correct cleanup
module_init creates dir + files → module_exit calls debugfs_remove_recursive() → rmmod → debugfs tree gone → safe
✘ Missing cleanup
module_init creates dir + files → module_exit skips removal → rmmod → module memory freed, debugfs files remain → orphaned

The Modern Way: debugfs_remove_recursive()

Early debugfs usage required calling debugfs_remove() once per file — tedious and easy to get wrong if you forget even one entry. Every current kernel avoids this entirely with a single recursive call:

void debugfs_remove_recursive(struct dentry *dentry);

The pattern is simple: keep a reference to the top-level parent directory dentry you created at init time, and pass that single pointer to debugfs_remove_recursive() in your module’s exit function. Every file and subdirectory beneath it is removed automatically, in one call, regardless of how many entries you created.

// module-scope variable set once in init
static struct dentry *gparent;

static int __init my_module_init(void)
{
    gparent = debugfs_create_dir("my_module", NULL);
    /* create files under gparent here */
    return 0;
}

static void __exit my_module_exit(void)
{
    debugfs_remove_recursive(gparent);
}

This is the single most important pattern in this lecture: store the parent dentry once, remove it once. You never need to track individual file dentries just for cleanup purposes.

Understanding a Kernel Oops

A “kernel Oops” is the kernel’s way of reporting that it hit an unexpected, invalid condition while executing — most commonly a dereference of a pointer that no longer points to valid memory. Unlike a user-space program crash, which the operating system can isolate and terminate cleanly, a fault inside the kernel is far more serious because the kernel is what everything else on the system depends on.

When an Oops occurs, the kernel does not necessarily halt the entire system immediately. Instead, it typically:

  • Prints detailed diagnostic information to the kernel log — the faulting instruction pointer, a register dump, and a call stack — via printk
  • Attempts to kill only the offending process/context if it can safely do so, allowing the rest of the system to keep running
  • Marks the kernel as “tainted,” signaling that something abnormal happened and the system’s stability can no longer be fully trusted

On production systems, this diagnostic output is often preserved through mechanisms like kdump so engineers can investigate the failure after the fact, even if the system later becomes fully unresponsive.

Why an orphaned debugfs entry leads to an Oops
User reads/writes
orphaned debugfs file
→
Kernel dereferences
freed module memory
→
Invalid memory access
→ kernel Oops

Why this matters for you as a driver developer: the scenario above is not hypothetical or rare — it’s a direct, mechanical consequence of skipping debugfs_remove_recursive(). Any debugfs file left behind after your module unloads is a landmine for the next person who happens to interact with it.

Real-World Use Cases

  • Driver developers use this cleanup pattern in virtually every mainline driver that exposes debugfs state — it is considered a baseline correctness requirement, not an optional nicety.
  • Kernel CI and fuzzing infrastructure specifically exercises module load/unload cycles to catch exactly this class of cleanup bug before it reaches production kernels.
  • Embedded systems teams often build automated load/unload stress tests into their driver test suites for this reason — a driver that “usually works” but occasionally leaves resources behind is a serious reliability risk in the field.

Common Mistakes and Troubleshooting

Mistake Symptom Fix
Not storing the top-level dentry No way to call debugfs_remove_recursive() at exit Save the return of your first debugfs_create_dir() call in a module-scope variable
Calling debugfs_remove() per file Easy to forget one entry as the module grows Use debugfs_remove_recursive() on the parent directory instead
Oops after rmmod when touching debug files Kernel log shows an invalid memory dereference Confirm cleanup runs in every exit path, including error paths in init
Partial init failure leaves files behind Module fails to load but debugfs entries linger Call debugfs_remove_recursive() in the init error-handling path too, not only in module_exit

Best Practices

  • Always store only the top-level parent dentry — never track individual file dentries purely for cleanup.
  • Call debugfs_remove_recursive() in every exit path of your module, including init-failure rollback code, not just the normal module_exit() function.
  • Test the full load → interact → unload → reload cycle repeatedly during development — many cleanup bugs only show up after multiple insmod/rmmod cycles.
  • Never assume “it worked once” means cleanup is correct; verify with kernel log inspection after unload.

Performance Considerations

debugfs_remove_recursive() walks the directory tree you pass it, so its cost scales with how many files and subdirectories exist beneath that parent. For the handful of debug files a typical driver exposes, this is negligible — well under the cost of the module unload itself. It only becomes worth thinking about if a subsystem dynamically creates a very large number of debugfs entries (for example, one directory per hardware queue on a system with hundreds of queues), in which case removal at unload time may briefly show up as measurable latency.

Security Considerations

An orphaned debugfs entry is not just a stability bug — because the resulting Oops involves the kernel dereferencing memory it no longer owns, this class of bug sits in the same general family as other use-after-free issues that security researchers actively look for. Treating debugfs cleanup as mandatory, not optional, is as much a security hygiene practice as it is a stability one.

Summary / Key Takeaways

  • Every debugfs entry your module creates must be removed when the module unloads — no exceptions.
  • Store the top-level parent dentry once; pass it to debugfs_remove_recursive() once, in every module exit path.
  • An orphaned debugfs entry left behind after unload points to freed memory — accessing it triggers a kernel Oops.
  • A kernel Oops is the kernel’s diagnostic response to an invalid memory access; it logs detailed state and, where possible, isolates the damage rather than halting the whole system outright.

Conclusion

Correct debugfs cleanup is a small amount of code — a single function call — but it’s the difference between a driver that’s safe to load and unload repeatedly and one that plants a landmine for the next engineer who touches it. As you continue through this free Linux kernel programming course, treat resource cleanup as a first-class part of every kernel interface you expose, not an afterthought bolted on at the end.

FAQ

Q1. Do I need to call debugfs_remove() for every individual file I created?
No — the modern approach is to call debugfs_remove_recursive() once on the top-level parent directory, which removes every file and subdirectory beneath it automatically.

Q2. What exactly is an “orphaned” debugfs entry?
It’s a debugfs file or directory still visible in the filesystem tree after the module that created it has been unloaded and its memory freed, so the entry now references memory it no longer owns.

Q3. What is a kernel Oops, in simple terms?
It’s the kernel’s diagnostic response to hitting an invalid condition during execution, most commonly an invalid memory dereference, reported via a detailed log dump rather than a silent failure.

Q4. Does a kernel Oops always crash the entire system?
Not necessarily. The kernel often tries to isolate the damage to the offending context, but the system is marked “tainted” and its continued stability can no longer be fully trusted.

Q5. Where do I need to store the dentry for cleanup to work?
Only the dentry returned by your top-level debugfs_create_dir() call needs to be stored, typically as a module-scope static variable.

Q6. Should cleanup only happen in module_exit()?
No — it should also run along any error-handling path in your init function if initialization partially succeeds and then fails, so nothing is left behind even when loading fails.

Q7. Is this cleanup pattern specific to debugfs, or does it apply elsewhere?
The same “always undo what you created” discipline applies to virtually every kernel resource — procfs entries, sysfs entries, allocated memory, registered interrupt handlers, and more.

Continue the Free Linux Kernel Programming Course

This lecture is part of EmbeddedPathashala’s free Linux kernel development course and free Linux device drivers course, covering everything from your first kernel module to advanced embedded systems programming.

Explore the Full Course

2 Comments

Leave a Reply

Your email address will not be published. Required fields are marked *