Commit eb952c47 authored by Linus Torvalds's avatar Linus Torvalds

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

Pull btrfs fixes from David Sterba:

 - update fstrim loop and add more cancellation points, fix reported
   delayed or blocked suspend if there's a huge chunk queued

 - fix error handling in recent qgroup xarray conversion

 - in zoned mode, fix warning printing device path without RCU
   protection

 - again fix invalid extent xarray state (6252690f), lost due to
   refactoring

* tag 'for-6.12-rc2-tag' of git://git.kernel.org/pub/scm/linux/kernel/git/kdave/linux:
  btrfs: fix clear_dirty and writeback ordering in submit_one_sector()
  btrfs: zoned: fix missing RCU locking in error message when loading zone info
  btrfs: fix missing error handling when adding delayed ref with qgroups enabled
  btrfs: add cancellation points to trim loops
  btrfs: split remaining space to discard in chunks
parents 5870963f e761be2a
...@@ -840,6 +840,8 @@ static void init_delayed_ref_head(struct btrfs_delayed_ref_head *head_ref, ...@@ -840,6 +840,8 @@ static void init_delayed_ref_head(struct btrfs_delayed_ref_head *head_ref,
* helper function to actually insert a head node into the rbtree. * helper function to actually insert a head node into the rbtree.
* this does all the dirty work in terms of maintaining the correct * this does all the dirty work in terms of maintaining the correct
* overall modification count. * overall modification count.
*
* Returns an error pointer in case of an error.
*/ */
static noinline struct btrfs_delayed_ref_head * static noinline struct btrfs_delayed_ref_head *
add_delayed_ref_head(struct btrfs_trans_handle *trans, add_delayed_ref_head(struct btrfs_trans_handle *trans,
...@@ -862,6 +864,9 @@ add_delayed_ref_head(struct btrfs_trans_handle *trans, ...@@ -862,6 +864,9 @@ add_delayed_ref_head(struct btrfs_trans_handle *trans,
if (ret) { if (ret) {
/* Clean up if insertion fails or item exists. */ /* Clean up if insertion fails or item exists. */
xa_release(&delayed_refs->dirty_extents, qrecord->bytenr); xa_release(&delayed_refs->dirty_extents, qrecord->bytenr);
/* Caller responsible for freeing qrecord on error. */
if (ret < 0)
return ERR_PTR(ret);
kfree(qrecord); kfree(qrecord);
} else { } else {
qrecord_inserted = true; qrecord_inserted = true;
...@@ -1000,27 +1005,35 @@ static int add_delayed_ref(struct btrfs_trans_handle *trans, ...@@ -1000,27 +1005,35 @@ static int add_delayed_ref(struct btrfs_trans_handle *trans,
struct btrfs_fs_info *fs_info = trans->fs_info; struct btrfs_fs_info *fs_info = trans->fs_info;
struct btrfs_delayed_ref_node *node; struct btrfs_delayed_ref_node *node;
struct btrfs_delayed_ref_head *head_ref; struct btrfs_delayed_ref_head *head_ref;
struct btrfs_delayed_ref_head *new_head_ref;
struct btrfs_delayed_ref_root *delayed_refs; struct btrfs_delayed_ref_root *delayed_refs;
struct btrfs_qgroup_extent_record *record = NULL; struct btrfs_qgroup_extent_record *record = NULL;
bool qrecord_inserted; bool qrecord_inserted;
int action = generic_ref->action; int action = generic_ref->action;
bool merged; bool merged;
int ret;
node = kmem_cache_alloc(btrfs_delayed_ref_node_cachep, GFP_NOFS); node = kmem_cache_alloc(btrfs_delayed_ref_node_cachep, GFP_NOFS);
if (!node) if (!node)
return -ENOMEM; return -ENOMEM;
head_ref = kmem_cache_alloc(btrfs_delayed_ref_head_cachep, GFP_NOFS); head_ref = kmem_cache_alloc(btrfs_delayed_ref_head_cachep, GFP_NOFS);
if (!head_ref) if (!head_ref) {
ret = -ENOMEM;
goto free_node; goto free_node;
}
if (btrfs_qgroup_full_accounting(fs_info) && !generic_ref->skip_qgroup) { if (btrfs_qgroup_full_accounting(fs_info) && !generic_ref->skip_qgroup) {
record = kzalloc(sizeof(*record), GFP_NOFS); record = kzalloc(sizeof(*record), GFP_NOFS);
if (!record) if (!record) {
ret = -ENOMEM;
goto free_head_ref; goto free_head_ref;
}
if (xa_reserve(&trans->transaction->delayed_refs.dirty_extents, if (xa_reserve(&trans->transaction->delayed_refs.dirty_extents,
generic_ref->bytenr, GFP_NOFS)) generic_ref->bytenr, GFP_NOFS)) {
ret = -ENOMEM;
goto free_record; goto free_record;
}
} }
init_delayed_ref_common(fs_info, node, generic_ref); init_delayed_ref_common(fs_info, node, generic_ref);
...@@ -1034,8 +1047,14 @@ static int add_delayed_ref(struct btrfs_trans_handle *trans, ...@@ -1034,8 +1047,14 @@ static int add_delayed_ref(struct btrfs_trans_handle *trans,
* insert both the head node and the new ref without dropping * insert both the head node and the new ref without dropping
* the spin lock * the spin lock
*/ */
head_ref = add_delayed_ref_head(trans, head_ref, record, new_head_ref = add_delayed_ref_head(trans, head_ref, record,
action, &qrecord_inserted); action, &qrecord_inserted);
if (IS_ERR(new_head_ref)) {
spin_unlock(&delayed_refs->lock);
ret = PTR_ERR(new_head_ref);
goto free_record;
}
head_ref = new_head_ref;
merged = insert_delayed_ref(trans, head_ref, node); merged = insert_delayed_ref(trans, head_ref, node);
spin_unlock(&delayed_refs->lock); spin_unlock(&delayed_refs->lock);
...@@ -1063,7 +1082,7 @@ static int add_delayed_ref(struct btrfs_trans_handle *trans, ...@@ -1063,7 +1082,7 @@ static int add_delayed_ref(struct btrfs_trans_handle *trans,
kmem_cache_free(btrfs_delayed_ref_head_cachep, head_ref); kmem_cache_free(btrfs_delayed_ref_head_cachep, head_ref);
free_node: free_node:
kmem_cache_free(btrfs_delayed_ref_node_cachep, node); kmem_cache_free(btrfs_delayed_ref_node_cachep, node);
return -ENOMEM; return ret;
} }
/* /*
...@@ -1094,6 +1113,7 @@ int btrfs_add_delayed_extent_op(struct btrfs_trans_handle *trans, ...@@ -1094,6 +1113,7 @@ int btrfs_add_delayed_extent_op(struct btrfs_trans_handle *trans,
struct btrfs_delayed_extent_op *extent_op) struct btrfs_delayed_extent_op *extent_op)
{ {
struct btrfs_delayed_ref_head *head_ref; struct btrfs_delayed_ref_head *head_ref;
struct btrfs_delayed_ref_head *head_ref_ret;
struct btrfs_delayed_ref_root *delayed_refs; struct btrfs_delayed_ref_root *delayed_refs;
struct btrfs_ref generic_ref = { struct btrfs_ref generic_ref = {
.type = BTRFS_REF_METADATA, .type = BTRFS_REF_METADATA,
...@@ -1113,11 +1133,15 @@ int btrfs_add_delayed_extent_op(struct btrfs_trans_handle *trans, ...@@ -1113,11 +1133,15 @@ int btrfs_add_delayed_extent_op(struct btrfs_trans_handle *trans,
delayed_refs = &trans->transaction->delayed_refs; delayed_refs = &trans->transaction->delayed_refs;
spin_lock(&delayed_refs->lock); spin_lock(&delayed_refs->lock);
add_delayed_ref_head(trans, head_ref, NULL, BTRFS_UPDATE_DELAYED_HEAD, head_ref_ret = add_delayed_ref_head(trans, head_ref, NULL,
NULL); BTRFS_UPDATE_DELAYED_HEAD, NULL);
spin_unlock(&delayed_refs->lock); spin_unlock(&delayed_refs->lock);
if (IS_ERR(head_ref_ret)) {
kmem_cache_free(btrfs_delayed_ref_head_cachep, head_ref);
return PTR_ERR(head_ref_ret);
}
/* /*
* Need to update the delayed_refs_rsv with any changes we may have * Need to update the delayed_refs_rsv with any changes we may have
* made. * made.
......
...@@ -1300,13 +1300,29 @@ static int btrfs_issue_discard(struct block_device *bdev, u64 start, u64 len, ...@@ -1300,13 +1300,29 @@ static int btrfs_issue_discard(struct block_device *bdev, u64 start, u64 len,
bytes_left = end - start; bytes_left = end - start;
} }
if (bytes_left) { while (bytes_left) {
u64 bytes_to_discard = min(BTRFS_MAX_DISCARD_CHUNK_SIZE, bytes_left);
ret = blkdev_issue_discard(bdev, start >> SECTOR_SHIFT, ret = blkdev_issue_discard(bdev, start >> SECTOR_SHIFT,
bytes_left >> SECTOR_SHIFT, bytes_to_discard >> SECTOR_SHIFT,
GFP_NOFS); GFP_NOFS);
if (!ret)
*discarded_bytes += bytes_left; if (ret) {
if (ret != -EOPNOTSUPP)
break;
continue;
}
start += bytes_to_discard;
bytes_left -= bytes_to_discard;
*discarded_bytes += bytes_to_discard;
if (btrfs_trim_interrupted()) {
ret = -ERESTARTSYS;
break;
}
} }
return ret; return ret;
} }
...@@ -6459,7 +6475,7 @@ static int btrfs_trim_free_extents(struct btrfs_device *device, u64 *trimmed) ...@@ -6459,7 +6475,7 @@ static int btrfs_trim_free_extents(struct btrfs_device *device, u64 *trimmed)
start += len; start += len;
*trimmed += bytes; *trimmed += bytes;
if (fatal_signal_pending(current)) { if (btrfs_trim_interrupted()) {
ret = -ERESTARTSYS; ret = -ERESTARTSYS;
break; break;
} }
......
...@@ -1306,6 +1306,13 @@ static int submit_one_sector(struct btrfs_inode *inode, ...@@ -1306,6 +1306,13 @@ static int submit_one_sector(struct btrfs_inode *inode,
free_extent_map(em); free_extent_map(em);
em = NULL; em = NULL;
/*
* Although the PageDirty bit is cleared before entering this
* function, subpage dirty bit is not cleared.
* So clear subpage dirty bit here so next time we won't submit
* a folio for a range already written to disk.
*/
btrfs_folio_clear_dirty(fs_info, folio, filepos, sectorsize);
btrfs_set_range_writeback(inode, filepos, filepos + sectorsize - 1); btrfs_set_range_writeback(inode, filepos, filepos + sectorsize - 1);
/* /*
* Above call should set the whole folio with writeback flag, even * Above call should set the whole folio with writeback flag, even
...@@ -1315,13 +1322,6 @@ static int submit_one_sector(struct btrfs_inode *inode, ...@@ -1315,13 +1322,6 @@ static int submit_one_sector(struct btrfs_inode *inode,
*/ */
ASSERT(folio_test_writeback(folio)); ASSERT(folio_test_writeback(folio));
/*
* Although the PageDirty bit is cleared before entering this
* function, subpage dirty bit is not cleared.
* So clear subpage dirty bit here so next time we won't submit
* folio for range already written to disk.
*/
btrfs_folio_clear_dirty(fs_info, folio, filepos, sectorsize);
submit_extent_folio(bio_ctrl, disk_bytenr, folio, submit_extent_folio(bio_ctrl, disk_bytenr, folio,
sectorsize, filepos - folio_pos(folio)); sectorsize, filepos - folio_pos(folio));
return 0; return 0;
......
...@@ -3809,7 +3809,7 @@ static int trim_no_bitmap(struct btrfs_block_group *block_group, ...@@ -3809,7 +3809,7 @@ static int trim_no_bitmap(struct btrfs_block_group *block_group,
if (async && *total_trimmed) if (async && *total_trimmed)
break; break;
if (fatal_signal_pending(current)) { if (btrfs_trim_interrupted()) {
ret = -ERESTARTSYS; ret = -ERESTARTSYS;
break; break;
} }
...@@ -4000,7 +4000,7 @@ static int trim_bitmaps(struct btrfs_block_group *block_group, ...@@ -4000,7 +4000,7 @@ static int trim_bitmaps(struct btrfs_block_group *block_group,
} }
block_group->discard_cursor = start; block_group->discard_cursor = start;
if (fatal_signal_pending(current)) { if (btrfs_trim_interrupted()) {
if (start != offset) if (start != offset)
reset_trimming_bitmap(ctl, offset); reset_trimming_bitmap(ctl, offset);
ret = -ERESTARTSYS; ret = -ERESTARTSYS;
......
...@@ -10,6 +10,7 @@ ...@@ -10,6 +10,7 @@
#include <linux/list.h> #include <linux/list.h>
#include <linux/spinlock.h> #include <linux/spinlock.h>
#include <linux/mutex.h> #include <linux/mutex.h>
#include <linux/freezer.h>
#include "fs.h" #include "fs.h"
struct inode; struct inode;
...@@ -56,6 +57,11 @@ static inline bool btrfs_free_space_trimming_bitmap( ...@@ -56,6 +57,11 @@ static inline bool btrfs_free_space_trimming_bitmap(
return (info->trim_state == BTRFS_TRIM_STATE_TRIMMING); return (info->trim_state == BTRFS_TRIM_STATE_TRIMMING);
} }
static inline bool btrfs_trim_interrupted(void)
{
return fatal_signal_pending(current) || freezing(current);
}
/* /*
* Deltas are an effective way to populate global statistics. Give macro names * Deltas are an effective way to populate global statistics. Give macro names
* to make it clear what we're doing. An example is discard_extents in * to make it clear what we're doing. An example is discard_extents in
......
...@@ -30,6 +30,12 @@ struct btrfs_zoned_device_info; ...@@ -30,6 +30,12 @@ struct btrfs_zoned_device_info;
#define BTRFS_MAX_DATA_CHUNK_SIZE (10ULL * SZ_1G) #define BTRFS_MAX_DATA_CHUNK_SIZE (10ULL * SZ_1G)
/*
* Arbitratry maximum size of one discard request to limit potentially long time
* spent in blkdev_issue_discard().
*/
#define BTRFS_MAX_DISCARD_CHUNK_SIZE (SZ_1G)
extern struct mutex uuid_mutex; extern struct mutex uuid_mutex;
#define BTRFS_STRIPE_LEN SZ_64K #define BTRFS_STRIPE_LEN SZ_64K
......
...@@ -1340,7 +1340,7 @@ static int btrfs_load_zone_info(struct btrfs_fs_info *fs_info, int zone_idx, ...@@ -1340,7 +1340,7 @@ static int btrfs_load_zone_info(struct btrfs_fs_info *fs_info, int zone_idx,
switch (zone.cond) { switch (zone.cond) {
case BLK_ZONE_COND_OFFLINE: case BLK_ZONE_COND_OFFLINE:
case BLK_ZONE_COND_READONLY: case BLK_ZONE_COND_READONLY:
btrfs_err(fs_info, btrfs_err_in_rcu(fs_info,
"zoned: offline/readonly zone %llu on device %s (devid %llu)", "zoned: offline/readonly zone %llu on device %s (devid %llu)",
(info->physical >> device->zone_info->zone_size_shift), (info->physical >> device->zone_info->zone_size_shift),
rcu_str_deref(device->name), device->devid); rcu_str_deref(device->name), device->devid);
......
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