美文网首页
Raspberry interrupt & workqueue/

Raspberry interrupt & workqueue/

作者: star_walker | 来源:发表于2021-09-12 22:07 被阅读0次

Introduction

This article is to show people how interrupt and workqueue co-works with a piece of code.

Most times, the interrupt need to work with workqueue to finish works, the interrupt handler works as the top half of interrupt, and the workqueue works as the bottom half of interrupt.
Below is a link for readers to understand the related things easily:
https://blog.csdn.net/AndroidBBC/article/details/81911065
Below is a link for readers to understand the difference between workqueue and tasklet.
http://blog.chinaunix.net/uid-20382483-id-4077101.html

Preparation

workqueue

// include/linux/workqueue.h
struct work_struct {
    atomic_long_t data;
    struct list_head entry;
    work_func_t func;
#ifdef CONFIG_LOCKDEP
    struct lockdep_map lockdep_map;
#endif
};

#define WORK_DATA_INIT()    ATOMIC_LONG_INIT((unsigned long)WORK_STRUCT_NO_POOL)
#define WORK_DATA_STATIC_INIT() \
    ATOMIC_LONG_INIT((unsigned long)(WORK_STRUCT_NO_POOL | WORK_STRUCT_STATIC))

struct delayed_work {
    struct work_struct work;
    struct timer_list timer;

    /* target workqueue and CPU ->timer uses to queue ->work */
    struct workqueue_struct *wq;
    int cpu;
};

#define DECLARE_WORK(n, f)                      \
    struct work_struct n = __WORK_INITIALIZER(n, f)

#define DECLARE_DELAYED_WORK(n, f)                  \
    struct delayed_work n = __DELAYED_WORK_INITIALIZER(n, f, 0)

#define DECLARE_DEFERRABLE_WORK(n, f)                   \
    struct delayed_work n = __DELAYED_WORK_INITIALIZER(n, f, TIMER_DEFERRABLE)

#define INIT_WORK(_work, _func)                     \
    __INIT_WORK((_work), (_func), 0)

#define INIT_DELAYED_WORK(_work, _func)                 \
    __INIT_DELAYED_WORK(_work, _func, 0)

static inline bool queue_work(struct workqueue_struct *wq,
                  struct work_struct *work)
{
    return queue_work_on(WORK_CPU_UNBOUND, wq, work);
}

static inline bool queue_delayed_work(struct workqueue_struct *wq,
                      struct delayed_work *dwork,
                      unsigned long delay)
{
    return queue_delayed_work_on(WORK_CPU_UNBOUND, wq, dwork, delay);
}

static inline bool mod_delayed_work(struct workqueue_struct *wq,
                    struct delayed_work *dwork,
                    unsigned long delay)
{
    return mod_delayed_work_on(WORK_CPU_UNBOUND, wq, dwork, delay);
}

static inline bool schedule_work_on(int cpu, struct work_struct *work)
{
    return queue_work_on(cpu, system_wq, work);
}

static inline bool schedule_work(struct work_struct *work)
{
    return queue_work(system_wq, work);
}

static inline bool schedule_delayed_work_on(int cpu, struct delayed_work *dwork,
                        unsigned long delay)
{
    return queue_delayed_work_on(cpu, system_wq, dwork, delay);
}

extern bool flush_work(struct work_struct *work);
extern bool cancel_work_sync(struct work_struct *work);
extern bool flush_delayed_work(struct delayed_work *dwork);
extern bool cancel_delayed_work(struct delayed_work *dwork);
extern bool cancel_delayed_work_sync(struct delayed_work *dwork);

tasklet

// include/linux/interrupt.h
struct tasklet_struct
{
    struct tasklet_struct *next;
    unsigned long state;
    atomic_t count;
    bool use_callback;
    union {
        void (*func)(unsigned long data);
        void (*callback)(struct tasklet_struct *t);
    };  
    unsigned long data;
};
#define DECLARE_TASKLET(name, _callback)        \
struct tasklet_struct name = {              \
    .count = ATOMIC_INIT(0),            \
    .callback = _callback,              \
    .use_callback = true,               \
}
static inline void tasklet_schedule(struct tasklet_struct *t)
{
    if (!test_and_set_bit(TASKLET_STATE_SCHED, &t->state))
        __tasklet_schedule(t);
}
static inline void tasklet_hi_schedule(struct tasklet_struct *t)
{
    if (!test_and_set_bit(TASKLET_STATE_SCHED, &t->state))
        __tasklet_hi_schedule(t);
}
static inline void tasklet_hi_schedule(struct tasklet_struct *t)
{
    if (!test_and_set_bit(TASKLET_STATE_SCHED, &t->state))
        __tasklet_hi_schedule(t);
}

static inline void tasklet_disable_nosync(struct tasklet_struct *t)
{
    atomic_inc(&t->count);
    smp_mb__after_atomic();
}

static inline void tasklet_disable(struct tasklet_struct *t)
{
    tasklet_disable_nosync(t);
    tasklet_unlock_wait(t);
    smp_mb();
}

static inline void tasklet_enable(struct tasklet_struct *t)
{
    smp_mb__before_atomic();
    atomic_dec(&t->count);
}

