Managed IRQ Allocation in Linux Device Drivers: devm_request_irq Explained
Free Linux Kernel Programming Course — Interrupt Handling Series
If you are writing a Linux driver today, using devm_request_irq in Linux device drivers is the recommended way to hook your hardware interrupt line into the kernel. In this free Linux device drivers course lecture, you will learn what devm_request_irq does, why it replaces the older request_irq() pattern in modern driver code, and how to use it correctly in a real probe function. This lecture is part of our free Linux kernel programming course and assumes only basic C and Linux fundamentals.
What You Will Learn
- What an IRQ line is and why a driver needs to register an interrupt handler for it
- The difference between request_irq() and devm_request_irq() in Linux device drivers
- How the devres (managed resource) framework automatically frees your IRQ
- A complete, modern, working code example using devm_request_irq
- Common mistakes beginners make with interrupt registration
- Best practices, performance notes, and security notes for interrupt handlers
Prerequisites
- Basic C programming knowledge
- A rough idea of what a Linux kernel module and a probe function are
- A Linux machine (or virtual machine) running kernel 5.x or 6.x for testing
- Familiarity with the earlier lectures in this free Linux kernel programming course on kernel modules and platform drivers is helpful but not mandatory
Why Interrupt Handling Matters in Linux Device Drivers
Almost every real hardware device — a network card, a touchscreen controller, a UART, a GPIO button — needs to tell the CPU “something happened, come look at me now.” It does this by raising a hardware interrupt on an IRQ line. Your driver’s job is to register a function that the kernel will call whenever that IRQ fires. Getting this registration right, and cleaning it up correctly when the driver is removed, is one of the most important parts of writing a correct Linux device driver.
This is exactly where devm_request_irq in Linux device drivers becomes useful: it ties the lifetime of the interrupt registration to the lifetime of the underlying struct device, so you do not have to remember to free it yourself.
request_irq vs devm_request_irq: What Changed
Older driver code (and older textbooks) show request_irq() paired with a manual free_irq() call in the driver’s remove path. This works, but it is easy to forget the cleanup call, especially when a probe function has multiple error-handling exit paths. The managed alternative removes that risk.
| Aspect | request_irq() | devm_request_irq() |
|---|---|---|
| Cleanup | Manual free_irq() required | Automatic, tied to device lifetime |
| Risk of leaks | Higher, easy to miss on error paths | Very low |
| Extra parameter | None | Needs struct device *dev as first argument |
| Recommended for new drivers | No, legacy pattern | Yes |
How Managed IRQ Allocation Works Internally
Every struct device carries a small internal list of “managed resources.” When you call a devm_* function, the kernel wraps your resource (in this case, the registered interrupt) in a node and adds it to that list. When the device is unbound — driver removal, unplug, or probe failure — the kernel walks that list in reverse order and releases each resource automatically, including your interrupt handler.
Step-by-Step: Using devm_request_irq in a Modern Driver
The example below targets a simple platform driver, written for a current kernel (6.x). Note that we use platform_get_irq(), the modern helper, instead of manually pulling an IORESOURCE_IRQ resource — this is the currently recommended approach for getting an IRQ number out of a platform device.
1. Getting the IRQ number the modern way
static int mydev_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
struct mydev_priv *priv;
int irq, ret;
priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
if (!priv)
return -ENOMEM;
irq = platform_get_irq(pdev, 0);
if (irq irq = irq;
platform_set_drvdata(pdev, priv);
ret = devm_request_irq(dev, irq, mydev_isr, 0,
"mydev-irq", priv);
if (ret) {
dev_err(dev, "failed to request IRQ %d: %d\n", irq, ret);
return ret;
}
dev_info(dev, "mydev interrupt handler registered on IRQ %d\n", irq);
return 0;
}
2. Writing the interrupt handler
static irqreturn_t mydev_isr(int irq, void *dev_id)
{
struct mydev_priv *priv = dev_id;
/* Keep this function short: read a status register,
* clear the interrupt source, and hand off any heavy
* work to a workqueue, tasklet, or threaded handler. */
if (!mydev_irq_is_mine(priv))
return IRQ_NONE;
mydev_clear_irq_status(priv);
queue_work(priv->wq, &priv->irq_work);
return IRQ_HANDLED;
}
3. The remove function — notice there is nothing to do
static int mydev_remove(struct platform_device *pdev)
{
/* No free_irq() call needed here. devm_request_irq()
* already registered the cleanup with the devres
* framework, so the kernel releases the IRQ for us. */
dev_info(&pdev->dev, "mydev removed\n");
return 0;
}
Real-World Use Cases
- Platform drivers for on-chip peripherals such as UARTs, I2C or SPI controllers, and GPIO controllers
- Network interface drivers where the hardware raises an IRQ per received packet burst
- Sensor drivers (accelerometers, touch controllers) that use a GPIO line as an interrupt source
- Any driver bound through the standard driver model (platform, I2C, SPI, PCI) where a struct device is already available
Common Mistakes When Using devm_request_irq
- Doing heavy work inside the handler. The primary handler runs with interrupts disabled on that line; keep it short and offload real work.
- Ignoring the return value of platform_get_irq(). A negative value can mean -EPROBE_DEFER, which your driver must propagate, not treat as a hard failure.
- Mixing devm_request_irq() with a manual free_irq() call. Calling free_irq() yourself on a managed IRQ can lead to a double-free during device removal.
- Passing NULL as the dev_id when you need it for shared IRQs. On a shared line, dev_id must be a unique, non-NULL pointer.
Best Practices
- Prefer devm_request_irq() in any driver that already has a struct device from the driver core
- Use platform_get_irq() (or the equivalent for your bus type) instead of manually parsing resources
- Return quickly from the primary handler; defer real processing to a workqueue, threaded handler, or tasklet
- Give every IRQ a descriptive name string — it shows up in /proc/interrupts and helps debugging
Performance Considerations
devm_request_irq() adds a small, one-time bookkeeping cost at probe time to register the resource in the devres list; there is no additional runtime cost per interrupt compared to request_irq(). Your real performance lever is how little work you do inside the primary handler itself.
Security Considerations
Always validate that the interrupt genuinely belongs to your device before acting on it, especially on shared IRQ lines, by checking a status register first. Never trust data pulled in during an interrupt without the same validation you would apply to any other untrusted input path.
Summary / Key Takeaways
- devm_request_irq in Linux device drivers ties IRQ cleanup to the device lifetime automatically
- It needs a struct device pointer, unlike the older request_irq()
- Combine it with platform_get_irq() for a fully modern, kernel 6.x-ready driver
- Keep your primary handler short; defer heavy work elsewhere
Conclusion
Managed IRQ allocation is one of the small changes that makes modern Linux drivers noticeably more robust than the pre-devres style of driver code. Once you get comfortable with devm_request_irq in Linux device drivers, you will find yourself reaching for the whole devm_* family — devm_kzalloc(), devm_ioremap_resource(), and this one — by default in almost every driver you write. In the next lecture of this free Linux kernel programming course, we move on to threaded interrupt handlers and when you actually need one.
Frequently Asked Questions (FAQ)
1. Is devm_request_irq() available on all kernel versions?
Yes, it has been part of the mainline kernel for a long time and is fully supported on current 6.x kernels.
2. Do I still need to call free_irq() when using devm_request_irq()?
No. The kernel calls it for you automatically when the device is unbound or the probe function fails after registration.
3. Can I use devm_request_irq() outside a probe function?
You can, but it is only genuinely useful if you have access to the struct device whose lifetime you want the IRQ tied to, which is why it is almost always used inside probe().
4. What does platform_get_irq() return on error?
It returns a negative error code, such as -EPROBE_DEFER when resources are not ready yet, which your probe function must return unchanged.
5. Is devm_request_irq() suitable for shared interrupt lines?
Yes, as long as you pass the IRQF_SHARED flag and a unique, non-NULL dev_id, exactly as you would with request_irq().
6. Does using devm_request_irq() affect interrupt latency?
No. It only changes how the resource is tracked for cleanup; the interrupt delivery path and latency are unaffected.
7. What happens if devm_request_irq() fails?
It returns a negative error code and no handler is registered, so your probe function should check the return value and bail out cleanly.
8. Should beginners learn request_irq() at all?
It is still worth understanding, since you will see it in older code and in the kernel’s own internals, but new driver code should use the managed version.
Continue the Free Linux Kernel Programming Course
More lectures on interrupt handling, kernel modules, and device drivers are coming up next.
Course Index Next Lecture
2 Comments