Skip to content

Commit 94a9717

Browse files
Waiman-LongIngo Molnar
authored andcommitted
locking/rwsem: Make rwsem->owner an atomic_long_t
The rwsem->owner contains not just the task structure pointer, it also holds some flags for storing the current state of the rwsem. Some of the flags may have to be atomically updated. To reflect the new reality, the owner is now changed to an atomic_long_t type. New helper functions are added to properly separate out the task structure pointer and the embedded flags. Suggested-by: Peter Zijlstra <peterz@infradead.org> Signed-off-by: Waiman Long <longman@redhat.com> Signed-off-by: Peter Zijlstra (Intel) <peterz@infradead.org> Cc: Borislav Petkov <bp@alien8.de> Cc: Davidlohr Bueso <dave@stgolabs.net> Cc: H. Peter Anvin <hpa@zytor.com> Cc: Linus Torvalds <torvalds@linux-foundation.org> Cc: Thomas Gleixner <tglx@linutronix.de> Cc: Tim Chen <tim.c.chen@linux.intel.com> Cc: Will Deacon <will.deacon@arm.com> Cc: huang ying <huang.ying.caritas@gmail.com> Link: https://lkml.kernel.org/r/20190520205918.22251-14-longman@redhat.com Signed-off-by: Ingo Molnar <mingo@kernel.org>
1 parent cf69482 commit 94a9717

File tree

3 files changed

+88
-52
lines changed

3 files changed

+88
-52
lines changed

include/linux/percpu-rwsem.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -117,7 +117,7 @@ static inline void percpu_rwsem_release(struct percpu_rw_semaphore *sem,
117117
lock_release(&sem->rw_sem.dep_map, 1, ip);
118118
#ifdef CONFIG_RWSEM_SPIN_ON_OWNER
119119
if (!read)
120-
sem->rw_sem.owner = RWSEM_OWNER_UNKNOWN;
120+
atomic_long_set(&sem->rw_sem.owner, RWSEM_OWNER_UNKNOWN);
121121
#endif
122122
}
123123

@@ -127,7 +127,7 @@ static inline void percpu_rwsem_acquire(struct percpu_rw_semaphore *sem,
127127
lock_acquire(&sem->rw_sem.dep_map, 0, 1, read, 1, NULL, ip);
128128
#ifdef CONFIG_RWSEM_SPIN_ON_OWNER
129129
if (!read)
130-
sem->rw_sem.owner = current;
130+
atomic_long_set(&sem->rw_sem.owner, (long)current);
131131
#endif
132132
}
133133

include/linux/rwsem.h

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -35,10 +35,11 @@
3535
struct rw_semaphore {
3636
atomic_long_t count;
3737
/*
38-
* Write owner or one of the read owners. Can be used as a
39-
* speculative check to see if the owner is running on the cpu.
38+
* Write owner or one of the read owners as well flags regarding
39+
* the current state of the rwsem. Can be used as a speculative
40+
* check to see if the write owner is running on the cpu.
4041
*/
41-
struct task_struct *owner;
42+
atomic_long_t owner;
4243
#ifdef CONFIG_RWSEM_SPIN_ON_OWNER
4344
struct optimistic_spin_queue osq; /* spinner MCS lock */
4445
#endif
@@ -53,7 +54,7 @@ struct rw_semaphore {
5354
* Setting all bits of the owner field except bit 0 will indicate
5455
* that the rwsem is writer-owned with an unknown owner.
5556
*/
56-
#define RWSEM_OWNER_UNKNOWN ((struct task_struct *)-2L)
57+
#define RWSEM_OWNER_UNKNOWN (-2L)
5758

5859
/* In all implementations count != 0 means locked */
5960
static inline int rwsem_is_locked(struct rw_semaphore *sem)
@@ -80,7 +81,7 @@ static inline int rwsem_is_locked(struct rw_semaphore *sem)
8081

8182
#define __RWSEM_INITIALIZER(name) \
8283
{ __RWSEM_INIT_COUNT(name), \
83-
.owner = NULL, \
84+
.owner = ATOMIC_LONG_INIT(0), \
8485
.wait_list = LIST_HEAD_INIT((name).wait_list), \
8586
.wait_lock = __RAW_SPIN_LOCK_UNLOCKED(name.wait_lock) \
8687
__RWSEM_OPT_INIT(name) \

kernel/locking/rwsem.c

Lines changed: 80 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,7 @@
6464
if (!debug_locks_silent && \
6565
WARN_ONCE(c, "DEBUG_RWSEMS_WARN_ON(%s): count = 0x%lx, owner = 0x%lx, curr 0x%lx, list %sempty\n",\
6666
#c, atomic_long_read(&(sem)->count), \
67-
(long)((sem)->owner), (long)current, \
67+
atomic_long_read(&(sem)->owner), (long)current, \
6868
list_empty(&(sem)->wait_list) ? "" : "not ")) \
6969
debug_locks_off(); \
7070
} while (0)
@@ -114,12 +114,20 @@
114114
*/
115115
static inline void rwsem_set_owner(struct rw_semaphore *sem)
116116
{
117-
WRITE_ONCE(sem->owner, current);
117+
atomic_long_set(&sem->owner, (long)current);
118118
}
119119

120120
static inline void rwsem_clear_owner(struct rw_semaphore *sem)
121121
{
122-
WRITE_ONCE(sem->owner, NULL);
122+
atomic_long_set(&sem->owner, 0);
123+
}
124+
125+
/*
126+
* Test the flags in the owner field.
127+
*/
128+
static inline bool rwsem_test_oflags(struct rw_semaphore *sem, long flags)
129+
{
130+
return atomic_long_read(&sem->owner) & flags;
123131
}
124132

125133
/*
@@ -133,10 +141,9 @@ static inline void rwsem_clear_owner(struct rw_semaphore *sem)
133141
static inline void __rwsem_set_reader_owned(struct rw_semaphore *sem,
134142
struct task_struct *owner)
135143
{
136-
unsigned long val = (unsigned long)owner | RWSEM_READER_OWNED
137-
| RWSEM_NONSPINNABLE;
144+
unsigned long val = (unsigned long)owner | RWSEM_READER_OWNED | RWSEM_NONSPINNABLE;
138145

139-
WRITE_ONCE(sem->owner, (struct task_struct *)val);
146+
atomic_long_set(&sem->owner, val);
140147
}
141148

142149
static inline void rwsem_set_reader_owned(struct rw_semaphore *sem)
@@ -145,13 +152,20 @@ static inline void rwsem_set_reader_owned(struct rw_semaphore *sem)
145152
}
146153

147154
/*
148-
* Return true if the a rwsem waiter can spin on the rwsem's owner
149-
* and steal the lock.
150-
* N.B. !owner is considered spinnable.
155+
* Return true if the rwsem is owned by a reader.
151156
*/
152-
static inline bool is_rwsem_owner_spinnable(struct task_struct *owner)
157+
static inline bool is_rwsem_reader_owned(struct rw_semaphore *sem)
153158
{
154-
return !((unsigned long)owner & RWSEM_NONSPINNABLE);
159+
#ifdef CONFIG_DEBUG_RWSEMS
160+
/*
161+
* Check the count to see if it is write-locked.
162+
*/
163+
long count = atomic_long_read(&sem->count);
164+
165+
if (count & RWSEM_WRITER_MASK)
166+
return false;
167+
#endif
168+
return rwsem_test_oflags(sem, RWSEM_READER_OWNED);
155169
}
156170

157171
#ifdef CONFIG_DEBUG_RWSEMS
@@ -163,18 +177,42 @@ static inline bool is_rwsem_owner_spinnable(struct task_struct *owner)
163177
*/
164178
static inline void rwsem_clear_reader_owned(struct rw_semaphore *sem)
165179
{
166-
unsigned long val = (unsigned long)current | RWSEM_READER_OWNED
167-
| RWSEM_NONSPINNABLE;
168-
if (READ_ONCE(sem->owner) == (struct task_struct *)val)
169-
cmpxchg_relaxed((unsigned long *)&sem->owner, val,
170-
RWSEM_READER_OWNED | RWSEM_NONSPINNABLE);
180+
unsigned long val = atomic_long_read(&sem->owner);
181+
182+
while ((val & ~RWSEM_OWNER_FLAGS_MASK) == (unsigned long)current) {
183+
if (atomic_long_try_cmpxchg(&sem->owner, &val,
184+
val & RWSEM_OWNER_FLAGS_MASK))
185+
return;
186+
}
171187
}
172188
#else
173189
static inline void rwsem_clear_reader_owned(struct rw_semaphore *sem)
174190
{
175191
}
176192
#endif
177193

194+
/*
195+
* Return just the real task structure pointer of the owner
196+
*/
197+
static inline struct task_struct *rwsem_owner(struct rw_semaphore *sem)
198+
{
199+
return (struct task_struct *)
200+
(atomic_long_read(&sem->owner) & ~RWSEM_OWNER_FLAGS_MASK);
201+
}
202+
203+
/*
204+
* Return the real task structure pointer of the owner and the embedded
205+
* flags in the owner. pflags must be non-NULL.
206+
*/
207+
static inline struct task_struct *
208+
rwsem_owner_flags(struct rw_semaphore *sem, unsigned long *pflags)
209+
{
210+
unsigned long owner = atomic_long_read(&sem->owner);
211+
212+
*pflags = owner & RWSEM_OWNER_FLAGS_MASK;
213+
return (struct task_struct *)(owner & ~RWSEM_OWNER_FLAGS_MASK);
214+
}
215+
178216
/*
179217
* Guide to the rw_semaphore's count field.
180218
*
@@ -208,7 +246,7 @@ void __init_rwsem(struct rw_semaphore *sem, const char *name,
208246
atomic_long_set(&sem->count, RWSEM_UNLOCKED_VALUE);
209247
raw_spin_lock_init(&sem->wait_lock);
210248
INIT_LIST_HEAD(&sem->wait_list);
211-
sem->owner = NULL;
249+
atomic_long_set(&sem->owner, 0L);
212250
#ifdef CONFIG_RWSEM_SPIN_ON_OWNER
213251
osq_lock_init(&sem->osq);
214252
#endif
@@ -511,9 +549,10 @@ static inline bool owner_on_cpu(struct task_struct *owner)
511549
static inline bool rwsem_can_spin_on_owner(struct rw_semaphore *sem)
512550
{
513551
struct task_struct *owner;
552+
unsigned long flags;
514553
bool ret = true;
515554

516-
BUILD_BUG_ON(is_rwsem_owner_spinnable(RWSEM_OWNER_UNKNOWN));
555+
BUILD_BUG_ON(!(RWSEM_OWNER_UNKNOWN & RWSEM_NONSPINNABLE));
517556

518557
if (need_resched()) {
519558
lockevent_inc(rwsem_opt_fail);
@@ -522,11 +561,9 @@ static inline bool rwsem_can_spin_on_owner(struct rw_semaphore *sem)
522561

523562
preempt_disable();
524563
rcu_read_lock();
525-
owner = READ_ONCE(sem->owner);
526-
if (owner) {
527-
ret = is_rwsem_owner_spinnable(owner) &&
528-
owner_on_cpu(owner);
529-
}
564+
owner = rwsem_owner_flags(sem, &flags);
565+
if ((flags & RWSEM_NONSPINNABLE) || (owner && !owner_on_cpu(owner)))
566+
ret = false;
530567
rcu_read_unlock();
531568
preempt_enable();
532569

@@ -553,25 +590,26 @@ enum owner_state {
553590
};
554591
#define OWNER_SPINNABLE (OWNER_NULL | OWNER_WRITER)
555592

556-
static inline enum owner_state rwsem_owner_state(unsigned long owner)
593+
static inline enum owner_state
594+
rwsem_owner_state(struct task_struct *owner, unsigned long flags)
557595
{
558-
if (!owner)
559-
return OWNER_NULL;
560-
561-
if (owner & RWSEM_NONSPINNABLE)
596+
if (flags & RWSEM_NONSPINNABLE)
562597
return OWNER_NONSPINNABLE;
563598

564-
if (owner & RWSEM_READER_OWNED)
599+
if (flags & RWSEM_READER_OWNED)
565600
return OWNER_READER;
566601

567-
return OWNER_WRITER;
602+
return owner ? OWNER_WRITER : OWNER_NULL;
568603
}
569604

570605
static noinline enum owner_state rwsem_spin_on_owner(struct rw_semaphore *sem)
571606
{
572-
struct task_struct *tmp, *owner = READ_ONCE(sem->owner);
573-
enum owner_state state = rwsem_owner_state((unsigned long)owner);
607+
struct task_struct *new, *owner;
608+
unsigned long flags, new_flags;
609+
enum owner_state state;
574610

611+
owner = rwsem_owner_flags(sem, &flags);
612+
state = rwsem_owner_state(owner, flags);
575613
if (state != OWNER_WRITER)
576614
return state;
577615

@@ -582,9 +620,9 @@ static noinline enum owner_state rwsem_spin_on_owner(struct rw_semaphore *sem)
582620
break;
583621
}
584622

585-
tmp = READ_ONCE(sem->owner);
586-
if (tmp != owner) {
587-
state = rwsem_owner_state((unsigned long)tmp);
623+
new = rwsem_owner_flags(sem, &new_flags);
624+
if ((new != owner) || (new_flags != flags)) {
625+
state = rwsem_owner_state(new, new_flags);
588626
break;
589627
}
590628

@@ -1001,8 +1039,7 @@ inline void __down_read(struct rw_semaphore *sem)
10011039
if (unlikely(atomic_long_fetch_add_acquire(RWSEM_READER_BIAS,
10021040
&sem->count) & RWSEM_READ_FAILED_MASK)) {
10031041
rwsem_down_read_slowpath(sem, TASK_UNINTERRUPTIBLE);
1004-
DEBUG_RWSEMS_WARN_ON(!((unsigned long)sem->owner &
1005-
RWSEM_READER_OWNED), sem);
1042+
DEBUG_RWSEMS_WARN_ON(!is_rwsem_reader_owned(sem), sem);
10061043
} else {
10071044
rwsem_set_reader_owned(sem);
10081045
}
@@ -1014,8 +1051,7 @@ static inline int __down_read_killable(struct rw_semaphore *sem)
10141051
&sem->count) & RWSEM_READ_FAILED_MASK)) {
10151052
if (IS_ERR(rwsem_down_read_slowpath(sem, TASK_KILLABLE)))
10161053
return -EINTR;
1017-
DEBUG_RWSEMS_WARN_ON(!((unsigned long)sem->owner &
1018-
RWSEM_READER_OWNED), sem);
1054+
DEBUG_RWSEMS_WARN_ON(!is_rwsem_reader_owned(sem), sem);
10191055
} else {
10201056
rwsem_set_reader_owned(sem);
10211057
}
@@ -1084,7 +1120,7 @@ inline void __up_read(struct rw_semaphore *sem)
10841120
{
10851121
long tmp;
10861122

1087-
DEBUG_RWSEMS_WARN_ON(!((unsigned long)sem->owner & RWSEM_READER_OWNED), sem);
1123+
DEBUG_RWSEMS_WARN_ON(!is_rwsem_reader_owned(sem), sem);
10881124
rwsem_clear_reader_owned(sem);
10891125
tmp = atomic_long_add_return_release(-RWSEM_READER_BIAS, &sem->count);
10901126
if (unlikely((tmp & (RWSEM_LOCK_MASK|RWSEM_FLAG_WAITERS)) ==
@@ -1103,8 +1139,8 @@ static inline void __up_write(struct rw_semaphore *sem)
11031139
* sem->owner may differ from current if the ownership is transferred
11041140
* to an anonymous writer by setting the RWSEM_NONSPINNABLE bits.
11051141
*/
1106-
DEBUG_RWSEMS_WARN_ON((sem->owner != current) &&
1107-
!((long)sem->owner & RWSEM_NONSPINNABLE), sem);
1142+
DEBUG_RWSEMS_WARN_ON((rwsem_owner(sem) != current) &&
1143+
!rwsem_test_oflags(sem, RWSEM_NONSPINNABLE), sem);
11081144
rwsem_clear_owner(sem);
11091145
tmp = atomic_long_fetch_add_release(-RWSEM_WRITER_LOCKED, &sem->count);
11101146
if (unlikely(tmp & RWSEM_FLAG_WAITERS))
@@ -1125,7 +1161,7 @@ static inline void __downgrade_write(struct rw_semaphore *sem)
11251161
* read-locked region is ok to be re-ordered into the
11261162
* write side. As such, rely on RELEASE semantics.
11271163
*/
1128-
DEBUG_RWSEMS_WARN_ON(sem->owner != current, sem);
1164+
DEBUG_RWSEMS_WARN_ON(rwsem_owner(sem) != current, sem);
11291165
tmp = atomic_long_fetch_add_release(
11301166
-RWSEM_WRITER_LOCKED+RWSEM_READER_BIAS, &sem->count);
11311167
rwsem_set_reader_owned(sem);
@@ -1296,8 +1332,7 @@ EXPORT_SYMBOL(down_write_killable_nested);
12961332

12971333
void up_read_non_owner(struct rw_semaphore *sem)
12981334
{
1299-
DEBUG_RWSEMS_WARN_ON(!((unsigned long)sem->owner & RWSEM_READER_OWNED),
1300-
sem);
1335+
DEBUG_RWSEMS_WARN_ON(!is_rwsem_reader_owned(sem), sem);
13011336
__up_read(sem);
13021337
}
13031338
EXPORT_SYMBOL(up_read_non_owner);

0 commit comments

Comments
 (0)