Linux MIPS Architecture development
 help / color / mirror / Atom feed
* [PATCH V3 1/2] MIPS: Remove irqflags.h dependency from bitops.h
@ 2012-09-03 21:52 Jim Quinlan
  2012-09-03 21:52 ` [PATCH V3 2/2] MIPS: make funcs preempt-safe for non-mipsr2 cpus Jim Quinlan
  2012-09-04 17:30 ` [PATCH V3 1/2] MIPS: Remove irqflags.h dependency from bitops.h David Daney
  0 siblings, 2 replies; 4+ messages in thread
From: Jim Quinlan @ 2012-09-03 21:52 UTC (permalink / raw)
  To: ralf, linux-mips; +Cc: ddaney.cavm, cernekee, Jim Quinlan

The "else clause" of most functions in bitops.h invoked
raw_local_irq_{save,restore}() and so had a dependency on irqflags.h.  This
fix moves said code to bitops.c, removing the dependency.

Signed-off-by: Jim Quinlan <jim2101024@gmail.com>
---
 arch/mips/include/asm/bitops.h |  114 +++++++------------------
 arch/mips/include/asm/io.h     |    1 +
 arch/mips/lib/Makefile         |    2 +-
 arch/mips/lib/bitops.c         |  180 ++++++++++++++++++++++++++++++++++++++++
 4 files changed, 214 insertions(+), 83 deletions(-)
 create mode 100644 arch/mips/lib/bitops.c

diff --git a/arch/mips/include/asm/bitops.h b/arch/mips/include/asm/bitops.h
index 82ad35c..9fd0b1d 100644
--- a/arch/mips/include/asm/bitops.h
+++ b/arch/mips/include/asm/bitops.h
@@ -14,7 +14,6 @@
 #endif
 
 #include <linux/compiler.h>
-#include <linux/irqflags.h>
 #include <linux/types.h>
 #include <asm/barrier.h>
 #include <asm/byteorder.h>		/* sigh ... */
@@ -44,6 +43,24 @@
 #define smp_mb__before_clear_bit()	smp_mb__before_llsc()
 #define smp_mb__after_clear_bit()	smp_llsc_mb()
 
