diff options
author | Nick Piggin <npiggin@kernel.dk> | 2010-08-17 14:37:37 -0400 |
---|---|---|
committer | Al Viro <viro@zeniv.linux.org.uk> | 2010-08-18 08:35:48 -0400 |
commit | 2dc91abe03d8ce6dd7f9251faffafca5f6b9e85d (patch) | |
tree | 1712fcb512310a80a7d2c7f92b30ce91750efaf1 /include/linux | |
parent | d996b62a8df1d935b01319bf8defb95b5709f7b8 (diff) |
lglock: introduce special lglock and brlock spin locks
lglock: introduce special lglock and brlock spin locks
This patch introduces "local-global" locks (lglocks). These can be used to:
- Provide fast exclusive access to per-CPU data, with exclusive access to
another CPU's data allowed but possibly subject to contention, and to provide
very slow exclusive access to all per-CPU data.
- Or to provide very fast and scalable read serialisation, and to provide
very slow exclusive serialisation of data (not necessarily per-CPU data).
Brlocks are also implemented as a short-hand notation for the latter use
case.
Thanks to Paul for local/global naming convention.
Cc: linux-kernel@vger.kernel.org
Cc: Al Viro <viro@ZenIV.linux.org.uk>
Cc: "Paul E. McKenney" <paulmck@linux.vnet.ibm.com>
Signed-off-by: Nick Piggin <npiggin@kernel.dk>
Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
Diffstat (limited to 'include/linux')
-rw-r--r-- | include/linux/lglock.h | 172 |
1 files changed, 172 insertions, 0 deletions
diff --git a/include/linux/lglock.h b/include/linux/lglock.h new file mode 100644 index 000000000000..b288cb713b90 --- /dev/null +++ b/include/linux/lglock.h | |||
@@ -0,0 +1,172 @@ | |||
1 | /* | ||
2 | * Specialised local-global spinlock. Can only be declared as global variables | ||
3 | * to avoid overhead and keep things simple (and we don't want to start using | ||
4 | * these inside dynamically allocated structures). | ||
5 | * | ||
6 | * "local/global locks" (lglocks) can be used to: | ||
7 | * | ||
8 | * - Provide fast exclusive access to per-CPU data, with exclusive access to | ||
9 | * another CPU's data allowed but possibly subject to contention, and to | ||
10 | * provide very slow exclusive access to all per-CPU data. | ||
11 | * - Or to provide very fast and scalable read serialisation, and to provide | ||
12 | * very slow exclusive serialisation of data (not necessarily per-CPU data). | ||
13 | * | ||
14 | * Brlocks are also implemented as a short-hand notation for the latter use | ||
15 | * case. | ||
16 | * | ||
17 | * Copyright 2009, 2010, Nick Piggin, Novell Inc. | ||
18 | */ | ||
19 | #ifndef __LINUX_LGLOCK_H | ||
20 | #define __LINUX_LGLOCK_H | ||
21 | |||
22 | #include <linux/spinlock.h> | ||
23 | #include <linux/lockdep.h> | ||
24 | #include <linux/percpu.h> | ||
25 | |||
26 | /* can make br locks by using local lock for read side, global lock for write */ | ||
27 | #define br_lock_init(name) name##_lock_init() | ||
28 | #define br_read_lock(name) name##_local_lock() | ||
29 | #define br_read_unlock(name) name##_local_unlock() | ||
30 | #define br_write_lock(name) name##_global_lock_online() | ||
31 | #define br_write_unlock(name) name##_global_unlock_online() | ||
32 | |||
33 | #define DECLARE_BRLOCK(name) DECLARE_LGLOCK(name) | ||
34 | #define DEFINE_BRLOCK(name) DEFINE_LGLOCK(name) | ||
35 | |||
36 | |||
37 | #define lg_lock_init(name) name##_lock_init() | ||
38 | #define lg_local_lock(name) name##_local_lock() | ||
39 | #define lg_local_unlock(name) name##_local_unlock() | ||
40 | #define lg_local_lock_cpu(name, cpu) name##_local_lock_cpu(cpu) | ||
41 | #define lg_local_unlock_cpu(name, cpu) name##_local_unlock_cpu(cpu) | ||
42 | #define lg_global_lock(name) name##_global_lock() | ||
43 | #define lg_global_unlock(name) name##_global_unlock() | ||
44 | #define lg_global_lock_online(name) name##_global_lock_online() | ||
45 | #define lg_global_unlock_online(name) name##_global_unlock_online() | ||
46 | |||
47 | #ifdef CONFIG_DEBUG_LOCK_ALLOC | ||
48 | #define LOCKDEP_INIT_MAP lockdep_init_map | ||
49 | |||
50 | #define DEFINE_LGLOCK_LOCKDEP(name) \ | ||
51 | struct lock_class_key name##_lock_key; \ | ||
52 | struct lockdep_map name##_lock_dep_map; \ | ||
53 | EXPORT_SYMBOL(name##_lock_dep_map) | ||
54 | |||
55 | #else | ||
56 | #define LOCKDEP_INIT_MAP(a, b, c, d) | ||
57 | |||
58 | #define DEFINE_LGLOCK_LOCKDEP(name) | ||
59 | #endif | ||
60 | |||
61 | |||
62 | #define DECLARE_LGLOCK(name) \ | ||
63 | extern void name##_lock_init(void); \ | ||
64 | extern void name##_local_lock(void); \ | ||
65 | extern void name##_local_unlock(void); \ | ||
66 | extern void name##_local_lock_cpu(int cpu); \ | ||
67 | extern void name##_local_unlock_cpu(int cpu); \ | ||
68 | extern void name##_global_lock(void); \ | ||
69 | extern void name##_global_unlock(void); \ | ||
70 | extern void name##_global_lock_online(void); \ | ||
71 | extern void name##_global_unlock_online(void); \ | ||
72 | |||
73 | #define DEFINE_LGLOCK(name) \ | ||
74 | \ | ||
75 | DEFINE_PER_CPU(arch_spinlock_t, name##_lock); \ | ||
76 | DEFINE_LGLOCK_LOCKDEP(name); \ | ||
77 | \ | ||
78 | void name##_lock_init(void) { \ | ||
79 | int i; \ | ||
80 | LOCKDEP_INIT_MAP(&name##_lock_dep_map, #name, &name##_lock_key, 0); \ | ||
81 | for_each_possible_cpu(i) { \ | ||
82 | arch_spinlock_t *lock; \ | ||
83 | lock = &per_cpu(name##_lock, i); \ | ||
84 | *lock = (arch_spinlock_t)__ARCH_SPIN_LOCK_UNLOCKED; \ | ||
85 | } \ | ||
86 | } \ | ||
87 | EXPORT_SYMBOL(name##_lock_init); \ | ||
88 | \ | ||
89 | void name##_local_lock(void) { \ | ||
90 | arch_spinlock_t *lock; \ | ||
91 | preempt_disable(); \ | ||
92 | rwlock_acquire_read(&name##_lock_dep_map, 0, 0, _THIS_IP_); \ | ||
93 | lock = &__get_cpu_var(name##_lock); \ | ||
94 | arch_spin_lock(lock); \ | ||
95 | } \ | ||
96 | EXPORT_SYMBOL(name##_local_lock); \ | ||
97 | \ | ||
98 | void name##_local_unlock(void) { \ | ||
99 | arch_spinlock_t *lock; \ | ||
100 | rwlock_release(&name##_lock_dep_map, 1, _THIS_IP_); \ | ||
101 | lock = &__get_cpu_var(name##_lock); \ | ||
102 | arch_spin_unlock(lock); \ | ||
103 | preempt_enable(); \ | ||
104 | } \ | ||
105 | EXPORT_SYMBOL(name##_local_unlock); \ | ||
106 | \ | ||
107 | void name##_local_lock_cpu(int cpu) { \ | ||
108 | arch_spinlock_t *lock; \ | ||
109 | preempt_disable(); \ | ||
110 | rwlock_acquire_read(&name##_lock_dep_map, 0, 0, _THIS_IP_); \ | ||
111 | lock = &per_cpu(name##_lock, cpu); \ | ||
112 | arch_spin_lock(lock); \ | ||
113 | } \ | ||
114 | EXPORT_SYMBOL(name##_local_lock_cpu); \ | ||
115 | \ | ||
116 | void name##_local_unlock_cpu(int cpu) { \ | ||
117 | arch_spinlock_t *lock; \ | ||
118 | rwlock_release(&name##_lock_dep_map, 1, _THIS_IP_); \ | ||
119 | lock = &per_cpu(name##_lock, cpu); \ | ||
120 | arch_spin_unlock(lock); \ | ||
121 | preempt_enable(); \ | ||
122 | } \ | ||
123 | EXPORT_SYMBOL(name##_local_unlock_cpu); \ | ||
124 | \ | ||
125 | void name##_global_lock_online(void) { \ | ||
126 | int i; \ | ||
127 | preempt_disable(); \ | ||
128 | rwlock_acquire(&name##_lock_dep_map, 0, 0, _RET_IP_); \ | ||
129 | for_each_online_cpu(i) { \ | ||
130 | arch_spinlock_t *lock; \ | ||
131 | lock = &per_cpu(name##_lock, i); \ | ||
132 | arch_spin_lock(lock); \ | ||
133 | } \ | ||
134 | } \ | ||
135 | EXPORT_SYMBOL(name##_global_lock_online); \ | ||
136 | \ | ||
137 | void name##_global_unlock_online(void) { \ | ||
138 | int i; \ | ||
139 | rwlock_release(&name##_lock_dep_map, 1, _RET_IP_); \ | ||
140 | for_each_online_cpu(i) { \ | ||
141 | arch_spinlock_t *lock; \ | ||
142 | lock = &per_cpu(name##_lock, i); \ | ||
143 | arch_spin_unlock(lock); \ | ||
144 | } \ | ||
145 | preempt_enable(); \ | ||
146 | } \ | ||
147 | EXPORT_SYMBOL(name##_global_unlock_online); \ | ||
148 | \ | ||
149 | void name##_global_lock(void) { \ | ||
150 | int i; \ | ||
151 | preempt_disable(); \ | ||
152 | rwlock_acquire(&name##_lock_dep_map, 0, 0, _RET_IP_); \ | ||
153 | for_each_online_cpu(i) { \ | ||
154 | arch_spinlock_t *lock; \ | ||
155 | lock = &per_cpu(name##_lock, i); \ | ||
156 | arch_spin_lock(lock); \ | ||
157 | } \ | ||
158 | } \ | ||
159 | EXPORT_SYMBOL(name##_global_lock); \ | ||
160 | \ | ||
161 | void name##_global_unlock(void) { \ | ||
162 | int i; \ | ||
163 | rwlock_release(&name##_lock_dep_map, 1, _RET_IP_); \ | ||
164 | for_each_online_cpu(i) { \ | ||
165 | arch_spinlock_t *lock; \ | ||
166 | lock = &per_cpu(name##_lock, i); \ | ||
167 | arch_spin_unlock(lock); \ | ||
168 | } \ | ||
169 | preempt_enable(); \ | ||
170 | } \ | ||
171 | EXPORT_SYMBOL(name##_global_unlock); | ||
172 | #endif | ||