2.调度tasklet
已触发的tasklet存放在两个单处理器结构中:tasklet_vec(普通tasklet)和tasklet_hi_vec(高优先级的tasklet)。这两个数据结构都是有tasklet_struct结构体构成的链表,链表中的每个tasklet_struct代表一个不同的tasklet。
tasklet由tasklet_schedule()和tasklet_hi_schedule()函数进行调度,它们接受一个指向tasklet_struct结构的指针作为参数。
- 在<Interrupt.h(include/linux)>中
- static inline void tasklet_schedule(struct tasklet_struct *t)
- {
- if (!test_and_set_bit(TASKLET_STATE_SCHED, &t->state))/*检查tasklet的状态是否为TASKLET_STATE_SCHED,如果是,说明tasklet已经被调度过了(有可能是一个tasklet已经被调度过但还没有来得及执行,而该tasklet又被唤起了一次),函数立即返回。*/
- __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);
- }
- 在<Softirq.c(Kernel)>中
- void fastcall __tasklet_schedule(struct tasklet_struct *t)
- {
- unsigned long flags;
- local_irq_save(flags);//保存中断状态,然后禁止本地中断。
- /*把需要调度的tasklet加到每个处理器一个的tasklet_vec链表的表头上*/
- t->next = __get_cpu_var(tasklet_vec).list;
- __get_cpu_var(tasklet_vec).list = t;
- /*唤起TASKLET_SOFTIRQ软中断,下次调用do_softirq()时就会执行该tasklet*/
- raise_softirq_irqoff(TASKLET_SOFTIRQ);
- local_irq_restore(flags);/*恢复中断到原状态并返回*/
- }
- void fastcall __tasklet_hi_schedule(struct tasklet_struct *t)
- {
- unsigned long flags;
- local_irq_save(flags);
- t->next = __get_cpu_var(tasklet_hi_vec).list;
- __get_cpu_var(tasklet_hi_vec).list = t;
- raise_softirq_irqoff(HI_SOFTIRQ);
- local_irq_restore(flags);
- }
- 在<Bitops.h(include/asm-i386)>
- /**
- * __test_and_set_bit - Set a bit and return its old value
- * @nr: Bit to set
- * @addr: Address to count from
- *
- * This operation is non-atomic and can be reordered.
- * If two examples of this operation race, one can appear to succeed
- * but actually fail. You must protect multiple accesses with a lock.
- */
- static inline int __test_and_set_bit(int nr, volatile unsigned long * addr)
- {
- int oldbit;
- __asm__(
- "btsl %2,%1/n/tsbbl %0,%0"
- :"=r" (oldbit),"+m" (ADDR)
- :"Ir" (nr));
- return oldbit;
- }
- /**
- * test_and_set_bit - Set a bit and return its old value
- * @nr: Bit to set
- * @addr: Address to count from
- *
- * This operation is atomic and cannot be reordered.
- * It may be reordered on other architectures than x86.
- * It also implies a memory barrier.
- */
- static inline int test_and_set_bit(int nr, volatile unsigned long * addr)
- {
- int oldbit;
- __asm__ __volatile__( LOCK_PREFIX
- "btsl %2,%1/n/tsbbl %0,%0"
- :"=r" (oldbit),"+m" (ADDR)
- :"Ir" (nr) : "memory");
- return oldbit;
- }
由于大部分tasklet和软中断都是在中断处理程序中被设置成待处理状态,所以最近一个中断返回的时候看起来就是执行do_softirq()的最佳时期。因为TASKLET_SOFTIRQ和HI_SOFTIRQ已经被触发了,所以do_softirq()会执行相应的软中断处理程序。tasklet_action()和tasklet_hi_action(),就是tasklet处理的核心。
- 在<Softirq.c(kernel)>中
- static void tasklet_action(struct softirq_action *a)
- {
- struct tasklet_struct *list;
- local_irq_disable();/*禁止中断*/
- list = __get_cpu_var(tasklet_vec).list;
- __get_cpu_var(tasklet_vec).list = NULL;/*将当前处理器上的该链表设置为NULL,达到清空的效果*/
- local_irq_enable();/*恢复响应中断*/
- /*循环遍历获得链表上的每一个待处理的tasklet*/
- while (list) {
- struct tasklet_struct *t = list;
- list = list->next;
- /* 如果是多处理器系统,通过检查TASKLET_STATE_RUN状态标志来判断这个tasklet是否正在其他处理器上运行。如果它正在运行,那么现在就不要执行,跳到下一个待处理的tasklet去 。如果当前这个tasklet没有执行,将其状态标志设置为TASKLET_STATE_RUN,这样别的处理器就不会再去执行它了,它就执行tasklet的处理程序,即if{}内的语句。*/
- if (tasklet_trylock(t)) {
- /*检查count值是否为0,确保tasklet没有被禁止。如果tasklet被禁止了则跳到下一个挂起的tasklet去。*/
- if (!atomic_read(&t->count)) {
- if (!test_and_clear_bit(TASKLET_STATE_SCHED, &t->state))
- BUG();
- t->func(t->data);/*执行tasklet的处理程序*/
- tasklet_unlock(t);
- continue;
- }
- tasklet_unlock(t);
- }
- /*如果TASKLET_STATE_RUN标志被设置,同一类型的tasklet已经在其他CPU上运行,因此该函数将该tasklet描述符重新插入tasklet_vec链表中,这样,直到其他CPU上没有同一类型的tasklet运行该tasklet才执行。*/
- local_irq_disable();
- t->next = __get_cpu_var(tasklet_vec).list;
- __get_cpu_var(tasklet_vec).list = t;
- __raise_softirq_irqoff(TASKLET_SOFTIRQ);/*重新激活TASKLET_SOFTIRQ*/
- local_irq_enable();
- }
- }
- static void tasklet_hi_action(struct softirq_action *a)
- {
- struct tasklet_struct *list;
- local_irq_disable();
- list = __get_cpu_var(tasklet_hi_vec).list;
- __get_cpu_var(tasklet_hi_vec).list = NULL;
- 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 = __get_cpu_var(tasklet_hi_vec).list;
- __get_cpu_var(tasklet_hi_vec).list = t;
- __raise_softirq_irqoff(HI_SOFTIRQ);
- local_irq_enable();
- }
- }
- 在<Interrupt.h(includelinux)>中
- enum
- {
- TASKLET_STATE_SCHED, /* Tasklet is scheduled for execution */
- TASKLET_STATE_RUN /* Tasklet is running (SMP only) */
- };
- #ifdef CONFIG_SMP
- 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(); }
- }
- #else
- #define tasklet_trylock(t) 1
- #define tasklet_unlock_wait(t) do { } while (0)
- #define tasklet_unlock(t) do { } while (0)
- #endif
- 在<Bitops.h(inclide/asm-i386)>
- /**
- * clear_bit - Clears a bit in memory
- * @nr: Bit to clear
- * @addr: Address to start counting from
- *
- * clear_bit() is atomic and may not be reordered. However, it does
- * not contain a memory barrier, so if it is used for locking purposes,
- * you should call smp_mb__before_clear_bit() and/or smp_mb__after_clear_bit()
- * in order to ensure changes are visible on other processors.
- */
- static inline void clear_bit(int nr, volatile unsigned long * addr)
- {
- __asm__ __volatile__( LOCK_PREFIX
- "btrl %1,%0"
- :"+m" (ADDR)
- :"Ir" (nr));
- }
- static inline void __clear_bit(int nr, volatile unsigned long * addr)
- {
- __asm__ __volatile__(
- "btrl %1,%0"
- :"+m" (ADDR)
- :"Ir" (nr));
- }
- #define test_bit(nr,addr) /
- (__builtin_constant_p(nr) ? /
- constant_test_bit((nr),(addr)) : /
- variable_test_bit((nr),(addr)))
- #define smp_mb__before_clear_bit() barrier()
- #define smp_mb__after_clear_bit() barrier()
- #if 0 /* Fool kernel-doc since it doesn't do macros yet */
- /**
- * test_bit - Determine whether a bit is set
- * @nr: bit number to test
- * @addr: Address to start counting from
- */
- static int test_bit(int nr, const volatile void * addr);
- #endif
- static __always_inline int constant_test_bit(int nr, const volatile unsigned long *addr)
- {
- return ((1UL << (nr & 31)) & (addr[nr >> 5])) != 0;
- }
- static inline int variable_test_bit(int nr, const volatile unsigned long * addr)
- {
- int oldbit;
- __asm__ __volatile__(
- "btl %2,%1/n/tsbbl %0,%0"
- :"=r" (oldbit)
- :"m" (ADDR),"Ir" (nr));
- return oldbit;
- }
- #define test_bit(nr,addr) /
- (__builtin_constant_p(nr) ? /
- constant_test_bit((nr),(addr)) : /
- variable_test_bit((nr),(addr)))
- 在<Bitops.h>中
- /**
- * test_and_clear_bit - Clear a bit and return its old value
- * @nr: Bit to clear
- * @addr: Address to count from
- *
- * This operation is atomic and cannot be reordered.
- * It can be reorderdered on other architectures other than x86.
- * It also implies a memory barrier.
- */
- static inline int test_and_clear_bit(int nr, volatile unsigned long * addr)
- {
- int oldbit;
- __asm__ __volatile__( LOCK_PREFIX
- "btrl %2,%1/n/tsbbl %0,%0"
- :"=r" (oldbit),"+m" (ADDR)
- :"Ir" (nr) : "memory");
- return oldbit;
- }
- 在<Atomic.h(include/asm-i386)>中
- /**
- * atomic_read - read atomic variable
- * @v: pointer of type atomic_t
- *
- * Atomically reads the value of @v.
- */
- #define atomic_read(v) ((v)->counter)
- 在<Bug.h(include/asm-i386)>
- /*
- * Tell the user there is some problem.
- * The offending file and line are encoded encoded in the __bug_table section.
- */
- #ifdef CONFIG_BUG
- #define HAVE_ARCH_BUG
- #ifdef CONFIG_DEBUG_BUGVERBOSE
- #define BUG() /
- do { /
- asm volatile("1:/tud2/n" /
- ".pushsection __bug_table,/"a/"/n" /
- "2:/t.long 1b, %c0/n" /
- "/t.word %c1, 0/n" /
- "/t.org 2b+%c2/n" /
- ".popsection" /
- : : "i" (__FILE__), "i" (__LINE__), /
- "i" (sizeof(struct bug_entry))); /
- for(;;) ; /
- } while(0)
- #else
- #define BUG() /
- do { /
- asm volatile("ud2"); /
- for(;;) ; /
- } while(0)
- #endif
- #endif
呵呵,总算把代码帖完了:P