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 |
|
|
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); |
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; |
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 */ |
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 |
|
|
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, ®s, action); |
handle_IRQ_event(irq, ®s, action); |
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; |
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 |
|
|
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(¤t->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. |