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>

authored by Waiman Long and committed by Ingo Molnar d7d760ef 67b8d5c7

+28 -21
+7 -10
kernel/locking/rwsem-xadd.c
··· 357 358 rcu_read_lock(); 359 owner = READ_ONCE(sem->owner); 360 - if (!rwsem_owner_is_writer(owner)) { 361 - /* 362 - * Don't spin if the rwsem is readers owned. 363 - */ 364 - ret = !rwsem_owner_is_reader(owner); 365 goto done; 366 } 367 ··· 379 { 380 struct task_struct *owner = READ_ONCE(sem->owner); 381 382 - if (!rwsem_owner_is_writer(owner)) 383 - goto out; 384 385 rcu_read_lock(); 386 - while (sem->owner == owner) { 387 /* 388 * Ensure we emit the owner->on_cpu, dereference _after_ 389 * checking sem->owner still matches owner, if that fails, ··· 405 cpu_relax(); 406 } 407 rcu_read_unlock(); 408 - out: 409 /* 410 * If there is a new owner or the owner is not set, we continue 411 * spinning. 412 */ 413 - return !rwsem_owner_is_reader(READ_ONCE(sem->owner)); 414 } 415 416 static bool rwsem_optimistic_spin(struct rw_semaphore *sem)
··· 357 358 rcu_read_lock(); 359 owner = READ_ONCE(sem->owner); 360 + if (!owner || !is_rwsem_owner_spinnable(owner)) { 361 + ret = !owner; /* !owner is spinnable */ 362 goto done; 363 } 364 ··· 382 { 383 struct task_struct *owner = READ_ONCE(sem->owner); 384 385 + if (!is_rwsem_owner_spinnable(owner)) 386 + return false; 387 388 rcu_read_lock(); 389 + while (owner && (READ_ONCE(sem->owner) == owner)) { 390 /* 391 * Ensure we emit the owner->on_cpu, dereference _after_ 392 * checking sem->owner still matches owner, if that fails, ··· 408 cpu_relax(); 409 } 410 rcu_read_unlock(); 411 + 412 /* 413 * If there is a new owner or the owner is not set, we continue 414 * spinning. 415 */ 416 + return is_rwsem_owner_spinnable(READ_ONCE(sem->owner)); 417 } 418 419 static bool rwsem_optimistic_spin(struct rw_semaphore *sem)
-2
kernel/locking/rwsem.c
··· 221 EXPORT_SYMBOL(up_read_non_owner); 222 223 #endif 224 - 225 -
··· 221 EXPORT_SYMBOL(up_read_non_owner); 222 223 #endif
+21 -9
kernel/locking/rwsem.h
··· 1 /* SPDX-License-Identifier: GPL-2.0 */ 2 /* 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 5 * the owner field when it unlocks. A reader, on the other hand, will 6 * not touch the owner field when it unlocks. 7 * 8 - * In essence, the owner field now has the following 3 states: 9 * 1) 0 10 * - lock is free or the owner hasn't set the field yet 11 * 2) RWSEM_READER_OWNED 12 * - lock is currently or previously owned by readers (lock is free 13 * or not set by owner yet) 14 - * 3) Other non-zero value 15 - * - a writer owns the lock 16 */ 17 - #define RWSEM_READER_OWNED ((struct task_struct *)1UL) 18 19 #ifdef CONFIG_DEBUG_RWSEMS 20 # define DEBUG_RWSEMS_WARN_ON(c) DEBUG_LOCKS_WARN_ON(c) ··· 55 WRITE_ONCE(sem->owner, RWSEM_READER_OWNED); 56 } 57 58 - static inline bool rwsem_owner_is_writer(struct task_struct *owner) 59 { 60 - return owner && owner != RWSEM_READER_OWNED; 61 } 62 63 - static inline bool rwsem_owner_is_reader(struct task_struct *owner) 64 { 65 - return owner == RWSEM_READER_OWNED; 66 } 67 #else 68 static inline void rwsem_set_owner(struct rw_semaphore *sem)
··· 1 /* SPDX-License-Identifier: GPL-2.0 */ 2 /* 3 * The owner field of the rw_semaphore structure will be set to 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 6 * not touch the owner field when it unlocks. 7 * 8 + * In essence, the owner field now has the following 4 states: 9 * 1) 0 10 * - lock is free or the owner hasn't set the field yet 11 * 2) RWSEM_READER_OWNED 12 * - lock is currently or previously owned by readers (lock is free 13 * or not set by owner yet) 14 + * 3) RWSEM_ANONYMOUSLY_OWNED bit set with some other bits set as well 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. 19 */ 20 + #define RWSEM_ANONYMOUSLY_OWNED (1UL << 0) 21 + #define RWSEM_READER_OWNED ((struct task_struct *)RWSEM_ANONYMOUSLY_OWNED) 22 23 #ifdef CONFIG_DEBUG_RWSEMS 24 # define DEBUG_RWSEMS_WARN_ON(c) DEBUG_LOCKS_WARN_ON(c) ··· 51 WRITE_ONCE(sem->owner, RWSEM_READER_OWNED); 52 } 53 54 + /* 55 + * Return true if the a rwsem waiter can spin on the rwsem's owner 56 + * and steal the lock, i.e. the lock is not anonymously owned. 57 + * N.B. !owner is considered spinnable. 58 + */ 59 + static inline bool is_rwsem_owner_spinnable(struct task_struct *owner) 60 { 61 + return !((unsigned long)owner & RWSEM_ANONYMOUSLY_OWNED); 62 } 63 64 + /* 65 + * Return true if rwsem is owned by an anonymous writer or readers. 66 + */ 67 + static inline bool rwsem_has_anonymous_owner(struct task_struct *owner) 68 { 69 + return (unsigned long)owner & RWSEM_ANONYMOUSLY_OWNED; 70 } 71 #else 72 static inline void rwsem_set_owner(struct rw_semaphore *sem)