diff --git a/block/blk-core.c b/block/blk-core.c index 365641266c9e8..940c1933d2330 100644 --- a/block/blk-core.c +++ b/block/blk-core.c @@ -473,6 +473,7 @@ struct request_queue *blk_alloc_queue(struct queue_limits *lim, int node_id) refcount_set(&q->refs, 1); mutex_init(&q->debugfs_mutex); mutex_init(&q->elevator_lock); + mutex_init(&q->elevator_queue_lock); mutex_init(&q->sysfs_lock); mutex_init(&q->limits_lock); mutex_init(&q->rq_qos_mutex); diff --git a/block/elevator.c b/block/elevator.c index 3bcd37c2aa340..65bdea27aa8a9 100644 --- a/block/elevator.c +++ b/block/elevator.c @@ -665,6 +665,13 @@ static int elevator_change(struct request_queue *q, struct elv_change_ctx *ctx) return ret; } + /* + * Acquire elevator_queue_lock to serialize the debugfs (un)register + * steps for the same queue. The elevator switch core part is protected + * by queue freezing and ->elevator_lock. + */ + mutex_lock(&q->elevator_queue_lock); + memflags = blk_mq_freeze_queue(q); /* * May be called before adding disk, when there isn't any FS I/O, @@ -690,6 +697,8 @@ static int elevator_change(struct request_queue *q, struct elv_change_ctx *ctx) if (!ctx->new) blk_mq_free_sched_res(&ctx->res, ctx->type, set); + mutex_unlock(&q->elevator_queue_lock); + return ret; } diff --git a/include/linux/blkdev.h b/include/linux/blkdev.h index 9213a5716f95a..dd0704b407deb 100644 --- a/include/linux/blkdev.h +++ b/include/linux/blkdev.h @@ -612,6 +612,13 @@ struct request_queue { */ struct mutex elevator_lock; + /* + * Serializes the whole elevator change operation for the same queue, + * including the debugfs (un)register steps. Must be acquired before + * freezing the queue and acquiring elevator_lock. + */ + struct mutex elevator_queue_lock; + struct mutex sysfs_lock; /* * Protects queue limits and also sysfs attribute read_ahead_kb.