Tuesday, 12 July 2016

How softirq implementation and works in linux ?

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
The prototype of a softirq handler, action, looks like:
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
Regardless of the method of invocation, softirq execution occurs in do_softirq(). The function is really quite simple. If there are pending softirqs, do_softirq() loops over each one, invoking its handler. Let's look at a simplified variant of the important part of do_softirq():

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,

  1. 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.
  2. 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.
  3. The pointer h is set to the first entry in the softirq_vec.
  4. If the first bit in pending is set, h->action(h) is called.
  5. The pointer h is incremented by one so that it now points to the second entry in the softirq_vec array.
  6. 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).
  7. 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.
  8. 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