Tasklet analysis (based on 3.16-rc4), tasklet3.16-rc4
Tasklet is implemented based on two soft interruptions: HI_SOFTIRQ and TASKLET_SOFTIRQ (they are implemented in the same source file, so that they are closely related). Its data structure and Soft Interrupt are similar, this blog post will analyze the initialization process of tasklet.
1. Data Structure related to tasklet
Tasklet_vec and tasklet_hi_vec arrays (kernel/softirq. c)
1 static DEFINE_PER_CPU(struct tasklet_head, tasklet_vec);2 static DEFINE_PER_CPU(struct tasklet_head, tasklet_hi_vec);
Row 1st creates an array named tasklet_vec for all CPUs. Each element is associated with a cpu. The array element type is struct tasklet_head. Similar to row 2nd and row 1st, the created tasklet_hi_vec array stores tasklet tasks with higher priority on each cpu. Next let's take a look at the struct tasklet_head structure (kernel/softirq. c ).
1 struct tasklet_head {2 struct tasklet_struct *head;3 struct tasklet_struct **tail;4 };
The kernel allocates such a struct variable to each cpu so that each cpu has a linked list of small task functions. Check the structure (kernel/softirq. c ).
1 struct tasklet_struct2 {3 struct tasklet_struct *next;4 unsigned long state;5 atomic_t count;6 void (*func)(unsigned long);7 unsigned long data;8 };
Row 3 points to the next node of the small task linked list, and row 3 points to the small task function.
2. tasklet Initialization
Small tasks are implemented based on two soft interruptions: HI_SOFTIRQ and TASKLET_SOFTIRQ. The Soft Interrupt function corresponding to HI_SOFTIRQ is tasklet_action, as follows (kernel/softirq. c ):
1 static void tasklet_action(struct softirq_action *a) 2 { 3 struct tasklet_struct *list; 4 5 local_irq_disable(); 6 list = __this_cpu_read(tasklet_vec.head); 7 __this_cpu_write(tasklet_vec.head, NULL); 8 __this_cpu_write(tasklet_vec.tail, &__get_cpu_var(tasklet_vec).head); 9 local_irq_enable();10 11 while (list) {12 struct tasklet_struct *t = list;13 14 list = list->next;15 16 if (tasklet_trylock(t)) {17 if (!atomic_read(&t->count)) {18 if (!test_and_clear_bit(TASKLET_STATE_SCHED,19 &t->state))20 BUG();21 t->func(t->data);22 tasklet_unlock(t);23 continue;24 }25 tasklet_unlock(t);26 }27 28 local_irq_disable();29 t->next = NULL;30 *__this_cpu_read(tasklet_vec.tail) = t;31 __this_cpu_write(tasklet_vec.tail, &(t->next));32 __raise_softirq_irqoff(TASKLET_SOFTIRQ);33 local_irq_enable();34 }35 }
The head field value of the elements related to the local cpu in the tasklet_vec array in the row 6th is saved in the list variable. In fact, the chain table of the current cpu tastlet task is removed and the list is the head pointer of the chain table. Line 7 and 8 set the head field of the tasklet_vec array and the elements related to the local cpu to NULL, and the tail field points to the head field. Line 11-26 executes all tasklet functions in a while loop. Row 3 is the execution of the tasklet function. Line 30-31 re-attaches the executed struct tasklet_struct linked list node to the tasklet_vec array. It can be seen that the struct containing the tasklet function does not need to be manually created by the programmer and is provided by the system. Reset the corresponding bit of the Soft Interrupt Mask table in row 32nd. The Soft Interrupt function corresponding to TASKLET_SOFTIRQ is tasklet_hi_action, which is similar to the execution process of tasklet_action and is not analyzed.
Let's take a look at the initialization process (kernel/softirq. c) of the tasklet_action function ).
1 void __init softirq_init(void) 2 { 3 int cpu; 4 5 for_each_possible_cpu(cpu) { 6 per_cpu(tasklet_vec, cpu).tail = 7 &per_cpu(tasklet_vec, cpu).head; 8 per_cpu(tasklet_hi_vec, cpu).tail = 9 &per_cpu(tasklet_hi_vec, cpu).head;10 }11 12 open_softirq(TASKLET_SOFTIRQ, tasklet_action);13 open_softirq(HI_SOFTIRQ, tasklet_hi_action);14 }
Line 5-10 initializes the elements of the tasklet_vec and tasklet_hi_vec arrays so that the tail domain of each element points to the head domain. The cpu number is used as the subscript of the two arrays. Line 12-13 stores the two soft interrupt functions tasklet_action and tasklet_hi_action that process the tasklet task into the corresponding elements of the Soft Interrupt array softirq_vec [NR_SOFTIRQS. Let's take a look at the open_softirq function (kernel/softirq. c ).
1 void open_softirq(int nr, void (*action)(struct softirq_action *))2 {3 softirq_vec[nr].action = action;4 }
I believe you will understand it at a Glance. You don't have to explain it ~
Let's take a look at the tasklet initialization process (kernel/softirq. c ):
1 void tasklet_init(struct tasklet_struct *t,2 void (*func)(unsigned long), unsigned long data)3 {4 t->next = NULL;5 t->state = 0;6 atomic_set(&t->count, 0);7 t->func = func;8 t->data = data;9 }
This function receives the tasklet_struct struct pointer and small task function pointer, initializes the tasklet_struct struct, and assigns the function pointer to the t-> func field. The tasklet_init function is called in the tasklet_hrtimer_init function.
Now, the initialization of tasklet is complete.