-
-
Notifications
You must be signed in to change notification settings - Fork 194
fix(fs): align post-write sync with Linux generic_write_sync #2147
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Changes from 1 commit
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -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<FilePrivateData>, | ||
| ) -> Result<(), SystemError> { | ||
| <Self as BlockDevice>::sync(self) | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. [P1] Retain and fsync the backing File, not only its inode LOOP_SET_FD currently stores only file.inode(), so after the setup fd is The new sync_file() activates BlockDevice::sync(), which calls inode.sync() Please retain an Arc for the backing object, use its file-level sync |
||
| } | ||
|
|
||
| fn read_at( | ||
| &self, | ||
| offset: usize, | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -1864,6 +1864,10 @@ impl IndexNode for LockedFATInode { | |
| Some(self.fs()) | ||
| } | ||
|
|
||
| fn supports_post_write_sync(&self, file_type: FileType) -> bool { | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. [P1] Do not opt FAT in before sync_file_range provides a durability boundary The current FAT sync_file_range() only writes back the selected page-cache Linux fat_file_fsync() runs __generic_file_fsync(), synchronizes the FAT |
||
| file_type == FileType::File | ||
| } | ||
|
|
||
| fn mmap(&self, _start: usize, _len: usize, _offset: usize) -> Result<(), SystemError> { | ||
| Ok(()) | ||
| } | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -310,6 +310,10 @@ impl IndexNode for OvlInode { | |
| Some(self.fs()) | ||
| } | ||
|
|
||
| fn supports_post_write_sync(&self, file_type: FileType) -> bool { | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. [P1] Do not run a second generic sync at the OverlayFS layer OverlayFS write_at() calls backing_file.pwrite() with the backing File's Linux ovl_write_iter() forwards IOCB_SYNC/IOCB_DSYNC to the real file and does Please remove this override and let the backing File own the synchronous-write |
||
| file_type == FileType::File | ||
| } | ||
|
|
||
| fn open( | ||
| &self, | ||
| data: crate::libs::mutex::MutexGuard<FilePrivateData>, | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -790,6 +790,10 @@ impl IndexNode for LockedTmpfsInode { | |
| Some(self.fs()) | ||
| } | ||
|
|
||
| fn supports_post_write_sync(&self, file_type: FileType) -> bool { | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. [P1] tmpfs/shmem does not use generic_write_sync() Linux 6.6 shmem_file_write_iter() returns directly after Please remove this override. RamFS and tmpfs should not be classified together |
||
| file_type == FileType::File | ||
| } | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. maybe we should add a default impl or make a better design instead of write these duplicated code. |
||
|
|
||
| fn mmap(&self, _start: usize, _len: usize, _offset: usize) -> Result<(), SystemError> { | ||
| Ok(()) | ||
| } | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -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) { | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. [P1] Include superblock synchronous semantics before this early return The later Please include the mount/superblock synchronous state in the trigger. Also, |
||
| 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 | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. [P1] Preserve the open-file writeback error contract here Calling inode.sync_file_range() directly bypasses the wb_error_seq check that Linux generic_write_sync() reaches vfs_fsync_range(), and Advancing the file offset before a later sync error is already consistent with |
||
| .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. | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -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 { | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. [P1] Model this as a write-operation policy, not a default-false inode capability Linux does not decide whether to run generic_write_sync() from an inode Making this a default-false IndexNode method creates a silent durability Please make the selected open-file/write operation own this decision. A |
||
| false | ||
| } | ||
|
|
||
| /// @brief 关闭文件 | ||
| /// | ||
| /// @return 成功:Ok() | ||
|
|
||
Uh oh!
There was an error while loading. Please reload this page.