Commit 5a3e5cb8e08bd876e2542c1451c9a93dab1b0e39
Committed by
Al Viro
1 parent
876a9f76ab
Exists in
master
and in
4 other branches
vfs: Fix sys_sync() and fsync_super() reliability (version 4)
So far, do_sync() called: sync_inodes(0); sync_supers(); sync_filesystems(0); sync_filesystems(1); sync_inodes(1); This ordering makes it kind of hard for filesystems as sync_inodes(0) need not submit all the IO (for example it skips inodes with I_SYNC set) so e.g. forcing transaction to disk in ->sync_fs() is not really enough. Therefore sys_sync has not been completely reliable on some filesystems (ext3, ext4, reiserfs, ocfs2 and others are hit by this) when racing e.g. with background writeback. A similar problem hits also other filesystems (e.g. ext2) because of write_supers() being called before the sync_inodes(1). Change the ordering of calls in do_sync() - this requires a new function sync_blockdevs() to preserve the property that block devices are always synced after write_super() / sync_fs() call. The same issue is fixed in __fsync_super() function used on umount / remount read-only. [AV: build fixes] Signed-off-by: Jan Kara <jack@suse.cz> Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
Showing 3 changed files with 40 additions and 2 deletions Side-by-side Diff
fs/internal.h
fs/super.c
... | ... | @@ -293,6 +293,7 @@ |
293 | 293 | { |
294 | 294 | sync_inodes_sb(sb, 0); |
295 | 295 | vfs_dq_sync(sb); |
296 | + sync_inodes_sb(sb, 1); | |
296 | 297 | lock_super(sb); |
297 | 298 | if (sb->s_dirt && sb->s_op->write_super) |
298 | 299 | sb->s_op->write_super(sb); |
... | ... | @@ -300,7 +301,6 @@ |
300 | 301 | if (sb->s_op->sync_fs) |
301 | 302 | sb->s_op->sync_fs(sb, 1); |
302 | 303 | sync_blockdev(sb->s_bdev); |
303 | - sync_inodes_sb(sb, 1); | |
304 | 304 | } |
305 | 305 | |
306 | 306 | /* |
... | ... | @@ -521,6 +521,33 @@ |
521 | 521 | spin_unlock(&sb_lock); |
522 | 522 | mutex_unlock(&mutex); |
523 | 523 | } |
524 | + | |
525 | +#ifdef CONFIG_BLOCK | |
526 | +/* | |
527 | + * Sync all block devices underlying some superblock | |
528 | + */ | |
529 | +void sync_blockdevs(void) | |
530 | +{ | |
531 | + struct super_block *sb; | |
532 | + | |
533 | + spin_lock(&sb_lock); | |
534 | +restart: | |
535 | + list_for_each_entry(sb, &super_blocks, s_list) { | |
536 | + if (!sb->s_bdev) | |
537 | + continue; | |
538 | + sb->s_count++; | |
539 | + spin_unlock(&sb_lock); | |
540 | + down_read(&sb->s_umount); | |
541 | + if (sb->s_root) | |
542 | + sync_blockdev(sb->s_bdev); | |
543 | + up_read(&sb->s_umount); | |
544 | + spin_lock(&sb_lock); | |
545 | + if (__put_super_and_need_restart(sb)) | |
546 | + goto restart; | |
547 | + } | |
548 | + spin_unlock(&sb_lock); | |
549 | +} | |
550 | +#endif | |
524 | 551 | |
525 | 552 | /** |
526 | 553 | * get_super - get the superblock of a device |
fs/sync.c
... | ... | @@ -13,6 +13,7 @@ |
13 | 13 | #include <linux/pagemap.h> |
14 | 14 | #include <linux/quotaops.h> |
15 | 15 | #include <linux/buffer_head.h> |
16 | +#include "internal.h" | |
16 | 17 | |
17 | 18 | #define VALID_FLAGS (SYNC_FILE_RANGE_WAIT_BEFORE|SYNC_FILE_RANGE_WRITE| \ |
18 | 19 | SYNC_FILE_RANGE_WAIT_AFTER) |
19 | 20 | |
... | ... | @@ -26,10 +27,11 @@ |
26 | 27 | wakeup_pdflush(0); |
27 | 28 | sync_inodes(0); /* All mappings, inodes and their blockdevs */ |
28 | 29 | vfs_dq_sync(NULL); |
30 | + sync_inodes(wait); /* Mappings, inodes and blockdevs, again. */ | |
29 | 31 | sync_supers(); /* Write the superblocks */ |
30 | 32 | sync_filesystems(0); /* Start syncing the filesystems */ |
31 | 33 | sync_filesystems(wait); /* Waitingly sync the filesystems */ |
32 | - sync_inodes(wait); /* Mappings, inodes and blockdevs, again. */ | |
34 | + sync_blockdevs(); | |
33 | 35 | if (!wait) |
34 | 36 | printk("Emergency Sync complete\n"); |
35 | 37 | if (unlikely(laptop_mode)) |