diff mbox series

[V2,1/2] block: fix race between adding/removing rq qos and normal IO

Message ID 20210608071903.431195-2-ming.lei@redhat.com (mailing list archive)
State New, archived
Headers show
Series block: fix race between adding wbt and normal IO | expand

Commit Message

Ming Lei June 8, 2021, 7:19 a.m. UTC
Yi reported several kernel panics on:

[16687.001777] Unable to handle kernel NULL pointer dereference at virtual address 0000000000000008
...
[16687.163549] pc : __rq_qos_track+0x38/0x60

or

[  997.690455] Unable to handle kernel NULL pointer dereference at virtual address 0000000000000020
...
[  997.850347] pc : __rq_qos_done+0x2c/0x50

Turns out it is caused by race between adding rq qos(wbt) and normal IO
because rq_qos_add can be run when IO is being submitted, fix this issue
by freezing queue before adding/deleting rq qos to queue.

rq_qos_exit() needn't to freeze queue because it is called after queue
has been frozen.

iolatency calls rq_qos_add() during allocating queue, so freezing won't
add delay because queue usage refcount works at atomic mode at that
time.

iocost calls rq_qos_add() when writing cgroup attribute file, that is
fine to freeze queue at that time since we usually freeze queue when
storing to queue sysfs attribute, meantime iocost only exists on the
root cgroup.

wbt_init calls it in blk_register_queue() and queue sysfs attribute
store(queue_wb_lat_store() when write it 1st time in case of !BLK_WBT_MQ),
the following patch will speedup the queue freezing in wbt_init.

Reported-by: Yi Zhang <yi.zhang@redhat.com>
Cc: Bart Van Assche <bvanassche@acm.org>
Signed-off-by: Ming Lei <ming.lei@redhat.com>
---
 block/blk-rq-qos.h | 13 +++++++++++++
 1 file changed, 13 insertions(+)

Comments

Bart Van Assche June 8, 2021, 3:04 p.m. UTC | #1
On 6/8/21 12:19 AM, Ming Lei wrote:
>  static inline void rq_qos_add(struct request_queue *q, struct rq_qos *rqos)
>  {
> +	/*
> +	 * No IO can be in-flight when adding rqos, so freeze queue, which
> +	 * is fine since we only support rq_qos for blk-mq queue
> +	 */
> +	blk_mq_freeze_queue(q);
>  	rqos->next = q->rq_qos;
>  	q->rq_qos = rqos;
> +	blk_mq_unfreeze_queue(q);
>  
>  	if (rqos->ops->debugfs_attrs)
>  		blk_mq_debugfs_register_rqos(rqos);
> @@ -110,12 +117,18 @@ static inline void rq_qos_del(struct request_queue *q, struct rq_qos *rqos)
>  {
>  	struct rq_qos **cur;
>  
> +	/*
> +	 * No IO can be in-flight when removing rqos, so freeze queue,
> +	 * which is fine since we only support rq_qos for blk-mq queue
> +	 */
> +	blk_mq_freeze_queue(q);
>  	for (cur = &q->rq_qos; *cur; cur = &(*cur)->next) {
>  		if (*cur == rqos) {
>  			*cur = rqos->next;
>  			break;
>  		}
>  	}
> +	blk_mq_unfreeze_queue(q);
>  
>  	blk_mq_debugfs_unregister_rqos(rqos);
>  }

Although this patch looks like an improvement to me, I think we also
need protection against concurrent rq_qos_add() and rq_qos_del() calls,
e.g. via a mutex.

Thanks,

Bart.
Ming Lei June 9, 2021, 12:55 a.m. UTC | #2
On Tue, Jun 08, 2021 at 08:04:00AM -0700, Bart Van Assche wrote:
> On 6/8/21 12:19 AM, Ming Lei wrote:
> >  static inline void rq_qos_add(struct request_queue *q, struct rq_qos *rqos)
> >  {
> > +	/*
> > +	 * No IO can be in-flight when adding rqos, so freeze queue, which
> > +	 * is fine since we only support rq_qos for blk-mq queue
> > +	 */
> > +	blk_mq_freeze_queue(q);
> >  	rqos->next = q->rq_qos;
> >  	q->rq_qos = rqos;
> > +	blk_mq_unfreeze_queue(q);
> >  
> >  	if (rqos->ops->debugfs_attrs)
> >  		blk_mq_debugfs_register_rqos(rqos);
> > @@ -110,12 +117,18 @@ static inline void rq_qos_del(struct request_queue *q, struct rq_qos *rqos)
> >  {
> >  	struct rq_qos **cur;
> >  
> > +	/*
> > +	 * No IO can be in-flight when removing rqos, so freeze queue,
> > +	 * which is fine since we only support rq_qos for blk-mq queue
> > +	 */
> > +	blk_mq_freeze_queue(q);
> >  	for (cur = &q->rq_qos; *cur; cur = &(*cur)->next) {
> >  		if (*cur == rqos) {
> >  			*cur = rqos->next;
> >  			break;
> >  		}
> >  	}
> > +	blk_mq_unfreeze_queue(q);
> >  
> >  	blk_mq_debugfs_unregister_rqos(rqos);
> >  }
> 
> Although this patch looks like an improvement to me, I think we also
> need protection against concurrent rq_qos_add() and rq_qos_del() calls,
> e.g. via a mutex.

Fine, one spinlock should be enough, will do it in V3.


Thanks,
Ming
diff mbox series

Patch

diff --git a/block/blk-rq-qos.h b/block/blk-rq-qos.h
index 2bc43e94f4c4..c9dccb344312 100644
--- a/block/blk-rq-qos.h
+++ b/block/blk-rq-qos.h
@@ -7,6 +7,7 @@ 
 #include <linux/blk_types.h>
 #include <linux/atomic.h>
 #include <linux/wait.h>
+#include <linux/blk-mq.h>
 
 #include "blk-mq-debugfs.h"
 
@@ -99,8 +100,14 @@  static inline void rq_wait_init(struct rq_wait *rq_wait)
 
 static inline void rq_qos_add(struct request_queue *q, struct rq_qos *rqos)
 {
+	/*
+	 * No IO can be in-flight when adding rqos, so freeze queue, which
+	 * is fine since we only support rq_qos for blk-mq queue
+	 */
+	blk_mq_freeze_queue(q);
 	rqos->next = q->rq_qos;
 	q->rq_qos = rqos;
+	blk_mq_unfreeze_queue(q);
 
 	if (rqos->ops->debugfs_attrs)
 		blk_mq_debugfs_register_rqos(rqos);
@@ -110,12 +117,18 @@  static inline void rq_qos_del(struct request_queue *q, struct rq_qos *rqos)
 {
 	struct rq_qos **cur;
 
+	/*
+	 * No IO can be in-flight when removing rqos, so freeze queue,
+	 * which is fine since we only support rq_qos for blk-mq queue
+	 */
+	blk_mq_freeze_queue(q);
 	for (cur = &q->rq_qos; *cur; cur = &(*cur)->next) {
 		if (*cur == rqos) {
 			*cur = rqos->next;
 			break;
 		}
 	}
+	blk_mq_unfreeze_queue(q);
 
 	blk_mq_debugfs_unregister_rqos(rqos);
 }