diff options
author | Takashi Sato <t-sato@yk.jp.nec.com> | 2009-01-09 19:40:58 -0500 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2009-01-09 19:54:42 -0500 |
commit | c4be0c1dc4cdc37b175579be1460f15ac6495e9a (patch) | |
tree | 716ea88318211ed27cadcebda0fd85c1f8246edb /fs/ext4 | |
parent | 69347a236b22c3962ea812511495e502dedfd50c (diff) |
filesystem freeze: add error handling of write_super_lockfs/unlockfs
Currently, ext3 in mainline Linux doesn't have the freeze feature which
suspends write requests. So, we cannot take a backup which keeps the
filesystem's consistency with the storage device's features (snapshot and
replication) while it is mounted.
In many case, a commercial filesystem (e.g. VxFS) has the freeze feature
and it would be used to get the consistent backup.
If Linux's standard filesystem ext3 has the freeze feature, we can do it
without a commercial filesystem.
So I have implemented the ioctls of the freeze feature.
I think we can take the consistent backup with the following steps.
1. Freeze the filesystem with the freeze ioctl.
2. Separate the replication volume or create the snapshot
with the storage device's feature.
3. Unfreeze the filesystem with the unfreeze ioctl.
4. Take the backup from the separated replication volume
or the snapshot.
This patch:
VFS:
Changed the type of write_super_lockfs and unlockfs from "void"
to "int" so that they can return an error.
Rename write_super_lockfs and unlockfs of the super block operation
freeze_fs and unfreeze_fs to avoid a confusion.
ext3, ext4, xfs, gfs2, jfs:
Changed the type of write_super_lockfs and unlockfs from "void"
to "int" so that write_super_lockfs returns an error if needed,
and unlockfs always returns 0.
reiserfs:
Changed the type of write_super_lockfs and unlockfs from "void"
to "int" so that they always return 0 (success) to keep a current behavior.
Signed-off-by: Takashi Sato <t-sato@yk.jp.nec.com>
Signed-off-by: Masayuki Hamaguchi <m-hamaguchi@ys.jp.nec.com>
Cc: <xfs-masters@oss.sgi.com>
Cc: <linux-ext4@vger.kernel.org>
Cc: Christoph Hellwig <hch@lst.de>
Cc: Dave Kleikamp <shaggy@austin.ibm.com>
Cc: Dave Chinner <david@fromorbit.com>
Cc: Alasdair G Kergon <agk@redhat.com>
Cc: Al Viro <viro@zeniv.linux.org.uk>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
Diffstat (limited to 'fs/ext4')
-rw-r--r-- | fs/ext4/super.c | 45 |
1 files changed, 31 insertions, 14 deletions
diff --git a/fs/ext4/super.c b/fs/ext4/super.c index 8f7e0be8ab1b..e5f06a5f045e 100644 --- a/fs/ext4/super.c +++ b/fs/ext4/super.c | |||
@@ -51,7 +51,7 @@ struct proc_dir_entry *ext4_proc_root; | |||
51 | 51 | ||
52 | static int ext4_load_journal(struct super_block *, struct ext4_super_block *, | 52 | static int ext4_load_journal(struct super_block *, struct ext4_super_block *, |
53 | unsigned long journal_devnum); | 53 | unsigned long journal_devnum); |
54 | static void ext4_commit_super(struct super_block *sb, | 54 | static int ext4_commit_super(struct super_block *sb, |
55 | struct ext4_super_block *es, int sync); | 55 | struct ext4_super_block *es, int sync); |
56 | static void ext4_mark_recovery_complete(struct super_block *sb, | 56 | static void ext4_mark_recovery_complete(struct super_block *sb, |
57 | struct ext4_super_block *es); | 57 | struct ext4_super_block *es); |
@@ -62,9 +62,9 @@ static const char *ext4_decode_error(struct super_block *sb, int errno, | |||
62 | char nbuf[16]); | 62 | char nbuf[16]); |
63 | static int ext4_remount(struct super_block *sb, int *flags, char *data); | 63 | static int ext4_remount(struct super_block *sb, int *flags, char *data); |
64 | static int ext4_statfs(struct dentry *dentry, struct kstatfs *buf); | 64 | static int ext4_statfs(struct dentry *dentry, struct kstatfs *buf); |
65 | static void ext4_unlockfs(struct super_block *sb); | 65 | static int ext4_unfreeze(struct super_block *sb); |
66 | static void ext4_write_super(struct super_block *sb); | 66 | static void ext4_write_super(struct super_block *sb); |
67 | static void ext4_write_super_lockfs(struct super_block *sb); | 67 | static int ext4_freeze(struct super_block *sb); |
68 | 68 | ||
69 | 69 | ||
70 | ext4_fsblk_t ext4_block_bitmap(struct super_block *sb, | 70 | ext4_fsblk_t ext4_block_bitmap(struct super_block *sb, |
@@ -978,8 +978,8 @@ static const struct super_operations ext4_sops = { | |||
978 | .put_super = ext4_put_super, | 978 | .put_super = ext4_put_super, |
979 | .write_super = ext4_write_super, | 979 | .write_super = ext4_write_super, |
980 | .sync_fs = ext4_sync_fs, | 980 | .sync_fs = ext4_sync_fs, |
981 | .write_super_lockfs = ext4_write_super_lockfs, | 981 | .freeze_fs = ext4_freeze, |
982 | .unlockfs = ext4_unlockfs, | 982 | .unfreeze_fs = ext4_unfreeze, |
983 | .statfs = ext4_statfs, | 983 | .statfs = ext4_statfs, |
984 | .remount_fs = ext4_remount, | 984 | .remount_fs = ext4_remount, |
985 | .clear_inode = ext4_clear_inode, | 985 | .clear_inode = ext4_clear_inode, |
@@ -2888,13 +2888,14 @@ static int ext4_load_journal(struct super_block *sb, | |||
2888 | return 0; | 2888 | return 0; |
2889 | } | 2889 | } |
2890 | 2890 | ||
2891 | static void ext4_commit_super(struct super_block *sb, | 2891 | static int ext4_commit_super(struct super_block *sb, |
2892 | struct ext4_super_block *es, int sync) | 2892 | struct ext4_super_block *es, int sync) |
2893 | { | 2893 | { |
2894 | struct buffer_head *sbh = EXT4_SB(sb)->s_sbh; | 2894 | struct buffer_head *sbh = EXT4_SB(sb)->s_sbh; |
2895 | int error = 0; | ||
2895 | 2896 | ||
2896 | if (!sbh) | 2897 | if (!sbh) |
2897 | return; | 2898 | return error; |
2898 | if (buffer_write_io_error(sbh)) { | 2899 | if (buffer_write_io_error(sbh)) { |
2899 | /* | 2900 | /* |
2900 | * Oh, dear. A previous attempt to write the | 2901 | * Oh, dear. A previous attempt to write the |
@@ -2918,14 +2919,19 @@ static void ext4_commit_super(struct super_block *sb, | |||
2918 | BUFFER_TRACE(sbh, "marking dirty"); | 2919 | BUFFER_TRACE(sbh, "marking dirty"); |
2919 | mark_buffer_dirty(sbh); | 2920 | mark_buffer_dirty(sbh); |
2920 | if (sync) { | 2921 | if (sync) { |
2921 | sync_dirty_buffer(sbh); | 2922 | error = sync_dirty_buffer(sbh); |
2922 | if (buffer_write_io_error(sbh)) { | 2923 | if (error) |
2924 | return error; | ||
2925 | |||
2926 | error = buffer_write_io_error(sbh); | ||
2927 | if (error) { | ||
2923 | printk(KERN_ERR "EXT4-fs: I/O error while writing " | 2928 | printk(KERN_ERR "EXT4-fs: I/O error while writing " |
2924 | "superblock for %s.\n", sb->s_id); | 2929 | "superblock for %s.\n", sb->s_id); |
2925 | clear_buffer_write_io_error(sbh); | 2930 | clear_buffer_write_io_error(sbh); |
2926 | set_buffer_uptodate(sbh); | 2931 | set_buffer_uptodate(sbh); |
2927 | } | 2932 | } |
2928 | } | 2933 | } |
2934 | return error; | ||
2929 | } | 2935 | } |
2930 | 2936 | ||
2931 | 2937 | ||
@@ -3058,12 +3064,14 @@ static int ext4_sync_fs(struct super_block *sb, int wait) | |||
3058 | * LVM calls this function before a (read-only) snapshot is created. This | 3064 | * LVM calls this function before a (read-only) snapshot is created. This |
3059 | * gives us a chance to flush the journal completely and mark the fs clean. | 3065 | * gives us a chance to flush the journal completely and mark the fs clean. |
3060 | */ | 3066 | */ |
3061 | static void ext4_write_super_lockfs(struct super_block *sb) | 3067 | static int ext4_freeze(struct super_block *sb) |
3062 | { | 3068 | { |
3069 | int error = 0; | ||
3070 | journal_t *journal; | ||
3063 | sb->s_dirt = 0; | 3071 | sb->s_dirt = 0; |
3064 | 3072 | ||
3065 | if (!(sb->s_flags & MS_RDONLY)) { | 3073 | if (!(sb->s_flags & MS_RDONLY)) { |
3066 | journal_t *journal = EXT4_SB(sb)->s_journal; | 3074 | journal = EXT4_SB(sb)->s_journal; |
3067 | 3075 | ||
3068 | if (journal) { | 3076 | if (journal) { |
3069 | /* Now we set up the journal barrier. */ | 3077 | /* Now we set up the journal barrier. */ |
@@ -3073,21 +3081,29 @@ static void ext4_write_super_lockfs(struct super_block *sb) | |||
3073 | * We don't want to clear needs_recovery flag when we | 3081 | * We don't want to clear needs_recovery flag when we |
3074 | * failed to flush the journal. | 3082 | * failed to flush the journal. |
3075 | */ | 3083 | */ |
3076 | if (jbd2_journal_flush(journal) < 0) | 3084 | error = jbd2_journal_flush(journal); |
3077 | return; | 3085 | if (error < 0) |
3086 | goto out; | ||
3078 | } | 3087 | } |
3079 | 3088 | ||
3080 | /* Journal blocked and flushed, clear needs_recovery flag. */ | 3089 | /* Journal blocked and flushed, clear needs_recovery flag. */ |
3081 | EXT4_CLEAR_INCOMPAT_FEATURE(sb, EXT4_FEATURE_INCOMPAT_RECOVER); | 3090 | EXT4_CLEAR_INCOMPAT_FEATURE(sb, EXT4_FEATURE_INCOMPAT_RECOVER); |
3082 | ext4_commit_super(sb, EXT4_SB(sb)->s_es, 1); | 3091 | ext4_commit_super(sb, EXT4_SB(sb)->s_es, 1); |
3092 | error = ext4_commit_super(sb, EXT4_SB(sb)->s_es, 1); | ||
3093 | if (error) | ||
3094 | goto out; | ||
3083 | } | 3095 | } |
3096 | return 0; | ||
3097 | out: | ||
3098 | jbd2_journal_unlock_updates(journal); | ||
3099 | return error; | ||
3084 | } | 3100 | } |
3085 | 3101 | ||
3086 | /* | 3102 | /* |
3087 | * Called by LVM after the snapshot is done. We need to reset the RECOVER | 3103 | * Called by LVM after the snapshot is done. We need to reset the RECOVER |
3088 | * flag here, even though the filesystem is not technically dirty yet. | 3104 | * flag here, even though the filesystem is not technically dirty yet. |
3089 | */ | 3105 | */ |
3090 | static void ext4_unlockfs(struct super_block *sb) | 3106 | static int ext4_unfreeze(struct super_block *sb) |
3091 | { | 3107 | { |
3092 | if (EXT4_SB(sb)->s_journal && !(sb->s_flags & MS_RDONLY)) { | 3108 | if (EXT4_SB(sb)->s_journal && !(sb->s_flags & MS_RDONLY)) { |
3093 | lock_super(sb); | 3109 | lock_super(sb); |
@@ -3097,6 +3113,7 @@ static void ext4_unlockfs(struct super_block *sb) | |||
3097 | unlock_super(sb); | 3113 | unlock_super(sb); |
3098 | jbd2_journal_unlock_updates(EXT4_SB(sb)->s_journal); | 3114 | jbd2_journal_unlock_updates(EXT4_SB(sb)->s_journal); |
3099 | } | 3115 | } |
3116 | return 0; | ||
3100 | } | 3117 | } |
3101 | 3118 | ||
3102 | static int ext4_remount(struct super_block *sb, int *flags, char *data) | 3119 | static int ext4_remount(struct super_block *sb, int *flags, char *data) |