Skip to content

Commit 1a38218

Browse files
committed
zephyr: Replace critical-section implementation
There is a fairly fundamental incompatibility between Zephyr spin locks and the Critical Section specification. Zephyr spin locks do not allow nesting from within a single spin lock. The critical section API only has an `acquire` and `release` entry, and provides no way (such as a stack frame) to have a unique context for different invocation places. Unfortunately, this means we cannot use spin locks for critical sections. Instead, this change implements critical sections using irq locking. The implementation of these macros on Zephyr does try to make them SMP safe, with a simple atomic lock, but there is still something preventing the riscv SMP from working. Also, these entries cannot be called from user mode. There are various other reasons we don't support usermode, so at this time, just have a compile time assertion that usermode is not enabled in the build. If it is needed, we will have to come up with another way to implement this. Signed-off-by: David Brown <[email protected]>
1 parent e3518de commit 1a38218

File tree

1 file changed

+18
-21
lines changed

1 file changed

+18
-21
lines changed

zephyr/src/sys.rs

+18-21
Original file line numberDiff line numberDiff line change
@@ -39,42 +39,39 @@ pub fn uptime_get() -> i64 {
3939
unsafe { crate::raw::k_uptime_get() }
4040
}
4141

42+
// The below implementation, based on interrupt locking has only been tested on single CPU. The
43+
// implementation suggests it should work on SMP, and can be tested. The docs for irq_lock()
44+
// explicitly state that it cannot be used from userspace. Unfortunately, spinlocks have
45+
// incompatible semantics with critical sections, so to work with userspace we'd need probably a
46+
// syscall.
47+
#[cfg(CONFIG_USERSPACE)]
48+
compile_error!("Critical-section implementation does not work with CONFIG_USERSPACE");
49+
4250
pub mod critical {
4351
//! Zephyr implementation of critical sections.
4452
//!
45-
//! Critical sections from Rust are handled with a single Zephyr spinlock. This doesn't allow
46-
//! any nesting, but neither does the `critical-section` crate.
47-
//!
48-
//! This provides the underlying critical section crate, which is useful for external crates
49-
//! that want this interface. However, it isn't a particularly hygienic interface to use. For
50-
//! something a bit nicer, please see [`sync::SpinMutex`].
51-
//!
52-
//! [`sync::SpinMutex`]: crate::sync::SpinMutex
53+
//! The critical-section crate explicitly states that critical sections can be nested.
54+
//! Unfortunately, Zephyr spinlocks cannot be nested. It is possible to nest different ones,
55+
//! but the critical-section implementation API doesn't give access to the stack.
5356
54-
use core::{ffi::c_int, ptr::addr_of_mut};
57+
use core::{ffi::c_int, sync::atomic::{fence, Ordering}};
5558

5659
use critical_section::RawRestoreState;
57-
use zephyr_sys::{k_spin_lock, k_spin_unlock, k_spinlock, k_spinlock_key_t};
60+
use zephyr_sys::{zr_irq_lock, zr_irq_unlock};
5861

5962
struct ZephyrCriticalSection;
6063
critical_section::set_impl!(ZephyrCriticalSection);
6164

62-
// The critical section shares a single spinlock.
63-
static mut LOCK: k_spinlock = unsafe { core::mem::zeroed() };
64-
6565
unsafe impl critical_section::Impl for ZephyrCriticalSection {
6666
unsafe fn acquire() -> RawRestoreState {
67-
let res = k_spin_lock(addr_of_mut!(LOCK));
68-
res.key as RawRestoreState
67+
let res = zr_irq_lock();
68+
fence(Ordering::Acquire);
69+
res as RawRestoreState
6970
}
7071

7172
unsafe fn release(token: RawRestoreState) {
72-
k_spin_unlock(
73-
addr_of_mut!(LOCK),
74-
k_spinlock_key_t {
75-
key: token as c_int,
76-
},
77-
);
73+
fence(Ordering::Release);
74+
zr_irq_unlock(token as c_int);
7875
}
7976
}
8077
}

0 commit comments

Comments
 (0)