【问题标题】:Bare metal spinlock implementation in rustrust 中的裸机自旋锁实现
【发布时间】:2021-10-17 11:15:15
【问题描述】:

我正在以 64 位模式运行的树莓派 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)上运行测试时,锁会起作用。但是,在树莓派上,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  /

【问题讨论】:

  • 你的自旋锁的目的是什么,这是很多工作。您是否考虑过使用其他东西或不必担心自旋锁的方法?我知道这不是您要问的问题,但如果我不得不担心自旋锁,我将永远无法完成任何事情。
  • 我的旧树莓派只有一个核心。可能更现代的是多核,但你是裸机,所以也许你没有做适当的多任务处理?因为我认为只有一个核心的自旋锁没有意义。
  • 但是测试应该还是可以的,因为这里的锁只是按这个顺序反复上锁和解锁;从来没有争吵。
  • @rodrigo:RPi 3B+ 有 4 个内核。
  • 你是裸机,没有启用缓存和MMU?所以ldxrb 总是会失败,因为排他锁永远不会被占用。

标签: rust raspberry-pi arm bare-metal spinlock


【解决方案1】:

今天我遇到了同样的问题,这让我感到非常怀疑。

这是一个非详尽的条件列表,我确信(我已经对它们进行了两种方式的测试)需要发生,以便原子在 EL1 (ARMv8) 中的 RPi 4 aarch64 上工作。 这可能与 ARMv7 非常相似。

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

这是您开始怀疑 ARM 的参考指南是否旨在推出公司专有调试工具的问题之一......

【讨论】:

    猜你喜欢
    • 2020-03-27
    • 1970-01-01
    • 2021-01-22
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-02-08
    • 2018-11-13
    相关资源
    最近更新 更多