Skip to content

Commit f1a4f4d

Browse files
htejunaxboe
authored andcommitted
block, cfq: fix cic lookup locking
* cfq_cic_lookup() may be called without queue_lock and multiple tasks can execute it simultaneously for the same shared ioc. Nothing prevents them racing each other and trying to drop the same dead cic entry multiple times. * smp_wmb() in cfq_exit_cic() doesn't really do anything and nothing prevents cfq_cic_lookup() seeing stale cic->key. This usually doesn't blow up because by the time cic is exited, all requests have been drained and new requests are terminated before going through elevator. However, it can still be triggered by plug merge path which doesn't grab queue_lock and thus can't check DEAD state reliably. This patch updates lookup locking such that, * Lookup is always performed under queue_lock. This doesn't add any more locking. The only issue is cfq_allow_merge() which can be called from plug merge path without holding any lock. For now, this is worked around by using cic of the request to merge into, which is guaranteed to have the same ioc. For longer term, I think it would be best to separate out plug merge method from regular one. * Spurious ioc->lock locking around cic lookup hint assignment dropped. Signed-off-by: Tejun Heo <[email protected]> Signed-off-by: Jens Axboe <[email protected]>
1 parent 216284c commit f1a4f4d

File tree

1 file changed

+35
-32
lines changed

1 file changed

+35
-32
lines changed

block/cfq-iosched.c

Lines changed: 35 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -1682,12 +1682,19 @@ static int cfq_allow_merge(struct request_queue *q, struct request *rq,
16821682
return false;
16831683

16841684
/*
1685-
* Lookup the cfqq that this bio will be queued with. Allow
1686-
* merge only if rq is queued there.
1685+
* Lookup the cfqq that this bio will be queued with and allow
1686+
* merge only if rq is queued there. This function can be called
1687+
* from plug merge without queue_lock. In such cases, ioc of @rq
1688+
* and %current are guaranteed to be equal. Avoid lookup which
1689+
* requires queue_lock by using @rq's cic.
16871690
*/
1688-
cic = cfq_cic_lookup(cfqd, current->io_context);
1689-
if (!cic)
1690-
return false;
1691+
if (current->io_context == RQ_CIC(rq)->ioc) {
1692+
cic = RQ_CIC(rq);
1693+
} else {
1694+
cic = cfq_cic_lookup(cfqd, current->io_context);
1695+
if (!cic)
1696+
return false;
1697+
}
16911698

16921699
cfqq = cic_to_cfqq(cic, cfq_bio_sync(bio));
16931700
return cfqq == RQ_CFQQ(rq);
@@ -2784,21 +2791,15 @@ static void cfq_exit_cic(struct cfq_io_context *cic)
27842791
struct io_context *ioc = cic->ioc;
27852792

27862793
list_del_init(&cic->queue_list);
2794+
cic->key = cfqd_dead_key(cfqd);
27872795

27882796
/*
2789-
* Make sure dead mark is seen for dead queues
2797+
* Both setting lookup hint to and clearing it from @cic are done
2798+
* under queue_lock. If it's not pointing to @cic now, it never
2799+
* will. Hint assignment itself can race safely.
27902800
*/
2791-
smp_wmb();
2792-
cic->key = cfqd_dead_key(cfqd);
2793-
2794-
rcu_read_lock();
2795-
if (rcu_dereference(ioc->ioc_data) == cic) {
2796-
rcu_read_unlock();
2797-
spin_lock(&ioc->lock);
2801+
if (rcu_dereference_raw(ioc->ioc_data) == cic)
27982802
rcu_assign_pointer(ioc->ioc_data, NULL);
2799-
spin_unlock(&ioc->lock);
2800-
} else
2801-
rcu_read_unlock();
28022803

28032804
if (cic->cfqq[BLK_RW_ASYNC]) {
28042805
cfq_exit_cfqq(cfqd, cic->cfqq[BLK_RW_ASYNC]);
@@ -3092,12 +3093,20 @@ cfq_drop_dead_cic(struct cfq_data *cfqd, struct io_context *ioc,
30923093
cfq_cic_free(cic);
30933094
}
30943095

3096+
/**
3097+
* cfq_cic_lookup - lookup cfq_io_context
3098+
* @cfqd: the associated cfq_data
3099+
* @ioc: the associated io_context
3100+
*
3101+
* Look up cfq_io_context associated with @cfqd - @ioc pair. Must be
3102+
* called with queue_lock held.
3103+
*/
30953104
static struct cfq_io_context *
30963105
cfq_cic_lookup(struct cfq_data *cfqd, struct io_context *ioc)
30973106
{
30983107
struct cfq_io_context *cic;
3099-
unsigned long flags;
31003108

3109+
lockdep_assert_held(cfqd->queue->queue_lock);
31013110
if (unlikely(!ioc))
31023111
return NULL;
31033112

@@ -3107,28 +3116,22 @@ cfq_cic_lookup(struct cfq_data *cfqd, struct io_context *ioc)
31073116
* we maintain a last-hit cache, to avoid browsing over the tree
31083117
*/
31093118
cic = rcu_dereference(ioc->ioc_data);
3110-
if (cic && cic->key == cfqd) {
3111-
rcu_read_unlock();
3112-
return cic;
3113-
}
3119+
if (cic && cic->key == cfqd)
3120+
goto out;
31143121

31153122
do {
31163123
cic = radix_tree_lookup(&ioc->radix_root, cfqd->queue->id);
3117-
rcu_read_unlock();
31183124
if (!cic)
31193125
break;
3120-
if (unlikely(cic->key != cfqd)) {
3121-
cfq_drop_dead_cic(cfqd, ioc, cic);
3122-
rcu_read_lock();
3123-
continue;
3126+
if (likely(cic->key == cfqd)) {
3127+
/* hint assignment itself can race safely */
3128+
rcu_assign_pointer(ioc->ioc_data, cic);
3129+
break;
31243130
}
3125-
3126-
spin_lock_irqsave(&ioc->lock, flags);
3127-
rcu_assign_pointer(ioc->ioc_data, cic);
3128-
spin_unlock_irqrestore(&ioc->lock, flags);
3129-
break;
3131+
cfq_drop_dead_cic(cfqd, ioc, cic);
31303132
} while (1);
3131-
3133+
out:
3134+
rcu_read_unlock();
31323135
return cic;
31333136
}
31343137

0 commit comments

Comments
 (0)