Skip to content

Commit 69ccd83

Browse files
adam900710kdave
authored andcommitted
btrfs: scrub: cancel the run if the process or fs is being frozen
It's a known bug that btrfs scrub/dev-replace can prevent the system from suspending. There are at least two factors involved: - Holding super_block::s_writers for the whole scrub/dev-replace duration We hold that percpu rw semaphore through mnt_want_write_file() for the whole scrub/dev-replace duration. That will prevent the fs being frozen, which can be initiated by either the user (e.g. fsfreeze) or power management suspension/hiberation. - Stuck in the kernel space for a long time During suspension all user processes (and some kernel threads) will be frozen. But if a user space progress has fallen into kernel (scrub ioctl) and do not return for a long time, it will make process freezing time out. Unfortunately scrub/dev-replace is a long running ioctl, and it will prevent the btrfs process from returning to the user space, thus make pm suspension/hiberation time out. Address them in one go: - Introduce a new helper should_cancel_scrub() Which includes the existing cancel request and new fs/process freezing checks. Here we have to check both fs and process freezing for pm suspension/hiberation. Pm can be configured to freeze fses before processes. (The current default is not to freeze fses, but planned to freeze the fses as the new default) Checking only fs freezing will fail pm without fs freezing, as the process freezing will time out. Checking only process freezing will fail pm with fs freezing since the fs freezing happens before process freezing. And the return value will indicate the reason, -ECANCLED for the explicitly canceled runs, and -EINTR for fs freeze or pm reasons. - Cancel the run if should_cancel_scrub() is true Unfortunately canceling is the only feasible solution here, pausing is not possible as we will still stay in the kernel space thus will still prevent the process from being frozen. This will cause a user impacting behavior change: Dev-replace can be interrupted by pm, and there is no way to resume but start from the beginning again. This means dev-replace may fail on newer kernels, and end users will need extra steps like using systemd-inhibit to prevent suspension/hibernation, to get back the old uninterrupted behavior. This behavior change will need extra documentation updates and communication with projects involving scrub/dev-replace including btrfs-progs. Reviewed-by: Filipe Manana <fdmanana@suse.com> Link: https://lore.kernel.org/linux-btrfs/d93b2a2d-6ad9-4c49-809f-11d769a6f30a@app.fastmail.com/ Reported-by: Chris Murphy <lists@colorremedies.com> Signed-off-by: Qu Wenruo <wqu@suse.com> Signed-off-by: David Sterba <dsterba@suse.com>
1 parent 368fd89 commit 69ccd83

File tree

1 file changed

+46
-7
lines changed

1 file changed

+46
-7
lines changed

fs/btrfs/scrub.c

Lines changed: 46 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2069,6 +2069,47 @@ static int queue_scrub_stripe(struct scrub_ctx *sctx, struct btrfs_block_group *
20692069
return 0;
20702070
}
20712071

2072+
/*
2073+
* Return 0 if we should not cancel the scrub.
2074+
* Return <0 if we need to cancel the scrub, returned value will
2075+
* indicate the reason:
2076+
* - -ECANCELED
2077+
* Being explicitly canceled through ioctl.
2078+
* - -EINTR
2079+
* Being interrupted by fs/process freezing.
2080+
*/
2081+
static int should_cancel_scrub(const struct scrub_ctx *sctx)
2082+
{
2083+
struct btrfs_fs_info *fs_info = sctx->fs_info;
2084+
2085+
if (atomic_read(&fs_info->scrub_cancel_req) ||
2086+
atomic_read(&sctx->cancel_req))
2087+
return -ECANCELED;
2088+
2089+
/*
2090+
* The user (e.g. fsfreeze command) or power management (pm)
2091+
* suspension/hibernation can freeze the fs.
2092+
* And pm suspension/hibernation will also freeze all user processes.
2093+
*
2094+
* A user process can only be frozen when it is in the user space, thus
2095+
* we have to cancel the run so that the process can return to the user
2096+
* space.
2097+
*
2098+
* Furthermore we have to check both fs and process freezing, as pm can
2099+
* be configured to freeze the fses before processes.
2100+
*
2101+
* If we only check fs freezing, then suspension without fs freezing
2102+
* will timeout, as the process is still in the kernel space.
2103+
*
2104+
* If we only check process freezing, then suspension with fs freezing
2105+
* will timeout, as the running scrub will prevent the fs from being frozen.
2106+
*/
2107+
if (fs_info->sb->s_writers.frozen > SB_UNFROZEN ||
2108+
freezing(current))
2109+
return -EINTR;
2110+
return 0;
2111+
}
2112+
20722113
static int scrub_raid56_parity_stripe(struct scrub_ctx *sctx,
20732114
struct btrfs_device *scrub_dev,
20742115
struct btrfs_block_group *bg,
@@ -2091,9 +2132,9 @@ static int scrub_raid56_parity_stripe(struct scrub_ctx *sctx,
20912132

20922133
ASSERT(sctx->raid56_data_stripes);
20932134

2094-
if (atomic_read(&fs_info->scrub_cancel_req) ||
2095-
atomic_read(&sctx->cancel_req))
2096-
return -ECANCELED;
2135+
ret = should_cancel_scrub(sctx);
2136+
if (ret < 0)
2137+
return ret;
20972138

20982139
if (atomic_read(&fs_info->scrub_pause_req))
20992140
scrub_blocked_if_needed(fs_info);
@@ -2275,11 +2316,9 @@ static int scrub_simple_mirror(struct scrub_ctx *sctx,
22752316
u64 found_logical = U64_MAX;
22762317
u64 cur_physical = physical + cur_logical - logical_start;
22772318

2278-
if (atomic_read(&fs_info->scrub_cancel_req) ||
2279-
atomic_read(&sctx->cancel_req)) {
2280-
ret = -ECANCELED;
2319+
ret = should_cancel_scrub(sctx);
2320+
if (ret < 0)
22812321
break;
2282-
}
22832322

22842323
if (atomic_read(&fs_info->scrub_pause_req))
22852324
scrub_blocked_if_needed(fs_info);

0 commit comments

Comments
 (0)