Here is a list of many of the most common supporting functions available to the device driver writer. If you find other supporting functions that are useful, please point them out to me. I know this is not a complete list, but I hope it is a helpful one.
This is a static function in ll_rw_block.c, and cannot be called by other code. However, an understanding of this function, as well as an understanding of ll_rw_block(), may help you understand the strategy routine.
If the device that the request is for has an empty request queue, the request is put on the queue and the strategy routine is called. Otherwise, the proper place in the queue is chosen and the request is inserted in the queue, maintaining proper order by insertion sort.
Proper order (the elevator algorithm) is defined as:
Defined in: drivers/block/ll_rw_block.c
See also: make_request(), ll_rw_block().
Installs the timer structures in the list timer in the timer list.
The timer_list structure is defined by:
struct timer_list { struct timer_list *next; struct timer_list *prev; unsigned long expires; unsigned long data; void (*function)(unsigned long); };
In order to call add_timer(), you need to allocate a timer_list structure, and then call init_timer(), passing it a pointer to your timer_list. It will nullify the next and prev elements, which is the correct initialization. If necessary, you can allocate multiple timer_list structures, and link them into a list. Do make sure that you properly initialize all the unused pointers to NULL, or the timer code may get very confused.
For each struct in your list, you set three variables:
Having created this list, you give a pointer to the first (usually the only) element of the list as the argument to add_timer(). Having passed that pointer, keep a copy of the pointer handy, because you will need to use it to modify the elements of the list (to set a new timeout when you need a function called again, to change the function to be called, or to change the data that is passed to the function) and to delete the timer, if necessary.
Note: This is not process-specific. Therefore, if you want to wake a certain process at a timeout, you will have to use the sleep and wake primitives. The functions that you install through this mechanism will run in the same context that interrupt handlers run in.
Defined in: kernel/sched.c
See also: timer_table in include/linux/timer.h, init_timer(), del_timer().
Prevents interrupts from being acknowledged. cli stands for ``CLear Interrupt enable''.
See also: sti()
Deletes the timer structures in the list timer in the timer list.
The timer list that you delete must be the address of a timer list you have earlier installed with add_timer(). Once you have called del_timer() to delete the timer from the kernel timer list, you may deallocate the memory used in the timer_list structures, as it is no longer referenced by the kernel timer list.
Defined in: kernel/sched.c
See also: timer_table in include/linux/timer.h, init_timer(), add_timer().
Called when a request has been satisfied or aborted. Takes one argument:
If the request was satisfied (uptodate != 0), end_request() maintains the request list, unlocks the buffer, and may arrange for the scheduler to be run at the next convenient time (need_resched = 1; this is implicit in wake_up(), and is not explicitly part of end_request()), before waking up all processes sleeping on the wait_for_request event, which is slept on in make_request(), ll_rw_page(), and ll_rw_swap_file().
Note: This function is a static function, defined in drivers/block/blk.h for every non-SCSI device that includes blk.h. (SCSI devices do this differently; the high-level SCSI code itself provides this functionality to the low-level device-specific SCSI device drivers.) It includes several defines dependent on static device information, such as the device number. This is marginally faster than a more generic normal C function.
Defined in: kernel/blk_drv/blk.h
See also: ll_rw_block(), add_request(), make_request().
Frees an irq previously aquired with request_irq() or irqaction(). Takes one argument:
Defined in: kernel/irq.c
See also: request_irq(), irqaction().
Allows a driver to access data in user space, which is in a different segment than the kernel. Derives the type of the argument and the return type automatically. This means that you have to use types correctly. Shoddy typing will simply fail to work.
Note: these functions may cause implicit I/O, if the memory being accessed has been swapped out, and therefore pre-emption may occur at this point. Do not include these functions in critical sections of your code even if the critical sections are protected by cli()/sti() pairs, because that implicit I/O will violate the integrity of your cli()/sti() pair. If you need to get at user-space memory, copy it to kernel-space memory before you enter your critical section.
These functions take one argument:
Defined in: include/asm/segment.h
See also: memcpy_*fs(), put_user(), cli(), sti().
Reads a byte from a port. inb() goes as fast as it can, while inb_p() pauses before returning. Some devices are happier if you don't read from them as fast as possible. Both functions take one argument:
Defined in: include/asm/io.h
See also: outb(), outb_p().
Inline function for initializing timer_list structures for use with add_timer().
Defined in: include/linux/timer.h
See also: add_timer().
Hardware interrupts are really a lot like signals. Therefore, it makes sense to be able to register an interrupt like a signal. The sa_restorer() field of the struct sigaction is not used, but otherwise it is the same. The int argument to the sa.handler() function may mean different things, depending on whether or not the IRQ is installed with the SA_INTERRUPT flag. If it is not installed with the SA_INTERRUPT flag, then the argument passed to the handler is a pointer to a register structure, and if it is installed with the SA_INTERRUPT flag, then the argument passed is the number of the IRQ. For an example of handler set to use the SA_INTERRUPT flag, look at how rs_interrupt() is installed in drivers/char/serial.c
The SA_INTERRUPT flag is used to determine whether or not the interrupt should be a ``fast'' interrupt. Normally, upon return from the interrupt, need_resched, a global flag, is checked. If it is set (!= 0), then schedule() is run, which may schedule another process to run. They are also run with all other interrupts still enabled. However, by setting the sigaction structure member sa_flags to SA_INTERRUPT, ``fast'' interrupts are chosen, which leave out some processing, and very specifically do not call schedule().
irqaction() takes two arguments:
Defined in: kernel/irq.c
See also: request_irq(), free_irq()
These five test to see if the inode is on a filesystem mounted the corresponding flag.
Free memory previously allocated with kmalloc(). There are two possible arguments:
[kfree_s() may be obsolete now.]
Defined in: mm/kmalloc.c, include/linux/malloc.h
See also: kmalloc().
kmalloc() used to be limited to 4096 bytes. It is now limited to 131056 bytes ((32*4096)-16) on Linux/Intel, and twice that on platforms such as Alpha with 8Kb pages. Buckets, which used to be all exact powers of 2, are now a power of 2 minus some small number, except for numbers less than or equal to 128. For more details, see the implementation in mm/kmalloc.c.
kmalloc() takes two arguments:
No device driver will ever call this code: it is called only through the buffer cache. However, an understanding of this function may help you understand the function of the strategy routine.
After sanity checking, if there are no pending requests on the device's request queue, ll_rw_block() ``plugs'' the queue so that the requests don't go out until all the requests are in the queue, sorted by the elevator algorithm. make_request() is then called for each request. If the queue had to be plugged, then the strategy routine for that device is not active, and it is called, with interrupts disabled. It is the responsibility of the strategy routine to re-enable interrupts.
Defined in: devices/block/ll_rw_block.c
See also: make_request(), add_request().
This takes a 16 bit device number and gives the associated major number by shifting off the minor number.
See also: MINOR().
This is a static function in ll_rw_block.c, and cannot be called by other code. However, an understanding of this function, as well as an understanding of ll_rw_block(), may help you understand the strategy routine.
make_request() first checks to see if the request is readahead or writeahead and the buffer is locked. If so, it simply ignores the request and returns. Otherwise, it locks the buffer and, except for SCSI devices, checks to make sure that write requests don't fill the queue, as read requests should take precedence.
If no spaces are available in the queue, and the request is neither readahead nor writeahead, make_request() sleeps on the event wait_for_request, and tries again when woken. When a space in the queue is found, the request information is filled in and add_request() is called to actually add the request to the queue. Defined in: devices/block/ll_rw_block.c
See also: add_request(), ll_rw_block().
This takes a 16 bit device number and gives the associated minor number by masking off the major number.
See also: MAJOR().
Copies memory between user space and kernel space in chunks larger than one byte, word, or long. Be very careful to get the order of the arguments right!
Note: these functions may cause implicit I/O, if the memory being accessed has been swapped out, and therefore pre-emption may occur at this point. Do not include these functions in critical sections of your code, even if the critical sections are protected by cli()/sti() pairs, because implicit I/O will violate the cli() protection. If you need to get at user-space memory, copy it to kernel-space memory before you enter your critical section.
These functions take three arguments:
Defined in: include/asm/segment.h
See also: get_user(), put_user(), cli(), sti().
Writes a byte to a port. outb() goes as fast as it can, while outb_p() pauses before returning. Some devices are happier if you don't write to them as fast as possible. Both functions take two arguments:
Defined in: include/asm/io.h
See also: inb(), inb_p().
printk() is a version of printf() for the kernel, with some restrictions. It cannot handle floats, and has a few other limitations, which are documented in kernel/vsprintf.c. It takes a variable number of arguments:
Note: printk() may cause implicit I/O, if the memory being accessed has been swapped out, and therefore pre-emption may occur at this point. Also, printk() will set the interrupt enable flag, so never use it in code protected by cli(). Because it causes I/O, it is not safe to use in protected code anyway, even it if didn't set the interrupt enable flag.
Defined in: kernel/printk.c.
Allows a driver to write data in user space, which is in a different segment than the kernel. Derives the type of the arguments and the storage size automatically. This means that you have to use types correctly. Shoddy typing will simply fail to work.
Note: these functions may cause implicit I/O, if the memory being accessed has been swapped out, and therefore pre-emption may occur at this point. Do not include these functions in critical sections of your code even if the critical sections are protected by cli()/sti() pairs, because that implicit I/O will violate the integrity of your cli()/sti() pair. If you need to get at user-space memory, copy it to kernel-space memory before you enter your critical section.
These functions take two arguments:
Defined in: asm/segment.h
See also: memcpy_*fs(), get_user(), cli(), sti().
Registers a device with the kernel, letting the kernel check to make sure that no other driver has already grabbed the same major number. Takes three arguments:
Defined in: fs/devices.c
See also: unregister_*dev()
Request an IRQ from the kernel, and install an IRQ interrupt handler if successful. Takes four arguments:
Defined in: kernel/irq.c
See also: free_irq(), irqaction().
Add a process to the proper select_wait queue. This function takes two arguments:
Defined in: linux/sched.h
See also: *sleep_on(), wake_up*()
Sleep on an event, putting a wait_queue entry in the list so that the process can be woken on that event. sleep_on() goes into an uninteruptible sleep: The only way the process can run is to be woken by wake_up(). interruptible_sleep_on() goes into an interruptible sleep that can be woken by signals and process timeouts will cause the process to wake up. A call to wake_up_interruptible() is necessary to wake up the process and allow it to continue running where it left off. Both take one argument:
Defined in: kernel/sched.c
See also: select_wait(), wake_up*().
Allows interrupts to be acknowledged. sti stands for ``SeT Interrupt enable''.
Defined in: asm/system.h
See also: cli().
These system calls may be used to get the information described in the table below, or the information can be extracted directly from the process table, like this:
foo = current->pid;
pid | Process ID |
uid | User ID |
gid | Group ID |
euid | Effective user ID |
egid | Effective group ID |
ppid | Process ID of process' parent process |
pgid | Group ID of process' parent process |
The system calls should not be used because they are slower and take more space. Because of this, they are no longer exported as symbols throughout the whole kernel.
Defined in: kernel/sched.c
Removes the registration for a device device with the kernel, letting the kernel give the major number to some other device. Takes two arguments:
Defined in: fs/devices.c
See also: register_*dev()
Wakes up a process that has been put to sleep by the matching *sleep_on() function. wake_up() can be used to wake up tasks in a queue where the tasks may be in a TASK_INTERRUPTIBLE or TASK_UNINTERRUPTIBLE state, while wake_up_interruptible() will only wake up tasks in a TASK_INTERRUPTIBLE state, and will be insignificantly faster than wake_up() on queues that have only interruptible tasks. These take one argument:
Note that wake_up() does not switch tasks, it only makes processes that are woken up runnable, so that the next time schedule() is called, they will be candidates to run.
Defined in: kernel/sched.c
See also: select_wait(), *sleep_on()
Copyright (C) 1992, 1993, 1994, 1996 Michael K. Johnson, [email protected].