Skip to content

Commit

Permalink
feat(ida): IDA内部改为使用XArray实现 (DragonOS-Community#934)
Browse files Browse the repository at this point in the history
目前可以记录哪些ID已经分配,支持了ID释放的功能.

Signed-off-by: longjin <[email protected]>
  • Loading branch information
fslongjin authored and BrahmaMantra committed Dec 9, 2024
1 parent dd4ce90 commit 0eb0731
Show file tree
Hide file tree
Showing 20 changed files with 273 additions and 83 deletions.
11 changes: 11 additions & 0 deletions docs/kernel/libs/id-allocation.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
# ID分配

:::{note}
本文作者:龙进 <[email protected]>

2024年9月25日
:::

内核提供了一个名为`IdAllocator`的ID分配器,位于`kernel/crates/ida`中。

它能够分配、释放ID。默认它会自增分配,假如ID大于设定的最大值,它会从最小值开始寻找空闲ID。如果没有空闲的ID,则会分配失败。
1 change: 1 addition & 0 deletions docs/kernel/libs/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -10,4 +10,5 @@
lib_ui/scm
lib_ui/textui
unified-init
id-allocation

3 changes: 1 addition & 2 deletions kernel/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ elf = { version = "=0.7.2", default-features = false }
fdt = { git = "https://git.mirrors.dragonos.org.cn/DragonOS-Community/fdt", rev = "9862813020" }
# 一个no_std的hashmap、hashset
hashbrown = "=0.13.2"
ida = { path = "src/libs/ida" }
ida = { path = "crates/ida" }
intertrait = { path = "crates/intertrait" }
kdepends = { path = "crates/kdepends" }
klog_types = { path = "crates/klog_types" }
Expand All @@ -57,7 +57,6 @@ wait_queue_macros = { path = "crates/wait_queue_macros" }
paste = "=1.0.14"
slabmalloc = { path = "crates/rust-slabmalloc" }
log = "0.4.21"
xarray = "0.1.0"
lru = "0.12.3"

# target为x86_64时,使用下面的依赖
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ name = "ida"
version = "0.1.0"
edition = "2021"
authors = ["longjin <[email protected]>"]

# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
description = "一个基于XArray的ID分配器"

[dependencies]
kdepends = { path = "../kdepends" }
3 changes: 3 additions & 0 deletions kernel/crates/ida/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# IDA

一个基于XArray的ID分配器
210 changes: 210 additions & 0 deletions kernel/crates/ida/src/lib.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,210 @@
#![no_std]
#![feature(core_intrinsics)]
#![allow(internal_features)]
#![allow(clippy::needless_return)]

#[cfg(test)]
#[macro_use]
extern crate std;

use core::cmp::min;
use core::intrinsics::unlikely;
use core::marker::PhantomData;
use core::ops::Deref;

struct EmptyIdaItemRef<'a> {
_marker: PhantomData<&'a EmptyIdaItem>,
}

impl<'a> Deref for EmptyIdaItemRef<'a> {
type Target = EmptyIdaItem;

fn deref(&self) -> &Self::Target {
&EmptyIdaItem
}
}

struct EmptyIdaItem;

unsafe impl kdepends::xarray::ItemEntry for EmptyIdaItem {
type Ref<'a> = EmptyIdaItemRef<'a> where Self: 'a;

fn into_raw(self) -> *const () {
core::ptr::null()
}

unsafe fn from_raw(_raw: *const ()) -> Self {
EmptyIdaItem
}

unsafe fn raw_as_ref<'a>(_raw: *const ()) -> Self::Ref<'a> {
EmptyIdaItemRef {
_marker: PhantomData,
}
}
}
/// id分配器
pub struct IdAllocator {
current_id: usize,
min_id: usize,
max_id: usize,
used: usize,
xarray: kdepends::xarray::XArray<EmptyIdaItem>,
}

