03eeafdd9a
It was found that the use of up_read_non_owner() in NFS was causing
the following warning when DEBUG_RWSEMS was configured.
DEBUG_LOCKS_WARN_ON(sem->owner != ((struct task_struct *)(1UL << 0)))
Looking into the rwsem.c file, it was discovered that the corresponding
down_read_non_owner() function was not setting the owner field properly.
This is fixed now, and the warning should be gone.
Fixes: 5149cbac42
("locking/rwsem: Add DEBUG_RWSEMS to look for lock/unlock mismatches")
Signed-off-by: Waiman Long <longman@redhat.com>
Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
Acked-by: Peter Zijlstra (Intel) <peterz@infradead.org>
Tested-by: Gavin Schenk <g.schenk@eckelmann.de>
Cc: Davidlohr Bueso <dave@stgolabs.net>
Cc: Dan Williams <dan.j.williams@intel.com>
Cc: Arnd Bergmann <arnd@arndb.de>
Cc: linux-nfs@vger.kernel.org
Cc: stable@vger.kernel.org
Link: https://lkml.kernel.org/r/1527168398-4291-1-git-send-email-longman@redhat.com
224 lines
4.4 KiB
C
224 lines
4.4 KiB
C
// SPDX-License-Identifier: GPL-2.0
|
|
/* kernel/rwsem.c: R/W semaphores, public implementation
|
|
*
|
|
* Written by David Howells (dhowells@redhat.com).
|
|
* Derived from asm-i386/semaphore.h
|
|
*/
|
|
|
|
#include <linux/types.h>
|
|
#include <linux/kernel.h>
|
|
#include <linux/sched.h>
|
|
#include <linux/sched/debug.h>
|
|
#include <linux/export.h>
|
|
#include <linux/rwsem.h>
|
|
#include <linux/atomic.h>
|
|
|
|
#include "rwsem.h"
|
|
|
|
/*
|
|
* lock for reading
|
|
*/
|
|
void __sched down_read(struct rw_semaphore *sem)
|
|
{
|
|
might_sleep();
|
|
rwsem_acquire_read(&sem->dep_map, 0, 0, _RET_IP_);
|
|
|
|
LOCK_CONTENDED(sem, __down_read_trylock, __down_read);
|
|
rwsem_set_reader_owned(sem);
|
|
}
|
|
|
|
EXPORT_SYMBOL(down_read);
|
|
|
|
int __sched down_read_killable(struct rw_semaphore *sem)
|
|
{
|
|
might_sleep();
|
|
rwsem_acquire_read(&sem->dep_map, 0, 0, _RET_IP_);
|
|
|
|
if (LOCK_CONTENDED_RETURN(sem, __down_read_trylock, __down_read_killable)) {
|
|
rwsem_release(&sem->dep_map, 1, _RET_IP_);
|
|
return -EINTR;
|
|
}
|
|
|
|
rwsem_set_reader_owned(sem);
|
|
return 0;
|
|
}
|
|
|
|
EXPORT_SYMBOL(down_read_killable);
|
|
|
|
/*
|
|
* trylock for reading -- returns 1 if successful, 0 if contention
|
|
*/
|
|
int down_read_trylock(struct rw_semaphore *sem)
|
|
{
|
|
int ret = __down_read_trylock(sem);
|
|
|
|
if (ret == 1) {
|
|
rwsem_acquire_read(&sem->dep_map, 0, 1, _RET_IP_);
|
|
rwsem_set_reader_owned(sem);
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
EXPORT_SYMBOL(down_read_trylock);
|
|
|
|
/*
|
|
* lock for writing
|
|
*/
|
|
void __sched down_write(struct rw_semaphore *sem)
|
|
{
|
|
might_sleep();
|
|
rwsem_acquire(&sem->dep_map, 0, 0, _RET_IP_);
|
|
|
|
LOCK_CONTENDED(sem, __down_write_trylock, __down_write);
|
|
rwsem_set_owner(sem);
|
|
}
|
|
|
|
EXPORT_SYMBOL(down_write);
|
|
|
|
/*
|
|
* lock for writing
|
|
*/
|
|
int __sched down_write_killable(struct rw_semaphore *sem)
|
|
{
|
|
might_sleep();
|
|
rwsem_acquire(&sem->dep_map, 0, 0, _RET_IP_);
|
|
|
|
if (LOCK_CONTENDED_RETURN(sem, __down_write_trylock, __down_write_killable)) {
|
|
rwsem_release(&sem->dep_map, 1, _RET_IP_);
|
|
return -EINTR;
|
|
}
|
|
|
|
rwsem_set_owner(sem);
|
|
return 0;
|
|
}
|
|
|
|
EXPORT_SYMBOL(down_write_killable);
|
|
|
|
/*
|
|
* trylock for writing -- returns 1 if successful, 0 if contention
|
|
*/
|
|
int down_write_trylock(struct rw_semaphore *sem)
|
|
{
|
|
int ret = __down_write_trylock(sem);
|
|
|
|
if (ret == 1) {
|
|
rwsem_acquire(&sem->dep_map, 0, 1, _RET_IP_);
|
|
rwsem_set_owner(sem);
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
EXPORT_SYMBOL(down_write_trylock);
|
|
|
|
/*
|
|
* release a read lock
|
|
*/
|
|
void up_read(struct rw_semaphore *sem)
|
|
{
|
|
rwsem_release(&sem->dep_map, 1, _RET_IP_);
|
|
DEBUG_RWSEMS_WARN_ON(sem->owner != RWSEM_READER_OWNED);
|
|
|
|
__up_read(sem);
|
|
}
|
|
|
|
EXPORT_SYMBOL(up_read);
|
|
|
|
/*
|
|
* release a write lock
|
|
*/
|
|
void up_write(struct rw_semaphore *sem)
|
|
{
|
|
rwsem_release(&sem->dep_map, 1, _RET_IP_);
|
|
DEBUG_RWSEMS_WARN_ON(sem->owner != current);
|
|
|
|
rwsem_clear_owner(sem);
|
|
__up_write(sem);
|
|
}
|
|
|
|
EXPORT_SYMBOL(up_write);
|
|
|
|
/*
|
|
* downgrade write lock to read lock
|
|
*/
|
|
void downgrade_write(struct rw_semaphore *sem)
|
|
{
|
|
lock_downgrade(&sem->dep_map, _RET_IP_);
|
|
DEBUG_RWSEMS_WARN_ON(sem->owner != current);
|
|
|
|
rwsem_set_reader_owned(sem);
|
|
__downgrade_write(sem);
|
|
}
|
|
|
|
EXPORT_SYMBOL(downgrade_write);
|
|
|
|
#ifdef CONFIG_DEBUG_LOCK_ALLOC
|
|
|
|
void down_read_nested(struct rw_semaphore *sem, int subclass)
|
|
{
|
|
might_sleep();
|
|
rwsem_acquire_read(&sem->dep_map, subclass, 0, _RET_IP_);
|
|
|
|
LOCK_CONTENDED(sem, __down_read_trylock, __down_read);
|
|
rwsem_set_reader_owned(sem);
|
|
}
|
|
|
|
EXPORT_SYMBOL(down_read_nested);
|
|
|
|
void _down_write_nest_lock(struct rw_semaphore *sem, struct lockdep_map *nest)
|
|
{
|
|
might_sleep();
|
|
rwsem_acquire_nest(&sem->dep_map, 0, 0, nest, _RET_IP_);
|
|
|
|
LOCK_CONTENDED(sem, __down_write_trylock, __down_write);
|
|
rwsem_set_owner(sem);
|
|
}
|
|
|
|
EXPORT_SYMBOL(_down_write_nest_lock);
|
|
|
|
void down_read_non_owner(struct rw_semaphore *sem)
|
|
{
|
|
might_sleep();
|
|
|
|
__down_read(sem);
|
|
rwsem_set_reader_owned(sem);
|
|
}
|
|
|
|
EXPORT_SYMBOL(down_read_non_owner);
|
|
|
|
void down_write_nested(struct rw_semaphore *sem, int subclass)
|
|
{
|
|
might_sleep();
|
|
rwsem_acquire(&sem->dep_map, subclass, 0, _RET_IP_);
|
|
|
|
LOCK_CONTENDED(sem, __down_write_trylock, __down_write);
|
|
rwsem_set_owner(sem);
|
|
}
|
|
|
|
EXPORT_SYMBOL(down_write_nested);
|
|
|
|
int __sched down_write_killable_nested(struct rw_semaphore *sem, int subclass)
|
|
{
|
|
might_sleep();
|
|
rwsem_acquire(&sem->dep_map, subclass, 0, _RET_IP_);
|
|
|
|
if (LOCK_CONTENDED_RETURN(sem, __down_write_trylock, __down_write_killable)) {
|
|
rwsem_release(&sem->dep_map, 1, _RET_IP_);
|
|
return -EINTR;
|
|
}
|
|
|
|
rwsem_set_owner(sem);
|
|
return 0;
|
|
}
|
|
|
|
EXPORT_SYMBOL(down_write_killable_nested);
|
|
|
|
void up_read_non_owner(struct rw_semaphore *sem)
|
|
{
|
|
DEBUG_RWSEMS_WARN_ON(sem->owner != RWSEM_READER_OWNED);
|
|
__up_read(sem);
|
|
}
|
|
|
|
EXPORT_SYMBOL(up_read_non_owner);
|
|
|
|
#endif
|