Softirqs are rarely used; tasklets
are a much more common form of bottom half. Nonetheless, because
tasklets are built on softirqs, So we'll cover them first. The softirq code
lives in kernel/softirq.c.
Implementation of Softirqs
Softirqs are statically allocated at compile-time.
Unlike tasklets, you cannot dynamically register and destroy softirqs.
Softirqs are represented by the softirq_action structure, which is defined in <linux/interrupt.h>:
/* * structure representing a single softirq entry */ struct softirq_action { void (*action)(struct softirq_action *); /* function to run */ void *data; /* data to pass to function */ };
A 32-entry array of this structure is declared in kernel/softirq.c:
static struct softirq_action softirq_vec[32];
Each registered softirq consumes one entry in the array. Consequently, there can be a maximum of 32 registered softirqs. Note that this cap is fixed the maximum number of registered softirqs cannot be dynamically changed.
The Softirq Handler
void softirq_handler(struct softirq_action *)
When the kernel runs a softirq handler, it executes this action function with a pointer to the corresponding softirq_action structure as its lone argument. For example, if my_softirq pointed to an entry in the softirq_vec array, the kernel would invoke the softirq handler function as
my_softirq->action(my_softirq)
It seems a bit odd that the kernel passes the entire structure, and not just the data
value, to the softirq handler. This trick allows future additions to
the structure without requiring a change in every softirq handler.
Softirq handlers can retrieve the data value, if they need to, simply by
dereferencing their argument and reading the data member.
A softirq never preempts another softirq. In fact,
the only event that can preempt a softirq is an interrupt handler.
Another softirqeven the same onecan run on another processor, however.Executing Softirqs:
A registered softirq must be marked before it will execute. This is called raising the softirq.
Usually, an interrupt handler marks its softirq for execution before
returning. Then, at a suitable time, the softirq runs. Pending softirqs
are checked for and executed in the following places:
- In the return from hardware interrupt code
- In the ksoftirqd kernel thread
- In any code that explicitly checks for and executes pending softirqs, such as the networking subsystem
u32 pending = softirq_pending(cpu); if (pending) { struct softirq_action *h = softirq_vec; softirq_pending(cpu) = 0; do { if (pending & 1) h->action(h); h++; pending >>= 1; } while (pending); }
This snippet is the heart of softirq processing. It checks for, and executes, any pending softirqs. Specifically,
- It sets the pending local variable to the value returned by the softirq_pending() macro. This is a 32-bit mask of pending softirqsif bit n is set, the nth softirq is pending.
- Now that the pending bitmask of softirqs is saved, it clears the actual bitmask.This actually occurs with local interrupts disabled, but that is omitted in this simplified example. If interrupts were not disabled, a softirq could have been raised (and thus be pending) in the intervening time between saving the mask and clearing it. This would result in incorrectly clearing a pending bit.
- The pointer h is set to the first entry in the softirq_vec.
- If the first bit in pending is set, h->action(h) is called.
- The pointer h is incremented by one so that it now points to the second entry in the softirq_vec array.
- The bitmask pending is right-shifted by one. This tosses the first bit away, and moves all other bits one place to the right. Consequently, the second bit is now the first (and so on).
- The pointer h now points to the second entry in the array and the pending bitmask now has the second bit as the first. Repeat the previous steps.
- Continue repeating until pending is zero, at which point there are no more pending softirqs and the work is done. Note, this check is sufficient to ensure h always points to a valid entry in softirq_vec because pending has at most 32 set bits and thus this loop executes at most 32 times.Code flow return from hardware interrupt code:
asm_do_IRQ(irq, struct pt_regs *regs) //arch/arm/kernel/irq.c
|irq_exit() //defined in kernel/softirq.c:|invoke_softirq();|do_softirq()|__do_softirq(){pending = local_softirq_pending();struct softirq_action *h = softirq_vec;do {if(pending & 1){h ->action(h); // the softirq handler gets called----}h++;pending >> = 1;} while(pending);pending = local_softirq_pending();if(pending)wakeup_softirqd();}
No comments:
Post a Comment