[patch] fix for some minor bh race

Andrea Arcangeli (andrea@suse.de)
Fri, 16 Jul 1999 17:40:29 +0200 (CEST)


This patch fixes some minor bh SMP race between bh_mask and bh_mask_count.
I also fixes the bh races in init_bh/remove_bh. Really bh_mask_count could
also return to be an int now, but I avoided this change since having it
atomic_t is harmless and doesn't break all archs.

Note: in init_bh/remove_bh I could also use set_bit(nr, &bh_mask) instead
of serializing the access of bh_mask via spinlock, but then I should use
set_bit/clear_bit even while I am forced to use the spinlock in
enable/disable_bh and I choosed to avoid the additional SMP-bus locking in
the common path ;).

Here it is the patch against 2.2.10 (moving the init/remove/mark_bh
functions some line down is necessary to call synchronize_bh):

--- /tmp/linux-2.2.10/arch/i386/kernel/irq.c Tue Jul 13 00:33:09 1999
+++ linux-2.2.10/arch/i386/kernel/irq.c Thu Jul 15 18:21:23 1999
@@ -449,6 +449,7 @@

atomic_t global_bh_count;
atomic_t global_bh_lock;
+spinlock_t i386_bh_lock = SPIN_LOCK_UNLOCKED;

/*
* "global_cli()" is a special case, in that it can hold the
--- /tmp/linux-2.2.10/include/asm-i386/softirq.h Sat Jan 23 17:28:57 1999
+++ linux-2.2.10/include/asm-i386/softirq.h Fri Jul 16 17:20:43 1999
@@ -9,25 +9,6 @@
#define get_active_bhs() (bh_mask & bh_active)
#define clear_active_bhs(x) atomic_clear_mask((x),&bh_active)

-extern inline void init_bh(int nr, void (*routine)(void))
-{
- bh_base[nr] = routine;
- atomic_set(&bh_mask_count[nr], 0);
- bh_mask |= 1 << nr;
-}
-
-extern inline void remove_bh(int nr)
-{
- bh_mask &= ~(1 << nr);
- mb();
- bh_base[nr] = NULL;
-}
-
-extern inline void mark_bh(int nr)
-{
- set_bit(nr, &bh_active);
-}
-
#ifdef __SMP__

/*
@@ -37,6 +18,7 @@
*/
extern atomic_t global_bh_lock;
extern atomic_t global_bh_count;
+extern spinlock_t i386_bh_lock;

extern void synchronize_bh(void);

@@ -91,21 +73,58 @@

#endif /* SMP */

+extern inline void init_bh(int nr, void (*routine)(void))
+{
+ unsigned long flags;
+
+ bh_base[nr] = routine;
+ atomic_set(&bh_mask_count[nr], 0);
+
+ spin_lock_irqsave(&i386_bh_lock, flags);
+ bh_mask |= 1 << nr;
+ spin_unlock_irqrestore(&i386_bh_lock, flags);
+}
+
+extern inline void remove_bh(int nr)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&i386_bh_lock, flags);
+ bh_mask &= ~(1 << nr);
+ spin_unlock_irqrestore(&i386_bh_lock, flags);
+
+ synchronize_bh();
+ bh_base[nr] = NULL;
+}
+
+extern inline void mark_bh(int nr)
+{
+ set_bit(nr, &bh_active);
+}
+
/*
* These use a mask count to correctly handle
* nested disable/enable calls
*/
extern inline void disable_bh(int nr)
{
+ unsigned long flags;
+
+ spin_lock_irqsave(&i386_bh_lock, flags);
bh_mask &= ~(1 << nr);
atomic_inc(&bh_mask_count[nr]);
+ spin_unlock_irqrestore(&i386_bh_lock, flags);
synchronize_bh();
}

extern inline void enable_bh(int nr)
{
+ unsigned long flags;
+
+ spin_lock_irqsave(&i386_bh_lock, flags);
if (atomic_dec_and_test(&bh_mask_count[nr]))
bh_mask |= 1 << nr;
+ spin_unlock_irqrestore(&i386_bh_lock, flags);
}

#endif /* __ASM_SOFTIRQ_H */
--- /tmp/linux-2.2.10/arch/i386/kernel/i386_ksyms.c Tue Jul 13 00:33:09 1999
+++ linux-2.2.10/arch/i386/kernel/i386_ksyms.c Fri Jul 16 17:16:29 1999
@@ -88,6 +88,7 @@
EXPORT_SYMBOL(global_bh_count);
EXPORT_SYMBOL(global_bh_lock);
EXPORT_SYMBOL(global_irq_holder);
+EXPORT_SYMBOL(i386_bh_lock);
EXPORT_SYMBOL(__global_cli);
EXPORT_SYMBOL(__global_sti);
EXPORT_SYMBOL(__global_save_flags);

Andrea

-
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@vger.rutgers.edu
Please read the FAQ at http://www.tux.org/lkml/