diff mbox

[1/1] blk-mq: fix hang caused by freeze/unfreeze sequence

Message ID 20160805174131.22043-1-roman.penyaev@profitbricks.com (mailing list archive)
State New, archived
Headers show

Commit Message

Roman Pen Aug. 5, 2016, 5:41 p.m. UTC
Long time ago there was a similar fix proposed by Akinobu Mita[1],
but it seems that time everyone decided to fix this subtle race in
percpu-refcount and Tejun Heo[2] did an attempt (as I can see that
patchset was not applied).

The following is a description of a queue hang - same fix but a bug
from another angle.

The hang happens on queue freeze because of a simultaneous calls of
blk_mq_freeze_queue() and blk_mq_unfreeze_queue() from different threads,
and because of a reference race percpu_ref_reinit() and percpu_ref_kill()
swap.

 CPU#0             CPU#1
 ----------------  -----------------
 percpu_ref_kill()
 
                   percpu_ref_kill() << atomic reference does not
 percpu_ref_reinit()                 << guarantee the order

                   blk_mq_freeze_queue_wait() << HANG HERE

                   percpu_ref_reinit()

Firstly this wrong sequence raises two kernel warnings:

  1st. WARNING at lib/percpu-recount.c:309
       percpu_ref_kill_and_confirm called more than once

  2nd. WARNING at lib/percpu-refcount.c:331

But the most unpleasant effect is a hang of a blk_mq_freeze_queue_wait(),
which waits for a zero of a q_usage_counter, which never happens
because percpu-ref was not reinited and stays in PERCPU state forever.

The simplified sequence above is reproduced on shared tags, when one
queue is going to die meanwhile another one is initing:

 CPU#0                           CPU#1
 ------------------------------- ------------------------------------
 q1 = blk_mq_init_queue(shared_tags)

                                q2 = blk_mq_init_queue(shared_tags):
                                  blk_mq_add_queue_tag_set(shared_tags):
                                    blk_mq_update_tag_set_depth(shared_tags):
                                      blk_mq_freeze_queue(q1)
 blk_cleanup_queue(q1)                 ...
   blk_mq_freeze_queue(q1)   <<<->>>   blk_mq_unfreeze_queue(q1)

[1] Message id: 1443287365-4244-7-git-send-email-akinobu.mita@gmail.com
[2] Message id: 1443563240-29306-6-git-send-email-tj@kernel.org

Signed-off-by: Roman Pen <roman.penyaev@profitbricks.com>
Cc: Akinobu Mita <akinobu.mita@gmail.com>
Cc: Tejun Heo <tj@kernel.org>
Cc: Jens Axboe <axboe@kernel.dk>
Cc: Christoph Hellwig <hch@lst.de>
Cc: linux-block@vger.kernel.org
Cc: linux-kernel@vger.kernel.org
---
 block/blk-core.c       |  1 +
 block/blk-mq.c         | 22 +++++++++++-----------
 include/linux/blkdev.h |  7 ++++++-
 3 files changed, 18 insertions(+), 12 deletions(-)

Comments

kernel test robot Aug. 5, 2016, 6:04 p.m. UTC | #1
Hi Roman,

[auto build test ERROR on block/for-next]
[also build test ERROR on v4.7 next-20160805]
[if your patch is applied to the wrong git tree, please drop us a note to help improve the system]

url:    https://github.com/0day-ci/linux/commits/Roman-Pen/blk-mq-fix-hang-caused-by-freeze-unfreeze-sequence/20160806-014441
base:   https://git.kernel.org/pub/scm/linux/kernel/git/axboe/linux-block.git for-next
config: x86_64-randconfig-x015-201631 (attached as .config)
compiler: gcc-6 (Debian 6.1.1-9) 6.1.1 20160705
reproduce:
        # save the attached .config to linux build tree
        make ARCH=x86_64 

All error/warnings (new ones prefixed by >>):

   In file included from include/linux/mmzone.h:9:0,
                    from include/linux/gfp.h:5,
                    from include/linux/kmod.h:22,
                    from include/linux/module.h:13,
                    from block/blk-core.c:15:
   block/blk-core.c: In function 'blk_queue_enter':
>> block/blk-core.c:661:18: error: passing argument 1 of 'atomic_read' from incompatible pointer type [-Werror=incompatible-pointer-types]
        !atomic_read(&q->mq_freeze_depth) ||
                     ^
   include/linux/wait.h:473:8: note: in definition of macro 'wait_event_interruptible'
     if (!(condition))      \
           ^~~~~~~~~
   In file included from arch/x86/include/asm/msr.h:66:0,
                    from arch/x86/include/asm/processor.h:20,
                    from arch/x86/include/asm/cpufeature.h:4,
                    from arch/x86/include/asm/thread_info.h:52,
                    from include/linux/thread_info.h:54,
                    from arch/x86/include/asm/preempt.h:6,
                    from include/linux/preempt.h:59,
                    from include/linux/spinlock.h:50,
                    from include/linux/seqlock.h:35,
                    from include/linux/time.h:5,
                    from include/linux/stat.h:18,
                    from include/linux/module.h:10,
                    from block/blk-core.c:15:
   arch/x86/include/asm/atomic.h:24:28: note: expected 'const atomic_t * {aka const struct <anonymous> *}' but argument is of type 'int *'
    static __always_inline int atomic_read(const atomic_t *v)
                               ^~~~~~~~~~~
   In file included from include/linux/mmzone.h:9:0,
                    from include/linux/gfp.h:5,
                    from include/linux/kmod.h:22,
                    from include/linux/module.h:13,
                    from block/blk-core.c:15:
>> block/blk-core.c:661:18: error: passing argument 1 of 'atomic_read' from incompatible pointer type [-Werror=incompatible-pointer-types]
        !atomic_read(&q->mq_freeze_depth) ||
                     ^
   include/linux/wait.h:278:7: note: in definition of macro '___wait_event'
      if (condition)      \
          ^~~~~~~~~
   include/linux/wait.h:474:11: note: in expansion of macro '__wait_event_interruptible'
      __ret = __wait_event_interruptible(wq, condition); \
              ^~~~~~~~~~~~~~~~~~~~~~~~~~
