diff --git a/config/spl-build.m4 b/config/spl-build.m4 index facaf740..0b9f8f43 100644 --- a/config/spl-build.m4 +++ b/config/spl-build.m4 @@ -77,6 +77,7 @@ AC_DEFUN([SPL_AC_CONFIG_KERNEL], [ SPL_AC_5ARGS_PROC_HANDLER SPL_AC_KVASPRINTF SPL_AC_3ARGS_FILE_FSYNC + SPL_AC_EXPORTED_RWSEM_IS_LOCKED ]) AC_DEFUN([SPL_AC_MODULE_SYMVERS], [ @@ -1598,3 +1599,19 @@ AC_DEFUN([SPL_AC_3ARGS_FILE_FSYNC], [ AC_MSG_RESULT(no) ]) ]) + +dnl # +dnl # 2.6.33 API change. Also backported in RHEL5 as of 2.6.18-190.el5. +dnl # Earlier versions of rwsem_is_locked() were inline and had a race +dnl # condition. The fixed version is exported as a symbol. The race +dnl # condition is fixed by acquiring sem->wait_lock, so we must not +dnl # call that version while holding sem->wait_lock. +dnl # +AC_DEFUN([SPL_AC_EXPORTED_RWSEM_IS_LOCKED], [ + SPL_CHECK_SYMBOL_EXPORT( + [rwsem_is_locked], + [lib/rwsem-spinlock.c], + [AC_DEFINE(RWSEM_IS_LOCKED_TAKES_WAIT_LOCK, 1, + [rwsem_is_locked() acquires sem->wait_lock])], + []) +]) diff --git a/configure b/configure index bcef31e4..285c9a43 100755 --- a/configure +++ b/configure @@ -15130,6 +15130,47 @@ fi + + { $as_echo "$as_me:$LINENO: checking whether symbol rwsem_is_locked is exported" >&5 +$as_echo_n "checking whether symbol rwsem_is_locked is exported... " >&6; } + grep -q -E '[[:space:]]rwsem_is_locked[[:space:]]' \ + $LINUX_OBJ/Module*.symvers 2>/dev/null + rc=$? + if test $rc -ne 0; then + export=0 + for file in lib/rwsem-spinlock.c; do + grep -q -E "EXPORT_SYMBOL.*(rwsem_is_locked)" \ + "$LINUX_OBJ/$file" 2>/dev/null + rc=$? + if test $rc -eq 0; then + export=1 + break; + fi + done + if test $export -eq 0; then + { $as_echo "$as_me:$LINENO: result: no" >&5 +$as_echo "no" >&6; } + + else + { $as_echo "$as_me:$LINENO: result: yes" >&5 +$as_echo "yes" >&6; } + +cat >>confdefs.h <<\_ACEOF +#define RWSEM_IS_LOCKED_TAKES_WAIT_LOCK 1 +_ACEOF + + fi + else + { $as_echo "$as_me:$LINENO: result: yes" >&5 +$as_echo "yes" >&6; } + +cat >>confdefs.h <<\_ACEOF +#define RWSEM_IS_LOCKED_TAKES_WAIT_LOCK 1 +_ACEOF + + fi + + ;; user) @@ -18643,6 +18684,47 @@ fi + { $as_echo "$as_me:$LINENO: checking whether symbol rwsem_is_locked is exported" >&5 +$as_echo_n "checking whether symbol rwsem_is_locked is exported... " >&6; } + grep -q -E '[[:space:]]rwsem_is_locked[[:space:]]' \ + $LINUX_OBJ/Module*.symvers 2>/dev/null + rc=$? + if test $rc -ne 0; then + export=0 + for file in lib/rwsem-spinlock.c; do + grep -q -E "EXPORT_SYMBOL.*(rwsem_is_locked)" \ + "$LINUX_OBJ/$file" 2>/dev/null + rc=$? + if test $rc -eq 0; then + export=1 + break; + fi + done + if test $export -eq 0; then + { $as_echo "$as_me:$LINENO: result: no" >&5 +$as_echo "no" >&6; } + + else + { $as_echo "$as_me:$LINENO: result: yes" >&5 +$as_echo "yes" >&6; } + +cat >>confdefs.h <<\_ACEOF +#define RWSEM_IS_LOCKED_TAKES_WAIT_LOCK 1 +_ACEOF + + fi + else + { $as_echo "$as_me:$LINENO: result: yes" >&5 +$as_echo "yes" >&6; } + +cat >>confdefs.h <<\_ACEOF +#define RWSEM_IS_LOCKED_TAKES_WAIT_LOCK 1 +_ACEOF + + fi + + + if test "x$AWK" != xgawk; then diff --git a/include/linux/rwsem_compat.h b/include/linux/rwsem_compat.h new file mode 100644 index 00000000..67a82bb4 --- /dev/null +++ b/include/linux/rwsem_compat.h @@ -0,0 +1,63 @@ +/*****************************************************************************\ + * Copyright (C) 2007-2010 Lawrence Livermore National Security, LLC. + * Copyright (C) 2007 The Regents of the University of California. + * Produced at Lawrence Livermore National Laboratory (cf, DISCLAIMER). + * Written by Brian Behlendorf . + * UCRL-CODE-235197 + * + * This file is part of the SPL, Solaris Porting Layer. + * For details, see . + * + * The SPL is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * The SPL is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + * + * You should have received a copy of the GNU General Public License along + * with the SPL. If not, see . +\*****************************************************************************/ + +#ifndef _SPL_RWSEM_COMPAT_H +#define _SPL_RWSEM_COMPAT_H + +#include + +#ifdef RWSEM_IS_LOCKED_TAKES_WAIT_LOCK +/* + * A race condition in rwsem_is_locked() was fixed in Linux 2.6.33 and the fix + * was backported to RHEL5 as of kernel 2.6.18-190.el5. Details can be found + * here: + * + * https://bugzilla.redhat.com/show_bug.cgi?id=526092 + + * The race condition was fixed in the kernel by acquiring the semaphore's + * wait_lock inside rwsem_is_locked(). The SPL worked around the race + * condition by acquiring the wait_lock before calling that function, but + * with the fix in place we must not do that. + */ + +#define spl_rwsem_is_locked(rwsem) \ +({ \ + rwsem_is_locked(rwsem); \ +}) + +#else + +#define spl_rwsem_is_locked(rwsem) \ +({ \ + unsigned long _flags_; \ + int _rc_; \ + spin_lock_irqsave(&rwsem->wait_lock, _flags_); \ + _rc_ = rwsem_is_locked(rwsem); \ + spin_unlock_irqrestore(&rwsem->wait_lock, _flags_); \ + _rc_; \ +}) + +#endif /* RWSEM_IS_LOCKED_TAKES_WAIT_LOCK */ + +#endif /* _SPL_RWSEM_COMPAT_H */ diff --git a/include/sys/rwlock.h b/include/sys/rwlock.h index eb763ec7..3d980859 100644 --- a/include/sys/rwlock.h +++ b/include/sys/rwlock.h @@ -27,6 +27,7 @@ #include #include +#include typedef enum { RW_DRIVER = 2, @@ -46,12 +47,6 @@ typedef struct { #define SEM(rwp) ((struct rw_semaphore *)(rwp)) -static inline kthread_t * -spl_rw_get_owner(krwlock_t *rwp) -{ - return rwp->rw_owner; -} - static inline void spl_rw_set_owner(krwlock_t *rwp) { @@ -79,7 +74,7 @@ rw_owner(krwlock_t *rwp) kthread_t *owner; spin_lock_irqsave(&SEM(rwp)->wait_lock, flags); - owner = spl_rw_get_owner(rwp); + owner = rwp->rw_owner; spin_unlock_irqrestore(&SEM(rwp)->wait_lock, flags); return owner; @@ -88,40 +83,21 @@ rw_owner(krwlock_t *rwp) static inline int RW_READ_HELD(krwlock_t *rwp) { - unsigned long flags; - int rc; - - spin_lock_irqsave(&SEM(rwp)->wait_lock, flags); - rc = (rwsem_is_locked(SEM(rwp)) && spl_rw_get_owner(rwp) == NULL); - spin_unlock_irqrestore(&SEM(rwp)->wait_lock, flags); - - return rc; + return (spl_rwsem_is_locked(SEM(rwp)) && + rw_owner(rwp) == NULL); } static inline int RW_WRITE_HELD(krwlock_t *rwp) { - unsigned long flags; - int rc; - - spin_lock_irqsave(&SEM(rwp)->wait_lock, flags); - rc = (rwsem_is_locked(SEM(rwp)) && spl_rw_get_owner(rwp) == current); - spin_unlock_irqrestore(&SEM(rwp)->wait_lock, flags); - - return rc; + return (spl_rwsem_is_locked(SEM(rwp)) && + rw_owner(rwp) == current); } static inline int RW_LOCK_HELD(krwlock_t *rwp) { - unsigned long flags; - int rc; - - spin_lock_irqsave(&SEM(rwp)->wait_lock, flags); - rc = rwsem_is_locked(SEM(rwp)); - spin_unlock_irqrestore(&SEM(rwp)->wait_lock, flags); - - return rc; + return spl_rwsem_is_locked(SEM(rwp)); } /* diff --git a/spl_config.h.in b/spl_config.h.in index 8d57a63e..d3928f4b 100644 --- a/spl_config.h.in +++ b/spl_config.h.in @@ -229,6 +229,9 @@ /* Define to the version of this package. */ #undef PACKAGE_VERSION +/* rwsem_is_locked() acquires sem->wait_lock */ +#undef RWSEM_IS_LOCKED_TAKES_WAIT_LOCK + /* Define the project alias string. */ #undef SPL_META_ALIAS