Skip to content

Commit 3ca0ff5

Browse files
Peter ZijlstraIngo Molnar
authored andcommitted
locking/mutex: Rework mutex::owner
The current mutex implementation has an atomic lock word and a non-atomic owner field. This disparity leads to a number of issues with the current mutex code as it means that we can have a locked mutex without an explicit owner (because the owner field has not been set, or already cleared). This leads to a number of weird corner cases, esp. between the optimistic spinning and debug code. Where the optimistic spinning code needs the owner field updated inside the lock region, the debug code is more relaxed because the whole lock is serialized by the wait_lock. Also, the spinning code itself has a few corner cases where we need to deal with a held lock without an owner field. Furthermore, it becomes even more of a problem when trying to fix starvation cases in the current code. We end up stacking special case on special case. To solve this rework the basic mutex implementation to be a single atomic word that contains the owner and uses the low bits for extra state. This matches how PI futexes and rt_mutex already work. By having the owner an integral part of the lock state a lot of the problems dissapear and we get a better option to deal with starvation cases, direct owner handoff. Changing the basic mutex does however invalidate all the arch specific mutex code; this patch leaves that unused in-place, a later patch will remove that. Tested-by: Jason Low <[email protected]> Signed-off-by: Peter Zijlstra (Intel) <[email protected]> Reviewed-by: Will Deacon <[email protected]> Cc: Andrew Morton <[email protected]> Cc: Linus Torvalds <[email protected]> Cc: Paul E. McKenney <[email protected]> Cc: Peter Zijlstra <[email protected]> Cc: Thomas Gleixner <[email protected]> Cc: [email protected] Signed-off-by: Ingo Molnar <[email protected]>
1 parent 3ab7c08 commit 3ca0ff5

File tree

7 files changed

+187
-305
lines changed

7 files changed

+187
-305
lines changed

include/linux/mutex-debug.h

Lines changed: 0 additions & 24 deletions
This file was deleted.

include/linux/mutex.h

Lines changed: 30 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
#include <linux/atomic.h>
1919
#include <asm/processor.h>
2020
#include <linux/osq_lock.h>
21+
#include <linux/debug_locks.h>
2122

