From: Waiman Long <waiman.long@hpe.com>
To: Waiman Long <Waiman.Long@hpe.com>
Cc: Ingo Molnar <mingo@redhat.com>,
Peter Zijlstra <peterz@infradead.org>,
<linux-kernel@vger.kernel.org>,
Linus Torvalds <torvalds@linux-foundation.org>,
Ding Tianhong <dingtianhong@huawei.com>,
Jason Low <jason.low2@hpe.com>,
Davidlohr Bueso <dave@stgolabs.net>,
"Paul E. McKenney" <paulmck@us.ibm.com>,
Thomas Gleixner <tglx@linutronix.de>,
Will Deacon <Will.Deacon@arm.com>,
Tim Chen <tim.c.chen@linux.intel.com>,
Imre Deak <imre.deak@intel.com>
Subject: Re: [PATCH v5 3/3] locking/mutex: Ensure forward progress of waiter-spinner
Date: Thu, 11 Aug 2016 11:01:27 -0400 [thread overview]
Message-ID: <57AC9347.1060105@hpe.com> (raw)
In-Reply-To: <1470853530-37540-4-git-send-email-Waiman.Long@hpe.com>
On 08/10/2016 02:25 PM, Waiman Long wrote:
> As both an optimistic spinner and a waiter-spinner (a woken task from
> the wait queue spinning) can be spinning on the lock at the same time,
> we cannot ensure forward progress for the waiter-spinner. So it is
> possible for the waiter-spinner to be starved of getting the lock,
> though not likely.
>
> This patch adds a flag to indicate that a waiter-spinner is
> spinning and hence has priority over the acquisition of the lock. A
> waiter-spinner sets this flag while spinning. An optimistic spinner
> will check this flag and yield if set. This essentially makes the
> waiter-spinner jump to the head of the optimistic spinning queue to
> acquire the lock.
>
> There will be no increase in size for the mutex structure for
> 64-bit architectures as there is an existing 4-byte hole. For 32-bit
> architectures, there will be a size increase of 4 bytes.
>
> Signed-off-by: Waiman Long<Waiman.Long@hpe.com>
> ---
> include/linux/mutex.h | 1 +
> kernel/locking/mutex.c | 36 +++++++++++++++++++++++++++---------
> 2 files changed, 28 insertions(+), 9 deletions(-)
>
> diff --git a/include/linux/mutex.h b/include/linux/mutex.h
> index 2cb7531..f8e91ad 100644
> --- a/include/linux/mutex.h
> +++ b/include/linux/mutex.h
> @@ -57,6 +57,7 @@ struct mutex {
> #endif
> #ifdef CONFIG_MUTEX_SPIN_ON_OWNER
> struct optimistic_spin_queue osq; /* Spinner MCS lock */
> + int waiter_spinning;
> #endif
> #ifdef CONFIG_DEBUG_MUTEXES
> void *magic;
> diff --git a/kernel/locking/mutex.c b/kernel/locking/mutex.c
> index 15b521a..0912964 100644
> --- a/kernel/locking/mutex.c
> +++ b/kernel/locking/mutex.c
> @@ -55,6 +55,7 @@ __mutex_init(struct mutex *lock, const char *name, struct lock_class_key *key)
> mutex_clear_owner(lock);
> #ifdef CONFIG_MUTEX_SPIN_ON_OWNER
> osq_lock_init(&lock->osq);
> + lock->waiter_spinning = false;
> #endif
>
> debug_mutex_init(lock, name, key);
> @@ -337,9 +338,21 @@ static bool mutex_optimistic_spin(struct mutex *lock,
> */
> if (!osq_lock(&lock->osq))
> goto done;
> + } else {
> + /*
> + * Turn on the waiter spinning flag to discourage the spinner
> + * from getting the lock.
> + */
> + lock->waiter_spinning = true;
> }
>
> - while (true) {
> + /*
> + * The cpu_relax_lowlatency() call is a compiler barrier which forces
> + * everything in this loop to be re-loaded. We don't need memory
> + * barriers as we'll eventually observe the right values at the cost
> + * of a few extra spins.
> + */
> + for (;; cpu_relax_lowlatency()) {
> struct task_struct *owner;
>
> if (use_ww_ctx&& ww_ctx->acquired> 0) {
> @@ -359,6 +372,17 @@ static bool mutex_optimistic_spin(struct mutex *lock,
> }
>
> /*
> + * For regular opt-spinner, it waits until the waiter_spinning
> + * flag isn't set. This will ensure forward progress for
> + * the waiter spinner.
> + */
> + if (!waiter&& READ_ONCE(lock->waiter_spinning)) {
> + if (need_resched())
> + break;
> + continue;
> + }
> +
> + /*
> * If there's an owner, wait for it to either
> * release the lock or go to sleep.
> */
> @@ -390,18 +414,12 @@ static bool mutex_optimistic_spin(struct mutex *lock,
> */
> if (!owner&& (need_resched() || rt_task(task)))
> break;
> -
> - /*
> - * The cpu_relax() call is a compiler barrier which forces
> - * everything in this loop to be re-loaded. We don't need
> - * memory barriers as we'll eventually observe the right
> - * values at the cost of a few extra spins.
> - */
> - cpu_relax_lowlatency();
> }
>
> if (!waiter)
> osq_unlock(&lock->osq);
> + else
> + lock->waiter_spinning = false;
> done:
> /*
> * If we fell out of the spin path because of need_resched(),
The following is the updated patch that should fix the build error in
non-x86 platform.
Cheers,
Longman
================================ cut here ================================
locking/mutex: Ensure forward progress of waiter-spinner
As both an optimistic spinner and a waiter-spinner (a woken task from
the wait queue spinning) can be spinning on the lock at the same time,
we cannot ensure forward progress for the waiter-spinner. So it is
possible for the waiter-spinner to be starved of getting the lock,
though not likely.
This patch adds a flag to indicate that a waiter-spinner is
spinning and hence has priority over the acquisition of the lock. A
waiter-spinner sets this flag while spinning. An optimistic spinner
will check this flag and yield if set. This essentially makes the
waiter-spinner jump to the head of the optimistic spinning queue to
acquire the lock.
There will be no increase in size for the mutex structure for
64-bit architectures as there is an existing 4-byte hole. For 32-bit
architectures, there will be a size increase of 4 bytes.
Signed-off-by: Waiman Long <Waiman.Long@hpe.com>
---
include/linux/mutex.h | 1 +
kernel/locking/mutex.c | 21 +++++++++++++++++++++
2 files changed, 22 insertions(+), 0 deletions(-)
diff --git a/include/linux/mutex.h b/include/linux/mutex.h
index 2cb7531..f8e91ad 100644
--- a/include/linux/mutex.h
+++ b/include/linux/mutex.h
@@ -57,6 +57,7 @@ struct mutex {
#endif
#ifdef CONFIG_MUTEX_SPIN_ON_OWNER
struct optimistic_spin_queue osq; /* Spinner MCS lock */
+ int waiter_spinning;
#endif
#ifdef CONFIG_DEBUG_MUTEXES
void *magic;
diff --git a/kernel/locking/mutex.c b/kernel/locking/mutex.c
index 15b521a..02d8029 100644
--- a/kernel/locking/mutex.c
+++ b/kernel/locking/mutex.c
@@ -55,6 +55,7 @@ __mutex_init(struct mutex *lock, const char *name,
struct lock_class_key *key)
mutex_clear_owner(lock);
#ifdef CONFIG_MUTEX_SPIN_ON_OWNER
osq_lock_init(&lock->osq);
+ lock->waiter_spinning = false;
#endif
debug_mutex_init(lock, name, key);
@@ -337,6 +338,12 @@ static bool mutex_optimistic_spin(struct mutex *lock,
*/
if (!osq_lock(&lock->osq))
goto done;
+ } else {
+ /*
+ * Turn on the waiter spinning flag to discourage the spinner
+ * from getting the lock.
+ */
+ lock->waiter_spinning = true;
}
while (true) {
@@ -359,6 +366,17 @@ static bool mutex_optimistic_spin(struct mutex *lock,
}
/*
+ * For regular opt-spinner, it waits until the waiter_spinning
+ * flag isn't set. This will ensure forward progress for
+ * the waiter spinner.
+ */
+ if (!waiter && READ_ONCE(lock->waiter_spinning)) {
+ if (need_resched())
+ break;
+ goto relax;
+ }
+
+ /*
* If there's an owner, wait for it to either
* release the lock or go to sleep.
*/
@@ -391,6 +409,7 @@ static bool mutex_optimistic_spin(struct mutex *lock,
if (!owner && (need_resched() || rt_task(task)))
break;
+relax:
/*
* The cpu_relax() call is a compiler barrier which forces
* everything in this loop to be re-loaded. We don't need
@@ -402,6 +421,8 @@ static bool mutex_optimistic_spin(struct mutex *lock,
if (!waiter)
osq_unlock(&lock->osq);
+ else
+ lock->waiter_spinning = false;
done:
/*
* If we fell out of the spin path because of need_resched(),
--
1.7.1
next prev parent reply other threads:[~2016-08-11 15:01 UTC|newest]
Thread overview: 12+ messages / expand[flat|nested] mbox.gz Atom feed top
2016-08-10 18:25 [PATCH v5 0/3] locking/mutex: Enable optimistic spinning of lock waiter Waiman Long
2016-08-10 18:25 ` [PATCH v5 1/3] locking/mutex: Add waiter parameter to mutex_optimistic_spin() Waiman Long
2016-08-10 18:25 ` [PATCH v5 2/3] locking/mutex: Enable optimistic spinning of woken task in wait queue Waiman Long
2016-08-10 18:25 ` [PATCH v5 3/3] locking/mutex: Ensure forward progress of waiter-spinner Waiman Long
2016-08-11 1:18 ` kbuild test robot
2016-08-11 1:28 ` kbuild test robot
2016-08-11 2:00 ` kbuild test robot
2016-08-11 15:01 ` Waiman Long [this message]
2016-08-18 15:58 ` Peter Zijlstra
2016-08-18 16:52 ` Imre Deak
2016-08-18 18:04 ` Jason Low
2016-08-18 19:44 ` Peter Zijlstra
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=57AC9347.1060105@hpe.com \
--to=waiman.long@hpe.com \
--cc=Will.Deacon@arm.com \
--cc=dave@stgolabs.net \
--cc=dingtianhong@huawei.com \
--cc=imre.deak@intel.com \
--cc=jason.low2@hpe.com \
--cc=linux-kernel@vger.kernel.org \
--cc=mingo@redhat.com \
--cc=paulmck@us.ibm.com \
--cc=peterz@infradead.org \
--cc=tglx@linutronix.de \
--cc=tim.c.chen@linux.intel.com \
--cc=torvalds@linux-foundation.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.