Skip site navigation (1)Skip section navigation (2)
Date:      Tue, 30 Jul 2013 15:56:36 -0400
From:      Jung-uk Kim <jkim@FreeBSD.org>
To:        freebsd-arch@FreeBSD.org
Cc:        kib@FreeBSD.org, dumbbell@FreeBSD.org
Subject:   [PATCH] Atomic swap and test-and-set
Message-ID:  <51F81A74.4030009@FreeBSD.org>

next in thread | raw e-mail | index | archive | help
This is a multi-part message in MIME format.
--------------050506090501030302020606
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 7bit

-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA1

The attached patch implements atomic_swap_<type>() and
atomic_testandset_<type>() for x86.  <type> is int or long ATM.

The semantics of each operation (in atomic(9) style):

atomic_swap(p, v)
	r = *p;
	*p = v;
	return (r);

atomic_testandset(p, s)
	v = (<type>)1 << s % (sizeof(<type>) * NBBY);
	r = (*p & v) != 0;
	*p |= v;
	return (r);

Note atomic_readandclear_<type>() is now obsolete and implemented as a
macro around atomic_swap_<type>() where the new value v is 0.

Please let me know if you have any objection.

Thanks!

Jung-uk Kim

* Note 1: This patch is also available from here.

http://people.freebsd.org/~jkim/atomic.diff

* Note 2: These atomic ops are useful for porting Linux drivers and it
may simplify diffs, e.g., drm2 for AMD GPUs project.  Currently, I am
using the following wrapper:

http://people.freebsd.org/~jkim/drm_atomic.h

A full patch against kms-radeon tree is here:

http://people.freebsd.org/~jkim/drm_atomic.diff
-----BEGIN PGP SIGNATURE-----
Version: GnuPG v2.0.20 (FreeBSD)

iQEcBAEBAgAGBQJR+Bp0AAoJECXpabHZMqHOpIsIAMqEjNehGRe7q7B7CPC2cqdY
aEWiRlKXLa0K3Vi2f9asYFlbhIm+PstV8Juhw2ijT/P3i55xoQTc4DqP6H4ISUgK
pKM8TLAFJWlItf3o8HV/q7Tns/iaqb8cnB0D2jLUtheeM8UTXEim+XOSWILL+csR
zuXGDSDGC0vTxip00ocPUdtdG/Y5Sg/Eq+5CZYp58DSoEBDfuKhGJc3xrC6QrMGx
zwBkmAp5escfFkyE1WLI99tJevrQZpqJViD+66dDcLW56w4Aqpqib69sMMEs/meJ
KrwKRwt9+kgvwEFiDGXzDsjVfcTky9Cb8zn3jbBrGxtNxd4aHgCNY1xeG98kHaM=
=6LmO
-----END PGP SIGNATURE-----

--------------050506090501030302020606
Content-Type: text/x-patch;
 name="atomic.diff"
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
 filename="atomic.diff"

Index: sys/amd64/include/atomic.h
===================================================================
--- sys/amd64/include/atomic.h	(revision 253812)
+++ sys/amd64/include/atomic.h	(working copy)
@@ -54,6 +54,7 @@
  * atomic_clear_int(P, V)	(*(u_int *)(P) &= ~(V))
  * atomic_add_int(P, V)		(*(u_int *)(P) += (V))
  * atomic_subtract_int(P, V)	(*(u_int *)(P) -= (V))
+ * atomic_swap_int(P, V)	(return (*(u_int *)(P)); *(u_int *)(P) = (V);)
  * atomic_readandclear_int(P)	(return (*(u_int *)(P)); *(u_int *)(P) = 0;)
  *
  * atomic_set_long(P, V)	(*(u_long *)(P) |= (V))
@@ -60,6 +61,7 @@
  * atomic_clear_long(P, V)	(*(u_long *)(P) &= ~(V))
  * atomic_add_long(P, V)	(*(u_long *)(P) += (V))
  * atomic_subtract_long(P, V)	(*(u_long *)(P) -= (V))
+ * atomic_swap_long(P, V)	(return (*(u_long *)(P)); *(u_long *)(P) = (V);)
  * atomic_readandclear_long(P)	(return (*(u_long *)(P)); *(u_long *)(P) = 0;)
  */
 
