Commit 6e68b996 authored by Linus Torvalds's avatar Linus Torvalds

Merge tag 'for-5.11-rc3-tag' of git://git.kernel.org/pub/scm/linux/kernel/git/kdave/linux

Pull btrfs fixes from David Sterba:
 "More material for stable trees.

   - tree-checker: check item end overflow

   - fix false warning during relocation regarding extent type

   - fix inode flushing logic, caused notable performance regression
     (since 5.10)

   - debugging fixups:
      - print correct offset for reloc tree key
      - pass reliable fs_info pointer to error reporting helper"

* tag 'for-5.11-rc3-tag' of git://git.kernel.org/pub/scm/linux/kernel/git/kdave/linux:
  btrfs: shrink delalloc pages instead of full inodes
  btrfs: reloc: fix wrong file extent type check to avoid false ENOENT
  btrfs: tree-checker: check if chunk item end overflows
  btrfs: prevent NULL pointer dereference in extent_io_tree_panic
  btrfs: print the actual offset in btrfs_root_name
parents c912fd05 e076ab2a
...@@ -1457,7 +1457,7 @@ void btrfs_check_leaked_roots(struct btrfs_fs_info *fs_info) ...@@ -1457,7 +1457,7 @@ void btrfs_check_leaked_roots(struct btrfs_fs_info *fs_info)
root = list_first_entry(&fs_info->allocated_roots, root = list_first_entry(&fs_info->allocated_roots,
struct btrfs_root, leak_list); struct btrfs_root, leak_list);
btrfs_err(fs_info, "leaked root %s refcount %d", btrfs_err(fs_info, "leaked root %s refcount %d",
btrfs_root_name(root->root_key.objectid, buf), btrfs_root_name(&root->root_key, buf),
refcount_read(&root->refs)); refcount_read(&root->refs));
while (refcount_read(&root->refs) > 1) while (refcount_read(&root->refs) > 1)
btrfs_put_root(root); btrfs_put_root(root);
......
...@@ -676,9 +676,7 @@ alloc_extent_state_atomic(struct extent_state *prealloc) ...@@ -676,9 +676,7 @@ alloc_extent_state_atomic(struct extent_state *prealloc)
static void extent_io_tree_panic(struct extent_io_tree *tree, int err) static void extent_io_tree_panic(struct extent_io_tree *tree, int err)
{ {
struct inode *inode = tree->private_data; btrfs_panic(tree->fs_info, err,
btrfs_panic(btrfs_sb(inode->i_sb), err,
"locking error: extent tree was modified by another thread while locked"); "locking error: extent tree was modified by another thread while locked");
} }
......
...@@ -9390,7 +9390,8 @@ static struct btrfs_delalloc_work *btrfs_alloc_delalloc_work(struct inode *inode ...@@ -9390,7 +9390,8 @@ static struct btrfs_delalloc_work *btrfs_alloc_delalloc_work(struct inode *inode
* some fairly slow code that needs optimization. This walks the list * some fairly slow code that needs optimization. This walks the list
* of all the inodes with pending delalloc and forces them to disk. * of all the inodes with pending delalloc and forces them to disk.
*/ */
static int start_delalloc_inodes(struct btrfs_root *root, u64 *nr, bool snapshot, static int start_delalloc_inodes(struct btrfs_root *root,
struct writeback_control *wbc, bool snapshot,
bool in_reclaim_context) bool in_reclaim_context)
{ {
struct btrfs_inode *binode; struct btrfs_inode *binode;
...@@ -9399,6 +9400,7 @@ static int start_delalloc_inodes(struct btrfs_root *root, u64 *nr, bool snapshot ...@@ -9399,6 +9400,7 @@ static int start_delalloc_inodes(struct btrfs_root *root, u64 *nr, bool snapshot
struct list_head works; struct list_head works;
struct list_head splice; struct list_head splice;
int ret = 0; int ret = 0;
bool full_flush = wbc->nr_to_write == LONG_MAX;
INIT_LIST_HEAD(&works); INIT_LIST_HEAD(&works);
INIT_LIST_HEAD(&splice); INIT_LIST_HEAD(&splice);
...@@ -9427,18 +9429,24 @@ static int start_delalloc_inodes(struct btrfs_root *root, u64 *nr, bool snapshot ...@@ -9427,18 +9429,24 @@ static int start_delalloc_inodes(struct btrfs_root *root, u64 *nr, bool snapshot
if (snapshot) if (snapshot)
set_bit(BTRFS_INODE_SNAPSHOT_FLUSH, set_bit(BTRFS_INODE_SNAPSHOT_FLUSH,
&binode->runtime_flags); &binode->runtime_flags);
work = btrfs_alloc_delalloc_work(inode); if (full_flush) {
if (!work) { work = btrfs_alloc_delalloc_work(inode);
iput(inode); if (!work) {
ret = -ENOMEM; iput(inode);
goto out; ret = -ENOMEM;
} goto out;
list_add_tail(&work->list, &works); }
btrfs_queue_work(root->fs_info->flush_workers, list_add_tail(&work->list, &works);
&work->work); btrfs_queue_work(root->fs_info->flush_workers,
if (*nr != U64_MAX) { &work->work);
(*nr)--; } else {
if (*nr == 0) ret = sync_inode(inode, wbc);
if (!ret &&
test_bit(BTRFS_INODE_HAS_ASYNC_EXTENT,
&BTRFS_I(inode)->runtime_flags))
ret = sync_inode(inode, wbc);
btrfs_add_delayed_iput(inode);
if (ret || wbc->nr_to_write <= 0)
goto out; goto out;
} }
cond_resched(); cond_resched();
...@@ -9464,18 +9472,29 @@ static int start_delalloc_inodes(struct btrfs_root *root, u64 *nr, bool snapshot ...@@ -9464,18 +9472,29 @@ static int start_delalloc_inodes(struct btrfs_root *root, u64 *nr, bool snapshot
int btrfs_start_delalloc_snapshot(struct btrfs_root *root) int btrfs_start_delalloc_snapshot(struct btrfs_root *root)
{ {
struct writeback_control wbc = {
.nr_to_write = LONG_MAX,
.sync_mode = WB_SYNC_NONE,
.range_start = 0,
.range_end = LLONG_MAX,
};
struct btrfs_fs_info *fs_info = root->fs_info; struct btrfs_fs_info *fs_info = root->fs_info;
u64 nr = U64_MAX;
if (test_bit(BTRFS_FS_STATE_ERROR, &fs_info->fs_state)) if (test_bit(BTRFS_FS_STATE_ERROR, &fs_info->fs_state))
return -EROFS; return -EROFS;
return start_delalloc_inodes(root, &nr, true, false); return start_delalloc_inodes(root, &wbc, true, false);
} }
int btrfs_start_delalloc_roots(struct btrfs_fs_info *fs_info, u64 nr, int btrfs_start_delalloc_roots(struct btrfs_fs_info *fs_info, u64 nr,
bool in_reclaim_context) bool in_reclaim_context)
{ {
struct writeback_control wbc = {
.nr_to_write = (nr == U64_MAX) ? LONG_MAX : (unsigned long)nr,
.sync_mode = WB_SYNC_NONE,
.range_start = 0,
.range_end = LLONG_MAX,
};
struct btrfs_root *root; struct btrfs_root *root;
struct list_head splice; struct list_head splice;
int ret; int ret;
...@@ -9489,6 +9508,13 @@ int btrfs_start_delalloc_roots(struct btrfs_fs_info *fs_info, u64 nr, ...@@ -9489,6 +9508,13 @@ int btrfs_start_delalloc_roots(struct btrfs_fs_info *fs_info, u64 nr,
spin_lock(&fs_info->delalloc_root_lock); spin_lock(&fs_info->delalloc_root_lock);
list_splice_init(&fs_info->delalloc_roots, &splice); list_splice_init(&fs_info->delalloc_roots, &splice);
while (!list_empty(&splice) && nr) { while (!list_empty(&splice) && nr) {
/*
* Reset nr_to_write here so we know that we're doing a full
* flush.
*/
if (nr == U64_MAX)
wbc.nr_to_write = LONG_MAX;
root = list_first_entry(&splice, struct btrfs_root, root = list_first_entry(&splice, struct btrfs_root,
delalloc_root); delalloc_root);
root = btrfs_grab_root(root); root = btrfs_grab_root(root);
...@@ -9497,9 +9523,9 @@ int btrfs_start_delalloc_roots(struct btrfs_fs_info *fs_info, u64 nr, ...@@ -9497,9 +9523,9 @@ int btrfs_start_delalloc_roots(struct btrfs_fs_info *fs_info, u64 nr,
&fs_info->delalloc_roots); &fs_info->delalloc_roots);
spin_unlock(&fs_info->delalloc_root_lock); spin_unlock(&fs_info->delalloc_root_lock);
ret = start_delalloc_inodes(root, &nr, false, in_reclaim_context); ret = start_delalloc_inodes(root, &wbc, false, in_reclaim_context);
btrfs_put_root(root); btrfs_put_root(root);
if (ret < 0) if (ret < 0 || wbc.nr_to_write <= 0)
goto out; goto out;
spin_lock(&fs_info->delalloc_root_lock); spin_lock(&fs_info->delalloc_root_lock);
} }
......
...@@ -26,22 +26,22 @@ static const struct root_name_map root_map[] = { ...@@ -26,22 +26,22 @@ static const struct root_name_map root_map[] = {
{ BTRFS_DATA_RELOC_TREE_OBJECTID, "DATA_RELOC_TREE" }, { BTRFS_DATA_RELOC_TREE_OBJECTID, "DATA_RELOC_TREE" },
}; };
const char *btrfs_root_name(u64 objectid, char *buf) const char *btrfs_root_name(const struct btrfs_key *key, char *buf)
{ {
int i; int i;
if (objectid == BTRFS_TREE_RELOC_OBJECTID) { if (key->objectid == BTRFS_TREE_RELOC_OBJECTID) {
snprintf(buf, BTRFS_ROOT_NAME_BUF_LEN, snprintf(buf, BTRFS_ROOT_NAME_BUF_LEN,
"TREE_RELOC offset=%llu", objectid); "TREE_RELOC offset=%llu", key->offset);
return buf; return buf;
} }
for (i = 0; i < ARRAY_SIZE(root_map); i++) { for (i = 0; i < ARRAY_SIZE(root_map); i++) {
if (root_map[i].id == objectid) if (root_map[i].id == key->objectid)
return root_map[i].name; return root_map[i].name;
} }
snprintf(buf, BTRFS_ROOT_NAME_BUF_LEN, "%llu", objectid); snprintf(buf, BTRFS_ROOT_NAME_BUF_LEN, "%llu", key->objectid);
return buf; return buf;
} }
......
...@@ -11,6 +11,6 @@ ...@@ -11,6 +11,6 @@
void btrfs_print_leaf(struct extent_buffer *l); void btrfs_print_leaf(struct extent_buffer *l);
void btrfs_print_tree(struct extent_buffer *c, bool follow); void btrfs_print_tree(struct extent_buffer *c, bool follow);
const char *btrfs_root_name(u64 objectid, char *buf); const char *btrfs_root_name(const struct btrfs_key *key, char *buf);
#endif #endif
...@@ -2975,11 +2975,16 @@ static int delete_v1_space_cache(struct extent_buffer *leaf, ...@@ -2975,11 +2975,16 @@ static int delete_v1_space_cache(struct extent_buffer *leaf,
return 0; return 0;
for (i = 0; i < btrfs_header_nritems(leaf); i++) { for (i = 0; i < btrfs_header_nritems(leaf); i++) {
u8 type;
btrfs_item_key_to_cpu(leaf, &key, i); btrfs_item_key_to_cpu(leaf, &key, i);
if (key.type != BTRFS_EXTENT_DATA_KEY) if (key.type != BTRFS_EXTENT_DATA_KEY)
continue; continue;
ei = btrfs_item_ptr(leaf, i, struct btrfs_file_extent_item); ei = btrfs_item_ptr(leaf, i, struct btrfs_file_extent_item);
if (btrfs_file_extent_type(leaf, ei) == BTRFS_FILE_EXTENT_REG && type = btrfs_file_extent_type(leaf, ei);
if ((type == BTRFS_FILE_EXTENT_REG ||
type == BTRFS_FILE_EXTENT_PREALLOC) &&
btrfs_file_extent_disk_bytenr(leaf, ei) == data_bytenr) { btrfs_file_extent_disk_bytenr(leaf, ei) == data_bytenr) {
found = true; found = true;
space_cache_ino = key.objectid; space_cache_ino = key.objectid;
......
...@@ -532,7 +532,9 @@ static void shrink_delalloc(struct btrfs_fs_info *fs_info, ...@@ -532,7 +532,9 @@ static void shrink_delalloc(struct btrfs_fs_info *fs_info,
loops = 0; loops = 0;
while ((delalloc_bytes || dio_bytes) && loops < 3) { while ((delalloc_bytes || dio_bytes) && loops < 3) {
btrfs_start_delalloc_roots(fs_info, items, true); u64 nr_pages = min(delalloc_bytes, to_reclaim) >> PAGE_SHIFT;
btrfs_start_delalloc_roots(fs_info, nr_pages, true);
loops++; loops++;
if (wait_ordered && !trans) { if (wait_ordered && !trans) {
......
...@@ -760,6 +760,7 @@ int btrfs_check_chunk_valid(struct extent_buffer *leaf, ...@@ -760,6 +760,7 @@ int btrfs_check_chunk_valid(struct extent_buffer *leaf,
{ {
struct btrfs_fs_info *fs_info = leaf->fs_info; struct btrfs_fs_info *fs_info = leaf->fs_info;
u64 length; u64 length;
u64 chunk_end;
u64 stripe_len; u64 stripe_len;
u16 num_stripes; u16 num_stripes;
u16 sub_stripes; u16 sub_stripes;
...@@ -814,6 +815,12 @@ int btrfs_check_chunk_valid(struct extent_buffer *leaf, ...@@ -814,6 +815,12 @@ int btrfs_check_chunk_valid(struct extent_buffer *leaf,
"invalid chunk length, have %llu", length); "invalid chunk length, have %llu", length);
return -EUCLEAN; return -EUCLEAN;
} }
if (unlikely(check_add_overflow(logical, length, &chunk_end))) {
chunk_err(leaf, chunk, logical,
"invalid chunk logical start and length, have logical start %llu length %llu",
logical, length);
return -EUCLEAN;
}
if (unlikely(!is_power_of_2(stripe_len) || stripe_len != BTRFS_STRIPE_LEN)) { if (unlikely(!is_power_of_2(stripe_len) || stripe_len != BTRFS_STRIPE_LEN)) {
chunk_err(leaf, chunk, logical, chunk_err(leaf, chunk, logical,
"invalid chunk stripe length: %llu", "invalid chunk stripe length: %llu",
......
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