Table of Contents
The most typical case is when the processor is spending time in user tasks.
If not, it might be running one of the following types of kernel code:
- System call - when the user task or kernel thread requests some service
from the kernel, it does a trap to privileged mode and some kernel code
is doing the requested service. System call handlers usually return an
integer, by convention if the return value is a small negative integer (-1
.. -515), it means the system call is returning an error (absolute value of
it should be one of the constants from
errno.h). Other values mean
successful system call completion. If you return an error, application will
usually get a positive error code in
errno and the system call will
return -1 to the application.
- Exception handling - when some instruction in userland or kernel raises
some exception, the kernel has to handle it as well. Unlike system call,
this action is not requested by the user directly. The most typical member
of this category is page table miss (or TLB miss on 64bit machines). Most of
these exception handlers reside in platform dependant files, some of them
then call some generic handling (like handle_mm_fault in our case).
- Kernel thread - some special processes execute in kernel space, and use
standard trap method of invoking system calls when necessary. When
booting, kernel spawns several kernel threads, some of them then do
execve system call by which they loose the kernel thread state and
become normal processes, which is the case of e.g. init.
- Interrupt - when some hardware requests some action, it sends an
interrupt to the kernel, which in turn calls some interrupt handler, if
someone registered it. This interrupt handler should be fast, so that it
does not lock the system for too long - interrupt handlers are usually
executed with disabled interrupts on local CPU.
Interrupts execute in context of the task which has been current on the IRQ
servicing CPU at the time when the interrupt came, so there is no interrupt
thread or something like that. Each thread has kernel space mapped as part
of it's address space, so unless you access user space (you should never try
that), it does not matter in which task context are you executing.
Every interrupt has an assigned interrupt number which you use e.g. in calls
disable_irq() (to disable that particular
interrupt). The exact interpretation of this number is platform dependent
and device driver writer should not assume anything about its value, it
should be just a 32bit integer with unknown value for it. Never use this
value to index into static arrays, it might work on one platform, but break
on another one. Some architectures have different interrupt numbers just for
the different interrupt levels, some encode board and slot numbers into it.
Like that, on some platform a
disable_irq() can disable just one
interrupt level from a certain card on a certain bus, while on other
platform, where the IRQ handling is not advanced that much yet, it just
disables a certain interrupt level on all CPUs.
- Bottom half handler - so that you don't block interrupts on local CPU
for too long, you can do some part of your interrupt handling in the
bottom half handler. Normally, in your short fast interrupt handler,
you call mark_bh() if you need some longer processing. Then, when your
interrupt handler is done, the system checks for pending bottom half
handlers. If it finds some pending, it enables interrupts and executes them.
Normally, to use a bottom half handler from within your interrupt handler,
you have to allocate a new bottom half handler type in
<linux/interrupt.h>, in your initialization register your
bottom half handler with its type (
init_bh()) and then in your
interrupt actually use it (
mark_bh()). Now, this is probably not a good
solution, as there are only 32 bottom half handlers available for
registering, half of them in use by other parts of the kernel already.
Another, and much nicer way how to run your bottom half handlers and not
waste precious global bottom half types comes in
<linux/tqueue.h> as task queues. This has nothing to do
with tasks as execution entities, here task means a function with some
arbitrary argument which you can schedule for later execution. Generally,
you fill in a
struct tq_struct structure, where
routine will be
your handler and
data its argument and then you
queue_task it onto
some task queue. This may be your custom task queue (then you have to
run_task_queue some time later on it), or it may be one of the
predefined global task queues. One of these is
tq_immediate, which is
run in the
IMMEDIATE_BH bottom half handler, so you can run your custom
bottom half handlers by doing
Table of Contents