From: Yu Kuai <yukuai3@xxxxxxxxxx> queue_lock is held to protect 'q->rqos' in rq_qos_add() and rq_qos_del(), however, it's not held in rq_qos_exit(), while they can operate the list concurrently: t1 t2 // configure iocost //remove device blk_iocost_init del_gendisk rq_qos_add rq_qos_exit 'rqos->ops->exit' can't be called under spinlock because it might be scheduled out, hence fix the problem by holding queue_lock to fetch the list and reset q->rq_qos first. Signed-off-by: Yu Kuai <yukuai3@xxxxxxxxxx> --- block/blk-rq-qos.c | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/block/blk-rq-qos.c b/block/blk-rq-qos.c index 88f0fe7dcf54..efffc6fa55db 100644 --- a/block/blk-rq-qos.c +++ b/block/blk-rq-qos.c @@ -288,9 +288,16 @@ void rq_qos_wait(struct rq_wait *rqw, void *private_data, void rq_qos_exit(struct request_queue *q) { - while (q->rq_qos) { - struct rq_qos *rqos = q->rq_qos; - q->rq_qos = rqos->next; - rqos->ops->exit(rqos); - } + struct rq_qos *rqos; + + spin_lock_irq(&q->queue_lock); + rqos = q->rq_qos; + q->rq_qos = NULL; + spin_unlock_irq(&q->queue_lock); + + do { + if (rqos->ops->exit) + rqos->ops->exit(rqos); + rqos = rqos->next; + } while (rqos); } -- 2.31.1