Skip to content

Commit d16baa3

Browse files
htejunaxboe
authored andcommitted
blk-iocost: fix NULL iocg deref from racing against initialization
When initializing iocost for a queue, its rqos should be registered before the blkcg policy is activated to allow policy data initiailization to lookup the associated ioc. This unfortunately means that the rqos methods can be called on bios before iocgs are attached to all existing blkgs. While the race is theoretically possible on ioc_rqos_throttle(), it mostly happened in ioc_rqos_merge() due to the difference in how they lookup ioc. The former determines it from the passed in @rqos and then bails before dereferencing iocg if the looked up ioc is disabled, which most likely is the case if initialization is still in progress. The latter looked up ioc by dereferencing the possibly NULL iocg making it a lot more prone to actually triggering the bug. * Make ioc_rqos_merge() use the same method as ioc_rqos_throttle() to look up ioc for consistency. * Make ioc_rqos_throttle() and ioc_rqos_merge() test for NULL iocg before dereferencing it. * Explain the danger of NULL iocgs in blk_iocost_init(). Signed-off-by: Tejun Heo <[email protected]> Reported-by: Jonathan Lemon <[email protected]> Cc: [email protected] # v5.4+ Signed-off-by: Jens Axboe <[email protected]>
1 parent 19cd340 commit d16baa3

File tree

1 file changed

+11
-5
lines changed

1 file changed

+11
-5
lines changed

block/blk-iocost.c

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2551,8 +2551,8 @@ static void ioc_rqos_throttle(struct rq_qos *rqos, struct bio *bio)
25512551
bool use_debt, ioc_locked;
25522552
unsigned long flags;
25532553

2554-
/* bypass IOs if disabled or for root cgroup */
2555-
if (!ioc->enabled || !iocg->level)
2554+
/* bypass IOs if disabled, still initializing, or for root cgroup */
2555+
if (!ioc->enabled || !iocg || !iocg->level)
25562556
return;
25572557

25582558
/* calculate the absolute vtime cost */
@@ -2679,14 +2679,14 @@ static void ioc_rqos_merge(struct rq_qos *rqos, struct request *rq,
26792679
struct bio *bio)
26802680
{
26812681
struct ioc_gq *iocg = blkg_to_iocg(bio->bi_blkg);
2682-
struct ioc *ioc = iocg->ioc;
2682+
struct ioc *ioc = rqos_to_ioc(rqos);
26832683
sector_t bio_end = bio_end_sector(bio);
26842684
struct ioc_now now;
26852685
u64 vtime, abs_cost, cost;
26862686
unsigned long flags;
26872687

2688-
/* bypass if disabled or for root cgroup */
2689-
if (!ioc->enabled || !iocg->level)
2688+
/* bypass if disabled, still initializing, or for root cgroup */
2689+
if (!ioc->enabled || !iocg || !iocg->level)
26902690
return;
26912691

26922692
abs_cost = calc_vtime_cost(bio, iocg, true);
@@ -2863,6 +2863,12 @@ static int blk_iocost_init(struct request_queue *q)
28632863
ioc_refresh_params(ioc, true);
28642864
spin_unlock_irq(&ioc->lock);
28652865

2866+
/*
2867+
* rqos must be added before activation to allow iocg_pd_init() to
2868+
* lookup the ioc from q. This means that the rqos methods may get
2869+
* called before policy activation completion, can't assume that the
2870+
* target bio has an iocg associated and need to test for NULL iocg.
2871+
*/
28662872
rq_qos_add(q, rqos);
28672873
ret = blkcg_activate_policy(q, &blkcg_policy_iocost);
28682874
if (ret) {

0 commit comments

Comments
 (0)