Commit acce952b authored by liubo's avatar liubo Committed by Chris Mason

Btrfs: forced readonly mounts on errors

This patch comes from "Forced readonly mounts on errors" ideas.

As we know, this is the first step in being more fault tolerant of disk
corruptions instead of just using BUG() statements.

The major content:
- add a framework for generating errors that should result in filesystems
  going readonly.
- keep FS state in disk super block.
- make sure that all of resource will be freed and released at umount time.
- make sure that fter FS is forced readonly on error, there will be no more
  disk change before FS is corrected. For this, we should stop write operation.

After this patch is applied, the conversion from BUG() to such a framework can
happen incrementally.
Signed-off-by: default avatarLiu Bo <liubo2009@cn.fujitsu.com>
Signed-off-by: default avatarChris Mason <chris.mason@oracle.com>
parent 6f88a440
...@@ -295,6 +295,14 @@ static inline unsigned long btrfs_chunk_item_size(int num_stripes) ...@@ -295,6 +295,14 @@ static inline unsigned long btrfs_chunk_item_size(int num_stripes)
#define BTRFS_FSID_SIZE 16 #define BTRFS_FSID_SIZE 16
#define BTRFS_HEADER_FLAG_WRITTEN (1ULL << 0) #define BTRFS_HEADER_FLAG_WRITTEN (1ULL << 0)
#define BTRFS_HEADER_FLAG_RELOC (1ULL << 1) #define BTRFS_HEADER_FLAG_RELOC (1ULL << 1)
/*
* File system states
*/
/* Errors detected */
#define BTRFS_SUPER_FLAG_ERROR (1ULL << 2)
#define BTRFS_SUPER_FLAG_SEEDING (1ULL << 32) #define BTRFS_SUPER_FLAG_SEEDING (1ULL << 32)
#define BTRFS_SUPER_FLAG_METADUMP (1ULL << 33) #define BTRFS_SUPER_FLAG_METADUMP (1ULL << 33)
...@@ -1058,6 +1066,9 @@ struct btrfs_fs_info { ...@@ -1058,6 +1066,9 @@ struct btrfs_fs_info {
unsigned metadata_ratio; unsigned metadata_ratio;
void *bdev_holder; void *bdev_holder;
/* filesystem state */
u64 fs_state;
}; };
/* /*
...@@ -2203,6 +2214,11 @@ int btrfs_set_block_group_rw(struct btrfs_root *root, ...@@ -2203,6 +2214,11 @@ int btrfs_set_block_group_rw(struct btrfs_root *root,
struct btrfs_block_group_cache *cache); struct btrfs_block_group_cache *cache);
void btrfs_put_block_group_cache(struct btrfs_fs_info *info); void btrfs_put_block_group_cache(struct btrfs_fs_info *info);
u64 btrfs_account_ro_block_groups_free_space(struct btrfs_space_info *sinfo); u64 btrfs_account_ro_block_groups_free_space(struct btrfs_space_info *sinfo);
int btrfs_error_unpin_extent_range(struct btrfs_root *root,
u64 start, u64 end);
int btrfs_error_discard_extent(struct btrfs_root *root, u64 bytenr,
u64 num_bytes);
/* ctree.c */ /* ctree.c */
int btrfs_bin_search(struct extent_buffer *eb, struct btrfs_key *key, int btrfs_bin_search(struct extent_buffer *eb, struct btrfs_key *key,
int level, int *slot); int level, int *slot);
...@@ -2556,6 +2572,14 @@ ssize_t btrfs_listxattr(struct dentry *dentry, char *buffer, size_t size); ...@@ -2556,6 +2572,14 @@ ssize_t btrfs_listxattr(struct dentry *dentry, char *buffer, size_t size);
/* super.c */ /* super.c */
int btrfs_parse_options(struct btrfs_root *root, char *options); int btrfs_parse_options(struct btrfs_root *root, char *options);
int btrfs_sync_fs(struct super_block *sb, int wait); int btrfs_sync_fs(struct super_block *sb, int wait);
void __btrfs_std_error(struct btrfs_fs_info *fs_info, const char *function,
unsigned int line, int errno);
#define btrfs_std_error(fs_info, errno) \
do { \
if ((errno)) \
__btrfs_std_error((fs_info), __func__, __LINE__, (errno));\
} while (0)
/* acl.c */ /* acl.c */
#ifdef CONFIG_BTRFS_FS_POSIX_ACL #ifdef CONFIG_BTRFS_FS_POSIX_ACL
......
This diff is collapsed.
...@@ -52,6 +52,7 @@ int write_ctree_super(struct btrfs_trans_handle *trans, ...@@ -52,6 +52,7 @@ int write_ctree_super(struct btrfs_trans_handle *trans,
struct btrfs_root *root, int max_mirrors); struct btrfs_root *root, int max_mirrors);
struct buffer_head *btrfs_read_dev_super(struct block_device *bdev); struct buffer_head *btrfs_read_dev_super(struct block_device *bdev);
int btrfs_commit_super(struct btrfs_root *root); int btrfs_commit_super(struct btrfs_root *root);
int btrfs_error_commit_super(struct btrfs_root *root);
struct extent_buffer *btrfs_find_tree_block(struct btrfs_root *root, struct extent_buffer *btrfs_find_tree_block(struct btrfs_root *root,
u64 bytenr, u32 blocksize); u64 bytenr, u32 blocksize);
struct btrfs_root *btrfs_lookup_fs_root(struct btrfs_fs_info *fs_info, struct btrfs_root *btrfs_lookup_fs_root(struct btrfs_fs_info *fs_info,
......
...@@ -8642,3 +8642,14 @@ int btrfs_remove_block_group(struct btrfs_trans_handle *trans, ...@@ -8642,3 +8642,14 @@ int btrfs_remove_block_group(struct btrfs_trans_handle *trans,
btrfs_free_path(path); btrfs_free_path(path);
return ret; return ret;
} }
int btrfs_error_unpin_extent_range(struct btrfs_root *root, u64 start, u64 end)
{
return unpin_extent_range(root, start, end);
}
int btrfs_error_discard_extent(struct btrfs_root *root, u64 bytenr,
u64 num_bytes)
{
return btrfs_discard_extent(root, bytenr, num_bytes);
}
...@@ -892,6 +892,17 @@ static ssize_t btrfs_file_aio_write(struct kiocb *iocb, ...@@ -892,6 +892,17 @@ static ssize_t btrfs_file_aio_write(struct kiocb *iocb,
if (err) if (err)
goto out; goto out;
/*
* If BTRFS flips readonly due to some impossible error
* (fs_info->fs_state now has BTRFS_SUPER_FLAG_ERROR),
* although we have opened a file as writable, we have
* to stop this write operation to ensure FS consistency.
*/
if (root->fs_info->fs_state & BTRFS_SUPER_FLAG_ERROR) {
err = -EROFS;
goto out;
}
file_update_time(file); file_update_time(file);
BTRFS_I(inode)->sequence++; BTRFS_I(inode)->sequence++;
......
...@@ -54,6 +54,90 @@ ...@@ -54,6 +54,90 @@
static const struct super_operations btrfs_super_ops; static const struct super_operations btrfs_super_ops;
static const char *btrfs_decode_error(struct btrfs_fs_info *fs_info, int errno,
char nbuf[16])
{
char *errstr = NULL;
switch (errno) {
case -EIO:
errstr = "IO failure";
break;
case -ENOMEM:
errstr = "Out of memory";
break;
case -EROFS:
errstr = "Readonly filesystem";
break;
default:
if (nbuf) {
if (snprintf(nbuf, 16, "error %d", -errno) >= 0)
errstr = nbuf;
}
break;
}
return errstr;
}
static void __save_error_info(struct btrfs_fs_info *fs_info)
{
/*
* today we only save the error info into ram. Long term we'll
* also send it down to the disk
*/
fs_info->fs_state = BTRFS_SUPER_FLAG_ERROR;
}
/* NOTE:
* We move write_super stuff at umount in order to avoid deadlock
* for umount hold all lock.
*/
static void save_error_info(struct btrfs_fs_info *fs_info)
{
__save_error_info(fs_info);
}
/* btrfs handle error by forcing the filesystem readonly */
static void btrfs_handle_error(struct btrfs_fs_info *fs_info)
{
struct super_block *sb = fs_info->sb;
if (sb->s_flags & MS_RDONLY)
return;
if (fs_info->fs_state & BTRFS_SUPER_FLAG_ERROR) {
sb->s_flags |= MS_RDONLY;
printk(KERN_INFO "btrfs is forced readonly\n");
}
}
/*
* __btrfs_std_error decodes expected errors from the caller and
* invokes the approciate error response.
*/
void __btrfs_std_error(struct btrfs_fs_info *fs_info, const char *function,
unsigned int line, int errno)
{
struct super_block *sb = fs_info->sb;
char nbuf[16];
const char *errstr;
/*
* Special case: if the error is EROFS, and we're already
* under MS_RDONLY, then it is safe here.
*/
if (errno == -EROFS && (sb->s_flags & MS_RDONLY))
return;
errstr = btrfs_decode_error(fs_info, errno, nbuf);
printk(KERN_CRIT "BTRFS error (device %s) in %s:%d: %s\n",
sb->s_id, function, line, errstr);
save_error_info(fs_info);
btrfs_handle_error(fs_info);
}
static void btrfs_put_super(struct super_block *sb) static void btrfs_put_super(struct super_block *sb)
{ {
struct btrfs_root *root = btrfs_sb(sb); struct btrfs_root *root = btrfs_sb(sb);
......
...@@ -181,6 +181,9 @@ static struct btrfs_trans_handle *start_transaction(struct btrfs_root *root, ...@@ -181,6 +181,9 @@ static struct btrfs_trans_handle *start_transaction(struct btrfs_root *root,
struct btrfs_trans_handle *h; struct btrfs_trans_handle *h;
struct btrfs_transaction *cur_trans; struct btrfs_transaction *cur_trans;
int ret; int ret;
if (root->fs_info->fs_state & BTRFS_SUPER_FLAG_ERROR)
return ERR_PTR(-EROFS);
again: again:
h = kmem_cache_alloc(btrfs_trans_handle_cachep, GFP_NOFS); h = kmem_cache_alloc(btrfs_trans_handle_cachep, GFP_NOFS);
if (!h) if (!h)
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment