mirror of
https://github.com/torvalds/linux.git
synced 2026-11-07 06:17:02 +01:00
blk-cgroup: don't free a blkg with its punt work queued
blkg_async_bio_workfn() relies on the bios on the list pinning the blkg.
But blkcg_punt_bio_submit() can call queue_work() after a running
instance already took and completed the bio, and queue_work() succeeds
on an empty list for the same reason. Nothing holds the blkg then, and
free_work shares its memory with async_bio_work on top of that.
Hold a reference across blkcg_punt_bio_submit(), give the two works
memory of their own, and cancel async_bio_work in blkg_free_workfn().
Fixes: d3f77dfdc7 ("blkcg: implement REQ_CGROUP_PUNT")
Cc: Tejun Heo <tj@kernel.org>
Signed-off-by: Josef Bacik <josef@toxicpanda.com>
Acked-by: Tejun Heo <tj@kernel.org>
Link: https://patch.msgid.link/20261028164415.1262013-20-axboe@kernel.dk
Signed-off-by: Jens Axboe <axboe@kernel.dk>
This commit is contained in:
parent
e335b853d9
commit
4def096314
|
|
@ -121,6 +121,11 @@ static void blkg_free_workfn(struct work_struct *work)
|
|||
struct request_queue *q = blkg->q;
|
||||
int i;
|
||||
|
||||
#ifdef CONFIG_BLK_CGROUP_PUNT_BIO
|
||||
/* It may have been queued for bios which an earlier run has taken */
|
||||
cancel_work_sync(&blkg->async_bio_work);
|
||||
#endif
|
||||
|
||||
/*
|
||||
* pd_free_fn() can also be called from blkcg_deactivate_policy(),
|
||||
* in order to make sure pd_free_fn() is called in order, the deletion
|
||||
|
|
@ -218,7 +223,7 @@ static void blkg_async_bio_workfn(struct work_struct *work)
|
|||
struct blk_plug plug;
|
||||
bool need_plug = false;
|
||||
|
||||
/* as long as there are pending bios, @blkg can't go away */
|
||||
/* blkg_free_workfn() waits for us */
|
||||
spin_lock(&blkg->async_bio_lock);
|
||||
bio_list_merge_init(&bios, &blkg->async_bios);
|
||||
spin_unlock(&blkg->async_bio_lock);
|
||||
|
|
@ -245,10 +250,16 @@ void blkcg_punt_bio_submit(struct bio *bio)
|
|||
struct blkcg_gq *blkg = bio->bi_blkg;
|
||||
|
||||
if (blkg->parent) {
|
||||
/*
|
||||
* Once it is on the list, a run which is under way can submit
|
||||
* @bio, and its reference goes when it completes.
|
||||
*/
|
||||
blkg_get(blkg);
|
||||
spin_lock(&blkg->async_bio_lock);
|
||||
bio_list_add(&blkg->async_bios, bio);
|
||||
spin_unlock(&blkg->async_bio_lock);
|
||||
queue_work(blkcg_punt_bio_wq, &blkg->async_bio_work);
|
||||
blkg_put(blkg);
|
||||
} else {
|
||||
/* never bounce for the root cgroup */
|
||||
submit_bio(bio);
|
||||
|
|
|
|||
|
|
@ -76,11 +76,9 @@ struct blkcg_gq {
|
|||
#ifdef CONFIG_BLK_CGROUP_PUNT_BIO
|
||||
spinlock_t async_bio_lock;
|
||||
struct bio_list async_bios;
|
||||
struct work_struct async_bio_work;
|
||||
#endif
|
||||
union {
|
||||
struct work_struct async_bio_work;
|
||||
struct work_struct free_work;
|
||||
};
|
||||
struct work_struct free_work;
|
||||
|
||||
atomic_t use_delay;
|
||||
atomic64_t delay_nsec;
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user