Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
36 commits
Select commit Hold shift + click to select a range
5cbe962
完成loop_device的注册,能在/dev下看见8个loopdevice
Vitus213 Aug 19, 2025
be65aaa
fmt & 删除loopdevice中的devid字段
Vitus213 Aug 20, 2025
c9157f8
添加了driver和control device和manager,将注册逻辑移动至driver中
Vitus213 Aug 20, 2025
0867f83
- 将具体注册逻辑移动到loopdriver中,由loopmanager封装init函数,管理loopdevice信息
Vitus213 Sep 22, 2025
d8eb2e7
注册loop-control设备
Vitus213 Sep 26, 2025
521d819
复用linux中关于loop的ioctl魔数
Vitus213 Sep 29, 2025
327d166
- 往loop_control_device中加入loopmanager的arc实例
Vitus213 Oct 9, 2025
e7363e3
添加不对回环路由代理
Vitus213 Oct 15, 2025
a1f2a30
add test
Vitus213 Oct 30, 2025
9f5ebdf
Merge remote-tracking branch 'upstream/master' into feat/loop-device
Vitus213 Oct 31, 2025
89c7fb1
merge 主线,调整版本号和工具链
Vitus213 Oct 31, 2025
9ef8d3c
fmt
Vitus213 Nov 1, 2025
786c435
fix : function loop_add()
Vitus213 Nov 2, 2025
29c7679
- 增加read_async,write_async
Vitus213 Nov 2, 2025
e87dcd8
feat:增添删除设备逻辑和测试
Vitus213 Nov 2, 2025
0b43777
fmt
Vitus213 Nov 2, 2025
bd115d9
- feat: add loop_set_status and loop_get_status
Vitus213 Nov 3, 2025
9f9f246
Merge remote-tracking branch 'upstream/master' into feat/loop-device
Vitus213 Nov 3, 2025
293f529
- add ioctl change_fd and set_capacity
Vitus213 Nov 8, 2025
e4bde6b
Merge remote-tracking branch 'origin/master' into feat/loop-device
Vitus213 Nov 9, 2025
2715d3a
- 修改为枚举
Vitus213 Nov 11, 2025
6df9ba3
- 修改major管理
Vitus213 Nov 11, 2025
3431ff1
- add idallocator,把和id和minor解耦,减少遍历的次数
Vitus213 Nov 11, 2025
332dd9c
- fix docs
Vitus213 Nov 11, 2025
d224fbf
Merge branch 'master' into feat/loop-device
Vitus213 Nov 11, 2025
3386be3
Merge branch 'master' into feat/loop-device
Vitus213 Nov 11, 2025
0c5d02c
Merge remote-tracking branch 'refs/remotes/origin/feat/loop-device' i…
Vitus213 Nov 12, 2025
eda602a
修正clippy
Vitus213 Nov 12, 2025
87ba21d
copilot审查修正
Vitus213 Nov 12, 2025
2360c1b
- 改动loop_device的位置
Vitus213 Nov 12, 2025
2ebf533
- 添加回调函数,阻塞io,保证io结束后再回调释放资源
Vitus213 Nov 27, 2025
912bc23
- 添加回调函数,阻塞io,保证io结束后再回调释放资源
Vitus213 Nov 27, 2025
5ef7728
Merge remote-tracking branch 'refs/remotes/origin/feat/loop-device' i…
Vitus213 Nov 27, 2025
95c0444
Merge branch 'master' into feat/loop-device
Vitus213 Nov 27, 2025
126bc5a
- refactor: 调整loop_device位置
Vitus213 Nov 27, 2025
88877ca
Merge remote-tracking branch 'refs/remotes/origin/feat/loop-device' i…
Vitus213 Nov 27, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions docs/kernel/device/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -6,3 +6,4 @@ Device
:maxdepth: 1

tty
loop_device
127 changes: 127 additions & 0 deletions docs/kernel/device/loop_device.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,127 @@
# Loop Device 架构设计

本文档阐述 DragonOS 中 loop 设备子系统的架构设计思路,用于指导开发和后续演进。

## 问题背景

在操作系统开发中,我们经常面临这样的需求:
- 如何将一个镜像文件当作块设备使用?
- 如何在不重启系统的情况下动态创建/删除虚拟块设备?

loop 设备正是解决这些问题的关键组件。

## 系统架构定位

loop 设备在 DragonOS 架构中扮演着"虚拟化桥梁"的角色:

```
用户态应用
loop-control 接口 (字符设备)
LoopManager (设备生命周期管理)
LoopDevice[] (虚拟块设备数组)
块设备层 ←→ 后端文件系统
```

这种分层设计的核心思想是:**将控制平面与数据平面分离**。

## 核心设计哲学

### 1. 状态驱动的设备管理

