blob: 918822bc767b1f3e9f112e98643af9c8e47a7a5f [file] [log] [blame] [edit]
/* Copyright (c) 2013 The Regents of the University of California
* Barret Rhoden <brho@cs.berkeley.edu>
* Kevin Klues <klueska@cs.berkeley.edu>
*
* Spinlocks and Spin-PDR locks (preemption detection/recovery)
*
* This file is part of Parlib.
*
* Parlib is free software: you can redistribute it and/or modify
* it under the terms of the Lesser GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Parlib is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* Lesser GNU General Public License for more details.
*
* See COPYING.LESSER for details on the GNU Lesser General Public License.
* See COPYING for details on the GNU General Public License. */
#include <stdlib.h>
#include <errno.h>
#include <assert.h>
#include <spinlock.h>
#include <vcore.h>
#include <uthread.h>
void spinlock_init(spinlock_t *lock)
{
lock->lock = 0;
}
int spinlock_trylock(spinlock_t *lock)
{
if (lock->lock)
return EBUSY;
return __sync_lock_test_and_set(&lock->lock, EBUSY);
}
void spinlock_lock(spinlock_t *lock)
{
while (spinlock_trylock(lock))
cpu_relax();
}
void spinlock_unlock(spinlock_t *lock)
{
__sync_lock_release(&lock->lock, 0);
}
/* Two different versions, with and without CAS. Default is with CAS. */
#ifndef CONFIG_SPINPDR_NO_CAS /* CAS version */
/* Spin-PRD locks (preemption detection/recovery). Idea is to CAS and put the
* lockholder's vcoreid in the lock, and all spinners ensure that vcore runs. */
void spin_pdr_init(struct spin_pdr_lock *pdr_lock)
{
pdr_lock->lock = SPINPDR_UNLOCKED;
}
/* Internal version of the locking func, doesn't care if notifs are disabled */
void __spin_pdr_lock(struct spin_pdr_lock *pdr_lock)
{
uint32_t vcoreid = vcore_id();
uint32_t lock_val;
do {
while ((lock_val = pdr_lock->lock) != SPINPDR_UNLOCKED) {
ensure_vcore_runs(lock_val);
cmb();
}
} while (!atomic_cas_u32(&pdr_lock->lock, lock_val, vcoreid));
cmb(); /* just need a cmb, the CAS handles the CPU wmb/wrmb() */
}
void __spin_pdr_unlock(struct spin_pdr_lock *pdr_lock)
{
/* could make an arch-dependent 'release barrier' out of these */
wmb(); /* Need to prevent the compiler from reordering older stores. */
rwmb(); /* And no old reads passing either. x86 makes both mbs a cmb() */
pdr_lock->lock = SPINPDR_UNLOCKED;
}
#else /* NON-CAS version */
/* Using regular spinlocks, with SPINPDR_VCOREID_UNKNOWN (-1) meaning 'no
* lockholder advertised yet'. There are two spots where the lockholder still
* holds the lock but hasn't advertised its vcoreid, and in those cases we
* ensure all vcores aren't preempted (linear scan). */
void spin_pdr_init(struct spin_pdr_lock *pdr_lock)
{
spinlock_init(&pdr_lock->spinlock);
pdr_lock->lockholder = SPINPDR_VCOREID_UNKNOWN;
}
void __spin_pdr_lock(struct spin_pdr_lock *pdr_lock)
{
uint32_t vcoreid = vcore_id();
uint32_t ensure_tgt;
while (spinlock_trylock(&pdr_lock->spinlock)) {
ensure_tgt = pdr_lock->lockholder;
/* ensure will make sure *every* vcore runs if you pass it your self. */
if (ensure_tgt == SPINPDR_VCOREID_UNKNOWN)
ensure_tgt = vcoreid;
ensure_vcore_runs(ensure_tgt);
cpu_relax();
}
pdr_lock->lockholder = vcoreid;
}
void __spin_pdr_unlock(struct spin_pdr_lock *pdr_lock)
{
pdr_lock->lockholder = SPINPDR_VCOREID_UNKNOWN;
spinlock_unlock(&pdr_lock->spinlock);
}
#endif /* CONFIG_SPINPDR_NO_CAS */
void spin_pdr_lock(struct spin_pdr_lock *pdr_lock)
{
/* Disable notifs, if we're an _M uthread */
uth_disable_notifs();
__spin_pdr_lock(pdr_lock);
}
void spin_pdr_unlock(struct spin_pdr_lock *pdr_lock)
{
__spin_pdr_unlock(pdr_lock);
/* Enable notifs, if we're an _M uthread */
uth_enable_notifs();
}