aboutsummaryrefslogtreecommitdiffstats
path: root/lib
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@linux-foundation.org>2013-09-02 15:12:15 -0400
committerLinus Torvalds <torvalds@linux-foundation.org>2013-09-02 15:12:15 -0400
commitbc08b449ee14ace4d869adaa1bb35a44ce68d775 (patch)
tree133319a81192fad41aa999eac135e87905ab5bab /lib
parent2f4f12e571c4e2f50f3818a3c2544929145f75dd (diff)
lockref: implement lockless reference count updates using cmpxchg()
Instead of taking the spinlock, the lockless versions atomically check that the lock is not taken, and do the reference count update using a cmpxchg() loop. This is semantically identical to doing the reference count update protected by the lock, but avoids the "wait for lock" contention that you get when accesses to the reference count are contended. Note that a "lockref" is absolutely _not_ equivalent to an atomic_t. Even when the lockref reference counts are updated atomically with cmpxchg, the fact that they also verify the state of the spinlock means that the lockless updates can never happen while somebody else holds the spinlock. So while "lockref_put_or_lock()" looks a lot like just another name for "atomic_dec_and_lock()", and both optimize to lockless updates, they are fundamentally different: the decrement done by atomic_dec_and_lock() is truly independent of any lock (as long as it doesn't decrement to zero), so a locked region can still see the count change. The lockref structure, in contrast, really is a *locked* reference count. If you hold the spinlock, the reference count will be stable and you can modify the reference count without using atomics, because even the lockless updates will see and respect the state of the lock. In order to enable the cmpxchg lockless code, the architecture needs to do three things: (1) Make sure that the "arch_spinlock_t" and an "unsigned int" can fit in an aligned u64, and have a "cmpxchg()" implementation that works on such a u64 data type. (2) define a helper function to test for a spinlock being unlocked ("arch_spin_value_unlocked()") (3) select the "ARCH_USE_CMPXCHG_LOCKREF" config variable in its Kconfig file. This enables it for x86-64 (but not 32-bit, we'd need to make sure cmpxchg() turns into the proper cmpxchg8b in order to enable it for 32-bit mode). Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
Diffstat (limited to 'lib')
-rw-r--r--lib/Kconfig10
-rw-r--r--lib/lockref.c60
2 files changed, 69 insertions, 1 deletions
diff --git a/lib/Kconfig b/lib/Kconfig
index 71d9f81f6eed..65561716c16c 100644
--- a/lib/Kconfig
+++ b/lib/Kconfig
@@ -48,6 +48,16 @@ config STMP_DEVICE
48config PERCPU_RWSEM 48config PERCPU_RWSEM
49 boolean 49 boolean
50 50
51config ARCH_USE_CMPXCHG_LOCKREF
52 bool
53
54config CMPXCHG_LOCKREF
55 def_bool y if ARCH_USE_CMPXCHG_LOCKREF
56 depends on SMP
57 depends on !GENERIC_LOCKBREAK
58 depends on !DEBUG_SPINLOCK
59 depends on !DEBUG_LOCK_ALLOC
60
51config CRC_CCITT 61config CRC_CCITT
52 tristate "CRC-CCITT functions" 62 tristate "CRC-CCITT functions"
53 help 63 help
diff --git a/lib/lockref.c b/lib/lockref.c
index a9a4f4e1eff5..7819c2d1d315 100644
--- a/lib/lockref.c
+++ b/lib/lockref.c
@@ -1,6 +1,33 @@
1#include <linux/export.h> 1#include <linux/export.h>
2#include <linux/lockref.h> 2#include <linux/lockref.h>
3 3
4#ifdef CONFIG_CMPXCHG_LOCKREF
5
6/*
7 * Note that the "cmpxchg()" reloads the "old" value for the
8 * failure case.
9 */
10#define CMPXCHG_LOOP(CODE, SUCCESS) do { \
11 struct lockref old; \
12 BUILD_BUG_ON(sizeof(old) != 8); \
13 old.lock_count = ACCESS_ONCE(lockref->lock_count); \
14 while (likely(arch_spin_value_unlocked(old.lock.rlock.raw_lock))) { \
15 struct lockref new = old, prev = old; \
16 CODE \
17 old.lock_count = cmpxchg(&lockref->lock_count, \
18 old.lock_count, new.lock_count); \
19 if (likely(old.lock_count == prev.lock_count)) { \
20 SUCCESS; \
21 } \
22 } \
23} while (0)
24
25#else
26
27#define CMPXCHG_LOOP(CODE, SUCCESS) do { } while (0)
28
29#endif
30
4/** 31/**
5 * lockref_get - Increments reference count unconditionally 32 * lockref_get - Increments reference count unconditionally
6 * @lockcnt: pointer to lockref structure 33 * @lockcnt: pointer to lockref structure
@@ -10,6 +37,12 @@
10 */ 37 */
11void lockref_get(struct lockref *lockref) 38void lockref_get(struct lockref *lockref)
12{ 39{
40 CMPXCHG_LOOP(
41 new.count++;
42 ,
43 return;
44 );
45
13 spin_lock(&lockref->lock); 46 spin_lock(&lockref->lock);
14 lockref->count++; 47 lockref->count++;
15 spin_unlock(&lockref->lock); 48 spin_unlock(&lockref->lock);
@@ -23,9 +56,18 @@ EXPORT_SYMBOL(lockref_get);
23 */ 56 */
24int lockref_get_not_zero(struct lockref *lockref) 57int lockref_get_not_zero(struct lockref *lockref)
25{ 58{
26 int retval = 0; 59 int retval;
60
61 CMPXCHG_LOOP(
62 new.count++;
63 if (!old.count)
64 return 0;
65 ,
66 return 1;
67 );
27 68
28 spin_lock(&lockref->lock); 69 spin_lock(&lockref->lock);
70 retval = 0;
29 if (lockref->count) { 71 if (lockref->count) {
30 lockref->count++; 72 lockref->count++;
31 retval = 1; 73 retval = 1;
@@ -43,6 +85,14 @@ EXPORT_SYMBOL(lockref_get_not_zero);
43 */ 85 */
44int lockref_get_or_lock(struct lockref *lockref) 86int lockref_get_or_lock(struct lockref *lockref)
45{ 87{
88 CMPXCHG_LOOP(
89 new.count++;
90 if (!old.count)
91 break;
92 ,
93 return 1;
94 );
95
46 spin_lock(&lockref->lock); 96 spin_lock(&lockref->lock);
47 if (!lockref->count) 97 if (!lockref->count)
48 return 0; 98 return 0;
@@ -59,6 +109,14 @@ EXPORT_SYMBOL(lockref_get_or_lock);
59 */ 109 */
60int lockref_put_or_lock(struct lockref *lockref) 110int lockref_put_or_lock(struct lockref *lockref)
61{ 111{
112 CMPXCHG_LOOP(
113 new.count--;
114 if (old.count <= 1)
115 break;
116 ,
117 return 1;
118 );
119
62 spin_lock(&lockref->lock); 120 spin_lock(&lockref->lock);
63 if (lockref->count <= 1) 121 if (lockref->count <= 1)
64 return 0; 122 return 0;