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 Linux Cross Reference a helpful reference for Linux types and functions (search: 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); | Defines a wait_queue_t object named wait. This object will be passed to prepare_to_wait and finish_wait. |
void prepare_to_wait(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. |
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. |
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);