* [PATCH v6 0/5] MCS Lock: MCS lock code cleanup and optimizations
[not found] <cover.1384885312.git.tim.c.chen@linux.intel.com>
@ 2013-11-20 1:37 ` Tim Chen
2013-11-20 1:37 ` [PATCH v6 1/5] MCS Lock: Restructure the MCS lock defines and locking code into its own file Tim Chen
` (4 subsequent siblings)
5 siblings, 0 replies; 7+ messages in thread
From: Tim Chen @ 2013-11-20 1:37 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
In this patch series, we separated out the MCS lock code which was
previously embedded in the mutex.c. This allows for easier reuse of
MCS lock in other places like rwsem and qrwlock. We also did some micro
optimizations and barrier cleanup.
The original code has potential leaks between critical sections, which
was not a problem when MCS was embedded within the mutex but needs
to be corrected when allowing the MCS lock to be used by itself for
other locking purposes.
Proper barriers are now embedded with the usage of smp_load_acquire() in
mcs_spin_lock() and smp_store_release() in mcs_spin_unlock. See
http://marc.info/?l=linux-arch&m=138386254111507 for info on the
new smp_load_acquire() and smp_store_release() functions.
This patches were previously part of the rwsem optimization patch series
but now we spearate them out.
We have also added hooks to allow for architecture specific
implementation of the mcs_spin_lock and mcs_spin_unlock functions.
Will, do you want to take a crack at adding implementation for ARM
with wfe instruction?
Tim
v6:
1. Fix a bug of improper xchg_acquire and extra space in barrier
fixing patch.
2. Added extra hooks to allow for architecture specific version
of mcs_spin_lock and mcs_spin_unlock to be used.
v5:
1. Rework barrier correction patch. We now use smp_load_acquire()
in mcs_spin_lock() and smp_store_release() in
mcs_spin_unlock() to allow for architecture dependent barriers to be
automatically used. This is clean and will provide the right
barriers for all architecture.
v4:
1. Move patch series to the latest tip after v3.12
v3:
1. modified memory barriers to support non x86 architectures that have
weak memory ordering.
v2:
1. change export mcs_spin_lock as a GPL export symbol
2. corrected mcs_spin_lock to references
Jason Low (1):
MCS Lock: optimizations and extra comments
Tim Chen (2):
MCS Lock: Restructure the MCS lock defines and locking code into its
own file
MCS Lock: Allows for architecture specific mcs lock and unlock
Waiman Long (2):
MCS Lock: Move mcs_lock/unlock function into its own file
MCS Lock: Barrier corrections
arch/Kconfig | 3 ++
include/linux/mcs_spinlock.h | 30 +++++++++++++
include/linux/mutex.h | 5 ++-
kernel/locking/Makefile | 6 +--
kernel/locking/mcs_spinlock.c | 98 +++++++++++++++++++++++++++++++++++++++++++
kernel/locking/mutex.c | 60 ++++----------------------
6 files changed, 144 insertions(+), 58 deletions(-)
create mode 100644 include/linux/mcs_spinlock.h
create mode 100644 kernel/locking/mcs_spinlock.c
--
1.7.11.7
^ permalink raw reply [flat|nested] 7+ messages in thread
* [PATCH v6 1/5] MCS Lock: Restructure the MCS lock defines and locking code into its own file
[not found] <cover.1384885312.git.tim.c.chen@linux.intel.com>
2013-11-20 1:37 ` [PATCH v6 0/5] MCS Lock: MCS lock code cleanup and optimizations Tim Chen
@ 2013-11-20 1:37 ` Tim Chen
2013-11-20 1:37 ` [PATCH v6 2/5] MCS Lock: optimizations and extra comments Tim Chen
` (3 subsequent siblings)
5 siblings, 0 replies; 7+ messages in thread
From: Tim Chen @ 2013-11-20 1:37 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
We will need the MCS lock code for doing optimistic spinning for rwsem
and queue rwlock. Extracting the MCS code from mutex.c and put into
its own file allow us to reuse this code easily.
Signed-off-by: Tim Chen <tim.c.chen@linux.intel.com>
Signed-off-by: Davidlohr Bueso <davidlohr@hp.com>
---
include/linux/mcs_spinlock.h | 64 ++++++++++++++++++++++++++++++++++++++++++++
include/linux/mutex.h | 5 ++--
kernel/locking/mutex.c | 60 +++++------------------------------------
3 files changed, 74 insertions(+), 55 deletions(-)
create mode 100644 include/linux/mcs_spinlock.h
diff --git a/include/linux/mcs_spinlock.h b/include/linux/mcs_spinlock.h
new file mode 100644
index 0000000..b5de3b0
--- /dev/null
+++ b/include/linux/mcs_spinlock.h
@@ -0,0 +1,64 @@
+/*
+ * MCS lock defines
+ *
+ * This file contains the main data structure and API definitions of MCS lock.
+ *
+ * The MCS lock (proposed by Mellor-Crummey and Scott) is a simple spin-lock
+ * with the desirable properties of being fair, and with each cpu trying
+ * to acquire the lock spinning on a local variable.
+ * It avoids expensive cache bouncings that common test-and-set spin-lock
+ * implementations incur.
+ */
+#ifndef __LINUX_MCS_SPINLOCK_H
+#define __LINUX_MCS_SPINLOCK_H
+
+struct mcs_spinlock {
+ struct mcs_spinlock *next;
+ int locked; /* 1 if lock acquired */
+};
+
+/*
+ * We don't inline mcs_spin_lock() so that perf can correctly account for the
+ * time spent in this lock function.
+ */
+static noinline
+void mcs_spin_lock(struct mcs_spinlock **lock, struct mcs_spinlock *node)
+{
+ struct mcs_spinlock *prev;
+
+ /* Init node */
+ node->locked = 0;
+ node->next = NULL;
+
+ prev = xchg(lock, node);
+ if (likely(prev == NULL)) {
+ /* Lock acquired */
+ node->locked = 1;
+ return;
+ }
+ ACCESS_ONCE(prev->next) = node;
+ smp_wmb();
+ /* Wait until the lock holder passes the lock down */
+ while (!ACCESS_ONCE(node->locked))
+ arch_mutex_cpu_relax();
+}
+
+static void mcs_spin_unlock(struct mcs_spinlock **lock, struct mcs_spinlock *node)
+{
+ struct mcs_spinlock *next = ACCESS_ONCE(node->next);
+
+ if (likely(!next)) {
+ /*
+ * Release the lock by setting it to NULL
+ */
+ if (cmpxchg(lock, node, NULL) == node)
+ return;
+ /* Wait until the next pointer is set */
+ while (!(next = ACCESS_ONCE(node->next)))
+ arch_mutex_cpu_relax();
+ }
+ ACCESS_ONCE(next->locked) = 1;
+ smp_wmb();
+}
+
+#endif /* __LINUX_MCS_SPINLOCK_H */
diff --git a/include/linux/mutex.h b/include/linux/mutex.h
index bab49da..32a32e6 100644
--- a/include/linux/mutex.h
+++ b/include/linux/mutex.h
@@ -46,6 +46,7 @@
* - detects multi-task circular deadlocks and prints out all affected
* locks and tasks (and only those tasks)
*/
+struct mcs_spinlock;
struct mutex {
/* 1: unlocked, 0: locked, negative: locked, possible waiters */
atomic_t count;
@@ -55,7 +56,7 @@ struct mutex {
struct task_struct *owner;
#endif
#ifdef CONFIG_MUTEX_SPIN_ON_OWNER
- void *spin_mlock; /* Spinner MCS lock */
+ struct mcs_spinlock *mcs_lock; /* Spinner MCS lock */
#endif
#ifdef CONFIG_DEBUG_MUTEXES
const char *name;
@@ -179,4 +180,4 @@ extern int atomic_dec_and_mutex_lock(atomic_t *cnt, struct mutex *lock);
# define arch_mutex_cpu_relax() cpu_relax()
#endif
-#endif
+#endif /* __LINUX_MUTEX_H */
diff --git a/kernel/locking/mutex.c b/kernel/locking/mutex.c
index d24105b..e08b183 100644
--- a/kernel/locking/mutex.c
+++ b/kernel/locking/mutex.c
@@ -25,6 +25,7 @@
#include <linux/spinlock.h>
#include <linux/interrupt.h>
#include <linux/debug_locks.h>
+#include <linux/mcs_spinlock.h>
/*
* In the DEBUG case we are using the "NULL fastpath" for mutexes,
@@ -52,7 +53,7 @@ __mutex_init(struct mutex *lock, const char *name, struct lock_class_key *key)
INIT_LIST_HEAD(&lock->wait_list);
mutex_clear_owner(lock);
#ifdef CONFIG_MUTEX_SPIN_ON_OWNER
- lock->spin_mlock = NULL;
+ lock->mcs_lock = NULL;
#endif
debug_mutex_init(lock, name, key);
@@ -111,54 +112,7 @@ EXPORT_SYMBOL(mutex_lock);
* more or less simultaneously, the spinners need to acquire a MCS lock
* first before spinning on the owner field.
*
- * We don't inline mspin_lock() so that perf can correctly account for the
- * time spent in this lock function.
*/
-struct mspin_node {
- struct mspin_node *next ;
- int locked; /* 1 if lock acquired */
-};
-#define MLOCK(mutex) ((struct mspin_node **)&((mutex)->spin_mlock))
-
-static noinline
-void mspin_lock(struct mspin_node **lock, struct mspin_node *node)
-{
- struct mspin_node *prev;
-
- /* Init node */
- node->locked = 0;
- node->next = NULL;
-
- prev = xchg(lock, node);
- if (likely(prev == NULL)) {
- /* Lock acquired */
- node->locked = 1;
- return;
- }
- ACCESS_ONCE(prev->next) = node;
- smp_wmb();
- /* Wait until the lock holder passes the lock down */
- while (!ACCESS_ONCE(node->locked))
- arch_mutex_cpu_relax();
-}
-
-static void mspin_unlock(struct mspin_node **lock, struct mspin_node *node)
-{
- struct mspin_node *next = ACCESS_ONCE(node->next);
-
- if (likely(!next)) {
- /*
- * Release the lock by setting it to NULL
- */
- if (cmpxchg(lock, node, NULL) == node)
- return;
- /* Wait until the next pointer is set */
- while (!(next = ACCESS_ONCE(node->next)))
- arch_mutex_cpu_relax();
- }
- ACCESS_ONCE(next->locked) = 1;
- smp_wmb();
-}
/*
* Mutex spinning code migrated from kernel/sched/core.c
@@ -448,7 +402,7 @@ __mutex_lock_common(struct mutex *lock, long state, unsigned int subclass,
for (;;) {
struct task_struct *owner;
- struct mspin_node node;
+ struct mcs_spinlock node;
if (use_ww_ctx && ww_ctx->acquired > 0) {
struct ww_mutex *ww;
@@ -470,10 +424,10 @@ __mutex_lock_common(struct mutex *lock, long state, unsigned int subclass,
* If there's an owner, wait for it to either
* release the lock or go to sleep.
*/
- mspin_lock(MLOCK(lock), &node);
+ mcs_spin_lock(&lock->mcs_lock, &node);
owner = ACCESS_ONCE(lock->owner);
if (owner && !mutex_spin_on_owner(lock, owner)) {
- mspin_unlock(MLOCK(lock), &node);
+ mcs_spin_unlock(&lock->mcs_lock, &node);
goto slowpath;
}
@@ -488,11 +442,11 @@ __mutex_lock_common(struct mutex *lock, long state, unsigned int subclass,
}
mutex_set_owner(lock);
- mspin_unlock(MLOCK(lock), &node);
+ mcs_spin_unlock(&lock->mcs_lock, &node);
preempt_enable();
return 0;
}
- mspin_unlock(MLOCK(lock), &node);
+ mcs_spin_unlock(&lock->mcs_lock, &node);
/*
* When there's no owner, we might have preempted between the
--
1.7.11.7
^ permalink raw reply related [flat|nested] 7+ messages in thread
* [PATCH v6 2/5] MCS Lock: optimizations and extra comments
[not found] <cover.1384885312.git.tim.c.chen@linux.intel.com>
2013-11-20 1:37 ` [PATCH v6 0/5] MCS Lock: MCS lock code cleanup and optimizations Tim Chen
2013-11-20 1:37 ` [PATCH v6 1/5] MCS Lock: Restructure the MCS lock defines and locking code into its own file Tim Chen
@ 2013-11-20 1:37 ` Tim Chen
2013-11-20 1:37 ` [PATCH v6 3/5] MCS Lock: Move mcs_lock/unlock function into its own file Tim Chen
` (2 subsequent siblings)
5 siblings, 0 replies; 7+ messages in thread
From: Tim Chen @ 2013-11-20 1:37 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
Remove unnecessary operation and make the cmpxchg(lock, node, NULL) == node
check in mcs_spin_unlock() likely() as it is likely that a race did not occur
most of the time.
Also add in more comments describing how the local node is used in MCS locks.
Reviewed-by: Paul E. McKenney <paulmck@linux.vnet.ibm.com>
Reviewed-by: Tim Chen <tim.c.chen@linux.intel.com>
Signed-off-by: Jason Low <jason.low2@hp.com>
---
include/linux/mcs_spinlock.h | 13 +++++++++++--
1 file changed, 11 insertions(+), 2 deletions(-)
diff --git a/include/linux/mcs_spinlock.h b/include/linux/mcs_spinlock.h
index b5de3b0..96f14299 100644
--- a/include/linux/mcs_spinlock.h
+++ b/include/linux/mcs_spinlock.h
@@ -18,6 +18,12 @@ struct mcs_spinlock {
};
/*
+ * 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
+ * on this node->locked until the previous lock holder sets the node->locked
+ * in mcs_spin_unlock().
+ *
* We don't inline mcs_spin_lock() so that perf can correctly account for the
* time spent in this lock function.
*/
@@ -33,7 +39,6 @@ void mcs_spin_lock(struct mcs_spinlock **lock, struct mcs_spinlock *node)
prev = xchg(lock, node);
if (likely(prev == NULL)) {
/* Lock acquired */
- node->locked = 1;
return;
}
ACCESS_ONCE(prev->next) = node;
@@ -43,6 +48,10 @@ void mcs_spin_lock(struct mcs_spinlock **lock, struct mcs_spinlock *node)
arch_mutex_cpu_relax();
}
+/*
+ * 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)
{
struct mcs_spinlock *next = ACCESS_ONCE(node->next);
@@ -51,7 +60,7 @@ static void mcs_spin_unlock(struct mcs_spinlock **lock, struct mcs_spinlock *nod
/*
* Release the lock by setting it to NULL
*/
- if (cmpxchg(lock, node, NULL) == node)
+ if (likely(cmpxchg(lock, node, NULL) == node))
return;
/* Wait until the next pointer is set */
while (!(next = ACCESS_ONCE(node->next)))
--
1.7.11.7
^ permalink raw reply related [flat|nested] 7+ messages in thread
* [PATCH v6 3/5] MCS Lock: Move mcs_lock/unlock function into its own file
[not found] <cover.1384885312.git.tim.c.chen@linux.intel.com>
` (2 preceding siblings ...)
2013-11-20 1:37 ` [PATCH v6 2/5] MCS Lock: optimizations and extra comments Tim Chen
@ 2013-11-20 1:37 ` Tim Chen
2013-11-20 1:37 ` [PATCH v6 4/5] MCS Lock: Barrier corrections Tim Chen
2013-11-20 1:37 ` [PATCH v6 5/5] MCS Lock: Allows for architecture specific mcs lock and unlock Tim Chen
5 siblings, 0 replies; 7+ messages in thread
From: Tim Chen @ 2013-11-20 1:37 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
The following changes are made:
1) Create a new mcs_spinlock.c file to contain the
mcs_spin_lock() and mcs_spin_unlock() function.
2) Include a number of prerequisite header files and define
arch_mutex_cpu_relax(), if not previously defined so the
mcs functions can be compiled for multiple architecture without
causing problems.
Reviewed-by: Paul E. McKenney <paulmck@linux.vnet.ibm.com>
Signed-off-by: Waiman Long <Waiman.Long@hp.com>
Signed-off-by: Tim Chen <tim.c.chen@linux.intel.com>
---
include/linux/mcs_spinlock.h | 56 ++--------------------
kernel/locking/Makefile | 6 +--
.../locking/mcs_spinlock.c | 33 ++++++-------
3 files changed, 24 insertions(+), 71 deletions(-)
copy include/linux/mcs_spinlock.h => kernel/locking/mcs_spinlock.c (75%)
diff --git a/include/linux/mcs_spinlock.h b/include/linux/mcs_spinlock.h
index 96f14299..d54bb23 100644
--- a/include/linux/mcs_spinlock.h
+++ b/include/linux/mcs_spinlock.h
@@ -17,57 +17,9 @@ struct mcs_spinlock {
int locked; /* 1 if lock acquired */
};
-/*
- * 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
- * on this node->locked until the previous lock holder sets the node->locked
- * in mcs_spin_unlock().
- *
- * We don't inline mcs_spin_lock() so that perf can correctly account for the
- * time spent in this lock function.
- */
-static noinline
-void mcs_spin_lock(struct mcs_spinlock **lock, struct mcs_spinlock *node)
-{
- struct mcs_spinlock *prev;
-
- /* Init node */
- node->locked = 0;
- node->next = NULL;
-
- prev = xchg(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))
- arch_mutex_cpu_relax();
-}
-
-/*
- * 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)
-{
- struct mcs_spinlock *next = ACCESS_ONCE(node->next);
-
- if (likely(!next)) {
- /*
- * Release the lock by setting it to NULL
- */
- if (likely(cmpxchg(lock, node, NULL) == node))
- return;
- /* Wait until the next pointer is set */
- while (!(next = ACCESS_ONCE(node->next)))
- arch_mutex_cpu_relax();
- }
- ACCESS_ONCE(next->locked) = 1;
- smp_wmb();
-}
+extern
+void mcs_spin_lock(struct mcs_spinlock **lock, struct mcs_spinlock *node);
+extern
+void mcs_spin_unlock(struct mcs_spinlock **lock, struct mcs_spinlock *node);
#endif /* __LINUX_MCS_SPINLOCK_H */
diff --git a/kernel/locking/Makefile b/kernel/locking/Makefile
index baab8e5..20d9d5c 100644
--- a/kernel/locking/Makefile
+++ b/kernel/locking/Makefile
@@ -13,12 +13,12 @@ obj-$(CONFIG_LOCKDEP) += lockdep.o
ifeq ($(CONFIG_PROC_FS),y)
obj-$(CONFIG_LOCKDEP) += lockdep_proc.o
endif
-obj-$(CONFIG_SMP) += spinlock.o
-obj-$(CONFIG_PROVE_LOCKING) += spinlock.o
+obj-$(CONFIG_SMP) += spinlock.o mcs_spinlock.o
+obj-$(CONFIG_PROVE_LOCKING) += spinlock.o mcs_spinlock.o
obj-$(CONFIG_RT_MUTEXES) += rtmutex.o
obj-$(CONFIG_DEBUG_RT_MUTEXES) += rtmutex-debug.o
obj-$(CONFIG_RT_MUTEX_TESTER) += rtmutex-tester.o
-obj-$(CONFIG_DEBUG_SPINLOCK) += spinlock.o
+obj-$(CONFIG_DEBUG_SPINLOCK) += spinlock.o mcs_spinlock.o
obj-$(CONFIG_DEBUG_SPINLOCK) += spinlock_debug.o
obj-$(CONFIG_RWSEM_GENERIC_SPINLOCK) += rwsem-spinlock.o
obj-$(CONFIG_RWSEM_XCHGADD_ALGORITHM) += rwsem-xadd.o
diff --git a/include/linux/mcs_spinlock.h b/kernel/locking/mcs_spinlock.c
similarity index 75%
copy from include/linux/mcs_spinlock.h
copy to kernel/locking/mcs_spinlock.c
index 96f14299..44fb092 100644
--- a/include/linux/mcs_spinlock.h
+++ b/kernel/locking/mcs_spinlock.c
@@ -1,7 +1,5 @@
/*
- * MCS lock defines
- *
- * This file contains the main data structure and API definitions of MCS lock.
+ * MCS lock
*
* The MCS lock (proposed by Mellor-Crummey and Scott) is a simple spin-lock
* with the desirable properties of being fair, and with each cpu trying
@@ -9,13 +7,20 @@
* It avoids expensive cache bouncings that common test-and-set spin-lock
* implementations incur.
*/
-#ifndef __LINUX_MCS_SPINLOCK_H
-#define __LINUX_MCS_SPINLOCK_H
+/*
+ * asm/processor.h may define arch_mutex_cpu_relax().
+ * If it is not defined, cpu_relax() will be used.
+ */
+#include <asm/barrier.h>
+#include <asm/cmpxchg.h>
+#include <asm/processor.h>
+#include <linux/compiler.h>
+#include <linux/mcs_spinlock.h>
+#include <linux/export.h>
-struct mcs_spinlock {
- struct mcs_spinlock *next;
- int locked; /* 1 if lock acquired */
-};
+#ifndef arch_mutex_cpu_relax
+# define arch_mutex_cpu_relax() cpu_relax()
+#endif
/*
* In order to acquire the lock, the caller should declare a local node and
@@ -23,11 +28,7 @@ struct mcs_spinlock {
* If the lock has already been acquired, then this will proceed to spin
* on this node->locked until the previous lock holder sets the node->locked
* in mcs_spin_unlock().
- *
- * We don't inline mcs_spin_lock() so that perf can correctly account for the
- * time spent in this lock function.
*/
-static noinline
void mcs_spin_lock(struct mcs_spinlock **lock, struct mcs_spinlock *node)
{
struct mcs_spinlock *prev;
@@ -47,12 +48,13 @@ void mcs_spin_lock(struct mcs_spinlock **lock, struct mcs_spinlock *node)
while (!ACCESS_ONCE(node->locked))
arch_mutex_cpu_relax();
}
+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);
@@ -69,5 +71,4 @@ static void mcs_spin_unlock(struct mcs_spinlock **lock, struct mcs_spinlock *nod
ACCESS_ONCE(next->locked) = 1;
smp_wmb();
}
-
-#endif /* __LINUX_MCS_SPINLOCK_H */
+EXPORT_SYMBOL_GPL(mcs_spin_unlock);
--
1.7.11.7
^ permalink raw reply related [flat|nested] 7+ messages in thread
* [PATCH v6 4/5] MCS Lock: Barrier corrections
[not found] <cover.1384885312.git.tim.c.chen@linux.intel.com>
` (3 preceding siblings ...)
2013-11-20 1:37 ` [PATCH v6 3/5] MCS Lock: Move mcs_lock/unlock function into its own file Tim Chen
@ 2013-11-20 1:37 ` Tim Chen
2013-11-20 15:31 ` Paul E. McKenney
2013-11-20 1:37 ` [PATCH v6 5/5] MCS Lock: Allows for architecture specific mcs lock and unlock Tim Chen
5 siblings, 1 reply; 7+ messages in thread
From: Tim Chen @ 2013-11-20 1:37 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
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 | 19 ++++++++++++++-----
1 file changed, 14 insertions(+), 5 deletions(-)
diff --git a/kernel/locking/mcs_spinlock.c b/kernel/locking/mcs_spinlock.c
index 44fb092..6f2ce8e 100644
--- a/kernel/locking/mcs_spinlock.c
+++ b/kernel/locking/mcs_spinlock.c
@@ -37,15 +37,19 @@ void mcs_spin_lock(struct mcs_spinlock **lock, struct mcs_spinlock *node)
node->locked = 0;
node->next = NULL;
+ /* xchg() provides a memory barrier */
prev = xchg(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);
@@ -68,7 +72,12 @@ void mcs_spin_unlock(struct mcs_spinlock **lock, struct mcs_spinlock *node)
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.11.7
^ permalink raw reply related [flat|nested] 7+ messages in thread
* [PATCH v6 5/5] MCS Lock: Allows for architecture specific mcs lock and unlock
[not found] <cover.1384885312.git.tim.c.chen@linux.intel.com>
` (4 preceding siblings ...)
2013-11-20 1:37 ` [PATCH v6 4/5] MCS Lock: Barrier corrections Tim Chen
@ 2013-11-20 1:37 ` Tim Chen
5 siblings, 0 replies; 7+ messages in thread
From: Tim Chen @ 2013-11-20 1:37 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
Restructure code to allow for architecture specific defines
of the arch_mcs_spin_lock and arch_mcs_spin_unlock funtion
that can be optimized for specific architecture. These
arch specific functions can be placed in asm/mcs_spinlock.h.
Otherwise the default arch_mcs_spin_lock and arch_mcs_spin_unlock
will be used.
Signed-off-by: Tim Chen <tim.c.chen@linux.intel.com>
---
arch/Kconfig | 3 ++
include/linux/mcs_spinlock.h | 5 +++
kernel/locking/mcs_spinlock.c | 93 +++++++++++++++++++++++++------------------
3 files changed, 62 insertions(+), 39 deletions(-)
diff --git a/arch/Kconfig b/arch/Kconfig
index ded747c..c96c696 100644
--- a/arch/Kconfig
+++ b/arch/Kconfig
@@ -306,6 +306,9 @@ config HAVE_CMPXCHG_LOCAL
config HAVE_CMPXCHG_DOUBLE
bool
+config HAVE_ARCH_MCS_LOCK
+ bool
+
config ARCH_WANT_IPC_PARSE_VERSION
bool
diff --git a/include/linux/mcs_spinlock.h b/include/linux/mcs_spinlock.h
index d54bb23..d64786a 100644
--- a/include/linux/mcs_spinlock.h
+++ b/include/linux/mcs_spinlock.h
@@ -12,6 +12,11 @@
#ifndef __LINUX_MCS_SPINLOCK_H
#define __LINUX_MCS_SPINLOCK_H
+/* arch specific mcs lock and unlock functions defined here */
+#ifdef CONFIG_HAVE_ARCH_MCS_LOCK
+#include <asm/mcs_spinlock.h>
+#endif
+
struct mcs_spinlock {
struct mcs_spinlock *next;
int locked; /* 1 if lock acquired */
diff --git a/kernel/locking/mcs_spinlock.c b/kernel/locking/mcs_spinlock.c
index 6f2ce8e..582584a 100644
--- a/kernel/locking/mcs_spinlock.c
+++ b/kernel/locking/mcs_spinlock.c
@@ -29,28 +29,36 @@
* on this node->locked until the previous lock holder sets the node->locked
* in mcs_spin_unlock().
*/
+#ifndef arch_mcs_spin_lock
+#define arch_mcs_spin_lock(lock, node) \
+{ \
+ struct mcs_spinlock *prev; \
+ \
+ /* Init node */ \
+ node->locked = 0; \
+ node->next = NULL; \
+ \
+ /* xchg() provides a memory barrier */ \
+ prev = xchg(lock, node); \
+ if (likely(prev == NULL)) { \
+ /* Lock acquired */ \
+ return; \
+ } \
+ ACCESS_ONCE(prev->next) = node; \
+ /* \
+ * 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(); \
+}
+#endif
+
void mcs_spin_lock(struct mcs_spinlock **lock, struct mcs_spinlock *node)
{
- struct mcs_spinlock *prev;
-
- /* Init node */
- node->locked = 0;
- node->next = NULL;
-
- /* xchg() provides a memory barrier */
- prev = xchg(lock, node);
- if (likely(prev == NULL)) {
- /* Lock acquired */
- return;
- }
- ACCESS_ONCE(prev->next) = node;
- /*
- * 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();
+ arch_mcs_spin_lock(lock, node);
}
EXPORT_SYMBOL_GPL(mcs_spin_lock);
@@ -58,26 +66,33 @@ EXPORT_SYMBOL_GPL(mcs_spin_lock);
* Releases the lock. The caller should pass in the corresponding node that
* was used to acquire the lock.
*/
+#ifndef arch_mcs_spin_unlock
+#define arch_mcs_spin_unlock(lock, node) \
+{ \
+ struct mcs_spinlock *next = ACCESS_ONCE(node->next); \
+ \
+ if (likely(!next)) { \
+ /* \
+ * Release the lock by setting it to NULL \
+ */ \
+ if (likely(cmpxchg(lock, node, NULL) == node)) \
+ return; \
+ /* Wait until the next pointer is set */ \
+ while (!(next = ACCESS_ONCE(node->next))) \
+ arch_mutex_cpu_relax(); \
+ } \
+ /* \
+ * 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); \
+}
+#endif
+
void mcs_spin_unlock(struct mcs_spinlock **lock, struct mcs_spinlock *node)
{
- struct mcs_spinlock *next = ACCESS_ONCE(node->next);
-
- if (likely(!next)) {
- /*
- * Release the lock by setting it to NULL
- */
- if (likely(cmpxchg(lock, node, NULL) == node))
- return;
- /* Wait until the next pointer is set */
- while (!(next = ACCESS_ONCE(node->next)))
- arch_mutex_cpu_relax();
- }
- /*
- * 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);
+ arch_mcs_spin_unlock(lock, node);
}
EXPORT_SYMBOL_GPL(mcs_spin_unlock);
--
1.7.11.7
^ permalink raw reply related [flat|nested] 7+ messages in thread
* Re: [PATCH v6 4/5] MCS Lock: Barrier corrections
2013-11-20 1:37 ` [PATCH v6 4/5] MCS Lock: Barrier corrections Tim Chen
@ 2013-11-20 15:31 ` Paul E. McKenney
0 siblings, 0 replies; 7+ messages in thread
From: Paul E. McKenney @ 2013-11-20 15:31 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 Tue, Nov 19, 2013 at 05:37:43PM -0800, Tim Chen wrote:
> 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 | 19 ++++++++++++++-----
> 1 file changed, 14 insertions(+), 5 deletions(-)
>
> diff --git a/kernel/locking/mcs_spinlock.c b/kernel/locking/mcs_spinlock.c
> index 44fb092..6f2ce8e 100644
> --- a/kernel/locking/mcs_spinlock.c
> +++ b/kernel/locking/mcs_spinlock.c
> @@ -37,15 +37,19 @@ void mcs_spin_lock(struct mcs_spinlock **lock, struct mcs_spinlock *node)
> node->locked = 0;
> node->next = NULL;
>
> + /* xchg() provides a memory barrier */
> prev = xchg(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);
> @@ -68,7 +72,12 @@ void mcs_spin_unlock(struct mcs_spinlock **lock, struct mcs_spinlock *node)
> 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);
However, there is one problem with this that I missed yesterday.
Documentation/memory-barriers.txt requires that an unlock-lock pair
provide a full barrier, but this is not guaranteed if we use
smp_store_release() for unlock and smp_load_acquire() for lock.
At least one of these needs a full memory barrier.
Thanx, Paul
> }
> EXPORT_SYMBOL_GPL(mcs_spin_unlock);
> --
> 1.7.11.7
>
>
>
^ permalink raw reply [flat|nested] 7+ messages in thread
end of thread, other threads:[~2013-11-20 15:31 UTC | newest]
Thread overview: 7+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
[not found] <cover.1384885312.git.tim.c.chen@linux.intel.com>
2013-11-20 1:37 ` [PATCH v6 0/5] MCS Lock: MCS lock code cleanup and optimizations Tim Chen
2013-11-20 1:37 ` [PATCH v6 1/5] MCS Lock: Restructure the MCS lock defines and locking code into its own file Tim Chen
2013-11-20 1:37 ` [PATCH v6 2/5] MCS Lock: optimizations and extra comments Tim Chen
2013-11-20 1:37 ` [PATCH v6 3/5] MCS Lock: Move mcs_lock/unlock function into its own file Tim Chen
2013-11-20 1:37 ` [PATCH v6 4/5] MCS Lock: Barrier corrections Tim Chen
2013-11-20 15:31 ` Paul E. McKenney
2013-11-20 1:37 ` [PATCH v6 5/5] MCS Lock: Allows for architecture specific mcs lock and unlock Tim Chen
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox