浏览代码

feat(ida): IDA内部改为使用XArray实现 (#934)

目前可以记录哪些ID已经分配,支持了ID释放的功能.

Signed-off-by: longjin <[email protected]>
LoGin 5 月之前
父节点
当前提交
013ffb708f

+ 11 - 0
docs/kernel/libs/id-allocation.md

@@ -0,0 +1,11 @@
+# ID分配
+
+:::{note}
+本文作者:龙进 <[email protected]>
+
+2024年9月25日
+:::
+
+内核提供了一个名为`IdAllocator`的ID分配器,位于`kernel/crates/ida`中。
+
+它能够分配、释放ID。默认它会自增分配,假如ID大于设定的最大值,它会从最小值开始寻找空闲ID。如果没有空闲的ID,则会分配失败。

+ 1 - 0
docs/kernel/libs/index.rst

@@ -10,4 +10,5 @@
    lib_ui/scm
    lib_ui/textui
    unified-init
+   id-allocation
 

+ 1 - 2
kernel/Cargo.toml

@@ -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" }
@@ -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时,使用下面的依赖

+ 2 - 2
kernel/src/libs/ida/Cargo.toml → kernel/crates/ida/Cargo.toml

@@ -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 - 0
kernel/crates/ida/README.md

@@ -0,0 +1,3 @@
+# IDA
+
+一个基于XArray的ID分配器

+ 210 - 0
kernel/crates/ida/src/lib.rs

@@ -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);
+    }
+}

+ 3 - 2
kernel/crates/kdepends/Cargo.toml

@@ -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]

+ 1 - 0
kernel/crates/kdepends/src/lib.rs

@@ -7,3 +7,4 @@ pub extern crate memoffset;
 pub extern crate ringbuffer;
 
 pub extern crate crc;
+pub extern crate xarray;

+ 4 - 3
kernel/src/debug/klog/mm.rs

@@ -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};
 
 /// 全局的内存分配器日志通道
 ///
@@ -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());
 
 /// 记录内存分配器的日志
 ///
@@ -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,

+ 7 - 3
kernel/src/driver/base/platform/platform_device.rs

@@ -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 {
@@ -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));
@@ -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);
             }
 

+ 4 - 3
kernel/src/driver/rtc/sysfs.rs

@@ -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";
 
@@ -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,
@@ -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);
     }
 }
 

+ 5 - 4
kernel/src/driver/virtio/sysfs.rs

@@ -30,6 +30,7 @@ use crate::{
         vfs::syscall::ModeType,
     },
     init::initcall::INITCALL_CORE,
+    libs::spinlock::SpinLock,
 };
 
 use super::{VirtIODevice, VirtIODeviceIndex, VirtIODriver, VIRTIO_DEV_ANY_ID};
@@ -255,7 +256,7 @@ pub struct VirtIODeviceIndexManager {
     // ID分配器
     ///
     /// ID分配器用于分配唯一的索引给VirtIO设备。
-    ida: IdAllocator,
+    ida: SpinLock<IdAllocator>,
 }
 
 // VirtIO设备索引管理器的新建实例
