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.
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.
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.
orphaned debugfs file
freed module memory
→ 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 normalmodule_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.
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