Skip to content

feat(vfs): Implement append lock manager for file operations#1483

Merged
fslongjin merged 2 commits intoDragonOS-Community:masterfrom
fslongjin:fix-concurrency-append-write
Dec 16, 2025
Merged

feat(vfs): Implement append lock manager for file operations#1483
fslongjin merged 2 commits intoDragonOS-Community:masterfrom
fslongjin:fix-concurrency-append-write

Conversation

@fslongjin
Copy link
Member

@fslongjin fslongjin commented Dec 16, 2025

  • Introduced an AppendLockManager to ensure atomicity for append operations across filesystems, preventing data corruption in concurrent write scenarios.
  • Updated file write methods to utilize the new append lock mechanism, ensuring that appending to files respects the latest end-of-file position.
  • Enhanced write_append and pwrite_append methods to support forced append semantics, aligning with Linux behavior.
  • Initialized the append lock manager during VFS initialization to ensure it is ready before any file write operations.

This addition improves the reliability of file operations in a multi-threaded environment, particularly for append scenarios.

Fix: #1467

- Introduced an `AppendLockManager` to ensure atomicity for append operations across filesystems, preventing data corruption in concurrent write scenarios.
- Updated file write methods to utilize the new append lock mechanism, ensuring that appending to files respects the latest end-of-file position.
- Enhanced `write_append` and `pwrite_append` methods to support forced append semantics, aligning with Linux behavior.
- Initialized the append lock manager during VFS initialization to ensure it is ready before any file write operations.

This addition improves the reliability of file operations in a multi-threaded environment, particularly for append scenarios.

Signed-off-by: longjin <longjin@DragonOS.org>
@github-actions github-actions bot added the enhancement New feature or request label Dec 16, 2025
@fslongjin
Copy link
Member Author

@claude review this PR in Chinese

@dragonos-community-ai-pr-reviewer
Copy link

dragonos-community-ai-pr-reviewer bot commented Dec 16, 2025

Claude finished @fslongjin's task in 15m 25s —— View job


Claude 正在审查 PR #1483

我将对这个PR进行代码审查。以下是我的审查计划:

  • 1. 分析PR概要和变更文件
  • 2. 阅读新增的append_lock.rs文件
  • 3. 查看file.rs中的修改
  • 4. 检查其他相关文件的修改
  • 5. 分析实现逻辑和并发安全性
  • 6. 提供综合审查反馈

代码审查报告:PR #1483 - feat(vfs): Implement append lock manager for file operations

概述

本PR实现了追加写操作的原子性保护,通过引入AppendLockManager来防止并发追加写操作导致的数据损坏。这是一个重要的并发安全性改进,特别是在多线程环境下。

主要变更

  1. 新增 append_lock.rs:实现了分片式的追加锁管理器
  2. 修改 file.rs:添加了write_appendpwrite_append方法,并在do_write中集成追加锁
  3. 修改 sys_pwritev2.rs:支持RWF_APPEND标志
  4. 修改 vcore.rs:在VFS初始化时初始化追加锁管理器
  5. 其他文件:小范围的清理和测试更新

设计分析

1. AppendLockManager 设计 (append_lock.rs:24-89)

  • 分片锁设计:使用51个分片减少锁竞争,这是一个合理的优化
  • 两级锁结构
    • 外层:SpinLock保护HashMap访问(短临界区)
    • 内层:Mutex<()>作为每个inode的睡眠锁(可能调度)
  • 清理机制:当Arc引用计数为2时(map引用+本地变量)自动清理,防止内存泄漏

2. 追加写语义实现 (file.rs:754-827)

  • Linux兼容性:正确实现了O_APPENDRWF_APPEND语义
  • 原子性保证:在锁内获取EOF并执行写入,确保"取EOF+写入"的原子性
  • 文件类型检查:只对普通文件应用追加锁,符合预期

优点

  1. 并发安全性:有效防止了多个线程同时追加写导致的数据覆盖问题
  2. 性能优化:分片设计减少了锁竞争,51个分片是一个合理的质数选择
  3. 内存管理:自动清理机制防止了无限制的内存增长
  4. Linux兼容:正确实现了RWF_APPEND语义,与Linux行为一致
  5. 集成完整:在VFS初始化时正确初始化,确保在所有文件操作之前可用

潜在问题和改进建议

1. 死锁风险分析

当前实现看起来没有明显的死锁风险:

  • 锁获取顺序:先获取分片SpinLock(短时间),再获取inode的Mutex
  • 没有交叉锁获取,死锁风险较低

2. 分片数选择依据

