On Fri, 2018-07-27 at 09:57 +0800, jianchao.wang wrote:
> Something like:
>
> percpu_ref_switch_to_atomic_sync(&q->q_usage_counter);
> if (!percpu_ref_is_zero(&q->q_usage_counter)) {
> ret = -EBUSY;
> pm_runtime_mark_last_busy(q->dev);
> } else {
> blk_set_preempt_only(q);
> if (!percpu_ref_is_zero(&q->q_usage_counter) {
> ret = -EBUSY;
> pm_runtime_mark_last_busy(q->dev);
> blk_clear_preempt_only(q);
> } else {
> q->rpm_status = RPM_SUSPENDIN;
> }
> }
I think this code is racy. Because there is no memory barrier in
blk_queue_enter() between the percpu_ref_tryget_live() and the
blk_queue_preempt_only() calls, the context that sets the PREEMPT_ONLY flag
has to use synchronize_rcu() or call_rcu() to ensure that blk_queue_enter()
sees the PREEMPT_ONLY flag after it has called percpu_ref_tryget_live().
See also http://lwn.net/Articles/573497/.
Bart.