#ifndef __SMP_LOCK_H_
#define __SMP_LOCK_H_
#include <config.h>
#include <types.h>
#include <util.h>
#include <mode/machine.h>
#include <arch/model/statedata.h>
#include <smp/ipi.h>
#include <util.h>
#if CONFIG_MAX_NUM_NODES > 1
typedef enum {
CLHState_Granted = 0,
CLHState_Pending
} clh_qnode_state_t;
typedef struct clh_qnode {
volatile clh_qnode_state_t value;
PAD_TO_NEXT_CACHE_LN(sizeof(clh_qnode_state_t));
} clh_qnode_t;
typedef struct clh_qnode_p {
volatile clh_qnode_t *node;
volatile clh_qnode_t *next;
volatile word_t ipi;
PAD_TO_NEXT_CACHE_LN(sizeof(clh_qnode_t *) +
sizeof(clh_qnode_t *) +
sizeof(word_t));
} clh_qnode_p_t;
typedef struct clh_lock {
volatile clh_qnode_t nodes[CONFIG_MAX_NUM_NODES + 1];
volatile clh_qnode_p_t node_owners[CONFIG_MAX_NUM_NODES];
volatile clh_qnode_t *head;
PAD_TO_NEXT_CACHE_LN(sizeof(clh_qnode_t *));
} clh_lock_t;
extern clh_lock_t big_kernel_lock;
BOOT_CODE void clh_lock_init(void);
static inline bool_t FORCE_INLINE
clh_is_ipi_pending(word_t cpu)
{
return big_kernel_lock.node_owners[cpu].ipi == 1;
}
static inline void FORCE_INLINE
clh_lock_acquire(word_t cpu, bool_t irqPath)
{
volatile clh_qnode_t *prev;
big_kernel_lock.node_owners[cpu].node->value = CLHState_Pending;
prev = __atomic_exchange_n(&big_kernel_lock.head,
big_kernel_lock.node_owners[cpu].node, __ATOMIC_ACQUIRE);
big_kernel_lock.node_owners[cpu].next = prev;
while (big_kernel_lock.node_owners[cpu].next->value != CLHState_Granted) {
if (clh_is_ipi_pending(cpu)) {
Arch_handleIPI(irq_remote_call_ipi, irqPath);
}
arch_pause();
}
asm volatile("" ::: "memory");
}
static inline void FORCE_INLINE
clh_lock_release(word_t cpu)
{
__atomic_thread_fence(__ATOMIC_RELEASE);
big_kernel_lock.node_owners[cpu].node->value = CLHState_Granted;
big_kernel_lock.node_owners[cpu].node =
big_kernel_lock.node_owners[cpu].next;
}
static inline bool_t FORCE_INLINE
clh_is_self_in_queue(void)
{
return big_kernel_lock.node_owners[getCurrentCPUIndex()].node->value == CLHState_Pending;
}
#define NODE_LOCK(_irqPath) do { \
clh_lock_acquire(getCurrentCPUIndex(), _irqPath); \
} while(0)
#define NODE_UNLOCK do { \
clh_lock_release(getCurrentCPUIndex()); \
} while(0)
#define NODE_LOCK_IF(_cond, _irqPath) do { \
if((_cond)) { \
NODE_LOCK(_irqPath); \
} \
} while(0)
#define NODE_UNLOCK_IF_HELD do { \
if(clh_is_self_in_queue()) { \
NODE_UNLOCK; \
} \
} while(0)
#else
#define NODE_LOCK(_irq) do {} while (0)
#define NODE_UNLOCK do {} while (0)
#define NODE_LOCK_IF(_cond, _irq) do {} while (0)
#define NODE_UNLOCK_IF_HELD do {} while (0)
#endif
#define NODE_LOCK_SYS NODE_LOCK(false)
#define NODE_LOCK_IRQ NODE_LOCK(true)
#define NODE_LOCK_SYS_IF(_cond) NODE_LOCK_IF(_cond, false)
#define NODE_LOCK_IRQ_IF(_cond) NODE_LOCK_IF(_cond, true)
#endif