const APPEND_LOCK_SHARDS: usize = 51; (append_lock.rs:18)

  • 51是一个质数,有助于哈希分布
  • 但缺少注释说明为什么选择这个特定值
  • 建议:添加注释解释选择依据(如性能测试结果或经验值)

3. 哈希函数安全性 ⚠️

fn shard_index(key: &AppendLockKey) -> usize {
    let mut x = (key.dev_id as u64) ^ ((key.inode_id.data() as u64) << 1);
    x ^= x >> 33;
    x = x.wrapping_mul(0xff51afd7ed558ccd);
    x ^= x >> 33;
    (x as usize) % APPEND_LOCK_SHARDS
}
  • 使用了简单的混合函数,注释提到"不依赖哈希质量"
  • 但需要注意dev_idinode_id的分布特性
  • 建议:考虑使用更稳定的哈希函数如fxhashahash,或者至少添加注释说明当前选择的局限性

4. 错误处理完整性 ⚠️

do_write中,如果with_inode_append_lock内的操作失败:

  • 锁会被正确释放(得益于RAII)
  • 但需要注意metadata()调用可能失败
  • 当前实现在锁内重新获取metadata:let md = self.inode.metadata()?; (file.rs:791)
  • 问题:如果metadata()失败,锁会被释放吗?✅ 会的,因为?操作符会提前返回,_inode_guard会被drop

5. 竞态条件检查

在清理逻辑中:

if let Some(current) = guard.get(&key) {
    if Arc::ptr_eq(current, &lock_arc) && Arc::strong_count(&lock_arc) == 2 {
        guard.remove(&key);
    }
}
  • 使用Arc::ptr_eq确保比较的是同一个Arc实例
  • 检查引用计数为2(map中的引用+本地变量)
  • 注意:这里存在一个微妙的竞态条件窗口,但在map.lock()保护下是安全的

6. 常量命名一致性

APPEND_LOCK_SHARDS使用大写蛇形命名,符合Rust常量命名规范。

7. 文档完整性 📝

  • 主要函数有良好的文档注释
  • 但缺少一些关键设计决策的文档
  • 建议:在APPEND_LOCK_SHARDS常量处添加注释说明选择依据

具体问题发现

1. file.rs:771 - S_APPEND检查逻辑

if inode_flags.contains(InodeFlags::S_APPEND) && !flags.contains(FileFlags::O_APPEND) {
    return Err(SystemError::EPERM);
}
  • 这里检查S_APPEND inode标志但没有O_APPEND文件标志时返回EPERM
  • 但后续的force_append参数(用于RWF_APPEND)可能绕过此检查
  • 分析force_append只影响is_append的计算,不影响S_APPEND检查。这是正确的,因为S_APPEND是inode属性,无论force_append如何都应该拒绝非追加写。

2. sys_write.rs中的清理

PR删除了sys_write.rs中的8行代码,看起来是移除了旧的append锁实现,这是正确的清理。

3. 测试覆盖

whitelist.txt中添加了pwritev2_test,但需要确认是否有针对追加锁的专门测试。

  • 建议:考虑添加并发追加写的单元测试

性能考虑

  1. 锁粒度:每个inode独立的Mutex,粒度合理
  2. 分片数:51个分片,在多数情况下应该能有效减少竞争
  3. 内存开销:每个活跃的追加写inode需要一个Arc<Mutex<()>>,开销很小
  4. 清理开销:每次追加写后都尝试清理,可能增加开销,但防止了内存泄漏

建议的改进

  1. 添加性能测试:验证分片数选择的合理性
  2. 增加文档:解释设计决策和常量选择
  3. 考虑哈希函数:评估当前哈希函数的分布质量
  4. 添加并发测试:测试多个线程同时追加写的正确性

总结

这是一个高质量的实现,解决了并发追加写的原子性问题。设计合理,代码清晰,集成了Linux兼容的语义。主要建议是增加一些文档和考虑哈希函数的选择。

推荐合并,但建议在合并前:

  1. APPEND_LOCK_SHARDS常量添加注释说明
  2. 考虑是否添加简单的并发测试

审查完成

注意:这是一个代码审查反馈,不包含实际代码修改。如需实现建议的改进,请另行提交修改。

@github-actions github-actions bot added the Bug fix A bug is fixed in this pull request label Dec 16, 2025
Signed-off-by: longjin <longjin@DragonOS.org>
@fslongjin fslongjin force-pushed the fix-concurrency-append-write branch from 76ced1f to 578e182 Compare December 16, 2025 15:55
@fslongjin fslongjin merged commit b28f766 into DragonOS-Community:master Dec 16, 2025
12 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Bug fix A bug is fixed in this pull request enhancement New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[BUG REPORT] OpenTest.AppendConcurrentWrite 概率性的失败

1 participant