[PATCH] 2.4.0-prerelease: preemptive kernel.

ludovic fernandez
2001-01-04T01:53:02Z

[-- Attachment #1: Type: text/plain, Size: 724 bytes --]

Hello,

For hackers,
The following patch makes the kernel preemptable.
It is against 2.4.0-prerelease on for i386 only.
It should work for UP and SMP even though I
didn't validate it on SMP.
Comments are welcome.

NOTES: since the lock implementation is modified,
you need obviously to re-compile all your modules.
I introduced a dependency between spinlock.h and sched.h
and this has some bad side effects: Some files will
generate warnings during the compilation complaining
that disable_preempt()/enable_preempt() are not defined.
The warnings should be harmless but I was too lazy to
fix all of them. If the compilation fails because of
that, there is big chance to fix things by including
sched.h in the cfile.

Ludo.

[-- Attachment #2: preempt.patch --]
[-- Type: text/plain, Size: 25240 bytes --]

diff -u --recursive linux-2.4-prerelease.org/arch/i386/kernel/apic.c linux-2.4-prerelease/arch/i386/kernel/apic.c
--- linux-2.4-prerelease.org/arch/i386/kernel/apic.c Wed Jan 3 17:19:44 2001
+++ linux-2.4-prerelease/arch/i386/kernel/apic.c Wed Jan 3 12:58:57 2001
@@ -726,6 +726,7 @@
* interrupt lock, which is the WrongThing (tm) to do.
*/
irq_enter(cpu, 0);
+ disable_preempt();
smp_local_timer_interrupt(regs);
irq_exit(cpu, 0);
}
@@ -746,6 +747,8 @@
if (v & (1 << (SPURIOUS_APIC_VECTOR & 0x1f)))
ack_APIC_irq();

+ disable_preempt();
+
/* see sw-dev-man vol 3, chapter 7.4.13.5 */
printk(KERN_INFO "spurious APIC interrupt on CPU#%d, should never happen.\n",
smp_processor_id());
@@ -776,6 +779,9 @@
6: Received illegal vector
7: Illegal register address
*/
+
+ disable_preempt();
+
printk (KERN_ERR "APIC error on CPU%d: %02lx(%02lx)\n",
smp_processor_id(), v , v1);
}
diff -u --recursive linux-2.4-prerelease.org/arch/i386/kernel/entry.S linux-2.4-prerelease/arch/i386/kernel/entry.S
--- linux-2.4-prerelease.org/arch/i386/kernel/entry.S Wed Jan 3 17:19:44 2001
+++ linux-2.4-prerelease/arch/i386/kernel/entry.S Wed Jan 3 12:58:57 2001
@@ -79,6 +79,8 @@
need_resched = 20
tsk_ptrace = 24
processor = 52
+preemptable = 56
+

ENOSYS = 38

@@ -203,6 +205,9 @@
call *SYMBOL_NAME(sys_call_table)(,%eax,4)
movl %eax,EAX(%esp) # save the return value
ENTRY(ret_from_sys_call)
+ movl $1, %edx
+ lock
+ xaddl %edx, preemptable(%ebx)
#ifdef CONFIG_SMP
movl processor(%ebx),%eax
shll $CONFIG_X86_L1_CACHE_SHIFT,%eax
@@ -213,13 +218,22 @@
testl SYMBOL_NAME(irq_stat)+4,%ecx # softirq_mask
#endif
jne handle_softirq
-
+ cmpl $0, %edx # task is preemptable ?
+ jne check_signal
ret_with_reschedule:
cmpl $0,need_resched(%ebx)
jne reschedule
+check_signal:
+#if 0
+ movl EFLAGS(%esp), %eax # mix EFLAGS and CS
+ movb CS(%esp), %al
+ testl $(VM_MASK | 3), %eax # return to user mode ?
+ je restore_all # no bypass signal check
+#endif
cmpl $0,sigpending(%ebx)
jne signal_return
restore_all:
+ decl preemptable(%ebx)
RESTORE_ALL

ALIGN
@@ -270,14 +284,22 @@
#endif
jne handle_softirq

