1d5de8841SJeremy Fitzhardinge /* 2d5de8841SJeremy Fitzhardinge * Split spinlock implementation out into its own file, so it can be 3d5de8841SJeremy Fitzhardinge * compiled in a FTRACE-compatible way. 4d5de8841SJeremy Fitzhardinge */ 5d5de8841SJeremy Fitzhardinge #include <linux/kernel_stat.h> 6d5de8841SJeremy Fitzhardinge #include <linux/spinlock.h> 7994025caSJeremy Fitzhardinge #include <linux/debugfs.h> 8994025caSJeremy Fitzhardinge #include <linux/log2.h> 95a0e3ad6STejun Heo #include <linux/gfp.h> 10354e7b76SKonrad Rzeszutek Wilk #include <linux/slab.h> 11d5de8841SJeremy Fitzhardinge 12d5de8841SJeremy Fitzhardinge #include <asm/paravirt.h> 13d5de8841SJeremy Fitzhardinge 14d5de8841SJeremy Fitzhardinge #include <xen/interface/xen.h> 15d5de8841SJeremy Fitzhardinge #include <xen/events.h> 16d5de8841SJeremy Fitzhardinge 17d5de8841SJeremy Fitzhardinge #include "xen-ops.h" 18994025caSJeremy Fitzhardinge #include "debugfs.h" 19994025caSJeremy Fitzhardinge 20994025caSJeremy Fitzhardinge #ifdef CONFIG_XEN_DEBUG_FS 21994025caSJeremy Fitzhardinge static struct xen_spinlock_stats 22994025caSJeremy Fitzhardinge { 23994025caSJeremy Fitzhardinge u64 taken; 24994025caSJeremy Fitzhardinge u32 taken_slow; 25994025caSJeremy Fitzhardinge u32 taken_slow_nested; 26994025caSJeremy Fitzhardinge u32 taken_slow_pickup; 27994025caSJeremy Fitzhardinge u32 taken_slow_spurious; 281e696f63SJeremy Fitzhardinge u32 taken_slow_irqenable; 29994025caSJeremy Fitzhardinge 30994025caSJeremy Fitzhardinge u64 released; 31994025caSJeremy Fitzhardinge u32 released_slow; 32994025caSJeremy Fitzhardinge u32 released_slow_kicked; 33994025caSJeremy Fitzhardinge 34f8eca41fSJeremy Fitzhardinge #define HISTO_BUCKETS 30 35f8eca41fSJeremy Fitzhardinge u32 histo_spin_total[HISTO_BUCKETS+1]; 36f8eca41fSJeremy Fitzhardinge u32 histo_spin_spinning[HISTO_BUCKETS+1]; 37f8eca41fSJeremy Fitzhardinge u32 histo_spin_blocked[HISTO_BUCKETS+1]; 38994025caSJeremy Fitzhardinge 39f8eca41fSJeremy Fitzhardinge u64 time_total; 40f8eca41fSJeremy Fitzhardinge u64 time_spinning; 41f8eca41fSJeremy Fitzhardinge u64 time_blocked; 42994025caSJeremy Fitzhardinge } spinlock_stats; 43994025caSJeremy Fitzhardinge 44994025caSJeremy Fitzhardinge static u8 zero_stats; 45994025caSJeremy Fitzhardinge 46994025caSJeremy Fitzhardinge static unsigned lock_timeout = 1 << 10; 47994025caSJeremy Fitzhardinge #define TIMEOUT lock_timeout 48994025caSJeremy Fitzhardinge 49994025caSJeremy Fitzhardinge static inline void check_zero(void) 50994025caSJeremy Fitzhardinge { 51994025caSJeremy Fitzhardinge if (unlikely(zero_stats)) { 52994025caSJeremy Fitzhardinge memset(&spinlock_stats, 0, sizeof(spinlock_stats)); 53994025caSJeremy Fitzhardinge zero_stats = 0; 54994025caSJeremy Fitzhardinge } 55994025caSJeremy Fitzhardinge } 56994025caSJeremy Fitzhardinge 57994025caSJeremy Fitzhardinge #define ADD_STATS(elem, val) \ 58994025caSJeremy Fitzhardinge do { check_zero(); spinlock_stats.elem += (val); } while(0) 59994025caSJeremy Fitzhardinge 60994025caSJeremy Fitzhardinge static inline u64 spin_time_start(void) 61994025caSJeremy Fitzhardinge { 62994025caSJeremy Fitzhardinge return xen_clocksource_read(); 63994025caSJeremy Fitzhardinge } 64994025caSJeremy Fitzhardinge 65994025caSJeremy Fitzhardinge static void __spin_time_accum(u64 delta, u32 *array) 66994025caSJeremy Fitzhardinge { 67994025caSJeremy Fitzhardinge unsigned index = ilog2(delta); 68994025caSJeremy Fitzhardinge 69994025caSJeremy Fitzhardinge check_zero(); 70994025caSJeremy Fitzhardinge 71994025caSJeremy Fitzhardinge if (index < HISTO_BUCKETS) 72994025caSJeremy Fitzhardinge array[index]++; 73994025caSJeremy Fitzhardinge else 74994025caSJeremy Fitzhardinge array[HISTO_BUCKETS]++; 75994025caSJeremy Fitzhardinge } 76994025caSJeremy Fitzhardinge 77f8eca41fSJeremy Fitzhardinge static inline void spin_time_accum_spinning(u64 start) 78994025caSJeremy Fitzhardinge { 79994025caSJeremy Fitzhardinge u32 delta = xen_clocksource_read() - start; 80994025caSJeremy Fitzhardinge 81f8eca41fSJeremy Fitzhardinge __spin_time_accum(delta, spinlock_stats.histo_spin_spinning); 82f8eca41fSJeremy Fitzhardinge spinlock_stats.time_spinning += delta; 83994025caSJeremy Fitzhardinge } 84994025caSJeremy Fitzhardinge 85f8eca41fSJeremy Fitzhardinge static inline void spin_time_accum_total(u64 start) 86994025caSJeremy Fitzhardinge { 87994025caSJeremy Fitzhardinge u32 delta = xen_clocksource_read() - start; 88994025caSJeremy Fitzhardinge 89f8eca41fSJeremy Fitzhardinge __spin_time_accum(delta, spinlock_stats.histo_spin_total); 90f8eca41fSJeremy Fitzhardinge spinlock_stats.time_total += delta; 91f8eca41fSJeremy Fitzhardinge } 92f8eca41fSJeremy Fitzhardinge 93f8eca41fSJeremy Fitzhardinge static inline void spin_time_accum_blocked(u64 start) 94f8eca41fSJeremy Fitzhardinge { 95f8eca41fSJeremy Fitzhardinge u32 delta = xen_clocksource_read() - start; 96f8eca41fSJeremy Fitzhardinge 97f8eca41fSJeremy Fitzhardinge __spin_time_accum(delta, spinlock_stats.histo_spin_blocked); 98f8eca41fSJeremy Fitzhardinge spinlock_stats.time_blocked += delta; 99994025caSJeremy Fitzhardinge } 100994025caSJeremy Fitzhardinge #else /* !CONFIG_XEN_DEBUG_FS */ 101994025caSJeremy Fitzhardinge #define TIMEOUT (1 << 10) 102994025caSJeremy Fitzhardinge #define ADD_STATS(elem, val) do { (void)(val); } while(0) 103994025caSJeremy Fitzhardinge 104994025caSJeremy Fitzhardinge static inline u64 spin_time_start(void) 105994025caSJeremy Fitzhardinge { 106994025caSJeremy Fitzhardinge return 0; 107994025caSJeremy Fitzhardinge } 108994025caSJeremy Fitzhardinge 109f8eca41fSJeremy Fitzhardinge static inline void spin_time_accum_total(u64 start) 110994025caSJeremy Fitzhardinge { 111994025caSJeremy Fitzhardinge } 112f8eca41fSJeremy Fitzhardinge static inline void spin_time_accum_spinning(u64 start) 113f8eca41fSJeremy Fitzhardinge { 114f8eca41fSJeremy Fitzhardinge } 115f8eca41fSJeremy Fitzhardinge static inline void spin_time_accum_blocked(u64 start) 116994025caSJeremy Fitzhardinge { 117994025caSJeremy Fitzhardinge } 118994025caSJeremy Fitzhardinge #endif /* CONFIG_XEN_DEBUG_FS */ 119d5de8841SJeremy Fitzhardinge 1207a7546b3SDavid Vrabel /* 1217a7546b3SDavid Vrabel * Size struct xen_spinlock so it's the same as arch_spinlock_t. 1227a7546b3SDavid Vrabel */ 1237a7546b3SDavid Vrabel #if NR_CPUS < 256 1247a7546b3SDavid Vrabel typedef u8 xen_spinners_t; 1257a7546b3SDavid Vrabel # define inc_spinners(xl) \ 1267a7546b3SDavid Vrabel asm(LOCK_PREFIX " incb %0" : "+m" ((xl)->spinners) : : "memory"); 1277a7546b3SDavid Vrabel # define dec_spinners(xl) \ 1287a7546b3SDavid Vrabel asm(LOCK_PREFIX " decb %0" : "+m" ((xl)->spinners) : : "memory"); 1297a7546b3SDavid Vrabel #else 1307a7546b3SDavid Vrabel typedef u16 xen_spinners_t; 1317a7546b3SDavid Vrabel # define inc_spinners(xl) \ 1327a7546b3SDavid Vrabel asm(LOCK_PREFIX " incw %0" : "+m" ((xl)->spinners) : : "memory"); 1337a7546b3SDavid Vrabel # define dec_spinners(xl) \ 1347a7546b3SDavid Vrabel asm(LOCK_PREFIX " decw %0" : "+m" ((xl)->spinners) : : "memory"); 1357a7546b3SDavid Vrabel #endif 1367a7546b3SDavid Vrabel 137d5de8841SJeremy Fitzhardinge struct xen_spinlock { 138d5de8841SJeremy Fitzhardinge unsigned char lock; /* 0 -> free; 1 -> locked */ 1397a7546b3SDavid Vrabel xen_spinners_t spinners; /* count of waiting cpus */ 140d5de8841SJeremy Fitzhardinge }; 141d5de8841SJeremy Fitzhardinge 142445c8951SThomas Gleixner static int xen_spin_is_locked(struct arch_spinlock *lock) 143d5de8841SJeremy Fitzhardinge { 144d5de8841SJeremy Fitzhardinge struct xen_spinlock *xl = (struct xen_spinlock *)lock; 145d5de8841SJeremy Fitzhardinge 146d5de8841SJeremy Fitzhardinge return xl->lock != 0; 147d5de8841SJeremy Fitzhardinge } 148d5de8841SJeremy Fitzhardinge 149445c8951SThomas Gleixner static int xen_spin_is_contended(struct arch_spinlock *lock) 150d5de8841SJeremy Fitzhardinge { 151d5de8841SJeremy Fitzhardinge struct xen_spinlock *xl = (struct xen_spinlock *)lock; 152d5de8841SJeremy Fitzhardinge 153d5de8841SJeremy Fitzhardinge /* Not strictly true; this is only the count of contended 154d5de8841SJeremy Fitzhardinge lock-takers entering the slow path. */ 155d5de8841SJeremy Fitzhardinge return xl->spinners != 0; 156d5de8841SJeremy Fitzhardinge } 157d5de8841SJeremy Fitzhardinge 158445c8951SThomas Gleixner static int xen_spin_trylock(struct arch_spinlock *lock) 159d5de8841SJeremy Fitzhardinge { 160d5de8841SJeremy Fitzhardinge struct xen_spinlock *xl = (struct xen_spinlock *)lock; 161d5de8841SJeremy Fitzhardinge u8 old = 1; 162d5de8841SJeremy Fitzhardinge 163d5de8841SJeremy Fitzhardinge asm("xchgb %b0,%1" 164d5de8841SJeremy Fitzhardinge : "+q" (old), "+m" (xl->lock) : : "memory"); 165d5de8841SJeremy Fitzhardinge 166d5de8841SJeremy Fitzhardinge return old == 0; 167d5de8841SJeremy Fitzhardinge } 168d5de8841SJeremy Fitzhardinge 169354e7b76SKonrad Rzeszutek Wilk static DEFINE_PER_CPU(char *, irq_name); 170d5de8841SJeremy Fitzhardinge static DEFINE_PER_CPU(int, lock_kicker_irq) = -1; 171d5de8841SJeremy Fitzhardinge static DEFINE_PER_CPU(struct xen_spinlock *, lock_spinners); 172d5de8841SJeremy Fitzhardinge 173168d2f46SJeremy Fitzhardinge /* 174168d2f46SJeremy Fitzhardinge * Mark a cpu as interested in a lock. Returns the CPU's previous 175168d2f46SJeremy Fitzhardinge * lock of interest, in case we got preempted by an interrupt. 176168d2f46SJeremy Fitzhardinge */ 177168d2f46SJeremy Fitzhardinge static inline struct xen_spinlock *spinning_lock(struct xen_spinlock *xl) 178d5de8841SJeremy Fitzhardinge { 179168d2f46SJeremy Fitzhardinge struct xen_spinlock *prev; 180168d2f46SJeremy Fitzhardinge 181780f36d8SChristoph Lameter prev = __this_cpu_read(lock_spinners); 182780f36d8SChristoph Lameter __this_cpu_write(lock_spinners, xl); 183168d2f46SJeremy Fitzhardinge 184d5de8841SJeremy Fitzhardinge wmb(); /* set lock of interest before count */ 185168d2f46SJeremy Fitzhardinge 1867a7546b3SDavid Vrabel inc_spinners(xl); 187168d2f46SJeremy Fitzhardinge 188168d2f46SJeremy Fitzhardinge return prev; 189d5de8841SJeremy Fitzhardinge } 190d5de8841SJeremy Fitzhardinge 191168d2f46SJeremy Fitzhardinge /* 192168d2f46SJeremy Fitzhardinge * Mark a cpu as no longer interested in a lock. Restores previous 193168d2f46SJeremy Fitzhardinge * lock of interest (NULL for none). 194168d2f46SJeremy Fitzhardinge */ 195168d2f46SJeremy Fitzhardinge static inline void unspinning_lock(struct xen_spinlock *xl, struct xen_spinlock *prev) 196d5de8841SJeremy Fitzhardinge { 1977a7546b3SDavid Vrabel dec_spinners(xl); 198168d2f46SJeremy Fitzhardinge wmb(); /* decrement count before restoring lock */ 199780f36d8SChristoph Lameter __this_cpu_write(lock_spinners, prev); 200d5de8841SJeremy Fitzhardinge } 201d5de8841SJeremy Fitzhardinge 202445c8951SThomas Gleixner static noinline int xen_spin_lock_slow(struct arch_spinlock *lock, bool irq_enable) 203d5de8841SJeremy Fitzhardinge { 204d5de8841SJeremy Fitzhardinge struct xen_spinlock *xl = (struct xen_spinlock *)lock; 205168d2f46SJeremy Fitzhardinge struct xen_spinlock *prev; 206780f36d8SChristoph Lameter int irq = __this_cpu_read(lock_kicker_irq); 207d5de8841SJeremy Fitzhardinge int ret; 208f8eca41fSJeremy Fitzhardinge u64 start; 209d5de8841SJeremy Fitzhardinge 210d5de8841SJeremy Fitzhardinge /* If kicker interrupts not initialized yet, just spin */ 211d5de8841SJeremy Fitzhardinge if (irq == -1) 212d5de8841SJeremy Fitzhardinge return 0; 213d5de8841SJeremy Fitzhardinge 214f8eca41fSJeremy Fitzhardinge start = spin_time_start(); 215f8eca41fSJeremy Fitzhardinge 216d5de8841SJeremy Fitzhardinge /* announce we're spinning */ 217168d2f46SJeremy Fitzhardinge prev = spinning_lock(xl); 218d5de8841SJeremy Fitzhardinge 219994025caSJeremy Fitzhardinge ADD_STATS(taken_slow, 1); 220994025caSJeremy Fitzhardinge ADD_STATS(taken_slow_nested, prev != NULL); 221994025caSJeremy Fitzhardinge 222168d2f46SJeremy Fitzhardinge do { 2234d576b57SJeremy Fitzhardinge unsigned long flags; 2244d576b57SJeremy Fitzhardinge 225d5de8841SJeremy Fitzhardinge /* clear pending */ 226d5de8841SJeremy Fitzhardinge xen_clear_irq_pending(irq); 227d5de8841SJeremy Fitzhardinge 228d5de8841SJeremy Fitzhardinge /* check again make sure it didn't become free while 229d5de8841SJeremy Fitzhardinge we weren't looking */ 230d5de8841SJeremy Fitzhardinge ret = xen_spin_trylock(lock); 231168d2f46SJeremy Fitzhardinge if (ret) { 232994025caSJeremy Fitzhardinge ADD_STATS(taken_slow_pickup, 1); 233994025caSJeremy Fitzhardinge 234168d2f46SJeremy Fitzhardinge /* 235168d2f46SJeremy Fitzhardinge * If we interrupted another spinlock while it 236168d2f46SJeremy Fitzhardinge * was blocking, make sure it doesn't block 237168d2f46SJeremy Fitzhardinge * without rechecking the lock. 238168d2f46SJeremy Fitzhardinge */ 239168d2f46SJeremy Fitzhardinge if (prev != NULL) 240168d2f46SJeremy Fitzhardinge xen_set_irq_pending(irq); 241d5de8841SJeremy Fitzhardinge goto out; 242168d2f46SJeremy Fitzhardinge } 243d5de8841SJeremy Fitzhardinge 244df9ee292SDavid Howells flags = arch_local_save_flags(); 2454d576b57SJeremy Fitzhardinge if (irq_enable) { 2464d576b57SJeremy Fitzhardinge ADD_STATS(taken_slow_irqenable, 1); 2474d576b57SJeremy Fitzhardinge raw_local_irq_enable(); 2484d576b57SJeremy Fitzhardinge } 2494d576b57SJeremy Fitzhardinge 250168d2f46SJeremy Fitzhardinge /* 251168d2f46SJeremy Fitzhardinge * Block until irq becomes pending. If we're 252168d2f46SJeremy Fitzhardinge * interrupted at this point (after the trylock but 253168d2f46SJeremy Fitzhardinge * before entering the block), then the nested lock 254168d2f46SJeremy Fitzhardinge * handler guarantees that the irq will be left 255168d2f46SJeremy Fitzhardinge * pending if there's any chance the lock became free; 256168d2f46SJeremy Fitzhardinge * xen_poll_irq() returns immediately if the irq is 257168d2f46SJeremy Fitzhardinge * pending. 258168d2f46SJeremy Fitzhardinge */ 259d5de8841SJeremy Fitzhardinge xen_poll_irq(irq); 2604d576b57SJeremy Fitzhardinge 2614d576b57SJeremy Fitzhardinge raw_local_irq_restore(flags); 2624d576b57SJeremy Fitzhardinge 263994025caSJeremy Fitzhardinge ADD_STATS(taken_slow_spurious, !xen_test_irq_pending(irq)); 264168d2f46SJeremy Fitzhardinge } while (!xen_test_irq_pending(irq)); /* check for spurious wakeups */ 265168d2f46SJeremy Fitzhardinge 266d6c88a50SThomas Gleixner kstat_incr_irqs_this_cpu(irq, irq_to_desc(irq)); 267d5de8841SJeremy Fitzhardinge 268d5de8841SJeremy Fitzhardinge out: 269168d2f46SJeremy Fitzhardinge unspinning_lock(xl, prev); 270f8eca41fSJeremy Fitzhardinge spin_time_accum_blocked(start); 271f8eca41fSJeremy Fitzhardinge 272d5de8841SJeremy Fitzhardinge return ret; 273d5de8841SJeremy Fitzhardinge } 274d5de8841SJeremy Fitzhardinge 275445c8951SThomas Gleixner static inline void __xen_spin_lock(struct arch_spinlock *lock, bool irq_enable) 276d5de8841SJeremy Fitzhardinge { 277d5de8841SJeremy Fitzhardinge struct xen_spinlock *xl = (struct xen_spinlock *)lock; 278994025caSJeremy Fitzhardinge unsigned timeout; 279d5de8841SJeremy Fitzhardinge u8 oldval; 280994025caSJeremy Fitzhardinge u64 start_spin; 281994025caSJeremy Fitzhardinge 282994025caSJeremy Fitzhardinge ADD_STATS(taken, 1); 283994025caSJeremy Fitzhardinge 284994025caSJeremy Fitzhardinge start_spin = spin_time_start(); 285d5de8841SJeremy Fitzhardinge 286d5de8841SJeremy Fitzhardinge do { 287994025caSJeremy Fitzhardinge u64 start_spin_fast = spin_time_start(); 288994025caSJeremy Fitzhardinge 289994025caSJeremy Fitzhardinge timeout = TIMEOUT; 290d5de8841SJeremy Fitzhardinge 291d5de8841SJeremy Fitzhardinge asm("1: xchgb %1,%0\n" 292d5de8841SJeremy Fitzhardinge " testb %1,%1\n" 293d5de8841SJeremy Fitzhardinge " jz 3f\n" 294d5de8841SJeremy Fitzhardinge "2: rep;nop\n" 295d5de8841SJeremy Fitzhardinge " cmpb $0,%0\n" 296d5de8841SJeremy Fitzhardinge " je 1b\n" 297d5de8841SJeremy Fitzhardinge " dec %2\n" 298d5de8841SJeremy Fitzhardinge " jnz 2b\n" 299d5de8841SJeremy Fitzhardinge "3:\n" 300d5de8841SJeremy Fitzhardinge : "+m" (xl->lock), "=q" (oldval), "+r" (timeout) 301d5de8841SJeremy Fitzhardinge : "1" (1) 302d5de8841SJeremy Fitzhardinge : "memory"); 303d5de8841SJeremy Fitzhardinge 304f8eca41fSJeremy Fitzhardinge spin_time_accum_spinning(start_spin_fast); 3051e696f63SJeremy Fitzhardinge 3061e696f63SJeremy Fitzhardinge } while (unlikely(oldval != 0 && 3071e696f63SJeremy Fitzhardinge (TIMEOUT == ~0 || !xen_spin_lock_slow(lock, irq_enable)))); 308994025caSJeremy Fitzhardinge 309f8eca41fSJeremy Fitzhardinge spin_time_accum_total(start_spin); 310d5de8841SJeremy Fitzhardinge } 311d5de8841SJeremy Fitzhardinge 312445c8951SThomas Gleixner static void xen_spin_lock(struct arch_spinlock *lock) 3131e696f63SJeremy Fitzhardinge { 3141e696f63SJeremy Fitzhardinge __xen_spin_lock(lock, false); 3151e696f63SJeremy Fitzhardinge } 3161e696f63SJeremy Fitzhardinge 317445c8951SThomas Gleixner static void xen_spin_lock_flags(struct arch_spinlock *lock, unsigned long flags) 3181e696f63SJeremy Fitzhardinge { 3191e696f63SJeremy Fitzhardinge __xen_spin_lock(lock, !raw_irqs_disabled_flags(flags)); 3201e696f63SJeremy Fitzhardinge } 3211e696f63SJeremy Fitzhardinge 322d5de8841SJeremy Fitzhardinge static noinline void xen_spin_unlock_slow(struct xen_spinlock *xl) 323d5de8841SJeremy Fitzhardinge { 324d5de8841SJeremy Fitzhardinge int cpu; 325d5de8841SJeremy Fitzhardinge 326994025caSJeremy Fitzhardinge ADD_STATS(released_slow, 1); 327994025caSJeremy Fitzhardinge 328d5de8841SJeremy Fitzhardinge for_each_online_cpu(cpu) { 329d5de8841SJeremy Fitzhardinge /* XXX should mix up next cpu selection */ 330d5de8841SJeremy Fitzhardinge if (per_cpu(lock_spinners, cpu) == xl) { 331994025caSJeremy Fitzhardinge ADD_STATS(released_slow_kicked, 1); 332d5de8841SJeremy Fitzhardinge xen_send_IPI_one(cpu, XEN_SPIN_UNLOCK_VECTOR); 333d5de8841SJeremy Fitzhardinge } 334d5de8841SJeremy Fitzhardinge } 335d5de8841SJeremy Fitzhardinge } 336d5de8841SJeremy Fitzhardinge 337445c8951SThomas Gleixner static void xen_spin_unlock(struct arch_spinlock *lock) 338d5de8841SJeremy Fitzhardinge { 339d5de8841SJeremy Fitzhardinge struct xen_spinlock *xl = (struct xen_spinlock *)lock; 340d5de8841SJeremy Fitzhardinge 341994025caSJeremy Fitzhardinge ADD_STATS(released, 1); 342994025caSJeremy Fitzhardinge 343d5de8841SJeremy Fitzhardinge smp_wmb(); /* make sure no writes get moved after unlock */ 344d5de8841SJeremy Fitzhardinge xl->lock = 0; /* release lock */ 345d5de8841SJeremy Fitzhardinge 3462496afbfSYang Xiaowei /* 3472496afbfSYang Xiaowei * Make sure unlock happens before checking for waiting 3482496afbfSYang Xiaowei * spinners. We need a strong barrier to enforce the 3492496afbfSYang Xiaowei * write-read ordering to different memory locations, as the 3502496afbfSYang Xiaowei * CPU makes no implied guarantees about their ordering. 3512496afbfSYang Xiaowei */ 3522496afbfSYang Xiaowei mb(); 353d5de8841SJeremy Fitzhardinge 354d5de8841SJeremy Fitzhardinge if (unlikely(xl->spinners)) 355d5de8841SJeremy Fitzhardinge xen_spin_unlock_slow(xl); 356d5de8841SJeremy Fitzhardinge } 357d5de8841SJeremy Fitzhardinge 358d5de8841SJeremy Fitzhardinge static irqreturn_t dummy_handler(int irq, void *dev_id) 359d5de8841SJeremy Fitzhardinge { 360d5de8841SJeremy Fitzhardinge BUG(); 361d5de8841SJeremy Fitzhardinge return IRQ_HANDLED; 362d5de8841SJeremy Fitzhardinge } 363d5de8841SJeremy Fitzhardinge 364*148f9bb8SPaul Gortmaker void xen_init_lock_cpu(int cpu) 365d5de8841SJeremy Fitzhardinge { 366d5de8841SJeremy Fitzhardinge int irq; 367354e7b76SKonrad Rzeszutek Wilk char *name; 368d5de8841SJeremy Fitzhardinge 369cb91f8f4SKonrad Rzeszutek Wilk WARN(per_cpu(lock_kicker_irq, cpu) >= 0, "spinlock on CPU%d exists on IRQ%d!\n", 370cb9c6f15SKonrad Rzeszutek Wilk cpu, per_cpu(lock_kicker_irq, cpu)); 371cb9c6f15SKonrad Rzeszutek Wilk 37270dd4998SKonrad Rzeszutek Wilk /* 37370dd4998SKonrad Rzeszutek Wilk * See git commit f10cd522c5fbfec9ae3cc01967868c9c2401ed23 37470dd4998SKonrad Rzeszutek Wilk * (xen: disable PV spinlocks on HVM) 37570dd4998SKonrad Rzeszutek Wilk */ 37670dd4998SKonrad Rzeszutek Wilk if (xen_hvm_domain()) 37770dd4998SKonrad Rzeszutek Wilk return; 37870dd4998SKonrad Rzeszutek Wilk 379d5de8841SJeremy Fitzhardinge name = kasprintf(GFP_KERNEL, "spinlock%d", cpu); 380d5de8841SJeremy Fitzhardinge irq = bind_ipi_to_irqhandler(XEN_SPIN_UNLOCK_VECTOR, 381d5de8841SJeremy Fitzhardinge cpu, 382d5de8841SJeremy Fitzhardinge dummy_handler, 383d5de8841SJeremy Fitzhardinge IRQF_DISABLED|IRQF_PERCPU|IRQF_NOBALANCING, 384d5de8841SJeremy Fitzhardinge name, 385d5de8841SJeremy Fitzhardinge NULL); 386d5de8841SJeremy Fitzhardinge 387d5de8841SJeremy Fitzhardinge if (irq >= 0) { 388d5de8841SJeremy Fitzhardinge disable_irq(irq); /* make sure it's never delivered */ 389d5de8841SJeremy Fitzhardinge per_cpu(lock_kicker_irq, cpu) = irq; 390354e7b76SKonrad Rzeszutek Wilk per_cpu(irq_name, cpu) = name; 391d5de8841SJeremy Fitzhardinge } 392d5de8841SJeremy Fitzhardinge 393d5de8841SJeremy Fitzhardinge printk("cpu %d spinlock event irq %d\n", cpu, irq); 394d5de8841SJeremy Fitzhardinge } 395d5de8841SJeremy Fitzhardinge 396d68d82afSAlex Nixon void xen_uninit_lock_cpu(int cpu) 397d68d82afSAlex Nixon { 39870dd4998SKonrad Rzeszutek Wilk /* 39970dd4998SKonrad Rzeszutek Wilk * See git commit f10cd522c5fbfec9ae3cc01967868c9c2401ed23 40070dd4998SKonrad Rzeszutek Wilk * (xen: disable PV spinlocks on HVM) 40170dd4998SKonrad Rzeszutek Wilk */ 40270dd4998SKonrad Rzeszutek Wilk if (xen_hvm_domain()) 40370dd4998SKonrad Rzeszutek Wilk return; 40470dd4998SKonrad Rzeszutek Wilk 405d68d82afSAlex Nixon unbind_from_irqhandler(per_cpu(lock_kicker_irq, cpu), NULL); 406cb9c6f15SKonrad Rzeszutek Wilk per_cpu(lock_kicker_irq, cpu) = -1; 407354e7b76SKonrad Rzeszutek Wilk kfree(per_cpu(irq_name, cpu)); 408354e7b76SKonrad Rzeszutek Wilk per_cpu(irq_name, cpu) = NULL; 409d68d82afSAlex Nixon } 410d68d82afSAlex Nixon 411d5de8841SJeremy Fitzhardinge void __init xen_init_spinlocks(void) 412d5de8841SJeremy Fitzhardinge { 41370dd4998SKonrad Rzeszutek Wilk /* 41470dd4998SKonrad Rzeszutek Wilk * See git commit f10cd522c5fbfec9ae3cc01967868c9c2401ed23 41570dd4998SKonrad Rzeszutek Wilk * (xen: disable PV spinlocks on HVM) 41670dd4998SKonrad Rzeszutek Wilk */ 41770dd4998SKonrad Rzeszutek Wilk if (xen_hvm_domain()) 41870dd4998SKonrad Rzeszutek Wilk return; 41970dd4998SKonrad Rzeszutek Wilk 4207a7546b3SDavid Vrabel BUILD_BUG_ON(sizeof(struct xen_spinlock) > sizeof(arch_spinlock_t)); 4217a7546b3SDavid Vrabel 422d5de8841SJeremy Fitzhardinge pv_lock_ops.spin_is_locked = xen_spin_is_locked; 423d5de8841SJeremy Fitzhardinge pv_lock_ops.spin_is_contended = xen_spin_is_contended; 424d5de8841SJeremy Fitzhardinge pv_lock_ops.spin_lock = xen_spin_lock; 4251e696f63SJeremy Fitzhardinge pv_lock_ops.spin_lock_flags = xen_spin_lock_flags; 426d5de8841SJeremy Fitzhardinge pv_lock_ops.spin_trylock = xen_spin_trylock; 427d5de8841SJeremy Fitzhardinge pv_lock_ops.spin_unlock = xen_spin_unlock; 428d5de8841SJeremy Fitzhardinge } 429994025caSJeremy Fitzhardinge 430994025caSJeremy Fitzhardinge #ifdef CONFIG_XEN_DEBUG_FS 431994025caSJeremy Fitzhardinge 432994025caSJeremy Fitzhardinge static struct dentry *d_spin_debug; 433994025caSJeremy Fitzhardinge 434994025caSJeremy Fitzhardinge static int __init xen_spinlock_debugfs(void) 435994025caSJeremy Fitzhardinge { 436994025caSJeremy Fitzhardinge struct dentry *d_xen = xen_init_debugfs(); 437994025caSJeremy Fitzhardinge 438994025caSJeremy Fitzhardinge if (d_xen == NULL) 439994025caSJeremy Fitzhardinge return -ENOMEM; 440994025caSJeremy Fitzhardinge 441994025caSJeremy Fitzhardinge d_spin_debug = debugfs_create_dir("spinlocks", d_xen); 442994025caSJeremy Fitzhardinge 443994025caSJeremy Fitzhardinge debugfs_create_u8("zero_stats", 0644, d_spin_debug, &zero_stats); 444994025caSJeremy Fitzhardinge 445994025caSJeremy Fitzhardinge debugfs_create_u32("timeout", 0644, d_spin_debug, &lock_timeout); 446994025caSJeremy Fitzhardinge 447994025caSJeremy Fitzhardinge debugfs_create_u64("taken", 0444, d_spin_debug, &spinlock_stats.taken); 448994025caSJeremy Fitzhardinge debugfs_create_u32("taken_slow", 0444, d_spin_debug, 449994025caSJeremy Fitzhardinge &spinlock_stats.taken_slow); 450994025caSJeremy Fitzhardinge debugfs_create_u32("taken_slow_nested", 0444, d_spin_debug, 451994025caSJeremy Fitzhardinge &spinlock_stats.taken_slow_nested); 452994025caSJeremy Fitzhardinge debugfs_create_u32("taken_slow_pickup", 0444, d_spin_debug, 453994025caSJeremy Fitzhardinge &spinlock_stats.taken_slow_pickup); 454994025caSJeremy Fitzhardinge debugfs_create_u32("taken_slow_spurious", 0444, d_spin_debug, 455994025caSJeremy Fitzhardinge &spinlock_stats.taken_slow_spurious); 4561e696f63SJeremy Fitzhardinge debugfs_create_u32("taken_slow_irqenable", 0444, d_spin_debug, 4571e696f63SJeremy Fitzhardinge &spinlock_stats.taken_slow_irqenable); 458994025caSJeremy Fitzhardinge 459994025caSJeremy Fitzhardinge debugfs_create_u64("released", 0444, d_spin_debug, &spinlock_stats.released); 460994025caSJeremy Fitzhardinge debugfs_create_u32("released_slow", 0444, d_spin_debug, 461994025caSJeremy Fitzhardinge &spinlock_stats.released_slow); 462994025caSJeremy Fitzhardinge debugfs_create_u32("released_slow_kicked", 0444, d_spin_debug, 463994025caSJeremy Fitzhardinge &spinlock_stats.released_slow_kicked); 464994025caSJeremy Fitzhardinge 465994025caSJeremy Fitzhardinge debugfs_create_u64("time_spinning", 0444, d_spin_debug, 466f8eca41fSJeremy Fitzhardinge &spinlock_stats.time_spinning); 467f8eca41fSJeremy Fitzhardinge debugfs_create_u64("time_blocked", 0444, d_spin_debug, 468f8eca41fSJeremy Fitzhardinge &spinlock_stats.time_blocked); 469994025caSJeremy Fitzhardinge debugfs_create_u64("time_total", 0444, d_spin_debug, 470f8eca41fSJeremy Fitzhardinge &spinlock_stats.time_total); 471994025caSJeremy Fitzhardinge 4729fe2a701SSrivatsa Vaddagiri debugfs_create_u32_array("histo_total", 0444, d_spin_debug, 473f8eca41fSJeremy Fitzhardinge spinlock_stats.histo_spin_total, HISTO_BUCKETS + 1); 4749fe2a701SSrivatsa Vaddagiri debugfs_create_u32_array("histo_spinning", 0444, d_spin_debug, 475f8eca41fSJeremy Fitzhardinge spinlock_stats.histo_spin_spinning, HISTO_BUCKETS + 1); 4769fe2a701SSrivatsa Vaddagiri debugfs_create_u32_array("histo_blocked", 0444, d_spin_debug, 477f8eca41fSJeremy Fitzhardinge spinlock_stats.histo_spin_blocked, HISTO_BUCKETS + 1); 478994025caSJeremy Fitzhardinge 479994025caSJeremy Fitzhardinge return 0; 480994025caSJeremy Fitzhardinge } 481994025caSJeremy Fitzhardinge fs_initcall(xen_spinlock_debugfs); 482994025caSJeremy Fitzhardinge 483994025caSJeremy Fitzhardinge #endif /* CONFIG_XEN_DEBUG_FS */ 484