/* waitQ.c * * This fourth version sets up an array of 3 event queues at module init. * * 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 #define MAX_QUEUES 3 #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 */ #include /* for the kmalloc and GFP_KERNEL constant */ /* The kernel will fill in the call table at dynamic load (insmod) */ extern void *sys_call_table[]; /* prototypes */ asmlinkage int my_eventSig_syscall(int eventId); asmlinkage int my_eventWait_syscall(int eventId); int init_module(void); void cleanup_module(void); /* Wait Queues */ /* Here's a simple declaration, but no initialization. * Might be able to initialize in init_module by calling * init_waitqueue_head defined in wait.h * I haven't tried this, but I could go back to experiment 3 * and try it. * Note: If you try it, then note that you have to kmalloc * the space for the wait_queue_head before calling * init_waitqueue_head. See below. */ /* wait_queue_head_t my_waitQ; */ /* Here's the way to declare one wait queue variable * and statically initialize it. The macro is in * This worked fine in experiment 3. static DECLARE_WAIT_QUEUE_HEAD(my_waitQ); */ /* In this program I'll declare an array of event descriptors containing * wait queues. * I still gotta fill this puppy in and static init macros don't * seem the way to go. See code below in init_module for run time init. * Note that you have to kamalloc the space for the wait_queue_head * before calling init_waitqueue_head from module_init. */ struct event_descr { int eventId; wait_queue_head_t *waitQ; }; typedef struct event_descr event_descr_t; static event_descr_t events[MAX_QUEUES]; /* My event wait system call */ asmlinkage int my_eventWait_syscall(int eventId) { int i; printk(KERN_ALERT "my_eventWait_syscall: entering.\n"); /* search for the event and sleep on the waitQ if found */ for (i = 0; i < MAX_QUEUES; i++) { if (events[i].eventId == eventId) { sleep_on(events[i].waitQ); printk(KERN_ALERT "my_eventWait_syscall: leaving after waking up from block.\n"); return 0; } } printk(KERN_ALERT "my_eventWait_syscall: leaving - event not found.\n"); return -1; } /* My event signal system call */ asmlinkage int my_eventSig_syscall(int eventId) { int i; printk(KERN_ALERT "my_eventSig_syscall: entering.\n"); /* search for the event; if found then wake_up releases all waitQ tasks */ for (i = 0; i < MAX_QUEUES; i++) { if (events[i].eventId == eventId) { wake_up(events[i].waitQ); printk(KERN_ALERT "my_eventSig_syscall: leaving after waking up tasks.\n"); return 0; } } printk(KERN_ALERT "my_eventSig_syscall: leaving - event not found.\n"); return -1; } /* 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) { int i; printk(KERN_ALERT "waitQ 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 event queue table of event descriptors. * In ths example I fill the whole thing up. Event Ids start * at 1 because 0 is reserved for empty-slot. */ for (i = 0; i < MAX_QUEUES; i++) { /* Got a null pointer error here - on the waitQ reference */ /* That means init_waitqueue_head doesn't fill in the reference. */ /* Let's try a kmalloc, maybe init_waitqueue_head expects the space */ /* Need to look at kmalloc definition in mm/slab.c for arguments */ /* I wish I didn't have to do a kmalloc. Is there a static way? */ /* Whew, it works */ events[i].eventId = i+1; events[i].waitQ = (wait_queue_head_t *) kmalloc(sizeof(wait_queue_head_t), GFP_KERNEL); init_waitqueue_head(events[i].waitQ); } /* A non-zero return means init_module failed; module can't be loaded */ return 0; } /* Restore the previous 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 "waitQ module unloaded\n"); } MODULE_LICENSE("GPL");