Synchronized bit operations

Currently the only way to specify how a bit operation is synchronized
is via __. F.e. there is

clear_bit

and

__clear_bit

clear_bit() implies atomic behavior whereas __clear_bit is not atomic.

However, neither one allows to specify an ordering mode that may
be necessary for locking and unlocking etc. For that purpose
we have additional macross smb_mb__before/after_xxx. However, processors
have the ability to specify the synchronization mode in the atomic
instruction itself and having cumbersome additional macros
around makes it difficult to optimize bit operations behavior
for various processors.

This adds a variant with a _mode postfix that allows the
specification of a synchronization mode:

I.e. instead of set_bit(x,y) we can do set_bit(x,y, mode).

The following modes are supported:

BMODE_NONE
	No synchronization at all. Non atomic version of bitmap operation.
	F.e. set_bit(x,y, MODE_NONE) == __set_bit(x,y)

BMODE_ATOMIC
	The operation is atomic but there is no guarantee how this
	operation is ordered respective to other memory operations.

BMODE_LOCK
	An atomic operation that is guaranteed to become visible before
	all subsequent memory accesses suitable for lock acquisition.

BMODE_UNLOCK
	An atomic operation that is guaranteed to become visible to
	other processors after all previos memory acceses and
	suitable for unlocking.

MODE_BARRIER
	An atomic operation that is guaranteed to become visible between
	previous and later memory operations.


For conveniences sake there are additional macros defined that
do not require an extra parameter. One can do

clear_bit_lock(x,y)
clear_bit_unlock(x,y)

and

clear_bit_barrier(x,y)

to get special synchronization behavior.

In order to utilize these extended bit operations one has to add

#include <asm/bitops_mode.h>


This patch only provides a generic implementation based on the
bit operations that are already universally supported. The use
of the generic definitions may not be optimal for many architectures.
An architecture may provide its own definitions for bitops with synchronization
modes by customizing include/asm-generic/bitmaps_mode.h and move it to
include/asm-xxxx/bitmaps_mode.h

Note that we are a long way from utilizing these synchronized bit modes
since they constitute a completely different locking paradigm from the
current barrier oriented scheme. In order for 

Signed-off-by: Christoph Lameter <clameter@sgi.com>

