* Re: [PATCH v5 4/4] MCS Lock: Barrier corrections
[not found] <20131112160827.GB25953@mudshark.cambridge.arm.com>
@ 2013-11-12 17:16 ` George Spelvin
0 siblings, 0 replies; 5+ messages in thread
From: George Spelvin @ 2013-11-12 17:16 UTC (permalink / raw)
To: tim.c.chen, will.deacon
Cc: a.p.zijlstra, aarcange, akpm, alex.shi, andi, arnd, aswin,
dave.hansen, davidlohr.bueso, figo1802, hpa, linux-arch,
linux-kernel, linux-mm, linux, matthew.r.wilcox, mingo, paulmck,
peter, raghavendra.kt, riel, scott.norton, tglx, torvalds,
waiman.long, walken
> On Mon, Nov 11, 2013 at 09:17:52PM +0000, Tim Chen wrote:
>> An alternate implementation is
>> while (!ACCESS_ONCE(node->locked))
>> arch_mutex_cpu_relax();
>> smp_load_acquire(&node->locked);
>>
>> Leaving the smp_load_acquire at the end to provide appropriate barrier.
>> Will that be acceptable?
Will Deacon <will.deacon@arm.com> wrote:
> It still doesn't solve my problem though: I want a way to avoid that busy
> loop by some architecture-specific manner. The arch_mutex_cpu_relax() hook
> is a start, but there is no corresponding hook on the unlock side to issue a
> wakeup. Given a sensible relax implementation, I don't have an issue with
> putting a load-acquire in a loop, since it shouldn't be aggresively spinning
> anymore.
So you want something like this?
/*
* This is a spin-wait with acquire semantics. That is, accesses after
* this are not allowed to be reordered before the load that meets
* the specified condition. This requires that it end with either a
* load-acquire or a full smp_mb(). The optimal way to do this is likely
* to be architecture-dependent. E.g. x86 MONITOR/MWAIT instructions.
*/
#ifndef smp_load_acquire_until
#define smp_load_acquire_until(addr, cond) \
while (!(smp_load_acquire(addr) cond)) { \
do { \
arch_mutex_cpu_relax(); \
} while (!(ACCESS_ONCE(*(addr)) cond)); \
}
#endif
smp_load_acquire_until(&node->locked, != 0);
Alternative implementations:
#define smp_load_acquire_until(addr, cond) { \
while (!(ACCESS_ONCE(*(addr)) cond)) \
arch_mutex_cpu_relax(); \
smp_mb(); }
#define smp_load_acquire_until(addr, cond) \
if (!(smp_load_acquire(addr) cond)) { \
do { \
arch_mutex_cpu_relax(); \
} while (!(ACCESS_ONCE(*(addr)) cond)); \
smp_mb(); \
}
^ permalink raw reply [flat|nested] 5+ messages in thread
* Re: [PATCH v5 4/4] MCS Lock: Barrier corrections
2013-11-19 19:21 ` Paul E. McKenney
@ 2013-11-19 19:46 ` Tim Chen
0 siblings, 0 replies; 5+ messages in thread
From: Tim Chen @ 2013-11-19 19:46 UTC (permalink / raw)
To: paulmck
Cc: Ingo Molnar, Andrew Morton, Thomas Gleixner, linux-kernel,
linux-mm, linux-arch, Linus Torvalds, Waiman Long,
Andrea Arcangeli, Alex Shi, Andi Kleen, Michel Lespinasse,
Davidlohr Bueso, Matthew R Wilcox, Dave Hansen, Peter Zijlstra,
Rik van Riel, Peter Hurley, Raghavendra K T, George Spelvin,
H. Peter Anvin, Arnd Bergmann, Aswin Chandramouleeswaran,
Scott J Norton, Will Deacon, Figo.zhang
On Tue, 2013-11-19 at 11:21 -0800, Paul E. McKenney wrote:
> On Fri, Nov 08, 2013 at 11:52:38AM -0800, Tim Chen wrote:
> > From: Waiman Long <Waiman.Long@hp.com>
> >
> > This patch corrects the way memory barriers are used in the MCS lock
> > with smp_load_acquire and smp_store_release fucnction.
> > It removes ones that are not needed.
> >
> > It uses architecture specific load-acquire and store-release
> > primitives for synchronization, if available. Generic implementations
> > are provided in case they are not defined even though they may not
> > be optimal. These generic implementation could be removed later on
> > once changes are made in all the relevant header files.
> >
> > Suggested-by: Michel Lespinasse <walken@google.com>
> > Signed-off-by: Waiman Long <Waiman.Long@hp.com>
> > Signed-off-by: Jason Low <jason.low2@hp.com>
> > Signed-off-by: Tim Chen <tim.c.chen@linux.intel.com>
>
> Please see comments below.
>
> Thanx, Paul
>
> > ---
> > kernel/locking/mcs_spinlock.c | 48 +++++++++++++++++++++++++++++++++++------
> > 1 files changed, 41 insertions(+), 7 deletions(-)
> >
> > diff --git a/kernel/locking/mcs_spinlock.c b/kernel/locking/mcs_spinlock.c
> > index b6f27f8..df5c167 100644
> > --- a/kernel/locking/mcs_spinlock.c
> > +++ b/kernel/locking/mcs_spinlock.c
> > @@ -23,6 +23,31 @@
> > #endif
> >
> > /*
> > + * Fall back to use the regular atomic operations and memory barrier if
> > + * the acquire/release versions are not defined.
> > + */
> > +#ifndef xchg_acquire
> > +# define xchg_acquire(p, v) xchg(p, v)
> > +#endif
> > +
> > +#ifndef smp_load_acquire
> > +# define smp_load_acquire(p) \
> > + ({ \
> > + typeof(*p) __v = ACCESS_ONCE(*(p)); \
> > + smp_mb(); \
> > + __v; \
> > + })
> > +#endif
> > +
> > +#ifndef smp_store_release
> > +# define smp_store_release(p, v) \
> > + do { \
> > + smp_mb(); \
> > + ACCESS_ONCE(*(p)) = v; \
> > + } while (0)
> > +#endif
> > +
> > +/*
> > * In order to acquire the lock, the caller should declare a local node and
> > * pass a reference of the node to this function in addition to the lock.
> > * If the lock has already been acquired, then this will proceed to spin
> > @@ -37,15 +62,19 @@ void mcs_spin_lock(struct mcs_spinlock **lock, struct mcs_spinlock *node)
> > node->locked = 0;
> > node->next = NULL;
> >
> > - prev = xchg(lock, node);
> > + /* xchg() provides a memory barrier */
> > + prev = xchg_acquire(lock, node);
>
> But if this is xchg_acquire() with only acquire semantics, it need not
> ensure that the initializations of node->locked and node->next above
> will happen before the "ACCESS_ONCE(prev->next) = node" below. This
> therefore needs to remain xchg(). Or you need an smp_store_release()
> below instead of an ACCESS_ONCE() assignment.
Good point. Will keep it as xchg.
>
> As currently written, the poor CPU doing the unlock can be fatally
> disappointed by seeing pre-initialized values of ->locked and ->next.
> This could, among other things, result in a hang where the handoff
> happens before the initialization.
>
> > if (likely(prev == NULL)) {
> > /* Lock acquired */
> > return;
> > }
> > ACCESS_ONCE(prev->next) = node;
> > - smp_wmb();
> > - /* Wait until the lock holder passes the lock down */
> > - while (!ACCESS_ONCE(node->locked))
> > + /*
> > + * Wait until the lock holder passes the lock down.
> > + * Using smp_load_acquire() provides a memory barrier that
> > + * ensures subsequent operations happen after the lock is acquired.
> > + */
> > + while (!(smp_load_acquire(&node->locked)))
> > arch_mutex_cpu_relax();
>
> OK, this smp_load_acquire() makes sense!
>
> > }
> > EXPORT_SYMBOL_GPL(mcs_spin_lock);
> > @@ -54,7 +83,7 @@ EXPORT_SYMBOL_GPL(mcs_spin_lock);
> > * Releases the lock. The caller should pass in the corresponding node that
> > * was used to acquire the lock.
> > */
> > -static void mcs_spin_unlock(struct mcs_spinlock **lock, struct mcs_spinlock *node)
> > +void mcs_spin_unlock(struct mcs_spinlock **lock, struct mcs_spinlock *node)
> > {
> > struct mcs_spinlock *next = ACCESS_ONCE(node->next);
> >
> > @@ -68,7 +97,12 @@ static void mcs_spin_unlock(struct mcs_spinlock **lock, struct mcs_spinlock *nod
> > while (!(next = ACCESS_ONCE(node->next)))
> > arch_mutex_cpu_relax();
> > }
> > - ACCESS_ONCE(next->locked) = 1;
> > - smp_wmb();
> > + /*
> > + * Pass lock to next waiter.
> > + * smp_store_release() provides a memory barrier to ensure
> > + * all operations in the critical section has been completed
> > + * before unlocking.
> > + */
> > + smp_store_release(&next->locked , 1);
>
> This smp_store_release() makes sense as well!
>
> Could you please get rid of the extraneous space before the comma?
Will do.
>
> > }
> > EXPORT_SYMBOL_GPL(mcs_spin_unlock);
> > --
> > 1.7.4.4
> >
> >
>
^ permalink raw reply [flat|nested] 5+ messages in thread
* Re: [PATCH v5 4/4] MCS Lock: Barrier corrections
2013-11-08 19:52 ` Tim Chen
[not found] ` <20131111181049.GL28302@mudshark.cambridge.arm.com>
@ 2013-11-19 19:21 ` Paul E. McKenney
2013-11-19 19:46 ` Tim Chen
1 sibling, 1 reply; 5+ messages in thread
From: Paul E. McKenney @ 2013-11-19 19:21 UTC (permalink / raw)
To: Tim Chen
Cc: Ingo Molnar, Andrew Morton, Thomas Gleixner, linux-kernel,
linux-mm, linux-arch, Linus Torvalds, Waiman Long,
Andrea Arcangeli, Alex Shi, Andi Kleen, Michel Lespinasse,
Davidlohr Bueso, Matthew R Wilcox, Dave Hansen, Peter Zijlstra,
Rik van Riel, Peter Hurley, Raghavendra K T, George Spelvin,
H. Peter Anvin, Arnd Bergmann, Aswin Chandramouleeswaran,
Scott J Norton, Will Deacon, Figo.zhang
On Fri, Nov 08, 2013 at 11:52:38AM -0800, Tim Chen wrote:
> From: Waiman Long <Waiman.Long@hp.com>
>
> This patch corrects the way memory barriers are used in the MCS lock
> with smp_load_acquire and smp_store_release fucnction.
> It removes ones that are not needed.
>
> It uses architecture specific load-acquire and store-release
> primitives for synchronization, if available. Generic implementations
> are provided in case they are not defined even though they may not
> be optimal. These generic implementation could be removed later on
> once changes are made in all the relevant header files.
>
> Suggested-by: Michel Lespinasse <walken@google.com>
> Signed-off-by: Waiman Long <Waiman.Long@hp.com>
> Signed-off-by: Jason Low <jason.low2@hp.com>
> Signed-off-by: Tim Chen <tim.c.chen@linux.intel.com>
Please see comments below.
Thanx, Paul
> ---
> kernel/locking/mcs_spinlock.c | 48 +++++++++++++++++++++++++++++++++++------
> 1 files changed, 41 insertions(+), 7 deletions(-)
>
> diff --git a/kernel/locking/mcs_spinlock.c b/kernel/locking/mcs_spinlock.c
> index b6f27f8..df5c167 100644
> --- a/kernel/locking/mcs_spinlock.c
> +++ b/kernel/locking/mcs_spinlock.c
> @@ -23,6 +23,31 @@
> #endif
>
> /*
> + * Fall back to use the regular atomic operations and memory barrier if
> + * the acquire/release versions are not defined.
> + */
> +#ifndef xchg_acquire
> +# define xchg_acquire(p, v) xchg(p, v)
> +#endif
> +
> +#ifndef smp_load_acquire
> +# define smp_load_acquire(p) \
> + ({ \
> + typeof(*p) __v = ACCESS_ONCE(*(p)); \
> + smp_mb(); \
> + __v; \
> + })
> +#endif
> +
> +#ifndef smp_store_release
> +# define smp_store_release(p, v) \
> + do { \
> + smp_mb(); \
> + ACCESS_ONCE(*(p)) = v; \
> + } while (0)
> +#endif
> +
> +/*
> * In order to acquire the lock, the caller should declare a local node and
> * pass a reference of the node to this function in addition to the lock.
> * If the lock has already been acquired, then this will proceed to spin
> @@ -37,15 +62,19 @@ void mcs_spin_lock(struct mcs_spinlock **lock, struct mcs_spinlock *node)
> node->locked = 0;
> node->next = NULL;
>
> - prev = xchg(lock, node);
> + /* xchg() provides a memory barrier */
> + prev = xchg_acquire(lock, node);
But if this is xchg_acquire() with only acquire semantics, it need not
ensure that the initializations of node->locked and node->next above
will happen before the "ACCESS_ONCE(prev->next) = node" below. This
therefore needs to remain xchg(). Or you need an smp_store_release()
below instead of an ACCESS_ONCE() assignment.
As currently written, the poor CPU doing the unlock can be fatally
disappointed by seeing pre-initialized values of ->locked and ->next.
This could, among other things, result in a hang where the handoff
happens before the initialization.
> if (likely(prev == NULL)) {
> /* Lock acquired */
> return;
> }
> ACCESS_ONCE(prev->next) = node;
> - smp_wmb();
> - /* Wait until the lock holder passes the lock down */
> - while (!ACCESS_ONCE(node->locked))
> + /*
> + * Wait until the lock holder passes the lock down.
> + * Using smp_load_acquire() provides a memory barrier that
> + * ensures subsequent operations happen after the lock is acquired.
> + */
> + while (!(smp_load_acquire(&node->locked)))
> arch_mutex_cpu_relax();
OK, this smp_load_acquire() makes sense!
> }
> EXPORT_SYMBOL_GPL(mcs_spin_lock);
> @@ -54,7 +83,7 @@ EXPORT_SYMBOL_GPL(mcs_spin_lock);
> * Releases the lock. The caller should pass in the corresponding node that
> * was used to acquire the lock.
> */
> -static void mcs_spin_unlock(struct mcs_spinlock **lock, struct mcs_spinlock *node)
> +void mcs_spin_unlock(struct mcs_spinlock **lock, struct mcs_spinlock *node)
> {
> struct mcs_spinlock *next = ACCESS_ONCE(node->next);
>
> @@ -68,7 +97,12 @@ static void mcs_spin_unlock(struct mcs_spinlock **lock, struct mcs_spinlock *nod
> while (!(next = ACCESS_ONCE(node->next)))
> arch_mutex_cpu_relax();
> }
> - ACCESS_ONCE(next->locked) = 1;
> - smp_wmb();
> + /*
> + * Pass lock to next waiter.
> + * smp_store_release() provides a memory barrier to ensure
> + * all operations in the critical section has been completed
> + * before unlocking.
> + */
> + smp_store_release(&next->locked , 1);
This smp_store_release() makes sense as well!
Could you please get rid of the extraneous space before the comma?
> }
> EXPORT_SYMBOL_GPL(mcs_spin_unlock);
> --
> 1.7.4.4
>
>
^ permalink raw reply [flat|nested] 5+ messages in thread
* Re: [PATCH v5 4/4] MCS Lock: Barrier corrections
[not found] ` <1384204673.10046.6.camel@schen9-mobl3>
@ 2013-11-12 14:54 ` Waiman Long
0 siblings, 0 replies; 5+ messages in thread
From: Waiman Long @ 2013-11-12 14:54 UTC (permalink / raw)
To: Tim Chen, Peter Zijlstra
Cc: Will Deacon, Ingo Molnar, Andrew Morton, Thomas Gleixner,
linux-kernel, linux-arch, Linus Torvalds, Andrea Arcangeli,
Andi Kleen, Michel Lespinasse, Davidlohr Bueso, Matthew R Wilcox,
Dave Hansen, Rik van Riel, Peter Hurley, Paul E.McKenney,
Raghavendra K T, George Spelvin, H. Peter Anvin, Arnd Bergmann,
Aswin Chandramouleeswaran, Scott J Norton, Figo.zhang
On 11/11/2013 04:17 PM, Tim Chen wrote:
>> You could then augment that with [cmp]xchg_{acquire,release} as
>> appropriate.
>>
>>> +/*
>>> * In order to acquire the lock, the caller should declare a local node and
>>> * pass a reference of the node to this function in addition to the lock.
>>> * If the lock has already been acquired, then this will proceed to spin
>>> @@ -37,15 +62,19 @@ void mcs_spin_lock(struct mcs_spinlock **lock, struct mcs_spinlock *node)
>>> node->locked = 0;
>>> node->next = NULL;
>>>
>>> - prev = xchg(lock, node);
>>> + /* xchg() provides a memory barrier */
>>> + prev = xchg_acquire(lock, node);
>>> if (likely(prev == NULL)) {
>>> /* Lock acquired */
>>> return;
>>> }
>>> ACCESS_ONCE(prev->next) = node;
>>> - smp_wmb();
>>> - /* Wait until the lock holder passes the lock down */
>>> - while (!ACCESS_ONCE(node->locked))
>>> + /*
>>> + * Wait until the lock holder passes the lock down.
>>> + * Using smp_load_acquire() provides a memory barrier that
>>> + * ensures subsequent operations happen after the lock is acquired.
>>> + */
>>> + while (!(smp_load_acquire(&node->locked)))
>>> arch_mutex_cpu_relax();
> An alternate implementation is
> while (!ACCESS_ONCE(node->locked))
> arch_mutex_cpu_relax();
> smp_load_acquire(&node->locked);
>
> Leaving the smp_load_acquire at the end to provide appropriate barrier.
> Will that be acceptable?
>
> Tim
I second Tim's opinion. It will be help to have a smp_mb_load_acquire()
function that provide a memory barrier with load-acquire semantic. I
don't think we need one for store-release as that will not be in a loop.
Peter, what do you think about adding that to your patch?
-Longman
^ permalink raw reply [flat|nested] 5+ messages in thread
* [PATCH v5 4/4] MCS Lock: Barrier corrections
[not found] <cover.1383935697.git.tim.c.chen@linux.intel.com>
@ 2013-11-08 19:52 ` Tim Chen
[not found] ` <20131111181049.GL28302@mudshark.cambridge.arm.com>
2013-11-19 19:21 ` Paul E. McKenney
0 siblings, 2 replies; 5+ messages in thread
From: Tim Chen @ 2013-11-08 19:52 UTC (permalink / raw)
To: Ingo Molnar, Andrew Morton, Thomas Gleixner
Cc: linux-kernel, linux-mm, linux-arch, Linus Torvalds, Waiman Long,
Andrea Arcangeli, Alex Shi, Andi Kleen, Michel Lespinasse,
Davidlohr Bueso, Matthew R Wilcox, Dave Hansen, Peter Zijlstra,
Rik van Riel, Peter Hurley, Paul E.McKenney, Tim Chen,
Raghavendra K T, George Spelvin, H. Peter Anvin, Arnd Bergmann,
Aswin Chandramouleeswaran, Scott J Norton, Will Deacon,
Figo.zhang
From: Waiman Long <Waiman.Long@hp.com>
This patch corrects the way memory barriers are used in the MCS lock
with smp_load_acquire and smp_store_release fucnction.
It removes ones that are not needed.
It uses architecture specific load-acquire and store-release
primitives for synchronization, if available. Generic implementations
are provided in case they are not defined even though they may not
be optimal. These generic implementation could be removed later on
once changes are made in all the relevant header files.
Suggested-by: Michel Lespinasse <walken@google.com>
Signed-off-by: Waiman Long <Waiman.Long@hp.com>
Signed-off-by: Jason Low <jason.low2@hp.com>
Signed-off-by: Tim Chen <tim.c.chen@linux.intel.com>
---
kernel/locking/mcs_spinlock.c | 48 +++++++++++++++++++++++++++++++++++------
1 files changed, 41 insertions(+), 7 deletions(-)
diff --git a/kernel/locking/mcs_spinlock.c b/kernel/locking/mcs_spinlock.c
index b6f27f8..df5c167 100644
--- a/kernel/locking/mcs_spinlock.c
+++ b/kernel/locking/mcs_spinlock.c
@@ -23,6 +23,31 @@
#endif
/*
+ * Fall back to use the regular atomic operations and memory barrier if
+ * the acquire/release versions are not defined.
+ */
+#ifndef xchg_acquire
+# define xchg_acquire(p, v) xchg(p, v)
+#endif
+
+#ifndef smp_load_acquire
+# define smp_load_acquire(p) \
+ ({ \
+ typeof(*p) __v = ACCESS_ONCE(*(p)); \
+ smp_mb(); \
+ __v; \
+ })
+#endif
+
+#ifndef smp_store_release
+# define smp_store_release(p, v) \
+ do { \
+ smp_mb(); \
+ ACCESS_ONCE(*(p)) = v; \
+ } while (0)
+#endif
+
+/*
* In order to acquire the lock, the caller should declare a local node and
* pass a reference of the node to this function in addition to the lock.
* If the lock has already been acquired, then this will proceed to spin
@@ -37,15 +62,19 @@ void mcs_spin_lock(struct mcs_spinlock **lock, struct mcs_spinlock *node)
node->locked = 0;
node->next = NULL;
- prev = xchg(lock, node);
+ /* xchg() provides a memory barrier */
+ prev = xchg_acquire(lock, node);
if (likely(prev == NULL)) {
/* Lock acquired */
return;
}
ACCESS_ONCE(prev->next) = node;
- smp_wmb();
- /* Wait until the lock holder passes the lock down */
- while (!ACCESS_ONCE(node->locked))
+ /*
+ * Wait until the lock holder passes the lock down.
+ * Using smp_load_acquire() provides a memory barrier that
+ * ensures subsequent operations happen after the lock is acquired.
+ */
+ while (!(smp_load_acquire(&node->locked)))
arch_mutex_cpu_relax();
}
EXPORT_SYMBOL_GPL(mcs_spin_lock);
@@ -54,7 +83,7 @@ EXPORT_SYMBOL_GPL(mcs_spin_lock);
* Releases the lock. The caller should pass in the corresponding node that
* was used to acquire the lock.
*/
-static void mcs_spin_unlock(struct mcs_spinlock **lock, struct mcs_spinlock *node)
+void mcs_spin_unlock(struct mcs_spinlock **lock, struct mcs_spinlock *node)
{
struct mcs_spinlock *next = ACCESS_ONCE(node->next);
@@ -68,7 +97,12 @@ static void mcs_spin_unlock(struct mcs_spinlock **lock, struct mcs_spinlock *nod
while (!(next = ACCESS_ONCE(node->next)))
arch_mutex_cpu_relax();
}
- ACCESS_ONCE(next->locked) = 1;
- smp_wmb();
+ /*
+ * Pass lock to next waiter.
+ * smp_store_release() provides a memory barrier to ensure
+ * all operations in the critical section has been completed
+ * before unlocking.
+ */
+ smp_store_release(&next->locked , 1);
}
EXPORT_SYMBOL_GPL(mcs_spin_unlock);
--
1.7.4.4
^ permalink raw reply [flat|nested] 5+ messages in thread
end of thread, other threads:[~2013-11-19 19:46 UTC | newest]
Thread overview: 5+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
[not found] <20131112160827.GB25953@mudshark.cambridge.arm.com>
2013-11-12 17:16 ` [PATCH v5 4/4] MCS Lock: Barrier corrections George Spelvin
[not found] <cover.1383935697.git.tim.c.chen@linux.intel.com>
2013-11-08 19:52 ` Tim Chen
[not found] ` <20131111181049.GL28302@mudshark.cambridge.arm.com>
[not found] ` <1384204673.10046.6.camel@schen9-mobl3>
2013-11-12 14:54 ` Waiman Long
2013-11-19 19:21 ` Paul E. McKenney
2013-11-19 19:46 ` Tim Chen
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox
all inboxes | Powered by JetHome®