What is devm_request_threaded_irq() Managed API Tutorial-Linux Device Drivers Course

devm_request_threaded_irq() Tutorial
Free Linux Device Drivers Course — The Managed Way to Allocate Threaded Interrupts

← Previous Lecture    Next Lecture →

In the previous lecture of this free Linux kernel programming course, we learned how threaded interrupt handlers split work between a fast primary handler and a sleep-capable kernel thread. Now we look at the API that modern Linux drivers actually use in practice: devm_request_threaded_irq(), the device-managed version of threaded interrupt allocation.

This lecture explains why “managed” resource APIs exist, walks through every parameter, and gives you an original, simple, modern-kernel driver example you can adapt for your own free Linux device drivers course project.

Topics Covered
devm_request_threaded_irq() Managed Resource APIs Automatic IRQ Cleanup Probe / Remove Lifecycle Platform Drivers

What You Will Learn

  • What “device-managed” (devm_) APIs mean in the Linux kernel
  • The exact function signature of devm_request_threaded_irq(), parameter by parameter
  • How it compares to plain request_threaded_irq()
  • A simple, original modern driver example using the managed API
  • When the managed API is not the right choice
  • Debugging and best practice tips

Prerequisites

This lecture builds directly on threaded interrupt handler concepts — primary handler, threaded handler, IRQF_ONESHOT — covered in the previous lecture of this free Linux kernel programming course. If any of those terms are new to you, review that lecture first.

Why “Managed” (devm_) APIs Exist

A typical Linux driver’s probe() function allocates several resources: memory, IRQ lines, clocks, regulators. If probe() fails halfway through, or when the device is later removed, every one of those resources must be released in exactly the right order. Getting this wrong is one of the most common sources of driver bugs — memory leaks, IRQ lines left registered, use-after-free crashes on unbind.

The kernel’s device-managed (devm_) family of APIs solves this by tying a resource’s lifetime to the underlying struct device. Once you allocate through a devm_* call, the kernel automatically frees that resource when the device is detached or the driver is unbound — you do not need to write manual cleanup code for it.

Manual vs Managed IRQ Cleanup
request_threaded_irq()

You must call free_irq() yourself in the remove() function, and in every failure path of probe()

devm_request_threaded_irq()

Kernel frees the IRQ automatically on device detach or driver unbind — no manual free_irq() needed

Function Signature Explained

int devm_request_threaded_irq(struct device *dev, unsigned int irq,
                               irq_handler_t handler, irq_handler_t thread_fn,
                               unsigned long irqflags, const char *devname,
                               void *dev_id);
Parameter Meaning
devPointer to the owning struct device; ties the IRQ’s lifetime to this device
irqThe IRQ number to allocate, usually obtained via a helper like platform_get_irq() or gpiod_to_irq()
handlerPrimary handler, runs in hardirq context, can be NULL
thread_fnThreaded handler, runs in process context, does the real work
irqflagsTrigger type and flags, e.g. IRQF_ONESHOT | IRQF_TRIGGER_FALLING
devnameName shown in /proc/interrupts and in the thread name
dev_idCookie passed back to your handlers, typically your driver’s private data pointer

The return value follows the standard kernel convention: 0 on success, a negative -Exxx errno value on failure. Always check it.

A Simple Original Example: I2C Sensor on a Modern Kernel

Here is an original, simplified example of an I2C client driver using devm_request_threaded_irq(), written for a current 6.x kernel. It represents a generic motion sensor that raises an interrupt line when new data is ready.

#include <linux/module.h>
#include <linux/i2c.h>
#include <linux/interrupt.h>
#include <linux/regmap.h>

struct my_sensor_dev {
    struct i2c_client *client;
};

#define SENSOR_REG_STATUS   0x00
#define SENSOR_REG_DATA     0x01
#define SENSOR_STATUS_READY BIT(0)

/* Threaded handler only: no primary handler needed for this device */
static irqreturn_t sensor_thread_fn(int irq, void *data)
{
    struct my_sensor_dev *sensor = data;
    int status, value;

    status = i2c_smbus_read_byte_data(sensor->client, SENSOR_REG_STATUS);
    if (status < 0 || !(status & SENSOR_STATUS_READY))
        return IRQ_NONE;

    value = i2c_smbus_read_byte_data(sensor->client, SENSOR_REG_DATA);
    if (value < 0)
        return IRQ_NONE;

    dev_info(&sensor->client->dev, "new sensor reading: %d\n", value);

    return IRQ_HANDLED;
}

