History

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.

Waiman Long committed 8 years ago

Commit d7d760e1 parent 67b8d5c3 files · +28 −21Browse files

Showing the changes to kernel (3 of 3 files). Show all

Unified · Split

3 files
  1. kernel/locking/rwsem-xadd.c +7 −10
  2. kernel/locking/rwsem.c +0 −2
  3. kernel/locking/rwsem.h +21 −9
kernel/locking/rwsem-xadd.c+7−10
@@ -357,11 +357,8 @@ static inline bool rwsem_can_spin_on_owner(struct rw_semaphore *sem) · 356 unmodified lines
357357
358358 rcu_read_lock();
359359 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);
360+ if (!owner || !is_rwsem_owner_spinnable(owner)) {
361+ ret = !owner; /* !owner is spinnable */
365362 goto done;
366363 }
367364
@@ -382,11 +379,11 @@ static noinline bool rwsem_spin_on_owner(struct rw_semaphore *sem) · 14 unmodified lines
382379 {
383380 struct task_struct *owner = READ_ONCE(sem->owner);
384381
385− if (!rwsem_owner_is_writer(owner))
386− goto out;
382+ if (!is_rwsem_owner_spinnable(owner))
383+ return false;
387384
388385 rcu_read_lock();
389− while (sem->owner == owner) {
386+ while (owner && (READ_ONCE(sem->owner) == owner)) {
390387 /*
391388 * Ensure we emit the owner->on_cpu, dereference _after_
392389 * checking sem->owner still matches owner, if that fails,
@@ -408,12 +405,12 @@ static noinline bool rwsem_spin_on_owner(struct rw_semaphore *sem) · 15 unmodified lines
408405 cpu_relax();
409406 }
410407 rcu_read_unlock();
411−out:
408+
412409 /*
413410 * If there is a new owner or the owner is not set, we continue
414411 * spinning.
415412 */
416− return !rwsem_owner_is_reader(READ_ONCE(sem->owner));
413+ return is_rwsem_owner_spinnable(READ_ONCE(sem->owner));
417414 }
418415
419416 static bool rwsem_optimistic_spin(struct rw_semaphore *sem)
kernel/locking/rwsem.c+0−2
@@ -221,5 +221,3 @@ void up_read_non_owner(struct rw_semaphore *sem) · 220 unmodified lines
221221 EXPORT_SYMBOL(up_read_non_owner);
222222
223223 #endif
224−
225−
kernel/locking/rwsem.h+21−9
@@ -1,20 +1,24 @@
11 /* SPDX-License-Identifier: GPL-2.0 */
22 /*
33 * 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
55 * the owner field when it unlocks. A reader, on the other hand, will
66 * not touch the owner field when it unlocks.
77 *
8− * In essence, the owner field now has the following 3 states:
8+ * In essence, the owner field now has the following 4 states:
99 * 1) 0
1010 * - lock is free or the owner hasn't set the field yet
1111 * 2) RWSEM_READER_OWNED
1212 * - lock is currently or previously owned by readers (lock is free
1313 * or not set by owner yet)
14− * 3) Other non-zero value
15− * - a writer owns the lock
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.
1619 */
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)
1822
1923 #ifdef CONFIG_DEBUG_RWSEMS
2024 # 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) · 30 unmodified lines
5155 WRITE_ONCE(sem->owner, RWSEM_READER_OWNED);
5256 }
5357
54−static 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+ */
63+static inline bool is_rwsem_owner_spinnable(struct task_struct *owner)
5564 {
56− return owner && owner != RWSEM_READER_OWNED;
65+ return !((unsigned long)owner & RWSEM_ANONYMOUSLY_OWNED);
5766 }
5867
59−static 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+ */
71+static inline bool rwsem_has_anonymous_owner(struct task_struct *owner)
6072 {
61− return owner == RWSEM_READER_OWNED;
73+ return (unsigned long)owner & RWSEM_ANONYMOUSLY_OWNED;
6274 }
6375 #else
6476 static inline void rwsem_set_owner(struct rw_semaphore *sem)