diff --git a/block/blk-cgroup.c b/block/blk-cgroup.c index c9187d95a54b..00da55c6ec43 100644 --- a/block/blk-cgroup.c +++ b/block/blk-cgroup.c @@ -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); diff --git a/block/blk-cgroup.h b/block/blk-cgroup.h index e67c69839129..1fd60cb4157c 100644 --- a/block/blk-cgroup.h +++ b/block/blk-cgroup.h @@ -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;