@@ -80,6 +82,8 @@ int	atomic_cmpset_int(volatile u_int *dst, u_int e
 int	atomic_cmpset_long(volatile u_long *dst, u_long expect, u_long src);
 u_int	atomic_fetchadd_int(volatile u_int *p, u_int v);
 u_long	atomic_fetchadd_long(volatile u_long *p, u_long v);
+int	atomic_testandset_int(volatile u_int *p, int v);
+int	atomic_testandset_long(volatile u_long *p, int v);
 
 #define	ATOMIC_LOAD(TYPE, LOP)					\
 u_##TYPE	atomic_load_acq_##TYPE(volatile u_##TYPE *p)
@@ -211,6 +215,44 @@ atomic_fetchadd_long(volatile u_long *p, u_long v)
 	return (v);
 }
 
+static __inline int
+atomic_testandset_int(volatile u_int *p, int v)
+{
+	u_char res;
+
+	__asm __volatile(
+	"	" MPLOCKED "		"
+	"	btsl	%2, %1 ;	"
+	"	setc	%0 ;		"
+	"# atomic_testandset_int"
+	: "=r" (res),			/* 0 */
+	  "=m" (*p)			/* 1 */
+	: "r" (v),			/* 2 */
+	  "m" (*p)
+	: "cc");
+
+	return (res);
+}
+
+static __inline int
+atomic_testandset_long(volatile u_long *p, int v)
+{
+	u_char res;
+
+	__asm __volatile(
+	"	" MPLOCKED "		"
+	"	btsq	%2, %1 ;	"
+	"	setc	%0 ;		"
+	"# atomic_testandset_long"
+	: "=r" (res),			/* 0 */
+	  "=m" (*p)			/* 1 */
+	: "r" ((u_long)v),		/* 2 */
+	  "m" (*p)
+	: "cc");
+
+	return (res);
+}
+
 /*
  * We assume that a = b will do atomic loads and stores.  Due to the
  * IA32 memory model, a simple store guarantees release semantics.
@@ -303,45 +345,41 @@ ATOMIC_STORE(long);
 
 #ifndef WANT_FUNCTIONS
 
-/* Read the current value and store a zero in the destination. */
+/* Read the current value and store a new value in the destination. */
 #ifdef __GNUCLIKE_ASM
 
 static __inline u_int
-atomic_readandclear_int(volatile u_int *addr)
+atomic_swap_int(volatile u_int *p, u_int v)
 {
-	u_int res;
 
-	res = 0;
 	__asm __volatile(
-	"	xchgl	%1,%0 ;		"
-	"# atomic_readandclear_int"
-	: "+r" (res),			/* 0 */
-	  "=m" (*addr)			/* 1 */
-	: "m" (*addr));
+	"	xchgl	%1, %0 ;	"
+	"# atomic_swap_int"
+	: "+r" (v),			/* 0 */
+	  "=m" (*p)			/* 1 */
+	: "m" (*p));
 
-	return (res);
+	return (v);
 }
 
 static __inline u_long
-atomic_readandclear_long(volatile u_long *addr)
+atomic_swap_long(volatile u_long *p, u_long v)
 {
-	u_long res;
 
-	res = 0;
 	__asm __volatile(
-	"	xchgq	%1,%0 ;		"
-	"# atomic_readandclear_long"
-	: "+r" (res),			/* 0 */
-	  "=m" (*addr)			/* 1 */
-	: "m" (*addr));
+	"	xchgq	%1, %0 ;	"
+	"# atomic_swap_long"
+	: "+r" (v),			/* 0 */
+	  "=m" (*p)			/* 1 */
+	: "m" (*p));
 
-	return (res);
+	return (v);
 }
 
 #else /* !__GNUCLIKE_ASM */
 
-u_int	atomic_readandclear_int(volatile u_int *addr);
-u_long	atomic_readandclear_long(volatile u_long *addr);
+u_int	atomic_swap_int(volatile u_int *, u_int);
+u_long	atomic_swap_long(volatile u_long *, u_long);
 
 #endif /* __GNUCLIKE_ASM */
 
@@ -385,6 +423,9 @@ static __inline u_long
 #define	atomic_cmpset_acq_long		atomic_cmpset_long
 #define	atomic_cmpset_rel_long		atomic_cmpset_long
 
