Implement a proper rw_tryupgrade

Current rw_tryupgrade does rw_exit and then rw_tryenter(RW_RWITER), and then
does rw_enter(RW_READER) if it fails. This violate the assumption that
rw_tryupgrade should be atomic and could cause extra contention or even lock
inversion.

This patch we implement a proper rw_tryupgrade. For rwsem-spinlock, we take
the spinlock to check rwsem->count and rwsem->wait_list. For normal rwsem, we
use cmpxchg on rwsem->count to change the value from single reader to single
writer.

Signed-off-by: Chunwei Chen <david.chen@osnexus.com>
Signed-off-by: Brian Behlendorf <behlendorf1@llnl.gov>
Signed-off-by: Tim Chase <tim@chase2k.com>
Closes zfsonlinux/zfs#4692
Closes #554
This commit is contained in:
Chunwei Chen
2016-05-25 16:35:42 -07:00
committed by Brian Behlendorf
parent c60a51b640
commit f58040c0fc
5 changed files with 147 additions and 13 deletions
+17
View File
@@ -27,6 +27,23 @@
#include <linux/rwsem.h>
#ifdef CONFIG_RWSEM_GENERIC_SPINLOCK
#define SPL_RWSEM_SINGLE_READER_VALUE (1)
#define SPL_RWSEM_SINGLE_WRITER_VALUE (-1)
#else
#define SPL_RWSEM_SINGLE_READER_VALUE (RWSEM_ACTIVE_READ_BIAS)
#define SPL_RWSEM_SINGLE_WRITER_VALUE (RWSEM_ACTIVE_WRITE_BIAS)
#endif
/* Linux 3.16 change activity to count for rwsem-spinlock */
#ifdef HAVE_RWSEM_ACTIVITY
#define RWSEM_COUNT(sem) sem->activity
#else
#define RWSEM_COUNT(sem) sem->count
#endif
int rwsem_tryupgrade(struct rw_semaphore *rwsem);
#if defined(RWSEM_SPINLOCK_IS_RAW)
#define spl_rwsem_lock_irqsave(lk, fl) raw_spin_lock_irqsave(lk, fl)
#define spl_rwsem_unlock_irqrestore(lk, fl) raw_spin_unlock_irqrestore(lk, fl)
+4 -7
View File
@@ -223,13 +223,10 @@ RW_LOCK_HELD(krwlock_t *rwp)
if (RW_WRITE_HELD(rwp)) { \
_rc_ = 1; \
} else { \
rw_exit(rwp); \
if (rw_tryenter(rwp, RW_WRITER)) { \
_rc_ = 1; \
} else { \
rw_enter(rwp, RW_READER); \
_rc_ = 0; \
} \
spl_rw_lockdep_off_maybe(rwp); \
if ((_rc_ = rwsem_tryupgrade(SEM(rwp)))) \
spl_rw_set_owner(rwp); \
spl_rw_lockdep_on_maybe(rwp); \
} \
_rc_; \
})