Hi Sandeep,

On 2023/6/21 04:38, Sandeep Dhavale wrote:
Hi,
I think we are under RCU read lock in the stack at
blk_mq_flush_plug_list+0x2b0/0x354

blk_mq_flush_plug_list calls blk_mq_run_dispatch_ops
which is a macro in block/blk-mq.h

/* run the code block in @dispatch_ops with rcu/srcu read lock held */
#define __blk_mq_run_dispatch_ops(q, check_sleep, dispatch_ops) \
do {                                                            \
         if ((q)->tag_set->flags & BLK_MQ_F_BLOCKING) {          \
                 struct blk_mq_tag_set *__tag_set = (q)->tag_set; \
                 int srcu_idx;                                   \
                                                                 \
                 might_sleep_if(check_sleep);                    \
                 srcu_idx = srcu_read_lock(__tag_set->srcu);     \
                 (dispatch_ops);                                 \
                 srcu_read_unlock(__tag_set->srcu, srcu_idx);    \
         } else {                                                \
                 rcu_read_lock();                                \
                 (dispatch_ops);                                 \
                 rcu_read_unlock();                              \
         }                                                       \
} while (0)

#define blk_mq_run_dispatch_ops(q, dispatch_ops)                \
         __blk_mq_run_dispatch_ops(q, true, dispatch_ops)        \

As you can see if BLK_MQ_F_BLOCKING is not set then dispatch_ops is
called with rcu_read_lock().

rcu_read_lock()
         __rcu_read_lock()
                 rcu_preempt_read_enter()

In rcu_preempt_read_enter() increments the rcu_read_lock_nesting which
is detected later during mutex_lock() as a warning.

Thanks for your analysis. That is much helpful to me.
So it seems a new path which calls end_io under rcu read lock.


Regarding use of !in_task(), that cannot detect rcu_read_lock_nesting
as far as I can tell so that may not be sufficient.

rcu_preempt_depth is a too low level api, I'm not sure if it's a good
way but we really don't want to trigger another workqueue here,
could we just use:

"!in_task() || irqs_disabled() || rcu_read_lock_any_held()"

Or do you have better ideas?

Thanks,
Gao Xiang


Thanks,
Sandeep.

Reply via email to