impl IdAllocator {
/// 创建一个新的id分配器
pub const fn new(initial_id: usize, max_id: usize) -> Option<Self> {
if initial_id >= max_id {
return None;
}
Some(Self {
current_id: initial_id,
min_id: initial_id,
max_id,
used: 0,
xarray: kdepends::xarray::XArray::new(),
})
}

/// 可用的id数量
#[inline]
pub fn available(&self) -> usize {
self.max_id - self.min_id - self.used
}

/// 分配一个新的id
///
/// ## 返回
///
/// 如果分配成功,返回Some(id),否则返回None
pub fn alloc(&mut self) -> Option<usize> {
if unlikely(self.available() == 0) {
return None;
}

if let Some(try1) = self.do_find_first_free_index(self.current_id, self.max_id) {
self.current_id = try1;
self.xarray.store(try1 as u64, EmptyIdaItem);
self.used += 1;
return Some(try1);
}

// 从头开始找
if let Some(try2) =
self.do_find_first_free_index(self.min_id, min(self.current_id, self.max_id))
{
self.current_id = try2;
self.xarray.store(try2 as u64, EmptyIdaItem);
self.used += 1;
return Some(try2);
}
return None;
}

/// 检查id是否存在
///
/// ## 参数
///
/// - `id`:要检查的id
///
/// ## 返回
///
/// 如果id存在,返回true,否则返回false
pub fn exists(&self, id: usize) -> bool {
if id < self.min_id || id >= self.max_id {
return false;
}
self.xarray.load(id as u64).is_some()
}

fn do_find_first_free_index(&self, start_id: usize, end: usize) -> Option<usize> {
(start_id..end).find(|&i| !self.exists(i))
}

/// 释放一个id
///
/// ## 参数
///
/// - `id`:要释放的id
pub fn free(&mut self, id: usize) {
if id < self.min_id || id >= self.max_id {
return;
}
if self.xarray.remove(id as u64).is_some() {
self.used -= 1;
}
}

/// 返回已经使用的id数量
pub fn used(&self) -> usize {
self.used
}
}

impl core::fmt::Debug for IdAllocator {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
f.debug_struct("IdAllocator")
.field("current_id", &self.current_id)
.field("min_id", &self.min_id)
.field("max_id", &self.max_id)
.field("used", &self.used)
.field("xarray", &"xarray<()>")
.finish()
}
}

#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_new_fail() {
assert_eq!(IdAllocator::new(10, 10).is_none(), true);
assert_eq!(IdAllocator::new(11, 10).is_none(), true);
}
#[test]
fn test_new_success() {
assert_eq!(IdAllocator::new(9, 10).is_some(), true);
assert_eq!(IdAllocator::new(0, 10).is_some(), true);
}

#[test]
fn test_id_allocator() {
let mut ida = IdAllocator::new(0, 10).unwrap();
assert_eq!(ida.alloc(), Some(0));
assert_eq!(ida.alloc(), Some(1));
assert_eq!(ida.alloc(), Some(2));
assert_eq!(ida.alloc(), Some(3));
assert_eq!(ida.alloc(), Some(4));
assert_eq!(ida.alloc(), Some(5));
assert_eq!(ida.alloc(), Some(6));
assert_eq!(ida.alloc(), Some(7));
assert_eq!(ida.alloc(), Some(8));
assert_eq!(ida.alloc(), Some(9));
assert_eq!(ida.alloc(), None);

for i in 0..10 {
assert_eq!(ida.exists(i), true);
}

ida.free(5);

for i in 0..10 {
if i == 5 {
assert_eq!(ida.exists(i), false);
} else {
assert_eq!(ida.exists(i), true);
}
}
assert_eq!(ida.used(), 9);
assert_eq!(ida.alloc(), Some(5));
assert_eq!(ida.alloc(), None);

assert_eq!(ida.used(), 10);
for i in 0..10 {
ida.free(i);
}

assert_eq!(ida.used(), 0);
}
}
5 changes: 3 additions & 2 deletions kernel/crates/kdepends/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,10 @@ description = "需要导出的依赖项(为保持内核依赖版本与调试
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

[dependencies]
ringbuffer = "0.15.0"
memoffset = "0.9.0"
crc = { path = "../crc" }
memoffset = "0.9.0"
ringbuffer = "0.15.0"
xarray = { git = "https://git.mirrors.dragonos.org.cn/DragonOS-Community/xarray", rev = "de93b57c34", features = ["slab-friendly"] }

# 一个无锁MPSC队列
[dependencies.thingbuf]
Expand Down
1 change: 1 addition & 0 deletions kernel/crates/kdepends/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,3 +7,4 @@ pub extern crate memoffset;
pub extern crate ringbuffer;

pub extern crate crc;
pub extern crate xarray;
7 changes: 4 additions & 3 deletions kernel/src/debug/klog/mm.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ use core::sync::atomic::{compiler_fence, Ordering};

use klog_types::{AllocatorLog, AllocatorLogType, LogSource, MMLogChannel};

use crate::{arch::CurrentTimeArch, process::Pid, time::TimeArch};
use crate::{arch::CurrentTimeArch, libs::spinlock::SpinLock, process::Pid, time::TimeArch};

