mirror of
https://github.com/torvalds/linux.git
synced 2026-11-10 17:11:02 +01:00
ntfs: do not clear the volume dirty bit during sync
ntfs_sync_fs() clears VOLUME_IS_DIRTY while the volume is still mounted read-write, so a sync running concurrently with an in-flight metadata modification can clear and persist a bit that was just set: the modification then lands on a volume that is clean on disk, and a crash does not run chkdsk. Stop clearing the bit in ntfs_sync_fs() and leave the clearing to the remount-to-read-only path, which the VFS reaches only after sb_prepare_remount_readonly() has drained in-flight writers, and to ntfs_put_super(), which runs after evict_inodes() on a quiesced filesystem. A mounted read-write volume now keeps the dirty bit until it is dismounted cleanly, which matches the NTFS semantics; the cost is a needless chkdsk if the machine crashes between a sync and the unmount. A recorded error state is still persisted right away when the filesystem is synced: with NVolErrors() set, ntfs_sync_volume_dirty_state() can only set the bit, never clear it, so it cannot lose a modification the way the old unconditional call did. This keeps the error report from being lost to a crash on a volume that has seen no modification; an error recorded after the last sync is still only persisted at the next quiescent transition. sync_blockdev() and blkdev_issue_flush() are both called and the first error is returned, so a writeback failure neither hides a flush failure nor skips it. A RWF_NOWAIT write still blocks in the marking when the volume looks clean, as it already did on the base; a non-blocking marking is follow-up work. Reported-by: Baolin Liu <liubaolin@kylinos.cn> Cc: stable@vger.kernel.org Signed-off-by: Hongling Zeng <zenghongling@kylinos.cn> Reviewed-by: Hyunchul Lee <hyc.lee@gmail.com> Reviewed-by: Baolin Liu <liubaolin@kylinos.cn> Signed-off-by: Namjae Jeon <linkinjeon@kernel.org>
This commit is contained in:
parent
5d0e57ea8e
commit
4a1bb54f57
|
|
@ -637,9 +637,10 @@ static ssize_t ntfs_file_write_iter(struct kiocb *iocb, struct iov_iter *from)
|
|||
|
||||
/*
|
||||
* The volume must be marked dirty before the modification is made,
|
||||
* without an unlocked check of the in-memory flag: ntfs_sync_fs()
|
||||
* can clear the bit concurrently and the modification would then
|
||||
* land on a volume that is clean on disk.
|
||||
* without an unlocked check of the in-memory flag: the dirty bit
|
||||
* is only cleared at the quiescent transitions, under the same
|
||||
* $Volume mrec_lock this call takes, so an unlocked skip could
|
||||
* lose the set to one of them.
|
||||
*/
|
||||
ntfs_set_volume_flags(vol, VOLUME_IS_DIRTY);
|
||||
|
||||
|
|
|
|||
|
|
@ -493,8 +493,9 @@ int ntfs_set_volume_flags(struct ntfs_volume *vol, __le16 flags)
|
|||
* This is the single point that persists the in-memory error state to disk.
|
||||
* The runtime error paths only record NVolErrors() because they run under a
|
||||
* variety of ntfs locks the dirty-bit write cannot be taken under (runlist
|
||||
* locks, vol->lcnbmp_lock, vol->mftbmp_lock, mrec_locks); the first
|
||||
* ntfs_sync_fs(), a remount, or the unmount then persists the flag here.
|
||||
* locks, vol->lcnbmp_lock, vol->mftbmp_lock, mrec_locks); a sync of a
|
||||
* volume with recorded errors, a remount to read-only, or the unmount,
|
||||
* then persists the flag here.
|
||||
*
|
||||
* A hibernated volume is not written from these persistence paths:
|
||||
* resuming Windows from a modified image corrupts it, so the dirty bit
|
||||
|
|
@ -2023,7 +2024,7 @@ static void ntfs_shutdown(struct super_block *sb)
|
|||
static int ntfs_sync_fs(struct super_block *sb, int wait)
|
||||
{
|
||||
struct ntfs_volume *vol = NTFS_SB(sb);
|
||||
int err = 0;
|
||||
int ret, err = 0;
|
||||
|
||||
if (NVolShutdown(vol))
|
||||
return -EIO;
|
||||
|
|
@ -2031,14 +2032,30 @@ static int ntfs_sync_fs(struct super_block *sb, int wait)
|
|||
if (!wait)
|
||||
return 0;
|
||||
|
||||
/* If there are some dirty buffers in the bdev inode */
|
||||
if (ntfs_sync_volume_dirty_state(vol)) {
|
||||
/*
|
||||
* The volume dirty bit is deliberately not cleared here: a sync
|
||||
* running concurrently with an in-flight modification could clear
|
||||
* and persist a bit that was just set, leaving the modification
|
||||
* on a volume that is clean on disk. The bit is only cleared at
|
||||
* the quiescent state transitions, remounting read-only and clean
|
||||
* unmount. A recorded error state, however, is persisted right
|
||||
* away so that it is not lost to a crash on a volume that has
|
||||
* seen no modification; with NVolErrors() set this can only set
|
||||
* the bit, never clear it.
|
||||
*/
|
||||
if (NVolErrors(vol) &&
|
||||
ntfs_sync_volume_dirty_state(vol)) {
|
||||
ntfs_warning(sb, "Failed to sync dirty bit in volume information flags. Run chkdsk.");
|
||||
err = -EIO;
|
||||
}
|
||||
sync_inodes_sb(sb);
|
||||
sync_blockdev(sb->s_bdev);
|
||||
blkdev_issue_flush(sb->s_bdev);
|
||||
ret = sync_blockdev(sb->s_bdev);
|
||||
if (ret && !err)
|
||||
err = ret;
|
||||
|
||||
ret = blkdev_issue_flush(sb->s_bdev);
|
||||
if (ret && !err)
|
||||
err = ret;
|
||||
return err;
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user