Commit 8efcbab674de2bee45a2e4cdf97de16b8e609ac8

Authored by Jeremy Fitzhardinge
Committed by Ingo Molnar
1 parent 74d4affde8

paravirt: introduce a "lock-byte" spinlock implementation

Implement a version of the old spinlock algorithm, in which everyone
spins waiting for a lock byte.  In order to be compatible with the
ticket-lock's use of a zero initializer, this uses the convention of
'0' for unlocked and '1' for locked.

This algorithm is much better than ticket locks in a virtual
envionment, because it doesn't interact badly with the vcpu scheduler.
If there are multiple vcpus spinning on a lock and the lock is
released, the next vcpu to be scheduled will take the lock, rather
than cycling around until the next ticketed vcpu gets it.

To use this, you must call paravirt_use_bytelocks() very early, before
any spinlocks have been taken.

Signed-off-by: Jeremy Fitzhardinge <jeremy.fitzhardinge@citrix.com>
Cc: Jens Axboe <axboe@kernel.dk>
Cc: Peter Zijlstra <a.p.zijlstra@chello.nl>
Cc: Christoph Lameter <clameter@linux-foundation.org>
Cc: Petr Tesarik <ptesarik@suse.cz>
Cc: Virtualization <virtualization@lists.linux-foundation.org>
Cc: Xen devel <xen-devel@lists.xensource.com>
Cc: Thomas Friebel <thomas.friebel@amd.com>
Cc: Nick Piggin <nickpiggin@yahoo.com.au>
Signed-off-by: Ingo Molnar <mingo@elte.hu>

Showing 3 changed files with 75 additions and 1 deletions Side-by-side Diff

arch/x86/kernel/paravirt.c
... ... @@ -268,6 +268,15 @@
268 268 return __get_cpu_var(paravirt_lazy_mode);
269 269 }
270 270  
  271 +void __init paravirt_use_bytelocks(void)
  272 +{
  273 + pv_lock_ops.spin_is_locked = __byte_spin_is_locked;
  274 + pv_lock_ops.spin_is_contended = __byte_spin_is_contended;
  275 + pv_lock_ops.spin_lock = __byte_spin_lock;
  276 + pv_lock_ops.spin_trylock = __byte_spin_trylock;
  277 + pv_lock_ops.spin_unlock = __byte_spin_unlock;
  278 +}
  279 +
271 280 struct pv_info pv_info = {
272 281 .name = "bare hardware",
273 282 .paravirt_enabled = 0,
include/asm-x86/paravirt.h
... ... @@ -1385,6 +1385,8 @@
1385 1385 void _paravirt_nop(void);
1386 1386 #define paravirt_nop ((void *)_paravirt_nop)
1387 1387  
  1388 +void paravirt_use_bytelocks(void);
  1389 +
1388 1390 static inline int __raw_spin_is_locked(struct raw_spinlock *lock)
1389 1391 {
1390 1392 return PVOP_CALL1(int, pv_lock_ops.spin_is_locked, lock);
include/asm-x86/spinlock.h
... ... @@ -184,7 +184,70 @@
184 184  
185 185 #define __raw_spin_lock_flags(lock, flags) __raw_spin_lock(lock)
186 186  
187   -#ifndef CONFIG_PARAVIRT
  187 +#ifdef CONFIG_PARAVIRT
  188 +/*
  189 + * Define virtualization-friendly old-style lock byte lock, for use in
  190 + * pv_lock_ops if desired.
  191 + *
  192 + * This differs from the pre-2.6.24 spinlock by always using xchgb
  193 + * rather than decb to take the lock; this allows it to use a
  194 + * zero-initialized lock structure. It also maintains a 1-byte
  195 + * contention counter, so that we can implement
  196 + * __byte_spin_is_contended.
  197 + */
  198 +struct __byte_spinlock {
  199 + s8 lock;
  200 + s8 spinners;
  201 +};
  202 +
  203 +static inline int __byte_spin_is_locked(raw_spinlock_t *lock)
  204 +{
  205 + struct __byte_spinlock *bl = (struct __byte_spinlock *)lock;
  206 + return bl->lock != 0;
  207 +}
  208 +
  209 +static inline int __byte_spin_is_contended(raw_spinlock_t *lock)
  210 +{
  211 + struct __byte_spinlock *bl = (struct __byte_spinlock *)lock;
  212 + return bl->spinners != 0;
  213 +}
  214 +
  215 +static inline void __byte_spin_lock(raw_spinlock_t *lock)
  216 +{
  217 + struct __byte_spinlock *bl = (struct __byte_spinlock *)lock;
  218 + s8 val = 1;
  219 +
  220 + asm("1: xchgb %1, %0\n"
  221 + " test %1,%1\n"
  222 + " jz 3f\n"
  223 + " " LOCK_PREFIX "incb %2\n"
  224 + "2: rep;nop\n"
  225 + " cmpb $1, %0\n"
  226 + " je 2b\n"
  227 + " " LOCK_PREFIX "decb %2\n"
  228 + " jmp 1b\n"
  229 + "3:"
  230 + : "+m" (bl->lock), "+q" (val), "+m" (bl->spinners): : "memory");
  231 +}
  232 +
  233 +static inline int __byte_spin_trylock(raw_spinlock_t *lock)
  234 +{
  235 + struct __byte_spinlock *bl = (struct __byte_spinlock *)lock;
  236 + u8 old = 1;
  237 +
  238 + asm("xchgb %1,%0"
  239 + : "+m" (bl->lock), "+q" (old) : : "memory");
  240 +
  241 + return old == 0;
  242 +}
  243 +
  244 +static inline void __byte_spin_unlock(raw_spinlock_t *lock)
  245 +{
  246 + struct __byte_spinlock *bl = (struct __byte_spinlock *)lock;
  247 + smp_wmb();
  248 + bl->lock = 0;
  249 +}
  250 +#else /* !CONFIG_PARAVIRT */
188 251 static inline int __raw_spin_is_locked(raw_spinlock_t *lock)
189 252 {
190 253 return __ticket_spin_is_locked(lock);