1 |
\chapter{Interrupts} |
\chapter{Interrupts} |
2 |
\section{Interrupt Basics: IDT Table} \index{set\_gate} |
\section{Interrupt Basics: IDT Table} \index{set\_gate} |
3 |
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. |
\textbf{\Large T}he interrupt mechanism is initialized by the functions \textit{trap\_init()} and \textit{init\_IRQ()} during the system bootup as explained in the section ~\ref{init:sk}. The following functions are used to insert the gates in the IDT. The function \textit{trap\_init()} sets the exception mechanism while \textit{init\_IRQ()} initializes up the interrupts. |
4 |
% TODO gate explaination and ref. |
\par Please, refer to appendix ~\ref{appendix1} for the details about these gates. |
5 |
\begin{verbatim} |
\begin{verbatim} |
6 |
|
|
7 |
set_intr_gate(n, addr); |
set_intr_gate(n, addr); |
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 |
\newline |
48 |
The lock field is used to prevent concurrent accesss in SMP environment. |
The lock field is used to prevent concurrent access in SMP environment. |
49 |
\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} |
\par The handler field describes 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} |
\subsection{hw\_interrupt\_type} |
52 |
\begin{verbatim} |
\begin{verbatim} |
63 |
}; |
}; |
64 |
|
|
65 |
\end{verbatim} |
\end{verbatim} |
66 |
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. |
The \textit{typename} is the irq name that appears in the file "/proc/interrupts". While the remaining fields are the functions pointers, which are invoked to interact with the hardware in response to driver calls like \textit{request\_irq()} and \textit{free\_irq()} and others. |
67 |
|
|
68 |
\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. |
\par The action field in \textit{hw\_desc\_t} desribes the linked list of interrupt actions that are performed when the interrupt occurs. 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} |
\subsection{irqaction} |
71 |
\begin{verbatim} |
\begin{verbatim} |
95 |
\item[name] The name of the interrupt. |
\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. |
\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. |
\item[next] The typical next pointer of a linked list. |
|
|
|
98 |
\end{description} |
\end{description} |
99 |
|
|
100 |
|
\section{Exception Processing} |
101 |
|
The exceptions are generated by the CPU itself as a result of processing error or limitations. The exceptions handers are set by the \textit{trap\_init} function defined in \url{arch/i386/kernel/traps.c}. Refer to Section ~\ref{init:trap_init} for details. The entries registered by the set\_trap\_gate function are used as ENTRY points to index the code in \url{arch/i386/kernel/entry.S}. |
102 |
|
\begin{verbatim} |
103 |
|
Example : NMI |
104 |
|
|
105 |
|
set_trap_gate(2,&nmi); |
106 |
|
|
107 |
|
ENTRY(nmi) |
108 |
|
pushl %eax |
109 |
|
SAVE_ALL |
110 |
|
movl %esp, %edx |
111 |
|
pushl $0 |
112 |
|
pushl %edx |
113 |
|
call do_nmi |
114 |
|
addl $8, %esp |
115 |
|
RESTORE_ALL |
116 |
|
|
117 |
|
jmp error_code |
118 |
|
\end{verbatim} |
119 |
|
|
120 |
|
All the exceptions perform the generic function by calling the function. The function name is obtained by appending the exception name to \textit{do\_*}. The code at label error\_code is used to save the exception code, function address on to the stack and call invoke the appropriate function. These functions are defined in \url{arch/i386/kernel/traps.c}. |
121 |
|
|
122 |
|
\begin{verbatim} |
123 |
|
error_code: |
124 |
|
pushl %ds |
125 |
|
pushl %eax |
126 |
|
xorl %eax, %eax |
127 |
|
pushl %ebp |
128 |
|
pushl %edi |
129 |
|
pushl %esi |
130 |
|
pushl %edx |
131 |
|
decl %eax # eax = -1 |
132 |
|
pushl %ecx |
133 |
|
pushl %ebx |
134 |
|
cld |
135 |
|
movl %es, %ecx |
136 |
|
movl ORIG_EAX(%esp), %esi # get the error code |
137 |
|
movl ES(%esp), %edi # get the function address |
138 |
|
movl %eax, ORIG_EAX(%esp) |
139 |
|
movl %ecx, ES(%esp) |
140 |
|
movl %esp, %edx |
141 |
|
pushl %esi # push the error code |
142 |
|
pushl %edx # push the pt_regs pointer |
143 |
|
movl $(__KERNEL_DS), %edx |
144 |
|
movl %edx, %ds |
145 |
|
movl %edx, %es |
146 |
|
GET_THREAD_INFO(%ebx) |
147 |
|
call *%edi |
148 |
|
addl $8, %esp |
149 |
|
preempt_stop |
150 |
|
jmp ret_from_exception |
151 |
|
\end{verbatim} |
152 |
|
When the function(exception handler) jumps to \textit{error\_code:}, it first pushes the address of the function to be called and then pushes all the CPU registers. The system call in \%eax is also stored in ORIG\_EAX. This value is used, during the re-execution of system calls. The segment registers \%ds, \%es contain the address of kernel segment while \%edi register is populated using the values in the stack(\%esp) and later used to invoke the exception handler. Note, the "\texit{cld}" instruction clears the direction flag from the eflags register, which autoincrements the \%esi, \%edi registers. Refer to intel architecture for details. |
153 |
|
\par While returning from exception, the code pops up the stack address of user mode registers and jumps to \textit{ret\_from\_exception}, wherein it checks for signals and so on. |
154 |
|
|
155 |
\section{Interrupt Processing} |
\section{Interrupt Processing} |
156 |
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. |
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. |
157 |
|
|
160 |
common_interrupt: |
common_interrupt: |
161 |
SAVE_ALL /* save flags on stack */ |
SAVE_ALL /* save flags on stack */ |
162 |
GET_THREAD_INFO(%ebx) /* read thread status */ |
GET_THREAD_INFO(%ebx) /* read thread status */ |
163 |
INC_PRE_COUNT(%ebx) /* counter incresed */ |
INC_PRE_COUNT(%ebx) /* counter increased */ |
164 |
call do_IRQ |
call do_IRQ |
165 |
jmp ret_from_intr /* RET from interrupt */ |
jmp ret_from_intr /* RET from interrupt */ |
166 |
|
|
232 |
} |
} |
233 |
|
|
234 |
\end{verbatim} |
\end{verbatim} |
235 |
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()}. |
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 executed 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()}. |
236 |
\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. |
\par The softirq status is checked before returning from the interrupt. 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. |
237 |
|
|
238 |
\subsection{Function handle\_IRQ\_event} |
\subsection{Function handle\_IRQ\_event} |
239 |
\par The handle\_IRQ\_event function is described below in parts. |
\par The handle\_IRQ\_event function is described below in parts. |
278 |
Then, the interrupt are enabled using \_\_cli() call. and the macro \textit{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. |
279 |
|
|
280 |
\subsection{interrupt related CPU status} \label{int:cpustat} \index{irq\_cpustat} |
\subsection{interrupt related CPU status} \label{int:cpustat} \index{irq\_cpustat} |
281 |
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}. |
The kernel defines interrupt related CPU status in the irq\_cpustat\_t structure. The structure defines the values of following parameters 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}. |
282 |
\begin{verbatim} |
\begin{verbatim} |
283 |
|
|
284 |
/* include/asm/hardirq.h ; offset sensitive code */ |
/* include/asm/hardirq.h ; offset sensitive code */ |
305 |
\end{verbatim} |
\end{verbatim} |
306 |
|
|
307 |
\section{Top Half} |
\section{Top Half} |
308 |
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. |
Since, a interrupt handling involves substantial amount of work, keeping the interrupts 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 function \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. |
309 |
\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. |
\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 acknowledging 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. |
310 |
\par Advantages of this scheme are listed below: |
\par Advantages of this scheme are listed below: |
311 |
\begin{itemize} |
\begin{itemize} |
312 |
\item Interrupts remain disabled for a very less amount of time. |
\item Interrupts remain disabled for a very less amount of time. |
338 |
|
|
339 |
\end{verbatim} |
\end{verbatim} |
340 |
|
|
341 |
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. |
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 weak 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. |
342 |
\par The bottom half is activated only when the \textit{mark\_bh()} function is called. The mark\_bh function performs following operations. |
\par The bottom half is activated only when the \textit{mark\_bh()} function is called. The mark\_bh function performs following operations. |
343 |
|
|
344 |
\subsection{Function mark\_bh} \label{int:markbh} |
\subsection{Function mark\_bh} \label{int:markbh} |
418 |
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. |
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. |
419 |
|
|
420 |
\section{Task Queues} \label{int:tqueue} |
\section{Task Queues} \label{int:tqueue} |
421 |
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: |
Task queues are often used in conjunction with bottom half handlers. Example: 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 , bottom halves have some limitations: |
422 |
\begin{itemize} |
\begin{itemize} |
423 |
\item There are only a fixed number (32) of bottom halves. |
\item There are only a fixed number (32) of bottom halves. |
424 |
\item Each bottom half can only be associated with one handler function. |
\item Each bottom half can only be associated with one handler function. |
426 |
\end{itemize} |
\end{itemize} |
427 |
So, with task queues, arbitrary number of functions can be chained and processed one after another at a later time. |
So, with task queues, arbitrary number of functions can be chained and processed one after another at a later time. |
428 |
\subsection{struct tq\_struct} \index{tq\_struct} |
\subsection{struct tq\_struct} \index{tq\_struct} |
429 |
\newline \par The task structure is defined in \url{include/linux/tqueue.h} as follows: |
\\ \par The task structure is defined in \url{include/linux/tqueue.h} as follows: |
430 |
\begin{verbatim} |
\begin{verbatim} |
431 |
|
|
432 |
struct tq_struct { |
struct tq_struct { |
449 |
|
|
450 |
\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}. |
\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}. |
451 |
|
|
|
\subsection{Function run\_task\_queue} |
|
|
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. |
|
|
% TODO run_task_queue task_queue = list_head Properties in tqueue.h |
|
|
|
|
452 |
\par Linux predefines the following task queues which are consumed at well known points: |
\par Linux predefines the following task queues which are consumed at well known points: |
453 |
\begin{description} |
\begin{description} |
454 |
\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. |
\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. |
460 |
\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. |
\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. |
461 |
\end{description} |
\end{description} |
462 |
|
|
463 |
|
\subsection{Function run\_task\_queue} |
464 |
|
\begin{verbatim} |
465 |
|
|
466 |
|
/**** kernel/timer.c *****/ |
467 |
|
|
468 |
|
void tqueue_bh(void) |
469 |
|
{ |
470 |
|
run_task_queue(&tq_timer); |
471 |
|
} |
472 |
|
|
473 |
|
void immediate_bh(void) |
474 |
|
{ |
475 |
|
run_task_queue(&tq_immediate); |
476 |
|
} |
477 |
|
|
478 |
|
\end{verbatim} |
479 |
|
The \texit{run\_task\_queue} function is invoked for a particular task queue from its associated bottom half. The function checks whether the task queue is active ( Active task queue is defined as a task queue with non-NULL list) and invokes \textit{\_\_run\_task\_queue()} function with the appropriate task queue as parameter. 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. The function is explaine below: |
480 |
|
\begin{verbatim} |
481 |
|
void __run_task_queue(task_queue *list) |
482 |
|
{ |
483 |
|
struct list_head head, *next; |
484 |
|
unsigned long flags; |
485 |
|
|
486 |
|
spin_lock_irqsave(&tqueue_lock, flags); |
487 |
|
list_add(&head, list); |
488 |
|
list_del_init(list); /* 1. */ |
489 |
|
spin_unlock_irqrestore(&tqueue_lock, flags); |
490 |
|
|
491 |
|
next = head.next; |
492 |
|
while (next != &head) { /* 2. */ |
493 |
|
void (*f) (void *); |
494 |
|
struct tq_struct *p; |
495 |
|
void *data; |
496 |
|
|
497 |
|
p = list_entry(next, struct tq_struct, list); /* 3. */ |
498 |
|
next = next->next; |
499 |
|
f = p->routine; |
500 |
|
data = p->data; |
501 |
|
wmb(); |
502 |
|
p->sync = 0; |
503 |
|
if (f) |
504 |
|
f(data); /* 4. */ |
505 |
|
} |
506 |
|
} |
507 |
|
|
508 |
|
\end{verbatim} |
509 |
|
\begin{enumerate} |
510 |
|
\item The function removes the first task queue for execution. In fact, this removal is an atomic operation, one that cannot be interrupted using the spin\_lock/unlock\_irqsave/restore pair. |
511 |
|
\item The function scans all the elements of the list till the starting node. Note, that the list is implemented as circular linked list. |
512 |
|
\item The macro list\_entry [ defined in \url{include/linux/list.h}] retrives the element of type "struct tq\_struct" from the "list" using the "next" pointer. |
513 |
|
\item Then each element in the queue has its handling routine called using the routine(f) and data(data). |
514 |
|
\end{enumerate} |
515 |
|
|
516 |
\section{tasklets} \label{int:tasklets} \index{tasklets} |
\section{tasklets} \label{int:tasklets} \index{tasklets} |
517 |
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: |
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: |
518 |
\begin{itemize} |
\begin{itemize} |
523 |
|
|
524 |
\end{itemize} |
\end{itemize} |
525 |
|
|
526 |
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. |
Tasklets are multithreaded analogues of bottom halves. Following information is directly taken from the \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. |
527 |
\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. |
\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. |
528 |
\par The properties of tasklets are listed below. We shall explain them later. Properties: |
\par The properties of tasklets are listed below. We shall explain them later. Properties: |
529 |
|
|
530 |
\begin{enumerate} |
\begin{enumerate} |
531 |
\item If tasklet\_schedule() is called, then tasklet is guaranteed to be executed on some cpu at least once after this. |
\item If tasklet\_schedule() is called, then tasklet is guaranteed to be executed on some cpu at least once after this. |
532 |
\item If the tasklet is already scheduled, but its excecution is still not started, it will be executed only once. |
\item If the tasklet is already scheduled, but its execution is still not started, it will be executed only once. |
533 |
\item If this tasklet is already running on another CPU (or schedule is called from tasklet itself), it is rescheduled for later. |
\item If this tasklet is already running on another CPU (or schedule is called from tasklet itself), it is rescheduled for later. |
534 |
\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. |
\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. |
535 |
\end{enumerate} |
\end{enumerate} |
577 |
} |
} |
578 |
|
|
579 |
\end{verbatim} |
\end{verbatim} |
580 |
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. |
The function checks if the interrupt is running or NOT. Killing a tasklet from interrupt 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. |
581 |
\par The important function related to tasklets is \textit{\_\_tasklet\_schedule()}. \index{tasklet\_schedule} |
\par The important function related to tasklets is \textit{\_\_tasklet\_schedule()}. \index{tasklet\_schedule} |
582 |
\begin{verbatim} |
\begin{verbatim} |
583 |
|
|
692 |
|
|
693 |
\end{verbatim} |
\end{verbatim} |
694 |
|
|
695 |
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}. |
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 structure in the global array \textit{softirq\_vec}. |
696 |
\begin{verbatim} |
\begin{verbatim} |
697 |
|
|
698 |
static struct softirq_action softirq_vec[32] __cacheline_aligned_in_smp ; |
static struct softirq_action softirq_vec[32] __cacheline_aligned_in_smp ; |
782 |
\newline |
\newline |
783 |
\subsection{ksoftirqd} \label{int:softirqd} \index{softirqd} |
\subsection{ksoftirqd} \label{int:softirqd} \index{softirqd} |
784 |
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. |
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. |
785 |
\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. |
\\ \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. |
786 |
\newline \par Following functions are related to the softirq daemon. |
\\ \par Following functions are related to the softirq daemon. |
787 |
\begin{itemize} |
\begin{itemize} |
788 |
\item sqawn\_ksoftirqd : Used to spawn softirq daemon by creating new kernel thread. |
\item sqawn\_ksoftirqd : Used to spawn softirq daemon by creating new kernel thread. |
789 |
\item wakeup\_softirqd : Used to wake up softirq daemon and execute pending softirqs. |
\item wakeup\_softirqd : Used to wake up softirq daemon and execute pending softirqs. |
790 |
\item ksoftirqd : Used as main sfotirq daemon action. |
\item ksoftirqd\_task : This macro is used to bind the specified task (generally "\textit{current}" task) with the softirq daemon. \textit{ksoftirqd\_task} is part of cpu interrupt status. Refer to section ~\ref{int:cpustat} for details. |
791 |
|
\item ksoftirqd : Used as main softirq daemon action. |
792 |
\end{itemize} |
\end{itemize} |
793 |
|
|
794 |
\subsection{ksoftirqd daemon} |
\subsection{ksoftirqd daemon} |
829 |
__set_current_state(TASK_INTERRUPTIBLE); |
__set_current_state(TASK_INTERRUPTIBLE); |
830 |
} |
} |
831 |
} |
} |
832 |
|
|
833 |
|
/***** spawn_softirqd function *****/ |
834 |
|
|
835 |
|
static __init int spawn_ksoftirqd(void) |
836 |
|
{ |
837 |
|
int cpu; |
838 |
|
for (cpu = 0; cpu < smp_num_cpus; cpu++) |
839 |
|
if (kernel_thread(ksoftirqd, (void *) (long) cpu, |
840 |
|
CLONE_FS | CLONE_FILES | CLONE_SIGNAL) < 0) |
841 |
|
printk("spawn_ksoftirqd() failed for cpu %d\n", cpu); |
842 |
|
else |
843 |
|
while (!ksoftirqd_task(cpu_logical_map(cpu))) |
844 |
|
yield(); |
845 |
|
return 0; |
846 |
|
} |
847 |
|
|
848 |
\end{verbatim} |
\end{verbatim} |
849 |
% TODO argument |
The ksoftirq daemon is spawned for all cpus from the \textit{spawn\_softirqd} function. The spu number is passed as parameter to the daemon function. Thus, each cpu runs a ksoftirqd daemon. If the softirq daemon starts successfully, it invokes the \texit{yield()=sys\_sched\_yield} function to schedule itself and add the current task to the runqueue. |
850 |
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. |
\par 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 the 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. |
851 |
\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. |
\\ \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. |