深入理解Linux网络技术内幕-中断和网络驱动程序(四)

4000阅读 1评论2013-06-06 visualfan
分类:LINUX

         现在的Linux系统中bottom_half功能大多数都已通过softirq或tasklet实现,tasklet是建立在softirq上的,通常由中断处理函数发出。softirq中的HI_SOFTIRQ主要用于实现高优先级的任务,而TASKLET_SOFTIRQ通常用于实现低优先级的任务。

         当需要延迟执行的任务,则建立一个tasklet_struct结构的实例,插入HI_SOFTIRQ的列表,或TASKLET_SOFTIRQ的列表。由于softirq是由每个CPU各自独自处理的,所以每个CPU都有自己的HI_SOFTIRQ列表和TASKLET_SOFTIRQ列表。

void __init softirq_init(void)
{
    int cpu;

    for_each_possible_cpu(cpu) {
        int i;

        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;
        for (i = 0; i < NR_SOFTIRQS; i++)
            INIT_LIST_HEAD(&per_cpu(softirq_work_list[i], cpu));
    }

    register_hotcpu_notifier(&remote_softirq_cpu_notifier);

    open_softirq(TASKLET_SOFTIRQ, tasklet_action);
    open_softirq(HI_SOFTIRQ, tasklet_hi_action);
}

每个CPU都有自己的tasklet_vec和tasklet_hi_vec链表:

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);

softirq_work_list其实是每个CPU的NR_SOFTIRQS个双向链表头数组,每个CPU针对每个softirq都有自己的链表:

DEFINE_PER_CPU(struct list_head [NR_SOFTIRQS], softirq_work_list);

tasklet_action和tasklet_hi_action分别是TASKLET_SOFTIRQ和HI_SOFTIRQ任务的入口执行函数。

 

tasklet结构为:

struct tasklet_struct
{
    struct tasklet_struct *next; //将任务加入对应的softirq链表
    unsigned long state;
    atomic_t count; //可以暂时关闭或开启这个tasklet,0表示关闭
    void (*func)(unsigned long);
    unsigned long data;
};

state对应的状态包括:

enum
{
    TASKLET_STATE_SCHED,   //这个tasklet已进入调度准备执行,相同的任务不能同时在不同的CPU上调度,当第一个tasklet还没开始执行前,其他此tasklet任务请求到来时会被丢弃

    TASKLET_STATE_RUN    //此tasklet正在被执行中,用于防止相同的tasklet的多个实例被同时执行

};

这几个函数用来互斥地设置tasklet的TASKLET_STATE_RUN标识:

static inline int tasklet_trylock(struct tasklet_struct *t)
{
    return !test_and_set_bit(TASKLET_STATE_RUN, &(t)->state);
}

static inline void tasklet_unlock(struct tasklet_struct *t)
{
    smp_mb__before_clear_bit();
    clear_bit(TASKLET_STATE_RUN, &(t)->state);
}

static inline void tasklet_unlock_wait(struct tasklet_struct *t)
{
    while (test_bit(TASKLET_STATE_RUN, &(t)->state)) { barrier(); }
}

tasklet相关的函数:

  1. 设置tasklet的函数和参数

void tasklet_init(struct tasklet_struct *t,
          void (*func)(unsigned long), unsigned long data)
{
    t->next = NULL;
    t->state = 0;
    atomic_set(&t->count, 0);
    t->func = func;
    t->data = data;
}

    2.分别执行tasklet和hi_tasklet的任务

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

    local_irq_disable(); //关闭中断
    list = __this_cpu_read(tasklet_vec.head); //取tasklet的链表得值,然后重置链表头
    __this_cpu_write(tasklet_vec.head, NULL);
    __this_cpu_write(tasklet_vec.tail, &__get_cpu_var(tasklet_vec).head);
    local_irq_enable();

 

    while (list) { //遍历,执行tasklet
        struct tasklet_struct *t = list;

        list = list->next;

        if (tasklet_trylock(t)) { //锁定task
            if (!atomic_read(&t->count)) {
                if (!test_and_clear_bit(TASKLET_STATE_SCHED, &t->state))
                    BUG();
                t->func(t->data);
                tasklet_unlock(t);
                continue; //执行顺利,则继续遍历链表执行
            }
            tasklet_unlock(t);
        }

        local_irq_disable(); //将未执行完的task,加入链表尾
        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();
    }
}

对hi_softirq的task一样:

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

    local_irq_disable();
    list = __this_cpu_read(tasklet_hi_vec.head);
    __this_cpu_write(tasklet_hi_vec.head, NULL);
    __this_cpu_write(tasklet_hi_vec.tail, &__get_cpu_var(tasklet_hi_vec).head);
    local_irq_enable();

    while (list) {
        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_unlock(t);
                continue;
            }
            tasklet_unlock(t);
        }

        local_irq_disable();
        t->next = NULL;
        *__this_cpu_read(tasklet_hi_vec.tail) = t;
        __this_cpu_write(tasklet_hi_vec.tail, &(t->next));
        __raise_softirq_irqoff(HI_SOFTIRQ);
        local_irq_enable();
    }
}

3.分别enable和disable task

//enable是一样的,用于开启tasklet

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

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

//diable共用一样的,关闭tasklet

static inline void tasklet_disable(struct tasklet_struct *t)//同步,tasklet终止执行后才返回
{
tasklet_disable_nosync(t);
tasklet_unlock_wait(t);
smp_mb();
}

static inline void tasklet_disable_nosync(struct tasklet_struct *t) //异步
{
atomic_inc(&t->count);
smp_mb__after_atomic_inc();
}


4.为低优先级和高优先级的tasklet调度,准备执行,将tasklet_struct结构添加到本地CPU管理的任务链表中,然后为softirq的TASKLET_SOFTIRQ或HI_SOFTIRQ调度,若tasklet已进入调度,则不做任何事。

static inline void tasklet_schedule(struct tasklet_struct *t)
{
    if (!test_and_set_bit(TASKLET_STATE_SCHED, &t->state)) //设置tasklet_struct的state值,为TASKLET_STATE_SCHED,若原来未设置,则将task加入链表
        __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; //加入链表尾
    __this_cpu_write(tasklet_vec.tail, &(t->next));
    raise_softirq_irqoff(TASKLET_SOFTIRQ); //设置TASKLET_SOFTIRQ softirq的调度尾
    local_irq_restore(flags);
}

//同理

static inline void tasklet_hi_schedule(struct tasklet_struct *t)
{
    if (!test_and_set_bit(TASKLET_STATE_SCHED, &t->state))
        __tasklet_hi_schedule(t);
}

void __tasklet_hi_schedule(struct tasklet_struct *t)
{
    unsigned long flags;

    local_irq_save(flags);
    t->next = NULL;
    *__this_cpu_read(tasklet_hi_vec.tail) = t;
    __this_cpu_write(tasklet_hi_vec.tail,  &(t->next));
    raise_softirq_irqoff(HI_SOFTIRQ);
    local_irq_restore(flags);
}

上一篇:深入理解Linux网络技术内幕-中断和网络驱动程序(三)
下一篇:proc文件系统相关内容

文章评论