+/*
+ * ret_from_intr is the common path used to return
+ * from interruptions (either hard of soft) and exceptions.
+ * At that point the preemption is disabled
+ * (see do_IRQ and handle_softirq)
+ * Reenable the preemption, verify that the current thread
+ * is preemptable and check for a pending scheduling request.
+ */
ENTRY(ret_from_intr)
GET_CURRENT(%ebx)
- movl EFLAGS(%esp),%eax # mix EFLAGS and CS
- movb CS(%esp),%al
- testl $(VM_MASK | 3),%eax # return to VM86 mode or non-supervisor?
- jne ret_with_reschedule
- jmp restore_all
-
+ cmpl $1, preemptable(%ebx)
+ jne restore_all
+ cmpl $0, state(%ebx) # current task is running ?
+ jne restore_all
+ jmp ret_with_reschedule
+
ALIGN
handle_softirq:
call SYMBOL_NAME(do_softirq)
@@ -286,6 +308,7 @@
ALIGN
reschedule:
call SYMBOL_NAME(schedule) # test
+ decl preemptable(%ebx)
jmp ret_from_sys_call

ENTRY(divide_error)
@@ -316,6 +339,13 @@
movl %edx,%ds
movl %edx,%es
GET_CURRENT(%ebx)
+/*
+ * All exceptions are called with the preemption disabled.
+ * In addition, some of them (page_fault) are not reentrant
+ * and need to be atomic until the preemption can be disabled.
+ */
+ incl preemptable(%ebx)
+ sti
call *%edi
addl $8,%esp
jmp ret_from_exception
@@ -334,6 +364,7 @@
pushl $-1 # mark this as an int
SAVE_ALL
GET_CURRENT(%ebx)
+ incl preemptable(%ebx)
pushl $ret_from_exception
movl %cr0,%eax
testl $0x4,%eax # EM (math emulation bit)
diff -u --recursive linux-2.4-prerelease.org/arch/i386/kernel/irq.c linux-2.4-prerelease/arch/i386/kernel/irq.c
--- linux-2.4-prerelease.org/arch/i386/kernel/irq.c Wed Jan 3 17:19:44 2001
+++ linux-2.4-prerelease/arch/i386/kernel/irq.c Wed Jan 3 12:58:57 2001
@@ -564,6 +564,12 @@
unsigned int status;

kstat.irqs[cpu][irq]++;
+ /*
+ * Disable preemption for the current task.
+ * ret_from_intr will reenable the preemption and
+ * check for a scheduling request.
+ */
+ disable_preempt();
spin_lock(&desc->lock);
desc->handler->ack(irq);
/*
diff -u --recursive linux-2.4-prerelease.org/arch/i386/kernel/smp.c linux-2.4-prerelease/arch/i386/kernel/smp.c
--- linux-2.4-prerelease.org/arch/i386/kernel/smp.c Wed Jan 3 17:19:44 2001
+++ linux-2.4-prerelease/arch/i386/kernel/smp.c Wed Jan 3 12:58:57 2001
@@ -277,6 +277,8 @@
{
unsigned long cpu = smp_processor_id();

+ disable_preempt();
+
if (!test_bit(cpu, &flush_cpumask))
return;
/*
@@ -518,6 +520,7 @@
asmlinkage void smp_reschedule_interrupt(void)
{
ack_APIC_irq();
+ disable_preempt();
}

asmlinkage void smp_call_function_interrupt(void)
@@ -532,6 +535,7 @@
* about to execute the function
*/
atomic_inc(&call_data->started);
+ disable_preempt();
/*
* At this point the info structure may be out of scope unless wait==1
*/
diff -u --recursive linux-2.4-prerelease.org/arch/i386/kernel/traps.c linux-2.4-prerelease/arch/i386/kernel/traps.c
--- linux-2.4-prerelease.org/arch/i386/kernel/traps.c Wed Jan 3 17:19:44 2001
+++ linux-2.4-prerelease/arch/i386/kernel/traps.c Wed Jan 3 12:58:57 2001
@@ -958,7 +958,7 @@
set_trap_gate(11,&segment_not_present);
set_trap_gate(12,&stack_segment);
set_trap_gate(13,&general_protection);
- set_trap_gate(14,&page_fault);
+ set_intr_gate(14,&page_fault);
set_trap_gate(15,&spurious_interrupt_bug);
set_trap_gate(16,&coprocessor_error);
set_trap_gate(17,&alignment_check);
diff -u --recursive linux-2.4-prerelease.org/arch/i386/lib/dec_and_lock.c linux-2.4-prerelease/arch/i386/lib/dec_and_lock.c
--- linux-2.4-prerelease.org/arch/i386/lib/dec_and_lock.c Wed Jan 3 17:19:44 2001
+++ linux-2.4-prerelease/arch/i386/lib/dec_and_lock.c Wed Jan 3 12:58:57 2001
@@ -9,6 +9,7 @@

