====== Useful Kernel Functions ======
Descriptions of kernel functions you may be unfamiliar with and may find helpful
in implementing your lab. Implementation files are given as a helpful pointer
for authoritative information on the workings of the functions, their locations are
given relative to a linux kernel source tree, e.g. ''/usr/src/linux/''.
You may also find the [[http://lxr.linux.no/|Linux Cross Reference]] a helpful reference
for Linux types and functions (search: [[http://lxr.linux.no/ident|Linux Cross Reference identifier search]]).
1. Mutex locking and unlocking. We provide our own version of Linux's ''spin_lock'' mutex type, because on the lab machines, Linux's implementation won't help you find bugs. But for Linux's implementation, see ''include/linux/spinlock.h''.
| ''void osp_spin_lock_init(osp_spinlock_t *mutex);'' | Initialize a mutex |
| ''void osp_spin_lock(osp_spinlock_t *lock);'' | Acquire a mutex (lock the mutex) |
| ''void osp_spin_unlock(osp_spinlock_t *lock);'' | Release (unlock) the mutex |
**Do not** call a scheduling function, such as ''schedule()'', while holding a spin lock! That is a recipe for disaster: your entire kernel might deadlock.
2. Signals, implementation in ''include/linux/sched.h'' and ''kernel/signal.c''.
| ''int signal_pending(struct task_struct *process);'' | Returns boolean indicating whether the given process has any pending signals. You will probably pass ''current'' for the ''process'' argument; ''current'' is Linux's name for the current process structure.|
| ''int send_sig(int sig, struct task_struct *p, int priv);'' | Send a signal ''sig'' to the process specified by ''p'' (the linux kernel has a ''task_struct'' for each process), we'll set ''priv'' to 0. Returns < 0 on error. |
3. Wait queues, implementation in ''kernel/wait.c'' and ''include/linux/wait.h''. These functions change a process's state from ''TASK_RUNNABLE'' to a blocked state, and allow other processes or kernel code to wake up blocked processes.
| ''DEFINE_WAIT(wait);'' \\ ''wait.func = &default_wake_function;'' | Defines a ''wait_queue_t'' object named ''wait''. This object will be passed to ''prepare_to_wait'' and ''finish_wait''. |
| ''void prepare_to_wait_exclusive(wait_queue_head_t *q, wait_queue_t *wait, int state);'' | Add the current task to the wait queue, using the named ''wait_queue_t'' object. The ''state'' member is the process's new state. Use ''TASK_INTERRUPTIBLE'', which is a type of blocking that can be interrupted by signals. This function only adds the task to the queue; a later call to ''schedule'' will actually block. This allows you to avoid sleep/wakeup races: first, add the task to the wait queue; second, release any mutexes; and third, call ''schedule''. \\ \\ You may need to call ''prepare_to_wait_exclusive'' multiple times (if a thread wakes up, but needs to block again). The ''default_wake_function'' assignment, above, means the process retains its original position on the queue until it calls ''finish_wait''. \\ \\ The ''prepare_to_wait_exclusive'' function marks the current process as an "exclusive" waiter. The ''prepare_to_wait'' function marks the current process as a "nonexclusive" waiter. The ''wake_up'' function wakes up all non-exclusive waiters, plus the first exclusive waiter; ''wake_up_all'' wakes up all waiters, exclusive or not. |
| ''void finish_wait(wait_queue_head_t *q, wait_queue_t *wait);'' | Remove the current task from the wait queue. |
| ''void wake_up_all(wait_queue_head_t *q);'' | Wake up //all// tasks in the wait queue by setting their states to ''TASK_RUNNABLE''. |
| ''void wake_up(wait_queue_head_t *q);'' | Wake up the //first sleeping exclusive// task in the wait queue by setting its state to ''TASK_RUNNABLE''. Also wakes up all non-exclusive tasks (''prepare_to_wait''). Skips over any //awake// exclusive tasks. |
| ''int waitqueue_active(wait_queue_head_t *q);'' | Returns 1 if the wait queue has a process on it, 0 if the wait queue is empty. |
4. CPU releasing/scheduling, implementation in ''kernel/timer.c''.
| ''void schedule(void);'' | Release control of the CPU. If the current task's state is ''TASK_RUNNABLE'', then the task might run again soon. If it is ''TASK_INTERRUPTIBLE'', then the task will block until it is woken up by a signal or by ''wake_up_all''. |
| ''long schedule_timeout(long timeout);'' | Release control of the CPU for at most ''timeout'' time, which is measured in "jiffies". (There are ''HZ'' jiffies per second; often ''HZ'' is 1000.) Returns the amount of time remaining until timeout is returned, 0 is returned if the call timed out. |
5. Block device requests, implementation in ''include/linux/blkdev.h''.
| ''unsigned int rq_data_dir(struct request *req);'' | Returns the value indicating the given request's type, 0 for reads and 1 for writes. |
6. File flags.
Your ''osprd_open'' and ''osprd_release'' functions will need to check whether your device file was opened for reading or writing. Given a ''struct file *filp'' variable:
| ''(filp->f_mode & FMODE_WRITE)'' | This expression is true (non-zero) if the file was opened for writing (''O_WRONLY'' or ''O_RDWR''). |
If you'd like to check other file flags specified at open time, such as ''O_EXCL'', for a design problem or something, do so like this:
| ''(filp->f_flags & O_EXCL)'' | This expression is true (non-zero) if the file was opened with the ''O_EXCL'' flag. |
7. Helper functions for Lab 2.
| ''osprd_device_t *file2osprd(struct file *filp);'' | Linux's ''struct file'' object type corresponds to open files. This function takes an open file and returns the corresponding OSP ramdisk structure, ''osprd_device_t''. This type is defined at the top of ''osprd.c''. If ''filp'' is not actually an OSP ramdisk file, returns NULL. |
| ''void for_each_open_file(struct task_struct *task, void (*hook)(struct file *filp, osprd_info_t *d), osprd_info_t *d);'' | Calls the ''hook'' function for each of ''task'''s open files. The function is called like ''hook(filp, d)'', where ''filp'' is the open file and ''d'' is the 3rd argument to ''for_each_open_file''. You will probably pass ''current'' for the ''task'' argument; ''current'' is Linux's name for the current process structure. |
The following example shows how to use ''**for_each_open_file**'' to count the number of ramdisk files that the current process, ''current'', has open.
typedef struct osprd_info {
// ...
int num_ramdisks_open; // we add this variable for use by for_each_file
} osprd_info_t;
void count_ramdisks_hook(struct file *filp, osprd_info_t *d) {
// This hook function will be passed to for_each_file.
if (file2osprd(filp) != NULL)
d->num_ramdisks_open++;
}
// ... somewhere else in the code, for example in osprd_open:
d->num_ramdisks_open = 0;
for_each_open_file(current, count_ramdisks_hook, d);
eprintk("Number of ramdisks = %d\n", d->num_ramdisks_open);
===== Lab 3 functions =====
Lab 3 can use a couple other functions and data structures, particularly for quotas. Here's a list.
* Given a file pointer ''filp'', you can get the file's corresponding Linux directory cache entry as ''filp->f_dentry''.
* Given a Linux directory cache entry for a file ''struct dentry *//dentry//'', you can get the inode number of the directory containing that file with ''dentry->d_parent->d_inode->i_ino''.
* Linux's version of the standard ''strtol'' function is ''simple_strtol''. For example:
long l;
char *xxx;
l = simple_strtol("100", &x, 10);
// At this point, l == 100.