mirror of
https://git.proxmox.com/git/mirror_zfs.git
synced 2026-05-22 10:37:35 +03:00
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:
committed by
Brian Behlendorf
parent
c60a51b640
commit
f58040c0fc
@@ -32,5 +32,46 @@
|
||||
|
||||
#define DEBUG_SUBSYSTEM S_RWLOCK
|
||||
|
||||
#ifdef CONFIG_RWSEM_GENERIC_SPINLOCK
|
||||
static int
|
||||
__rwsem_tryupgrade(struct rw_semaphore *rwsem)
|
||||
{
|
||||
int ret = 0;
|
||||
unsigned long flags;
|
||||
spl_rwsem_lock_irqsave(&rwsem->wait_lock, flags);
|
||||
if (RWSEM_COUNT(rwsem) == SPL_RWSEM_SINGLE_READER_VALUE &&
|
||||
list_empty(&rwsem->wait_list)) {
|
||||
ret = 1;
|
||||
RWSEM_COUNT(rwsem) = SPL_RWSEM_SINGLE_WRITER_VALUE;
|
||||
}
|
||||
spl_rwsem_unlock_irqrestore(&rwsem->wait_lock, flags);
|
||||
return (ret);
|
||||
}
|
||||
#else
|
||||
static int
|
||||
__rwsem_tryupgrade(struct rw_semaphore *rwsem)
|
||||
{
|
||||
typeof (rwsem->count) val;
|
||||
val = cmpxchg(&rwsem->count, SPL_RWSEM_SINGLE_READER_VALUE,
|
||||
SPL_RWSEM_SINGLE_WRITER_VALUE);
|
||||
return (val == SPL_RWSEM_SINGLE_READER_VALUE);
|
||||
}
|
||||
#endif
|
||||
|
||||
int
|
||||
rwsem_tryupgrade(struct rw_semaphore *rwsem)
|
||||
{
|
||||
if (__rwsem_tryupgrade(rwsem)) {
|
||||
rwsem_release(&rwsem->dep_map, 1, _RET_IP_);
|
||||
rwsem_acquire(&rwsem->dep_map, 0, 1, _RET_IP_);
|
||||
#ifdef CONFIG_RWSEM_SPIN_ON_OWNER
|
||||
rwsem->owner = current;
|
||||
#endif
|
||||
return (1);
|
||||
}
|
||||
return (0);
|
||||
}
|
||||
EXPORT_SYMBOL(rwsem_tryupgrade);
|
||||
|
||||
int spl_rw_init(void) { return 0; }
|
||||
void spl_rw_fini(void) { }
|
||||
|
||||
Reference in New Issue
Block a user