新增rust版本的lockref (#135)

* new:Rust封装cpu_relax(),通过pause指令,让cpu休息一会儿。降低空转功耗

* new: Rust版本的lockref

* Rust的RawSpinlock新增is_locked()和set_value()方法。

* lockref文档
This commit is contained in:
login
2023-01-03 23:09:25 +08:00
committed by GitHub
parent 2726f101b4
commit 61de2cdc3f
10 changed files with 557 additions and 7 deletions

View File

@ -0,0 +1,7 @@
#include <arch/x86_64/include/asm/cmpxchg.h>
bool __try_cmpxchg_q(uint64_t *ptr, uint64_t *old_ptr, uint64_t *new_ptr)
{
bool success = __raw_try_cmpxchg(ptr, old_ptr, *new_ptr, 8);
return success;
}

View File

@ -0,0 +1,12 @@
// 该函数在cmpxchg.c中实现
extern "C" {
fn __try_cmpxchg_q(ptr: *mut u64, old_ptr: *mut u64, new_ptr: *mut u64) -> bool;
}
/// @brief 封装lock cmpxchg指令
/// 由于Rust实现这部分的内联汇编比较麻烦实在想不出办法因此使用C的实现。
#[inline]
pub unsafe fn try_cmpxchg_q(ptr: *mut u64, old_ptr: *mut u64, new_ptr: *mut u64) -> bool {
let retval = __try_cmpxchg_q(ptr, old_ptr, new_ptr);
return retval;
}

View File

@ -2,4 +2,5 @@ pub mod irqflags;
#[macro_use]
pub mod current;
pub mod ptrace;
pub mod bitops;
pub mod bitops;
pub mod cmpxchg;

View File

@ -14,3 +14,10 @@ pub fn arch_current_apic_id() -> u8 {
}
return (cpuid_res >> 24) as u8;
}
/// @brief 通过pause指令让cpu休息一会儿。降低空转功耗
pub fn cpu_relax() {
unsafe {
asm!("pause");
}
}

View File

