Name: Synchronize Kernel Author: Rusty Russell Section: Misc Status: Untested D: This patch implements the sync_kernel() call needed for safe module D: unload and hotplug CPU. diff -urN -I \$.*\$ --exclude TAGS -X /home/rusty/current-dontdiff --minimal linux-2.5.4-pre6/include/linux/sched.h working-2.5.4-pre6-hotplug/include/linux/sched.h --- linux-2.5.4-pre6/include/linux/sched.h Mon Feb 11 15:48:18 2002 +++ working-2.5.4-pre6-hotplug/include/linux/sched.h Mon Feb 11 15:53:29 2002 @@ -93,6 +93,7 @@ #define TASK_ZOMBIE 4 #define TASK_STOPPED 8 #define PREEMPT_ACTIVE 0x4000000 +#define PREEMPT_SYNCKERN 0x2000000 #define __set_task_state(tsk, state_value) \ do { (tsk)->state = (state_value); } while (0) @@ -447,6 +448,7 @@ extern void set_user_nice(task_t *p, long nice); asmlinkage long sys_sched_yield(void); #define yield() sys_sched_yield() +extern void sync_kernel(void); /* * The default (Linux) execution domain. diff -urN -I \$.*\$ --exclude TAGS -X /home/rusty/devel/kernel/kernel-patches/current-dontdiff --minimal linux-2.5.4-pre6/kernel/sched.c working-2.5.4-pre6-synckern/kernel/sched.c --- linux-2.5.4-pre6/kernel/sched.c Thu Oct 3 08:14:55 2002 +++ working-2.5.4-pre6-synckern/kernel/sched.c Mon Feb 11 14:57:40 2002 @@ -641,6 +641,58 @@ spin_unlock(&rq->lock); } +static DECLARE_MUTEX(sync_mutex); +static atomic_t sync_kern_count = ATOMIC_INIT(0); +static struct completion sync_kern_complete; + +/* Mark running or preempted stuff sync, and return number set */ +static unsigned int mark_sync(list_t *q, unsigned int cpu) +{ + unsigned int marked = 0; + struct list_head *i; + + list_for_each(i, q) { + task_t *t; + + t = list_entry(i, task_t, run_list); + if ((t->thread_info->preempt_count & PREEMPT_ACTIVE) + || t == runqueues[cpu].curr) { + t->thread_info->preempt_count += PREEMPT_SYNCKERN; + marked++; + } + } + return marked; +} + +void sync_kernel(void) +{ + unsigned int i, q, marked; + + down(&sync_mutex); + BUG_ON(atomic_read(&sync_kern_count) != 0); + init_completion(&sync_kern_complete); + + /* lock all runqueues */ + local_irq_disable(); + for (i = 0; i < NR_CPUS; i++) spin_lock(&runqueues[i].lock); + + marked = 0; + for (i = 0; i < NR_CPUS; i++) { + for (q = 0; q < MAX_PRIO; q++) { + marked += mark_sync(cpu_rq(i)->active->queue + q, i); + marked += mark_sync(cpu_rq(i)->expired->queue + q, i); + } + } + atomic_set(&sync_kern_count, marked); + + /* unlock all runqueues */ + for (i = 0; i < NR_CPUS; i++) spin_unlock(&runqueues[i].lock); + local_irq_enable(); + + /* Now wait for them all to schedule */ + wait_for_completion(&sync_kern_complete); +} + void scheduling_functions_start_here(void) { } /* @@ -656,6 +708,15 @@ if (unlikely(in_interrupt())) BUG(); + + /* If sync_kernel is waiting, and this is a voluntary + schedule, see if we're the last */ + if (unlikely((preempt_get_count() & (PREEMPT_ACTIVE|PREEMPT_SYNCKERN)) + == PREEMPT_SYNCKERN) { + current_thread_info()->preempt_count -= PREEMPT_SYNCKERN; + if (atomic_dec_and_test(&sync_kern_count)) + complete(&sync_kern_complete); + } preempt_disable(); prev = current; @@ -1355,7 +1417,7 @@ spin_unlock(&rq2->lock); } -void __init init_idle(task_t *idle, int cpu) +void __devinit init_idle(task_t *idle, int cpu) { runqueue_t *idle_rq = cpu_rq(cpu), *rq = idle->array->rq; unsigned long flags;