Rust 中的裸机自旋锁实现

问题描述 投票:0回答:2

我正在以 64 位模式运行的 Raspberry Pi 3 上进行 Rust 裸机编程。我已经实现了一个自旋锁,如下所示:

use core::{sync::atomic::{AtomicBool, Ordering}, cell::UnsafeCell, ops::{Deref, DerefMut}};

pub struct SpinMutex<T> {
    lock: AtomicBool,
    data: UnsafeCell<T>
}

impl<T> SpinMutex<T> {
    #[allow(dead_code)]
    pub const fn new(data: T) -> Self {
        Self {
            lock: AtomicBool::new(false),
            data: UnsafeCell::new(data)
        }
    }

    pub fn lock(&self) -> SpinMutexGuard<T> {
        while self.lock.compare_exchange(false, true, Ordering::Acquire, Ordering::Relaxed).is_err() {}

        SpinMutexGuard {
            lock: &self.lock,
            data: unsafe { &mut *self.data.get() }
        }
    }
}

unsafe impl<T> Sync for SpinMutex<T> {}

pub struct SpinMutexGuard<'a, T> {
    lock: &'a AtomicBool,
    data: &'a mut T
}

impl<'a, T> Deref for SpinMutexGuard<'a, T> {
    type Target = T;
    fn deref(&self) -> &T {
        self.data
    }
}

impl<'a, T> DerefMut for SpinMutexGuard<'a, T> {
    fn deref_mut(&mut self) -> &mut T {
        self.data
    }
}

impl<'a, T> Drop for SpinMutexGuard<'a, T> {
    /// The dropping of the MutexGuard will release the lock it was created from.
    fn drop(&mut self) {
        self.lock.store(false, Ordering::Release);
    }
}

#[cfg(test)]
mod tests {
    use super::{SpinMutex};

    #[test]
    fn test_spin_mutex() {
        let state = SpinMutex::new(0);

        assert_eq!(*state.lock().data, 0);

        *state.lock().data = 9;

        assert_eq!(*state.lock().data, 9);
    }
}

当我在本地计算机(64 位 Windows)上运行测试时,锁可以工作。然而,在 Raspberry Pi 上,

lock
方法陷入无限循环并且永远不会返回。出现这种情况有什么原因吗?

这是 Rust 如何在禁用内联的情况下编译

compare_exchange_weak

   80ba8:   9100400a    add x10, x0, #0x10
   80bac:   085f7d48    ldxrb   w8, [x10]
   80bb0:   34000068    cbz w8, 80bbc
   80bb4:   d5033f5f    clrex
   80bb8:   14000004    b   80bc8
   80bbc:   52800029    mov w9, #0x1                    // #1
   80bc0:   080b7d49    stxrb   w11, w9, [x10]
   80bc4:   3400004b    cbz w11, 80bcc
   80bc8:   2a1f03e9    mov w9, wzr
   80bcc:   7100011f    cmp w8, #0x0
   80bd0:   52000120    eor w0, w9, #0x1
   80bd4:   1a9f07e1    cset    w1, ne  /
rust raspberry-pi arm bare-metal spinlock
2个回答
2
投票

这里有一个非详尽的条件列表,我确定(我已经以两种方式测试了它们)需要满足这些条件才能使原子在

EL1
(ARMv8) 中的 RPi 4 aarch64 上工作。 这可能与 RPi 3 (ARMv7) 非常相似。

  • 必须启用 MMU(
    SCTLR_EL1
    位 [0] 设置为
    0b1
  • 必须启用数据缓存(
    SCTLR_EL1
    位 [2] 设置为
    0b1
  • 锁所在的页面必须通过
    MAIR
    标记为正常的可缓存内存(我使用过
    0xff
    - 我不确定哪些位是多余的。原子,但我不认为有有充分理由使用其他任何东西来保持正常记忆)。

0
投票

ARM8 自旋锁测试存储独占操作是否成功,否则它们会继续旋转。如果 CPU 未处于正确状态,从另一个项目复制的代码可能会永远旋转。从这个意义上说,上面 Blazej Michalik 的回答和 artless-noise-bye-due2AI 的评论都是开门见山的。在满足 Blazej 的三个条件之前,我无法检测到成功的 STXR 操作,即所有争夺存储独占自旋锁的核心必须启用其缓存和 mmu,并且自旋锁本身必须位于正常的可缓存内存中。我很惊讶 ARM8 上的独占锁定需要正常的可缓存内存(而不是未缓存内存)。我在 Pi3b+、Pi4 和 Pi5 上使用 aarch64 EL3,所有三个平台的行为方式相同。

最新问题
© www.soinside.com 2019 - 2025. All rights reserved.