我们仿照linux设计引入状态机来管理设备生命周期

```
Unbound → Bound → Rundown → Deleting
↓ ↓ ↓
Deleting Unbound Deleting
```

**设计考量**:
- 防止非法状态转换(如直接在 Bound 状态删除设备)
- 提供清晰的设备生命周期语义
- 为未来的扩展(如设备热插拔)奠定基础

### 2. 双重接口策略

我们的设计刻意区分了两种接口:

**字符控制接口** (`/dev/loop-control`):
- 负责设备的生命周期管理
- 提供用户友好的设备分配/回收机制
- 与 Linux 标准接口保持兼容

**块设备接口** (`/dev/loopX`):
- 专注于数据读写功能
- 提供标准的块设备语义
- 支持偏移、大小限制等高级功能

**设计价值**:这种分离使得控制逻辑与数据路径互不干扰,提高了系统的可维护性。

### 3. 安全性优先

在与用户态交互时,我们采用了多重安全检查:

- **参数边界检查**:所有偏移和大小都必须 LBA 对齐
- **内存安全**:使用 `UserBufferReader/Writer` 进行用户态数据拷贝
- **权限验证**:只读设备拒绝写入操作
- **状态验证**:每个操作前都检查当前设备状态是否允许

## 模块协作架构

### LoopManager 的定位
LoopManager 不是简单的设备数组管理器,而是整个子系统的"调度中心":

- **设备分配策略**:采用"就近分配"原则,优先复用空闲设备
- **资源池管理**:预注册 8 个设备,避免运行时分配开销
- **并发安全**:所有设备操作都在锁保护下进行

### LoopDevice 的抽象设计
LoopDevice 的核心抽象是"**后端文件的块设备视图**":

```
用户视角 内部实现
/dev/loop0 ←→ 文件偏移 + 大小限制
块0-100 文件偏移0-51200
块101-200 文件偏移51200-102400
```

这种设计允许用户将文件的任意部分映射为块设备,为容器等应用场景提供了极大的灵活性。

## 关键设计

### 为什么选择 256 个设备上限?
- 足够满足大多数应用场景需求
- 避免无限制增长导致的资源耗尽
- 与 Linux 系统默认上限保持一致,保证兼容性

### 为什么预注册 8 个设备?
- 覆盖常见的测试场景(通常不超过 4-5 个)
- 减少首次使用的等待时间
- 提供一个合理的初始工作集

### 为什么使用 SpinLock 而不是其他锁?
- loop 设备操作大多是短时操作
- 避免复杂的锁层级和死锁问题
- 简化实现,提高性能

## 兼容性考量

我们的设计在很大程度上参考了 Linux loop 驱动的接口,这是有意的选择:

1. **用户态软件兼容**:现有的 loop 工具无需修改即可使用
2. **API 契约一致性**:避免因接口差异导致的潜在问题
3. **社区知识复用**:开发者可以复用现有的 loop 设备知识

## 总结

DragonOS 的 loop 设备设计遵循以下核心原则:
1. **架构清晰**:控制平面与数据平面分离
2. **状态安全**:基于状态机的设备生命周期管理
3. **接口兼容**:与 Linux 标准接口保持兼容
4. **扩展友好**:为未来功能预留架构空间
5. **测试完备**:通过多层级测试保证质量

