mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
synced 2026-07-22 02:17:36 -04:00
Merge tag 'for-7.2-rc4-tag' of git://git.kernel.org/pub/scm/linux/kernel/git/kdave/linux
Pull btrfs fixes from David Sterba:
"I'm catching up with the fix backlog in the development branch, so
here's a number of them and will probably send one more for this or
the next rc:
- relocation fixes:
- skip attempting compression on reloc inodes
- exclude inline extents from file extent offset checks
- fix minor memory leak after error when adding reloc root
- fix root cleanup after inserting and merging
- fix clearing folio tags after writeback
- clear logging flag of extent map before splitting
- fix unsigned 32/64 type conversions when accounting dirty metadata,
leading to continually exceeding threshold
- fix regression in 32bit compat ioctl for subvolume info
- fix type of SEARCH_TREE ioctl buffer in UAPI header
- fix expression in ASSERT expression which can be unconditionally
evaluated on some compilers
- only account delalloc bytes for regular inodes"
* tag 'for-7.2-rc4-tag' of git://git.kernel.org/pub/scm/linux/kernel/git/kdave/linux:
btrfs: fix GET_SUBVOL_INFO after compat refactor
btrfs: free mapping node on duplicate reloc root insert
btrfs: fix a regression where PAGECACHE_TAG_DIRTY is never cleared
btrfs: don't propagate EXTENT_FLAG_LOGGING to split extent maps
btrfs: fix u32 to s64 type conversion in dirty_metadata_bytes accounting
btrfs: fix NULL pointer deref during assertion in btrfs_backref_free_node()
btrfs: only account delalloc bytes for regular file inodes in btrfs_getattr()
btrfs: reject inline file extents item in get_new_location()
btrfs: do not try compression for data reloc inodes
btrfs: declare btrfs_ioctl_search_args_v2::buf as __u8
btrfs: fix reloc root cleanup in merge_reloc_roots()
btrfs: fix use-after-free on reloc root after error in insert_dirty_subvol()
This commit is contained in:
@@ -3054,7 +3054,10 @@ void btrfs_backref_free_node(struct btrfs_backref_cache *cache,
|
||||
if (node) {
|
||||
ASSERT(list_empty(&node->list));
|
||||
ASSERT(list_empty(&node->lower));
|
||||
ASSERT(node->eb == NULL, "node->eb->start=%llu", node->eb->start);
|
||||
ASSERT(node->eb == NULL, "node->eb->start=%llu level=%d owner=%llu",
|
||||
node->eb ? node->eb->start : 0,
|
||||
node->eb ? btrfs_header_level(node->eb) : 0,
|
||||
node->eb ? btrfs_header_owner(node->eb) : 0);
|
||||
cache->nr_nodes--;
|
||||
btrfs_put_root(node->root);
|
||||
kfree(node);
|
||||
|
||||
@@ -476,6 +476,8 @@ static inline bool btrfs_inode_can_compress(const struct btrfs_inode *inode)
|
||||
if (inode->flags & BTRFS_INODE_NODATACOW ||
|
||||
inode->flags & BTRFS_INODE_NODATASUM)
|
||||
return false;
|
||||
if (btrfs_is_data_reloc_root(inode->root))
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@@ -2004,7 +2004,7 @@ static noinline_for_stack bool lock_extent_buffer_for_io(struct extent_buffer *e
|
||||
|
||||
btrfs_set_header_flag(eb, BTRFS_HEADER_FLAG_WRITTEN);
|
||||
percpu_counter_add_batch(&fs_info->dirty_metadata_bytes,
|
||||
-eb->len,
|
||||
-(s64)eb->len,
|
||||
fs_info->dirty_metadata_batch);
|
||||
ret = true;
|
||||
} else {
|
||||
@@ -3774,7 +3774,7 @@ void btrfs_clear_buffer_dirty(struct btrfs_trans_handle *trans,
|
||||
return;
|
||||
|
||||
buffer_tree_clear_mark(eb, PAGECACHE_TAG_DIRTY);
|
||||
percpu_counter_add_batch(&fs_info->dirty_metadata_bytes, -eb->len,
|
||||
percpu_counter_add_batch(&fs_info->dirty_metadata_bytes, -(s64)eb->len,
|
||||
fs_info->dirty_metadata_batch);
|
||||
|
||||
for (int i = 0; i < num_extent_folios(eb); i++) {
|
||||
|
||||
@@ -866,13 +866,13 @@ void btrfs_drop_extent_map_range(struct btrfs_inode *inode, u64 start, u64 end,
|
||||
goto next;
|
||||
}
|
||||
|
||||
flags = em->flags;
|
||||
/*
|
||||
* In case we split the extent map, we want to preserve the
|
||||
* EXTENT_FLAG_LOGGING flag on our extent map, but we don't want
|
||||
* it on the new extent maps.
|
||||
*/
|
||||
em->flags &= ~(EXTENT_FLAG_PINNED | EXTENT_FLAG_LOGGING);
|
||||
flags = em->flags & ~EXTENT_FLAG_LOGGING;
|
||||
em->flags &= ~EXTENT_FLAG_PINNED;
|
||||
modified = !list_empty(&em->list);
|
||||
|
||||
/*
|
||||
|
||||
@@ -8068,7 +8068,8 @@ static int btrfs_getattr(struct mnt_idmap *idmap,
|
||||
stat->result_mask |= STATX_SUBVOL;
|
||||
|
||||
spin_lock(&BTRFS_I(inode)->lock);
|
||||
delalloc_bytes = BTRFS_I(inode)->new_delalloc_bytes;
|
||||
delalloc_bytes = S_ISREG(inode->i_mode) ?
|
||||
BTRFS_I(inode)->new_delalloc_bytes : 0;
|
||||
inode_bytes = inode_get_bytes(inode);
|
||||
spin_unlock(&BTRFS_I(inode)->lock);
|
||||
stat->blocks = (ALIGN(inode_bytes, blocksize) +
|
||||
|
||||
@@ -2048,6 +2048,7 @@ static int _btrfs_ioctl_get_subvol_info(struct inode *inode,
|
||||
ret = -ENOENT;
|
||||
goto out;
|
||||
}
|
||||
ret = 0;
|
||||
}
|
||||
|
||||
out:
|
||||
|
||||
@@ -588,6 +588,7 @@ static int __add_reloc_root(struct btrfs_root *root, struct reloc_control *rc)
|
||||
btrfs_err(fs_info,
|
||||
"Duplicate root found for start=%llu while inserting into relocation tree",
|
||||
node->bytenr);
|
||||
kfree(node);
|
||||
return -EEXIST;
|
||||
}
|
||||
|
||||
@@ -890,6 +891,13 @@ static int get_new_location(struct inode *reloc_inode, u64 *new_bytenr,
|
||||
leaf = path->nodes[0];
|
||||
fi = btrfs_item_ptr(leaf, path->slots[0],
|
||||
struct btrfs_file_extent_item);
|
||||
if (unlikely(btrfs_file_extent_type(leaf, fi) == BTRFS_FILE_EXTENT_INLINE)) {
|
||||
btrfs_print_leaf(leaf);
|
||||
btrfs_err(fs_info,
|
||||
"unexpected inline file extent item for data reloc inode %llu key offset %llu",
|
||||
btrfs_ino(BTRFS_I(reloc_inode)), bytenr);
|
||||
return -EUCLEAN;
|
||||
}
|
||||
|
||||
/*
|
||||
* The cluster-boundary key searched above is always written by
|
||||
@@ -1518,6 +1526,17 @@ static int insert_dirty_subvol(struct btrfs_trans_handle *trans,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void clear_reloc_root(struct btrfs_root *root)
|
||||
{
|
||||
root->reloc_root = NULL;
|
||||
/*
|
||||
* Need barrier to ensure clear_bit() only happens after
|
||||
* root->reloc_root = NULL. Pairs with have_reloc_root().
|
||||
*/
|
||||
smp_wmb();
|
||||
clear_bit(BTRFS_ROOT_DEAD_RELOC_TREE, &root->state);
|
||||
}
|
||||
|
||||
static int clean_dirty_subvols(struct reloc_control *rc)
|
||||
{
|
||||
struct btrfs_root *root;
|
||||
@@ -1532,13 +1551,7 @@ static int clean_dirty_subvols(struct reloc_control *rc)
|
||||
struct btrfs_root *reloc_root = root->reloc_root;
|
||||
|
||||
list_del_init(&root->reloc_dirty_list);
|
||||
root->reloc_root = NULL;
|
||||
/*
|
||||
* Need barrier to ensure clear_bit() only happens after
|
||||
* root->reloc_root = NULL. Pairs with have_reloc_root.
|
||||
*/
|
||||
smp_wmb();
|
||||
clear_bit(BTRFS_ROOT_DEAD_RELOC_TREE, &root->state);
|
||||
clear_reloc_root(root);
|
||||
if (reloc_root) {
|
||||
/*
|
||||
* btrfs_drop_snapshot drops our ref we hold for
|
||||
@@ -1914,21 +1927,39 @@ void merge_reloc_roots(struct reloc_control *rc)
|
||||
goto out;
|
||||
}
|
||||
ret = merge_reloc_root(rc, root);
|
||||
btrfs_put_root(root);
|
||||
if (ret) {
|
||||
if (list_empty(&reloc_root->root_list))
|
||||
/*
|
||||
* Clear the reloc root since below we will call
|
||||
* free_reloc_roots(), otherwise we leave
|
||||
* root->reloc_root pointing to a freed reloc
|
||||
* root and trigger a use-after-free during
|
||||
* unmount or elsewhere.
|
||||
*/
|
||||
clear_reloc_root(root);
|
||||
btrfs_put_root(root);
|
||||
/*
|
||||
* We are adding the reloc_root to the local
|
||||
* reloc_roots list, so we add a ref for this
|
||||
* list which will be dropped below by the call
|
||||
* to free_reloc_roots().
|
||||
*/
|
||||
if (list_empty(&reloc_root->root_list)) {
|
||||
list_add_tail(&reloc_root->root_list,
|
||||
&reloc_roots);
|
||||
btrfs_grab_root(reloc_root);
|
||||
}
|
||||
/* Now drop the ref for root->reloc_root. */
|
||||
btrfs_put_root(reloc_root);
|
||||
goto out;
|
||||
}
|
||||
btrfs_put_root(root);
|
||||
} else {
|
||||
if (!IS_ERR(root)) {
|
||||
if (root->reloc_root == reloc_root) {
|
||||
root->reloc_root = NULL;
|
||||
clear_reloc_root(root);
|
||||
/* Drop the ref for root->reloc_root. */
|
||||
btrfs_put_root(reloc_root);
|
||||
}
|
||||
clear_bit(BTRFS_ROOT_DEAD_RELOC_TREE,
|
||||
&root->state);
|
||||
btrfs_put_root(root);
|
||||
}
|
||||
|
||||
|
||||
@@ -359,6 +359,23 @@ void btrfs_subpage_set_dirty(const struct btrfs_fs_info *fs_info,
|
||||
folio_mark_dirty(folio);
|
||||
}
|
||||
|
||||
static void folio_clear_tags(struct folio *folio)
|
||||
{
|
||||
struct address_space *mapping = folio_mapping(folio);
|
||||
XA_STATE(xas, &mapping->i_pages, folio->index);
|
||||
unsigned long flags;
|
||||
|
||||
ASSERT(folio_test_locked(folio));
|
||||
ASSERT(mapping);
|
||||
ASSERT(mapping_use_writeback_tags(mapping));
|
||||
|
||||
xas_lock_irqsave(&xas, flags);
|
||||
xas_load(&xas);
|
||||
xas_clear_mark(&xas, PAGECACHE_TAG_DIRTY);
|
||||
xas_clear_mark(&xas, PAGECACHE_TAG_TOWRITE);
|
||||
xas_unlock_irqrestore(&xas, flags);
|
||||
}
|
||||
|
||||
/*
|
||||
* Extra clear_and_test function for subpage dirty bitmap.
|
||||
*
|
||||
@@ -403,7 +420,6 @@ void btrfs_subpage_set_writeback(const struct btrfs_fs_info *fs_info,
|
||||
unsigned int start_bit = subpage_calc_start_bit(fs_info, folio,
|
||||
writeback, start, len);
|
||||
unsigned long flags;
|
||||
bool keep_write;
|
||||
|
||||
spin_lock_irqsave(&bfs->lock, flags);
|
||||
bitmap_set(bfs->bitmaps, start_bit, len >> fs_info->sectorsize_bits);
|
||||
@@ -413,10 +429,14 @@ void btrfs_subpage_set_writeback(const struct btrfs_fs_info *fs_info,
|
||||
* folio. Doing so can cause WB_SYNC_ALL writepages() to overlook it,
|
||||
* assume writeback is complete, and exit too early — violating sync
|
||||
* ordering guarantees.
|
||||
*
|
||||
* Instead we manually clear the DIRTY and TOWRITE tags after the folio
|
||||
* is no longer dirty.
|
||||
*/
|
||||
keep_write = folio_test_dirty(folio);
|
||||
if (!folio_test_writeback(folio))
|
||||
__folio_start_writeback(folio, keep_write);
|
||||
__folio_start_writeback(folio, true);
|
||||
if (!folio_test_dirty(folio))
|
||||
folio_clear_tags(folio);
|
||||
spin_unlock_irqrestore(&bfs->lock, flags);
|
||||
}
|
||||
|
||||
|
||||
@@ -598,7 +598,7 @@ struct btrfs_ioctl_search_args_v2 {
|
||||
__u64 buf_size; /* in - size of buffer
|
||||
* out - on EOVERFLOW: needed size
|
||||
* to store item */
|
||||
__u64 buf[]; /* out - found items */
|
||||
__u8 buf[]; /* out - found items */
|
||||
};
|
||||
|
||||
/* With a @src_length of zero, the range from @src_offset->EOF is cloned! */
|
||||
|
||||
Reference in New Issue
Block a user