@@ -265,7 +266,7 @@ impl VirtIODeviceIndexManager {
     /// 创建一个新的VirtIO设备索引管理器实例,初始时分配器从0开始,直到最大usize值。
     const fn new() -> Self {
         Self {
-            ida: IdAllocator::new(0, usize::MAX),
+            ida: SpinLock::new(IdAllocator::new(0, usize::MAX).unwrap()),
         }
     }
 
@@ -273,7 +274,7 @@ impl VirtIODeviceIndexManager {
     ///
     /// 分配一个唯一的索引给VirtIO设备。
     pub fn alloc(&self) -> VirtIODeviceIndex {
-        VirtIODeviceIndex(self.ida.alloc().unwrap())
+        VirtIODeviceIndex(self.ida.lock().alloc().unwrap())
     }
 
     // 释放一个VirtIO设备索引
@@ -281,7 +282,7 @@ impl VirtIODeviceIndexManager {
     /// 释放之前分配的VirtIO设备索引,使其可以被重新使用。
     #[allow(dead_code)]
     pub fn free(&self, index: VirtIODeviceIndex) {
-        self.ida.free(index.0);
+        self.ida.lock().free(index.0);
     }
 }
 

+ 3 - 3
kernel/src/filesystem/devpts/mod.rs

@@ -41,7 +41,7 @@ const PTY_NR_LIMIT: usize = 4096;
 pub struct DevPtsFs {
     /// 根节点
     root_inode: Arc<LockedDevPtsFSInode>,
-    pts_ida: IdAllocator,
+    pts_ida: SpinLock<IdAllocator>,
     pts_count: AtomicU32,
 }
 
@@ -50,7 +50,7 @@ impl DevPtsFs {
         let root_inode = Arc::new(LockedDevPtsFSInode::new());
         let ret = Arc::new(Self {
             root_inode,
-            pts_ida: IdAllocator::new(1, NR_UNIX98_PTY_MAX as usize),
+            pts_ida: SpinLock::new(IdAllocator::new(1, NR_UNIX98_PTY_MAX as usize).unwrap()),
             pts_count: AtomicU32::new(0),
         });
 
@@ -60,7 +60,7 @@ impl DevPtsFs {
     }
 
     pub fn alloc_index(&self) -> Result<usize, SystemError> {
-        self.pts_ida.alloc().ok_or(SystemError::ENOSPC)
+        self.pts_ida.lock().alloc().ok_or(SystemError::ENOSPC)
     }
 }
 

+ 6 - 2
kernel/src/filesystem/eventfd.rs

@@ -15,7 +15,8 @@ use core::any::Any;
 use ida::IdAllocator;
 use system_error::SystemError;
 
-static EVENTFD_ID_ALLOCATOR: IdAllocator = IdAllocator::new(0, u32::MAX as usize);
+static EVENTFD_ID_ALLOCATOR: SpinLock<IdAllocator> =
+    SpinLock::new(IdAllocator::new(0, u32::MAX as usize).unwrap());
 
 bitflags! {
     pub struct EventFdFlags: u32{
@@ -251,7 +252,10 @@ impl Syscall {
     /// See: https://man7.org/linux/man-pages/man2/eventfd2.2.html
     pub fn sys_eventfd(init_val: u32, flags: u32) -> Result<usize, SystemError> {
         let flags = EventFdFlags::from_bits(flags).ok_or(SystemError::EINVAL)?;
-        let id = EVENTFD_ID_ALLOCATOR.alloc().ok_or(SystemError::ENOMEM)? as u32;
+        let id = EVENTFD_ID_ALLOCATOR
+            .lock()
+            .alloc()
+            .ok_or(SystemError::ENOMEM)? as u32;
         let eventfd = EventFd::new(init_val as u64, flags, id);
         let inode = Arc::new(EventFdInode::new(eventfd));
         let filemode = if flags.contains(EventFdFlags::EFD_CLOEXEC) {

+ 1 - 1
kernel/src/filesystem/vfs/file.rs

@@ -5,9 +5,9 @@ use alloc::{
     sync::{Arc, Weak},
     vec::Vec,
 };
+use kdepends::xarray::XArray;
 use log::error;
 use system_error::SystemError;
-use xarray::XArray;
 
 use super::{Dirent, FileType, IndexNode, InodeId, Metadata, SpecialNodeData};
 use crate::filesystem::eventfd::EventFdInode;

+ 1 - 1
kernel/src/ipc/shm.rs

@@ -125,7 +125,7 @@ pub struct ShmManager {
 impl ShmManager {
     pub fn new() -> Self {
         ShmManager {
-            id_allocator: IdAllocator::new(0, usize::MAX - 1),
+            id_allocator: IdAllocator::new(0, usize::MAX - 1).unwrap(),
             id2shm: HashMap::new(),
             key2id: HashMap::new(),
         }

+ 1 - 0
kernel/src/lib.rs

@@ -6,6 +6,7 @@
 #![feature(concat_idents)]
 #![feature(const_for)]
 #![feature(const_mut_refs)]
+#![feature(const_option)]
 #![feature(const_trait_impl)]
 #![feature(const_refs_to_cell)]
 #![feature(core_intrinsics)]

+ 0 - 52
kernel/src/libs/ida/src/lib.rs

@@ -1,52 +0,0 @@
-#![no_std]
-#![feature(core_intrinsics)]
-#![allow(internal_features)]
-#![allow(clippy::needless_return)]
-
-use core::intrinsics::unlikely;
-use core::sync::atomic::{AtomicBool, AtomicUsize, Ordering};
-
-/// id分配器
-///
-/// TODO: 当前只是为了简单实现功能,将来这里应使用类似linux的ida的方式去实现
-#[derive(Debug)]
-pub struct IdAllocator {
-    current_id: AtomicUsize,
-    max_id: usize,
-    dead: AtomicBool,
-}
-
-impl IdAllocator {
-    /// 创建一个新的id分配器
-    pub const fn new(initial_id: usize, max_id: usize) -> Self {
-        Self {
-            current_id: AtomicUsize::new(initial_id),
-            max_id,
-            dead: AtomicBool::new(false),
-        }
-    }
-
-    /// 分配一个新的id
-    ///
-    /// ## 返回
-    ///
-    /// 如果分配成功,返回Some(id),否则返回None
-    pub fn alloc(&self) -> Option<usize> {
-        if unlikely(self.dead.load(Ordering::SeqCst)) {
-            return None;
-        }
-
-        let ret = self.current_id.fetch_add(1, Ordering::SeqCst);
-        // 如果id溢出,panic
-        if ret == self.max_id {
-            self.dead.store(true, Ordering::SeqCst);
-            return None;
-        }
-
-        return Some(ret);
-    }
-
-    pub fn free(&self, _id: usize) {
-        // todo: free
-    }
-}

+ 4 - 3
kernel/src/mm/ucontext.rs

@@ -54,7 +54,8 @@ use super::{
 pub const DEFAULT_MMAP_MIN_ADDR: usize = 65536;
 
 /// LockedVMA的id分配器
-static LOCKEDVMA_ID_ALLOCATOR: IdAllocator = IdAllocator::new(0, usize::MAX);
+static LOCKEDVMA_ID_ALLOCATOR: SpinLock<IdAllocator> =
+    SpinLock::new(IdAllocator::new(0, usize::MAX).unwrap());
 
 #[derive(Debug)]
 pub struct AddressSpace {
@@ -1097,7 +1098,7 @@ impl Eq for LockedVMA {}
 impl LockedVMA {
     pub fn new(vma: VMA) -> Arc<Self> {
         let r = Arc::new(Self {
-            id: LOCKEDVMA_ID_ALLOCATOR.alloc().unwrap(),
+            id: LOCKEDVMA_ID_ALLOCATOR.lock().alloc().unwrap(),
             vma: SpinLock::new(vma),
         });
         r.vma.lock_irqsave().self_ref = Arc::downgrade(&r);
@@ -1316,7 +1317,7 @@ impl LockedVMA {
 
 impl Drop for LockedVMA {
     fn drop(&mut self) {
-        LOCKEDVMA_ID_ALLOCATOR.free(self.id);
+        LOCKEDVMA_ID_ALLOCATOR.lock().free(self.id);
     }
 }
 

+ 5 - 2
kernel/src/net/socket/handle.rs

@@ -1,6 +1,8 @@
 use ida::IdAllocator;
 use smoltcp::iface::SocketHandle;
 
+use crate::libs::spinlock::SpinLock;
+
 int_like!(KernelHandle, usize);
 
 /// # socket的句柄管理组件
@@ -12,7 +14,8 @@ pub enum GlobalSocketHandle {
     Kernel(KernelHandle),
 }
 
-static KERNEL_HANDLE_IDA: IdAllocator = IdAllocator::new(0, usize::MAX);
+static KERNEL_HANDLE_IDA: SpinLock<IdAllocator> =
+    SpinLock::new(IdAllocator::new(0, usize::MAX).unwrap());
 
 impl GlobalSocketHandle {
     pub fn new_smoltcp_handle(handle: SocketHandle) -> Self {
@@ -20,7 +23,7 @@ impl GlobalSocketHandle {
     }
 
     pub fn new_kernel_handle() -> Self {
-        return Self::Kernel(KernelHandle::new(KERNEL_HANDLE_IDA.alloc().unwrap()));
+        return Self::Kernel(KernelHandle::new(KERNEL_HANDLE_IDA.lock().alloc().unwrap()));
     }
 
     pub fn smoltcp_handle(&self) -> Option<SocketHandle> {