61 changes: 50 additions & 11 deletions kernel/src/driver/base/block/gendisk/mod.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
use core::{
convert::TryFrom,
ops::{Deref, DerefMut},
sync::atomic::{AtomicU32, Ordering},
};
Expand All @@ -10,17 +11,17 @@ use alloc::{
use hashbrown::HashMap;
use system_error::SystemError;

use super::block_device::{BlockDevice, BlockId, GeneralBlockRange, LBA_SIZE};
use crate::{
driver::base::device::device_number::DeviceNumber,
driver::block::loop_device::LoopDevice,
filesystem::{
devfs::{DevFS, DeviceINode, LockedDevFSInode},
vfs::{syscall::ModeType, utils::DName, IndexNode, Metadata},
},
libs::{rwlock::RwLock, spinlock::SpinLockGuard},
};

use super::block_device::{BlockDevice, BlockId, GeneralBlockRange, LBA_SIZE};

const MINORS_PER_DISK: u32 = 256;

#[derive(Debug)]
Expand Down Expand Up @@ -221,30 +222,54 @@ impl IndexNode for GenDisk {

fn read_at(
&self,
_offset: usize,
_len: usize,
_buf: &mut [u8],
offset: usize,
len: usize,
buf: &mut [u8],
_data: SpinLockGuard<crate::filesystem::vfs::FilePrivateData>,
) -> Result<usize, SystemError> {
Err(SystemError::EPERM)
if len == 0 {
return Ok(0);
}
if len > buf.len() {
return Err(SystemError::ENOBUFS);
}
self.read_at_bytes(&mut buf[..len], offset)
}

fn write_at(
&self,
_offset: usize,
_len: usize,
_buf: &[u8],
offset: usize,
len: usize,
buf: &[u8],
_data: SpinLockGuard<crate::filesystem::vfs::FilePrivateData>,
) -> Result<usize, SystemError> {
Err(SystemError::EPERM)
if len == 0 {
return Ok(0);
}
if len > buf.len() {
return Err(SystemError::E2BIG);
}
self.write_at_bytes(&buf[..len], offset)
}

fn list(&self) -> Result<alloc::vec::Vec<alloc::string::String>, system_error::SystemError> {
Err(SystemError::ENOSYS)
}

fn metadata(&self) -> Result<crate::filesystem::vfs::Metadata, SystemError> {
Ok(self.metadata.clone())
let mut meta = self.metadata.clone();
let bdev = self.block_device();
let range = bdev.disk_range();
let blocks = range.lba_end.saturating_sub(range.lba_start);
let size_in_bytes = blocks.saturating_mul(LBA_SIZE);

meta.size = match i64::try_from(size_in_bytes) {
Ok(val) => val,
Err(_) => i64::MAX,
};
meta.blocks = blocks;
meta.blk_size = LBA_SIZE;
Ok(meta)
}

fn dname(&self) -> Result<DName, SystemError> {
Expand Down Expand Up @@ -273,6 +298,20 @@ impl IndexNode for GenDisk {
) -> Result<(), SystemError> {
Ok(())
}

fn ioctl(
&self,
cmd: u32,
data: usize,
private_data: &crate::filesystem::vfs::FilePrivateData,
) -> Result<usize, SystemError> {
let bdev = self.block_device();
if let Some(loop_dev) = BlockDevice::as_any_ref(&*bdev).downcast_ref::<LoopDevice>() {
loop_dev.ioctl(cmd, data, private_data)
} else {
Err(SystemError::ENOSYS)
}
}
}

impl DeviceINode for GenDisk {
Expand Down
25 changes: 18 additions & 7 deletions kernel/src/driver/base/block/manager.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use core::{fmt::Formatter, sync::atomic::AtomicU32};

use alloc::sync::Arc;
use alloc::{sync::Arc, vec::Vec};
use hashbrown::HashMap;
use system_error::SystemError;
use unified_init::macros::unified_init;
Expand All @@ -11,7 +11,7 @@ use crate::{
device::{device_number::Major, DevName},
},
filesystem::{
devfs::devfs_register,
devfs::{devfs_register, devfs_unregister},
mbr::MbrDiskPartionTable,
vfs::{utils::DName, IndexNode},
},
Expand Down Expand Up @@ -167,13 +167,24 @@ impl BlockDevManager {

/// 卸载磁盘设备
#[allow(dead_code)]
pub fn unregister(&self, dev: &Arc<dyn BlockDevice>) {
pub fn unregister(&self, dev: &Arc<dyn BlockDevice>) -> Result<(), SystemError> {
let mut inner = self.inner();
inner.disks.remove(dev.dev_name());
// todo: 这里应该callback一下磁盘设备,但是现在还没实现热插拔,所以暂时没做这里
todo!("BlockDevManager: unregister disk")
if inner.disks.remove(dev.dev_name()).is_none() {
return Err(SystemError::ENOENT);
}
let blk_meta = dev.blkdev_meta();
let gendisks = {
let mut meta_inner = blk_meta.inner();
let disks: Vec<Arc<GenDisk>> = meta_inner.gendisks.values().cloned().collect();
meta_inner.gendisks.clear();
disks
};
for gendisk in gendisks {
let dname = gendisk.dname()?;
devfs_unregister(dname.as_ref(), gendisk)?;
}
Ok(())
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

image 这里要让真实的磁盘设备之类的,执行资源释放的操作,而不是简单的在这里删除元数据的记录。 不然的话会有资源泄漏。这里需要一个回调机制。

}

/// 通过路径查找gendisk
///
/// # 参数
Expand Down
1 change: 0 additions & 1 deletion kernel/src/driver/base/block/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ pub mod block_device;
pub mod disk_info;
pub mod gendisk;
pub mod manager;

#[derive(Debug)]
#[allow(dead_code)]
pub enum SeekFrom {
Expand Down
2 changes: 2 additions & 0 deletions kernel/src/driver/base/device/device_number.rs
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,8 @@ impl Major {
pub const fn data(&self) -> u32 {
self.0
}
pub const LOOP_MAJOR: Self = Self::new(7);
pub const LOOP_CONTROL_MAJOR: Self = Self::new(10);
}

impl Hash for Major {
Expand Down
Loading