[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: [Xen-devel] [PATCHv2 5/6] xen: use ticket locks for spin locks



At 15:19 +0100 on 10 Apr (1428679196), David Vrabel wrote:
> Replace the byte locks with ticket locks.  Ticket locks are: a) fair;
> and b) peform better when contented since they spin without an atomic
> operation.
> 
> The lock is split into two ticket values: head and tail.  A locker
> acquires a ticket by (atomically) increasing tail and using the
> previous tail value.  A CPU holds the lock if its ticket == head.  The
> lock is released by increasing head.
> 
> Architectures need only provide an xadd() implementation.

I like this.  Thanks for measuring the IRQ latency too -- that's the
most nervous-making part of the overall change. 

+1 to Andrew's comment about needing some more barriers on ARM.  In
particular read barriers during the spin and full acquire/release on
lock/unlock.

Should there be a follow-up patch that removes all the arch-specific
raw_spinlock_t stuff?  Or is it still in use somewhere else?

> +static int __spin_is_locked(spinlock_t *lock)
> +{
> +    return lock->tickets.head != lock->tickets.tail;
> +}

Maybe atomic_read the lock and do the comparison on the local value?
Just from an abundance of caution - can't think of an obvious reason
why this race should matter.

> +
>  int _spin_is_locked(spinlock_t *lock)
>  {
>      check_lock(&lock->debug);
> -    return _raw_spin_is_locked(&lock->raw);
> +    return __spin_is_locked(lock);
>  }
>  
>  int _spin_trylock(spinlock_t *lock)
>  {
> +    spinlock_tickets_t old, new;
> +
>      check_lock(&lock->debug);
> -    if ( !_raw_spin_trylock(&lock->raw) )
> +    old.head_tail = read_atomic(&lock->tickets.head_tail);

This is common enough that we might want a helper like:
static inline spinlock_tickets_t observe_lock (spinlock_tickets_t *l)
{
    smp_rmb();  
    return (spinlock_tickets_t) { .head_tail = 
read_atomic(&l->tickets.head_tail) };
}
(or some similar formulation that's actually valid C).

> @@ -239,7 +228,7 @@ void _spin_barrier(spinlock_t *lock)
>      u64      loop = 0;
>  
>      check_barrier(&lock->debug);
> -    do { smp_mb(); loop++;} while ( _raw_spin_is_locked(&lock->raw) );
> +    do { smp_mb(); loop++;} while ( __spin_is_locked(lock) );
>      if ((loop > 1) && lock->profile)
>      {
>          lock->profile->time_block += NOW() - block;
> @@ -247,14 +236,14 @@ void _spin_barrier(spinlock_t *lock)
>      }
>  #else
>      check_barrier(&lock->debug);
> -    do { smp_mb(); } while ( _raw_spin_is_locked(&lock->raw) );
> +    do { smp_mb(); } while ( __spin_is_locked(lock) );

For a contended lock this is potentially a lot stronger than the old
barrier: this waits until the lock is held _and_ no other CPU is waiting
for it.   I think what we need here is:
 - sample := observe_lock() 
 - if sample.head == sample.tail, exit immediately
 - else wait until observe_lock().head != sample.head

Cheers,

Tim.

_______________________________________________
Xen-devel mailing list
Xen-devel@xxxxxxxxxxxxx
http://lists.xen.org/xen-devel


 


Rackspace

Lists.xenproject.org is hosted with RackSpace, monitoring our
servers 24x7x365 and backed by RackSpace's Fanatical Support®.