1
0
mirror of https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git synced 2026-01-11 17:10:13 +00:00

for-6.19-rc4-tag

-----BEGIN PGP SIGNATURE-----
 
 iQIzBAABCgAdFiEE8rQSAMVO+zA4DBdWxWXV+ddtWDsFAmlb99EACgkQxWXV+ddt
 WDtJfQ//cppHAHSxb3NNDGXDiKx4ccCp9CWiOF7z+BTFfngsNGvbs2FzKFnYI2f3
 dT/DlPV8uBgVX3uYL3ZI1na/5MShXvS+sajIRhz3woyKBb2shVqVnFmfA8A3pKf6
 3Dfm6FWrJHGCgV28Oi5pbg/UQeTAHAmA2aPLYJKRnNwIq8pSSzDWRCVNFfYrt4o2
 7UUW1PzasZ7tuqL55HcwzuXjVTYr/t3puLjq+ydVfGSJSZlmlMd3pnZXz8S7/BC6
 jVQGOT6nK9SWCnfXD9plqqr4CY+ThJZJNSdhVTwfVxkxVHmEBWfqfhAToqZaLKX9
 co3rXvvZyIQf5KeHMmtbb2P736zaAcKb7G41liRN7EZg/gOsROE+UziYRkTg+Xyg
 rztTksc913DsuHj19sZhIgcKRcym2h57wyZyt7vYAdsv9uksLUgKUo3U9CiTbEsb
 8d/vgt1e3+ELoVcc+xVZSSGRDVzvZnxVmRHQV2dAtIXK34FXzqCDeKnFG0wsjqtF
 Kw6bV93cXLohfcB7fPPBdAHzVN89kfUXTBT8mrri7HnjSnZTJNeHrGpcRNNQ76BT
 8RL6gSP32Mpo9HZOYYhl1Xj2hRonRiJrUQAb6x9CY1MMUP2vwVvVBUVj2NAohWdM
 vAYwRQDigw92RoKIYvHu+X+E5PXgX2AQ9NV8qiL79od+A7NFLgY=
 =hmbc
 -----END PGP SIGNATURE-----

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

Pull btrfs fixes from David Sterba:

 - fix potential deadlock due to mismatching transaction states when
   waiting for the current transaction

 - fix squota accounting with nested snapshots

 - fix quota inheritance of qgroups with multiple parent qgroups

 - fix NULL inode pointer in evict tracepoint

 - fix writes beyond end of file on systems with 64K page size and 4K
   block size

 - fix logging of inodes after exchange rename

 - fix use after free when using ref_tracker feature

 - space reservation fixes

* tag 'for-6.19-rc4-tag' of git://git.kernel.org/pub/scm/linux/kernel/git/kdave/linux:
  btrfs: fix reservation leak in some error paths when inserting inline extent
  btrfs: do not free data reservation in fallback from inline due to -ENOSPC
  btrfs: fix use-after-free warning in btrfs_get_or_create_delayed_node()
  btrfs: always detect conflicting inodes when logging inode refs
  btrfs: fix beyond-EOF write handling
  btrfs: fix deadlock in wait_current_trans() due to ignored transaction type
  btrfs: fix NULL dereference on root when tracing inode eviction
  btrfs: qgroup: update all parent qgroups when doing quick inherit
  btrfs: fix qgroup_snapshot_quick_inherit() squota bug
This commit is contained in:
Linus Torvalds 2026-01-05 14:10:48 -08:00
commit 7f98ab9da0
7 changed files with 65 additions and 38 deletions

View File