+#define	atomic_readandclear_int(p)	atomic_swap_int(p, 0)
+#define	atomic_readandclear_long(p)	atomic_swap_long(p, 0)
+
 /* Operations on 8-bit bytes. */
 #define	atomic_set_8		atomic_set_char
 #define	atomic_set_acq_8	atomic_set_acq_char
@@ -435,8 +476,10 @@ static __inline u_long
 #define	atomic_cmpset_32	atomic_cmpset_int
 #define	atomic_cmpset_acq_32	atomic_cmpset_acq_int
 #define	atomic_cmpset_rel_32	atomic_cmpset_rel_int
+#define	atomic_swap_32		atomic_swap_int
 #define	atomic_readandclear_32	atomic_readandclear_int
 #define	atomic_fetchadd_32	atomic_fetchadd_int
+#define	atomic_testandset_32	atomic_testandset_int
 
 /* Operations on 64-bit quad words. */
 #define	atomic_set_64		atomic_set_long
@@ -456,7 +499,9 @@ static __inline u_long
 #define	atomic_cmpset_64	atomic_cmpset_long
 #define	atomic_cmpset_acq_64	atomic_cmpset_acq_long
 #define	atomic_cmpset_rel_64	atomic_cmpset_rel_long
+#define	atomic_swap_64		atomic_swap_long
 #define	atomic_readandclear_64	atomic_readandclear_long
+#define	atomic_testandset_64	atomic_testandset_long
 
 /* Operations on pointers. */
 #define	atomic_set_ptr		atomic_set_long
@@ -476,6 +521,7 @@ static __inline u_long
 #define	atomic_cmpset_ptr	atomic_cmpset_long
 #define	atomic_cmpset_acq_ptr	atomic_cmpset_acq_long
 #define	atomic_cmpset_rel_ptr	atomic_cmpset_rel_long
+#define	atomic_swap_ptr		atomic_swap_long
 #define	atomic_readandclear_ptr	atomic_readandclear_long
 
 #endif /* !WANT_FUNCTIONS */
Index: sys/i386/include/atomic.h
===================================================================
--- sys/i386/include/atomic.h	(revision 253812)
+++ sys/i386/include/atomic.h	(working copy)
@@ -54,6 +54,7 @@
  * atomic_clear_int(P, V)	(*(u_int *)(P) &= ~(V))
  * atomic_add_int(P, V)		(*(u_int *)(P) += (V))
  * atomic_subtract_int(P, V)	(*(u_int *)(P) -= (V))
+ * atomic_swap_int(P, V)	(return (*(u_int *)(P)); *(u_int *)(P) = (V);)
  * atomic_readandclear_int(P)	(return (*(u_int *)(P)); *(u_int *)(P) = 0;)
  *
  * atomic_set_long(P, V)	(*(u_long *)(P) |= (V))
@@ -60,6 +61,7 @@
  * atomic_clear_long(P, V)	(*(u_long *)(P) &= ~(V))
  * atomic_add_long(P, V)	(*(u_long *)(P) += (V))
  * atomic_subtract_long(P, V)	(*(u_long *)(P) -= (V))
+ * atomic_swap_long(P, V)	(return (*(u_long *)(P)); *(u_long *)(P) = (V);)
  * atomic_readandclear_long(P)	(return (*(u_long *)(P)); *(u_long *)(P) = 0;)
  */
 
@@ -78,6 +80,7 @@ void atomic_##NAME##_barr_##TYPE(volatile u_##TYPE
 
 int	atomic_cmpset_int(volatile u_int *dst, u_int expect, u_int src);
 u_int	atomic_fetchadd_int(volatile u_int *p, u_int v);
+int	atomic_testandset_int(volatile u_int *p, int v);
 
 #define	ATOMIC_LOAD(TYPE, LOP)					\
 u_##TYPE	atomic_load_acq_##TYPE(volatile u_##TYPE *p)
@@ -281,6 +284,25 @@ atomic_fetchadd_int(volatile u_int *p, u_int v)
 	return (v);
 }
 
