Hi Rusty,
Here is an attempt at a possible version of synchronize_kernel() that
should work on a preemptible kernel.  I haven't tested it yet.
static int sync_count = 0;
static struct task_struct *syncing_task = NULL;
static DECLARE_MUTEX(synchronize_kernel_mtx);
void
synchronize_kernel()
{
	struct list_head *tmp;
	struct task_struct *p;
	/* Guard against multiple calls to this function */
	down(&synchronize_kernel_mtx);
	/* Mark all tasks on the runqueue */
	spin_lock_irq(&runqueue_lock);
	list_for_each(tmp, &runqueue_head) {
		p = list_entry(tmp, struct task_struct, run_list);
		if (p == current)
			continue;
		if (p->state == TASK_RUNNING ||
				(p->state == (TASK_RUNNING|TASK_PREEMPTED))) {
			p->flags |= PF_SYNCING;
			sync_count++;
		}
	}
	if (sync_count == 0)
		goto out;
	syncing_task = current;
	spin_unlock_irq(&runqueue_lock);
	/*
	 * Cause a schedule on every CPU, as for a non-preemptible
	 * kernel
	 */
	/* Save current state */
	cpus_allowed = current->cpus_allowed;
	policy = current->policy;
	rt_priority = current->rt_priority;
	/* Create an unreal time task. */
	current->policy = SCHED_FIFO;
	current->rt_priority = 1001 + sys_sched_get_priority_max(SCHED_FIFO);
	/* Make us schedulable on all CPUs. */
	current->cpus_allowed = (1UL<<smp_num_cpus)-1;
	/* Eliminate current cpu, reschedule */
	while ((current->cpus_allowed &= ~(1 << smp_processor_id())) != 0)
		schedule();
	/* Back to normal. */
	current->cpus_allowed = cpus_allowed;
	current->policy = policy;
	current->rt_priority = rt_priority;
	/*
	 * Wait, if necessary, until all preempted tasks
	 * have reached a sync point.
	 */
	spin_lock_irq(&runqueue_lock);
	for (;;) {
		set_current_state(TASK_UNINTERRUPTIBLE);
		if (sync_count == 0)
			break;
		spin_unlock_irq(&runqueue_lock);
		schedule();
		spin_lock_irq(&runqueue_lock);
	}
	current->state = TASK_RUNNING;
	syncing_task =  NULL;
out:
	spin_unlock_irq(&runqueue_lock);
	up(&synchronize_kernel_mtx);
}
And add this code to the beginning of schedule(), just after the
runqueue_lock is taken (the flags field is probably not be the right
place to put the synchronize mark; and the test should be optimized for
the fast path in the same way as the other tests in schedule(), but you
get the idea):
	if ((prev->flags & PF_SYNCING) && !(prev->state & TASK_PREEMPTED)) {
		prev->flags &= ~PF_SYNCING;
		if (--sync_count == 0) {
			syncing_task->state = TASK_RUNNING;
			if (!task_on_runqueue(syncing_task))
				add_to_runqueue(syncing_task);
			syncing_task = NULL;
		}
	}
Nigel Gamble                                    nigel@nrg.org
Mountain View, CA, USA.                         http://www.nrg.org/
MontaVista Software                             nigel@mvista.com
-
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Please read the FAQ at  http://www.tux.org/lkml/