summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--config/spl-build.m422
-rwxr-xr-xconfigure104
-rw-r--r--include/sys/atomic.h146
-rw-r--r--module/spl/spl-atomic.c6
-rw-r--r--spl_config.h.in3
5 files changed, 202 insertions, 79 deletions
diff --git a/config/spl-build.m4 b/config/spl-build.m4
index e6a626108..15d10841b 100644
--- a/config/spl-build.m4
+++ b/config/spl-build.m4
@@ -25,6 +25,7 @@ AC_DEFUN([SPL_AC_CONFIG_KERNEL], [
SPL_AC_DEBUG_KMEM
SPL_AC_DEBUG_KSTAT
SPL_AC_DEBUG_CALLB
+ SPL_AC_ATOMIC_SPINLOCK
SPL_AC_TYPE_UINTPTR_T
SPL_AC_TYPE_ATOMIC64_T
SPL_AC_3ARGS_INIT_WORK
@@ -303,6 +304,27 @@ AC_DEFUN([SPL_AC_DEBUG_CALLB], [
])
dnl #
+dnl # Use the atomic implemenation based on global spinlocks. This
+dnl # should never be needed, however it has been left in place as
+dnl # a fallback option in case problems are observed with directly
+dnl # mapping to the native Linux atomic operations.
+dnl #
+AC_DEFUN([SPL_AC_ATOMIC_SPINLOCK], [
+ AC_ARG_ENABLE([atomic-spinlocks],
+ [AS_HELP_STRING([--enable-atomic-spinlocks],
+ [Atomic types use spinlocks @<:@default=no@:>@])],
+ [],
+ [enable_atomic_spinlocks=no])
+
+ AS_IF([test "x$enable_atomic_spinlocks" = xyes],
+ [AC_DEFINE([ATOMIC_SPINLOCK], [1],
+ [Atomic types use spinlocks])])
+
+ AC_MSG_CHECKING([whether atomic types use spinlocks])
+ AC_MSG_RESULT([$enable_atomic_spinlocks])
+])
+
+dnl #
dnl # SPL_LINUX_CONFTEST
dnl #
AC_DEFUN([SPL_LINUX_CONFTEST], [
diff --git a/configure b/configure
index 8c8736883..bcbd0fb54 100755
--- a/configure
+++ b/configure
@@ -1038,6 +1038,8 @@ Optional Features:
--enable-debug-kmem Enable kmem debug support (default off)
--enable-debug-kstat Enable kstat debug support (default off)
--enable-debug-callb Enable callb debug support (default off)
+ --enable-atomic-spinlocks
+ Atomic types use spinlocks [default=no]
Optional Packages:
--with-PACKAGE[=ARG] use PACKAGE [ARG=yes]
@@ -3986,7 +3988,7 @@ ia64-*-hpux*)
;;
*-*-irix6*)
# Find out which ABI we are using.
- echo '#line 3989 "configure"' > conftest.$ac_ext
+ echo '#line 3991 "configure"' > conftest.$ac_ext
if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5
(eval $ac_compile) 2>&5
ac_status=$?
@@ -5585,7 +5587,7 @@ fi
# Provide some information about the compiler.
-echo "$as_me:5588:" \
+echo "$as_me:5590:" \
"checking for Fortran 77 compiler version" >&5
ac_compiler=`set X $ac_compile; echo $2`
{ (eval echo "$as_me:$LINENO: \"$ac_compiler --version </dev/null >&5\"") >&5
@@ -6648,11 +6650,11 @@ else
-e 's:.*FLAGS}\{0,1\} :&$lt_compiler_flag :; t' \
-e 's: [^ ]*conftest\.: $lt_compiler_flag&:; t' \
-e 's:$: $lt_compiler_flag:'`
- (eval echo "\"\$as_me:6651: $lt_compile\"" >&5)
+ (eval echo "\"\$as_me:6653: $lt_compile\"" >&5)
(eval "$lt_compile" 2>conftest.err)
ac_status=$?
cat conftest.err >&5
- echo "$as_me:6655: \$? = $ac_status" >&5
+ echo "$as_me:6657: \$? = $ac_status" >&5
if (exit $ac_status) && test -s "$ac_outfile"; then
# The compiler can only warn and ignore the option if not recognized
# So say no if there are warnings other than the usual output.
@@ -6916,11 +6918,11 @@ else
-e 's:.*FLAGS}\{0,1\} :&$lt_compiler_flag :; t' \
-e 's: [^ ]*conftest\.: $lt_compiler_flag&:; t' \
-e 's:$: $lt_compiler_flag:'`
- (eval echo "\"\$as_me:6919: $lt_compile\"" >&5)
+ (eval echo "\"\$as_me:6921: $lt_compile\"" >&5)
(eval "$lt_compile" 2>conftest.err)
ac_status=$?
cat conftest.err >&5
- echo "$as_me:6923: \$? = $ac_status" >&5
+ echo "$as_me:6925: \$? = $ac_status" >&5
if (exit $ac_status) && test -s "$ac_outfile"; then
# The compiler can only warn and ignore the option if not recognized
# So say no if there are warnings other than the usual output.
@@ -7020,11 +7022,11 @@ else
-e 's:.*FLAGS}\{0,1\} :&$lt_compiler_flag :; t' \
-e 's: [^ ]*conftest\.: $lt_compiler_flag&:; t' \
-e 's:$: $lt_compiler_flag:'`
- (eval echo "\"\$as_me:7023: $lt_compile\"" >&5)
+ (eval echo "\"\$as_me:7025: $lt_compile\"" >&5)
(eval "$lt_compile" 2>out/conftest.err)
ac_status=$?
cat out/conftest.err >&5
- echo "$as_me:7027: \$? = $ac_status" >&5
+ echo "$as_me:7029: \$? = $ac_status" >&5
if (exit $ac_status) && test -s out/conftest2.$ac_objext
then
# The compiler can only warn and ignore the option if not recognized
@@ -8489,7 +8491,7 @@ linux*)
libsuff=
case "$host_cpu" in
x86_64*|s390x*|powerpc64*)
- echo '#line 8492 "configure"' > conftest.$ac_ext
+ echo '#line 8494 "configure"' > conftest.$ac_ext
if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5
(eval $ac_compile) 2>&5
ac_status=$?
@@ -9386,7 +9388,7 @@ else
lt_dlunknown=0; lt_dlno_uscore=1; lt_dlneed_uscore=2
lt_status=$lt_dlunknown
cat > conftest.$ac_ext <<EOF
-#line 9389 "configure"
+#line 9391 "configure"
#include "confdefs.h"
#if HAVE_DLFCN_H
@@ -9486,7 +9488,7 @@ else
lt_dlunknown=0; lt_dlno_uscore=1; lt_dlneed_uscore=2
lt_status=$lt_dlunknown
cat > conftest.$ac_ext <<EOF
-#line 9489 "configure"
+#line 9491 "configure"
#include "confdefs.h"
#if HAVE_DLFCN_H
@@ -11829,11 +11831,11 @@ else
-e 's:.*FLAGS}\{0,1\} :&$lt_compiler_flag :; t' \
-e 's: [^ ]*conftest\.: $lt_compiler_flag&:; t' \
-e 's:$: $lt_compiler_flag:'`
- (eval echo "\"\$as_me:11832: $lt_compile\"" >&5)
+ (eval echo "\"\$as_me:11834: $lt_compile\"" >&5)
(eval "$lt_compile" 2>conftest.err)
ac_status=$?
cat conftest.err >&5
- echo "$as_me:11836: \$? = $ac_status" >&5
+ echo "$as_me:11838: \$? = $ac_status" >&5
if (exit $ac_status) && test -s "$ac_outfile"; then
# The compiler can only warn and ignore the option if not recognized
# So say no if there are warnings other than the usual output.
@@ -11933,11 +11935,11 @@ else
-e 's:.*FLAGS}\{0,1\} :&$lt_compiler_flag :; t' \
-e 's: [^ ]*conftest\.: $lt_compiler_flag&:; t' \
-e 's:$: $lt_compiler_flag:'`
- (eval echo "\"\$as_me:11936: $lt_compile\"" >&5)
+ (eval echo "\"\$as_me:11938: $lt_compile\"" >&5)
(eval "$lt_compile" 2>out/conftest.err)
ac_status=$?
cat out/conftest.err >&5
- echo "$as_me:11940: \$? = $ac_status" >&5
+ echo "$as_me:11942: \$? = $ac_status" >&5
if (exit $ac_status) && test -s out/conftest2.$ac_objext
then
# The compiler can only warn and ignore the option if not recognized
@@ -12469,7 +12471,7 @@ linux*)
libsuff=
case "$host_cpu" in
x86_64*|s390x*|powerpc64*)
- echo '#line 12472 "configure"' > conftest.$ac_ext
+ echo '#line 12474 "configure"' > conftest.$ac_ext
if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5
(eval $ac_compile) 2>&5
ac_status=$?
@@ -13527,11 +13529,11 @@ else
-e 's:.*FLAGS}\{0,1\} :&$lt_compiler_flag :; t' \
-e 's: [^ ]*conftest\.: $lt_compiler_flag&:; t' \
-e 's:$: $lt_compiler_flag:'`
- (eval echo "\"\$as_me:13530: $lt_compile\"" >&5)
+ (eval echo "\"\$as_me:13532: $lt_compile\"" >&5)
(eval "$lt_compile" 2>conftest.err)
ac_status=$?
cat conftest.err >&5
- echo "$as_me:13534: \$? = $ac_status" >&5
+ echo "$as_me:13536: \$? = $ac_status" >&5
if (exit $ac_status) && test -s "$ac_outfile"; then
# The compiler can only warn and ignore the option if not recognized
# So say no if there are warnings other than the usual output.
@@ -13631,11 +13633,11 @@ else
-e 's:.*FLAGS}\{0,1\} :&$lt_compiler_flag :; t' \
-e 's: [^ ]*conftest\.: $lt_compiler_flag&:; t' \
-e 's:$: $lt_compiler_flag:'`
- (eval echo "\"\$as_me:13634: $lt_compile\"" >&5)
+ (eval echo "\"\$as_me:13636: $lt_compile\"" >&5)
(eval "$lt_compile" 2>out/conftest.err)
ac_status=$?
cat out/conftest.err >&5
- echo "$as_me:13638: \$? = $ac_status" >&5
+ echo "$as_me:13640: \$? = $ac_status" >&5
if (exit $ac_status) && test -s out/conftest2.$ac_objext
then
# The compiler can only warn and ignore the option if not recognized
@@ -15080,7 +15082,7 @@ linux*)
libsuff=
case "$host_cpu" in
x86_64*|s390x*|powerpc64*)
- echo '#line 15083 "configure"' > conftest.$ac_ext
+ echo '#line 15085 "configure"' > conftest.$ac_ext
if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5
(eval $ac_compile) 2>&5
ac_status=$?
@@ -15858,11 +15860,11 @@ else
-e 's:.*FLAGS}\{0,1\} :&$lt_compiler_flag :; t' \
-e 's: [^ ]*conftest\.: $lt_compiler_flag&:; t' \
-e 's:$: $lt_compiler_flag:'`
- (eval echo "\"\$as_me:15861: $lt_compile\"" >&5)
+ (eval echo "\"\$as_me:15863: $lt_compile\"" >&5)
(eval "$lt_compile" 2>conftest.err)
ac_status=$?
cat conftest.err >&5
- echo "$as_me:15865: \$? = $ac_status" >&5
+ echo "$as_me:15867: \$? = $ac_status" >&5
if (exit $ac_status) && test -s "$ac_outfile"; then
# The compiler can only warn and ignore the option if not recognized
# So say no if there are warnings other than the usual output.
@@ -16126,11 +16128,11 @@ else
-e 's:.*FLAGS}\{0,1\} :&$lt_compiler_flag :; t' \
-e 's: [^ ]*conftest\.: $lt_compiler_flag&:; t' \
-e 's:$: $lt_compiler_flag:'`
- (eval echo "\"\$as_me:16129: $lt_compile\"" >&5)
+ (eval echo "\"\$as_me:16131: $lt_compile\"" >&5)
(eval "$lt_compile" 2>conftest.err)
ac_status=$?
cat conftest.err >&5
- echo "$as_me:16133: \$? = $ac_status" >&5
+ echo "$as_me:16135: \$? = $ac_status" >&5
if (exit $ac_status) && test -s "$ac_outfile"; then
# The compiler can only warn and ignore the option if not recognized
# So say no if there are warnings other than the usual output.
@@ -16230,11 +16232,11 @@ else
-e 's:.*FLAGS}\{0,1\} :&$lt_compiler_flag :; t' \
-e 's: [^ ]*conftest\.: $lt_compiler_flag&:; t' \
-e 's:$: $lt_compiler_flag:'`
- (eval echo "\"\$as_me:16233: $lt_compile\"" >&5)
+ (eval echo "\"\$as_me:16235: $lt_compile\"" >&5)
(eval "$lt_compile" 2>out/conftest.err)
ac_status=$?
cat out/conftest.err >&5
- echo "$as_me:16237: \$? = $ac_status" >&5
+ echo "$as_me:16239: \$? = $ac_status" >&5
if (exit $ac_status) && test -s out/conftest2.$ac_objext
then
# The compiler can only warn and ignore the option if not recognized
@@ -17699,7 +17701,7 @@ linux*)
libsuff=
case "$host_cpu" in
x86_64*|s390x*|powerpc64*)
- echo '#line 17702 "configure"' > conftest.$ac_ext
+ echo '#line 17704 "configure"' > conftest.$ac_ext
if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5
(eval $ac_compile) 2>&5
ac_status=$?
@@ -19203,6 +19205,29 @@ _ACEOF
echo "${ECHO_T}no" >&6
fi
+
+ # Check whether --enable-atomic-spinlocks or --disable-atomic-spinlocks was given.
+if test "${enable_atomic_spinlocks+set}" = set; then
+ enableval="$enable_atomic_spinlocks"
+
+else
+ enable_atomic_spinlocks=no
+fi;
+
+ if test "x$enable_atomic_spinlocks" = xyes; then
+
+cat >>confdefs.h <<\_ACEOF
+#define ATOMIC_SPINLOCK 1
+_ACEOF
+
+fi
+
+
+ echo "$as_me:$LINENO: checking whether atomic types use spinlocks" >&5
+echo $ECHO_N "checking whether atomic types use spinlocks... $ECHO_C" >&6
+ echo "$as_me:$LINENO: result: $enable_atomic_spinlocks" >&5
+echo "${ECHO_T}$enable_atomic_spinlocks" >&6
+
echo "$as_me:$LINENO: checking whether kernel defines uintptr_t" >&5
echo $ECHO_N "checking whether kernel defines uintptr_t... $ECHO_C" >&6
@@ -22351,6 +22376,29 @@ _ACEOF
echo "${ECHO_T}no" >&6
fi
+
+ # Check whether --enable-atomic-spinlocks or --disable-atomic-spinlocks was given.
+if test "${enable_atomic_spinlocks+set}" = set; then
+ enableval="$enable_atomic_spinlocks"
+
+else
+ enable_atomic_spinlocks=no
+fi;
+
+ if test "x$enable_atomic_spinlocks" = xyes; then
+
+cat >>confdefs.h <<\_ACEOF
+#define ATOMIC_SPINLOCK 1
+_ACEOF
+
+fi
+
+
+ echo "$as_me:$LINENO: checking whether atomic types use spinlocks" >&5
+echo $ECHO_N "checking whether atomic types use spinlocks... $ECHO_C" >&6
+ echo "$as_me:$LINENO: result: $enable_atomic_spinlocks" >&5
+echo "${ECHO_T}$enable_atomic_spinlocks" >&6
+
echo "$as_me:$LINENO: checking whether kernel defines uintptr_t" >&5
echo $ECHO_N "checking whether kernel defines uintptr_t... $ECHO_C" >&6
diff --git a/include/sys/atomic.h b/include/sys/atomic.h
index cd0eb3b0a..4f4a1e058 100644
--- a/include/sys/atomic.h
+++ b/include/sys/atomic.h
@@ -27,31 +27,94 @@
#ifndef _SPL_ATOMIC_H
#define _SPL_ATOMIC_H
-#ifdef __cplusplus
-extern "C" {
-#endif
-
#include <linux/module.h>
#include <linux/spinlock.h>
-#include <sys/isa_defs.h>
+#include <sys/types.h>
-/* XXX: Serialize everything through global locks. This is
- * going to be bad for performance, but for now it's the easiest
- * way to ensure correct behavior. I don't like it at all.
- * It would be nicer to make these function to the atomic linux
- * functions, but the normal uint64_t type complicates this.
+/*
+ * Two approaches to atomic operations are implemented each with its
+ * own benefits are drawbacks imposed by the Solaris API. Neither
+ * approach handles the issue of word breaking when using a 64-bit
+ * atomic variable on a 32-bit arch. The Solaris API would need to
+ * add an atomic read call to correctly support this.
+ *
+ * When ATOMIC_SPINLOCK is defined all atomic operations will be
+ * serialized through global spin locks. This is bad for performance
+ * but it does allow a simple generic implementation.
+ *
+ * When ATOMIC_SPINLOCK is not defined the Linux atomic operations
+ * are used. This is safe as long as the core Linux implementation
+ * doesn't change because we are relying on the fact that an atomic
+ * type is really just a uint32 or uint64. If this changes at some
+ * point in the future we need to fall-back to the spin approach.
*/
-extern spinlock_t atomic64_lock;
+#ifdef ATOMIC_SPINLOCK
extern spinlock_t atomic32_lock;
+extern spinlock_t atomic64_lock;
-static __inline__ uint32_t
+static __inline__ void
+atomic_inc_32(volatile uint32_t *target)
+{
+ spin_lock(&atomic32_lock);
+ (*target)++;
+ spin_unlock(&atomic32_lock);
+}
+
+static __inline__ void
+atomic_dec_32(volatile uint32_t *target)
+{
+ spin_lock(&atomic32_lock);
+ (*target)--;
+ spin_unlock(&atomic32_lock);
+}
+
+static __inline__ void
atomic_add_32(volatile uint32_t *target, int32_t delta)
{
+ spin_lock(&atomic32_lock);
+ *target += delta;
+ spin_unlock(&atomic32_lock);
+}
+
+static __inline__ void
+atomic_sub_32(volatile uint32_t *target, int32_t delta)
+{
+ spin_lock(&atomic32_lock);
+ *target -= delta;
+ spin_unlock(&atomic32_lock);
+}
+
+static __inline__ uint32_t
+atomic_add_32_nv(volatile uint32_t *target, uint32_t delta)
+{
+ spin_lock(&atomic32_lock);
+ *target += delta;
+ spin_unlock(&atomic32_lock);
+
+ return *target;
+}
+
+static __inline__ uint32_t
+atomic_sub_32_nv(volatile uint32_t *target, uint32_t delta)
+{
+ spin_lock(&atomic32_lock);
+ *target -= delta;
+ spin_unlock(&atomic32_lock);
+
+ return *target;
+}
+
+static __inline__ uint32_t
+atomic_cas_32(volatile uint32_t *target, uint32_t cmp,
+ uint32_t newval)
+{
uint32_t rc;
spin_lock(&atomic32_lock);
rc = *target;
- *target += delta;
+ if (*target == cmp)
+ *target = newval;
+
spin_unlock(&atomic32_lock);
return rc;
@@ -73,30 +136,20 @@ atomic_dec_64(volatile uint64_t *target)
spin_unlock(&atomic64_lock);
}
-static __inline__ uint64_t
+static __inline__ void
atomic_add_64(volatile uint64_t *target, uint64_t delta)
{
- uint64_t rc;
-
spin_lock(&atomic64_lock);
- rc = *target;
*target += delta;
spin_unlock(&atomic64_lock);
-
- return rc;
}
-static __inline__ uint64_t
+static __inline__ void
atomic_sub_64(volatile uint64_t *target, uint64_t delta)
{
- uint64_t rc;
-
spin_lock(&atomic64_lock);
- rc = *target;
*target -= delta;
spin_unlock(&atomic64_lock);
-
- return rc;
}
static __inline__ uint64_t
@@ -121,7 +174,7 @@ atomic_sub_64_nv(volatile uint64_t *target, uint64_t delta)
static __inline__ uint64_t
atomic_cas_64(volatile uint64_t *target, uint64_t cmp,
- uint64_t newval)
+ uint64_t newval)
{
uint64_t rc;
@@ -134,45 +187,40 @@ atomic_cas_64(volatile uint64_t *target, uint64_t cmp,
return rc;
}
-static __inline__ uint32_t
-atomic_cas_32(volatile uint32_t *target, uint32_t cmp,
- uint32_t newval)
-{
- uint32_t rc;
- spin_lock(&atomic32_lock);
- rc = *target;
- if (*target == cmp)
- *target = newval;
+#else /* ATOMIC_SPINLOCK */
- spin_unlock(&atomic32_lock);
+#define atomic_inc_32(v) atomic_inc((atomic_t *)(v))
+#define atomic_dec_32(v) atomic_dec((atomic_t *)(v))
+#define atomic_add_32(v, i) atomic_add((i), (atomic_t *)(v))
+#define atomic_sub_32(v, i) atomic_sub((i), (atomic_t *)(v))
+#define atomic_add_32_nv(v, i) atomic_add_return((i), (atomic_t *)(v))
+#define atomic_sub_32_nv(v, i) atomic_sub_return((i), (atomic_t *)(v))
+#define atomic_cas_32(v, x, y) atomic_cmpxchg((atomic_t *)(v), x, y)
+#define atomic_inc_64(v) atomic64_inc((atomic64_t *)(v))
+#define atomic_dec_64(v) atomic64_dec((atomic64_t *)(v))
+#define atomic_add_64(v, i) atomic64_add((i), (atomic64_t *)(v))
+#define atomic_sub_64(v, i) atomic64_sub((i), (atomic64_t *)(v))
+#define atomic_add_64_nv(v, i) atomic64_add_return((i), (atomic64_t *)(v))
+#define atomic_sub_64_nv(v, i) atomic64_sub_return((i), (atomic64_t *)(v))
+#define atomic_cas_64(v, x, y) atomic64_cmpxchg((atomic64_t *)(v), x, y)
- return rc;
-}
+#endif /* ATOMIC_SPINLOCK */
#ifdef _LP64
-/* XXX: Implement atomic_cas_ptr() in terms of uint64'ts. This
- * is of course only safe and correct for 64 bit arches... but
- * for now I'm OK with that.
- */
static __inline__ void *
atomic_cas_ptr(volatile void *target, void *cmp, void *newval)
{
return (void *)atomic_cas_64((volatile uint64_t *)target,
(uint64_t)cmp, (uint64_t)newval);
}
-#else
+#else /* _LP64 */
static __inline__ void *
atomic_cas_ptr(volatile void *target, void *cmp, void *newval)
{
return (void *)atomic_cas_32((volatile uint32_t *)target,
(uint32_t)cmp, (uint32_t)newval);
}
-#endif
-
-#ifdef __cplusplus
-}
-#endif
+#endif /* _LP64 */
#endif /* _SPL_ATOMIC_H */
-
diff --git a/module/spl/spl-atomic.c b/module/spl/spl-atomic.c
index 40cdb06cc..decf9515e 100644
--- a/module/spl/spl-atomic.c
+++ b/module/spl/spl-atomic.c
@@ -32,9 +32,11 @@
#define DEBUG_SUBSYSTEM S_ATOMIC
+#ifdef ATOMIC_SPINLOCK
/* Global atomic lock declarations */
-spinlock_t atomic64_lock = SPIN_LOCK_UNLOCKED;
spinlock_t atomic32_lock = SPIN_LOCK_UNLOCKED;
+spinlock_t atomic64_lock = SPIN_LOCK_UNLOCKED;
-EXPORT_SYMBOL(atomic64_lock);
EXPORT_SYMBOL(atomic32_lock);
+EXPORT_SYMBOL(atomic64_lock);
+#endif /* ATOMIC_SPINLOCK */
diff --git a/spl_config.h.in b/spl_config.h.in
index 176da466b..feb09a865 100644
--- a/spl_config.h.in
+++ b/spl_config.h.in
@@ -1,5 +1,8 @@
/* spl_config.h.in. Generated from configure.ac by autoheader. */
+/* Atomic types use spinlocks */
+#undef ATOMIC_SPINLOCK
+
/* Define to 1 to enable callb debugging */
#undef DEBUG_CALLB