dm mpath: fix infinite recursion in ioctl when no paths and !queue_if_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: e56f81e0b0
("dm: refactor ioctl handling")
Signed-off-by: Jun'ichi Nomura <j-nomura@ce.jp.nec.com>
Cc: Christoph Hellwig <hch@lst.de>
Cc: Mike Snitzer <snitzer@redhat.com>
Signed-off-by: Mike Snitzer <snitzer@redhat.com>
This commit is contained in:
parent
647a20d5ca
commit
43e43c9ea6
|
@ -1537,29 +1537,31 @@ static int multipath_prepare_ioctl(struct dm_target *ti,
|
||||||
struct block_device **bdev, fmode_t *mode)
|
struct block_device **bdev, fmode_t *mode)
|
||||||
{
|
{
|
||||||
struct multipath *m = ti->private;
|
struct multipath *m = ti->private;
|
||||||
struct pgpath *pgpath;
|
|
||||||
unsigned long flags;
|
unsigned long flags;
|
||||||
int r;
|
int r;
|
||||||
|
|
||||||
r = 0;
|
|
||||||
|
|
||||||
spin_lock_irqsave(&m->lock, flags);
|
spin_lock_irqsave(&m->lock, flags);
|
||||||
|
|
||||||
if (!m->current_pgpath)
|
if (!m->current_pgpath)
|
||||||
__choose_pgpath(m, 0);
|
__choose_pgpath(m, 0);
|
||||||
|
|
||||||
pgpath = m->current_pgpath;
|
if (m->current_pgpath) {
|
||||||
|
if (!m->queue_io) {
|
||||||
if (pgpath) {
|
*bdev = m->current_pgpath->path.dev->bdev;
|
||||||
*bdev = pgpath->path.dev->bdev;
|
*mode = m->current_pgpath->path.dev->mode;
|
||||||
*mode = 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);
|
spin_unlock_irqrestore(&m->lock, flags);
|
||||||
|
|
||||||
if (r == -ENOTCONN) {
|
if (r == -ENOTCONN) {
|
||||||
|
|
Loading…
Reference in New Issue