aboutsummaryrefslogtreecommitdiffstats
path: root/arch/x86/xen/spinlock.c
diff options
context:
space:
mode:
Diffstat (limited to 'arch/x86/xen/spinlock.c')
-rw-r--r--arch/x86/xen/spinlock.c165
1 files changed, 162 insertions, 3 deletions
diff --git a/arch/x86/xen/spinlock.c b/arch/x86/xen/spinlock.c
index 4884bc603aa7..0d8f3b2d9bec 100644
--- a/arch/x86/xen/spinlock.c
+++ b/arch/x86/xen/spinlock.c
@@ -4,6 +4,8 @@
4 */ 4 */
5#include <linux/kernel_stat.h> 5#include <linux/kernel_stat.h>
6#include <linux/spinlock.h> 6#include <linux/spinlock.h>
7#include <linux/debugfs.h>
8#include <linux/log2.h>
7 9
8#include <asm/paravirt.h> 10#include <asm/paravirt.h>
9 11
@@ -11,6 +13,93 @@
11#include <xen/events.h> 13#include <xen/events.h>
12 14
13#include "xen-ops.h" 15#include "xen-ops.h"
16#include "debugfs.h"
17
18#ifdef CONFIG_XEN_DEBUG_FS
19static struct xen_spinlock_stats
20{
21 u64 taken;
22 u32 taken_slow;
23 u32 taken_slow_nested;
24 u32 taken_slow_pickup;
25 u32 taken_slow_spurious;
26
27 u64 released;
28 u32 released_slow;
29 u32 released_slow_kicked;
30
31#define HISTO_BUCKETS 20
32 u32 histo_spin_fast[HISTO_BUCKETS+1];
33 u32 histo_spin[HISTO_BUCKETS+1];
34
35 u64 spinning_time;
36 u64 total_time;
37} spinlock_stats;
38
39static u8 zero_stats;
40
41static unsigned lock_timeout = 1 << 10;
42#define TIMEOUT lock_timeout
43
44static inline void check_zero(void)
45{
46 if (unlikely(zero_stats)) {
47 memset(&spinlock_stats, 0, sizeof(spinlock_stats));
48 zero_stats = 0;
49 }
50}
51
52#define ADD_STATS(elem, val) \
53 do { check_zero(); spinlock_stats.elem += (val); } while(0)
54
55static inline u64 spin_time_start(void)
56{
57 return xen_clocksource_read();
58}
59
60static void __spin_time_accum(u64 delta, u32 *array)
61{
62 unsigned index = ilog2(delta);
63
64 check_zero();
65
66 if (index < HISTO_BUCKETS)
67 array[index]++;
68 else
69 array[HISTO_BUCKETS]++;
70}
71
72static inline void spin_time_accum_fast(u64 start)
73{
74 u32 delta = xen_clocksource_read() - start;
75
76 __spin_time_accum(delta, spinlock_stats.histo_spin_fast);
77 spinlock_stats.spinning_time += delta;
78}
79
80static inline void spin_time_accum(u64 start)
81{
82 u32 delta = xen_clocksource_read() - start;
83
84 __spin_time_accum(delta, spinlock_stats.histo_spin);
85 spinlock_stats.total_time += delta;
86}
87#else /* !CONFIG_XEN_DEBUG_FS */
88#define TIMEOUT (1 << 10)
89#define ADD_STATS(elem, val) do { (void)(val); } while(0)
90
91static inline u64 spin_time_start(void)
92{
93 return 0;
94}
95
96static inline void spin_time_accum_fast(u64 start)
97{
98}
99static inline void spin_time_accum(u64 start)
100{
101}
102#endif /* CONFIG_XEN_DEBUG_FS */
14 103
15struct xen_spinlock { 104struct xen_spinlock {
16 unsigned char lock; /* 0 -> free; 1 -> locked */ 105 unsigned char lock; /* 0 -> free; 1 -> locked */
@@ -92,6 +181,9 @@ static noinline int xen_spin_lock_slow(struct raw_spinlock *lock)
92 /* announce we're spinning */ 181 /* announce we're spinning */
93 prev = spinning_lock(xl); 182 prev = spinning_lock(xl);
94 183
184 ADD_STATS(taken_slow, 1);
185 ADD_STATS(taken_slow_nested, prev != NULL);
186
95 do { 187 do {
96 /* clear pending */ 188 /* clear pending */
97 xen_clear_irq_pending(irq); 189 xen_clear_irq_pending(irq);
@@ -100,6 +192,8 @@ static noinline int xen_spin_lock_slow(struct raw_spinlock *lock)
100 we weren't looking */ 192 we weren't looking */
101 ret = xen_spin_trylock(lock); 193 ret = xen_spin_trylock(lock);
102 if (ret) { 194 if (ret) {
195 ADD_STATS(taken_slow_pickup, 1);
196
103 /* 197 /*
104 * If we interrupted another spinlock while it 198 * If we interrupted another spinlock while it
105 * was blocking, make sure it doesn't block 199 * was blocking, make sure it doesn't block
@@ -120,6 +214,7 @@ static noinline int xen_spin_lock_slow(struct raw_spinlock *lock)
120 * pending. 214 * pending.
121 */ 215 */
122 xen_poll_irq(irq); 216 xen_poll_irq(irq);
217 ADD_STATS(taken_slow_spurious, !xen_test_irq_pending(irq));
123 } while (!xen_test_irq_pending(irq)); /* check for spurious wakeups */ 218 } while (!xen_test_irq_pending(irq)); /* check for spurious wakeups */
124 219
125 kstat_this_cpu.irqs[irq]++; 220 kstat_this_cpu.irqs[irq]++;
@@ -132,11 +227,18 @@ out:
132static void xen_spin_lock(struct raw_spinlock *lock) 227static void xen_spin_lock(struct raw_spinlock *lock)
133{ 228{
134 struct xen_spinlock *xl = (struct xen_spinlock *)lock; 229 struct xen_spinlock *xl = (struct xen_spinlock *)lock;
135 int timeout; 230 unsigned timeout;
136 u8 oldval; 231 u8 oldval;
232 u64 start_spin;
233
234 ADD_STATS(taken, 1);
235
236 start_spin = spin_time_start();
137 237
138 do { 238 do {
139 timeout = 1 << 10; 239 u64 start_spin_fast = spin_time_start();
240
241 timeout = TIMEOUT;
140 242
141 asm("1: xchgb %1,%0\n" 243 asm("1: xchgb %1,%0\n"
142 " testb %1,%1\n" 244 " testb %1,%1\n"
@@ -151,16 +253,22 @@ static void xen_spin_lock(struct raw_spinlock *lock)
151 : "1" (1) 253 : "1" (1)
152 : "memory"); 254 : "memory");
153 255
154 } while (unlikely(oldval != 0 && !xen_spin_lock_slow(lock))); 256 spin_time_accum_fast(start_spin_fast);
257 } while (unlikely(oldval != 0 && (TIMEOUT == ~0 || !xen_spin_lock_slow(lock))));
258
259 spin_time_accum(start_spin);
155} 260}
156 261
157static noinline void xen_spin_unlock_slow(struct xen_spinlock *xl) 262static noinline void xen_spin_unlock_slow(struct xen_spinlock *xl)
158{ 263{
159 int cpu; 264 int cpu;
160 265
266 ADD_STATS(released_slow, 1);
267
161 for_each_online_cpu(cpu) { 268 for_each_online_cpu(cpu) {
162 /* XXX should mix up next cpu selection */ 269 /* XXX should mix up next cpu selection */
163 if (per_cpu(lock_spinners, cpu) == xl) { 270 if (per_cpu(lock_spinners, cpu) == xl) {
271 ADD_STATS(released_slow_kicked, 1);
164 xen_send_IPI_one(cpu, XEN_SPIN_UNLOCK_VECTOR); 272 xen_send_IPI_one(cpu, XEN_SPIN_UNLOCK_VECTOR);
165 break; 273 break;
166 } 274 }
@@ -171,6 +279,8 @@ static void xen_spin_unlock(struct raw_spinlock *lock)
171{ 279{
172 struct xen_spinlock *xl = (struct xen_spinlock *)lock; 280 struct xen_spinlock *xl = (struct xen_spinlock *)lock;
173 281
282 ADD_STATS(released, 1);
283
174 smp_wmb(); /* make sure no writes get moved after unlock */ 284 smp_wmb(); /* make sure no writes get moved after unlock */
175 xl->lock = 0; /* release lock */ 285 xl->lock = 0; /* release lock */
176 286
@@ -216,3 +326,52 @@ void __init xen_init_spinlocks(void)
216 pv_lock_ops.spin_trylock = xen_spin_trylock; 326 pv_lock_ops.spin_trylock = xen_spin_trylock;
217 pv_lock_ops.spin_unlock = xen_spin_unlock; 327 pv_lock_ops.spin_unlock = xen_spin_unlock;
218} 328}
329
330#ifdef CONFIG_XEN_DEBUG_FS
331
332static struct dentry *d_spin_debug;
333
334static int __init xen_spinlock_debugfs(void)
335{
336 struct dentry *d_xen = xen_init_debugfs();
337
338 if (d_xen == NULL)
339 return -ENOMEM;
340
341 d_spin_debug = debugfs_create_dir("spinlocks", d_xen);
342
343 debugfs_create_u8("zero_stats", 0644, d_spin_debug, &zero_stats);
344
345 debugfs_create_u32("timeout", 0644, d_spin_debug, &lock_timeout);
346
347 debugfs_create_u64("taken", 0444, d_spin_debug, &spinlock_stats.taken);
348 debugfs_create_u32("taken_slow", 0444, d_spin_debug,
349 &spinlock_stats.taken_slow);
350 debugfs_create_u32("taken_slow_nested", 0444, d_spin_debug,
351 &spinlock_stats.taken_slow_nested);
352 debugfs_create_u32("taken_slow_pickup", 0444, d_spin_debug,
353 &spinlock_stats.taken_slow_pickup);
354 debugfs_create_u32("taken_slow_spurious", 0444, d_spin_debug,
355 &spinlock_stats.taken_slow_spurious);
356
357 debugfs_create_u64("released", 0444, d_spin_debug, &spinlock_stats.released);
358 debugfs_create_u32("released_slow", 0444, d_spin_debug,
359 &spinlock_stats.released_slow);
360 debugfs_create_u32("released_slow_kicked", 0444, d_spin_debug,
361 &spinlock_stats.released_slow_kicked);
362
363 debugfs_create_u64("time_spinning", 0444, d_spin_debug,
364 &spinlock_stats.spinning_time);
365 debugfs_create_u64("time_total", 0444, d_spin_debug,
366 &spinlock_stats.total_time);
367
368 xen_debugfs_create_u32_array("histo_total", 0444, d_spin_debug,
369 spinlock_stats.histo_spin, HISTO_BUCKETS + 1);
370 xen_debugfs_create_u32_array("histo_spinning", 0444, d_spin_debug,
371 spinlock_stats.histo_spin_fast, HISTO_BUCKETS + 1);
372
373 return 0;
374}
375fs_initcall(xen_spinlock_debugfs);
376
377#endif /* CONFIG_XEN_DEBUG_FS */