diff options
Diffstat (limited to 'arch/x86/xen/spinlock.c')
-rw-r--r-- | arch/x86/xen/spinlock.c | 165 |
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 | ||
19 | static 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 | |||
39 | static u8 zero_stats; | ||
40 | |||
41 | static unsigned lock_timeout = 1 << 10; | ||
42 | #define TIMEOUT lock_timeout | ||
43 | |||
44 | static 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 | |||
55 | static inline u64 spin_time_start(void) | ||
56 | { | ||
57 | return xen_clocksource_read(); | ||
58 | } | ||
59 | |||
60 | static 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 | |||
72 | static 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 | |||
80 | static 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 | |||
91 | static inline u64 spin_time_start(void) | ||
92 | { | ||
93 | return 0; | ||
94 | } | ||
95 | |||
96 | static inline void spin_time_accum_fast(u64 start) | ||
97 | { | ||
98 | } | ||
99 | static inline void spin_time_accum(u64 start) | ||
100 | { | ||
101 | } | ||
102 | #endif /* CONFIG_XEN_DEBUG_FS */ | ||
14 | 103 | ||
15 | struct xen_spinlock { | 104 | struct 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: | |||
132 | static void xen_spin_lock(struct raw_spinlock *lock) | 227 | static 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 | ||
157 | static noinline void xen_spin_unlock_slow(struct xen_spinlock *xl) | 262 | static 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 | |||
332 | static struct dentry *d_spin_debug; | ||
333 | |||
334 | static 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 | } | ||
375 | fs_initcall(xen_spinlock_debugfs); | ||
376 | |||
377 | #endif /* CONFIG_XEN_DEBUG_FS */ | ||