The software interrupt kernel thread

The previous section has described how software interrupts are handled in interrupt context on the return path from hardware interrupt handling, but there is also a kernel thread (in fact, one per CPU) dedicated to handling software interrupts. This thread is woken up when the load of software interrupts becomes too great to handle in interrupt context (it would take too many machine cycles from the current process).

16.1.4.1 Spawning kernel threads to handle software interrupts

At boot time, one kernel thread running ksoftirqd is spawned for each CPU, by the function shown in Figure 16.6, from kernel/softirq .c.

399 static_init int spawn_ksoftirqd(void)

400 {

401 int cpu;

40 3 for (cpu = 0; cpu < smp_num_cpus; cpu++) {

404 if (kernel_thread(ksoftirqd, (void *) (long) cpu,

40 5 CLONE_FS | CLONE_FILES | CLONE_SIGNAL)<0)

406 printk("spawn_ksoftirqd() failed for cpu %d\n", cpu);

407 else {

408 while (!ksoftirqd_task(cpu_logical_map(cpu))) {

409 current->policy | = SCHED_YIELD;

410 schedule();

411 }

412 }

413 }

415 return 0;

Figure 16.6 Spawning kernel threads for software interrupts

403-413 we go through each CPU, using the count of CPUs defined in Section 7.2.1.1.

404-406 we create a kernel thread; if we are unable to do this an informational message is printed.

404 the kernel_thread() function was described in Section 8.5. Its parameters are a pointer to

408-412 408

the function to run (see Section 16.1.4.2), a pointer to the argument to that function (the number of the CPU on which it is to run, cast as a pointer to void), and the clone flags that govern its creation.

the function is to share file systems, open files, and signal handlers with its parent (see Section 8.3.1 for these flags).

this block of code is executed if the thread was created successfully.

this converts a sequence number to a CPU ID, using the function given in Section 7.2.1.2. Then, if the newly cloned thread has not yet completed its initialisation (by announcing itself in the array in Section 16.5.1.1), the parent does not go on to create the next thread until this one is fully set up.

this yields the CPU for one pass through the scheduler. The ksoftirq kernel thread

The function executed by the ksoftirq kernel thread is shown in Figure 16.7, from kernel/softirq.c.

362 static int ksoftirqd(void *_bind_cpu)

int cpu = cpu_logical_map(bind_cpu);

demonise();

sigfillset(&current->blocked);

current->cpus_allowed = 1UL << cpu; while (smp_processor_id() ! = cpu) schedule();

sprintf(current->comm, "ksoftirqd_CPU%d",bind_cpu);

_set_current_state(TASK_INTERRUPTIBLE);

ksoftirqd_task(cpu)= current;

if (!softirq_pending(cpu)) schedule();

set_current_state(TASK_RUNNING);

while (softirq_pending(cpu)) { do_softirq();

if(current->need_resched) schedule();

369 372

373-374

379 381

383-396

384-385

387 389-393 390 391-392

39 5 _set_current_state(TASK_INTERRUPTIBLE);

396 }

397 }

Figure 16.7 The ksoftirqd kernel thread the parameter is an int, containing the sequence number of the CPU for which it is created, cast as a pointer to void.

this casts the void pointer to long, and then to int.

this converts the sequence number to a logical ID, using the function from Section 7.2.1.2. this function deallocates memory and file-system resources, not needed by a kernel thread. the priority is set very low.

this marks all signals blocked to this thread, using the function from Section 17.2.2.5.

setting the bit corresponding to cpu in this bitmap limits it to running on that CPU only.

if the process is not currently running on its home processor, then the scheduler is called, and the CPU is given up. When the process runs again, it will be on its home processor.

in the comm field of the task_struct of this thread, we set up the name of the program it is running as ksoftirqd_CPUx, where x is the sequence number of the CPU.

here, the process is set it up for sleeping interruptibly (long term), using the macro from Section 4.6.1.3.

this macro forces strict CPU ordering, so that all CPUs see the foregoing assignments.

this puts a pointer to its task_struct into the appropriate element of the array described in Section 16.5.1.1, using the macro from Section 16.5.1.2.

now that the initialisation has been done, the program goes into its infinite loop.

if no software interrupts are pending, then the CPU is yielded. The state of the process will be TASK_INTERRUPTIBLE, from line 378 or 395.

when the process wakes up again, it is set to be runable.

this loop is executed as long as there are any software interrupts pending.

they are executed, using the function from Section 16.1.3.

if the process is marked as needing to be rescheduled, the CPU is yielded.

395 control comes here only when there are no further software interrupts to run. The process is set up for sleeping interruptibly (long term), using the macro from Section 4.6.1.3.

16.1.4.3 Waking up the software interrupt kernel thread

When the load of software interrupts becomes too great to handle in interrupt context, do_softirq() wakes up the softirqd kernel thread to handle them. The function to do this is shown in Figure 16.8, from kernel/softirq. c.

53 static inline void wakeup_softirqd(unsigned cpu)

55 struct task_struct * tsk = ksoftirqd_task(cpu);

57 if(tsk&&tsk->state ! = TASK_RUNNING)

58 wake_up_process(tsk);

Figure 16.8 Waking up the softirqd kernel thread

55 this macro, from Section 16.5.1.2, returns a pointer to the task_struct of the softirqd kernel thread corresponding to cpu.

57-58 if there is such a thread, and it is not currently runable, it is woken up, using the function from Section 4.7.5.

Continue reading here: Raising a software interrupt

Was this article helpful?

0 0