btrfs: add cancellation points to trim loops

There are reports that system cannot suspend due to running trim because
the task responsible for trimming the device isn't able to finish in
time, especially since we have a free extent discarding phase, which can
trim a lot of unallocated space. There are no limits on the trim size
(unlike the block group part).

Since trime isn't a critical call it can be interrupted at any time,
in such cases we stop the trim, report the amount of discarded bytes and
return an error.

Link: https://bugzilla.kernel.org/show_bug.cgi?id=219180
Link: https://bugzilla.suse.com/show_bug.cgi?id=1229737
CC: stable@vger.kernel.org # 5.15+
Signed-off-by: Luca Stefani <luca.stefani.ge1@gmail.com>
Reviewed-by: David Sterba <dsterba@suse.com>
Signed-off-by: David Sterba <dsterba@suse.com>
This commit is contained in:
Luca Stefani 2024-09-17 22:33:05 +02:00 committed by David Sterba
parent a99fcb0158
commit 69313850dc
3 changed files with 14 additions and 3 deletions

View File

@ -1316,6 +1316,11 @@ static int btrfs_issue_discard(struct block_device *bdev, u64 start, u64 len,
start += bytes_to_discard; start += bytes_to_discard;
bytes_left -= bytes_to_discard; bytes_left -= bytes_to_discard;
*discarded_bytes += bytes_to_discard; *discarded_bytes += bytes_to_discard;
if (btrfs_trim_interrupted()) {
ret = -ERESTARTSYS;
break;
}
} }
return ret; return ret;
@ -6470,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;
} }

View File

@ -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,
} }
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;

View File

@ -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(
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