From f4fc796d65edd2decfb77a58490c2eee1d58406f Mon Sep 17 00:00:00 2001 From: Donjuanplatinum Date: Mon, 20 Jul 2026 07:51:02 +0800 Subject: [PATCH 1/3] fs: align post-write sync with Linux generic_write_sync Make VFS post-write sync opt-in and range-aware. Only inodes that explicitly support Linux generic write sync semantics now perform sync after a positive-length write. Sync O_SYNC/O_DSYNC/S_SYNC writes over the actual write range not all the file , and keep S_SYNC as a datasync trigger when leaving full metadata sync to O_SYNC/__O_SYNC. Enable the behavior for regular files only on real filesystems and for block-device inodes. Signed-off-by: Donjuanplatinum --- kernel/src/driver/base/block/gendisk/mod.rs | 14 ++++- .../driver/block/loop_device/loop_device.rs | 12 ++++ kernel/src/driver/block/pmem/device.rs | 17 +++++- kernel/src/driver/block/virtio_blk.rs | 15 ++++- kernel/src/filesystem/ext4/inode.rs | 4 ++ kernel/src/filesystem/fat/fs.rs | 4 ++ kernel/src/filesystem/fuse/inode/vfs.rs | 4 ++ kernel/src/filesystem/overlayfs/inode.rs | 4 ++ kernel/src/filesystem/ramfs/mod.rs | 4 ++ kernel/src/filesystem/tmpfs/mod.rs | 4 ++ kernel/src/filesystem/vfs/file.rs | 59 +++++++++++-------- kernel/src/filesystem/vfs/mod.rs | 9 +++ kernel/src/filesystem/vfs/mount/mod.rs | 4 ++ 13 files changed, 125 insertions(+), 29 deletions(-) diff --git a/kernel/src/driver/base/block/gendisk/mod.rs b/kernel/src/driver/base/block/gendisk/mod.rs index f5953d55aa..48ea8df414 100644 --- a/kernel/src/driver/base/block/gendisk/mod.rs +++ b/kernel/src/driver/base/block/gendisk/mod.rs @@ -16,7 +16,7 @@ use crate::{ driver::{base::device::device_number::DeviceNumber, block::loop_device::LoopDevice}, filesystem::{ devfs::{DevFS, DeviceINode, LockedDevFSInode}, - vfs::{utils::DName, IndexNode, InodeMode, Metadata}, + vfs::{utils::DName, FilePrivateData, FileType, IndexNode, InodeMode, Metadata}, }, libs::{mutex::MutexGuard, rwlock::RwLock}, }; @@ -219,6 +219,18 @@ impl IndexNode for GenDisk { self } + fn supports_post_write_sync(&self, file_type: FileType) -> bool { + file_type == FileType::BlockDevice + } + + fn sync_file( + &self, + _datasync: bool, + _data: MutexGuard, + ) -> Result<(), SystemError> { + self.sync() + } + fn read_at( &self, offset: usize, diff --git a/kernel/src/driver/block/loop_device/loop_device.rs b/kernel/src/driver/block/loop_device/loop_device.rs index e6048e94ba..3e486f13f9 100644 --- a/kernel/src/driver/block/loop_device/loop_device.rs +++ b/kernel/src/driver/block/loop_device/loop_device.rs @@ -1010,6 +1010,18 @@ impl IndexNode for LoopDevice { self } + fn supports_post_write_sync(&self, file_type: FileType) -> bool { + file_type == FileType::BlockDevice + } + + fn sync_file( + &self, + _datasync: bool, + _data: MutexGuard, + ) -> Result<(), SystemError> { + ::sync(self) + } + fn read_at( &self, offset: usize, diff --git a/kernel/src/driver/block/pmem/device.rs b/kernel/src/driver/block/pmem/device.rs index 2514ea895a..d5959140ea 100644 --- a/kernel/src/driver/block/pmem/device.rs +++ b/kernel/src/driver/block/pmem/device.rs @@ -30,7 +30,10 @@ use crate::{ filesystem::{ devfs::{DevFS, DeviceINode, LockedDevFSInode}, kernfs::KernFSInode, - vfs::{utils::DName, FilePrivateData, IndexNode, InodeFlags, InodeId, InodeMode, Metadata}, + vfs::{ + utils::DName, FilePrivateData, FileType, IndexNode, InodeFlags, InodeId, InodeMode, + Metadata, + }, }, libs::{ align::{page_align_down, page_align_up}, @@ -222,6 +225,18 @@ impl IndexNode for PmemBlockDevice { self } + fn supports_post_write_sync(&self, file_type: FileType) -> bool { + file_type == FileType::BlockDevice + } + + fn sync_file( + &self, + _datasync: bool, + _data: MutexGuard, + ) -> Result<(), SystemError> { + ::sync(self) + } + fn read_at( &self, _offset: usize, diff --git a/kernel/src/driver/block/virtio_blk.rs b/kernel/src/driver/block/virtio_blk.rs index f0925ffffc..45874b5105 100644 --- a/kernel/src/driver/block/virtio_blk.rs +++ b/kernel/src/driver/block/virtio_blk.rs @@ -58,7 +58,7 @@ use crate::{ devfs::{DevFS, DeviceINode, LockedDevFSInode}, kernfs::KernFSInode, mbr::MbrDiskPartionTable, - vfs::{utils::DName, IndexNode, InodeMode, Metadata}, + vfs::{utils::DName, FilePrivateData, FileType, IndexNode, InodeMode, Metadata}, }, init::initcall::INITCALL_POSTCORE, libs::{ @@ -849,6 +849,19 @@ impl IndexNode for VirtIOBlkDevice { fn as_any_ref(&self) -> &dyn core::any::Any { self } + + fn supports_post_write_sync(&self, file_type: FileType) -> bool { + file_type == FileType::BlockDevice + } + + fn sync_file( + &self, + _datasync: bool, + _data: MutexGuard, + ) -> Result<(), SystemError> { + ::sync(self) + } + fn read_at( &self, _offset: usize, diff --git a/kernel/src/filesystem/ext4/inode.rs b/kernel/src/filesystem/ext4/inode.rs index a0f3a62ca9..d6130c726c 100644 --- a/kernel/src/filesystem/ext4/inode.rs +++ b/kernel/src/filesystem/ext4/inode.rs @@ -280,6 +280,10 @@ impl IndexNode for LockedExt4Inode { Some(self.fs()) } + fn supports_post_write_sync(&self, file_type: vfs::FileType) -> bool { + file_type == vfs::FileType::File + } + fn retention_state(&self) -> Option<&InodeRetentionState> { Some(&self.retention) } diff --git a/kernel/src/filesystem/fat/fs.rs b/kernel/src/filesystem/fat/fs.rs index f2c0fc0325..6b8ad93890 100644 --- a/kernel/src/filesystem/fat/fs.rs +++ b/kernel/src/filesystem/fat/fs.rs @@ -1864,6 +1864,10 @@ impl IndexNode for LockedFATInode { Some(self.fs()) } + fn supports_post_write_sync(&self, file_type: FileType) -> bool { + file_type == FileType::File + } + fn mmap(&self, _start: usize, _len: usize, _offset: usize) -> Result<(), SystemError> { Ok(()) } diff --git a/kernel/src/filesystem/fuse/inode/vfs.rs b/kernel/src/filesystem/fuse/inode/vfs.rs index ba01bbf484..a612fd69db 100644 --- a/kernel/src/filesystem/fuse/inode/vfs.rs +++ b/kernel/src/filesystem/fuse/inode/vfs.rs @@ -48,6 +48,10 @@ impl IndexNode for FuseNode { self.try_fs() } + fn supports_post_write_sync(&self, file_type: FileType) -> bool { + file_type == FileType::File + } + fn as_any_ref(&self) -> &dyn core::any::Any { self } diff --git a/kernel/src/filesystem/overlayfs/inode.rs b/kernel/src/filesystem/overlayfs/inode.rs index a423646d4f..784d558ce9 100644 --- a/kernel/src/filesystem/overlayfs/inode.rs +++ b/kernel/src/filesystem/overlayfs/inode.rs @@ -310,6 +310,10 @@ impl IndexNode for OvlInode { Some(self.fs()) } + fn supports_post_write_sync(&self, file_type: FileType) -> bool { + file_type == FileType::File + } + fn open( &self, data: crate::libs::mutex::MutexGuard, diff --git a/kernel/src/filesystem/ramfs/mod.rs b/kernel/src/filesystem/ramfs/mod.rs index 8d040c20b8..38df0ebd30 100644 --- a/kernel/src/filesystem/ramfs/mod.rs +++ b/kernel/src/filesystem/ramfs/mod.rs @@ -348,6 +348,10 @@ impl IndexNode for LockedRamFSInode { Some(self.fs()) } + fn supports_post_write_sync(&self, file_type: FileType) -> bool { + file_type == FileType::File + } + fn mmap(&self, _start: usize, _len: usize, _offset: usize) -> Result<(), SystemError> { Ok(()) } diff --git a/kernel/src/filesystem/tmpfs/mod.rs b/kernel/src/filesystem/tmpfs/mod.rs index 9d93d91d28..38f73783d0 100644 --- a/kernel/src/filesystem/tmpfs/mod.rs +++ b/kernel/src/filesystem/tmpfs/mod.rs @@ -790,6 +790,10 @@ impl IndexNode for LockedTmpfsInode { Some(self.fs()) } + fn supports_post_write_sync(&self, file_type: FileType) -> bool { + file_type == FileType::File + } + fn mmap(&self, _start: usize, _len: usize, _offset: usize) -> Result<(), SystemError> { Ok(()) } diff --git a/kernel/src/filesystem/vfs/file.rs b/kernel/src/filesystem/vfs/file.rs index 626acda291..d71da230f0 100644 --- a/kernel/src/filesystem/vfs/file.rs +++ b/kernel/src/filesystem/vfs/file.rs @@ -205,10 +205,6 @@ struct WriteConfig { update_offset: bool, /// 偏移量更新方式 offset_update: OffsetUpdate, - /// 文件标志 - flags: FileFlags, - /// inode 标志 - inode_flags: InodeFlags, } /// Namespace fd backing data, typically created from /proc/thread-self/ns/* files. #[derive(Clone)] @@ -828,9 +824,16 @@ impl File { fn maybe_sync_after_write( &self, + file_type: FileType, + start: usize, + written_len: usize, flags: FileFlags, inode_flags: InodeFlags, ) -> Result<(), SystemError> { + if written_len == 0 || !self.inode.supports_post_write_sync(file_type) { + return Ok(()); + } + // O_SYNC 包含 O_DSYNC 位,所以只需检查 O_DSYNC 即可判断是否需要数据同步 let need_data_sync = flags.contains(FileFlags::O_DSYNC); // 检查是否需要元数据同步(O_SYNC = __O_SYNC | O_DSYNC) @@ -840,12 +843,15 @@ impl File { let inode_sync = inode_flags.contains(InodeFlags::S_SYNC); if need_data_sync || inode_sync { - if need_metadata_sync || inode_sync { - // O_SYNC 或 S_SYNC: 完整同步(数据 + 元数据) - self.inode.sync_file(false, self.private_data.lock())?; + let end = start.saturating_add(written_len).saturating_sub(1); + if need_metadata_sync { + // O_SYNC: 完整同步(数据 + 元数据) + self.inode + .sync_file_range(start, end, false, self.private_data.lock())?; } else { - // O_DSYNC: 仅数据同步 - self.inode.sync_file(true, self.private_data.lock())?; + // O_DSYNC 或 S_SYNC: datasync;完整同步只由 O_SYNC 决定。 + self.inode + .sync_file_range(start, end, true, self.private_data.lock())?; } } Ok(()) @@ -919,7 +925,6 @@ impl File { } } - self.maybe_sync_after_write(config.flags, config.inode_flags)?; Ok(written_len) } /// @brief 创建一个新的文件对象 @@ -1484,31 +1489,32 @@ impl File { WriteConfig { update_offset, offset_update: OffsetUpdate::StoreEnd, - flags, - inode_flags, }, ) + .map(|written_len| (actual_offset, written_len)) }; - return match self.append_lock_domain.as_ref() { + let (actual_offset, written_len) = match self.append_lock_domain.as_ref() { Some(domain) => with_inode_append_lock(domain.key(&md), append_write), None => append_write(), - }; + }?; + self.maybe_sync_after_write(file_type, actual_offset, written_len, flags, inode_flags)?; + return Ok(written_len); } let actual_offset = offset; let actual_len = self.limit_write_len_by_fsize(file_type, actual_offset, len)?; - self.write_at_and_finalize( + let written_len = self.write_at_and_finalize( actual_offset, actual_len, buf, WriteConfig { update_offset, offset_update: OffsetUpdate::Add, - flags, - inode_flags, }, - ) + )?; + self.maybe_sync_after_write(file_type, actual_offset, written_len, flags, inode_flags)?; + Ok(written_len) } pub fn do_write_user( @@ -1553,31 +1559,32 @@ impl File { WriteConfig { update_offset, offset_update: OffsetUpdate::StoreEnd, - flags, - inode_flags, }, ) + .map(|written_len| (actual_offset, written_len)) }; - return match self.append_lock_domain.as_ref() { + let (actual_offset, written_len) = match self.append_lock_domain.as_ref() { Some(domain) => with_inode_append_lock(domain.key(&md), append_write), None => append_write(), - }; + }?; + self.maybe_sync_after_write(file_type, actual_offset, written_len, flags, inode_flags)?; + return Ok(written_len); } let actual_offset = offset; let actual_len = self.limit_write_len_by_fsize(file_type, actual_offset, len)?; - self.write_user_at_and_finalize( + let written_len = self.write_user_at_and_finalize( actual_offset, actual_len, reader, WriteConfig { update_offset, offset_update: OffsetUpdate::Add, - flags, - inode_flags, }, - ) + )?; + self.maybe_sync_after_write(file_type, actual_offset, written_len, flags, inode_flags)?; + Ok(written_len) } /// Write to the file from a userspace buffer. diff --git a/kernel/src/filesystem/vfs/mod.rs b/kernel/src/filesystem/vfs/mod.rs index da1ec9214e..28ea062c92 100644 --- a/kernel/src/filesystem/vfs/mod.rs +++ b/kernel/src/filesystem/vfs/mod.rs @@ -587,6 +587,15 @@ pub trait IndexNode: Any + Sync + Send + Debug + CastFromSync { /// protocol flags. fn adjust_file_mode_after_open(&self, _data: &FilePrivateData, _mode: &mut FileMode) {} + /// Whether the VFS write path should apply Linux `generic_write_sync()` + /// semantics after a successful positive-length write. + /// + /// This is intentionally opt-in: many pseudo files are reported as regular + /// files but their Linux write paths do not perform generic write syncing. + fn supports_post_write_sync(&self, _file_type: FileType) -> bool { + false + } + /// @brief 关闭文件 /// /// @return 成功:Ok() diff --git a/kernel/src/filesystem/vfs/mount/mod.rs b/kernel/src/filesystem/vfs/mount/mod.rs index 7cc21bdb92..ad059854c3 100644 --- a/kernel/src/filesystem/vfs/mount/mod.rs +++ b/kernel/src/filesystem/vfs/mount/mod.rs @@ -3611,6 +3611,10 @@ impl IndexNode for MountFSInode { self.dentry.inode.adjust_file_mode_after_open(data, mode) } + fn supports_post_write_sync(&self, file_type: FileType) -> bool { + self.dentry.inode.supports_post_write_sync(file_type) + } + fn mmap(&self, start: usize, len: usize, offset: usize) -> Result<(), SystemError> { return self.dentry.inode.mmap(start, len, offset); } From 854c4370ecd3f6bfe014edad1ac3816ecf4c9b50 Mon Sep 17 00:00:00 2001 From: Donjuanplatinum Date: Tue, 21 Jul 2026 10:53:42 +0800 Subject: [PATCH 2/3] add IOGuard for loopdevice sync Signed-off-by: Donjuanplatinum --- kernel/src/driver/block/loop_device/loop_device.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/kernel/src/driver/block/loop_device/loop_device.rs b/kernel/src/driver/block/loop_device/loop_device.rs index 3e486f13f9..51bde201ee 100644 --- a/kernel/src/driver/block/loop_device/loop_device.rs +++ b/kernel/src/driver/block/loop_device/loop_device.rs @@ -1342,6 +1342,8 @@ impl BlockDevice for LoopDevice { } fn sync(&self) -> Result<(), SystemError> { + let _io_guard = IoGuard::new(self)?; + let inode = self.inner().file_inode.clone().ok_or(SystemError::ENODEV)?; inode.sync()?; inode.fs().sync_fs(true) From 679252125441a5712790d66762e402111317ea37 Mon Sep 17 00:00:00 2001 From: Donjuanplatinum Date: Sun, 26 Jul 2026 00:07:05 +0800 Subject: [PATCH 3/3] change maybe_sync_after_write Signed-off-by: Donjuanplatinum --- kernel/src/filesystem/fat/fs.rs | 4 --- kernel/src/filesystem/overlayfs/inode.rs | 4 --- kernel/src/filesystem/tmpfs/mod.rs | 4 --- kernel/src/filesystem/vfs/file.rs | 38 +++++++++++++++--------- 4 files changed, 24 insertions(+), 26 deletions(-) diff --git a/kernel/src/filesystem/fat/fs.rs b/kernel/src/filesystem/fat/fs.rs index 6b8ad93890..f2c0fc0325 100644 --- a/kernel/src/filesystem/fat/fs.rs +++ b/kernel/src/filesystem/fat/fs.rs @@ -1864,10 +1864,6 @@ impl IndexNode for LockedFATInode { Some(self.fs()) } - fn supports_post_write_sync(&self, file_type: FileType) -> bool { - file_type == FileType::File - } - fn mmap(&self, _start: usize, _len: usize, _offset: usize) -> Result<(), SystemError> { Ok(()) } diff --git a/kernel/src/filesystem/overlayfs/inode.rs b/kernel/src/filesystem/overlayfs/inode.rs index 784d558ce9..a423646d4f 100644 --- a/kernel/src/filesystem/overlayfs/inode.rs +++ b/kernel/src/filesystem/overlayfs/inode.rs @@ -310,10 +310,6 @@ impl IndexNode for OvlInode { Some(self.fs()) } - fn supports_post_write_sync(&self, file_type: FileType) -> bool { - file_type == FileType::File - } - fn open( &self, data: crate::libs::mutex::MutexGuard, diff --git a/kernel/src/filesystem/tmpfs/mod.rs b/kernel/src/filesystem/tmpfs/mod.rs index 38f73783d0..9d93d91d28 100644 --- a/kernel/src/filesystem/tmpfs/mod.rs +++ b/kernel/src/filesystem/tmpfs/mod.rs @@ -790,10 +790,6 @@ impl IndexNode for LockedTmpfsInode { Some(self.fs()) } - fn supports_post_write_sync(&self, file_type: FileType) -> bool { - file_type == FileType::File - } - fn mmap(&self, _start: usize, _len: usize, _offset: usize) -> Result<(), SystemError> { Ok(()) } diff --git a/kernel/src/filesystem/vfs/file.rs b/kernel/src/filesystem/vfs/file.rs index d71da230f0..213aa26d21 100644 --- a/kernel/src/filesystem/vfs/file.rs +++ b/kernel/src/filesystem/vfs/file.rs @@ -10,7 +10,7 @@ use system_error::SystemError; use super::{ append_lock::{with_inode_append_lock, AppendLockKey}, inode_lifecycle::{InodeRetentionGuard, InodeRetentionKind}, - mount::{MountExternalGuard, MountFSInode}, + mount::{MountExternalGuard, MountFSInode, MountFlags}, utils::should_remove_sgid, DirectoryEntry, FileSystem, FileType, IndexNode, InodeId, Metadata, SetMetadataMask, SpecialNodeData, @@ -822,6 +822,13 @@ impl File { Ok(len.min(limit.saturating_sub(offset))) } + fn combined_mount_flags(&self) -> MountFlags { + match self.inode.clone().downcast_arc::() { + Some(mnt_inode) => mnt_inode.mount_fs().combined_flags(), + None => self.inode.mount_flags(), + } + } + fn maybe_sync_after_write( &self, file_type: FileType, @@ -830,7 +837,7 @@ impl File { flags: FileFlags, inode_flags: InodeFlags, ) -> Result<(), SystemError> { - if written_len == 0 || !self.inode.supports_post_write_sync(file_type) { + if written_len == 0 { return Ok(()); } @@ -841,20 +848,23 @@ impl File { // inode 级别的 S_SYNC 标志 let inode_sync = inode_flags.contains(InodeFlags::S_SYNC); + // Linux IS_SYNC(inode) 同时包含 inode S_SYNC 与 superblock/mount sync 语义。 + let mount_sync = self + .combined_mount_flags() + .contains(MountFlags::SYNCHRONOUS); - if need_data_sync || inode_sync { - let end = start.saturating_add(written_len).saturating_sub(1); - if need_metadata_sync { - // O_SYNC: 完整同步(数据 + 元数据) - self.inode - .sync_file_range(start, end, false, self.private_data.lock())?; - } else { - // O_DSYNC 或 S_SYNC: datasync;完整同步只由 O_SYNC 决定。 - self.inode - .sync_file_range(start, end, true, self.private_data.lock())?; - } + if !(need_data_sync || inode_sync || mount_sync) { + return Ok(()); } - Ok(()) + + if !self.inode.supports_post_write_sync(file_type) { + return Ok(()); + } + + let end = start.saturating_add(written_len).saturating_sub(1); + // Linux generic_write_sync() 只有 IOCB_SYNC 才请求完整 metadata sync; + // O_DSYNC、S_SYNC 与 SB_SYNCHRONOUS 都按 datasync 处理。 + self.sync_range_and_check_wb_error(start, end, !need_metadata_sync) } #[inline(never)]