/[lkdp]/lkdp/pm/interrupts.tex
ViewVC logotype

Diff of /lkdp/pm/interrupts.tex

Parent Directory Parent Directory | Revision Log Revision Log | View Patch Patch

revision 1.1 by nayaniabhishek, Tue Nov 5 19:35:42 2002 UTC revision 1.2 by kdlinux2001, Thu Nov 21 13:08:15 2002 UTC
# Line 1  Line 1 
1  \chapter{Interrupts}  \chapter{Interrupts}
2          \section{Interrupt Basics: IDT Table}          \section{Interrupt Basics: IDT Table} \index{set\_gate}
3                   The interrupt machanism is initialised by the functions trap\_init() and init\_IRQ() during the system bootup as explained int the section ~\ref{init:start}. The following functions are used to insert the gates in the IDT.                   The interrupt machanism is initialised by the functions \textit{trap\_init()} and \textit{init\_IRQ()} during the system bootup as explained int the section ~\ref{init:sk}. The following functions are used to insert the gates in the IDT.
4   % TODO gate explaination and ref.   % TODO gate explaination and ref.
5          \begin{verbatim}          \begin{verbatim}
6    
7              set_intr_gate(n, addr);              set_intr_gate(n, addr);
8              set_system_gate(n, addr);              set_system_gate(n, addr);
9              set_trap_gate(n, addr);              set_trap_gate(n, addr);
10    
11          \end{verbatim}          \end{verbatim}
12    
13          \section{Structures}          \section{Structures}
14                  The primitive structure in the interrupt management is irq descriptor [irq\_desc\_t] structure defined in \url{include/linux/irq.h}.          \subsection{irq\_desc\_t}
15                    The primitive structure in the interrupt management is irq descriptor \textit{[irq\_desc\_t]} structure defined in \url{include/linux/irq.h}.
16    
17          \begin{verbatim}          \begin{verbatim}
18    
# Line 40  Line 43 
43          IRQ_PER_CPU     /* IRQ is per CPU, used in SMP environments */          IRQ_PER_CPU     /* IRQ is per CPU, used in SMP environments */
44                    
45          \end{verbatim}          \end{verbatim}
46                  The depth field is used to balance the irq enable and disable requests.          The depth field is used to balance the irq enable and disable requests.
47            \newline
48          The lock field is used to prevent concurrent accesss in SMP environment.          The lock field is used to prevent concurrent accesss in SMP environment.
49                  The handler field desribes the list of all interrupt functions as descibed in the structure hw\_irq\_controller. The structure is defined in \url{include/linux/irq.h}          \par    The handler field desribes the list of all interrupt functions as descibed in the structure hw\_irq\_controller. The structure is defined in \url{include/linux/irq.h}
50                    
51            \subsection{hw\_interrupt\_type}
52          \begin{verbatim}          \begin{verbatim}
53    
54          struct hw_interrupt_type {          struct hw_interrupt_type {
55              const char * typename;              const char * typename;
56              unsigned int (*startup)(unsigned int irq);              unsigned int (*startup)(unsigned int irq);
# Line 55  Line 61 
61              void (*end)(unsigned int irq);              void (*end)(unsigned int irq);
62              void (*set_affinity)(unsigned int irq, unsigned long mask);              void (*set_affinity)(unsigned int irq, unsigned long mask);
63          };          };
64    
65          \end{verbatim}          \end{verbatim}
66                  The typename is the irq name that appears in the file /proc/interrupts. While the remaining fiels are the functions pointers, which are invoked to interact with the hardware in responce to driver calls like request\_irq() and free\_irq() and others.                  The \textit{typename} is the irq name that appears in the file "/proc/interrupts". While the remaining fiels are the functions pointers, which are invoked to interact with the hardware in responce to driver calls like \textit{request\_irq()} and \textit{free\_irq()} and others.
67    
68                  The action field in hw\_desc\_t desribes the linked list of interrupt actions that are performed when the interrupt occures. The structure is defined in \url{include/linux/interrupt.h}. Since, the irq line may be shred by multiple devices, the irq functions are stored in a linked list. This linked list is populated with every call using the request\_irq() function for a particular irq line. These functions are NOT the microprocessor level ISRs, but links to those functions.          \par    The action field in \textit{hw\_desc\_t} desribes the linked list of interrupt actions that are performed when the interrupt occures. The structure is defined in \url{include/linux/interrupt.h}. Since, the irq line may be shared by multiple devices, the irq functions are stored in a linked list. This linked list is populated with every call using the request\_irq() function for a particular irq line. These functions are NOT the microprocessor level ISRs, but links to those functions.
69                    
70            \subsection{irqaction}
71          \begin{verbatim}          \begin{verbatim}
72    
73          struct irqaction {          struct irqaction {
74              void (*handler)(int, void *, struct pt_regs *); /* function prototpye */              void (*handler)(int, void *, struct pt_regs *); /* function prototpye */
75              unsigned long flags;              unsigned long flags;
# Line 69  Line 78 
78              void *dev_id;              void *dev_id;
79              struct irqaction *next;                         /* linked list of actions */              struct irqaction *next;                         /* linked list of actions */
80          };          };
81    
82          \end{verbatim}          \end{verbatim}
83    
84            The structure elements are described below:
85            \begin{description}
86    
87            \item[handler] The interrupt action to be performed, when interrupt occurs.
88            \item[flags] This can have any of values defined:
89            \begin{itemize} \index{SA\_INTERRUPT}
90            \item SA\_INTERRUPT : indicates a fast interrupt handler. Fast handlers are executed with interrupts disabled.
91            \item SA\_SAMPLE\_RANDOM : indicates that the generated interrupts can contribute to pool used by \textit{"/dev/random"} and \textit{"/dev/urandom"}.
92            \item SA\_SHIRQ : indicates that the interrupt can be shared between devices.
93            \end{itemize}
94            \item[mask] The interrupt status mask.
95            \item[name] The name of the interrupt.
96            \item[dev\_id] The devices on which, the current interrupt action is going to operate. This has different values in case of a shared interrupt line.
97            \item[next] The typical next pointer of a linked list.
98    
99            \end{description}
100                    
101          \section{Interrupt Processing}          \section{Interrupt Processing}
102                  The interrupt handling is performed by the do\_IRQ function defined in \url{arch/i386/kernel/irq.c}. The do\_IRQ is invoked for all device IRQs from the common\_interrupt handler defined in \url{arch/i386/kernel/entry.S}. The special cross-CPU SMP interrupts are handled by different interrupt handler.                  The interrupt handling is performed by the \textit{do\_IRQ} function defined in \url{arch/i386/kernel/irq.c}. The do\_IRQ is invoked for all device IRQs from the common\_interrupt handler defined in \url{arch/i386/kernel/entry.S}. The special cross-CPU SMP interrupts are handled by different interrupt handler.
103                    
104          \begin{verbatim}          \begin{verbatim}
105    
106          common_interrupt:          common_interrupt:
107              SAVE_ALL                 /* save flags on stack */              SAVE_ALL                 /* save flags on stack */
108              GET_THREAD_INFO(%ebx)    /* read thread status  */              GET_THREAD_INFO(%ebx)    /* read thread status  */
109              INC_PRE_COUNT(%ebx)      /* counter incresed    */              INC_PRE_COUNT(%ebx)      /* counter incresed    */
110                call do_IRQ                call do_IRQ
111              jmp ret_from_intr        /* RET from interrupt  */              jmp ret_from_intr        /* RET from interrupt  */
112    
113          \end{verbatim}          \end{verbatim}
114                    
115            \subsection{Function do\_IRQ} \index{do\_IRQ}
116          The do\_IRQ function is described below in parts.          The do\_IRQ function is described below in parts.
117                    
118          \begin{verbatim}          \begin{verbatim}
119    
120          asmlinkage unsigned int do_IRQ(struct pt_regs regs)          asmlinkage unsigned int do_IRQ(struct pt_regs regs)
121          {          {
122              int irq = regs.orig_eax & 0xff; /* high bits used in ret_from_ code  */              int irq = regs.orig_eax & 0xff; /* high bits used in ret_from_ code  */
# Line 97  Line 128 
128              kstat.irqs[cpu][irq]++;              kstat.irqs[cpu][irq]++;
129              spin_lock(&desc->lock);              spin_lock(&desc->lock);
130              desc->handler->ack(irq);              desc->handler->ack(irq);
131    
132          \end{verbatim}          \end{verbatim}
133                    
134                  Initially, do\_IRQ() reads the interrupt source into the local variable irq,using the high order bits of the eax register. The desc jumps to the appropriate location of the irq\_desc array to access the descriptor structure. At this point, the interrupt request source has been uniquely identified. Next, kstat kernel structure is updated to count the interrupt, which updates /proc/interrupts. The kstat structure is defined as 2-D array of [NR\_CPUS][NR\_IRQS] in \url{include/linux/kernel_stat.h} Then the handler takes a spinlock to protect the interrupt controller hardware from simultaneous access by multiple CPUs. Then , it acknowledges the interrupt to the requesting source interrupt controller by calling desc->handler->ack() function.                  Initially, do\_IRQ() reads the interrupt source into the local variable irq,using the high order bits of the eax register. The desc jumps to the appropriate location of the \textit{irq\_desc} array to access the descriptor structure. At this point, the interrupt request source has been uniquely identified. Next, kstat kernel structure is updated to count the interrupt, which updates "/proc/interrupts". The kstat structure is defined as 2-D array of [NR\_CPUS][NR\_IRQS] in \url{include/linux/kernel_stat.h} Then the handler takes a spinlock to protect the interrupt controller hardware from simultaneous access by multiple CPUs. Then , it acknowledges the interrupt to the requesting source interrupt controller by calling \textit{desc-\_>handler-\_>ack()} function.
135                    
136          \begin{verbatim}          \begin{verbatim}
137    
# Line 121  Line 153 
153           */           */
154          if (!action)          if (!action)
155              goto out;              goto out;
156    
157          \end{verbatim}          \end{verbatim}
158                  The code deals with updating the status field of the irq descriptor explained in section ~\ref{irq:status}. and interrupt handling among the CPUs. A CPU which starts processing a particular interrupt sets the IRQ\_INPROGRESS status with valid descriptor action while the remaining CPUs NULLify the action when any of the other CPUs is executing the interrupt.                  The code deals with updating the status field of the irq descriptor explained in section ~\ref{irq:status}. and interrupt handling among the CPUs. A CPU which starts processing a particular interrupt sets the IRQ\_INPROGRESS status with valid descriptor action while the remaining CPUs NULLify the action when any of the other CPUs is executing the interrupt.
159    
160  \begin{verbatim}          \begin{verbatim}
161    
162          for (;;) {          for (;;) {
163              spin_unlock(&desc->lock);              spin_unlock(&desc->lock);
164              handle_IRQ_event(irq, &regs, action);              handle_IRQ_event(irq, &regs, action);
# Line 142  out: Line 176  out:
176              do_softirq();              do_softirq();
177          return 1;          return 1;
178          }          }
179  \end{verbatim}  
180                  After the initial sanity checks, the IRQ is handled by the handle\_IRQ\_event() function defined in \url{arch/i386/kernel/irq.c}. The spinlock is released before calling the handle\_IRQ\_event function because the handler functions can be exceuted by any CPU. Then, the handler updated the IRQ status and invokes the "end" function to the requesting source interrupt controller by calling desc->handler->end().          \end{verbatim}
181          The softirq status is checked before returning from the interupt. This is explained in section ~\ref{int:softirq}                  After the initial sanity checks, the IRQ is handled by the \textit{handle\_IRQ\_event()} function defined in \url{arch/i386/kernel/irq.c}. The spinlock is released before calling the handle\_IRQ\_event function because the handler functions can be exceuted by any CPU. Then, the handler updates the IRQ status and invokes the "end" function to the requesting source interrupt controller by calling \textit{desc-\_> handler-\_>end()}.
182          The return value of 1 is returned for CPU executing the interrupt while other CPUs return 0.          \par The softirq status is checked before returning from the interupt. Softirqs are explained in section ~\ref{int:softirq}. The return value of 1 is returned for CPU executing the interrupt while other CPUs return 0.
183            
184            \subsection{Function handle\_IRQ\_event}
185          \par The handle\_IRQ\_event function is described below in parts.          \par The handle\_IRQ\_event function is described below in parts.
186    
187          \begin{verbatim}          \begin{verbatim}
188    
189          int handle_IRQ_event(unsigned int irq, struct pt_regs * regs, struct irqaction * action)          int handle_IRQ_event(unsigned int irq, struct pt_regs * regs, struct irqaction * action)
190          {          {
191              int status;              int status;
# Line 157  out: Line 193  out:
193    
194              irq_enter(cpu, irq);              irq_enter(cpu, irq);
195              status = 1;              status = 1;
         \end{verbatim}  
                 The macro irq\_enter is used to increament the local number of interrupts running per CPU. This is defined in \url{include/asm/hardirq.h}. The structure defines the values of foll. parametes running on each CPU.  
196    
         \begin{verbatim}  
         typedef struct {  
         unsigned int __softirq_active;    /* active softirqs */  
         unsigned int __softirq_mask;      /* masked softirqs */  
         unsigned int __local_irq_count;   /* interrupts running */  
         unsigned int __local_bh_count;    /* bottom halves running */  
         unsigned int __syscall_count;     /* system calls */  
         unsigned int __nmi_count;         /* arch dependent NMIs */  
         } ____cacheline_aligned irq_cpustat_t;  
197          \end{verbatim}          \end{verbatim}
198            \index{irq\_enter}
199          The status = 1, indicates that the bottom halves will be executed forcefully. This code is going to get modified, to check whether bottom halves need to be executed and then the status will be updated accordingly.                  The macro \textit{irq\_enter} is used to increament the local number of interrupts running per CPU. \footnote{\textit{irq\_enter} macro is defined in \url{include/asm/hardirq.h}.} Please refer to section ~\ref{int:cpustat} for \textit{irq\_cpustat\_t} structure.
200            \par The status = 1, indicates that the bottom halves will be executed forcefully. This code is going to get modified, to check whether bottom halves need to be executed and then the status will be updated accordingly.
201    
202          \begin{verbatim}          \begin{verbatim}
203    
204          if (!(action->flags & SA_INTERRUPT))          if (!(action->flags & SA_INTERRUPT))
205               __sti();               __sti();
206    
# Line 190  out: Line 217  out:
217          irq_exit(cpu, irq);          irq_exit(cpu, irq);
218    
219          return status;          return status;
220    
221          \end{verbatim}          \end{verbatim}
222    
223                  The interrupts are disabled using \_\_sti() if SA\_INTERRUPT flag is set. This flag value is passed the irq device during the request\_irq() function call. Then, all the interrupt handlers in the linked list are invoked one by one till the end of list is reached.                  The interrupts are disabled using \_\_sti() if \textit{SA\_INTERRUPT} flag is set. This flag value is passed the irq device during the request\_irq() function call. Then, all the interrupt handlers in the linked list are invoked one by one till the end of list is reached.
224          Then, the interrupt are enabled using \_\_cli() call. and the macro irq\_exit decreaments the local number of interrupts running per CPU.          Then, the interrupt are enabled using \_\_cli() call. and the macro \textit{irq\_exit} decreaments the local number of interrupts running per CPU.
225    
226            \subsection{interrupt related CPU status} \label{int:cpustat} \index{irq\_cpustat}
227                    The kernel defines interrupt related CPU status in the irq\_cpustat\_t structure. The structure defines the values of following parametes running on each CPU. All of these parameters store the interrupt, bottom half, softirq, system calls status for the cpu. These fields are frequently accessed by the interrupt handling code to determine the state of cpu, whether it is running interrupts, to check the number of bottom halves running, to check pending status of softirqs and so on. The macros related to access these elements are defined in \url{include/linux/irq_cpustat.h}.
228            \begin{verbatim}
229    
230            /*      include/asm/hardirq.h ; offset sensitive code */
231    
232            typedef struct {
233            unsigned int __softirq_pending;       /* pending softirqs      */
234            unsigned int __local_irq_count;       /* interrupts running    */
235            unsigned int __local_bh_count;        /* bottom halves running */
236            unsigned int __syscall_count;         /* system calls          */
237            struct task_struct* __ksoftirqd_task; /* softirq stat          */
238            unsigned long idle_timestamp;         /* CPU idle time         */
239            unsigned int __nmi_count;             /* arch dependent NMIs   */
240            } ____cacheline_aligned irq_cpustat_t;
241    
242            /*      include/linux/irq_cpustat.h   */
243    
244            #ifdef CONFIG_SMP
245            #define __IRQ_STAT(cpu, member) (irq_stat[cpu].member)
246            #else
247            #define __IRQ_STAT(cpu, member) ((void)(cpu), irq_stat[0].member)
248            #endif
249    
250            #define syscall_count(cpu)      __IRQ_STAT((cpu), __syscall_count)
251            \end{verbatim}
252    
253          \section{Top Half}          \section{Top Half}
254                    Since, a interrupt handling involves substantial amount of work, keeping the interupts disabled for a long time in not desirable. Hence, interrupt handlers need to finish up quickly. Hence, interrupt handler is split into two halves. The \textit{top half} is the routine that actually responds to the interrupt. This is the handler registered with the funcion \textit{request\_irq}, while the bottom half is a routine that is scheduled by the top half to be executed later, at a safer time.
255            \par Thus, a given interrupt handler is divided into a top half and a bottom half. Whenever an interrupt is raised, only the top half portion of the interrupt handler is executed. It generally includes the status updating of the interrupt flags and acknowleding the interrupting device. The bottom half of an interrupt handler is the part that not need to be done right away. It is schedules at a safer time.
256            \par Advantages of this scheme are listed below:
257            \begin{itemize}
258            \item Interrupts remain disabled for a very less amount of time.
259            \item It minimizes overall interrupt latency.
260            \item The interrupt controller chip is told to disable the specific IRQ be serviced while the kernel is executing the top half.
261            \item Bottom half consists of things that don t need to be done on ever single interrupt.
262            \end{itemize}
263    
264          \section{Bottom Half} \label{int:bh}          \section{Bottom Half} \label{int:bh}
265          \section{Wait Queues}          As mentioned earlier, bottom half is executed as a post interrupt handling function, when the kernel finishes basic handling of an interrupt. The concept of bottom halves is more or less deprecated from kernel version 2.2. The bottom halves are replaced with new concepts of tasklets and softirqs. Here we describe the old style bottom halves.
266          \section{tasklets}          \par The bottom halves defined in \url{kernel/softirq.c} as an array of 32 function [ void *] pointers. These pointers are used to store the bottom half routines for each of the 32 predefined bottom halves. Another array of 32 \textit{tasklet\_struct} are also defined. Please, refer to ~\ref{int:tasklets} for tasklet details. The \textit{func} pointer in the tasklet\_struct is associated with the above array of bh\_base function pointers.
267          \section{softirqs} \label{int:softirq}  
268            \begin{verbatim}
269    
270            static void (*bh_base[32])(void);
271            struct tasklet_struct bh_task_vec[32];
272    
273            void init_bh(int nr, void (*routine)(void))
274            {
275                bh_base[nr] = routine;
276                mb();
277            }
278    
279            void remove_bh(int nr)
280            {
281                    tasklet_kill(bh_task_vec+nr);
282                    bh_base[nr] = NULL;
283            }
284    
285            \end{verbatim}
286    
287            A bottom half is defined by using \textit{init\_bh} function, with the bh number and routine parameters and deleted by using \textit{remove\_bh} function. The init\_bh function sets the bh routine parameter in the appropriate position in \textit{bh\_base} array \footnote{The \textit{mb()} function is related to CPU ordering. For wak ordered intel CPUs, this function deals with the memory barriers. Refer to \url{include/asm-i386/system.h} for details.} while remove\_bh function sets it to NULL.
288            \par The bottom half is activated only when the \textit{mark\_bh()} function is called. The mark\_bh function performs following operations.
289    
290            \subsection{Function mark\_bh} \label{int:markbh}
291            \begin{verbatim}
292    
293            tasklet_hi_schedule(bh_task_vec+nr);
294    
295             if (!test_and_set_bit(TASKLET_STATE_SCHED, &t->state))
296                  __tasklet_hi_schedule(t);
297    
298              do { softirq_pending(cpu) |= 1UL << (nr); } while (0)  /* __cpu_raise_softirq */
299    
300              if (!(local_irq_count(cpu) | local_bh_count(cpu)))
301                   wakeup_softirqd(cpu);
302    
303            struct task_struct * tsk = ksoftirqd_task(cpu);          /* wakeup_softirqd() */
304    
305            if (tsk && tsk->state != TASK_RUNNING)
306                 wake_up_process(tsk);
307    
308            \end{verbatim}
309    
310            \begin{enumerate}
311             \item  It co-relates the "nr" bottom half to the tasklet array.
312             \item  Tests the tasklet state and calls function to schedule it.
313             \item  The function \textit{tasklet\_hi\_schedule} saves the local irq state and calls function \textit{cpu\_raise\_softirq()}.
314             \item  The function updates the softirq pending of the cpu ans checks if any other irq or bottom half is running on the current cpu. It none of them are running, then the kernel softirq daemon is woken up.
315             \item  The function \textit{wakeup\_softirqd} determines the process identifier of the ksoftirqd task. It confirms that the task is NOT already running and calls another function \textit{wake\_up\_process} to activate and schedule the task.
316    
317            \end{enumerate}
318    
319            \par Whenever mark\_bh is called, bottom half gets activated and invokes the associated function. This function is set in \textit{softirq\_init} ~\ref{init:softirq_init} by calling \textit{taslet\_init} function.
320    
321            \begin{verbatim}
322    
323            extern void tasklet_init(struct tasklet_struct *t,   /* Prototype */
324                           void (*func)(unsigned long), unsigned long data);
325    
326            for (i=0; i<32; i++)
327                 tasklet_init(bh_task_vec+i, bh_action, i);
328    
329            \end{verbatim}
330    
331            This function sets the \textit{func} parameter to \textit{bh\_action()} for all 32 bottom halves. The function is explained below.
332    
333            \begin{verbatim}
334            static void bh_action(unsigned long nr)
335            {
336                int cpu = smp_processor_id();
337    
338                if (!spin_trylock(&global_bh_lock))
339                    goto resched;
340    
341                if (!hardirq_trylock(cpu))
342                    goto resched_unlock;
343    
344                if (bh_base[nr])
345                    bh_base[nr]();
346    
347                hardirq_endlock(cpu);
348                spin_unlock(&global_bh_lock);
349                return;
350    
351            resched_unlock:
352                spin_unlock(&global_bh_lock);
353            resched:
354                mark_bh(nr);
355            }
356    
357            /* include/asm-i386/hardirq.h */
358    
359            #ifndef CONFIG_SMP
360            #define hardirq_trylock(cpu)    (local_irq_count(cpu) == 0)
361            #define hardirq_endlock(cpu)    do { } while (0)
362    
363            \end{verbatim}
364            The function checks whether the global bh spinlock is held by other cpu or the cpu is executing any other irq. If both of these conditions are false, if calls function to invoke the bottom half. When the function returns, the cpu releases the global bh spinlock.
365    
366            \section{Task Queues} \label{int:tqueue}
367            Task queues are often used in conjunction with bottom half handlers. Exmaple: the timer task queue is processed when the timer queue bottom half handler runs. Thus, task queues are dynamic extension to bottom halves. Please, refer to section ~\ref{int:bh} for functional description of bottom halves. Since , botton halves have some limitations:
368            \begin{itemize}
369            \item There are only a fixed number (32) of bottom halves.
370            \item Each bottom half can only be associated with one handler function.
371            \item Bottom halves are consumed with a spinlock held so they cannot block.
372            \end{itemize}
373                 So, with task queues, arbitrary number of functions can be chained and processed one after another at a later time.
374            \subsection{struct tq\_struct} \index{tq\_struct}
375            \newline \par The task structure is defined in \url{include/linux/tqueue.h} as follows:
376            \begin{verbatim}
377    
378            struct tq_struct {
379                struct list_head list;          /* linked list of active bh's */
380                unsigned long sync;             /* must be initialized to zero */
381                void (*routine)(void *);        /* function to call */
382                void *data;                     /* argument to function */
383            };
384    
385            \end{verbatim}
386    
387            A task queue is a linked list of the tasks as shown below.
388    
389            \begin{verbatim}
390    
391            typedef struct list_head task_queue;
392            extern task_queue tq_timer, tq_immediate;
393    
394            \end{verbatim}
395    
396            \par A task queue can be created using the \textit{DECLARE\_TASK\_QUEUE()} macro while a task onto it can be queued using the \textit{queue\_task()} function. The task queue then can be processed using \textit{run\_task\_queue()}.  The function run\_task\_queue() checks if the queue is active or not and calls the function from \url{kernel/softirq.c}.
397    
398            \subsection{Function run\_task\_queue}
399            When task queues are processed, the pointer to the first element in the queue is removed from the queue and replaced with a null pointer. In fact, this removal is an atomic operation, one that cannot be interrupted. Then each element in the queue has its handling routine called in turn.
400            % TODO run_task_queue task_queue = list_head Properties in tqueue.h
401    
402            \par  Linux predefines the following task queues which are consumed at well known points:
403            \begin{description}
404            \item[ tq\_timer ] \index{tq\_timer} The timer task queue, run on each timer interrupt and when releasing a tty device (closing or releasing a half opened terminal device). Since the timer handler runs in interrupt context, the tq\_timer tasks also run in interrupt context and thus cannot block.
405    
406            \item[ tq\_scheduler ] \index{tq\_scheduler} The scheduler task queue, consumed by the scheduler (and also when closing tty devices, like tq\_timer). Since the scheduler executed in the context of the process being re-scheduled, the tq\_scheduler tasks can do anything they like, i.e. block, use process context data etc.
407    
408            \item[ tq\_immediate ] \index{tq\_immediate} This is really a bottom half IMMEDIATE\_BH, so drivers can queue\_task(task, \&tq\_immediate) and then mark\_bh(IMMEDIATE\_BH) to be consumed in interrupt context.
409    
410            \item[ tq\_disk ] Used by low level block device access (and RAID) to start the actual requests. This task queue is exported to modules but shouldn't be used except for the special purposes which it was designed for. Unless a driver uses its own task queues, it does not need to call \textit{run\_tasks\_queues()} to process the queue, except under circumstances explained below.
411            \end{description}
412    
413            \section{tasklets} \label{int:tasklets} \index{tasklets}
414            The term tasklet is somewhat misleading, because tasklets have nothing to do with active processes or tasks in the operating system. The tasklet architecture is defined below:
415            \begin{itemize}
416    
417            \item  A tasklet is little more than a glorified function pointer, not all that different from a softirq.
418            \item  Two softirqs, HI\_SOFTIRQ and TASKLET\_SOFTIRQ, are devoted to run tasklets.
419            \item  The HI\_SOFTIRQ set is devoted to run bottom halves.
420    
421            \end{itemize}
422    
423            Tasklets are multithreaded analogues of bottom halves. Following information is directly taken fromt he \url{include/linux/interrupt.h}. Main feature differing tasklets from generic softirqs is that tasklet runs on only one cpu simultaneously while softirqs can run simultaneously on multiple cpus. Hence, while executing tasklet, it should be confirmed that the tasklet is NOT running on other cpu.
424            \par Main feature differing tasklets from bottom halves is that different tasklets may be run simultaneously on different CPUs. But, this is NOT true for bottom halves.
425            \par The properties of tasklets are listed below. We shall explain them later.  Properties:
426    
427            \begin{enumerate}
428            \item If tasklet\_schedule() is called, then tasklet is guaranteed to be executed on some cpu at least once after this.
429            \item If the tasklet is already scheduled, but its excecution is still not started, it will be executed only once.
430            \item If this tasklet is already running on another CPU (or schedule is called from tasklet itself), it is rescheduled for later.
431            \item Tasklet is strictly serialized with respect to itself, but not w.r.t. another tasklets. If client needs some intertask synchronization, he makes it with spinlocks.
432            \end{enumerate}
433    
434            The tasklet structure is defined in \url{include/linux/interrupt.h} as follows: \index{tasklet\_struct}
435            \begin{verbatim}
436    
437            struct tasklet_struct
438            {
439                struct tasklet_struct *next;
440                unsigned long state;
441                atomic_t count;
442                void (*func)(unsigned long);
443                unsigned long data;
444            };
445    
446            #define DECLARE_TASKLET(name, func, data) \
447            struct tasklet_struct name = { NULL, 0, ATOMIC_INIT(0), func, data }
448    
449            \end{verbatim}
450    
451            The structure is defined as linked list of tasklets. The other components are as follows:
452            \begin{description}
453            \item[state] Tasklet states. This can have one of the two values TASKLET\_STATE\_SCHED, TASKLET\_STATE\_RUN.
454            \item[count] atomic counter. Updated using atomic functions during \textit{tasklet\_enable} and \textit{tasklet\_disable} functions.
455            \item[func] The function associated with the tasklet.
456            \item[data] The data passed as argument to above function pointer.
457            \end{description}
458            \newline     The tasklet is defined by using the \textit{DECLARE\_TASKLET} macro and initialized by \textit{tasklet\_init()} function. This function initializes all the parameters of the tasklet\_struct. If the tasklet is running, it can be deactivated by \textit{tasklet\_kill()} function.
459            \begin{verbatim}
460    
461            void tasklet_kill(struct tasklet_struct *t)
462            {
463                if (in_interrupt())
464                    printk("Attempt to kill tasklet from interrupt\n");
465    
466                while (test_and_set_bit(TASKLET_STATE_SCHED, &t->state)) {
467                    current->state = TASK_RUNNING;
468                    do
469                        yield();
470                    while (test_bit(TASKLET_STATE_SCHED, &t->state));
471                }
472                tasklet_unlock_wait(t);
473                clear_bit(TASKLET_STATE_SCHED, &t->state);
474            }
475    
476            \end{verbatim}
477            The function checks if the interrupt is running or NOT. Killing a tasklet from interupt context is not recommended. Then, the function schedules the tasklet \footnote{\#define yield() sys\_sched\_yield() in \url{include/linux/sched.h}} while the tasklet state is TASKLET\_STATE\_SCHED.
478            \par The important function related to tasklets is \textit{\_\_tasklet\_schedule()}. \index{tasklet\_schedule}
479            \begin{verbatim}
480    
481            static struct tasklet_head tasklet_vec __per_cpu_data = { NULL };
482    
483            void __tasklet_schedule(struct tasklet_struct *t)
484            {
485                unsigned long flags;
486    
487                local_irq_save(flags);
488                t->next = this_cpu(tasklet_vec).list;
489                this_cpu(tasklet_vec).list = t;
490                cpu_raise_softirq(smp_processor_id(), TASKLET_SOFTIRQ);
491                local_irq_restore(flags);
492            }
493    
494            \end{verbatim}
495            The \textit{tasklet\_vec} structure is used as header node to keep track of the linked tasklets. Whenever a new tasklet is scheduled, it is added to the tasklet list. The macro \textit{this\_cpu}, defined in \url{include/linux/percpu.h} just returns the argument for i386 architecture. Later,it calls the function cpu\_raise\_softirq with TASKLET\_SOFTIRQ parameter. This functions similar to the section ~\ref{int:markbh}, except separate bit of the softirq\_pending is updated.
496    
497            \par Another important function related to tasklets is \textit{tasklet\_action}. These function is associated to TASKLET\_SOFTIRQ.
498            \par The softirq HI\_SOFTIRQ is used by high priority bottom halves as old-style BHs and sound related drivers. It triggers execution of tasklet\_hi\_action(). TASKLET\_SOFTIRQ runs lower priority bottom halves (e.g. in the console subsystem). It triggers execution of tasklet\_action().
499    
500            \subsection{Function tasklet\_action}
501            \begin{verbatim}
502    
503            static void tasklet_action(struct softirq_action *a)
504            {
505                struct tasklet_struct *list;
506    
507                local_irq_disable();
508                list = this_cpu(tasklet_vec).list;
509                this_cpu(tasklet_vec).list = NULL;  /* .1. */
510                local_irq_enable();
511    
512                while (list) {
513                    struct tasklet_struct *t = list;
514    
515                    list = list->next;              /* .2. */
516    
517                    if (tasklet_trylock(t)) {
518                       if (!atomic_read(&t->count)) { /* .3. */
519                            if (!test_and_clear_bit(TASKLET_STATE_SCHED, &t->state))
520                                BUG();               /* .4. */
521                            t->func(t->data);
522                            tasklet_unlock(t);       /* .5. */
523                            continue;
524                        }
525                    tasklet_unlock(t);
526                    }
527    
528                    local_irq_disable();
529                    t->next = this_cpu(tasklet_vec).list;    /* .6. */
530                    this_cpu(tasklet_vec).list = t;
531                    __cpu_raise_softirq(smp_processor_id(), TASKLET_SOFTIRQ);
532                    local_irq_enable();              /* .7. */
533                }
534            }
535    
536                  /*    include/linux/interrupt.h     */
537    
538            #ifdef CONFIG_SMP
539            static inline int tasklet_trylock(struct tasklet_struct *t)
540            {
541                return !test_and_set_bit(TASKLET_STATE_RUN, &(t)->state);
542            }
543            #else
544                #define tasklet_trylock(t) 1
545            #endif
546    
547            \end{verbatim}
548    
549            \begin{enumerate}
550            \item  Initially, the function grabs the tasklet list for the current CPU. This list was populated by adding tasklet by the \textit{\_\_tasklet\_schedule} function. The macro \textit{this\_cpu} returns just returns the argument.
551            \item When the tasklet list is obtained, it is scanned for tasks in NOT running state. The inline function \textit{test\_and\_set\_bit} checks whether the task state is running. If the state is not running, it sets it to TASK\_RUNNING and returns 1. For non-SMP systems, this simply turns out to be a macro returning 1.
552            \item Then, it checks tasklet state using the "t->count". If the count is 1, the tasklet is disabled.\footnote{verify tasklet state using macros DECLARE\_TASKLET\_DISABLED in \url{include/linux/interrupt.h}.}
553            \item Then it tests whether the tasklet is schedulable or NOT. If the tasklet state is not schedulable, a BUG is reported.
554            \item After all these important sanity checks, the function associated with tasklet is invoked with the associated data. The tasklet is unlocked by reseting the TASKLET\_STATE\_RUN state and the function continues to scan the next tasklet in the linked list.
555            \item The tasklet is added to the tasklet list running on this cpu. This operation is performed with the local interrupts disabled.
556            \item Finally the function \_\_cpu\_raise\_softirq is called. This may be used to wake up the softirq daemon and schedule the tasklets.
557            \end{enumerate}
558    
559            \section{softirqs} \label{int:softirq} \index{softirq}
560            A softirq is an interrupt handler that runs outside of the normal interrupt context, runs with interrupts enabled, and runs concurrently when necessary. The kernel runs up to one copy of a softirq on each processor in a system. Softirqs are introduced to replace the bottom-half handler strategy used in Linux 2.2 and older kernels, and are designed to provide more scalable interrupt handling in SMP settings. The softirq properties are listed below:
561            \begin{itemize}
562    
563            \item  A softirq is just a function pointer and an argument to pass to that function.
564            \item  For each softirq, there is a one- bit flag that indicates whether it should be run.
565            \item  The kernel polls the softirq flags regularly.
566            \item  Normally, once the softirq flag has turned on, kernel will invoke the softirq in a short time.
567    
568            \end{itemize}
569    
570            Following softirqs are currently defined in Linux kernel:
571            \begin{verbatim}
572            enum
573            {
574                HI_SOFTIRQ=0,       /* devoted to run bottom halves.   */
575                NET_TX_SOFTIRQ,     /* devoted to network transmitters */
576                NET_RX_SOFTIRQ,     /* devoted to network receivers    */
577                TASKLET_SOFTIRQ     /* devoted to run tasklets.        */
578            };
579    
580            \end{verbatim}
581            The \textit{softirq\_action} structure is similar to the irqaction structure, which is used to store the softirq function pointer and its data argument.
582    
583            \begin{verbatim}
584            struct softirq_action
585            {
586                void    (*action)(struct softirq_action *);
587                void    *data;
588            };
589    
590            \end{verbatim}
591    
592            The function \textit{open\_softirq} is used to populate softirq properties. This function is used from softirq\_init. Refer section ~\ref{init:softirq_init} for softirq initialization. The function sets the data and the parameters of the softirq strcture in the global array \textit{softirq\_vec}.
593            \begin{verbatim}
594    
595            static struct softirq_action softirq_vec[32] __cacheline_aligned_in_smp ;
596    
597            void open_softirq(int nr, void (*action)(struct softirq_action*), void *data)
598            {
599                softirq_vec[nr].data = data;
600                softirq_vec[nr].action = action;
601            }
602    
603            void raise_softirq(unsigned int nr)
604            {
605                long flags;
606    
607                local_irq_save(flags);
608                cpu_raise_softirq(smp_processor_id(), nr);
609                local_irq_restore(flags);
610            }
611    
612            \end{verbatim}
613    
614            The function raise\_softirq is a dummy function. If is not called from any code, seems it needs to be fixed. This function has the advantage that it can be called from any irq state. It may be meant for the device drivers. It saves current irq flags and calls the function cpu\_raise\_softirq with [nr] SOFTIRQ parameter\footnote{This may be relevant from future point of view, for new softirqs}. This functions similar to the section ~\ref{int:markbh}, except separate bit of the softirq\_pending is updated.
615    
616            \subsection{Function do\_softirq} \index{do\_softirq}
617            \par The primary function that handles softirqs is \textit{do\_softirq}. This function is explained below.
618            \begin{verbatim}
619    
620            asmlinkage void do_softirq()
621            {
622                int cpu = smp_processor_id();
623                __u32 pending;
624                long flags;
625                __u32 mask;
626    
627                if (in_interrupt())     /* .1. */
628                    return;
629    
630                local_irq_save(flags);  /* .2. */
631    
632                pending = softirq_pending(cpu);
633    
634                if (pending) {
635                    struct softirq_action *h;
636    
637                    mask = ~pending;
638                    local_bh_disable();
639            restart:
640                    /* Reset the pending bitmask before enabling irqs */
641                    softirq_pending(cpu) = 0;
642    
643                    local_irq_enable();
644    
645                    h = softirq_vec;
646    
647                    do {
648                            if (pending & 1)
649                                h->action(h);   /* .3.  */
650                            h++;
651                            pending >>= 1;
652                    } while (pending);
653    
654                    local_irq_disable();
655    
656                    pending = softirq_pending(cpu);
657                    if (pending & mask) {
658                        mask &= ~pending;       /* .4.  */
659                        goto restart;
660                    }
661                    __local_bh_enable();
662    
663                    if (pending)
664                        wakeup_softirqd(cpu);   /* .5.  */
665                    }
666                    local_irq_restore(flags);
667            }
668    
669            \end{verbatim}
670    
671            \begin{enumerate}
672            \item The softirq operation should be performed when the cpu is NOT performing any interrupt operation. Hence, the code first checks if the cpu is executing any interrupt by calling in\_interrupt().
673            \item Then, it saves the current irq status (FLAGS). The softirq pending status is obtained from the \textit{irq\_stat} structure. \footnote{\#define softirq\_pending(cpu)    \_\_IRQ\_STAT((cpu), \_\_softirq\_pending).   Refer to \~ref{int:cpustat} for details about irq\_stat.}
674            \item If any of the softirqs are pending, the corresponding functions need to be called. So, first it enables local interrupt and scans all the bits of the local var, "pending". If the bit is 1, then the corresponding function is called. After servicing all softirqs, local interrupts are disabled.
675            \item If any of the new softirq is raised, during the execution of the softirq actions [in the while loop], it needs to be serviced again. The new softirq is checked by using the mask field obtained previously. The function checks the \textit{softirq\_pending} field again and continues to do so, till there are no new softirqs, encountered.
676            \item But, if any of the softirq, already serviced, is raised again, the softirqd daemon is waken up. Refer to next section ~\ref{int:softirqd} for details of softirq daemon.
677            \end{enumerate}
678            \newline
679            \newline
680            \subsection{ksoftirqd} \label{int:softirqd} \index{softirqd}
681            Softirq daemon is introduced in later versions of kernel 2.4. By design, \textit{ksoftirqd} is only run when softirq load is so high that IRQ contexts are not able to handle it all. The current softirq code is such that the the daemon is invoked when the softirq handler misses any pending softirqs that got activated while running softirqs and those pending softirqs will only be processed in the next IRQ tick or whenever the softirqd is scheduled.
682            \newline \par Thus, Softirqd is a successful attempt to solve the latency problem created by the current design of softirqs. The softirqd is run at low priority. Kernel developers suggest that to get optimal performance is obtained, when both softirqs and tasklets are avoided, and processing is done in the hardirq context.
683            \newline \par Following functions are related to the softirq daemon.
684            \begin{itemize}
685            \item sqawn\_ksoftirqd : Used to spawn softirq daemon by creating new kernel thread.
686            \item wakeup\_softirqd : Used to wake up softirq daemon and execute pending softirqs.
687            \item ksoftirqd : Used as main sfotirq daemon action.
688            \end{itemize}
689    
690            \subsection{ksoftirqd daemon}
691            \begin{verbatim}
692            static int ksoftirqd(void * __bind_cpu)
693            {
694                int bind_cpu = (int) (long) __bind_cpu;
695                int cpu = cpu_logical_map(bind_cpu);
696    
697                daemonize();
698                set_user_nice(current, 19);
699                current->flags |= PF_IOTHREAD;
700                sigfillset(&current->blocked);
701    
702                /* Migrate to the right CPU */
703                set_cpus_allowed(current, 1UL << cpu);
704                if (smp_processor_id() != cpu)
705                    BUG();
706    
707                sprintf(current->comm, "ksoftirqd_CPU%d", bind_cpu);
708    
709                __set_current_state(TASK_INTERRUPTIBLE);
710                mb();
711    
712                ksoftirqd_task(cpu) = current;
713    
714                for (;;) {
715                    if (!softirq_pending(cpu))
716                        schedule();
717    
718                    __set_current_state(TASK_RUNNING);
719    
720                    while (softirq_pending(cpu)) {
721                        do_softirq();
722                        cond_resched();
723                    }
724    
725                    __set_current_state(TASK_INTERRUPTIBLE);
726                }
727            }
728            \end{verbatim}
729            % TODO argument
730            The function cpu\_logical\_map() is architecture dependant. For i386 arch, it just returns the cpu. Then, the softirq is converted to daemon by daemonize() function. The softirq task sets its priority \footnote{nice = 19, specifies process priority. [-19...0...19] range is allowed where -19 is lowest priority while +19 is tht highest priority.} and also sets the signal properties. The function \textit{set\_cpus\_allowed} \footnote{set\_cpus\_allowed is defined in \url{kernel/sched.c}. The function is SMP specific.} migrates the softirq process to proper CPU, so that the function is scheduled only on this CPU and not allowed to run on other CPUs. If that condition occurs, a BUG is reported. The comm field of the process structure stores the comment namely the daemon name.
731            \newline \par Then the function enters a loop, wherein it continuously checks whether any softirqs are pending or not. If any softirqs are pending, the scheduler is called to run the process and its state is change to TASK\_RUNNING.

Legend:
Removed from v.1.1  
changed lines
  Added in v.1.2

savannah-hackers-public@gnu.org
ViewVC Help
Powered by ViewVC 1.1.26