+static __inline int
+atomic_testandset_int(volatile u_int *p, int v)
+{
+	u_char res;
+
+	__asm __volatile(
+	"	" MPLOCKED "		"
+	"	btsl	%2, %1 ;	"
+	"	setc	%0 ;		"
+	"# atomic_testandset_int"
+	: "=r" (res),			/* 0 */
+	  "=m" (*p)			/* 1 */
+	: "r" (v),			/* 2 */
+	  "m" (*p)
+	: "cc");
+
+	return (res);
+}
+
 /*
  * We assume that a = b will do atomic loads and stores.  Due to the
  * IA32 memory model, a simple store guarantees release semantics.
@@ -393,45 +415,41 @@ atomic_fetchadd_long(volatile u_long *p, u_long v)
 	return (atomic_fetchadd_int((volatile u_int *)p, (u_int)v));
 }
 
-/* Read the current value and store a zero in the destination. */
+static __inline int
+atomic_testandset_long(volatile u_long *p, int v)
+{
+
+	return (atomic_testandset_int((volatile u_int *)p, v));
+}
+
+/* Read the current value and store a new value in the destination. */
 #ifdef __GNUCLIKE_ASM
 
 static __inline u_int
-atomic_readandclear_int(volatile u_int *addr)
+atomic_swap_int(volatile u_int *p, u_int v)
 {
-	u_int res;
 
-	res = 0;
 	__asm __volatile(
-	"	xchgl	%1,%0 ;		"
-	"# atomic_readandclear_int"
-	: "+r" (res),			/* 0 */
-	  "=m" (*addr)			/* 1 */
-	: "m" (*addr));
+	"	xchgl	%1, %0 ;	"
+	"# atomic_swap_int"
+	: "+r" (v),			/* 0 */
+	  "=m" (*p)			/* 1 */
+	: "m" (*p));
 
-	return (res);
+	return (v);
 }
 
 static __inline u_long
-atomic_readandclear_long(volatile u_long *addr)
+atomic_swap_long(volatile u_long *p, u_long v)
 {
-	u_long res;
 
-	res = 0;
-	__asm __volatile(
-	"	xchgl	%1,%0 ;		"
-	"# atomic_readandclear_long"
-	: "+r" (res),			/* 0 */
-	  "=m" (*addr)			/* 1 */
-	: "m" (*addr));
-
-	return (res);
+	return (atomic_swap_int((volatile u_int *)p, (u_int)v));
 }
 
 #else /* !__GNUCLIKE_ASM */
 
-u_int	atomic_readandclear_int(volatile u_int *addr);
-u_long	atomic_readandclear_long(volatile u_long *addr);
+u_int	atomic_swap_int(volatile u_int *, u_int);
+u_long	atomic_swap_long(volatile u_long *, u_long);
 
 #endif /* __GNUCLIKE_ASM */
 
@@ -475,6 +493,9 @@ static __inline u_long
 #define	atomic_cmpset_acq_long		atomic_cmpset_long
 #define	atomic_cmpset_rel_long		atomic_cmpset_long
 
+#define	atomic_readandclear_int(p)	atomic_swap_int(p, 0)
+#define	atomic_readandclear_long(p)	atomic_swap_long(p, 0)
+
 /* Operations on 8-bit bytes. */
 #define	atomic_set_8		atomic_set_char
 #define	atomic_set_acq_8	atomic_set_acq_char
@@ -525,8 +546,10 @@ static __inline u_long
 #define	atomic_cmpset_32	atomic_cmpset_int
 #define	atomic_cmpset_acq_32	atomic_cmpset_acq_int
 #define	atomic_cmpset_rel_32	atomic_cmpset_rel_int
+#define	atomic_swap_32		atomic_swap_int
 #define	atomic_readandclear_32	atomic_readandclear_int
 #define	atomic_fetchadd_32	atomic_fetchadd_int
+#define	atomic_testandset_32	atomic_testandset_int
 
 /* Operations on pointers. */
 #define	atomic_set_ptr(p, v) \
@@ -565,6 +588,8 @@ static __inline u_long
 #define	atomic_cmpset_rel_ptr(dst, old, new) \
 	atomic_cmpset_rel_int((volatile u_int *)(dst), (u_int)(old), \
 	    (u_int)(new))
+#define	atomic_swap_ptr(p, v) \
+	atomic_swap_int((volatile u_int *)(p), (u_int)(v))
 #define	atomic_readandclear_ptr(p) \
 	atomic_readandclear_int((volatile u_int *)(p))
 

--------------050506090501030302020606--



Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?51F81A74.4030009>