Skip to content

Commit 895c672

Browse files
boryaskdave
authored andcommitted
btrfs: make btrfs_discard_workfn() block_group ref explicit
Currently, the async discard machinery owns a ref to the block_group when the block_group is queued on a discard list. However, to handle races with discard cancellation and the discard workfn, we have a specific logic to detect that the block_group is *currently* running in the workfn, to protect the workfn's usage amidst cancellation. As far as I can tell, this doesn't have any overt bugs (though finish_discard_pass() and remove_from_discard_list() racing can have a surprising outcome for the caller of remove_from_discard_list() in that it is again added at the end). But it is needlessly complicated to rely on locking and the nullity of discard_ctl->block_group. Simplify this significantly by just taking a refcount while we are in the workfn and unconditionally drop it in both the remove and workfn paths, regardless of if they race. Reviewed-by: Filipe Manana <[email protected]> Signed-off-by: Boris Burkov <[email protected]> Signed-off-by: David Sterba <[email protected]>
1 parent 7511e29 commit 895c672

File tree

1 file changed

+16
-18
lines changed

1 file changed

+16
-18
lines changed

fs/btrfs/discard.c

Lines changed: 16 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -167,13 +167,7 @@ static bool remove_from_discard_list(struct btrfs_discard_ctl *discard_ctl,
167167
block_group->discard_eligible_time = 0;
168168
queued = !list_empty(&block_group->discard_list);
169169
list_del_init(&block_group->discard_list);
170-
/*
171-
* If the block group is currently running in the discard workfn, we
172-
* don't want to deref it, since it's still being used by the workfn.
173-
* The workfn will notice this case and deref the block group when it is
174-
* finished.
175-
*/
176-
if (queued && !running)
170+
if (queued)
177171
btrfs_put_block_group(block_group);
178172

179173
spin_unlock(&discard_ctl->lock);
@@ -260,9 +254,10 @@ static struct btrfs_block_group *peek_discard_list(
260254
block_group->discard_cursor = block_group->start;
261255
block_group->discard_state = BTRFS_DISCARD_EXTENTS;
262256
}
263-
discard_ctl->block_group = block_group;
264257
}
265258
if (block_group) {
259+
btrfs_get_block_group(block_group);
260+
discard_ctl->block_group = block_group;
266261
*discard_state = block_group->discard_state;
267262
*discard_index = block_group->discard_index;
268263
}
@@ -493,9 +488,20 @@ static void btrfs_discard_workfn(struct work_struct *work)
493488

494489
block_group = peek_discard_list(discard_ctl, &discard_state,
495490
&discard_index, now);
496-
if (!block_group || !btrfs_run_discard_work(discard_ctl))
491+
if (!block_group)
497492
return;
493+
if (!btrfs_run_discard_work(discard_ctl)) {
494+
spin_lock(&discard_ctl->lock);
495+
btrfs_put_block_group(block_group);
496+
discard_ctl->block_group = NULL;
497+
spin_unlock(&discard_ctl->lock);
498+
return;
499+
}
498500
if (now < block_group->discard_eligible_time) {
501+
spin_lock(&discard_ctl->lock);
502+
btrfs_put_block_group(block_group);
503+
discard_ctl->block_group = NULL;
504+
spin_unlock(&discard_ctl->lock);
499505
btrfs_discard_schedule_work(discard_ctl, false);
500506
return;
501507
}
@@ -547,15 +553,7 @@ static void btrfs_discard_workfn(struct work_struct *work)
547553
spin_lock(&discard_ctl->lock);
548554
discard_ctl->prev_discard = trimmed;
549555
discard_ctl->prev_discard_time = now;
550-
/*
551-
* If the block group was removed from the discard list while it was
552-
* running in this workfn, then we didn't deref it, since this function
553-
* still owned that reference. But we set the discard_ctl->block_group
554-
* back to NULL, so we can use that condition to know that now we need
555-
* to deref the block_group.
556-
*/
557-
if (discard_ctl->block_group == NULL)
558-
btrfs_put_block_group(block_group);
556+
btrfs_put_block_group(block_group);
559557
discard_ctl->block_group = NULL;
560558
__btrfs_discard_schedule_work(discard_ctl, now, false);
561559
spin_unlock(&discard_ctl->lock);

0 commit comments

Comments
 (0)