#include <linux/spinlock.h>
#include <asm/atomic.h>
+#include <linux/sched.h>

int atomic_dec_and_lock(atomic_t *atomic, spinlock_t *lock)
{
diff -u --recursive linux-2.4-prerelease.org/arch/i386/mm/fault.c linux-2.4-prerelease/arch/i386/mm/fault.c
--- linux-2.4-prerelease.org/arch/i386/mm/fault.c Wed Jan 3 17:19:44 2001
+++ linux-2.4-prerelease/arch/i386/mm/fault.c Wed Jan 3 12:58:57 2001
@@ -112,6 +112,8 @@
unsigned long page;
unsigned long fixup;
int write;
+ int ret;
+
siginfo_t info;

/* get the address */
@@ -193,7 +195,17 @@
* make sure we exit gracefully rather than endlessly redo
* the fault.
*/
- switch (handle_mm_fault(mm, vma, address, write)) {
+
+ /*
+ * Re-enable the preemption before calling the generic handler.
+ * This is rather for fun and to validate things a bit since
+ * the mm semaphore is hold at that point and that can cause
+ * a lot of contentions.
+ */
+ enable_preempt();
+ ret = handle_mm_fault(mm, vma, address, write);
+ disable_preempt();
+ switch (ret) {
case 1:
tsk->min_flt++;
break;
diff -u --recursive linux-2.4-prerelease.org/drivers/pcmcia/ds.c linux-2.4-prerelease/drivers/pcmcia/ds.c
--- linux-2.4-prerelease.org/drivers/pcmcia/ds.c Wed Jan 3 17:19:44 2001
+++ linux-2.4-prerelease/drivers/pcmcia/ds.c Wed Jan 3 12:58:57 2001
@@ -880,7 +880,16 @@
int i, ret;

DEBUG(0, "%s\n", version);
-
+#if 1
+ /*
+ * I got some problems with PCMCIA initialization and a
+ * preemptive kernel;
+ * init_pcmcia_ds() beeing called before the completion
+ * of pending scheduled tasks. I don't know if this is the
+ * right fix though.
+ */
+ flush_scheduled_tasks();
+#endif
/*
* Ugly. But we want to wait for the socket threads to have started up.
* We really should let the drivers themselves drive some of this..
diff -u --recursive linux-2.4-prerelease.org/include/asm-i386/smplock.h linux-2.4-prerelease/include/asm-i386/smplock.h
--- linux-2.4-prerelease.org/include/asm-i386/smplock.h Wed Jan 3 17:19:44 2001
+++ linux-2.4-prerelease/include/asm-i386/smplock.h Wed Jan 3 17:29:36 2001
@@ -17,6 +17,7 @@
*/
#define release_kernel_lock(task, cpu) \
do { \
+ disable_preempt(); \
if (task->lock_depth >= 0) \
spin_unlock(&kernel_flag); \
release_irqlock(cpu); \
@@ -30,6 +31,7 @@
do { \
if (task->lock_depth >= 0) \
spin_lock(&kernel_flag); \
+ enable_preempt(); \
} while (0)

@@ -43,6 +45,7 @@
extern __inline__ void lock_kernel(void)
{
#if 1
+ disable_preempt();
if (!++current->lock_depth)
spin_lock(&kernel_flag);
#else
@@ -63,6 +66,7 @@
#if 1
if (--current->lock_depth < 0)
spin_unlock(&kernel_flag);
+ enable_preempt();
#else
__asm__ __volatile__(
"decl %1\n\t"
diff -u --recursive linux-2.4-prerelease.org/include/asm-i386/softirq.h linux-2.4-prerelease/include/asm-i386/softirq.h
--- linux-2.4-prerelease.org/include/asm-i386/softirq.h Wed Jan 3 17:19:44 2001
+++ linux-2.4-prerelease/include/asm-i386/softirq.h Wed Jan 3 14:21:58 2001
@@ -7,8 +7,10 @@
#define cpu_bh_disable(cpu) do { local_bh_count(cpu)++; barrier(); } while (0)
#define cpu_bh_enable(cpu) do { barrier(); local_bh_count(cpu)--; } while (0)

-#define local_bh_disable() cpu_bh_disable(smp_processor_id())
-#define local_bh_enable() cpu_bh_enable(smp_processor_id())
+#define local_bh_disable() \
+do { disable_preempt(); cpu_bh_disable(smp_processor_id()); } while (0)
+#define local_bh_enable() \
+do { cpu_bh_enable(smp_processor_id()); enable_preempt(); } while (0)

#define in_softirq() (local_bh_count(smp_processor_id()) != 0)

diff -u --recursive linux-2.4-prerelease.org/include/asm-i386/spinlock.h linux-2.4-prerelease/include/asm-i386/spinlock.h
--- linux-2.4-prerelease.org/include/asm-i386/spinlock.h Wed Jan 3 17:19:44 2001
+++ linux-2.4-prerelease/include/asm-i386/spinlock.h Wed Jan 3 14:21:58 2001
@@ -65,7 +65,7 @@
#define spin_unlock_string \
"movb $1,%0"

-static inline int spin_trylock(spinlock_t *lock)
+static inline int _spin_trylock(spinlock_t *lock)
{
char oldval;
__asm__ __volatile__(
@@ -75,7 +75,7 @@
return oldval > 0;
}

-static inline void spin_lock(spinlock_t *lock)
+static inline void _spin_lock(spinlock_t *lock)
{
#if SPINLOCK_DEBUG
__label__ here;
@@ -90,7 +90,7 @@
:"=m" (lock->lock) : : "memory");
}

-static inline void spin_unlock(spinlock_t *lock)
+static inline void _spin_unlock(spinlock_t *lock)
{
#if SPINLOCK_DEBUG
if (lock->magic != SPINLOCK_MAGIC)
@@ -143,7 +143,7 @@
*/
/* the spinlock helpers are in arch/i386/kernel/semaphore.c */

-static inline void read_lock(rwlock_t *rw)
+static inline void _read_lock(rwlock_t *rw)
{
#if SPINLOCK_DEBUG
if (rw->magic != RWLOCK_MAGIC)
@@ -152,7 +152,7 @@
__build_read_lock(rw, "__read_lock_failed");
}

-static inline void write_lock(rwlock_t *rw)
+static inline void _write_lock(rwlock_t *rw)
{
#if SPINLOCK_DEBUG
if (rw->magic != RWLOCK_MAGIC)
@@ -161,8 +161,8 @@
__build_write_lock(rw, "__write_lock_failed");
}

-#define read_unlock(rw) asm volatile("lock ; incl %0" :"=m" ((rw)->lock) : : "memory")
-#define write_unlock(rw) asm volatile("lock ; addl $" RW_LOCK_BIAS_STR ",%0":"=m" ((rw)->lock) : : "memory")
+#define _read_unlock(rw) asm volatile("lock ; incl %0" :"=m" ((rw)->lock) : : "memory")
+#define _write_unlock(rw) asm volatile("lock ; addl $" RW_LOCK_BIAS_STR ",%0":"=m" ((rw)->lock) : : "memory")

static inline int write_trylock(rwlock_t *lock)
{
diff -u --recursive linux-2.4-prerelease.org/include/asm-i386/system.h linux-2.4-prerelease/include/asm-i386/system.h
--- linux-2.4-prerelease.org/include/asm-i386/system.h Wed Jan 3 17:19:44 2001
+++ linux-2.4-prerelease/include/asm-i386/system.h Wed Jan 3 14:21:58 2001
@@ -306,6 +306,13 @@
#define local_irq_disable() __cli()
#define local_irq_enable() __sti()

+static inline int local_irq_are_enabled(void)
+{
+ unsigned long flags;
+ __save_flags(flags);
+ return (flags & 0x00000200);
+}
+
#ifdef CONFIG_SMP

extern void __global_cli(void);
diff -u --recursive linux-2.4-prerelease.org/include/linux/sched.h linux-2.4-prerelease/include/linux/sched.h
--- linux-2.4-prerelease.org/include/linux/sched.h Wed Jan 3 17:19:44 2001
+++ linux-2.4-prerelease/include/linux/sched.h Wed Jan 3 15:31:41 2001
@@ -296,6 +296,7 @@
unsigned long policy;
struct mm_struct *mm;
int has_cpu, processor;
+ atomic_t preemptable;
unsigned long cpus_allowed;
/*
* (only the 'next' pointer fits into the cacheline, but
@@ -443,6 +444,7 @@
policy: SCHED_OTHER, \
mm: NULL, \
active_mm: &init_mm, \
+ preemptable: ATOMIC_INIT(0), \
cpus_allowed: -1, \
run_list: LIST_HEAD_INIT(tsk.run_list), \
next_task: &tsk, \
@@ -524,6 +526,7 @@
extern void free_uid(struct user_struct *);

#include <asm/current.h>
+#include <asm/hardirq.h>

extern unsigned long volatile jiffies;
extern unsigned long itimer_ticks;
@@ -634,6 +637,41 @@
{
return (current->sas_ss_size == 0 ? SS_DISABLE
: on_sig_stack(sp) ? SS_ONSTACK : 0);
+}
+
+static inline void disable_preempt(void)
+{
+ atomic_inc(&current->preemptable);
+}
+
+static inline void enable_preempt(void)
+{
+ if (atomic_read(&current->preemptable) <= 0) {
+ BUG();
+ }
+ if (atomic_read(&current->preemptable) == 1) {
+ /*
+ * At that point a scheduling is healthy iff:
+ * - a scheduling request is pending.
+ * - the task is in running state.
+ * - this is not an interrupt context.
+ * - local interrupts are enabled.
+ */
+ if (current->need_resched == 1 &&
+ current->state == TASK_RUNNING &&
+ !in_interrupt() &&
+ local_irq_are_enabled())
+ {
+ schedule();
+ }
+ }
+ atomic_dec(&current->preemptable);
+}
+
+static inline int preemptable(void)
+{
+ return (!in_interrupt() &&
+ !atomic_read(&current->preemptable));
}

extern int request_irq(unsigned int,
diff -u --recursive linux-2.4-prerelease.org/include/linux/smp_lock.h linux-2.4-prerelease/include/linux/smp_lock.h
--- linux-2.4-prerelease.org/include/linux/smp_lock.h Wed Jan 3 17:19:44 2001
+++ linux-2.4-prerelease/include/linux/smp_lock.h Wed Jan 3 17:30:04 2001
@@ -5,11 +5,37 @@

#ifndef CONFIG_SMP

-#define lock_kernel() do { } while(0)
-#define unlock_kernel() do { } while(0)
-#define release_kernel_lock(task, cpu) do { } while(0)
-#define reacquire_kernel_lock(task) do { } while(0)
-#define kernel_locked() 1
+/*
+ * Release global kernel lock.
+ * Regarding preemption, this actually does the reverse -
+ */
+#define release_kernel_lock(task, cpu) \
+do { \
+ disable_preempt(); \
+} while (0)
+
+/*
+ * Re-acquire the kernel lock
+ * Re-enable the preemption - see comments above.
+ * Note: enable_preempt() cannot not be called at
+ * that point (otherwise schedule() becomes reentrant).
+ */
+#define reacquire_kernel_lock(task) \
+do { \
+ atomic_dec(&current->preemptable); \
+} while (0)
+
+#define lock_kernel() \
+do { \
+ disable_preempt(); \
+} while(0);
+
+#define unlock_kernel() \
+do { \
+ enable_preempt(); \
+} while (0)
+
+#define kernel_locked() (!preemptable())

#else

diff -u --recursive linux-2.4-prerelease.org/include/linux/spinlock.h linux-2.4-prerelease/include/linux/spinlock.h
--- linux-2.4-prerelease.org/include/linux/spinlock.h Wed Jan 3 17:19:44 2001
+++ linux-2.4-prerelease/include/linux/spinlock.h Wed Jan 3 14:21:58 2001
@@ -3,33 +3,72 @@

#include <linux/config.h>

+static inline void disable_preempt(void);
+static inline void enable_preempt(void);
+
/*
* These are the generic versions of the spinlocks and read-write
* locks..
*/
-#define spin_lock_irqsave(lock, flags) do { local_irq_save(flags); spin_lock(lock); } while (0)
-#define spin_lock_irq(lock) do { local_irq_disable(); spin_lock(lock); } while (0)
-#define spin_lock_bh(lock) do { local_bh_disable(); spin_lock(lock); } while (0)
-
-#define read_lock_irqsave(lock, flags) do { local_irq_save(flags); read_lock(lock); } while (0)
-#define read_lock_irq(lock) do { local_irq_disable(); read_lock(lock); } while (0)
-#define read_lock_bh(lock) do { local_bh_disable(); read_lock(lock); } while (0)
-
-#define write_lock_irqsave(lock, flags) do { local_irq_save(flags); write_lock(lock); } while (0)
-#define write_lock_irq(lock) do { local_irq_disable(); write_lock(lock); } while (0)
-#define write_lock_bh(lock) do { local_bh_disable(); write_lock(lock); } while (0)
-
-#define spin_unlock_irqrestore(lock, flags) do { spin_unlock(lock); local_irq_restore(flags); } while (0)
-#define spin_unlock_irq(lock) do { spin_unlock(lock); local_irq_enable(); } while (0)
-#define spin_unlock_bh(lock) do { spin_unlock(lock); local_bh_enable(); } while (0)
-
-#define read_unlock_irqrestore(lock, flags) do { read_unlock(lock); local_irq_restore(flags); } while (0)
-#define read_unlock_irq(lock) do { read_unlock(lock); local_irq_enable(); } while (0)
-#define read_unlock_bh(lock) do { read_unlock(lock); local_bh_enable(); } while (0)
-
-#define write_unlock_irqrestore(lock, flags) do { write_unlock(lock); local_irq_restore(flags); } while (0)
-#define write_unlock_irq(lock) do { write_unlock(lock); local_irq_enable(); } while (0)
-#define write_unlock_bh(lock) do { write_unlock(lock); local_bh_enable(); } while (0)
+#define spin_lock_irqsave(lock, flags) \
+ do { disable_preempt(); local_irq_save(flags); _spin_lock(lock); } while (0)
+#define spin_lock_irq(lock) \
+ do { disable_preempt(); local_irq_disable(); _spin_lock(lock); } while (0)
+#define spin_lock_bh(lock) \
+ do { disable_preempt(); local_bh_disable(); _spin_lock(lock); } while (0)
+
+#define read_lock_irqsave(lock, flags) \
+ do { disable_preempt(); local_irq_save(flags); _read_lock(lock); } while (0)
+#define read_lock_irq(lock) \
+ do { disable_preempt(); local_irq_disable(); _read_lock(lock); } while (0)
+#define read_lock_bh(lock) \
+ do { disable_preempt(); local_bh_disable(); _read_lock(lock); } while (0)
+
+#define write_lock_irqsave(lock, flags) \
+ do { disable_preempt(); local_irq_save(flags); _write_lock(lock); } while (0)
+#define write_lock_irq(lock) \
+ do { disable_preempt(); local_irq_disable(); _write_lock(lock); } while (0)
+#define write_lock_bh(lock) \
+ do { disable_preempt(); local_bh_disable(); _write_lock(lock); } while (0)
+
+#define spin_unlock_irqrestore(lock, flags) \
+ do { _spin_unlock(lock); local_irq_restore(flags); enable_preempt(); } while (0)
+#define spin_unlock_irq(lock) \
+ do { _spin_unlock(lock); local_irq_enable(); enable_preempt(); } while (0)
+#define spin_unlock_bh(lock) \
+ do { _spin_unlock(lock); local_bh_enable(); enable_preempt(); } while (0)
+
+#define read_unlock_irqrestore(lock, flags) \
+ do { _read_unlock(lock); local_irq_restore(flags); enable_preempt(); } while (0)
+#define read_unlock_irq(lock) \
+ do { _read_unlock(lock); local_irq_enable(); enable_preempt(); } while (0)
+#define read_unlock_bh(lock) \
+ do { _read_unlock(lock); local_bh_enable(); enable_preempt(); } while (0)
+
+#define write_unlock_irqrestore(lock, flags) \
+ do { _write_unlock(lock); local_irq_restore(flags); enable_preempt(); } while (0)
+#define write_unlock_irq(lock) \
+ do { _write_unlock(lock); local_irq_enable(); enable_preempt(); } while (0)
+#define write_unlock_bh(lock) \
+ do { _write_unlock(lock); local_bh_enable(); enable_preempt(); } while (0)
+
+#define spin_lock(lock) \
+ do { disable_preempt(); _spin_lock(lock); } while (0)
+#define spin_unlock(lock) \
+ do { _spin_unlock(lock); enable_preempt(); } while (0)
+#define spin_trylock(lock) \
+ ({disable_preempt(); _spin_trylock(lock)? 1: (enable_preempt(), 0);})
+
+#define read_lock(lock) \
+ do { disable_preempt(); _read_lock(lock); } while (0)
+#define read_unlock(lock) \
+ do { _read_unlock(lock); enable_preempt(); } while (0)
+
+#define write_lock(lock) \
+ do { disable_preempt(); _write_lock(lock); } while (0)
+#define write_unlock(lock) \
+ do { _write_unlock(lock); enable_preempt(); } while (0)
+

#ifdef CONFIG_SMP
#include <asm/spinlock.h>
@@ -40,8 +79,6 @@

#if (DEBUG_SPINLOCKS < 1)

-#define atomic_dec_and_lock(atomic,lock) atomic_dec_and_test(atomic)
-
/*
* Your basic spinlocks, allowing only a single CPU anywhere
*
@@ -56,11 +93,11 @@
#endif

#define spin_lock_init(lock) do { } while(0)
-#define spin_lock(lock) (void)(lock) /* Not "unused variable". */
+#define _spin_lock(lock) (void)(lock) /* Not "unused variable". */
#define spin_is_locked(lock) (0)
-#define spin_trylock(lock) ({1; })
+#define _spin_trylock(lock) ({1; })
#define spin_unlock_wait(lock) do { } while(0)
-#define spin_unlock(lock) do { } while(0)
+#define _spin_unlock(lock) do { } while(0)

#elif (DEBUG_SPINLOCKS < 2)

@@ -71,11 +108,11 @@

#define spin_lock_init(x) do { (x)->lock = 0; } while (0)
#define spin_is_locked(lock) (test_bit(0,(lock)))
-#define spin_trylock(lock) (!test_and_set_bit(0,(lock)))
+#define _spin_trylock(lock) (!test_and_set_bit(0,(lock)))

-#define spin_lock(x) do { (x)->lock = 1; } while (0)
+#define _spin_lock(x) do { (x)->lock = 1; } while (0)
#define spin_unlock_wait(x) do { } while (0)
-#define spin_unlock(x) do { (x)->lock = 0; } while (0)
+#define _spin_unlock(x) do { (x)->lock = 0; } while (0)

#else /* (DEBUG_SPINLOCKS >= 2) */

@@ -90,11 +127,11 @@

#define spin_lock_init(x) do { (x)->lock = 0; } while (0)
#define spin_is_locked(lock) (test_bit(0,(lock)))
-#define spin_trylock(lock) (!test_and_set_bit(0,(lock)))
+#define _spin_trylock(lock) (!test_and_set_bit(0,(lock)))

-#define spin_lock(x) do {unsigned long __spinflags; save_flags(__spinflags); cli(); if ((x)->lock&&(x)->babble) {printk("%s:%d: spin_lock(%s:%p) already locked\n", __BASE_FILE__,__LINE__, (x)->module, (x));(x)->babble--;} (x)->lock = 1; restore_flags(__spinflags);} while (0)
+#define _spin_lock(x) do {unsigned long __spinflags; save_flags(__spinflags); cli(); if ((x)->lock&&(x)->babble) {printk("%s:%d: spin_lock(%s:%p) already locked\n", __BASE_FILE__,__LINE__, (x)->module, (x));(x)->babble--;} (x)->lock = 1; restore_flags(__spinflags);} while (0)
#define spin_unlock_wait(x) do {unsigned long __spinflags; save_flags(__spinflags); cli(); if ((x)->lock&&(x)->babble) {printk("%s:%d: spin_unlock_wait(%s:%p) deadlock\n", __BASE_FILE__,__LINE__, (x)->module, (x));(x)->babble--;} restore_flags(__spinflags);} while (0)
-#define spin_unlock(x) do {unsigned long __spinflags; save_flags(__spinflags); cli(); if (!(x)->lock&&(x)->babble) {printk("%s:%d: spin_unlock(%s:%p) not locked\n", __BASE_FILE__,__LINE__, (x)->module, (x));(x)->babble--;} (x)->lock = 0; restore_flags(__spinflags);} while (0)
+#define _spin_unlock(x) do {unsigned long __spinflags; save_flags(__spinflags); cli(); if (!(x)->lock&&(x)->babble) {printk("%s:%d: spin_unlock(%s:%p) not locked\n", __BASE_FILE__,__LINE__, (x)->module, (x));(x)->babble--;} (x)->lock = 0; restore_flags(__spinflags);} while (0)

#endif /* DEBUG_SPINLOCKS */

@@ -119,10 +156,10 @@
#endif

#define rwlock_init(lock) do { } while(0)
-#define read_lock(lock) (void)(lock) /* Not "unused variable". */
-#define read_unlock(lock) do { } while(0)
-#define write_lock(lock) (void)(lock) /* Not "unused variable". */
-#define write_unlock(lock) do { } while(0)
+#define _read_lock(lock) (void)(lock) /* Not "unused variable". */
+#define _read_unlock(lock) do { } while(0)
+#define _write_lock(lock) (void)(lock) /* Not "unused variable". */
+#define _write_unlock(lock) do { } while(0)

#endif /* !SMP */

diff -u --recursive linux-2.4-prerelease.org/kernel/fork.c linux-2.4-prerelease/kernel/fork.c
--- linux-2.4-prerelease.org/kernel/fork.c Wed Jan 3 17:19:44 2001
+++ linux-2.4-prerelease/kernel/fork.c Wed Jan 3 17:27:38 2001
@@ -622,6 +622,7 @@
}
#endif
p->lock_depth = -1; /* -1 = no lock */
+ atomic_set(&p->preemptable, 0);
p->start_time = jiffies;

retval = -ENOMEM;
diff -u --recursive linux-2.4-prerelease.org/kernel/sched.c linux-2.4-prerelease/kernel/sched.c
--- linux-2.4-prerelease.org/kernel/sched.c Wed Jan 3 17:19:44 2001
+++ linux-2.4-prerelease/kernel/sched.c Wed Jan 3 13:53:17 2001
@@ -550,6 +550,16 @@
del_from_runqueue(prev);
case TASK_RUNNING:
}
+ /*
+ * Check if the context switch is still necessary.
+ * This catches up things like if (need_resched) schedule()
+ * that is not atomic and open a window with a preemptive
+ * kernel where a task can be scheduled twice.
+ */
+ if (prev->need_resched == 0 && prev->state == TASK_RUNNING) {
+ spin_unlock_irq(&runqueue_lock);
+ goto same_process;
+ }
prev->need_resched = 0;

/*
@@ -1150,7 +1160,7 @@
printk(" %5d\n", p->p_osptr->pid);
else
printk("\n");
-
+ printk(" preemptable : %d\n", atomic_read(&p->preemptable));
{
struct sigqueue *q;
char s[sizeof(sigset_t)*2+1], b[sizeof(sigset_t)*2+1];
diff -u --recursive linux-2.4-prerelease.org/lib/dec_and_lock.c linux-2.4-prerelease/lib/dec_and_lock.c
--- linux-2.4-prerelease.org/lib/dec_and_lock.c Wed Jan 3 17:19:44 2001
+++ linux-2.4-prerelease/lib/dec_and_lock.c Wed Jan 3 12:58:58 2001
@@ -1,5 +1,6 @@
#include <linux/spinlock.h>
#include <asm/atomic.h>
+#include <linux/sched.h>

/*
* This is an architecture-neutral, but slow,