+
+/*
+ * These are the "slower" versions of the functions and are in bitops.c.
+ * These functions call raw_local_irq_{save,restore}().
+ */
+extern void atomic_set_bit(unsigned long nr, volatile unsigned long *addr);
+extern void atomic_clear_bit(unsigned long nr, volatile unsigned long *addr);
+extern void atomic_change_bit(unsigned long nr, volatile unsigned long *addr);
+extern int atomic_test_and_set_bit(unsigned long nr,
+				   volatile unsigned long *addr);
+extern int atomic_test_and_set_bit_lock(unsigned long nr,
+					volatile unsigned long *addr);
+extern int atomic_test_and_clear_bit(unsigned long nr,
+				     volatile unsigned long *addr);
+extern int atomic_test_and_change_bit(unsigned long nr,
+				      volatile unsigned long *addr);
+
+
 /*
  * set_bit - Atomically set a bit in memory
  * @nr: the bit to set
@@ -92,17 +109,8 @@ static inline void set_bit(unsigned long nr, volatile unsigned long *addr)
 			: "=&r" (temp), "+m" (*m)
 			: "ir" (1UL << bit));
 		} while (unlikely(!temp));
-	} else {
-		volatile unsigned long *a = addr;
-		unsigned long mask;
-		unsigned long flags;
-
-		a += nr >> SZLONG_LOG;
-		mask = 1UL << bit;
-		raw_local_irq_save(flags);
-		*a |= mask;
-		raw_local_irq_restore(flags);
-	}
+	} else
+		atomic_set_bit(nr, addr);
 }
 
 /*
@@ -153,17 +161,8 @@ static inline void clear_bit(unsigned long nr, volatile unsigned long *addr)
 			: "=&r" (temp), "+m" (*m)
 			: "ir" (~(1UL << bit)));
 		} while (unlikely(!temp));
-	} else {
-		volatile unsigned long *a = addr;
-		unsigned long mask;
-		unsigned long flags;
-
-		a += nr >> SZLONG_LOG;
-		mask = 1UL << bit;
-		raw_local_irq_save(flags);
-		*a &= ~mask;
-		raw_local_irq_restore(flags);
-	}
+	} else
+		atomic_clear_bit(nr, addr);
 }
 
 /*
@@ -220,17 +219,8 @@ static inline void change_bit(unsigned long nr, volatile unsigned long *addr)
 			: "=&r" (temp), "+m" (*m)
 			: "ir" (1UL << bit));
 		} while (unlikely(!temp));
-	} else {
-		volatile unsigned long *a = addr;
-		unsigned long mask;
-		unsigned long flags;
-
-		a += nr >> SZLONG_LOG;
-		mask = 1UL << bit;
-		raw_local_irq_save(flags);
-		*a ^= mask;
-		raw_local_irq_restore(flags);
-	}
+	} else
+		atomic_change_bit(nr, addr);
 }
 
 /*
@@ -281,18 +271,8 @@ static inline int test_and_set_bit(unsigned long nr,
 		} while (unlikely(!res));
 
 		res = temp & (1UL << bit);
-	} else {
-		volatile unsigned long *a = addr;
-		unsigned long mask;
-		unsigned long flags;
-
-		a += nr >> SZLONG_LOG;
-		mask = 1UL << bit;
-		raw_local_irq_save(flags);
-		res = (mask & *a);
-		*a |= mask;
-		raw_local_irq_restore(flags);
-	}
+	} else
+		res = atomic_test_and_set_bit(nr, addr);
 
 	smp_llsc_mb();
 
@@ -345,18 +325,8 @@ static inline int test_and_set_bit_lock(unsigned long nr,
 		} while (unlikely(!res));
 
 		res = temp & (1UL << bit);
-	} else {
-		volatile unsigned long *a = addr;
-		unsigned long mask;
-		unsigned long flags;
-
-		a += nr >> SZLONG_LOG;
-		mask = 1UL << bit;
-		raw_local_irq_save(flags);
-		res = (mask & *a);
-		*a |= mask;
-		raw_local_irq_restore(flags);
-	}
+	} else
+		res = atomic_test_and_set_bit_lock(nr, addr);
 
 	smp_llsc_mb();
 
@@ -428,18 +398,8 @@ static inline int test_and_clear_bit(unsigned long nr,
 		} while (unlikely(!res));
 
 		res = temp & (1UL << bit);
-	} else {
-		volatile unsigned long *a = addr;
-		unsigned long mask;
-		unsigned long flags;
-
-		a += nr >> SZLONG_LOG;
-		mask = 1UL << bit;
-		raw_local_irq_save(flags);
-		res = (mask & *a);
-		*a &= ~mask;
-		raw_local_irq_restore(flags);
-	}
+	} else
+		res = atomic_test_and_clear_bit(nr, addr);
 
 	smp_llsc_mb();
 
@@ -494,18 +454,8 @@ static inline int test_and_change_bit(unsigned long nr,
 		} while (unlikely(!res));
 
 		res = temp & (1UL << bit);
-	} else {
-		volatile unsigned long *a = addr;
-		unsigned long mask;
-		unsigned long flags;
-
-		a += nr >> SZLONG_LOG;
-		mask = 1UL << bit;
-		raw_local_irq_save(flags);
-		res = (mask & *a);
-		*a ^= mask;
-		raw_local_irq_restore(flags);
-	}
+	} else
+		res = atomic_test_and_change_bit(nr, addr);
 
 	smp_llsc_mb();
 
diff --git a/arch/mips/include/asm/io.h b/arch/mips/include/asm/io.h
index 29d9c23..ff2e034 100644
--- a/arch/mips/include/asm/io.h
+++ b/arch/mips/include/asm/io.h
@@ -15,6 +15,7 @@
 #include <linux/compiler.h>
 #include <linux/kernel.h>
 #include <linux/types.h>
+#include <linux/irqflags.h>
 
 #include <asm/addrspace.h>
 #include <asm/bug.h>
diff --git a/arch/mips/lib/Makefile b/arch/mips/lib/Makefile
index c4a82e8..a7b8937 100644
--- a/arch/mips/lib/Makefile
+++ b/arch/mips/lib/Makefile
@@ -2,7 +2,7 @@
 # Makefile for MIPS-specific library files..
 #
 
-lib-y	+= csum_partial.o delay.o memcpy.o memset.o \
+lib-y	+= bitops.o csum_partial.o delay.o memcpy.o memset.o \
 	   strlen_user.o strncpy_user.o strnlen_user.o uncached.o
 
 obj-y			+= iomap.o
diff --git a/arch/mips/lib/bitops.c b/arch/mips/lib/bitops.c
new file mode 100644
index 0000000..6562ab2
--- /dev/null
+++ b/arch/mips/lib/bitops.c
@@ -0,0 +1,180 @@
+
+/*
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License.  See the file "COPYING" in the main directory of this archive
+ * for more details.
+ *
+ * Copyright (c) 1994-1997, 99, 2000, 06, 07 Ralf Baechle (ralf@linux-mips.org)
+ * Copyright (c) 1999, 2000  Silicon Graphics, Inc.
+ */
+
+#include <linux/irqflags.h>
+
+#if _MIPS_SZLONG == 32
+#define SZLONG_LOG 5
+#define SZLONG_MASK 31UL
+#elif _MIPS_SZLONG == 64
+#define SZLONG_MASK 63UL
+#define SZLONG_LOG 6
+#endif
+
+
+/*
+ * atomic_set_bit - Atomically set a bit in memory.  This is called by
+ * if set_bit() if it cannot find a faster solution.
+ * @nr: the bit to set
+ * @addr: the address to start counting from
+ */
+void atomic_set_bit(unsigned long nr, volatile unsigned long *addr)
+{
+	volatile unsigned long *a = addr;
+	unsigned short bit = nr & SZLONG_MASK;
+	unsigned long mask;
+	unsigned long flags;
+
+	a += nr >> SZLONG_LOG;
+	mask = 1UL << bit;
+	raw_local_irq_save(flags);
+	*a |= mask;
+	raw_local_irq_restore(flags);
+}
+
+
+/*
+ * atomic_clear_bit - Clears a bit in memory.  This is called by clear_bit() if
+ * it cannot find a faster solution.
+ * @nr: Bit to clear
+ * @addr: Address to start counting from
+ */
+void atomic_clear_bit(unsigned long nr, volatile unsigned long *addr)
+{
+	volatile unsigned long *a = addr;
+	unsigned short bit = nr & SZLONG_MASK;
+	unsigned long mask;
+	unsigned long flags;
+
+	a += nr >> SZLONG_LOG;
+	mask = 1UL << bit;
+	raw_local_irq_save(flags);
+	*a &= ~mask;
+	raw_local_irq_restore(flags);
+}
+
+
+/*
+ * atomic_change_bit - Toggle a bit in memory.  This is called by change_bit()
+ * if it cannot find a faster solution.
+ * @nr: Bit to change
+ * @addr: Address to start counting from
+ */
+void atomic_change_bit(unsigned long nr, volatile unsigned long *addr)
+{
+	volatile unsigned long *a = addr;
+	unsigned short bit = nr & SZLONG_MASK;
+	unsigned long mask;
+	unsigned long flags;
+
+	a += nr >> SZLONG_LOG;
+	mask = 1UL << bit;
+	raw_local_irq_save(flags);
+	*a ^= mask;
+	raw_local_irq_restore(flags);
+}
+
+
+/*
+ * atomic_test_and_set_bit - Set a bit and return its old value.  This is
+ * called by test_and_set_bit() if it cannot find a faster solution.
+ * @nr: Bit to set
+ * @addr: Address to count from
+ */
+int atomic_test_and_set_bit(unsigned long nr,
+			    volatile unsigned long *addr)
+{
+	volatile unsigned long *a = addr;
+	unsigned short bit = nr & SZLONG_MASK;
+	unsigned long mask;
+	unsigned long flags;
+	unsigned long res;
+
+	a += nr >> SZLONG_LOG;
+	mask = 1UL << bit;
+	raw_local_irq_save(flags);
+	res = (mask & *a);
+	*a |= mask;
+	raw_local_irq_restore(flags);
+	return res;
+}
+
+
+/*
+ * atomic_test_and_set_bit_lock - Set a bit and return its old value.  This is
+ * called by test_and_set_bit_lock() if it cannot find a faster solution.
+ * @nr: Bit to set
+ * @addr: Address to count from
+ */
+int atomic_test_and_set_bit_lock(unsigned long nr,
+				 volatile unsigned long *addr)
+{
+	volatile unsigned long *a = addr;
+	unsigned short bit = nr & SZLONG_MASK;
+	unsigned long mask;
+	unsigned long flags;
+	unsigned long res;
+
+	a += nr >> SZLONG_LOG;
+	mask = 1UL << bit;
+	raw_local_irq_save(flags);
+	res = (mask & *a);
+	*a |= mask;
+	raw_local_irq_restore(flags);
+	return res;
+}
+
+
+/*
+ * atomic_test_and_clear_bit - Clear a bit and return its old value.  This is
+ * called by test_and_clear_bit() if it cannot find a faster solution.
+ * @nr: Bit to clear
+ * @addr: Address to count from
+ */
+int atomic_test_and_clear_bit(unsigned long nr, volatile unsigned long *addr)
+{
+	volatile unsigned long *a = addr;
+	unsigned short bit = nr & SZLONG_MASK;
+	unsigned long mask;
+	unsigned long flags;
+	unsigned long res;
+
+	a += nr >> SZLONG_LOG;
+	mask = 1UL << bit;
+	raw_local_irq_save(flags);
+	res = (mask & *a);
+	*a &= ~mask;
+	raw_local_irq_restore(flags);
+	return res;
+}
+
+
+/*
+ * atomic_test_and_change_bit - Change a bit and return its old value.  This is
+ * called by test_and_change_bit() if it cannot find a faster solution.
+ * @nr: Bit to change
+ * @addr: Address to count from
+ */
+int atomic_test_and_change_bit(unsigned long nr, volatile unsigned long *addr)
+{
+	volatile unsigned long *a = addr;
+	unsigned short bit = nr & SZLONG_MASK;
+	unsigned long mask;
+	unsigned long flags;
+	unsigned long res;
+
+	a += nr >> SZLONG_LOG;
+	mask = 1UL << bit;
+	raw_local_irq_save(flags);
+	res = (mask & *a);
+	*a ^= mask;
+	raw_local_irq_restore(flags);
+	return res;
+}
-- 
1.7.6