@ -152,37 +152,39 @@ again:
return ERR_PTR(-ENOMEM); return ERR_PTR(-ENOMEM);
btrfs_init_delayed_node(node, root, ino); btrfs_init_delayed_node(node, root, ino);
/* Cached in the inode and can be accessed. */
refcount_set(&node->refs, 2);
btrfs_delayed_node_ref_tracker_alloc(node, tracker, GFP_NOFS);
btrfs_delayed_node_ref_tracker_alloc(node, &node->inode_cache_tracker, GFP_NOFS);
/* Allocate and reserve the slot, from now it can return a NULL from xa_load(). */ /* Allocate and reserve the slot, from now it can return a NULL from xa_load(). */
ret = xa_reserve(&root->delayed_nodes, ino, GFP_NOFS); ret = xa_reserve(&root->delayed_nodes, ino, GFP_NOFS);
if (ret == -ENOMEM) { if (ret == -ENOMEM)
btrfs_delayed_node_ref_tracker_dir_exit(node); goto cleanup;
kmem_cache_free(delayed_node_cache, node);
return ERR_PTR(-ENOMEM);
}
xa_lock(&root->delayed_nodes); xa_lock(&root->delayed_nodes);
ptr = xa_load(&root->delayed_nodes, ino); ptr = xa_load(&root->delayed_nodes, ino);
if (ptr) { if (ptr) {
/* Somebody inserted it, go back and read it. */ /* Somebody inserted it, go back and read it. */
xa_unlock(&root->delayed_nodes); xa_unlock(&root->delayed_nodes);
btrfs_delayed_node_ref_tracker_dir_exit(node); goto cleanup;
kmem_cache_free(delayed_node_cache, node);
node = NULL;
goto again;
} }
ptr = __xa_store(&root->delayed_nodes, ino, node, GFP_ATOMIC); ptr = __xa_store(&root->delayed_nodes, ino, node, GFP_ATOMIC);
ASSERT(xa_err(ptr) != -EINVAL); ASSERT(xa_err(ptr) != -EINVAL);
ASSERT(xa_err(ptr) != -ENOMEM); ASSERT(xa_err(ptr) != -ENOMEM);
ASSERT(ptr == NULL); ASSERT(ptr == NULL);
/* Cached in the inode and can be accessed. */
refcount_set(&node->refs, 2);
btrfs_delayed_node_ref_tracker_alloc(node, tracker, GFP_ATOMIC);
btrfs_delayed_node_ref_tracker_alloc(node, &node->inode_cache_tracker, GFP_ATOMIC);
btrfs_inode->delayed_node = node; btrfs_inode->delayed_node = node;
xa_unlock(&root->delayed_nodes); xa_unlock(&root->delayed_nodes);
return node; return node;
cleanup:
btrfs_delayed_node_ref_tracker_free(node, tracker);
btrfs_delayed_node_ref_tracker_free(node, &node->inode_cache_tracker);
btrfs_delayed_node_ref_tracker_dir_exit(node);
kmem_cache_free(delayed_node_cache, node);
if (ret)
return ERR_PTR(ret);
goto again;
} }
/* /*

View File

@ -1728,7 +1728,7 @@ static noinline_for_stack int extent_writepage_io(struct btrfs_inode *inode,
struct btrfs_ordered_extent *ordered; struct btrfs_ordered_extent *ordered;
ordered = btrfs_lookup_first_ordered_range(inode, cur, ordered = btrfs_lookup_first_ordered_range(inode, cur,
folio_end - cur); fs_info->sectorsize);
/* /*
* We have just run delalloc before getting here, so * We have just run delalloc before getting here, so
* there must be an ordered extent. * there must be an ordered extent.
@ -1742,7 +1742,7 @@ static noinline_for_stack int extent_writepage_io(struct btrfs_inode *inode,
btrfs_put_ordered_extent(ordered); btrfs_put_ordered_extent(ordered);
btrfs_mark_ordered_io_finished(inode, folio, cur, btrfs_mark_ordered_io_finished(inode, folio, cur,
end - cur, true); fs_info->sectorsize, true);
/* /*
* This range is beyond i_size, thus we don't need to * This range is beyond i_size, thus we don't need to
* bother writing back. * bother writing back.
@ -1751,8 +1751,8 @@ static noinline_for_stack int extent_writepage_io(struct btrfs_inode *inode,
* writeback the sectors with subpage dirty bits, * writeback the sectors with subpage dirty bits,
* causing writeback without ordered extent. * causing writeback without ordered extent.
*/ */
btrfs_folio_clear_dirty(fs_info, folio, cur, end - cur); btrfs_folio_clear_dirty(fs_info, folio, cur, fs_info->sectorsize);
break; continue;
} }
ret = submit_one_sector(inode, folio, cur, bio_ctrl, i_size); ret = submit_one_sector(inode, folio, cur, bio_ctrl, i_size);
if (unlikely(ret < 0)) { if (unlikely(ret < 0)) {

View File

@ -618,19 +618,22 @@ static noinline int __cow_file_range_inline(struct btrfs_inode *inode,
struct btrfs_drop_extents_args drop_args = { 0 }; struct btrfs_drop_extents_args drop_args = { 0 };
struct btrfs_root *root = inode->root; struct btrfs_root *root = inode->root;
struct btrfs_fs_info *fs_info = root->fs_info; struct btrfs_fs_info *fs_info = root->fs_info;
struct btrfs_trans_handle *trans; struct btrfs_trans_handle *trans = NULL;
u64 data_len = (compressed_size ?: size); u64 data_len = (compressed_size ?: size);
int ret; int ret;
struct btrfs_path *path; struct btrfs_path *path;
path = btrfs_alloc_path(); path = btrfs_alloc_path();
if (!path) if (!path) {
return -ENOMEM; ret = -ENOMEM;
goto out;
}
trans = btrfs_join_transaction(root); trans = btrfs_join_transaction(root);
if (IS_ERR(trans)) { if (IS_ERR(trans)) {
btrfs_free_path(path); ret = PTR_ERR(trans);
return PTR_ERR(trans); trans = NULL;
goto out;
} }
trans->block_rsv = &inode->block_rsv; trans->block_rsv = &inode->block_rsv;
@ -674,10 +677,15 @@ out:
* it won't count as data extent, free them directly here. * it won't count as data extent, free them directly here.
* And at reserve time, it's always aligned to page size, so * And at reserve time, it's always aligned to page size, so
* just free one page here. * just free one page here.
*
* If we fallback to non-inline (ret == 1) due to -ENOSPC, then we need
* to keep the data reservation.
*/ */
btrfs_qgroup_free_data(inode, NULL, 0, fs_info->sectorsize, NULL); if (ret <= 0)
btrfs_qgroup_free_data(inode, NULL, 0, fs_info->sectorsize, NULL);
btrfs_free_path(path); btrfs_free_path(path);
btrfs_end_transaction(trans); if (trans)
btrfs_end_transaction(trans);
return ret; return ret;
} }

View File

@ -3208,9 +3208,15 @@ static int qgroup_snapshot_quick_inherit(struct btrfs_fs_info *fs_info,
{ {
struct btrfs_qgroup *src; struct btrfs_qgroup *src;
struct btrfs_qgroup *parent; struct btrfs_qgroup *parent;
struct btrfs_qgroup *qgroup;
struct btrfs_qgroup_list *list; struct btrfs_qgroup_list *list;
LIST_HEAD(qgroup_list);
const u32 nodesize = fs_info->nodesize;
int nr_parents = 0; int nr_parents = 0;
if (btrfs_qgroup_mode(fs_info) != BTRFS_QGROUP_MODE_FULL)
return 0;
src = find_qgroup_rb(fs_info, srcid); src = find_qgroup_rb(fs_info, srcid);
if (!src) if (!src)
return -ENOENT; return -ENOENT;
@ -3245,8 +3251,19 @@ static int qgroup_snapshot_quick_inherit(struct btrfs_fs_info *fs_info,
if (parent->excl != parent->rfer) if (parent->excl != parent->rfer)
return 1; return 1;
parent->excl += fs_info->nodesize; qgroup_iterator_add(&qgroup_list, parent);
parent->rfer += fs_info->nodesize; list_for_each_entry(qgroup, &qgroup_list, iterator) {
qgroup->rfer += nodesize;
qgroup->rfer_cmpr += nodesize;
qgroup->excl += nodesize;
qgroup->excl_cmpr += nodesize;
qgroup_dirty(fs_info, qgroup);
/* Append parent qgroups to @qgroup_list. */
list_for_each_entry(list, &qgroup->groups, next_group)
qgroup_iterator_add(&qgroup_list, list->group);
}
qgroup_iterator_clean(&qgroup_list);
return 0; return 0;
} }

View File

@ -520,13 +520,14 @@ static inline int is_transaction_blocked(struct btrfs_transaction *trans)
* when this is done, it is safe to start a new transaction, but the current * when this is done, it is safe to start a new transaction, but the current
* transaction might not be fully on disk. * transaction might not be fully on disk.
*/ */
static void wait_current_trans(struct btrfs_fs_info *fs_info) static void wait_current_trans(struct btrfs_fs_info *fs_info, unsigned int type)
{ {
struct btrfs_transaction *cur_trans; struct btrfs_transaction *cur_trans;
spin_lock(&fs_info->trans_lock); spin_lock(&fs_info->trans_lock);
cur_trans = fs_info->running_transaction; cur_trans = fs_info->running_transaction;
if (cur_trans && is_transaction_blocked(cur_trans)) { if (cur_trans && is_transaction_blocked(cur_trans) &&
(btrfs_blocked_trans_types[cur_trans->state] & type)) {
refcount_inc(&cur_trans->use_count); refcount_inc(&cur_trans->use_count);
spin_unlock(&fs_info->trans_lock); spin_unlock(&fs_info->trans_lock);
@ -701,12 +702,12 @@ again:
sb_start_intwrite(fs_info->sb); sb_start_intwrite(fs_info->sb);
if (may_wait_transaction(fs_info, type)) if (may_wait_transaction(fs_info, type))
wait_current_trans(fs_info); wait_current_trans(fs_info, type);
do { do {
ret = join_transaction(fs_info, type); ret = join_transaction(fs_info, type);
if (ret == -EBUSY) { if (ret == -EBUSY) {
wait_current_trans(fs_info); wait_current_trans(fs_info, type);
if (unlikely(type == TRANS_ATTACH || if (unlikely(type == TRANS_ATTACH ||
type == TRANS_JOIN_NOSTART)) type == TRANS_JOIN_NOSTART))
ret = -ENOENT; ret = -ENOENT;
@ -1003,7 +1004,7 @@ out:
void btrfs_throttle(struct btrfs_fs_info *fs_info) void btrfs_throttle(struct btrfs_fs_info *fs_info)
{ {
wait_current_trans(fs_info); wait_current_trans(fs_info, TRANS_START);
} }
bool btrfs_should_end_transaction(struct btrfs_trans_handle *trans) bool btrfs_should_end_transaction(struct btrfs_trans_handle *trans)

View File

@ -6341,10 +6341,8 @@ again:
* and no keys greater than that, so bail out. * and no keys greater than that, so bail out.
*/ */
break; break;
} else if ((min_key->type == BTRFS_INODE_REF_KEY || } else if (min_key->type == BTRFS_INODE_REF_KEY ||
min_key->type == BTRFS_INODE_EXTREF_KEY) && min_key->type == BTRFS_INODE_EXTREF_KEY) {
(inode->generation == trans->transid ||
ctx->logging_conflict_inodes)) {
u64 other_ino = 0; u64 other_ino = 0;
u64 other_parent = 0; u64 other_parent = 0;

View File

@ -224,7 +224,8 @@ DECLARE_EVENT_CLASS(btrfs__inode,
__entry->generation = BTRFS_I(inode)->generation; __entry->generation = BTRFS_I(inode)->generation;
__entry->last_trans = BTRFS_I(inode)->last_trans; __entry->last_trans = BTRFS_I(inode)->last_trans;
__entry->logged_trans = BTRFS_I(inode)->logged_trans; __entry->logged_trans = BTRFS_I(inode)->logged_trans;
__entry->root_objectid = btrfs_root_id(BTRFS_I(inode)->root); __entry->root_objectid = BTRFS_I(inode)->root ?
btrfs_root_id(BTRFS_I(inode)->root) : 0;
), ),
TP_printk_btrfs("root=%llu(%s) gen=%llu ino=%llu blocks=%llu " TP_printk_btrfs("root=%llu(%s) gen=%llu ino=%llu blocks=%llu "