123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146 |
- /* Copyright 2019 SiFive, Inc */
- /* SPDX-License-Identifier: Apache-2.0 */
- #ifndef METAL__LOCK_H
- #define METAL__LOCK_H
- #include <metal/machine.h>
- #include <metal/memory.h>
- #include <metal/compiler.h>
- /*!
- * @file lock.h
- * @brief An API for creating and using a software lock/mutex
- */
- /* TODO: How can we make the exception code platform-independant? */
- #define _METAL_STORE_AMO_ACCESS_FAULT 7
- #define METAL_LOCK_BACKOFF_CYCLES 32
- #define METAL_LOCK_BACKOFF_EXPONENT 2
- /*!
- * @def METAL_LOCK_DECLARE
- * @brief Declare a lock
- *
- * Locks must be declared with METAL_LOCK_DECLARE to ensure that the lock
- * is linked into a memory region which supports atomic memory operations.
- */
- #define METAL_LOCK_DECLARE(name) \
- __attribute__((section(".data.locks"))) \
- struct metal_lock name
- /*!
- * @brief A handle for a lock
- */
- struct metal_lock {
- int _state;
- };
- /*!
- * @brief Initialize a lock
- * @param lock The handle for a lock
- * @return 0 if the lock is successfully initialized. A non-zero code indicates failure.
- *
- * If the lock cannot be initialized, attempts to take or give the lock
- * will result in a Store/AMO access fault.
- */
- __inline__ int metal_lock_init(struct metal_lock *lock) {
- #ifdef __riscv_atomic
- /* Get a handle for the memory which holds the lock state */
- struct metal_memory *lock_mem = metal_get_memory_from_address((uintptr_t) &(lock->_state));
- if(!lock_mem) {
- return 1;
- }
- /* If the memory doesn't support atomics, report an error */
- if(!metal_memory_supports_atomics(lock_mem)) {
- return 2;
- }
- lock->_state = 0;
- return 0;
- #else
- return 3;
- #endif
- }
- /*!
- * @brief Take a lock
- * @param lock The handle for a lock
- * @return 0 if the lock is successfully taken
- *
- * If the lock initialization failed, attempts to take a lock will result in
- * a Store/AMO access fault.
- */
- __inline__ int metal_lock_take(struct metal_lock *lock) {
- #ifdef __riscv_atomic
- int old = 1;
- int new = 1;
- int backoff = 1;
- const int max_backoff = METAL_LOCK_BACKOFF_CYCLES * METAL_MAX_CORES;
- while(1) {
- __asm__ volatile("amoswap.w.aq %[old], %[new], (%[state])"
- : [old] "=r" (old)
- : [new] "r" (new), [state] "r" (&(lock->_state))
- : "memory");
- if (old == 0) {
- break;
- }
- for (int i = 0; i < backoff; i++) {
- __asm__ volatile("");
- }
- if (backoff < max_backoff) {
- backoff *= METAL_LOCK_BACKOFF_EXPONENT;
- }
- }
- return 0;
- #else
- /* Store the memory address in mtval like a normal store/amo access fault */
- __asm__ ("csrw mtval, %[state]"
- :: [state] "r" (&(lock->_state)));
- /* Trigger a Store/AMO access fault */
- _metal_trap(_METAL_STORE_AMO_ACCESS_FAULT);
- /* If execution returns, indicate failure */
- return 1;
- #endif
- }
- /*!
- * @brief Give back a held lock
- * @param lock The handle for a lock
- * @return 0 if the lock is successfully given
- *
- * If the lock initialization failed, attempts to give a lock will result in
- * a Store/AMO access fault.
- */
- __inline__ int metal_lock_give(struct metal_lock *lock) {
- #ifdef __riscv_atomic
- __asm__ volatile("amoswap.w.rl x0, x0, (%[state])"
- :: [state] "r" (&(lock->_state))
- : "memory");
- return 0;
- #else
- /* Store the memory address in mtval like a normal store/amo access fault */
- __asm__ ("csrw mtval, %[state]"
- :: [state] "r" (&(lock->_state)));
- /* Trigger a Store/AMO access fault */
- _metal_trap(_METAL_STORE_AMO_ACCESS_FAULT);
- /* If execution returns, indicate failure */
- return 1;
- #endif
- }
- #endif /* METAL__LOCK_H */
|