^ permalink raw reply related	[flat|nested] 4+ messages in thread

* [PATCH V3 2/2] MIPS: make funcs preempt-safe for non-mipsr2 cpus
  2012-09-03 21:52 [PATCH V3 1/2] MIPS: Remove irqflags.h dependency from bitops.h Jim Quinlan
@ 2012-09-03 21:52 ` Jim Quinlan
  2012-09-04 17:36   ` David Daney
  2012-09-04 17:30 ` [PATCH V3 1/2] MIPS: Remove irqflags.h dependency from bitops.h David Daney
  1 sibling, 1 reply; 4+ messages in thread
From: Jim Quinlan @ 2012-09-03 21:52 UTC (permalink / raw)
  To: ralf, linux-mips; +Cc: ddaney.cavm, cernekee, Jim Quinlan

For non MIPSr2 processors, such as the BMIPS 5000, calls to
arch_local_irq_disable() and others may be preempted, and in doing
so a stale value may be restored to c0_status.  This fix disables
preemption for such processors prior to the call and enables it
after the call.

Those functions that needed this fix have been "outlined" to
mips-atomic.c, as they are no longer good candidates for inlining.

This bug was observed in a BMIPS 5000, occuring once every few hours
in a continuous reboot test.  It was traced to the write_lock_irq()
function which was being invoked in release_task() in exit.c.
By placing a number of "nops" inbetween the mfc0/mtc0 pair in
arch_local_irq_disable(), which is called by write_lock_irq(), we
were able to greatly increase the occurance of this bug.  Similarly,
the application of this commit silenced the bug.

Signed-off-by: Jim Quinlan <jim2101024@gmail.com>
---
 arch/mips/include/asm/irqflags.h |   92 +++++---------------
 arch/mips/lib/Makefile           |    3 +-
 arch/mips/lib/mips-atomic.c      |  179 ++++++++++++++++++++++++++++++++++++++
 3 files changed, 202 insertions(+), 72 deletions(-)
 create mode 100644 arch/mips/lib/mips-atomic.c

diff --git a/arch/mips/include/asm/irqflags.h b/arch/mips/include/asm/irqflags.h
index 309cbcd..9174d88 100644
--- a/arch/mips/include/asm/irqflags.h
+++ b/arch/mips/include/asm/irqflags.h
@@ -16,6 +16,15 @@
 #include <linux/compiler.h>
 #include <asm/hazards.h>
 
+#if !defined(CONFIG_CPU_MIPSR2) || defined(CONFIG_MT_SMTC)
+/* Functions that require preempt_{dis,en}able() are in mips-atomic.c */
+extern void arch_local_irq_disable(void);
+extern unsigned long arch_local_irq_save(void);
+extern void arch_local_irq_restore(unsigned long flags);
+extern void __arch_local_irq_restore(unsigned long flags);
+#endif
+
+
 __asm__(
 	"	.macro	arch_local_irq_enable				\n"
 	"	.set	push						\n"
@@ -57,42 +66,12 @@ static inline void arch_local_irq_enable(void)
 }
 
 
-/*
- * For cli() we have to insert nops to make sure that the new value
- * has actually arrived in the status register before the end of this
- * macro.
- * R4000/R4400 need three nops, the R4600 two nops and the R10000 needs
- * no nops at all.
- */
-/*
- * For TX49, operating only IE bit is not enough.
- *
- * If mfc0 $12 follows store and the mfc0 is last instruction of a
- * page and fetching the next instruction causes TLB miss, the result
- * of the mfc0 might wrongly contain EXL bit.
- *
- * ERT-TX49H2-027, ERT-TX49H3-012, ERT-TX49HL3-006, ERT-TX49H4-008
- *
- * Workaround: mask EXL bit of the result or place a nop before mfc0.
- */
+#if defined(CONFIG_CPU_MIPSR2) && !defined(CONFIG_MT_SMTC)
 __asm__(
 	"	.macro	arch_local_irq_disable\n"
 	"	.set	push						\n"
 	"	.set	noat						\n"
-#ifdef CONFIG_MIPS_MT_SMTC
-	"	mfc0	$1, $2, 1					\n"
-	"	ori	$1, 0x400					\n"
-	"	.set	noreorder					\n"
-	"	mtc0	$1, $2, 1					\n"
-#elif defined(CONFIG_CPU_MIPSR2)
 	"	di							\n"
-#else
-	"	mfc0	$1,$12						\n"
-	"	ori	$1,0x1f						\n"
-	"	xori	$1,0x1f						\n"
-	"	.set	noreorder					\n"
-	"	mtc0	$1,$12						\n"
-#endif
 	"	irq_disable_hazard					\n"
 	"	.set	pop						\n"
 	"	.endm							\n");
@@ -105,6 +84,8 @@ static inline void arch_local_irq_disable(void)
 		: /* no inputs */
 		: "memory");
 }
+#endif
+
 
 __asm__(
 	"	.macro	arch_local_save_flags flags			\n"
@@ -125,27 +106,15 @@ static inline unsigned long arch_local_save_flags(void)
 	return flags;
 }
 
+
+#if defined(CONFIG_CPU_MIPSR2) && !defined(CONFIG_MT_SMTC)
 __asm__(
 	"	.macro	arch_local_irq_save result			\n"
 	"	.set	push						\n"
 	"	.set	reorder						\n"
 	"	.set	noat						\n"
-#ifdef CONFIG_MIPS_MT_SMTC
-	"	mfc0	\\result, $2, 1					\n"
-	"	ori	$1, \\result, 0x400				\n"
-	"	.set	noreorder					\n"
-	"	mtc0	$1, $2, 1					\n"
-	"	andi	\\result, \\result, 0x400			\n"
-#elif defined(CONFIG_CPU_MIPSR2)
 	"	di	\\result					\n"
 	"	andi	\\result, 1					\n"
-#else
-	"	mfc0	\\result, $12					\n"
-	"	ori	$1, \\result, 0x1f				\n"
-	"	xori	$1, 0x1f					\n"
-	"	.set	noreorder					\n"
-	"	mtc0	$1, $12						\n"
-#endif
 	"	irq_disable_hazard					\n"
 	"	.set	pop						\n"
 	"	.endm							\n");
@@ -159,20 +128,16 @@ static inline unsigned long arch_local_irq_save(void)
 		     : "memory");
 	return flags;
 }
+#endif
 
+
+#if defined(CONFIG_CPU_MIPSR2) && !defined(CONFIG_MT_SMTC)
 __asm__(
 	"	.macro	arch_local_irq_restore flags			\n"
 	"	.set	push						\n"
 	"	.set	noreorder					\n"
 	"	.set	noat						\n"
-#ifdef CONFIG_MIPS_MT_SMTC
-	"mfc0	$1, $2, 1						\n"
-	"andi	\\flags, 0x400						\n"
-	"ori	$1, 0x400						\n"
-	"xori	$1, 0x400						\n"
-	"or	\\flags, $1						\n"
-	"mtc0	\\flags, $2, 1						\n"
-#elif defined(CONFIG_CPU_MIPSR2) && defined(CONFIG_IRQ_CPU)
+#if defined(CONFIG_IRQ_CPU)
 	/*
 	 * Slow, but doesn't suffer from a relatively unlikely race
 	 * condition we're having since days 1.
@@ -181,20 +146,13 @@ __asm__(
 	"	 di							\n"
 	"	ei							\n"
 	"1:								\n"
-#elif defined(CONFIG_CPU_MIPSR2)
+#else
 	/*
 	 * Fast, dangerous.  Life is fun, life is good.
 	 */
 	"	mfc0	$1, $12						\n"
 	"	ins	$1, \\flags, 0, 1				\n"
 	"	mtc0	$1, $12						\n"
-#else
-	"	mfc0	$1, $12						\n"
-	"	andi	\\flags, 1					\n"
-	"	ori	$1, 0x1f					\n"
-	"	xori	$1, 0x1f					\n"
-	"	or	\\flags, $1					\n"
-	"	mtc0	\\flags, $12					\n"
 #endif
 	"	irq_disable_hazard					\n"
 	"	.set	pop						\n"
@@ -205,16 +163,6 @@ static inline void arch_local_irq_restore(unsigned long flags)
 {
 	unsigned long __tmp1;
 
-#ifdef CONFIG_MIPS_MT_SMTC
-	/*
-	 * SMTC kernel needs to do a software replay of queued
-	 * IPIs, at the cost of branch and call overhead on each
-	 * local_irq_restore()
-	 */
-	if (unlikely(!(flags & 0x0400)))
-		smtc_ipi_replay();
-#endif
-
 	__asm__ __volatile__(
 		"arch_local_irq_restore\t%0"
 		: "=r" (__tmp1)
@@ -232,6 +180,8 @@ static inline void __arch_local_irq_restore(unsigned long flags)
 		: "0" (flags)
 		: "memory");
 }
+#endif
+
 
 static inline int arch_irqs_disabled_flags(unsigned long flags)
 {
diff --git a/arch/mips/lib/Makefile b/arch/mips/lib/Makefile
index a7b8937..eeddc58 100644
--- a/arch/mips/lib/Makefile
+++ b/arch/mips/lib/Makefile
@@ -3,7 +3,8 @@
 #
 
 lib-y	+= bitops.o csum_partial.o delay.o memcpy.o memset.o \
-	   strlen_user.o strncpy_user.o strnlen_user.o uncached.o
+	   mips-atomic.o strlen_user.o strncpy_user.o \
+	   strnlen_user.o uncached.o
 
 obj-y			+= iomap.o
 obj-$(CONFIG_PCI)	+= iomap-pci.o
diff --git a/arch/mips/lib/mips-atomic.c b/arch/mips/lib/mips-atomic.c
new file mode 100644
index 0000000..546eb25
--- /dev/null
+++ b/arch/mips/lib/mips-atomic.c
@@ -0,0 +1,179 @@
+/*
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License.  See the file "COPYING" in the main directory of this archive
+ * for more details.
+ *
+ * Copyright (C) 1994, 95, 96, 97, 98, 99, 2003 by Ralf Baechle
+ * Copyright (C) 1996 by Paul M. Antoine
+ * Copyright (C) 1999 Silicon Graphics
+ * Copyright (C) 2000 MIPS Technologies, Inc.
+ */
+#include <asm/irqflags.h>
+#include <asm/hazards.h>
+#include <linux/compiler.h>
+#include <linux/preempt.h>
+
+#if !defined(CONFIG_CPU_MIPSR2) || defined(CONFIG_MIPS_MT_SMTC)
+
+#if defined(CONFIG_PREEMPT)
+#define arch_local_preempt_enable() preempt_enable()
+#define arch_local_preempt_disable() preempt_disable()
+#else
+#define arch_local_preempt_enable()
+#define arch_local_preempt_disable()
+#endif
+
+
+/*
+ * For cli() we have to insert nops to make sure that the new value
+ * has actually arrived in the status register before the end of this
+ * macro.
+ * R4000/R4400 need three nops, the R4600 two nops and the R10000 needs
+ * no nops at all.
+ */
+/*
+ * For TX49, operating only IE bit is not enough.
+ *
+ * If mfc0 $12 follows store and the mfc0 is last instruction of a
+ * page and fetching the next instruction causes TLB miss, the result
+ * of the mfc0 might wrongly contain EXL bit.
+ *
+ * ERT-TX49H2-027, ERT-TX49H3-012, ERT-TX49HL3-006, ERT-TX49H4-008
+ *
+ * Workaround: mask EXL bit of the result or place a nop before mfc0.
+ */
+__asm__(
+	"	.macro	arch_local_irq_disable\n"
+	"	.set	push						\n"
+	"	.set	noat						\n"
+#ifdef CONFIG_MIPS_MT_SMTC
+	"	mfc0	$1, $2, 1					\n"
+	"	ori	$1, 0x400					\n"
+	"	.set	noreorder					\n"
+	"	mtc0	$1, $2, 1					\n"
+#elif defined(CONFIG_CPU_MIPSR2)
+	/* see irqflags.h for inline function */
+#else
+	"	mfc0	$1,$12						\n"
+	"	ori	$1,0x1f						\n"
+	"	xori	$1,0x1f						\n"
+	"	.set	noreorder					\n"
+	"	mtc0	$1,$12						\n"
+#endif
+	"	irq_disable_hazard					\n"
+	"	.set	pop						\n"
+	"	.endm							\n");
+
+void arch_local_irq_disable(void)
+{
+	arch_local_preempt_disable();
+	__asm__ __volatile__(
+		"arch_local_irq_disable"
+		: /* no outputs */
+		: /* no inputs */
+		: "memory");
+	arch_local_preempt_enable();
+}
+
+
+__asm__(
+	"	.macro	arch_local_irq_save result			\n"
+	"	.set	push						\n"
+	"	.set	reorder						\n"
+	"	.set	noat						\n"
+#ifdef CONFIG_MIPS_MT_SMTC
+	"	mfc0	\\result, $2, 1					\n"
+	"	ori	$1, \\result, 0x400				\n"
+	"	.set	noreorder					\n"
+	"	mtc0	$1, $2, 1					\n"
+	"	andi	\\result, \\result, 0x400			\n"
+#elif defined(CONFIG_CPU_MIPSR2)
+	/* see irqflags.h for inline function */
+#else
+	"	mfc0	\\result, $12					\n"
+	"	ori	$1, \\result, 0x1f				\n"
+	"	xori	$1, 0x1f					\n"
+	"	.set	noreorder					\n"
+	"	mtc0	$1, $12						\n"
+#endif
+	"	irq_disable_hazard					\n"
+	"	.set	pop						\n"
+	"	.endm							\n");
+
+unsigned long arch_local_irq_save(void)
+{
+	unsigned long flags;
+	arch_local_preempt_disable();
+	asm volatile("arch_local_irq_save\t%0"
+		     : "=r" (flags)
+		     : /* no inputs */
+		     : "memory");
+	arch_local_preempt_enable();
+	return flags;
+}
+
+
+__asm__(
+	"	.macro	arch_local_irq_restore flags			\n"
+	"	.set	push						\n"
+	"	.set	noreorder					\n"
+	"	.set	noat						\n"
+#ifdef CONFIG_MIPS_MT_SMTC
+	"mfc0	$1, $2, 1						\n"
+	"andi	\\flags, 0x400						\n"
+	"ori	$1, 0x400						\n"
+	"xori	$1, 0x400						\n"
+	"or	\\flags, $1						\n"
+	"mtc0	\\flags, $2, 1						\n"
+#elif defined(CONFIG_CPU_MIPSR2) && defined(CONFIG_IRQ_CPU)
+	/* see irqflags.h for inline function */
+#elif defined(CONFIG_CPU_MIPSR2)
+	/* see irqflags.h for inline function */
+#else
+	"	mfc0	$1, $12						\n"
+	"	andi	\\flags, 1					\n"
+	"	ori	$1, 0x1f					\n"
+	"	xori	$1, 0x1f					\n"
+	"	or	\\flags, $1					\n"
+	"	mtc0	\\flags, $12					\n"
+#endif
+	"	irq_disable_hazard					\n"
+	"	.set	pop						\n"
+	"	.endm							\n");
+
+void arch_local_irq_restore(unsigned long flags)
+{
+	unsigned long __tmp1;
+
+#ifdef CONFIG_MIPS_MT_SMTC
+	/*
+	 * SMTC kernel needs to do a software replay of queued
+	 * IPIs, at the cost of branch and call overhead on each
+	 * local_irq_restore()
+	 */
+	if (unlikely(!(flags & 0x0400)))
+		smtc_ipi_replay();
+#endif
+	arch_local_preempt_disable();
+	__asm__ __volatile__(
+		"arch_local_irq_restore\t%0"
+		: "=r" (__tmp1)
+		: "0" (flags)
+		: "memory");
+	arch_local_preempt_enable();
+}
+
+void __arch_local_irq_restore(unsigned long flags)
+{
+	unsigned long __tmp1;
+
+	arch_local_preempt_disable();
+	__asm__ __volatile__(
+		"arch_local_irq_restore\t%0"
+		: "=r" (__tmp1)
+		: "0" (flags)
+		: "memory");
+	arch_local_preempt_enable();
+}
+
+#endif /* !defined(CONFIG_CPU_MIPSR2) || defined(CONFIG_MIPS_MT_SMTC) */
-- 
1.7.6

^ permalink raw reply related	[flat|nested] 4+ messages in thread

* Re: [PATCH V3 1/2] MIPS: Remove irqflags.h dependency from bitops.h
  2012-09-03 21:52 [PATCH V3 1/2] MIPS: Remove irqflags.h dependency from bitops.h Jim Quinlan
  2012-09-03 21:52 ` [PATCH V3 2/2] MIPS: make funcs preempt-safe for non-mipsr2 cpus Jim Quinlan
