2 * Split spinlock implementation out into its own file, so it can be
3 * compiled in a FTRACE-compatible way.
5 #include <linux/kernel_stat.h>
6 #include <linux/spinlock.h>
7 #include <linux/debugfs.h>
8 #include <linux/log2.h>
10 #include <linux/slab.h>
12 #include <asm/paravirt.h>
14 #include <xen/interface/xen.h>
15 #include <xen/events.h>
20 #ifdef CONFIG_XEN_DEBUG_FS
21 static struct xen_spinlock_stats
25 u32 taken_slow_nested
;
26 u32 taken_slow_pickup
;
27 u32 taken_slow_spurious
;
28 u32 taken_slow_irqenable
;
32 u32 released_slow_kicked
;
34 #define HISTO_BUCKETS 30
35 u32 histo_spin_total
[HISTO_BUCKETS
+1];
36 u32 histo_spin_spinning
[HISTO_BUCKETS
+1];
37 u32 histo_spin_blocked
[HISTO_BUCKETS
+1];
46 static unsigned lock_timeout
= 1 << 10;
47 #define TIMEOUT lock_timeout
49 static inline void check_zero(void)
51 if (unlikely(zero_stats
)) {
52 memset(&spinlock_stats
, 0, sizeof(spinlock_stats
));
57 #define ADD_STATS(elem, val) \
58 do { check_zero(); spinlock_stats.elem += (val); } while(0)
60 static inline u64
spin_time_start(void)
62 return xen_clocksource_read();
65 static void __spin_time_accum(u64 delta
, u32
*array
)
67 unsigned index
= ilog2(delta
);
71 if (index
< HISTO_BUCKETS
)
74 array
[HISTO_BUCKETS
]++;
77 static inline void spin_time_accum_spinning(u64 start
)
79 u32 delta
= xen_clocksource_read() - start
;
81 __spin_time_accum(delta
, spinlock_stats
.histo_spin_spinning
);
82 spinlock_stats
.time_spinning
+= delta
;
85 static inline void spin_time_accum_total(u64 start
)
87 u32 delta
= xen_clocksource_read() - start
;
89 __spin_time_accum(delta
, spinlock_stats
.histo_spin_total
);
90 spinlock_stats
.time_total
+= delta
;
93 static inline void spin_time_accum_blocked(u64 start
)
95 u32 delta
= xen_clocksource_read() - start
;
97 __spin_time_accum(delta
, spinlock_stats
.histo_spin_blocked
);
98 spinlock_stats
.time_blocked
+= delta
;
100 #else /* !CONFIG_XEN_DEBUG_FS */
101 #define TIMEOUT (1 << 10)
102 #define ADD_STATS(elem, val) do { (void)(val); } while(0)
104 static inline u64
spin_time_start(void)
109 static inline void spin_time_accum_total(u64 start
)
112 static inline void spin_time_accum_spinning(u64 start
)
115 static inline void spin_time_accum_blocked(u64 start
)
118 #endif /* CONFIG_XEN_DEBUG_FS */
121 * Size struct xen_spinlock so it's the same as arch_spinlock_t.
124 typedef u8 xen_spinners_t
;
125 # define inc_spinners(xl) \
126 asm(LOCK_PREFIX " incb %0" : "+m" ((xl)->spinners) : : "memory");
127 # define dec_spinners(xl) \
128 asm(LOCK_PREFIX " decb %0" : "+m" ((xl)->spinners) : : "memory");
130 typedef u16 xen_spinners_t
;
131 # define inc_spinners(xl) \
132 asm(LOCK_PREFIX " incw %0" : "+m" ((xl)->spinners) : : "memory");
133 # define dec_spinners(xl) \
134 asm(LOCK_PREFIX " decw %0" : "+m" ((xl)->spinners) : : "memory");
137 struct xen_spinlock
{
138 unsigned char lock
; /* 0 -> free; 1 -> locked */
139 xen_spinners_t spinners
; /* count of waiting cpus */
142 static int xen_spin_is_locked(struct arch_spinlock
*lock
)
144 struct xen_spinlock
*xl
= (struct xen_spinlock
*)lock
;
146 return xl
->lock
!= 0;
149 static int xen_spin_is_contended(struct arch_spinlock
*lock
)
151 struct xen_spinlock
*xl
= (struct xen_spinlock
*)lock
;
153 /* Not strictly true; this is only the count of contended
154 lock-takers entering the slow path. */
155 return xl
->spinners
!= 0;
158 static int xen_spin_trylock(struct arch_spinlock
*lock
)
160 struct xen_spinlock
*xl
= (struct xen_spinlock
*)lock
;
164 : "+q" (old
), "+m" (xl
->lock
) : : "memory");
169 static DEFINE_PER_CPU(char *, irq_name
);
170 static DEFINE_PER_CPU(int, lock_kicker_irq
) = -1;
171 static DEFINE_PER_CPU(struct xen_spinlock
*, lock_spinners
);
174 * Mark a cpu as interested in a lock. Returns the CPU's previous
175 * lock of interest, in case we got preempted by an interrupt.
177 static inline struct xen_spinlock
*spinning_lock(struct xen_spinlock
*xl
)
179 struct xen_spinlock
*prev
;
181 prev
= __this_cpu_read(lock_spinners
);
182 __this_cpu_write(lock_spinners
, xl
);
184 wmb(); /* set lock of interest before count */
192 * Mark a cpu as no longer interested in a lock. Restores previous
193 * lock of interest (NULL for none).
195 static inline void unspinning_lock(struct xen_spinlock
*xl
, struct xen_spinlock
*prev
)
198 wmb(); /* decrement count before restoring lock */
199 __this_cpu_write(lock_spinners
, prev
);
202 static noinline
int xen_spin_lock_slow(struct arch_spinlock
*lock
, bool irq_enable
)
204 struct xen_spinlock
*xl
= (struct xen_spinlock
*)lock
;
205 struct xen_spinlock
*prev
;
206 int irq
= __this_cpu_read(lock_kicker_irq
);
210 /* If kicker interrupts not initialized yet, just spin */
214 start
= spin_time_start();
216 /* announce we're spinning */
217 prev
= spinning_lock(xl
);
219 ADD_STATS(taken_slow
, 1);
220 ADD_STATS(taken_slow_nested
, prev
!= NULL
);
226 xen_clear_irq_pending(irq
);
228 /* check again make sure it didn't become free while
229 we weren't looking */
230 ret
= xen_spin_trylock(lock
);
232 ADD_STATS(taken_slow_pickup
, 1);
235 * If we interrupted another spinlock while it
236 * was blocking, make sure it doesn't block
237 * without rechecking the lock.
240 xen_set_irq_pending(irq
);
244 flags
= arch_local_save_flags();
246 ADD_STATS(taken_slow_irqenable
, 1);
247 raw_local_irq_enable();
251 * Block until irq becomes pending. If we're
252 * interrupted at this point (after the trylock but
253 * before entering the block), then the nested lock
254 * handler guarantees that the irq will be left
255 * pending if there's any chance the lock became free;
256 * xen_poll_irq() returns immediately if the irq is
261 raw_local_irq_restore(flags
);
263 ADD_STATS(taken_slow_spurious
, !xen_test_irq_pending(irq
));
264 } while (!xen_test_irq_pending(irq
)); /* check for spurious wakeups */
266 kstat_incr_irqs_this_cpu(irq
, irq_to_desc(irq
));
269 unspinning_lock(xl
, prev
);
270 spin_time_accum_blocked(start
);
275 static inline void __xen_spin_lock(struct arch_spinlock
*lock
, bool irq_enable
)
277 struct xen_spinlock
*xl
= (struct xen_spinlock
*)lock
;
284 start_spin
= spin_time_start();
287 u64 start_spin_fast
= spin_time_start();
291 asm("1: xchgb %1,%0\n"
300 : "+m" (xl
->lock
), "=q" (oldval
), "+r" (timeout
)
304 spin_time_accum_spinning(start_spin_fast
);
306 } while (unlikely(oldval
!= 0 &&
307 (TIMEOUT
== ~0 || !xen_spin_lock_slow(lock
, irq_enable
))));
309 spin_time_accum_total(start_spin
);
312 static void xen_spin_lock(struct arch_spinlock
*lock
)
314 __xen_spin_lock(lock
, false);
317 static void xen_spin_lock_flags(struct arch_spinlock
*lock
, unsigned long flags
)
319 __xen_spin_lock(lock
, !raw_irqs_disabled_flags(flags
));
322 static noinline
void xen_spin_unlock_slow(struct xen_spinlock
*xl
)
326 ADD_STATS(released_slow
, 1);
328 for_each_online_cpu(cpu
) {
329 /* XXX should mix up next cpu selection */
330 if (per_cpu(lock_spinners
, cpu
) == xl
) {
331 ADD_STATS(released_slow_kicked
, 1);
332 xen_send_IPI_one(cpu
, XEN_SPIN_UNLOCK_VECTOR
);
337 static void xen_spin_unlock(struct arch_spinlock
*lock
)
339 struct xen_spinlock
*xl
= (struct xen_spinlock
*)lock
;
341 ADD_STATS(released
, 1);
343 smp_wmb(); /* make sure no writes get moved after unlock */
344 xl
->lock
= 0; /* release lock */
347 * Make sure unlock happens before checking for waiting
348 * spinners. We need a strong barrier to enforce the
349 * write-read ordering to different memory locations, as the
350 * CPU makes no implied guarantees about their ordering.
354 if (unlikely(xl
->spinners
))
355 xen_spin_unlock_slow(xl
);
358 static irqreturn_t
dummy_handler(int irq
, void *dev_id
)
364 void xen_init_lock_cpu(int cpu
)
369 WARN(per_cpu(lock_kicker_irq
, cpu
) >= 0, "spinlock on CPU%d exists on IRQ%d!\n",
370 cpu
, per_cpu(lock_kicker_irq
, cpu
));
373 * See git commit f10cd522c5fbfec9ae3cc01967868c9c2401ed23
374 * (xen: disable PV spinlocks on HVM)
376 if (xen_hvm_domain())
379 name
= kasprintf(GFP_KERNEL
, "spinlock%d", cpu
);
380 irq
= bind_ipi_to_irqhandler(XEN_SPIN_UNLOCK_VECTOR
,
383 IRQF_DISABLED
|IRQF_PERCPU
|IRQF_NOBALANCING
,
388 disable_irq(irq
); /* make sure it's never delivered */
389 per_cpu(lock_kicker_irq
, cpu
) = irq
;
390 per_cpu(irq_name
, cpu
) = name
;
393 printk("cpu %d spinlock event irq %d\n", cpu
, irq
);
396 void xen_uninit_lock_cpu(int cpu
)
399 * See git commit f10cd522c5fbfec9ae3cc01967868c9c2401ed23
400 * (xen: disable PV spinlocks on HVM)
402 if (xen_hvm_domain())
405 unbind_from_irqhandler(per_cpu(lock_kicker_irq
, cpu
), NULL
);
406 per_cpu(lock_kicker_irq
, cpu
) = -1;
407 kfree(per_cpu(irq_name
, cpu
));
408 per_cpu(irq_name
, cpu
) = NULL
;
411 void __init
xen_init_spinlocks(void)
414 * See git commit f10cd522c5fbfec9ae3cc01967868c9c2401ed23
415 * (xen: disable PV spinlocks on HVM)
417 if (xen_hvm_domain())
420 BUILD_BUG_ON(sizeof(struct xen_spinlock
) > sizeof(arch_spinlock_t
));
422 pv_lock_ops
.spin_is_locked
= xen_spin_is_locked
;
423 pv_lock_ops
.spin_is_contended
= xen_spin_is_contended
;
424 pv_lock_ops
.spin_lock
= xen_spin_lock
;
425 pv_lock_ops
.spin_lock_flags
= xen_spin_lock_flags
;
426 pv_lock_ops
.spin_trylock
= xen_spin_trylock
;
427 pv_lock_ops
.spin_unlock
= xen_spin_unlock
;
430 #ifdef CONFIG_XEN_DEBUG_FS
432 static struct dentry
*d_spin_debug
;
434 static int __init
xen_spinlock_debugfs(void)
436 struct dentry
*d_xen
= xen_init_debugfs();
441 d_spin_debug
= debugfs_create_dir("spinlocks", d_xen
);
443 debugfs_create_u8("zero_stats", 0644, d_spin_debug
, &zero_stats
);
445 debugfs_create_u32("timeout", 0644, d_spin_debug
, &lock_timeout
);
447 debugfs_create_u64("taken", 0444, d_spin_debug
, &spinlock_stats
.taken
);
448 debugfs_create_u32("taken_slow", 0444, d_spin_debug
,
449 &spinlock_stats
.taken_slow
);
450 debugfs_create_u32("taken_slow_nested", 0444, d_spin_debug
,
451 &spinlock_stats
.taken_slow_nested
);
452 debugfs_create_u32("taken_slow_pickup", 0444, d_spin_debug
,
453 &spinlock_stats
.taken_slow_pickup
);
454 debugfs_create_u32("taken_slow_spurious", 0444, d_spin_debug
,
455 &spinlock_stats
.taken_slow_spurious
);
456 debugfs_create_u32("taken_slow_irqenable", 0444, d_spin_debug
,
457 &spinlock_stats
.taken_slow_irqenable
);
459 debugfs_create_u64("released", 0444, d_spin_debug
, &spinlock_stats
.released
);
460 debugfs_create_u32("released_slow", 0444, d_spin_debug
,
461 &spinlock_stats
.released_slow
);
462 debugfs_create_u32("released_slow_kicked", 0444, d_spin_debug
,
463 &spinlock_stats
.released_slow_kicked
);
465 debugfs_create_u64("time_spinning", 0444, d_spin_debug
,
466 &spinlock_stats
.time_spinning
);
467 debugfs_create_u64("time_blocked", 0444, d_spin_debug
,
468 &spinlock_stats
.time_blocked
);
469 debugfs_create_u64("time_total", 0444, d_spin_debug
,
470 &spinlock_stats
.time_total
);
472 debugfs_create_u32_array("histo_total", 0444, d_spin_debug
,
473 spinlock_stats
.histo_spin_total
, HISTO_BUCKETS
+ 1);
474 debugfs_create_u32_array("histo_spinning", 0444, d_spin_debug
,
475 spinlock_stats
.histo_spin_spinning
, HISTO_BUCKETS
+ 1);
476 debugfs_create_u32_array("histo_blocked", 0444, d_spin_debug
,
477 spinlock_stats
.histo_spin_blocked
, HISTO_BUCKETS
+ 1);
481 fs_initcall(xen_spinlock_debugfs
);
483 #endif /* CONFIG_XEN_DEBUG_FS */