linux中断子系统 - softirq/tasklet

linux 4.6.3版本代码

假如写驱动的时候我们需要用到tasklet,那么首先需要分配一个新的tasklet_struct数据结构并初始化它,然后我们要把tasklet加入到总的tasklet链表中,最后要使得softirq可以调用它来执行驱动要做的工作,老规矩我们按照这个逻辑来组织本文。

分配一个新的tasklet并初始化

内核中用struct tasklet_struct来表示一个tasklet

struct tasklet_struct
{
    struct tasklet_struct *next;-----指向链表中下一个tasklet
    unsigned long state;-------------tasklet状态
    atomic_t count;------------------锁计数器,tasklet_enable/tasklet_disable函数会修改此字段
    void (*func)(unsigned long);-----tasklet函数指针
    unsigned long data;--------------函数参数
};

我们可以使用下面的宏来定义自己的tasklet,也可以使用函数tasklet_init()来初始化

#define DECLARE_TASKLET(name, func, data) \--------------初始化时enable自己的tasklet
struct tasklet_struct name = { NULL, 0, ATOMIC_INIT(0), func, data }

#define DECLARE_TASKLET_DISABLED(name, func, data) \-----初始化时disable自己的tasklet
struct tasklet_struct name = { NULL, 0, ATOMIC_INIT(1), func, data }

把tasklet加入到总链表中

总tasklet

内核会为每个cpu都分配一个tasklet链表,定义如下

struct tasklet_head {
    struct tasklet_struct *head;
    struct tasklet_struct **tail;
};

static DEFINE_PER_CPU(struct tasklet_head, tasklet_vec);
static DEFINE_PER_CPU(struct tasklet_head, tasklet_hi_vec);

把自己的tasklet加入到总tasklet中

我们需要把自己的tasklet加入到总链表中,这里分两种tasklet_schedule和tasklet_hi_schedule,这两种分别对应tasklet和hi_tasklet,其实执行步骤差不多,我们来看看tasklet_schedule

static inline void tasklet_schedule(struct tasklet_struct *t)
{
    if (!test_and_set_bit(TASKLET_STATE_SCHED, &t->state))
        __tasklet_schedule(t);
}
void __tasklet_schedule(struct tasklet_struct *t)
{
    unsigned long flags;

    local_irq_save(flags);
    t->next = NULL;
    *__this_cpu_read(tasklet_vec.tail) = t;-----------加入总链表tasklet_vec
    __this_cpu_write(tasklet_vec.tail, &(t->next));
    raise_softirq_irqoff(TASKLET_SOFTIRQ);------------激活softirq/tasklet
    local_irq_restore(flags);
}

tasklet是如何执行并调用到自定义的函数

初始化softirq的tasklet

tasklet的初始化在函数softirq_init中,这个函数是在start_kernel即系统初始化时调用的

void __init softirq_init(void)
{
    int cpu;

    for_each_possible_cpu(cpu) {
        per_cpu(tasklet_vec, cpu).tail =
            &per_cpu(tasklet_vec, cpu).head;
        per_cpu(tasklet_hi_vec, cpu).tail =
            &per_cpu(tasklet_hi_vec, cpu).head;
    }

    open_softirq(TASKLET_SOFTIRQ, tasklet_action);----初始化tasklet的执行函数为tasklet_action
    open_softirq(HI_SOFTIRQ, tasklet_hi_action);
}

tasklet执行步骤

从上面可以看到tasklet的执行函数为tasklet_action,softirq最终会调用此函数

static void tasklet_action(struct softirq_action *a)
{
    struct tasklet_struct *list;

    local_irq_disable();
    list = __this_cpu_read(tasklet_vec.head);
    __this_cpu_write(tasklet_vec.head, NULL);
    __this_cpu_write(tasklet_vec.tail, this_cpu_ptr(&tasklet_vec.head));
    local_irq_enable();

    while (list) {---进行链表循环调用每一个tasklet_struct 
        struct tasklet_struct *t = list;

        list = list->next;

        if (tasklet_trylock(t)) {
            if (!atomic_read(&t->count)) {
                if (!test_and_clear_bit(TASKLET_STATE_SCHED,
                            &t->state))
                    BUG();
                t->func(t->data);---------调用到tasklet的函数func
                tasklet_unlock(t);
                continue;
            }
            tasklet_unlock(t);
        }

        local_irq_disable();
        t->next = NULL;
        *__this_cpu_read(tasklet_vec.tail) = t;
        __this_cpu_write(tasklet_vec.tail, &(t->next));
        __raise_softirq_irqoff(TASKLET_SOFTIRQ);
        local_irq_enable();
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值