@ 2012-09-04 17:30 ` David Daney
  1 sibling, 0 replies; 4+ messages in thread
From: David Daney @ 2012-09-04 17:30 UTC (permalink / raw)
  To: Jim Quinlan; +Cc: ralf, linux-mips, cernekee

On 09/03/2012 02:52 PM, Jim Quinlan wrote:
> The "else clause" of most functions in bitops.h invoked
> raw_local_irq_{save,restore}() and so had a dependency on irqflags.h.  This
> fix moves said code to bitops.c, removing the dependency.
>
> Signed-off-by: Jim Quinlan <jim2101024@gmail.com>


This is much better I think.

Now only a few very minor things I would change...

> ---
>   arch/mips/include/asm/bitops.h |  114 +++++++------------------
>   arch/mips/include/asm/io.h     |    1 +
>   arch/mips/lib/Makefile         |    2 +-
>   arch/mips/lib/bitops.c         |  180 ++++++++++++++++++++++++++++++++++++++++
>   4 files changed, 214 insertions(+), 83 deletions(-)
>   create mode 100644 arch/mips/lib/bitops.c
>
> diff --git a/arch/mips/include/asm/bitops.h b/arch/mips/include/asm/bitops.h
> index 82ad35c..9fd0b1d 100644
> --- a/arch/mips/include/asm/bitops.h
> +++ b/arch/mips/include/asm/bitops.h
> @@ -14,7 +14,6 @@
>   #endif
>
>   #include <linux/compiler.h>
> -#include <linux/irqflags.h>
>   #include <linux/types.h>
>   #include <asm/barrier.h>
>   #include <asm/byteorder.h>		/* sigh ... */
> @@ -44,6 +43,24 @@
>   #define smp_mb__before_clear_bit()	smp_mb__before_llsc()
>   #define smp_mb__after_clear_bit()	smp_llsc_mb()
>
> +
> +/*
> + * These are the "slower" versions of the functions and are in bitops.c.
> + * These functions call raw_local_irq_{save,restore}().
> + */
> +extern void atomic_set_bit(unsigned long nr, volatile unsigned long *addr);
> +extern void atomic_clear_bit(unsigned long nr, volatile unsigned long *addr);
> +extern void atomic_change_bit(unsigned long nr, volatile unsigned long *addr);
> +extern int atomic_test_and_set_bit(unsigned long nr,
> +				   volatile unsigned long *addr);
> +extern int atomic_test_and_set_bit_lock(unsigned long nr,
> +					volatile unsigned long *addr);
> +extern int atomic_test_and_clear_bit(unsigned long nr,
> +				     volatile unsigned long *addr);
> +extern int atomic_test_and_change_bit(unsigned long nr,
> +				      volatile unsigned long *addr);
> +

No 'extern' needed.

These shouldn't be directly called from user code.  I would suggest 
renaming the functions to something like:

__mips_set_bit();


[...]
> diff --git a/arch/mips/lib/bitops.c b/arch/mips/lib/bitops.c
> new file mode 100644
> index 0000000..6562ab2
> --- /dev/null
> +++ b/arch/mips/lib/bitops.c
> @@ -0,0 +1,180 @@
> +
> +/*
> + * This file is subject to the terms and conditions of the GNU General Public
> + * License.  See the file "COPYING" in the main directory of this archive
> + * for more details.
> + *
> + * Copyright (c) 1994-1997, 99, 2000, 06, 07 Ralf Baechle (ralf@linux-mips.org)
> + * Copyright (c) 1999, 2000  Silicon Graphics, Inc.
> + */
> +
> +#include <linux/irqflags.h>
> +
> +#if _MIPS_SZLONG == 32
> +#define SZLONG_LOG 5
> +#define SZLONG_MASK 31UL
> +#elif _MIPS_SZLONG == 64
> +#define SZLONG_MASK 63UL
> +#define SZLONG_LOG 6
> +#endif
> +

There has to be a cleaner way to do this...  Perhaps:



#define SZLONG_LOG (ilog2(sizeof(unsigned long)) + 3)
#define SZLONG_MASK ((1 << SZLONG_LOG) - 1)

> +
> +/*
> + * atomic_set_bit - Atomically set a bit in memory.  This is called by
> + * if set_bit() if it cannot find a faster solution.
> + * @nr: the bit to set
> + * @addr: the address to start counting from
> + */
> +void atomic_set_bit(unsigned long nr, volatile unsigned long *addr)
> +{
> +	volatile unsigned long *a = addr;
> +	unsigned short bit = nr & SZLONG_MASK;

just make bit type 'int'.  In some cases forcing a narrower type than 
necessary requires the compiler to emit extra code.  I am not sure if it 
would here, but why use a type other than int unless absolutely necessary?

> +	unsigned long mask;
> +	unsigned long flags;
> +
> +	a += nr >> SZLONG_LOG;
> +	mask = 1UL << bit;
> +	raw_local_irq_save(flags);
> +	*a |= mask;
> +	raw_local_irq_restore(flags);
> +}

All these must be EXPORT_SYMBOL(), so the bitop intrinsics can be used 
from modules.

^ permalink raw reply	[flat|nested] 4+ messages in thread

* Re: [PATCH V3 2/2] MIPS: make funcs preempt-safe for non-mipsr2 cpus
  2012-09-03 21:52 ` [PATCH V3 2/2] MIPS: make funcs preempt-safe for non-mipsr2 cpus Jim Quinlan
