| /* basic locking code that compiles on linux.  #included directly into | 
 |  * lock_test.  It's a .h so that make tests doesn't build it. */ | 
 |  | 
 | #define ARCH_CL_SIZE 64 | 
 | #define SPINLOCK_INITIALIZER {FALSE} | 
 |  | 
 | typedef struct { | 
 | 	bool locked; | 
 | } spinlock_t; | 
 |  | 
 | void __attribute__((noinline)) spinlock_init(spinlock_t *lock) | 
 | { | 
 | 	lock->locked = FALSE; | 
 | } | 
 |  | 
 | /* Returns TRUE if we grabbed the lock */ | 
 | bool __attribute__((noinline)) spinlock_trylock(spinlock_t *lock) | 
 | { | 
 | 	if (lock->locked) | 
 | 		return FALSE; | 
 | 	return !__sync_lock_test_and_set(&lock->locked, TRUE); | 
 | } | 
 |  | 
 | void __attribute__((noinline)) spinlock_lock(spinlock_t *lock) | 
 | { | 
 | 	while (!spinlock_trylock(lock)) | 
 | 		cpu_relax(); | 
 | } | 
 |  | 
 | void __attribute__((noinline)) spinlock_unlock(spinlock_t *lock) | 
 | { | 
 | 	__sync_lock_release(&lock->locked, FALSE); | 
 | } | 
 |  | 
 | #define MCS_LOCK_INIT {0} | 
 | #define MCS_QNODE_INIT {0, 0} | 
 |  | 
 | typedef struct mcs_lock_qnode | 
 | { | 
 | 	struct mcs_lock_qnode *next; | 
 | 	int locked; | 
 | }__attribute__((aligned(ARCH_CL_SIZE))) mcs_lock_qnode_t; | 
 |  | 
 | /* included for the dummy init in lock_thread */ | 
 | struct mcs_pdro_qnode | 
 | { | 
 | 	struct mcs_pdro_qnode *next; | 
 | 	int locked; | 
 | 	uint32_t vcoreid; | 
 | }__attribute__((aligned(ARCH_CL_SIZE))); | 
 |  | 
 | #define MCSPDRO_QNODE_INIT {0, 0, 0} | 
 | #define mcs_pdr_init(args...) {} | 
 |  | 
 | typedef struct mcs_lock | 
 | { | 
 | 	mcs_lock_qnode_t *lock; | 
 | } mcs_lock_t; | 
 |  | 
 | void __attribute__((noinline)) mcs_lock_init(struct mcs_lock *lock) | 
 | { | 
 | 	memset(lock, 0, sizeof(mcs_lock_t)); | 
 | } | 
 |  | 
 | static inline mcs_lock_qnode_t *mcs_qnode_swap(mcs_lock_qnode_t **addr, | 
 |                                                mcs_lock_qnode_t *val) | 
 | { | 
 | 	return (mcs_lock_qnode_t*) __sync_lock_test_and_set((void**)addr, val); | 
 | } | 
 |  | 
 | void __attribute__((noinline)) | 
 | mcs_lock_lock(struct mcs_lock *lock, struct mcs_lock_qnode *qnode) | 
 | { | 
 | 	qnode->next = 0; | 
 | 	cmb();	/* swap provides a CPU mb() */ | 
 | 	mcs_lock_qnode_t *predecessor = mcs_qnode_swap(&lock->lock, qnode); | 
 | 	if (predecessor) { | 
 | 		qnode->locked = 1; | 
 | 		wmb(); | 
 | 		predecessor->next = qnode; | 
 | 		/* no need for a wrmb(), since this will only get unlocked | 
 | 		 * after they read our previous write */ | 
 | 		while (qnode->locked) | 
 | 			cpu_relax(); | 
 | 	} | 
 | 	cmb();	/* just need a cmb, the swap handles the CPU wmb/wrmb() */ | 
 | } | 
 |  | 
 | void __attribute__((noinline)) | 
 | mcs_lock_unlock(struct mcs_lock *lock, struct mcs_lock_qnode *qnode) | 
 | { | 
 | 	/* Check if someone is already waiting on us to unlock */ | 
 | 	if (qnode->next == 0) { | 
 | 		cmb(); /* no need for CPU mbs, since there's an atomic_swap() */ | 
 | 		/* Unlock it */ | 
 | 		mcs_lock_qnode_t *old_tail = mcs_qnode_swap(&lock->lock,0); | 
 | 		/* no one else was already waiting, so we successfully unlocked | 
 | 		 * and can return */ | 
 | 		if (old_tail == qnode) | 
 | 			return; | 
 | 		/* someone else was already waiting on the lock (last one on | 
 | 		 * the list), and we accidentally took them off.  Try and put | 
 | 		 * it back. */ | 
 | 		mcs_lock_qnode_t *usurper = mcs_qnode_swap(&lock->lock, | 
 | 							   old_tail); | 
 | 		/* since someone else was waiting, they should have made | 
 | 		 * themselves our next.  spin (very briefly!) til it happens. | 
 | 		 * */ | 
 | 		while (qnode->next == 0) | 
 | 			cpu_relax(); | 
 | 		if (usurper) { | 
 | 			/* an usurper is someone who snuck in before we could | 
 | 			 * put the old tail back.  They now have the lock. | 
 | 			 * Let's put whoever is supposed to be next as their | 
 | 			 * next one. */ | 
 | 			usurper->next = qnode->next; | 
 | 		} else { | 
 | 			/* No usurper meant we put things back correctly, so we | 
 | 			 * should just pass the lock / unlock whoever is next */ | 
 | 			qnode->next->locked = 0; | 
 | 		} | 
 | 	} else { | 
 | 		/* mb()s necessary since we didn't call an atomic_swap() */ | 
 | 		/* need to make sure any previous writes don't pass unlocking */ | 
 | 		wmb(); | 
 | 		/* need to make sure any reads happen before the unlocking */ | 
 | 		rwmb(); | 
 | 		/* simply unlock whoever is next */ | 
 | 		qnode->next->locked = 0; | 
 | 	} | 
 | } | 
 |  | 
 | /* CAS style mcs locks, kept around til we use them.  We're using the | 
 |  * usurper-style, since RISCV doesn't have a real CAS (yet?). */ | 
 | void __attribute__((noinline)) | 
 | mcs_lock_unlock_cas(struct mcs_lock *lock, struct mcs_lock_qnode *qnode) | 
 | { | 
 | 	/* Check if someone is already waiting on us to unlock */ | 
 | 	if (qnode->next == 0) { | 
 | 		cmb();	/* no need for CPU mbs, since there's an atomic_cas() */ | 
 | 		/* If we're still the lock, just swap it with 0 (unlock) and | 
 | 		 * return */ | 
 | 		if (__sync_bool_compare_and_swap((void**)&lock->lock, qnode, 0)) | 
 | 			return; | 
 | 		/* We failed, someone is there and we are some (maybe a | 
 | 		 * different) thread's pred.  Since someone else was waiting, | 
 | 		 * they should have made themselves our next.  Spin (very | 
 | 		 * briefly!) til it happens. */ | 
 | 		while (qnode->next == 0) | 
 | 			cpu_relax(); | 
 | 		/* Alpha wants a read_barrier_depends() here */ | 
 | 		/* Now that we have a next, unlock them */ | 
 | 		qnode->next->locked = 0; | 
 | 	} else { | 
 | 		/* mb()s necessary since we didn't call an atomic_swap() */ | 
 | 		/* need to make sure any previous writes don't pass unlocking */ | 
 | 		wmb(); | 
 | 		/* need to make sure any reads happen before the unlocking */ | 
 | 		rwmb(); | 
 | 		/* simply unlock whoever is next */ | 
 | 		qnode->next->locked = 0; | 
 | 	} | 
 | } |