extern void tasklet_kill(struct tasklet_struct *t);
extern void tasklet_kill_immediate(struct tasklet_struct *t, unsigned int cpu);
extern void tasklet_init(struct tasklet_struct *t,
             void (*func)(unsigned long), unsigned long data);
extern void tasklet_setup(struct tasklet_struct *t,
              void (*callback)(struct tasklet_struct *));

See, tasklet is defined in include/linux/interrupt.h, it must be more real time than workqueue :-)

Sample code

We will implement tasklet and workqueue at the same time, and do an experiment to see which one is would be scheduled earlier.

#include <linux/init.h>             
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/fs.h>
#include <linux/cdev.h>
#include <linux/device.h>
#include <linux/input.h>
#include <linux/platform_device.h>
#include <linux/gpio.h>
#include <linux/sysfs.h>
#include <linux/interrupt.h>
#include <linux/workqueue.h>

MODULE_LICENSE("GPL");

#define LIGHTSENSORGPIO 23
int lightsensor_irq;

struct work_struct  l_work;
struct delayed_work l_delayed_work;

static void l_work_func(struct work_struct *work)
{
    printk(KERN_INFO "hello: l_work got!\n");
}

static void l_delayed_work_func(struct work_struct *work)
{
    printk(KERN_INFO "hello: l_delayed_work got!\n");
}

static void l_tasklet_func(unsigned long data)
{
    printk(KERN_INFO "hello: l_tasklet got!\n");
}

static DECLARE_TASKLET_DISABLED_OLD(l_tasklet, l_tasklet_func);

static irqreturn_t lightsensor_irq_handler(int irq, void *dev_id)
{
    printk(KERN_INFO "lightsensor irq, got it\n");
    mod_delayed_work(system_wq, &l_delayed_work, msecs_to_jiffies(100)); // Trigger a delayed work
    schedule_work(&l_work); // Trigger a work
    tasklet_schedule(&l_tasklet); // Trigger a tasklet
    return IRQ_HANDLED;
}

static int __init hello_world_init(void)
{
    int ret;

    printk(KERN_ERR "hello world!!!\n");

    ret = gpio_request(LIGHTSENSORGPIO, "LightSensorIrq");
    if (ret < 0) {
        printk(KERN_ERR "hello: request gpio failed\n");
        goto error;
    }

    ret = gpio_direction_input(LIGHTSENSORGPIO);
    if (ret < 0) {
        printk(KERN_ERR "hello: set gpio direction failed\n");
        goto error;
    }

    lightsensor_irq = gpio_to_irq(LIGHTSENSORGPIO);
    ret = request_irq(lightsensor_irq, lightsensor_irq_handler, IRQF_TRIGGER_RISING, "LightSensorIrq", NULL);
    if (ret < 0) {
        printk(KERN_ERR "hello: request irq failed\n");
        goto error;
    }

    INIT_WORK(&l_work, l_work_func);
    INIT_DELAYED_WORK(&l_delayed_work, l_delayed_work_func);
    tasklet_enable(&l_tasklet);
    return 0;
error:
    gpio_free(LIGHTSENSORGPIO);
    return -1;
}

static void __exit hello_world_exit(void)
{
    tasklet_disable(&l_tasklet);
    tasklet_kill(&l_tasklet);
    free_irq(lightsensor_irq, NULL);
    gpio_free(LIGHTSENSORGPIO);
    printk(KERN_ALERT "hell_exit\r\n");
}

module_init(hello_world_init);
module_exit(hello_world_exit);

Result

Speed of schedule: tasklet > work > delayed work

root@l:/home/l# [ 2195.243668] lightsensor irq, got it
[ 2195.247278] hello: l_tasklet got!
[ 2195.250666] hello: l_work got!
[ 2195.345661] hello: l_delayed_work got!

root@l:/home/l# 
root@l:/home/l# 
root@l:/home/l# [ 2197.591458] lightsensor irq, got it
[ 2197.594984] lightsensor irq, got it
[ 2197.598495] hello: l_tasklet got!
[ 2197.601882] hello: l_work got!
[ 2197.701723] hello: l_delayed_work got!

root@l:/home/l# 
root@l:/home/l# 
root@l:/home/l# 
root@l:/home/l# [ 2201.746468] lightsensor irq, got it
[ 2201.749996] lightsensor irq, got it
[ 2201.753507] hello: l_tasklet got!
[ 2201.756923] hello: l_work got!
[ 2201.857818] hello: l_delayed_work got!

root@l:/home/l# 
root@l:/home/l# 
root@l:/home/l# [ 2204.516888] lightsensor irq, got it
[ 2204.520443] hello: l_tasklet got!
[ 2204.523858] hello: l_work got!
[ 2204.567136] lightsensor irq, got it
[ 2204.570740] hello: l_tasklet got!
[ 2204.574242] hello: l_work got!
[ 2204.673894] hello: l_delayed_work got!

root@l:/home/l# 
root@l:/home/l# 
root@l:/home/l

相关文章

网友评论

      本文标题:Raspberry interrupt & workqueue/

      本文链接:https://www.haomeiwen.com/subject/xbyawltx.html