@ 2012-09-04 17:36   ` David Daney
  0 siblings, 0 replies; 4+ messages in thread
From: David Daney @ 2012-09-04 17:36 UTC (permalink / raw)
  To: Jim Quinlan; +Cc: ralf, linux-mips, cernekee

On 09/03/2012 02:52 PM, Jim Quinlan wrote:
> For non MIPSr2 processors, such as the BMIPS 5000, calls to
> arch_local_irq_disable() and others may be preempted, and in doing
> so a stale value may be restored to c0_status.  This fix disables
> preemption for such processors prior to the call and enables it
> after the call.
>
> Those functions that needed this fix have been "outlined" to
> mips-atomic.c, as they are no longer good candidates for inlining.
>
> This bug was observed in a BMIPS 5000, occuring once every few hours
> in a continuous reboot test.  It was traced to the write_lock_irq()
> function which was being invoked in release_task() in exit.c.
> By placing a number of "nops" inbetween the mfc0/mtc0 pair in
> arch_local_irq_disable(), which is called by write_lock_irq(), we
> were able to greatly increase the occurance of this bug.  Similarly,
> the application of this commit silenced the bug.
>
> Signed-off-by: Jim Quinlan <jim2101024@gmail.com>

This one seems better too...

[...]
> diff --git a/arch/mips/lib/mips-atomic.c b/arch/mips/lib/mips-atomic.c
> new file mode 100644
> index 0000000..546eb25
> --- /dev/null
> +++ b/arch/mips/lib/mips-atomic.c
> @@ -0,0 +1,179 @@
> +/*
> + * This file is subject to the terms and conditions of the GNU General Public
> + * License.  See the file "COPYING" in the main directory of this archive
> + * for more details.
> + *
> + * Copyright (C) 1994, 95, 96, 97, 98, 99, 2003 by Ralf Baechle
> + * Copyright (C) 1996 by Paul M. Antoine
> + * Copyright (C) 1999 Silicon Graphics
> + * Copyright (C) 2000 MIPS Technologies, Inc.
> + */
> +#include <asm/irqflags.h>
> +#include <asm/hazards.h>
> +#include <linux/compiler.h>
> +#include <linux/preempt.h>
> +
> +#if !defined(CONFIG_CPU_MIPSR2) || defined(CONFIG_MIPS_MT_SMTC)
> +
> +#if defined(CONFIG_PREEMPT)
> +#define arch_local_preempt_enable() preempt_enable()
> +#define arch_local_preempt_disable() preempt_disable()
> +#else
> +#define arch_local_preempt_enable()
> +#define arch_local_preempt_disable()
> +#endif
> +
> +
> +/*
> + * For cli() we have to insert nops to make sure that the new value
> + * has actually arrived in the status register before the end of this
> + * macro.
> + * R4000/R4400 need three nops, the R4600 two nops and the R10000 needs
> + * no nops at all.
> + */
> +/*
> + * For TX49, operating only IE bit is not enough.
> + *
> + * If mfc0 $12 follows store and the mfc0 is last instruction of a
> + * page and fetching the next instruction causes TLB miss, the result
> + * of the mfc0 might wrongly contain EXL bit.
> + *
> + * ERT-TX49H2-027, ERT-TX49H3-012, ERT-TX49HL3-006, ERT-TX49H4-008
> + *
> + * Workaround: mask EXL bit of the result or place a nop before mfc0.
> + */
> +__asm__(
> +	"	.macro	arch_local_irq_disable\n"
> +	"	.set	push						\n"
> +	"	.set	noat						\n"
> +#ifdef CONFIG_MIPS_MT_SMTC
> +	"	mfc0	$1, $2, 1					\n"
> +	"	ori	$1, 0x400					\n"
> +	"	.set	noreorder					\n"
> +	"	mtc0	$1, $2, 1					\n"
> +#elif defined(CONFIG_CPU_MIPSR2)
> +	/* see irqflags.h for inline function */
> +#else
> +	"	mfc0	$1,$12						\n"
> +	"	ori	$1,0x1f						\n"
> +	"	xori	$1,0x1f						\n"
> +	"	.set	noreorder					\n"
> +	"	mtc0	$1,$12						\n"
> +#endif
> +	"	irq_disable_hazard					\n"
> +	"	.set	pop						\n"
> +	"	.endm							\n");
> +
> +void arch_local_irq_disable(void)
> +{
> +	arch_local_preempt_disable();
> +	__asm__ __volatile__(
> +		"arch_local_irq_disable"
> +		: /* no outputs */
> +		: /* no inputs */
> +		: "memory");
> +	arch_local_preempt_enable();
> +}

I think this function must be EXPORT_SYMBOL() too.

David Daney

^ permalink raw reply	[flat|nested] 4+ messages in thread

end of thread, other threads:[~2012-09-04 17:36 UTC | newest]

Thread overview: 4+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2012-09-03 21:52 [PATCH V3 1/2] MIPS: Remove irqflags.h dependency from bitops.h Jim Quinlan
2012-09-03 21:52 ` [PATCH V3 2/2] MIPS: make funcs preempt-safe for non-mipsr2 cpus Jim Quinlan
2012-09-04 17:36   ` David Daney
2012-09-04 17:30 ` [PATCH V3 1/2] MIPS: Remove irqflags.h dependency from bitops.h David Daney

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox