aboutsummaryrefslogtreecommitdiffstats
path: root/kernel/locking
diff options
context:
space:
mode:
authorWaiman Long <longman@redhat.com>2018-05-15 17:49:50 -0400
committerIngo Molnar <mingo@kernel.org>2018-05-16 05:45:15 -0400
commitd7d760efad70c7a030725499bf9f342f04af24dd (patch)
tree6d4d57b3349f9df4e0dab493b9202dd1073117f8 /kernel/locking
parent67b8d5c7081221efa252e111cd52532ec6d4266f (diff)
locking/rwsem: Add a new RWSEM_ANONYMOUSLY_OWNED flag
There are use cases where a rwsem can be acquired by one task, but released by another task. In thess cases, optimistic spinning may need to be disabled. One example will be the filesystem freeze/thaw code where the task that freezes the filesystem will acquire a write lock on a rwsem and then un-owns it before returning to userspace. Later on, another task will come along, acquire the ownership, thaw the filesystem and release the rwsem. Bit 0 of the owner field was used to designate that it is a reader owned rwsem. It is now repurposed to mean that the owner of the rwsem is not known. If only bit 0 is set, the rwsem is reader owned. If bit 0 and other bits are set, it is writer owned with an unknown owner. One such value for the latter case is (-1L). So we can set owner to 1 for reader-owned, -1 for writer-owned. The owner is unknown in both cases. To handle transfer of rwsem ownership, the higher level code should set the owner field to -1 to indicate a write-locked rwsem with unknown owner. Optimistic spinning will be disabled in this case. Once the higher level code figures who the new owner is, it can then set the owner field accordingly. Tested-by: Amir Goldstein <amir73il@gmail.com> Signed-off-by: Waiman Long <longman@redhat.com> Acked-by: Peter Zijlstra <peterz@infradead.org> Cc: Andrew Morton <akpm@linux-foundation.org> Cc: Davidlohr Bueso <dave@stgolabs.net> Cc: Jan Kara <jack@suse.cz> Cc: Linus Torvalds <torvalds@linux-foundation.org> Cc: Matthew Wilcox <willy@infradead.org> Cc: Oleg Nesterov <oleg@redhat.com> Cc: Paul E. McKenney <paulmck@linux.vnet.ibm.com> Cc: Theodore Y. Ts'o <tytso@mit.edu> Cc: Thomas Gleixner <tglx@linutronix.de> Cc: Will Deacon <will.deacon@arm.com> Cc: linux-fsdevel@vger.kernel.org Link: http://lkml.kernel.org/r/1526420991-21213-2-git-send-email-longman@redhat.com Signed-off-by: Ingo Molnar <mingo@kernel.org>
Diffstat (limited to 'kernel/locking')
-rw-r--r--kernel/locking/rwsem-xadd.c17
-rw-r--r--kernel/locking/rwsem.c2
-rw-r--r--kernel/locking/rwsem.h30
3 files changed, 28 insertions, 21 deletions
diff --git a/kernel/locking/rwsem-xadd.c b/kernel/locking/rwsem-xadd.c
index e795908f3607..604d247ea8c3 100644
--- a/kernel/locking/rwsem-xadd.c
+++ b/kernel/locking/rwsem-xadd.c
@@ -357,11 +357,8 @@ static inline bool rwsem_can_spin_on_owner(struct rw_semaphore *sem)
357 357
358 rcu_read_lock(); 358 rcu_read_lock();
359 owner = READ_ONCE(sem->owner); 359 owner = READ_ONCE(sem->owner);
360 if (!rwsem_owner_is_writer(owner)) { 360 if (!owner || !is_rwsem_owner_spinnable(owner)) {
361 /* 361 ret = !owner; /* !owner is spinnable */
362 * Don't spin if the rwsem is readers owned.
363 */
364 ret = !rwsem_owner_is_reader(owner);
365 goto done; 362 goto done;
366 } 363 }
367 364
@@ -382,11 +379,11 @@ static noinline bool rwsem_spin_on_owner(struct rw_semaphore *sem)
382{ 379{
383 struct task_struct *owner = READ_ONCE(sem->owner); 380 struct task_struct *owner = READ_ONCE(sem->owner);
384 381
385 if (!rwsem_owner_is_writer(owner)) 382 if (!is_rwsem_owner_spinnable(owner))
386 goto out; 383 return false;
387 384
388 rcu_read_lock(); 385 rcu_read_lock();
389 while (sem->owner == owner) { 386 while (owner && (READ_ONCE(sem->owner) == owner)) {
390 /* 387 /*
391 * Ensure we emit the owner->on_cpu, dereference _after_ 388 * Ensure we emit the owner->on_cpu, dereference _after_
392 * checking sem->owner still matches owner, if that fails, 389 * checking sem->owner still matches owner, if that fails,
@@ -408,12 +405,12 @@ static noinline bool rwsem_spin_on_owner(struct rw_semaphore *sem)
408 cpu_relax(); 405 cpu_relax();
409 } 406 }
410 rcu_read_unlock(); 407 rcu_read_unlock();
411out: 408
412 /* 409 /*
413 * If there is a new owner or the owner is not set, we continue 410 * If there is a new owner or the owner is not set, we continue
414 * spinning. 411 * spinning.
415 */ 412 */
416 return !rwsem_owner_is_reader(READ_ONCE(sem->owner)); 413 return is_rwsem_owner_spinnable(READ_ONCE(sem->owner));
417} 414}
418 415
419static bool rwsem_optimistic_spin(struct rw_semaphore *sem) 416static bool rwsem_optimistic_spin(struct rw_semaphore *sem)
diff --git a/kernel/locking/rwsem.c b/kernel/locking/rwsem.c
index 30465a2f2b6c..bc1e507be9ff 100644
--- a/kernel/locking/rwsem.c
+++ b/kernel/locking/rwsem.c
@@ -221,5 +221,3 @@ void up_read_non_owner(struct rw_semaphore *sem)
221EXPORT_SYMBOL(up_read_non_owner); 221EXPORT_SYMBOL(up_read_non_owner);
222 222
223#endif 223#endif
224
225
diff --git a/kernel/locking/rwsem.h b/kernel/locking/rwsem.h
index a17cba8d94bb..b9d0e72aa80f 100644
--- a/kernel/locking/rwsem.h
+++ b/kernel/locking/rwsem.h
@@ -1,20 +1,24 @@
1/* SPDX-License-Identifier: GPL-2.0 */ 1/* SPDX-License-Identifier: GPL-2.0 */
2/* 2/*
3 * The owner field of the rw_semaphore structure will be set to 3 * The owner field of the rw_semaphore structure will be set to
4 * RWSEM_READ_OWNED when a reader grabs the lock. A writer will clear 4 * RWSEM_READER_OWNED when a reader grabs the lock. A writer will clear
5 * the owner field when it unlocks. A reader, on the other hand, will 5 * the owner field when it unlocks. A reader, on the other hand, will
6 * not touch the owner field when it unlocks. 6 * not touch the owner field when it unlocks.
7 * 7 *
8 * In essence, the owner field now has the following 3 states: 8 * In essence, the owner field now has the following 4 states:
9 * 1) 0 9 * 1) 0
10 * - lock is free or the owner hasn't set the field yet 10 * - lock is free or the owner hasn't set the field yet
11 * 2) RWSEM_READER_OWNED 11 * 2) RWSEM_READER_OWNED
12 * - lock is currently or previously owned by readers (lock is free 12 * - lock is currently or previously owned by readers (lock is free
13 * or not set by owner yet) 13 * or not set by owner yet)
14 * 3) Other non-zero value 14 * 3) RWSEM_ANONYMOUSLY_OWNED bit set with some other bits set as well
15 * - a writer owns the lock 15 * - lock is owned by an anonymous writer, so spinning on the lock
16 * owner should be disabled.
17 * 4) Other non-zero value
18 * - a writer owns the lock and other writers can spin on the lock owner.
16 */ 19 */
17#define RWSEM_READER_OWNED ((struct task_struct *)1UL) 20#define RWSEM_ANONYMOUSLY_OWNED (1UL << 0)
21#define RWSEM_READER_OWNED ((struct task_struct *)RWSEM_ANONYMOUSLY_OWNED)
18 22
19#ifdef CONFIG_DEBUG_RWSEMS 23#ifdef CONFIG_DEBUG_RWSEMS
20# define DEBUG_RWSEMS_WARN_ON(c) DEBUG_LOCKS_WARN_ON(c) 24# define DEBUG_RWSEMS_WARN_ON(c) DEBUG_LOCKS_WARN_ON(c)
@@ -51,14 +55,22 @@ static inline void rwsem_set_reader_owned(struct rw_semaphore *sem)
51 WRITE_ONCE(sem->owner, RWSEM_READER_OWNED); 55 WRITE_ONCE(sem->owner, RWSEM_READER_OWNED);
52} 56}
53 57
54static inline bool rwsem_owner_is_writer(struct task_struct *owner) 58/*
59 * Return true if the a rwsem waiter can spin on the rwsem's owner
60 * and steal the lock, i.e. the lock is not anonymously owned.
61 * N.B. !owner is considered spinnable.
62 */
63static inline bool is_rwsem_owner_spinnable(struct task_struct *owner)
55{ 64{
56 return owner && owner != RWSEM_READER_OWNED; 65 return !((unsigned long)owner & RWSEM_ANONYMOUSLY_OWNED);
57} 66}
58 67
59static inline bool rwsem_owner_is_reader(struct task_struct *owner) 68/*
69 * Return true if rwsem is owned by an anonymous writer or readers.
70 */
71static inline bool rwsem_has_anonymous_owner(struct task_struct *owner)
60{ 72{
61 return owner == RWSEM_READER_OWNED; 73 return (unsigned long)owner & RWSEM_ANONYMOUSLY_OWNED;
62} 74}
63#else 75#else
64static inline void rwsem_set_owner(struct rw_semaphore *sem) 76static inline void rwsem_set_owner(struct rw_semaphore *sem)