/* * Copyright (c) 2018 Intel Corporation. * * SPDX-License-Identifier: Apache-2.0 */ #ifndef ZEPHYR_INCLUDE_SPINLOCK_H_ #define ZEPHYR_INCLUDE_SPINLOCK_H_ #include #if defined(CONFIG_ASSERT) && (CONFIG_MP_NUM_CPUS < 4) #include #define SPIN_VALIDATE #endif struct k_spinlock_key { int key; }; typedef struct k_spinlock_key k_spinlock_key_t; struct k_spinlock { #ifdef CONFIG_SMP atomic_t locked; #endif #ifdef SPIN_VALIDATE /* Stores the thread that holds the lock with the locking CPU * ID in the bottom two bits. */ size_t thread_cpu; #endif }; static ALWAYS_INLINE k_spinlock_key_t k_spin_lock(struct k_spinlock *l) { k_spinlock_key_t k; /* Note that we need to use the underlying arch-specific lock * implementation. The "irq_lock()" API in SMP context is * actually a wrapper for a global spinlock! */ k.key = _arch_irq_lock(); #ifdef SPIN_VALIDATE if (l->thread_cpu) { __ASSERT((l->thread_cpu & 3) != _current_cpu->id, "Recursive spinlock"); } l->thread_cpu = _current_cpu->id | (u32_t)_current; #endif #ifdef CONFIG_SMP while (!atomic_cas(&l->locked, 0, 1)) { } #endif return k; } static ALWAYS_INLINE void k_spin_unlock(struct k_spinlock *l, k_spinlock_key_t key) { #ifdef SPIN_VALIDATE __ASSERT(l->thread_cpu == (_current_cpu->id | (u32_t)_current), "Not my spinlock!"); l->thread_cpu = 0; #endif #ifdef CONFIG_SMP /* Strictly we don't need atomic_clear() here (which is an * exchange operation that returns the old value). We are always * setting a zero and (because we hold the lock) know the existing * state won't change due to a race. But some architectures need * a memory barrier when used like this, and we don't have a * Zephyr framework for that. */ atomic_clear(&l->locked); #endif _arch_irq_unlock(key.key); } #endif /* ZEPHYR_INCLUDE_SPINLOCK_H_ */