@ -79,3 +79,5 @@ extern void __cmpxchg_wrong_size(void) __compiletime_error("Bad argument size fo
#define arch_try_cmpxchg(ptr, old_ptr, new_ptr) \
__raw_try_cmpxchg((ptr), (old_ptr), (new_ptr), sizeof(*ptr))
bool __try_cmpxchg_q(uint64_t *ptr, uint64_t *old_ptr, uint64_t *new_ptr);

View File

@ -29,8 +29,6 @@ mod sched;
mod smp;
mod time;
extern crate alloc;
use mm::allocator::KernelAllocator;
@ -39,6 +37,7 @@ use mm::allocator::KernelAllocator;
use crate::{
arch::asm::current::current_pcb,
include::bindings::bindings::{process_do_exit, BLACK, GREEN},
libs::lockref::LockRef,
};
// 声明全局的slab分配器
@ -85,5 +84,6 @@ pub fn panic(info: &PanicInfo) -> ! {
#[no_mangle]
pub extern "C" fn __rust_demo_func() -> i32 {
printk_color!(GREEN, BLACK, "__rust_demo_func()\n");
return 0;
}

355
kernel/src/libs/lockref.rs Normal file
View File

@ -0,0 +1,355 @@
#![allow(dead_code)]
use super::spinlock::RawSpinlock;
use crate::{
arch::asm::cmpxchg::try_cmpxchg_q,
include::bindings::bindings::{ENOTSUP, ETIMEDOUT},
};
use core::{fmt::Debug, intrinsics::size_of};
#[cfg(target_arch = "x86_64")]
/// 由于需要cmpxchg所以整个lockref按照8字节对齐
#[repr(align(8))]
#[derive(Debug)]
pub struct LockRef {
pub lock: RawSpinlock,
pub count: i32,
}
/// 除了x86_64以外的架构不使用cmpxchg进行优化
#[cfg(not(target_arch = "x86_64"))]
pub struct LockRef {
lock: RawSpinlock,
count: i32,
}
enum CmpxchgMode {
Increase,
IncreaseNotZero,
IncreaseNotDead,
Decrease,
DecreaseReturn,
DecreaseNotZero,
DecreaseOrLockNotZero,
}
impl LockRef {
pub const INIT: LockRef = LockRef {
lock: RawSpinlock::INIT,
count: 0,
};
pub fn new() -> LockRef {
assert_eq!(size_of::<LockRef>(), 8);
return LockRef::INIT;
}
/// @brief 为X86架构实现cmpxchg循环以支持无锁操作。
///
/// @return 操作成功返回Ok(new.count)
/// @return 操作失败,原因:超时 => 返回Err(-ETIMEDOUT)
/// @return 操作失败,原因:不满足规则 => 返回Err(1)
#[cfg(target_arch = "x86_64")]
#[inline]
fn cmpxchg_loop(&mut self, mode: CmpxchgMode) -> Result<i32, i32> {
use core::ptr::read_volatile;
use crate::arch::cpu::cpu_relax;
let mut old: LockRef = LockRef::INIT;
old.count = unsafe { read_volatile(&self.count) };
for _ in 0..100 {
if !old.lock.is_locked() {
let mut new = LockRef::INIT;
unsafe {
*(&mut new as *mut LockRef as *mut usize as *mut u64) =
read_volatile(&mut old as *mut LockRef as *mut usize as *mut u64);
new.lock.set_value(false);
}
// 根据不同情况,执行不同代码
match mode {
CmpxchgMode::Increase => {
new.count += 1;
}
CmpxchgMode::IncreaseNotZero => {
// 操作失败
if old.count <= 0 {
return Err(1);
}
new.count += 1;
}
CmpxchgMode::IncreaseNotDead => {
if old.count < 0 {
return Err(1);
}
new.count += 1;
}
CmpxchgMode::Decrease | CmpxchgMode::DecreaseReturn => {
if old.count <= 0 {
return Err(1);
}
new.count -= 1;
}
CmpxchgMode::DecreaseNotZero | CmpxchgMode::DecreaseOrLockNotZero => {
if old.count <= 1 {
return Err(1);
}
new.count -= 1;
}
}
if unsafe {
try_cmpxchg_q(
self as *mut LockRef as *mut usize as *mut u64,
&mut old as *mut LockRef as *mut usize as *mut u64,
&mut new as *mut LockRef as *mut usize as *mut u64,
)
} {
// 无锁操作成功,返回新的值
return Ok(new.count);
}
cpu_relax();
}
}
return Err(-(ETIMEDOUT as i32));
}
/// @brief 对于不支持无锁lockref的架构直接返回Err(-ENOTSUP),表示不支持
#[cfg(not(target_arch = "x86_64"))]
#[inline]
fn cmpxchg_loop(&mut self, mode: CmpxchgMode) -> Result<i32, i32> {
use crate::include::bindings::bindings::ENOTSUP;
return Err(-(ENOTSUP as i32));
}
/// @brief 原子的将引用计数加1
pub fn inc(&mut self) {
let cmpxchg_result = self.cmpxchg_loop(CmpxchgMode::Increase);
if cmpxchg_result.is_ok() {
return;
}
self.lock.lock();
self.count += 1;
self.lock.unlock();
}
/**
* @brief 原子地将引用计数加1.如果原来的count≤0则操作失败。
*
* @return Result<i32, i32> 操作成功=>Ok(self.count)
* 操作失败=>Err(-1)
*/
pub fn inc_not_zero(&mut self) -> Result<i32, i32> {
{
let cmpxchg_res = self.cmpxchg_loop(CmpxchgMode::IncreaseNotZero);
if cmpxchg_res.is_ok() {
return cmpxchg_res;
} else if cmpxchg_res.unwrap_err() == 1 {
// 不满足not zero 的条件
return Err(-1);
}
}
let mut retval = Err(-1);
self.lock.lock();
if self.count > 0 {
self.count += 1;
retval = Ok(self.count);
}
self.lock.unlock();
return retval;
}
/**
* @brief 引用计数自增1。除非该lockref已经被标记为死亡
*
* @return Ok(self.count) 操作成功
* @return Err(-1) 操作失败lockref已死亡
*/
pub fn inc_not_dead(&mut self) -> Result<i32, i32> {
{
let cmpxchg_result = self.cmpxchg_loop(CmpxchgMode::IncreaseNotDead);
if cmpxchg_result.is_ok() {
return cmpxchg_result;
} else if cmpxchg_result.unwrap_err() == 1 {
return Err(-1);
}
}
// 快捷路径操作失败,尝试加锁
let mut retval = Err(-1);
self.lock.lock();
if self.count >= 0 {
self.count += 1;
retval = Ok(self.count);
}
self.lock.unlock();
return retval;
}
/**
* @brief 原子地将引用计数-1。如果已处于count≤0的状态则返回Err(-1)
*
* 本函数与lockref_dec_return()的区别在于当在cmpxchg()中检测到count<=0或已加锁本函数会再次尝试通过加锁来执行操作
* 而后者会直接返回错误
*
* @return int 操作成功 => 返回新的引用变量值
* 操作失败lockref处于count≤0的状态 => 返回-1
*/
pub fn dec(&mut self) -> Result<i32, i32> {
{
let cmpxchg_result = self.cmpxchg_loop(CmpxchgMode::Decrease);
if cmpxchg_result.is_ok() {
return cmpxchg_result;
}
}
let retval: Result<i32, i32>;
self.lock.lock();
if self.count > 0 {
self.count -= 1;
retval = Ok(self.count);
} else {
retval = Err(-1);
}
self.lock.unlock();
return retval;
}
/**
* @brief 原子地将引用计数减1。如果处于已加锁或count≤0的状态则返回-1
* 若当前处理器架构不支持cmpxchg则退化为self.dec()
*
* 本函数与lockref_dec()的区别在于当在cmpxchg()中检测到count<=0或已加锁本函数会直接返回错误
* 而后者会再次尝试通过加锁来执行操作
*
* @return int 操作成功 => 返回新的引用变量值
* 操作失败lockref处于已加锁或count≤0的状态 => 返回-1
*/
pub fn dec_return(&mut self) -> Result<i32, i32> {
let cmpxchg_result = self.cmpxchg_loop(CmpxchgMode::DecreaseReturn);
if cmpxchg_result.is_ok() {
return cmpxchg_result;
} else if cmpxchg_result.unwrap_err() == 1 {
return Err(-1);
}
// 由于cmpxchg超时操作失败
if cmpxchg_result.unwrap_err() != -(ENOTSUP as i32) {
return Err(-1);
}
// 能走到这里代表架构当前不支持cmpxchg
// 退化为直接dec加锁
return self.dec();
}
/**
* @brief 原子地将引用计数减1。若当前的引用计数≤1则操作失败
*
* 该函数与lockref_dec_or_lock_not_zero()的区别在于当cmpxchg()时发现old.count≤1时该函数会直接返回Err(-1)
* 而后者在这种情况下,会尝试加锁来进行操作。
*
* @return Ok(self.count) 成功将引用计数减1
* @return Err(-1) 如果当前的引用计数≤1操作失败
*/
pub fn dec_not_zero(&mut self) -> Result<i32, i32> {
{
let cmpxchg_result = self.cmpxchg_loop(CmpxchgMode::DecreaseNotZero);
if cmpxchg_result.is_ok() {
return cmpxchg_result;
} else if cmpxchg_result.unwrap_err() == 1 {
return Err(-1);
}
}
let retval: Result<i32, i32>;
self.lock.lock();
if self.count > 1 {
self.count -= 1;
retval = Ok(self.count);
} else {
retval = Err(-1);
}
self.lock.unlock();
return retval;
}
/**
* @brief 原子地将引用计数减1。若当前的引用计数≤1则操作失败
*
* 该函数与lockref_dec_not_zero()的区别在于当cmpxchg()时发现old.count≤1时该函数会尝试加锁来进行操作。
* 而后者在这种情况下会直接返回Err(-1).
*
* @return Ok(self.count) 成功将引用计数减1
* @return Err(-1) 如果当前的引用计数≤1操作失败
*/
pub fn dec_or_lock_not_zero(&mut self) -> Result<i32, i32> {
{
let cmpxchg_result = self.cmpxchg_loop(CmpxchgMode::DecreaseOrLockNotZero);
if cmpxchg_result.is_ok() {
return cmpxchg_result;
}
}
let retval: Result<i32, i32>;
self.lock.lock();
if self.count > 1 {
self.count -= 1;
retval = Ok(self.count);
} else {
retval = Err(-1);
}
self.lock.unlock();
return retval;
}
/**
* @brief 原子地将lockref变量标记为已经死亡将count设置为负值
*/
pub fn mark_dead(&mut self) {
self.lock.lock();
self.count = -128;
self.lock.unlock();
}
}
/*
* 您可以使用以下代码测试lockref
let mut lockref = LockRef::new();
kdebug!("lockref={:?}", lockref);
lockref.inc();
assert_eq!(lockref.count, 1);
kdebug!("lockref={:?}", lockref);
assert!(lockref.dec().is_ok());
assert_eq!(lockref.count, 0);
assert!(lockref.dec().is_err());
assert_eq!(lockref.count, 0);
lockref.inc();
assert_eq!(lockref.count, 1);
assert!(lockref.dec_not_zero().is_err());
lockref.inc();
assert_eq!(lockref.count, 2);
assert!(lockref.dec_not_zero().is_ok());
lockref.mark_dead();
assert!(lockref.count < 0);
assert!(lockref.inc_not_dead().is_err());
kdebug!("lockref={:?}", lockref);
*/

View File

@ -5,4 +5,5 @@ pub mod ffi_convert;
pub mod refcount;
pub mod atomic;
pub mod wait_queue;
pub mod list;
pub mod list;
pub mod lockref;

View File

@ -57,6 +57,10 @@ pub fn spin_unlock_irq(lock: *mut spinlock_t) {
sti();
}
/// 原始的Spinlock自旋锁
/// 请注意这个自旋锁和C的不兼容。
///
/// @param self.0 这个AtomicBool的值为false时表示没有被加锁。当它为true时表示自旋锁已经被上锁。
#[derive(Debug)]
pub struct RawSpinlock(AtomicBool);
@ -107,6 +111,21 @@ impl RawSpinlock {
sti();
}
/// @brief 判断自旋锁是否被上锁
///
/// @return true 自旋锁被上锁
/// @return false 自旋锁处于解锁状态
pub fn is_locked(&self)->bool
{
return self.0.load(Ordering::Relaxed).into();
}
/// @brief 强制设置自旋锁的状态
/// 请注意这样操作可能会带来未知的风险。因此它是unsafe的。尽管从Rust语言本身来说它是safe的
pub unsafe fn set_value(&mut self, value:bool){
self.0.store(value, Ordering::SeqCst);
}
// todo: spin_lock_irqsave
// todo: spin_unlock_irqrestore