/// 全局的内存分配器日志通道
///
Expand All @@ -16,7 +16,8 @@ static __MM_ALLOCATOR_LOG_CHANNEL: MMLogChannel<{ MMDebugLogManager::MAX_ALLOC_L
/// 全局的内存分配器日志id分配器
///
/// id从1开始, 因为0是无效的id
static __MM_DEBUG_LOG_IDA: ida::IdAllocator = ida::IdAllocator::new(1, usize::MAX);
static __MM_DEBUG_LOG_IDA: SpinLock<ida::IdAllocator> =
SpinLock::new(ida::IdAllocator::new(1, usize::MAX).unwrap());

/// 记录内存分配器的日志
///
Expand Down Expand Up @@ -50,7 +51,7 @@ impl MMDebugLogManager {
/// - `pid`:日志来源的pid
#[allow(dead_code)]
pub fn log(log_type: AllocatorLogType, source: LogSource, pid: Option<Pid>) {
let id = __MM_DEBUG_LOG_IDA.alloc().unwrap();
let id = __MM_DEBUG_LOG_IDA.lock_irqsave().alloc().unwrap();
let log = AllocatorLog::new(
id as u64,
log_type,
Expand Down
10 changes: 7 additions & 3 deletions kernel/src/driver/base/platform/platform_device.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,8 @@ use system_error::SystemError;
use super::{super::device::DeviceState, platform_bus, platform_bus_device, CompatibleTable};

/// 平台设备id分配器
static PLATFORM_DEVID_IDA: IdAllocator = IdAllocator::new(0, i32::MAX as usize);
static PLATFORM_DEVID_IDA: SpinLock<IdAllocator> =
SpinLock::new(IdAllocator::new(0, i32::MAX as usize).unwrap());

#[inline(always)]
pub fn platform_device_manager() -> &'static PlatformDeviceManager {
Expand Down Expand Up @@ -93,7 +94,10 @@ impl PlatformDeviceManager {
pdev.set_name(pdev.pdev_name().to_string());
}
PLATFORM_DEVID_AUTO => {
let id = PLATFORM_DEVID_IDA.alloc().ok_or(SystemError::EOVERFLOW)?;
let id = PLATFORM_DEVID_IDA
.lock()
.alloc()
.ok_or(SystemError::EOVERFLOW)?;
pdev.set_pdev_id(id as i32);
pdev.set_pdev_id_auto(true);
pdev.set_name(format!("{}.{}.auto", pdev.pdev_name(), pdev.pdev_id().0));
Expand All @@ -112,7 +116,7 @@ impl PlatformDeviceManager {
// failed
let pdevid = pdev.pdev_id();
if pdevid.1 {
PLATFORM_DEVID_IDA.free(pdevid.0 as usize);
PLATFORM_DEVID_IDA.lock().free(pdevid.0 as usize);
pdev.set_pdev_id(PLATFORM_DEVID_AUTO);
}

Expand Down
7 changes: 4 additions & 3 deletions kernel/src/driver/rtc/sysfs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,8 @@ use super::{
GeneralRtcPriority, RtcClassOps, RtcDevice,
};

static RTC_GENERAL_DEVICE_IDA: IdAllocator = IdAllocator::new(0, usize::MAX);
static RTC_GENERAL_DEVICE_IDA: SpinLock<IdAllocator> =
SpinLock::new(IdAllocator::new(0, usize::MAX).unwrap());

pub(super) const RTC_HCTOSYS_DEVICE: &str = "rtc0";

Expand Down Expand Up @@ -63,7 +64,7 @@ impl RtcGeneralDevice {
///
/// 注意,由于还需要进行其他的初始化操作,因此这个函数并不是公开的构造函数。
fn new(priority: GeneralRtcPriority) -> Arc<Self> {
let id = RTC_GENERAL_DEVICE_IDA.alloc().unwrap();
let id = RTC_GENERAL_DEVICE_IDA.lock().alloc().unwrap();
let name = format!("rtc{}", id);
Arc::new(Self {
name,
Expand Down Expand Up @@ -106,7 +107,7 @@ impl RtcGeneralDevice {

impl Drop for RtcGeneralDevice {
fn drop(&mut self) {
RTC_GENERAL_DEVICE_IDA.free(self.id);
RTC_GENERAL_DEVICE_IDA.lock().free(self.id);
}
}

Expand Down
Loading

0 comments on commit 0eb0731

Please sign in to comment.