Index: linux-2.6.16-mm2/include/asm-generic/bitops_mode.h
===================================================================
--- /dev/null	1970-01-01 00:00:00.000000000 +0000
+++ linux-2.6.16-mm2/include/asm-generic/bitops_mode.h	2006-03-31 11:10:41.000000000 -0800
@@ -0,0 +1,245 @@
+#ifndef _ASM_GENERIC_BITOPS_MODE_H
+#define _ASM_GENERIC_BITOPS_MODE_H
+
+/*
+ * Copyright (C) 2006 Silicon Graphics, Incorporated
+ *	Christoph Lameter <christoph@lameter.com>
+ *
+ * Fallback logic for bit operations with synchronization mode
+ */
+
+#include <linux/compiler.h>
+#include <linux/types.h>
+#include <asm/intrinsics.h>
+
+#define BMODE_NONE 0
+#define BMODE_ATOMIC 1
+#define BMODE_LOCK 2
+#define BMODE_UNLOCK 3
+#define BMODE_BARRIER 4
+
+/**
+ * set_bit_mode - Set a bit in memory
+ *
+ * The address must be (at least) "long" aligned.
+ * Note that there are driver (e.g., eepro100) which use these operations to
+ * operate on hw-defined data-structures, so we can't easily change these
+ * operations to force a bigger alignment.
+ *
+ * bit 0 is the LSB of addr; bit 32 is the LSB of (addr+1).
+ */
+static __inline__ void
+set_bit_mode (int nr, void *addr, int mode)
+{
+	switch (mode) {
+	case BMODE_NONE:
+		__set_bit(nr,addr);
+		return;
+
+	case BMODE_ATOMIC:
+		set_bit(nr,addr);
+		return;
+
+	case BMODE_LOCK:
+		set_bit(nr,addr);
+		smp_mb();
+		return;
+
+	case BMODE_UNLOCK:
+		smp_mb();
+		set_bit(nr,addr);
+		return;
+
+	case BMODE_BARRIER:
+		smp_mb();
+		set_bit(nr,addr);
+		smp_mb();
+		return;
+	}
+}
+
+/**
+ * clear_bit_mode - Clears a bit in memory
+ */
+static __inline__ void
+clear_bit_mode (int nr, void *addr, int mode)
+{
+	switch (mode) {
+	case BMODE_NONE:
+		__clear_bit(nr,addr);
+		return;
+
+	case BMODE_ATOMIC:
+		clear_bit(nr,addr);
+		return;
+
+	case BMODE_LOCK:
+		clear_bit(nr,addr);
+		smp_mb();
+		return;
+
+	case BMODE_UNLOCK:
+		smp_mb();
+		clear_bit(nr,addr);
+		return;
+
+	case BMODE_BARRIER:
+		smp_mb();
+		clear_bit(nr,addr);
+		smp_mb();
+		return;
+	}
+}
+
+/**
+ * change_bit_mode - Toggle a bit in memory
+ */
+static __inline__ void
+change_bit_mode (int nr, void *addr, int mode)
+{
+	switch (mode) {
+	case BMODE_NONE:
+		__change_bit(nr,addr);
+		return;
+
+	case BMODE_ATOMIC:
+		change_bit(nr,addr);
+		return;
+
+	case BMODE_LOCK:
+		change_bit(nr,addr);
+		smp_mb();
+		return;
+
+	case BMODE_UNLOCK:
+		smp_mb();
+		change_bit(nr,addr);
+		return;
+
+	case BMODE_BARRIER:
+		smp_mb();
+		change_bit(nr,addr);
+		smp_mb();
+		return;
+	}
+}
+
+/**
+ * test_and_set_bit_mode - Set a bit and return its old value
+ */
+static __inline__ int
+test_and_set_bit_mode (int nr, void *addr, int mode)
+{
+	int x;
+	switch (mode) {
+	case BMODE_NONE:
+		return __test_and_set_bit(nr,addr);
+
+	case BMODE_ATOMIC:
+	 	return test_and_set_bit(nr,addr);
+
+	case BMODE_LOCK:
+		x = test_and_set_bit(nr,addr);
+		smp_mb();
+		return x;
+
+	case BMODE_UNLOCK:
+		smp_mb();
+		return test_and_set_bit(nr,addr);
+
+	case BMODE_BARRIER:
+		smp_mb();
+		x = test_and_set_bit(nr,addr);
+		smp_mb();
+		return x;
+	}
+}
+
+/**
+ * test_and_clear_bit - Clear a bit and return its old value
+ */
+static __inline__ int
+test_and_clear_bit_mode (int nr, void *addr, int mode)
+{
+	int x;
+	switch (mode) {
+	case BMODE_NONE:
+		return __test_and_clear_bit(nr,addr);
+
+	case BMODE_ATOMIC:
+	 	return test_and_clear_bit(nr,addr);
+
+	case BMODE_LOCK:
+		x = test_and_clear_bit(nr,addr);
+		smp_mb();
+		return x;
+
+	case BMODE_UNLOCK:
+		smp_mb();
+		return test_and_clear_bit(nr,addr);
+
+	case BMODE_BARRIER:
+		smp_mb();
+		x = test_and_set_bit(nr,addr);
+		smp_mb();
+		return x;
+	}
+}
+
+/**
+ * test_and_change_bit - Change a bit and return its old value
+ */
+static __inline__ int
+test_and_change_bit_mode (int nr, void *addr, int mode)
+{
+	int x;
+	switch (mode) {
+	case BMODE_NONE:
+		return __test_and_change_bit(nr,addr);
+
+	case BMODE_ATOMIC:
+	 	return test_and_change_bit(nr,addr);
+
+	case BMODE_LOCK:
+		x = test_and_change_bit(nr,addr);
+		smp_mb();
+		return x;
+
+	case BMODE_UNLOCK:
+		smp_mb();
+		return test_and_change_bit(nr,addr);
+
+	case BMODE_BARRIER:
+		smp_mb();
+		x = test_and_change_bit(nr,addr);
+		smp_mb();
+		return x;
+	}
+}
+
+/* A set of convenience definitions */
+#define set_bit_lock(a,b) set_bit_mode(a,b,BMODE_LOCK)
+#define set_bit_unlock(a,b) set_bit_mode(a,b,BMODE_UNLOCK)
+#define set_bit_barrier(a,b) set_bit_mode(a,b,BMODE_BARRIER)
+
+#define clear_bit_lock(a,b) clear_bit_mode(a,b,BMODE_LOCK)
+#define clear_bit_unlock(a,b) clear_bit_mode(a,b,BMODE_UNLOCK)
+#define clear_bit_barrier(a,b) clear_bit_mode(a,b,BMODE_BARRIER)
+
+#define change_bit_lock(a,b) change_bit_mode(a,b,BMODE_LOCK)
+#define change_bit_unlock(a,b) change_bit_mode(a,b,BMODE_UNLOCK)
+#define change_bit_barrier(a,b) change_bit_mode(a,b,BMODE_BARRIER)
+
+#define test_and_set_bit_lock(a,b) test_and_set_bit_mode(a,b,BMODE_LOCK)
+#define test_and_set_bit_unlock(a,b) test_and_set_bit_mode(a,b,BMODE_UNLOCK)
+#define test_and_set_bit_barrier(a,b) test_and_set_bit_mode(a,b,BMODE_BARRIER)
+
+#define test_and_clear_bit_lock(a,b) test_and_clear_bit_mode(a,b,BMODE_LOCK)
+#define test_and_clear_bit_unlock(a,b) test_and_clear_bit_mode(a,b,BMODE_UNLOCK)
+#define test_and_clear_bit_barrier(a,b) test_and_clear_bit_mode(a,b,BMODE_BARRIER)
+
+#define test_and_change_bit_lock(a,b) test_and_change_bit_mode(a,b,BMODE_LOCK)
+#define test_and_change_bit_unlock(a,b) test_and_change_bit_mode(a,b,BMODE_UNLOCK)
+#define test_and_change_bit_barrier(a,b) test_and_change_bit_mode(a,b,BMODE_BARRIER)
+
+#endif /* _ASM_GENERIC_BITOPS_MODE_H */