2223
/*
2324
* Simple, straightforward mutexes with strict semantics:
@@ -48,16 +49,12 @@
4849
* locks and tasks (and only those tasks)
4950
*/
5051
struct mutex {
51-
/* 1: unlocked, 0: locked, negative: locked, possible waiters */
52-
atomic_t count;
52+
atomic_long_t owner;
5353
spinlock_t wait_lock;
54-
struct list_head wait_list;
55-
#if defined(CONFIG_DEBUG_MUTEXES) || defined(CONFIG_MUTEX_SPIN_ON_OWNER)
56-
struct task_struct *owner;
57-
#endif
5854
#ifdef CONFIG_MUTEX_SPIN_ON_OWNER
5955
struct optimistic_spin_queue osq; /* Spinner MCS lock */
6056
#endif
57+
struct list_head wait_list;
6158
#ifdef CONFIG_DEBUG_MUTEXES
6259
void *magic;
6360
#endif
@@ -66,6 +63,11 @@ struct mutex {
6663
#endif
6764
};
6865

66+
static inline struct task_struct *__mutex_owner(struct mutex *lock)
67+
{
68+
return (struct task_struct *)(atomic_long_read(&lock->owner) & ~0x03);
69+
}
70+
6971
/*
7072
* This is the control structure for tasks blocked on mutex,
7173
* which resides on the blocked task's kernel stack:
@@ -79,9 +81,20 @@ struct mutex_waiter {
7981
};
8082

8183
#ifdef CONFIG_DEBUG_MUTEXES
82-
# include <linux/mutex-debug.h>
84+
85+
#define __DEBUG_MUTEX_INITIALIZER(lockname) \
86+
, .magic = &lockname
87+
88+
extern void mutex_destroy(struct mutex *lock);
89+
8390
#else
91+
8492
# define __DEBUG_MUTEX_INITIALIZER(lockname)
93+
94+
static inline void mutex_destroy(struct mutex *lock) {}
95+
96+
#endif
97+
8598
/**
8699
* mutex_init - initialize the mutex
87100
* @mutex: the mutex to be initialized
@@ -90,14 +103,12 @@ struct mutex_waiter {
90103
*
91104
* It is not allowed to initialize an already locked mutex.
92105
*/
93-
# define mutex_init(mutex) \
94-
do { \
95-
static struct lock_class_key __key; \
96-
\
97-
__mutex_init((mutex), #mutex, &__key); \
106+
#define mutex_init(mutex) \
107+
do { \
108+
static struct lock_class_key __key; \
109+
\
110+
__mutex_init((mutex), #mutex, &__key); \
98111
} while (0)
99-
static inline void mutex_destroy(struct mutex *lock) {}
100-
#endif
101112

102113
#ifdef CONFIG_DEBUG_LOCK_ALLOC
103114
# define __DEP_MAP_MUTEX_INITIALIZER(lockname) \
@@ -107,7 +118,7 @@ static inline void mutex_destroy(struct mutex *lock) {}
107118
#endif
108119

109120
#define __MUTEX_INITIALIZER(lockname) \
110-
{ .count = ATOMIC_INIT(1) \
121+
{ .owner = ATOMIC_LONG_INIT(0) \
111122
, .wait_lock = __SPIN_LOCK_UNLOCKED(lockname.wait_lock) \
112123
, .wait_list = LIST_HEAD_INIT(lockname.wait_list) \
113124
__DEBUG_MUTEX_INITIALIZER(lockname) \
@@ -127,7 +138,10 @@ extern void __mutex_init(struct mutex *lock, const char *name,
127138
*/
128139
static inline int mutex_is_locked(struct mutex *lock)
129140
{
130-
return atomic_read(&lock->count) != 1;
141+
/*
142+
* XXX think about spin_is_locked
143+
*/
144+
return __mutex_owner(lock) != NULL;
131145
}
132146

133147
/*

kernel/locking/mutex-debug.c

Lines changed: 0 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -73,21 +73,8 @@ void debug_mutex_unlock(struct mutex *lock)
7373
{
7474
if (likely(debug_locks)) {
7575
DEBUG_LOCKS_WARN_ON(lock->magic != lock);
76-
77-
if (!lock->owner)
78-
DEBUG_LOCKS_WARN_ON(!lock->owner);
79-
else
80-
DEBUG_LOCKS_WARN_ON(lock->owner != current);
81-
8276
DEBUG_LOCKS_WARN_ON(!lock->wait_list.prev && !lock->wait_list.next);
8377
}
84-
85-
/*
86-
* __mutex_slowpath_needs_to_unlock() is explicitly 0 for debug
87-
* mutexes so that we can do it here after we've verified state.
88-
*/
89-
mutex_clear_owner(lock);
90-
atomic_set(&lock->count, 1);
9178
}
9279

9380
void debug_mutex_init(struct mutex *lock, const char *name,

kernel/locking/mutex-debug.h

Lines changed: 0 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -27,16 +27,6 @@ extern void debug_mutex_unlock(struct mutex *lock);
2727
extern void debug_mutex_init(struct mutex *lock, const char *name,
2828
struct lock_class_key *key);
2929

30-
static inline void mutex_set_owner(struct mutex *lock)
31-
{
32-
WRITE_ONCE(lock->owner, current);
33-
}
34-
35-
static inline void mutex_clear_owner(struct mutex *lock)
36-
{
37-
WRITE_ONCE(lock->owner, NULL);
38-
}
39-
4030
#define spin_lock_mutex(lock, flags) \
4131
do { \
4232
struct mutex *l = container_of(lock, struct mutex, wait_lock); \

0 commit comments

Comments
 (0)