Skip to content

Commit

Permalink
dm mpath: fix infinite recursion in ioctl when no paths and !queue_if…
Browse files Browse the repository at this point in the history
…_no_path

In multipath_prepare_ioctl(),
  - pgpath is a path selected from available paths
  - m->queue_io is true if we cannot send a request immediately to
    paths, either because:
      * there is no available path
      * the path group needs activation (pg_init)
          - pg_init is not started
          - pg_init is still running
  - m->queue_if_no_path is true if the device is configured to queue
    I/O if there are no available paths

If !pgpath && !m->queue_if_no_path, the handler should return -EIO.
However in the course of refactoring the condition check has broken
and returns success in that case.  Since bdev points to the dm device
itself, dm_blk_ioctl() calls __blk_dev_driver_ioctl() for itself and
recurses until crash.

You could reproduce the problem like this:

  # dmsetup create mp --table '0 1024 multipath 0 0 0 0'
  # sg_inq /dev/mapper/mp
  <crash>
  [  172.648615] BUG: unable to handle kernel paging request at fffffffc81b10268
  [  172.662843] PGD 19dd067 PUD 0
  [  172.666269] Thread overran stack, or stack corrupted
  [  172.671808] Oops: 0000 [#1] SMP
  ...

Fix the condition check with some clarifications.

Fixes: e56f81e ("dm: refactor ioctl handling")
Signed-off-by: Jun'ichi Nomura <[email protected]>
Cc: Christoph Hellwig <[email protected]>
Cc: Mike Snitzer <[email protected]>
Signed-off-by: Mike Snitzer <[email protected]>
  • Loading branch information
nomuranec authored and snitm committed Nov 17, 2015
1 parent 647a20d commit 43e43c9
Showing 1 changed file with 15 additions and 13 deletions.
28 changes: 15 additions & 13 deletions drivers/md/dm-mpath.c
Original file line number Diff line number Diff line change
Expand Up @@ -1537,29 +1537,31 @@ static int multipath_prepare_ioctl(struct dm_target *ti,
struct block_device **bdev, fmode_t *mode)
{
struct multipath *m = ti->private;
struct pgpath *pgpath;
unsigned long flags;
int r;

r = 0;

spin_lock_irqsave(&m->lock, flags);

if (!m->current_pgpath)
__choose_pgpath(m, 0);

pgpath = m->current_pgpath;

if (pgpath) {
*bdev = pgpath->path.dev->bdev;
*mode = pgpath->path.dev->mode;
if (m->current_pgpath) {
if (!m->queue_io) {
*bdev = m->current_pgpath->path.dev->bdev;
*mode = m->current_pgpath->path.dev->mode;
r = 0;
} else {
/* pg_init has not started or completed */
r = -ENOTCONN;
}
} else {
/* No path is available */
if (m->queue_if_no_path)
r = -ENOTCONN;
else
r = -EIO;
}

if ((pgpath && m->queue_io) || (!pgpath && m->queue_if_no_path))
r = -ENOTCONN;
else if (!*bdev)
r = -EIO;

spin_unlock_irqrestore(&m->lock, flags);

if (r == -ENOTCONN) {
Expand Down

0 comments on commit 43e43c9

Please sign in to comment.