static int my_sensor_probe(struct i2c_client *client)
{
    struct my_sensor_dev *sensor;
    int ret;

    sensor = devm_kzalloc(&client->dev, sizeof(*sensor), GFP_KERNEL);
    if (!sensor)
        return -ENOMEM;

    sensor->client = client;
    i2c_set_clientdata(client, sensor);

    if (client->irq <= 0)
        return -EINVAL;

    /* NULL primary handler: kernel supplies the default, and
     * IRQF_ONESHOT is applied automatically in this case */
    ret = devm_request_threaded_irq(&client->dev, client->irq,
                                     NULL, sensor_thread_fn,
                                     IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
                                     "my_sensor", sensor);
    if (ret) {
        dev_err(&client->dev, "failed to request threaded irq: %d\n", ret);
        return ret;
    }

    dev_info(&client->dev, "my_sensor driver probed, irq=%d\n", client->irq);
    return 0;
}

static const struct i2c_device_id my_sensor_id[] = {
    { "my_sensor", 0 },
    { }
};
MODULE_DEVICE_TABLE(i2c, my_sensor_id);

static struct i2c_driver my_sensor_driver = {
    .driver = {
        .name = "my_sensor",
    },
    .probe    = my_sensor_probe,
    .id_table = my_sensor_id,
};
module_i2c_driver(my_sensor_driver);

MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("Simple devm_request_threaded_irq I2C sensor example");

Notice there is no remove() function freeing the IRQ, and no manual free_irq() call anywhere. Because the IRQ was requested through the devm_ variant tied to &client->dev, the kernel releases it automatically when the I2C device is unbound.

Comparison Table

Aspect request_threaded_irq() devm_request_threaded_irq()
CleanupManual free_irq() requiredAutomatic on device detach
Extra parameterNonestruct device *dev as first argument
Typical useNon-probe contexts, custom lifetimesStandard driver probe() functions
Risk of leaksHigher if error paths are incompleteLower, tied to device lifecycle

When NOT to Use the Managed API

The managed API is the recommended default, but it is not always the right tool:

  • If you need to free the IRQ at a specific point before the device is removed (for example, temporarily disabling interrupt handling while reconfiguring hardware), a manual free_irq() gives you precise control that devm_ cleanup ordering does not
  • If your resource’s lifetime does not match the device’s lifetime, forcing it into the devm model can hide bugs rather than prevent them
  • Mixing manual and managed cleanup for the same resource in one driver is a common source of double-free bugs — pick one style per resource

Debugging Tips

  • Check registered interrupts:
    cat /proc/interrupts | grep my_sensor
  • Confirm the interrupt thread is running:
    ps -eLo pid,comm | grep "irq/"
  • Use dev_err() return codes from devm_request_threaded_irq() to catch IRQ number or flag mistakes early during probe

Best Practices and Security Considerations

  • Always validate client->irq (or equivalent) before requesting it — a negative or zero value means the interrupt was never wired up in the device tree or ACPI table
  • Never trust interrupt-triggered data blindly; validate register reads before acting on them, since a misbehaving or spoofed device could otherwise trigger unexpected driver behavior
  • Keep the threaded handler’s critical sections short even though it can sleep, to avoid starving other interrupt threads
  • Prefer devm_request_threaded_irq() in standard probe() flows to minimize resource-leak bugs on error paths

Key Takeaways

  • devm_request_threaded_irq() ties an IRQ’s lifetime to a struct device, removing the need for manual free_irq() in most drivers
  • All parameters besides the added struct device *dev match plain request_threaded_irq()
  • The managed API is the modern recommended default for driver probe() functions, but manual control is still sometimes required

FAQ

Q1. What does devm_request_threaded_irq() do differently from request_threaded_irq()?
It links the IRQ allocation to a struct device, so the kernel automatically frees it when that device is removed, instead of requiring a manual free_irq() call.

Q2. Do I still need to call free_irq() with the managed API?
No. That is the entire point of the devm_ pattern — cleanup happens automatically on device detach or driver unbind.

Q3. Is devm_request_threaded_irq() always the right choice?
In most standard probe() flows, yes. But if you need precise control over exactly when the IRQ is freed, independent of the device’s overall lifecycle, manual request_threaded_irq() and free_irq() may be more appropriate.

Q4. Does the managed API change how flags like IRQF_ONESHOT work?
No, flag behavior is identical; only the resource cleanup mechanism differs.

Q5. What happens if devm_request_threaded_irq() fails?
It returns a negative errno value. You should log the error and return it from probe() so the kernel knows the driver failed to initialize.

Q6. Can devm_request_threaded_irq() be used in platform drivers as well as I2C drivers?
Yes, it works with any bus type as long as you have a valid struct device pointer and IRQ number.

Continue Your Free Linux Device Drivers Course

You now understand both the plain and managed threaded interrupt APIs. Move on to the next lecture to keep building your embedded Linux driver skills.

← Previous Lecture    Next Lecture →

2 Comments

Leave a Reply

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