[PATCH] lockdep: prove rwsem locking correctness
Use the lock validator framework to prove rwsem locking correctness. Signed-off-by: Ingo Molnar <mingo@elte.hu> Signed-off-by: Arjan van de Ven <arjan@linux.intel.com> Signed-off-by: Andrew Morton <akpm@osdl.org> Signed-off-by: Linus Torvalds <torvalds@osdl.org>
This commit is contained in:
parent
a8f24a3978
commit
4ea2176dfa
9 changed files with 184 additions and 51 deletions
|
@ -40,6 +40,7 @@
|
|||
|
||||
#include <linux/list.h>
|
||||
#include <linux/spinlock.h>
|
||||
#include <linux/lockdep.h>
|
||||
|
||||
struct rwsem_waiter;
|
||||
|
||||
|
@ -61,21 +62,34 @@ struct rw_semaphore {
|
|||
#define RWSEM_ACTIVE_WRITE_BIAS (RWSEM_WAITING_BIAS + RWSEM_ACTIVE_BIAS)
|
||||
spinlock_t wait_lock;
|
||||
struct list_head wait_list;
|
||||
#ifdef CONFIG_DEBUG_LOCK_ALLOC
|
||||
struct lockdep_map dep_map;
|
||||
#endif
|
||||
};
|
||||
|
||||
#ifdef CONFIG_DEBUG_LOCK_ALLOC
|
||||
# define __RWSEM_DEP_MAP_INIT(lockname) , .dep_map = { .name = #lockname }
|
||||
#else
|
||||
# define __RWSEM_DEP_MAP_INIT(lockname)
|
||||
#endif
|
||||
|
||||
|
||||
#define __RWSEM_INITIALIZER(name) \
|
||||
{ RWSEM_UNLOCKED_VALUE, SPIN_LOCK_UNLOCKED, LIST_HEAD_INIT((name).wait_list) \
|
||||
}
|
||||
__RWSEM_DEP_MAP_INIT(name) }
|
||||
|
||||
#define DECLARE_RWSEM(name) \
|
||||
struct rw_semaphore name = __RWSEM_INITIALIZER(name)
|
||||
|
||||
static inline void init_rwsem(struct rw_semaphore *sem)
|
||||
{
|
||||
sem->count = RWSEM_UNLOCKED_VALUE;
|
||||
spin_lock_init(&sem->wait_lock);
|
||||
INIT_LIST_HEAD(&sem->wait_list);
|
||||
}
|
||||
extern void __init_rwsem(struct rw_semaphore *sem, const char *name,
|
||||
struct lock_class_key *key);
|
||||
|
||||
#define init_rwsem(sem) \
|
||||
do { \
|
||||
static struct lock_class_key __key; \
|
||||
\
|
||||
__init_rwsem((sem), #sem, &__key); \
|
||||
} while (0)
|
||||
|
||||
/*
|
||||
* lock for reading
|
||||
|
@ -128,7 +142,7 @@ LOCK_PREFIX " cmpxchgl %2,%0\n\t"
|
|||
/*
|
||||
* lock for writing
|
||||
*/
|
||||
static inline void __down_write(struct rw_semaphore *sem)
|
||||
static inline void __down_write_nested(struct rw_semaphore *sem, int subclass)
|
||||
{
|
||||
int tmp;
|
||||
|
||||
|
@ -152,6 +166,11 @@ LOCK_PREFIX " xadd %%edx,(%%eax)\n\t" /* subtract 0x0000ffff, returns the
|
|||
: "memory", "cc");
|
||||
}
|
||||
|
||||
static inline void __down_write(struct rw_semaphore *sem)
|
||||
{
|
||||
__down_write_nested(sem, 0);
|
||||
}
|
||||
|
||||
/*
|
||||
* trylock for writing -- returns 1 if successful, 0 if contention
|
||||
*/
|
||||
|
|
|
@ -61,6 +61,9 @@ struct rw_semaphore {
|
|||
signed long count;
|
||||
spinlock_t wait_lock;
|
||||
struct list_head wait_list;
|
||||
#ifdef CONFIG_DEBUG_LOCK_ALLOC
|
||||
struct lockdep_map dep_map;
|
||||
#endif
|
||||
};
|
||||
|
||||
#ifndef __s390x__
|
||||
|
@ -80,8 +83,16 @@ struct rw_semaphore {
|
|||
/*
|
||||
* initialisation
|
||||
*/
|
||||
|
||||
#ifdef CONFIG_DEBUG_LOCK_ALLOC
|
||||
# define __RWSEM_DEP_MAP_INIT(lockname) , .dep_map = { .name = #lockname }
|
||||
#else
|
||||
# define __RWSEM_DEP_MAP_INIT(lockname)
|
||||
#endif
|
||||
|
||||
#define __RWSEM_INITIALIZER(name) \
|
||||
{ RWSEM_UNLOCKED_VALUE, SPIN_LOCK_UNLOCKED, LIST_HEAD_INIT((name).wait_list) }
|
||||
{ RWSEM_UNLOCKED_VALUE, SPIN_LOCK_UNLOCKED, LIST_HEAD_INIT((name).wait_list) \
|
||||
__RWSEM_DEP_MAP_INIT(name) }
|
||||
|
||||
#define DECLARE_RWSEM(name) \
|
||||
struct rw_semaphore name = __RWSEM_INITIALIZER(name)
|
||||
|
@ -93,6 +104,17 @@ static inline void init_rwsem(struct rw_semaphore *sem)
|
|||
INIT_LIST_HEAD(&sem->wait_list);
|
||||
}
|
||||
|
||||
extern void __init_rwsem(struct rw_semaphore *sem, const char *name,
|
||||
struct lock_class_key *key);
|
||||
|
||||
#define init_rwsem(sem) \
|
||||
do { \
|
||||
static struct lock_class_key __key; \
|
||||
\
|
||||
__init_rwsem((sem), #sem, &__key); \
|
||||
} while (0)
|
||||
|
||||
|
||||
/*
|
||||
* lock for reading
|
||||
*/
|
||||
|
@ -155,7 +177,7 @@ static inline int __down_read_trylock(struct rw_semaphore *sem)
|
|||
/*
|
||||
* lock for writing
|
||||
*/
|
||||
static inline void __down_write(struct rw_semaphore *sem)
|
||||
static inline void __down_write_nested(struct rw_semaphore *sem, int subclass)
|
||||
{
|
||||
signed long old, new, tmp;
|
||||
|
||||
|
@ -181,6 +203,11 @@ static inline void __down_write(struct rw_semaphore *sem)
|
|||
rwsem_down_write_failed(sem);
|
||||
}
|
||||
|
||||
static inline void __down_write(struct rw_semaphore *sem)
|
||||
{
|
||||
__down_write_nested(sem, 0);
|
||||
}
|
||||
|
||||
/*
|
||||
* trylock for writing -- returns 1 if successful, 0 if contention
|
||||
*/
|
||||
|
|
|
@ -37,7 +37,8 @@ struct semaphore {
|
|||
|
||||
static inline void sema_init (struct semaphore *sem, int val)
|
||||
{
|
||||
*sem = (struct semaphore) __SEMAPHORE_INITIALIZER((*sem),val);
|
||||
atomic_set(&sem->count, val);
|
||||
init_waitqueue_head(&sem->wait);
|
||||
}
|
||||
|
||||
static inline void init_MUTEX (struct semaphore *sem)
|
||||
|
|
|
@ -32,18 +32,37 @@ struct rw_semaphore {
|
|||
__s32 activity;
|
||||
spinlock_t wait_lock;
|
||||
struct list_head wait_list;
|
||||
#ifdef CONFIG_DEBUG_LOCK_ALLOC
|
||||
struct lockdep_map dep_map;
|
||||
#endif
|
||||
};
|
||||
|
||||
#ifdef CONFIG_DEBUG_LOCK_ALLOC
|
||||
# define __RWSEM_DEP_MAP_INIT(lockname) , .dep_map = { .name = #lockname }
|
||||
#else
|
||||
# define __RWSEM_DEP_MAP_INIT(lockname)
|
||||
#endif
|
||||
|
||||
#define __RWSEM_INITIALIZER(name) \
|
||||
{ 0, SPIN_LOCK_UNLOCKED, LIST_HEAD_INIT((name).wait_list) }
|
||||
{ 0, SPIN_LOCK_UNLOCKED, LIST_HEAD_INIT((name).wait_list) __RWSEM_DEP_MAP_INIT(name) }
|
||||
|
||||
#define DECLARE_RWSEM(name) \
|
||||
struct rw_semaphore name = __RWSEM_INITIALIZER(name)
|
||||
|
||||
extern void FASTCALL(init_rwsem(struct rw_semaphore *sem));
|
||||
extern void __init_rwsem(struct rw_semaphore *sem, const char *name,
|
||||
struct lock_class_key *key);
|
||||
|
||||
#define init_rwsem(sem) \
|
||||
do { \
|
||||
static struct lock_class_key __key; \
|
||||
\
|
||||
__init_rwsem((sem), #sem, &__key); \
|
||||
} while (0)
|
||||
|
||||
extern void FASTCALL(__down_read(struct rw_semaphore *sem));
|
||||
extern int FASTCALL(__down_read_trylock(struct rw_semaphore *sem));
|
||||
extern void FASTCALL(__down_write(struct rw_semaphore *sem));
|
||||
extern void FASTCALL(__down_write_nested(struct rw_semaphore *sem, int subclass));
|
||||
extern int FASTCALL(__down_write_trylock(struct rw_semaphore *sem));
|
||||
extern void FASTCALL(__up_read(struct rw_semaphore *sem));
|
||||
extern void FASTCALL(__up_write(struct rw_semaphore *sem));
|
||||
|
|
|
@ -27,64 +27,55 @@ struct rw_semaphore;
|
|||
/*
|
||||
* lock for reading
|
||||
*/
|
||||
static inline void down_read(struct rw_semaphore *sem)
|
||||
{
|
||||
might_sleep();
|
||||
__down_read(sem);
|
||||
}
|
||||
extern void down_read(struct rw_semaphore *sem);
|
||||
|
||||
/*
|
||||
* trylock for reading -- returns 1 if successful, 0 if contention
|
||||
*/
|
||||
static inline int down_read_trylock(struct rw_semaphore *sem)
|
||||
{
|
||||
int ret;
|
||||
ret = __down_read_trylock(sem);
|
||||
return ret;
|
||||
}
|
||||
extern int down_read_trylock(struct rw_semaphore *sem);
|
||||
|
||||
/*
|
||||
* lock for writing
|
||||
*/
|
||||
static inline void down_write(struct rw_semaphore *sem)
|
||||
{
|
||||
might_sleep();
|
||||
__down_write(sem);
|
||||
}
|
||||
extern void down_write(struct rw_semaphore *sem);
|
||||
|
||||
/*
|
||||
* trylock for writing -- returns 1 if successful, 0 if contention
|
||||
*/
|
||||
static inline int down_write_trylock(struct rw_semaphore *sem)
|
||||
{
|
||||
int ret;
|
||||
ret = __down_write_trylock(sem);
|
||||
return ret;
|
||||
}
|
||||
extern int down_write_trylock(struct rw_semaphore *sem);
|
||||
|
||||
/*
|
||||
* release a read lock
|
||||
*/
|
||||
static inline void up_read(struct rw_semaphore *sem)
|
||||
{
|
||||
__up_read(sem);
|
||||
}
|
||||
extern void up_read(struct rw_semaphore *sem);
|
||||
|
||||
/*
|
||||
* release a write lock
|
||||
*/
|
||||
static inline void up_write(struct rw_semaphore *sem)
|
||||
{
|
||||
__up_write(sem);
|
||||
}
|
||||
extern void up_write(struct rw_semaphore *sem);
|
||||
|
||||
/*
|
||||
* downgrade write lock to read lock
|
||||
*/
|
||||
static inline void downgrade_write(struct rw_semaphore *sem)
|
||||
{
|
||||
__downgrade_write(sem);
|
||||
}
|
||||
extern void downgrade_write(struct rw_semaphore *sem);
|
||||
|
||||
#ifdef CONFIG_DEBUG_LOCK_ALLOC
|
||||
/*
|
||||
* nested locking:
|
||||
*/
|
||||
extern void down_read_nested(struct rw_semaphore *sem, int subclass);
|
||||
extern void down_write_nested(struct rw_semaphore *sem, int subclass);
|
||||
/*
|
||||
* Take/release a lock when not the owner will release it:
|
||||
*/
|
||||
extern void down_read_non_owner(struct rw_semaphore *sem);
|
||||
extern void up_read_non_owner(struct rw_semaphore *sem);
|
||||
#else
|
||||
# define down_read_nested(sem, subclass) down_read(sem)
|
||||
# define down_write_nested(sem, subclass) down_write(sem)
|
||||
# define down_read_non_owner(sem) down_read(sem)
|
||||
# define up_read_non_owner(sem) up_read(sem)
|
||||
#endif
|
||||
|
||||
#endif /* __KERNEL__ */
|
||||
#endif /* _LINUX_RWSEM_H */
|
||||
|
|
|
@ -8,7 +8,7 @@ obj-y = sched.o fork.o exec_domain.o panic.o printk.o profile.o \
|
|||
signal.o sys.o kmod.o workqueue.o pid.o \
|
||||
rcupdate.o extable.o params.o posix-timers.o \
|
||||
kthread.o wait.o kfifo.o sys_ni.o posix-cpu-timers.o mutex.o \
|
||||
hrtimer.o
|
||||
hrtimer.o rwsem.o
|
||||
|
||||
obj-$(CONFIG_STACKTRACE) += stacktrace.o
|
||||
obj-y += time/
|
||||
|
|
|
@ -103,3 +103,45 @@ void downgrade_write(struct rw_semaphore *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_);
|
||||
|
||||
__down_read(sem);
|
||||
}
|
||||
|
||||
EXPORT_SYMBOL(down_read_nested);
|
||||
|
||||
void down_read_non_owner(struct rw_semaphore *sem)
|
||||
{
|
||||
might_sleep();
|
||||
|
||||
__down_read(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_);
|
||||
|
||||
__down_write_nested(sem, subclass);
|
||||
}
|
||||
|
||||
EXPORT_SYMBOL(down_write_nested);
|
||||
|
||||
void up_read_non_owner(struct rw_semaphore *sem)
|
||||
{
|
||||
__up_read(sem);
|
||||
}
|
||||
|
||||
EXPORT_SYMBOL(up_read_non_owner);
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
|
|
|
@ -20,8 +20,16 @@ struct rwsem_waiter {
|
|||
/*
|
||||
* initialise the semaphore
|
||||
*/
|
||||
void fastcall init_rwsem(struct rw_semaphore *sem)
|
||||
void __init_rwsem(struct rw_semaphore *sem, const char *name,
|
||||
struct lock_class_key *key)
|
||||
{
|
||||
#ifdef CONFIG_DEBUG_LOCK_ALLOC
|
||||
/*
|
||||
* Make sure we are not reinitializing a held semaphore:
|
||||
*/
|
||||
debug_check_no_locks_freed((void *)sem, sizeof(*sem));
|
||||
lockdep_init_map(&sem->dep_map, name, key);
|
||||
#endif
|
||||
sem->activity = 0;
|
||||
spin_lock_init(&sem->wait_lock);
|
||||
INIT_LIST_HEAD(&sem->wait_list);
|
||||
|
@ -183,7 +191,7 @@ int fastcall __down_read_trylock(struct rw_semaphore *sem)
|
|||
* get a write lock on the semaphore
|
||||
* - we increment the waiting count anyway to indicate an exclusive lock
|
||||
*/
|
||||
void fastcall __sched __down_write(struct rw_semaphore *sem)
|
||||
void fastcall __sched __down_write_nested(struct rw_semaphore *sem, int subclass)
|
||||
{
|
||||
struct rwsem_waiter waiter;
|
||||
struct task_struct *tsk;
|
||||
|
@ -223,6 +231,11 @@ void fastcall __sched __down_write(struct rw_semaphore *sem)
|
|||
;
|
||||
}
|
||||
|
||||
void fastcall __sched __down_write(struct rw_semaphore *sem)
|
||||
{
|
||||
__down_write_nested(sem, 0);
|
||||
}
|
||||
|
||||
/*
|
||||
* trylock for writing -- returns 1 if successful, 0 if contention
|
||||
*/
|
||||
|
@ -292,9 +305,10 @@ void fastcall __downgrade_write(struct rw_semaphore *sem)
|
|||
spin_unlock_irqrestore(&sem->wait_lock, flags);
|
||||
}
|
||||
|
||||
EXPORT_SYMBOL(init_rwsem);
|
||||
EXPORT_SYMBOL(__init_rwsem);
|
||||
EXPORT_SYMBOL(__down_read);
|
||||
EXPORT_SYMBOL(__down_read_trylock);
|
||||
EXPORT_SYMBOL(__down_write_nested);
|
||||
EXPORT_SYMBOL(__down_write);
|
||||
EXPORT_SYMBOL(__down_write_trylock);
|
||||
EXPORT_SYMBOL(__up_read);
|
||||
|
|
20
lib/rwsem.c
20
lib/rwsem.c
|
@ -8,6 +8,26 @@
|
|||
#include <linux/init.h>
|
||||
#include <linux/module.h>
|
||||
|
||||
/*
|
||||
* Initialize an rwsem:
|
||||
*/
|
||||
void __init_rwsem(struct rw_semaphore *sem, const char *name,
|
||||
struct lock_class_key *key)
|
||||
{
|
||||
#ifdef CONFIG_DEBUG_LOCK_ALLOC
|
||||
/*
|
||||
* Make sure we are not reinitializing a held semaphore:
|
||||
*/
|
||||
debug_check_no_locks_freed((void *)sem, sizeof(*sem));
|
||||
lockdep_init_map(&sem->dep_map, name, key);
|
||||
#endif
|
||||
sem->count = RWSEM_UNLOCKED_VALUE;
|
||||
spin_lock_init(&sem->wait_lock);
|
||||
INIT_LIST_HEAD(&sem->wait_list);
|
||||
}
|
||||
|
||||
EXPORT_SYMBOL(__init_rwsem);
|
||||
|
||||
struct rwsem_waiter {
|
||||
struct list_head list;
|
||||
struct task_struct *task;
|
||||
|
|
Loading…
Reference in a new issue