>> block/blk-core.c:660:9: note: in expansion of macro 'wait_event_interruptible'
      ret = wait_event_interruptible(q->mq_freeze_wq,
            ^~~~~~~~~~~~~~~~~~~~~~~~
   In file included from arch/x86/include/asm/msr.h:66:0,
                    from arch/x86/include/asm/processor.h:20,
                    from arch/x86/include/asm/cpufeature.h:4,
                    from arch/x86/include/asm/thread_info.h:52,
                    from include/linux/thread_info.h:54,
                    from arch/x86/include/asm/preempt.h:6,
                    from include/linux/preempt.h:59,
                    from include/linux/spinlock.h:50,
                    from include/linux/seqlock.h:35,
                    from include/linux/time.h:5,
                    from include/linux/stat.h:18,
                    from include/linux/module.h:10,
                    from block/blk-core.c:15:
   arch/x86/include/asm/atomic.h:24:28: note: expected 'const atomic_t * {aka const struct <anonymous> *}' but argument is of type 'int *'
    static __always_inline int atomic_read(const atomic_t *v)
                               ^~~~~~~~~~~
   cc1: some warnings being treated as errors

vim +/atomic_read +661 block/blk-core.c

3ef28e83 Dan Williams      2015-10-21  654  		if (percpu_ref_tryget_live(&q->q_usage_counter))
3ef28e83 Dan Williams      2015-10-21  655  			return 0;
3ef28e83 Dan Williams      2015-10-21  656  
6f3b0e8b Christoph Hellwig 2015-11-26  657  		if (nowait)
3ef28e83 Dan Williams      2015-10-21  658  			return -EBUSY;
3ef28e83 Dan Williams      2015-10-21  659  
3ef28e83 Dan Williams      2015-10-21 @660  		ret = wait_event_interruptible(q->mq_freeze_wq,
3ef28e83 Dan Williams      2015-10-21 @661  				!atomic_read(&q->mq_freeze_depth) ||
3ef28e83 Dan Williams      2015-10-21  662  				blk_queue_dying(q));
3ef28e83 Dan Williams      2015-10-21  663  		if (blk_queue_dying(q))
3ef28e83 Dan Williams      2015-10-21  664  			return -ENODEV;

:::::: The code at line 661 was first introduced by commit
:::::: 3ef28e83ab15799742e55fd13243a5f678b04242 block: generic request_queue reference counting

:::::: TO: Dan Williams <dan.j.williams@intel.com>
:::::: CC: Jens Axboe <axboe@fb.com>

---
0-DAY kernel test infrastructure                Open Source Technology Center
https://lists.01.org/pipermail/kbuild-all                   Intel Corporation
kernel test robot Aug. 5, 2016, 6:19 p.m. UTC | #2
Hi Roman,

[auto build test WARNING on block/for-next]
[also build test WARNING on v4.7 next-20160805]
[if your patch is applied to the wrong git tree, please drop us a note to help improve the system]

url:    https://github.com/0day-ci/linux/commits/Roman-Pen/blk-mq-fix-hang-caused-by-freeze-unfreeze-sequence/20160806-014441
base:   https://git.kernel.org/pub/scm/linux/kernel/git/axboe/linux-block.git for-next
config: x86_64-randconfig-x012-201631 (attached as .config)
compiler: gcc-6 (Debian 6.1.1-9) 6.1.1 20160705
reproduce:
        # save the attached .config to linux build tree
        make ARCH=x86_64 

All warnings (new ones prefixed by >>):

   In file included from include/linux/linkage.h:4:0,
                    from include/linux/kernel.h:6,
                    from block/blk-core.c:14:
   block/blk-core.c: In function 'blk_queue_enter':
   block/blk-core.c:661:18: error: passing argument 1 of 'atomic_read' from incompatible pointer type [-Werror=incompatible-pointer-types]
        !atomic_read(&q->mq_freeze_depth) ||
                     ^
   include/linux/compiler.h:151:30: note: in definition of macro '__trace_if'
     if (__builtin_constant_p(!!(cond)) ? !!(cond) :   \
                                 ^~~~
>> include/linux/wait.h:473:2: note: in expansion of macro 'if'
     if (!(condition))      \
     ^~
   block/blk-core.c:660:9: note: in expansion of macro 'wait_event_interruptible'
      ret = wait_event_interruptible(q->mq_freeze_wq,
            ^~~~~~~~~~~~~~~~~~~~~~~~
   In file included from arch/x86/include/asm/msr.h:66:0,
                    from arch/x86/include/asm/processor.h:20,
                    from arch/x86/include/asm/cpufeature.h:4,
                    from arch/x86/include/asm/thread_info.h:52,
                    from include/linux/thread_info.h:54,
                    from arch/x86/include/asm/preempt.h:6,
                    from include/linux/preempt.h:59,
                    from include/linux/spinlock.h:50,
                    from include/linux/seqlock.h:35,
                    from include/linux/time.h:5,
                    from include/linux/stat.h:18,
                    from include/linux/module.h:10,
                    from block/blk-core.c:15:
   arch/x86/include/asm/atomic.h:24:28: note: expected 'const atomic_t * {aka const struct <anonymous> *}' but argument is of type 'int *'
    static __always_inline int atomic_read(const atomic_t *v)
                               ^~~~~~~~~~~
   In file included from include/linux/linkage.h:4:0,
                    from include/linux/kernel.h:6,
                    from block/blk-core.c:14:
   block/blk-core.c:661:18: error: passing argument 1 of 'atomic_read' from incompatible pointer type [-Werror=incompatible-pointer-types]
        !atomic_read(&q->mq_freeze_depth) ||
                     ^
   include/linux/compiler.h:151:42: note: in definition of macro '__trace_if'
     if (__builtin_constant_p(!!(cond)) ? !!(cond) :   \
                                             ^~~~
>> include/linux/wait.h:473:2: note: in expansion of macro 'if'
     if (!(condition))      \
     ^~
   block/blk-core.c:660:9: note: in expansion of macro 'wait_event_interruptible'
      ret = wait_event_interruptible(q->mq_freeze_wq,
            ^~~~~~~~~~~~~~~~~~~~~~~~
   In file included from arch/x86/include/asm/msr.h:66:0,
                    from arch/x86/include/asm/processor.h:20,
                    from arch/x86/include/asm/cpufeature.h:4,
                    from arch/x86/include/asm/thread_info.h:52,
                    from include/linux/thread_info.h:54,
                    from arch/x86/include/asm/preempt.h:6,
                    from include/linux/preempt.h:59,
                    from include/linux/spinlock.h:50,
                    from include/linux/seqlock.h:35,
                    from include/linux/time.h:5,
                    from include/linux/stat.h:18,
                    from include/linux/module.h:10,
                    from block/blk-core.c:15:
   arch/x86/include/asm/atomic.h:24:28: note: expected 'const atomic_t * {aka const struct <anonymous> *}' but argument is of type 'int *'
    static __always_inline int atomic_read(const atomic_t *v)
                               ^~~~~~~~~~~
   In file included from include/linux/linkage.h:4:0,
                    from include/linux/kernel.h:6,
                    from block/blk-core.c:14:
   block/blk-core.c:661:18: error: passing argument 1 of 'atomic_read' from incompatible pointer type [-Werror=incompatible-pointer-types]
        !atomic_read(&q->mq_freeze_depth) ||
                     ^
   include/linux/compiler.h:162:16: note: in definition of macro '__trace_if'
      ______r = !!(cond);     \
                   ^~~~
>> include/linux/wait.h:473:2: note: in expansion of macro 'if'
     if (!(condition))      \
     ^~
   block/blk-core.c:660:9: note: in expansion of macro 'wait_event_interruptible'
      ret = wait_event_interruptible(q->mq_freeze_wq,
            ^~~~~~~~~~~~~~~~~~~~~~~~
   In file included from arch/x86/include/asm/msr.h:66:0,
                    from arch/x86/include/asm/processor.h:20,
                    from arch/x86/include/asm/cpufeature.h:4,
                    from arch/x86/include/asm/thread_info.h:52,
                    from include/linux/thread_info.h:54,
                    from arch/x86/include/asm/preempt.h:6,
                    from include/linux/preempt.h:59,
                    from include/linux/spinlock.h:50,
                    from include/linux/seqlock.h:35,
                    from include/linux/time.h:5,
                    from include/linux/stat.h:18,
                    from include/linux/module.h:10,
                    from block/blk-core.c:15:
   arch/x86/include/asm/atomic.h:24:28: note: expected 'const atomic_t * {aka const struct <anonymous> *}' but argument is of type 'int *'
    static __always_inline int atomic_read(const atomic_t *v)
                               ^~~~~~~~~~~
   In file included from include/linux/linkage.h:4:0,
                    from include/linux/kernel.h:6,
                    from block/blk-core.c:14:
   block/blk-core.c:661:18: error: passing argument 1 of 'atomic_read' from incompatible pointer type [-Werror=incompatible-pointer-types]
        !atomic_read(&q->mq_freeze_depth) ||
                     ^
   include/linux/compiler.h:151:30: note: in definition of macro '__trace_if'
     if (__builtin_constant_p(!!(cond)) ? !!(cond) :   \
                                 ^~~~
   include/linux/wait.h:278:3: note: in expansion of macro 'if'
      if (condition)      \
      ^~
>> include/linux/wait.h:451:2: note: in expansion of macro '___wait_event'
     ___wait_event(wq, condition, TASK_INTERRUPTIBLE, 0, 0,  \
     ^~~~~~~~~~~~~
   include/linux/wait.h:474:11: note: in expansion of macro '__wait_event_interruptible'
      __ret = __wait_event_interruptible(wq, condition); \
              ^~~~~~~~~~~~~~~~~~~~~~~~~~
   block/blk-core.c:660:9: note: in expansion of macro 'wait_event_interruptible'
      ret = wait_event_interruptible(q->mq_freeze_wq,
            ^~~~~~~~~~~~~~~~~~~~~~~~
   In file included from arch/x86/include/asm/msr.h:66:0,
                    from arch/x86/include/asm/processor.h:20,
                    from arch/x86/include/asm/cpufeature.h:4,
                    from arch/x86/include/asm/thread_info.h:52,
                    from include/linux/thread_info.h:54,
                    from arch/x86/include/asm/preempt.h:6,
                    from include/linux/preempt.h:59,
                    from include/linux/spinlock.h:50,
                    from include/linux/seqlock.h:35,
                    from include/linux/time.h:5,
                    from include/linux/stat.h:18,
                    from include/linux/module.h:10,
                    from block/blk-core.c:15:
   arch/x86/include/asm/atomic.h:24:28: note: expected 'const atomic_t * {aka const struct <anonymous> *}' but argument is of type 'int *'
    static __always_inline int atomic_read(const atomic_t *v)
                               ^~~~~~~~~~~
   In file included from include/linux/linkage.h:4:0,
                    from include/linux/kernel.h:6,
                    from block/blk-core.c:14:
   block/blk-core.c:661:18: error: passing argument 1 of 'atomic_read' from incompatible pointer type [-Werror=incompatible-pointer-types]
        !atomic_read(&q->mq_freeze_depth) ||
                     ^
   include/linux/compiler.h:151:42: note: in definition of macro '__trace_if'
     if (__builtin_constant_p(!!(cond)) ? !!(cond) :   \
                                             ^~~~
   include/linux/wait.h:278:3: note: in expansion of macro 'if'
      if (condition)      \
      ^~
>> include/linux/wait.h:451:2: note: in expansion of macro '___wait_event'
     ___wait_event(wq, condition, TASK_INTERRUPTIBLE, 0, 0,  \
     ^~~~~~~~~~~~~
   include/linux/wait.h:474:11: note: in expansion of macro '__wait_event_interruptible'
      __ret = __wait_event_interruptible(wq, condition); \
              ^~~~~~~~~~~~~~~~~~~~~~~~~~
   block/blk-core.c:660:9: note: in expansion of macro 'wait_event_interruptible'
      ret = wait_event_interruptible(q->mq_freeze_wq,
            ^~~~~~~~~~~~~~~~~~~~~~~~
   In file included from arch/x86/include/asm/msr.h:66:0,
                    from arch/x86/include/asm/processor.h:20,
                    from arch/x86/include/asm/cpufeature.h:4,
                    from arch/x86/include/asm/thread_info.h:52,
                    from include/linux/thread_info.h:54,
                    from arch/x86/include/asm/preempt.h:6,
                    from include/linux/preempt.h:59,
                    from include/linux/spinlock.h:50,
                    from include/linux/seqlock.h:35,
                    from include/linux/time.h:5,
                    from include/linux/stat.h:18,
                    from include/linux/module.h:10,
                    from block/blk-core.c:15:
   arch/x86/include/asm/atomic.h:24:28: note: expected 'const atomic_t * {aka const struct <anonymous> *}' but argument is of type 'int *'
    static __always_inline int atomic_read(const atomic_t *v)
                               ^~~~~~~~~~~
   In file included from include/linux/linkage.h:4:0,
                    from include/linux/kernel.h:6,
                    from block/blk-core.c:14:
   block/blk-core.c:661:18: error: passing argument 1 of 'atomic_read' from incompatible pointer type [-Werror=incompatible-pointer-types]
        !atomic_read(&q->mq_freeze_depth) ||
                     ^
   include/linux/compiler.h:162:16: note: in definition of macro '__trace_if'
      ______r = !!(cond);     \
                   ^~~~
   include/linux/wait.h:278:3: note: in expansion of macro 'if'
      if (condition)      \
      ^~
>> include/linux/wait.h:451:2: note: in expansion of macro '___wait_event'
     ___wait_event(wq, condition, TASK_INTERRUPTIBLE, 0, 0,  \
     ^~~~~~~~~~~~~
   include/linux/wait.h:474:11: note: in expansion of macro '__wait_event_interruptible'
      __ret = __wait_event_interruptible(wq, condition); \
              ^~~~~~~~~~~~~~~~~~~~~~~~~~
   block/blk-core.c:660:9: note: in expansion of macro 'wait_event_interruptible'
      ret = wait_event_interruptible(q->mq_freeze_wq,
            ^~~~~~~~~~~~~~~~~~~~~~~~
   In file included from arch/x86/include/asm/msr.h:66:0,
                    from arch/x86/include/asm/processor.h:20,
                    from arch/x86/include/asm/cpufeature.h:4,
                    from arch/x86/include/asm/thread_info.h:52,
                    from include/linux/thread_info.h:54,
                    from arch/x86/include/asm/preempt.h:6,
                    from include/linux/preempt.h:59,
                    from include/linux/spinlock.h:50,
                    from include/linux/seqlock.h:35,
                    from include/linux/time.h:5,
                    from include/linux/stat.h:18,
                    from include/linux/module.h:10,
                    from block/blk-core.c:15:
   arch/x86/include/asm/atomic.h:24:28: note: expected 'const atomic_t * {aka const struct <anonymous> *}' but argument is of type 'int *'
    static __always_inline int atomic_read(const atomic_t *v)
                               ^~~~~~~~~~~
   cc1: some warnings being treated as errors

vim +/if +473 include/linux/wait.h

41a1431b1 Peter Zijlstra 2013-10-02  272  	else								\
c2d816443 Oleg Nesterov  2013-10-07  273  		__wait.flags = 0;					\
c2d816443 Oleg Nesterov  2013-10-07  274  									\
c2d816443 Oleg Nesterov  2013-10-07  275  	for (;;) {							\
c2d816443 Oleg Nesterov  2013-10-07  276  		long __int = prepare_to_wait_event(&wq, &__wait, state);\
41a1431b1 Peter Zijlstra 2013-10-02  277  									\
41a1431b1 Peter Zijlstra 2013-10-02 @278  		if (condition)						\
41a1431b1 Peter Zijlstra 2013-10-02  279  			break;						\
41a1431b1 Peter Zijlstra 2013-10-02  280  									\
c2d816443 Oleg Nesterov  2013-10-07  281  		if (___wait_is_interruptible(state) && __int) {		\
c2d816443 Oleg Nesterov  2013-10-07  282  			__ret = __int;					\
41a1431b1 Peter Zijlstra 2013-10-02  283  			if (exclusive) {				\
41a1431b1 Peter Zijlstra 2013-10-02  284  				abort_exclusive_wait(&wq, &__wait,	\
41a1431b1 Peter Zijlstra 2013-10-02  285  						     state, NULL);	\
41a1431b1 Peter Zijlstra 2013-10-02  286  				goto __out;				\
41a1431b1 Peter Zijlstra 2013-10-02  287  			}						\
41a1431b1 Peter Zijlstra 2013-10-02  288  			break;						\
41a1431b1 Peter Zijlstra 2013-10-02  289  		}							\
41a1431b1 Peter Zijlstra 2013-10-02  290  									\
41a1431b1 Peter Zijlstra 2013-10-02  291  		cmd;							\
41a1431b1 Peter Zijlstra 2013-10-02  292  	}								\
41a1431b1 Peter Zijlstra 2013-10-02  293  	finish_wait(&wq, &__wait);					\
35a2af94c Peter Zijlstra 2013-10-02  294  __out:	__ret;								\
35a2af94c Peter Zijlstra 2013-10-02  295  })
41a1431b1 Peter Zijlstra 2013-10-02  296  
^1da177e4 Linus Torvalds 2005-04-16  297  #define __wait_event(wq, condition)					\
35a2af94c Peter Zijlstra 2013-10-02  298  	(void)___wait_event(wq, condition, TASK_UNINTERRUPTIBLE, 0, 0,	\
35a2af94c Peter Zijlstra 2013-10-02  299  			    schedule())
^1da177e4 Linus Torvalds 2005-04-16  300  
^1da177e4 Linus Torvalds 2005-04-16  301  /**
^1da177e4 Linus Torvalds 2005-04-16  302   * wait_event - sleep until a condition gets true
^1da177e4 Linus Torvalds 2005-04-16  303   * @wq: the waitqueue to wait on
^1da177e4 Linus Torvalds 2005-04-16  304   * @condition: a C expression for the event to wait for
^1da177e4 Linus Torvalds 2005-04-16  305   *
^1da177e4 Linus Torvalds 2005-04-16  306   * The process is put to sleep (TASK_UNINTERRUPTIBLE) until the
^1da177e4 Linus Torvalds 2005-04-16  307   * @condition evaluates to true. The @condition is checked each time
^1da177e4 Linus Torvalds 2005-04-16  308   * the waitqueue @wq is woken up.
^1da177e4 Linus Torvalds 2005-04-16  309   *
^1da177e4 Linus Torvalds 2005-04-16  310   * wake_up() has to be called after changing any variable that could
^1da177e4 Linus Torvalds 2005-04-16  311   * change the result of the wait condition.
^1da177e4 Linus Torvalds 2005-04-16  312   */
^1da177e4 Linus Torvalds 2005-04-16  313  #define wait_event(wq, condition)					\
^1da177e4 Linus Torvalds 2005-04-16  314  do {									\
e22b886a8 Peter Zijlstra 2014-09-24  315  	might_sleep();							\
^1da177e4 Linus Torvalds 2005-04-16  316  	if (condition)							\
^1da177e4 Linus Torvalds 2005-04-16  317  		break;							\
^1da177e4 Linus Torvalds 2005-04-16  318  	__wait_event(wq, condition);					\
^1da177e4 Linus Torvalds 2005-04-16  319  } while (0)
^1da177e4 Linus Torvalds 2005-04-16  320  
2c5612465 Peter Zijlstra 2015-02-03  321  #define __io_wait_event(wq, condition)					\
2c5612465 Peter Zijlstra 2015-02-03  322  	(void)___wait_event(wq, condition, TASK_UNINTERRUPTIBLE, 0, 0,	\
2c5612465 Peter Zijlstra 2015-02-03  323  			    io_schedule())
2c5612465 Peter Zijlstra 2015-02-03  324  
2c5612465 Peter Zijlstra 2015-02-03  325  /*
2c5612465 Peter Zijlstra 2015-02-03  326   * io_wait_event() -- like wait_event() but with io_schedule()
2c5612465 Peter Zijlstra 2015-02-03  327   */
2c5612465 Peter Zijlstra 2015-02-03  328  #define io_wait_event(wq, condition)					\
2c5612465 Peter Zijlstra 2015-02-03  329  do {									\
2c5612465 Peter Zijlstra 2015-02-03  330  	might_sleep();							\
2c5612465 Peter Zijlstra 2015-02-03  331  	if (condition)							\
2c5612465 Peter Zijlstra 2015-02-03  332  		break;							\
2c5612465 Peter Zijlstra 2015-02-03  333  	__io_wait_event(wq, condition);					\
2c5612465 Peter Zijlstra 2015-02-03  334  } while (0)
2c5612465 Peter Zijlstra 2015-02-03  335  
36df04bc5 Peter Zijlstra 2014-10-29  336  #define __wait_event_freezable(wq, condition)				\
36df04bc5 Peter Zijlstra 2014-10-29  337  	___wait_event(wq, condition, TASK_INTERRUPTIBLE, 0, 0,		\
36df04bc5 Peter Zijlstra 2014-10-29  338  			    schedule(); try_to_freeze())
36df04bc5 Peter Zijlstra 2014-10-29  339  
36df04bc5 Peter Zijlstra 2014-10-29  340  /**
f4bcfa1da Stafford Horne 2016-02-23  341   * wait_event_freezable - sleep (or freeze) until a condition gets true
36df04bc5 Peter Zijlstra 2014-10-29  342   * @wq: the waitqueue to wait on
36df04bc5 Peter Zijlstra 2014-10-29  343   * @condition: a C expression for the event to wait for
36df04bc5 Peter Zijlstra 2014-10-29  344   *
36df04bc5 Peter Zijlstra 2014-10-29  345   * The process is put to sleep (TASK_INTERRUPTIBLE -- so as not to contribute
36df04bc5 Peter Zijlstra 2014-10-29  346   * to system load) until the @condition evaluates to true. The
36df04bc5 Peter Zijlstra 2014-10-29  347   * @condition is checked each time the waitqueue @wq is woken up.
36df04bc5 Peter Zijlstra 2014-10-29  348   *
36df04bc5 Peter Zijlstra 2014-10-29  349   * wake_up() has to be called after changing any variable that could
36df04bc5 Peter Zijlstra 2014-10-29  350   * change the result of the wait condition.
36df04bc5 Peter Zijlstra 2014-10-29  351   */
36df04bc5 Peter Zijlstra 2014-10-29  352  #define wait_event_freezable(wq, condition)				\
36df04bc5 Peter Zijlstra 2014-10-29  353  ({									\
36df04bc5 Peter Zijlstra 2014-10-29  354  	int __ret = 0;							\
36df04bc5 Peter Zijlstra 2014-10-29  355  	might_sleep();							\
36df04bc5 Peter Zijlstra 2014-10-29  356  	if (!(condition))						\
36df04bc5 Peter Zijlstra 2014-10-29  357  		__ret = __wait_event_freezable(wq, condition);		\
36df04bc5 Peter Zijlstra 2014-10-29  358  	__ret;								\
36df04bc5 Peter Zijlstra 2014-10-29  359  })
36df04bc5 Peter Zijlstra 2014-10-29  360  
35a2af94c Peter Zijlstra 2013-10-02  361  #define __wait_event_timeout(wq, condition, timeout)			\
35a2af94c Peter Zijlstra 2013-10-02  362  	___wait_event(wq, ___wait_cond_timeout(condition),		\
35a2af94c Peter Zijlstra 2013-10-02  363  		      TASK_UNINTERRUPTIBLE, 0, timeout,			\
35a2af94c Peter Zijlstra 2013-10-02  364  		      __ret = schedule_timeout(__ret))
^1da177e4 Linus Torvalds 2005-04-16  365  
^1da177e4 Linus Torvalds 2005-04-16  366  /**
^1da177e4 Linus Torvalds 2005-04-16  367   * wait_event_timeout - sleep until a condition gets true or a timeout elapses
^1da177e4 Linus Torvalds 2005-04-16  368   * @wq: the waitqueue to wait on
^1da177e4 Linus Torvalds 2005-04-16  369   * @condition: a C expression for the event to wait for
^1da177e4 Linus Torvalds 2005-04-16  370   * @timeout: timeout, in jiffies
^1da177e4 Linus Torvalds 2005-04-16  371   *
^1da177e4 Linus Torvalds 2005-04-16  372   * The process is put to sleep (TASK_UNINTERRUPTIBLE) until the
^1da177e4 Linus Torvalds 2005-04-16  373   * @condition evaluates to true. The @condition is checked each time
^1da177e4 Linus Torvalds 2005-04-16  374   * the waitqueue @wq is woken up.
^1da177e4 Linus Torvalds 2005-04-16  375   *
^1da177e4 Linus Torvalds 2005-04-16  376   * wake_up() has to be called after changing any variable that could
^1da177e4 Linus Torvalds 2005-04-16  377   * change the result of the wait condition.
^1da177e4 Linus Torvalds 2005-04-16  378   *
6b44f5190 Scot Doyle     2014-08-24  379   * Returns:
6b44f5190 Scot Doyle     2014-08-24  380   * 0 if the @condition evaluated to %false after the @timeout elapsed,
6b44f5190 Scot Doyle     2014-08-24  381   * 1 if the @condition evaluated to %true after the @timeout elapsed,
6b44f5190 Scot Doyle     2014-08-24  382   * or the remaining jiffies (at least 1) if the @condition evaluated
6b44f5190 Scot Doyle     2014-08-24  383   * to %true before the @timeout elapsed.
^1da177e4 Linus Torvalds 2005-04-16  384   */
^1da177e4 Linus Torvalds 2005-04-16  385  #define wait_event_timeout(wq, condition, timeout)			\
^1da177e4 Linus Torvalds 2005-04-16  386  ({									\
^1da177e4 Linus Torvalds 2005-04-16  387  	long __ret = timeout;						\
e22b886a8 Peter Zijlstra 2014-09-24  388  	might_sleep();							\
8922915b3 Oleg Nesterov  2013-10-07  389  	if (!___wait_cond_timeout(condition))				\
35a2af94c Peter Zijlstra 2013-10-02  390  		__ret = __wait_event_timeout(wq, condition, timeout);	\
^1da177e4 Linus Torvalds 2005-04-16  391  	__ret;								\
^1da177e4 Linus Torvalds 2005-04-16  392  })
^1da177e4 Linus Torvalds 2005-04-16  393  
36df04bc5 Peter Zijlstra 2014-10-29  394  #define __wait_event_freezable_timeout(wq, condition, timeout)		\
36df04bc5 Peter Zijlstra 2014-10-29  395  	___wait_event(wq, ___wait_cond_timeout(condition),		\
36df04bc5 Peter Zijlstra 2014-10-29  396  		      TASK_INTERRUPTIBLE, 0, timeout,			\
36df04bc5 Peter Zijlstra 2014-10-29  397  		      __ret = schedule_timeout(__ret); try_to_freeze())
36df04bc5 Peter Zijlstra 2014-10-29  398  
36df04bc5 Peter Zijlstra 2014-10-29  399  /*
36df04bc5 Peter Zijlstra 2014-10-29  400   * like wait_event_timeout() -- except it uses TASK_INTERRUPTIBLE to avoid
36df04bc5 Peter Zijlstra 2014-10-29  401   * increasing load and is freezable.
36df04bc5 Peter Zijlstra 2014-10-29  402   */
36df04bc5 Peter Zijlstra 2014-10-29  403  #define wait_event_freezable_timeout(wq, condition, timeout)		\
36df04bc5 Peter Zijlstra 2014-10-29  404  ({									\
36df04bc5 Peter Zijlstra 2014-10-29  405  	long __ret = timeout;						\
36df04bc5 Peter Zijlstra 2014-10-29  406  	might_sleep();							\
36df04bc5 Peter Zijlstra 2014-10-29  407  	if (!___wait_cond_timeout(condition))				\
36df04bc5 Peter Zijlstra 2014-10-29  408  		__ret = __wait_event_freezable_timeout(wq, condition, timeout);	\
36df04bc5 Peter Zijlstra 2014-10-29  409  	__ret;								\
36df04bc5 Peter Zijlstra 2014-10-29  410  })
36df04bc5 Peter Zijlstra 2014-10-29  411  
9f3520c31 Yuanhan Liu    2015-05-08  412  #define __wait_event_exclusive_cmd(wq, condition, cmd1, cmd2)		\
9f3520c31 Yuanhan Liu    2015-05-08  413  	(void)___wait_event(wq, condition, TASK_UNINTERRUPTIBLE, 1, 0,	\
9f3520c31 Yuanhan Liu    2015-05-08  414  			    cmd1; schedule(); cmd2)
9f3520c31 Yuanhan Liu    2015-05-08  415  /*
9f3520c31 Yuanhan Liu    2015-05-08  416   * Just like wait_event_cmd(), except it sets exclusive flag
9f3520c31 Yuanhan Liu    2015-05-08  417   */
9f3520c31 Yuanhan Liu    2015-05-08  418  #define wait_event_exclusive_cmd(wq, condition, cmd1, cmd2)		\
9f3520c31 Yuanhan Liu    2015-05-08  419  do {									\
9f3520c31 Yuanhan Liu    2015-05-08  420  	if (condition)							\
9f3520c31 Yuanhan Liu    2015-05-08  421  		break;							\
9f3520c31 Yuanhan Liu    2015-05-08  422  	__wait_event_exclusive_cmd(wq, condition, cmd1, cmd2);		\
9f3520c31 Yuanhan Liu    2015-05-08  423  } while (0)
9f3520c31 Yuanhan Liu    2015-05-08  424  
82e06c811 Shaohua Li     2013-11-14  425  #define __wait_event_cmd(wq, condition, cmd1, cmd2)			\
82e06c811 Shaohua Li     2013-11-14  426  	(void)___wait_event(wq, condition, TASK_UNINTERRUPTIBLE, 0, 0,	\
82e06c811 Shaohua Li     2013-11-14  427  			    cmd1; schedule(); cmd2)
82e06c811 Shaohua Li     2013-11-14  428  
82e06c811 Shaohua Li     2013-11-14  429  /**
82e06c811 Shaohua Li     2013-11-14  430   * wait_event_cmd - sleep until a condition gets true
82e06c811 Shaohua Li     2013-11-14  431   * @wq: the waitqueue to wait on
82e06c811 Shaohua Li     2013-11-14  432   * @condition: a C expression for the event to wait for
f434f7afa Masanari Iida  2014-01-22  433   * @cmd1: the command will be executed before sleep
f434f7afa Masanari Iida  2014-01-22  434   * @cmd2: the command will be executed after sleep
82e06c811 Shaohua Li     2013-11-14  435   *
82e06c811 Shaohua Li     2013-11-14  436   * The process is put to sleep (TASK_UNINTERRUPTIBLE) until the
82e06c811 Shaohua Li     2013-11-14  437   * @condition evaluates to true. The @condition is checked each time
82e06c811 Shaohua Li     2013-11-14  438   * the waitqueue @wq is woken up.
82e06c811 Shaohua Li     2013-11-14  439   *
82e06c811 Shaohua Li     2013-11-14  440   * wake_up() has to be called after changing any variable that could
82e06c811 Shaohua Li     2013-11-14  441   * change the result of the wait condition.
82e06c811 Shaohua Li     2013-11-14  442   */
82e06c811 Shaohua Li     2013-11-14  443  #define wait_event_cmd(wq, condition, cmd1, cmd2)			\
82e06c811 Shaohua Li     2013-11-14  444  do {									\
82e06c811 Shaohua Li     2013-11-14  445  	if (condition)							\
82e06c811 Shaohua Li     2013-11-14  446  		break;							\
82e06c811 Shaohua Li     2013-11-14  447  	__wait_event_cmd(wq, condition, cmd1, cmd2);			\
82e06c811 Shaohua Li     2013-11-14  448  } while (0)
82e06c811 Shaohua Li     2013-11-14  449  
35a2af94c Peter Zijlstra 2013-10-02  450  #define __wait_event_interruptible(wq, condition)			\
35a2af94c Peter Zijlstra 2013-10-02 @451  	___wait_event(wq, condition, TASK_INTERRUPTIBLE, 0, 0,		\
f13f4c41c Peter Zijlstra 2013-10-02  452  		      schedule())
^1da177e4 Linus Torvalds 2005-04-16  453  
^1da177e4 Linus Torvalds 2005-04-16  454  /**
^1da177e4 Linus Torvalds 2005-04-16  455   * wait_event_interruptible - sleep until a condition gets true
^1da177e4 Linus Torvalds 2005-04-16  456   * @wq: the waitqueue to wait on
^1da177e4 Linus Torvalds 2005-04-16  457   * @condition: a C expression for the event to wait for
^1da177e4 Linus Torvalds 2005-04-16  458   *
^1da177e4 Linus Torvalds 2005-04-16  459   * The process is put to sleep (TASK_INTERRUPTIBLE) until the
^1da177e4 Linus Torvalds 2005-04-16  460   * @condition evaluates to true or a signal is received.
^1da177e4 Linus Torvalds 2005-04-16  461   * The @condition is checked each time the waitqueue @wq is woken up.
^1da177e4 Linus Torvalds 2005-04-16  462   *
^1da177e4 Linus Torvalds 2005-04-16  463   * wake_up() has to be called after changing any variable that could
^1da177e4 Linus Torvalds 2005-04-16  464   * change the result of the wait condition.
^1da177e4 Linus Torvalds 2005-04-16  465   *
^1da177e4 Linus Torvalds 2005-04-16  466   * The function will return -ERESTARTSYS if it was interrupted by a
^1da177e4 Linus Torvalds 2005-04-16  467   * signal and 0 if @condition evaluated to true.
^1da177e4 Linus Torvalds 2005-04-16  468   */
^1da177e4 Linus Torvalds 2005-04-16  469  #define wait_event_interruptible(wq, condition)				\
^1da177e4 Linus Torvalds 2005-04-16  470  ({									\
^1da177e4 Linus Torvalds 2005-04-16  471  	int __ret = 0;							\
e22b886a8 Peter Zijlstra 2014-09-24  472  	might_sleep();							\
^1da177e4 Linus Torvalds 2005-04-16 @473  	if (!(condition))						\
35a2af94c Peter Zijlstra 2013-10-02  474  		__ret = __wait_event_interruptible(wq, condition);	\
^1da177e4 Linus Torvalds 2005-04-16  475  	__ret;								\
^1da177e4 Linus Torvalds 2005-04-16  476  })

:::::: The code at line 473 was first introduced by commit
:::::: 1da177e4c3f41524e886b7f1b8a0c1fc7321cac2 Linux-2.6.12-rc2

:::::: TO: Linus Torvalds <torvalds@ppc970.osdl.org>
:::::: CC: Linus Torvalds <torvalds@ppc970.osdl.org>

---
0-DAY kernel test infrastructure                Open Source Technology Center
https://lists.01.org/pipermail/kbuild-all                   Intel Corporation
kernel test robot Aug. 5, 2016, 6:28 p.m. UTC | #3
Hi Roman,

[auto build test WARNING on block/for-next]
[also build test WARNING on v4.7 next-20160805]
[if your patch is applied to the wrong git tree, please drop us a note to help improve the system]

url:    https://github.com/0day-ci/linux/commits/Roman-Pen/blk-mq-fix-hang-caused-by-freeze-unfreeze-sequence/20160806-014441
base:   https://git.kernel.org/pub/scm/linux/kernel/git/axboe/linux-block.git for-next
config: tile-allyesconfig (attached as .config)
compiler: tilegx-linux-gcc (GCC) 4.6.2
reproduce:
        wget https://git.kernel.org/cgit/linux/kernel/git/wfg/lkp-tests.git/plain/sbin/make.cross -O ~/bin/make.cross
        chmod +x ~/bin/make.cross
        # save the attached .config to linux build tree
        make.cross ARCH=tile 

All warnings (new ones prefixed by >>):

   block/blk-core.c: In function 'blk_queue_enter':
>> block/blk-core.c:660:3: warning: passing argument 1 of 'atomic_read' from incompatible pointer type [enabled by default]
   arch/tile/include/asm/atomic.h:35:19: note: expected 'const struct atomic_t *' but argument is of type 'int *'
>> block/blk-core.c:660:3: warning: passing argument 1 of 'atomic_read' from incompatible pointer type [enabled by default]
   arch/tile/include/asm/atomic.h:35:19: note: expected 'const struct atomic_t *' but argument is of type 'int *'

vim +/atomic_read +660 block/blk-core.c

^1da177e drivers/block/ll_rw_blk.c Linus Torvalds    2005-04-16  644  {
c304a51b block/blk-core.c          Ezequiel Garcia   2012-11-10  645  	return blk_alloc_queue_node(gfp_mask, NUMA_NO_NODE);
1946089a drivers/block/ll_rw_blk.c Christoph Lameter 2005-06-23  646  }
1946089a drivers/block/ll_rw_blk.c Christoph Lameter 2005-06-23  647  EXPORT_SYMBOL(blk_alloc_queue);
^1da177e drivers/block/ll_rw_blk.c Linus Torvalds    2005-04-16  648  
6f3b0e8b block/blk-core.c          Christoph Hellwig 2015-11-26  649  int blk_queue_enter(struct request_queue *q, bool nowait)
3ef28e83 block/blk-core.c          Dan Williams      2015-10-21  650  {
3ef28e83 block/blk-core.c          Dan Williams      2015-10-21  651  	while (true) {
3ef28e83 block/blk-core.c          Dan Williams      2015-10-21  652  		int ret;
3ef28e83 block/blk-core.c          Dan Williams      2015-10-21  653  
3ef28e83 block/blk-core.c          Dan Williams      2015-10-21  654  		if (percpu_ref_tryget_live(&q->q_usage_counter))
3ef28e83 block/blk-core.c          Dan Williams      2015-10-21  655  			return 0;
3ef28e83 block/blk-core.c          Dan Williams      2015-10-21  656  
6f3b0e8b block/blk-core.c          Christoph Hellwig 2015-11-26  657  		if (nowait)
3ef28e83 block/blk-core.c          Dan Williams      2015-10-21  658  			return -EBUSY;
3ef28e83 block/blk-core.c          Dan Williams      2015-10-21  659  
3ef28e83 block/blk-core.c          Dan Williams      2015-10-21 @660  		ret = wait_event_interruptible(q->mq_freeze_wq,
3ef28e83 block/blk-core.c          Dan Williams      2015-10-21  661  				!atomic_read(&q->mq_freeze_depth) ||
3ef28e83 block/blk-core.c          Dan Williams      2015-10-21  662  				blk_queue_dying(q));
3ef28e83 block/blk-core.c          Dan Williams      2015-10-21  663  		if (blk_queue_dying(q))
3ef28e83 block/blk-core.c          Dan Williams      2015-10-21  664  			return -ENODEV;
3ef28e83 block/blk-core.c          Dan Williams      2015-10-21  665  		if (ret)
3ef28e83 block/blk-core.c          Dan Williams      2015-10-21  666  			return ret;
3ef28e83 block/blk-core.c          Dan Williams      2015-10-21  667  	}
3ef28e83 block/blk-core.c          Dan Williams      2015-10-21  668  }

:::::: The code at line 660 was first introduced by commit
:::::: 3ef28e83ab15799742e55fd13243a5f678b04242 block: generic request_queue reference counting

:::::: TO: Dan Williams <dan.j.williams@intel.com>
:::::: CC: Jens Axboe <axboe@fb.com>

---
0-DAY kernel test infrastructure                Open Source Technology Center
https://lists.01.org/pipermail/kbuild-all                   Intel Corporation
diff mbox

Patch

diff --git a/block/blk-core.c b/block/blk-core.c
index ef78848..01dcb02 100644
--- a/block/blk-core.c
+++ b/block/blk-core.c
@@ -740,6 +740,7 @@  struct request_queue *blk_alloc_queue_node(gfp_t gfp_mask, int node_id)
 	__set_bit(QUEUE_FLAG_BYPASS, &q->queue_flags);
 
 	init_waitqueue_head(&q->mq_freeze_wq);
+	mutex_init(&q->mq_freeze_lock);
 
 	/*
 	 * Init percpu_ref in atomic mode so that it's faster to shutdown.
diff --git a/block/blk-mq.c b/block/blk-mq.c
index 6d6f8fe..1f3e81b 100644
--- a/block/blk-mq.c
+++ b/block/blk-mq.c
@@ -80,13 +80,13 @@  static void blk_mq_hctx_clear_pending(struct blk_mq_hw_ctx *hctx,
 
 void blk_mq_freeze_queue_start(struct request_queue *q)
 {
-	int freeze_depth;
-
-	freeze_depth = atomic_inc_return(&q->mq_freeze_depth);
-	if (freeze_depth == 1) {
+	mutex_lock(&q->mq_freeze_lock);
+	if (++q->mq_freeze_depth == 1) {
 		percpu_ref_kill(&q->q_usage_counter);
+		mutex_unlock(&q->mq_freeze_lock);
 		blk_mq_run_hw_queues(q, false);
-	}
+	} else
+		mutex_unlock(&q->mq_freeze_lock);
 }
 EXPORT_SYMBOL_GPL(blk_mq_freeze_queue_start);
 
@@ -124,14 +124,14 @@  EXPORT_SYMBOL_GPL(blk_mq_freeze_queue);
 
 void blk_mq_unfreeze_queue(struct request_queue *q)
 {
-	int freeze_depth;
-
-	freeze_depth = atomic_dec_return(&q->mq_freeze_depth);
-	WARN_ON_ONCE(freeze_depth < 0);
-	if (!freeze_depth) {
+	mutex_lock(&q->mq_freeze_lock);
+	q->mq_freeze_depth--;
+	WARN_ON_ONCE(q->mq_freeze_depth < 0);
+	if (!q->mq_freeze_depth) {
 		percpu_ref_reinit(&q->q_usage_counter);
 		wake_up_all(&q->mq_freeze_wq);
 	}
+	mutex_unlock(&q->mq_freeze_lock);
 }
 EXPORT_SYMBOL_GPL(blk_mq_unfreeze_queue);
 
@@ -2105,7 +2105,7 @@  void blk_mq_free_queue(struct request_queue *q)
 static void blk_mq_queue_reinit(struct request_queue *q,
 				const struct cpumask *online_mask)
 {
-	WARN_ON_ONCE(!atomic_read(&q->mq_freeze_depth));
+	WARN_ON_ONCE(!q->mq_freeze_depth);
 
 	blk_mq_sysfs_unregister(q);
 
diff --git a/include/linux/blkdev.h b/include/linux/blkdev.h
index f6ff9d1..d692c16 100644
--- a/include/linux/blkdev.h
+++ b/include/linux/blkdev.h
@@ -445,7 +445,7 @@  struct request_queue {
 	struct mutex		sysfs_lock;
 
 	int			bypass_depth;
-	atomic_t		mq_freeze_depth;
+	int			mq_freeze_depth;
 
 #if defined(CONFIG_BLK_DEV_BSG)
 	bsg_job_fn		*bsg_job_fn;
@@ -459,6 +459,11 @@  struct request_queue {
 #endif
 	struct rcu_head		rcu_head;
 	wait_queue_head_t	mq_freeze_wq;
+	/*
+	 * Protect concurrent access to q_usage_counter by
+	 * percpu_ref_kill() and percpu_ref_reinit().
+	 */
+	struct mutex		mq_freeze_lock;
 	struct percpu_ref	q_usage_counter;
 	struct list_head	all_q_node;