/* waitOne.c * * This version has locking using kernel locking functions. * I use a wait queue, but only for the lock, not for the queue. * * I use irqsave and irqrestore locks because a task switch could * occur inside the locked region. I'm guessing that's the right * way to go. I don't get the flags argument in the locking calls. * * Derived from mod-syscall-242 sample system code illustrating * how to add system calls using a kernel module. * System calls use printk for debugging messages. * System calls here only pass and return simple stack values. * so copy_to_user, copy_from_user are not needed. * * Derived from the no-parameter or result hello-1 example in the * Linux Kernel Module Programmer's Guide for 2.4 Kernel. * * Uses existing unused syscall entries in the sys_call_table. * eventWait is sched_setaffinity 241 * eventSig is sched_getaffinity 242 * * Include file locations * is /usr/src/linux/include/linux * is /usr/src/linux/include/asm * is /usr/include/sys * * Source file locations * sys_call_table[] /usr/src/linux/arch/i386/kernel/entry.S */ /* kernel module programming */ #ifndef MODULE #define MODULE #endif #ifndef LINUX #define LINUX #endif #ifndef __KERNEL__ #define __KERNEL__ #endif #define MAX_BUF 80 #include /* needed by all modules */ #include /* for KERN_ALERT */ #include /* for __NR_futex & other system call numbers */ #include /* current, wake_up_process, schedule, TASK_UNIN and all the locking stuff */ /* The kernel will fill in the call table at dynamic load (insmod) */ extern void *sys_call_table[]; /* prototypes */ asmlinkage int my_eventSig_syscall(void); asmlinkage int my_eventWait_syscall(void); int init_module(void); void cleanup_module(void); /* Wait Queue data structures * Right now I just have zero or one waiting tasks. * I'm using the wait queue because then I can do * locking just like in sleep_on and wake_up. * I'm just going to use the wait queue lock, not the queue. */ struct task_struct *my_eventWait_task = NULL; wait_queue_head_t my_waitQ; /* My event signal system call */ asmlinkage int my_eventSig_syscall(void) { int retval; unsigned long flags; /* for write locks with irq save and restore */ printk(KERN_ALERT "my_eventSig_syscall: entering.\n"); /* Write lock */ wq_write_lock_irqsave((&my_waitQ)->lock, flags); /* why not just my_waitQ.lock ?? */ retval = 1; if (my_eventWait_task) { printk(KERN_ALERT "my_eventSig_syscall: waking up One task.\n"); wake_up_process(my_eventWait_task); my_eventWait_task = NULL; retval = 0; } /* Write unlock */ wq_write_unlock_irqrestore((&my_waitQ)->lock, flags); if (retval) printk(KERN_ALERT "my_eventSig_syscall: leaving but no process to wake up.\n"); else printk(KERN_ALERT "my_eventSig_syscall: leaving after waking up a process.\n"); return retval; } /* My event wait system call */ asmlinkage int my_eventWait_syscall(void) { int retval; unsigned long flags; /* for write locks with irq save and restore */ printk(KERN_ALERT "my_eventWait_syscall: entering.\n"); /* Write lock */ wq_write_lock_irqsave((&my_waitQ)->lock, flags); retval = 1; if (!my_eventWait_task) { my_eventWait_task = current; printk(KERN_ALERT "my_eventWait: One task going to sleep.\n"); current->state = TASK_UNINTERRUPTIBLE; /* should use set_current_state */ schedule(); retval = 0; } /* Write unlock */ wq_write_unlock_irqrestore((&my_waitQ)->lock, flags); if (retval) printk(KERN_ALERT "my_eventWait_syscall: leaving without blocking wait.\n"); else printk(KERN_ALERT "my_eventWait_syscall: leaving after waking up from block.\n"); return retval; } /* Original system call for NR_sched_setaffinity sys.c of src/linux/kernel */ asmlinkage long (*original_call_241) (void); /* Original system call for NR_sched_getaffinity sys.c of src/linux/kernel */ asmlinkage long (*original_call_242) (void); /* Initialize the module at insmod - insert the new system calls */ int init_module(void) { printk(KERN_ALERT "waitOne module loaded\n"); /* save the original syscall entry and insert our own entry */ original_call_241 = sys_call_table[__NR_sched_setaffinity]; original_call_242 = sys_call_table[__NR_sched_getaffinity]; sys_call_table[__NR_sched_setaffinity] = my_eventWait_syscall; sys_call_table[__NR_sched_getaffinity] = my_eventSig_syscall; /* Initialize the wait queue data structures */ my_eventWait_task = NULL; /* A non-zero return means init_module failed; module can't be loaded */ return 0; } /* Restore the preious system calls at rmmod. * Check to see if someone else tried to replace our * system calls while we were loaded. */ void cleanup_module(void) { if ( sys_call_table[__NR_sched_setaffinity] != my_eventWait_syscall || sys_call_table[__NR_sched_getaffinity] != my_eventSig_syscall) { printk(KERN_ALERT "syscall module entry has been corrupted\n"); } /* restore the original syscall entries */ sys_call_table[__NR_sched_setaffinity] = original_call_241; sys_call_table[__NR_sched_getaffinity] = original_call_242; printk(KERN_ALERT "waitOne module unloaded\n"); } MODULE_LICENSE("GPL");