From: Hannes Reinecke Date: Thu, 19 Nov 2009 13:54:56 +0100 Subject: Accept failed paths for multipath maps References: bnc#458037,bnc#458393 Patch-Mainline: submitted to dm-devel 12/18/2013 The multipath kernel module is rejecting any map with an invalid device. However, as the multipathd is processing the events serially it will try to push a map with invalid devices if more than one device failed at the same time. So we can as well accept those maps and make sure to mark the paths as down. Signed-off-by: Hannes Reinecke --- drivers/md/dm-mpath.c | 112 +++++++++++++++++++++++++++++++++++++------------- drivers/md/dm-mpath.h | 1 drivers/md/dm-table.c | 3 + 3 files changed, 88 insertions(+), 28 deletions(-) --- a/drivers/md/dm-mpath.c +++ b/drivers/md/dm-mpath.c @@ -166,7 +166,8 @@ static void free_pgpaths(struct list_hea list_for_each_entry_safe(pgpath, tmp, pgpaths, list) { list_del(&pgpath->list); - dm_put_device(ti, pgpath->path.dev); + if (pgpath->path.dev) + dm_put_device(ti, pgpath->path.dev); free_pgpath(pgpath); } } @@ -367,6 +368,9 @@ static struct pgpath *choose_path_in_pg( pgpath = path_to_pgpath(path); + if (!pgpath->path.dev) + return ERR_PTR(-ENODEV); + if (unlikely(READ_ONCE(m->current_pg) != pg)) { /* Only update current_pgpath if pg changed */ spin_lock_irqsave(&m->lock, flags); @@ -502,7 +506,7 @@ static int multipath_clone_and_map(struc struct pgpath *pgpath; struct block_device *bdev; struct dm_mpath_io *mpio = get_mpio(map_context); - struct request_queue *q; + struct request_queue *q = NULL; struct request *clone; /* Do we need to select a new pgpath? */ @@ -526,12 +530,16 @@ static int multipath_clone_and_map(struc mpio->pgpath = pgpath; mpio->nr_bytes = nr_bytes; - bdev = pgpath->path.dev->bdev; - q = bdev_get_queue(bdev); - clone = blk_get_request(q, rq->cmd_flags | REQ_NOMERGE, GFP_ATOMIC); + if (pgpath->path.dev) { + bdev = pgpath->path.dev->bdev; + q = bdev_get_queue(bdev); + clone = blk_get_request(q, rq->cmd_flags | REQ_NOMERGE, + GFP_ATOMIC); + } else + clone = ERR_PTR(-ENODEV); if (IS_ERR(clone)) { /* EBUSY, ENODEV or EWOULDBLOCK: requeue */ - bool queue_dying = blk_queue_dying(q); + bool queue_dying = q ? blk_queue_dying(q) : false; if (queue_dying) { atomic_inc(&m->pg_init_in_progress); activate_or_offline_path(pgpath); @@ -585,7 +593,7 @@ static int __multipath_map_bio(struct mu return DM_MAPIO_SUBMITTED; } - if (!pgpath) { + if (!pgpath || !pgpath->path.dev) { if (must_push_back_bio(m)) return DM_MAPIO_REQUEUE; dm_report_EIO(m); @@ -754,6 +762,7 @@ static struct pgpath *parse_path(struct { int r; struct pgpath *p; + const char *path; struct multipath *m = ti->private; struct request_queue *q = NULL; const char *attached_handler_name; @@ -768,17 +777,40 @@ static struct pgpath *parse_path(struct if (!p) return ERR_PTR(-ENOMEM); - r = dm_get_device(ti, dm_shift_arg(as), dm_table_get_mode(ti->table), + path = dm_shift_arg(as); + r = dm_get_device(ti, path, dm_table_get_mode(ti->table), &p->path.dev); if (r) { - ti->error = "error getting device"; - goto bad; + unsigned major, minor; + + /* Try to add a failed device */ + if (r == -ENXIO && sscanf(path, "%u:%u", &major, &minor) == 2) { + dev_t dev; + + /* Extract the major/minor numbers */ + dev = MKDEV(major, minor); + if (MAJOR(dev) != major || MINOR(dev) != minor) { + /* Nice try, didn't work */ + DMWARN("Invalid device path %s", path); + ti->error = "error converting devnum"; + goto bad; + } + DMWARN("adding disabled device %d:%d", major, minor); + p->path.dev = NULL; + format_dev_t(p->path.pdev, dev); + p->is_active = 0; + } else { + ti->error = "error getting device"; + goto bad; + } + } else { + memcpy(p->path.pdev, p->path.dev->name, 16); } - if (test_bit(MPATHF_RETAIN_ATTACHED_HW_HANDLER, &m->flags) || m->hw_handler_name) + if (p->path.dev) q = bdev_get_queue(p->path.dev->bdev); - if (test_bit(MPATHF_RETAIN_ATTACHED_HW_HANDLER, &m->flags)) { + if (q && test_bit(MPATHF_RETAIN_ATTACHED_HW_HANDLER, &m->flags)) { retain: attached_handler_name = scsi_dh_attached_handler_name(q, GFP_KERNEL); if (attached_handler_name) { @@ -802,7 +834,7 @@ retain: } } - if (m->hw_handler_name) { + if (q && m->hw_handler_name) { r = scsi_dh_attach(q, m->hw_handler_name); if (r == -EBUSY) { char b[BDEVNAME_SIZE]; @@ -830,10 +862,16 @@ retain: r = ps->type->add_path(ps, &p->path, as->argc, as->argv, &ti->error); if (r) { - dm_put_device(ti, p->path.dev); + if (p->path.dev) + dm_put_device(ti, p->path.dev); goto bad; } + if (!p->is_active) { + ps->type->fail_path(ps, &p->path); + p->fail_count++; + atomic_dec(&m->nr_valid_paths); + } return p; bad: @@ -1178,10 +1216,10 @@ static int fail_path(struct pgpath *pgpa spin_lock_irqsave(&m->lock, flags); - if (!pgpath->is_active) + if (!pgpath->path.dev || !pgpath->is_active) goto out; - DMWARN("Failing path %s.", pgpath->path.dev->name); + DMWARN("Failing path %s.", pgpath->path.pdev); pgpath->pg->ps.type->fail_path(&pgpath->pg->ps, &pgpath->path); pgpath->is_active = false; @@ -1193,7 +1231,7 @@ static int fail_path(struct pgpath *pgpa m->current_pgpath = NULL; dm_path_uevent(DM_UEVENT_PATH_FAILED, m->ti, - pgpath->path.dev->name, atomic_read(&m->nr_valid_paths)); + pgpath->path.pdev, atomic_read(&m->nr_valid_paths)); schedule_work(&m->trigger_event); @@ -1218,7 +1256,13 @@ static int reinstate_path(struct pgpath if (pgpath->is_active) goto out; - DMWARN("Reinstating path %s.", pgpath->path.dev->name); + if (!pgpath->path.dev) { + DMWARN("Cannot reinstate disabled path %s", pgpath->path.pdev); + r = -ENODEV; + goto out; + } + + DMWARN("Reinstating path %s.", pgpath->path.pdev); r = pgpath->pg->ps.type->reinstate_path(&pgpath->pg->ps, &pgpath->path); if (r) @@ -1236,7 +1280,7 @@ static int reinstate_path(struct pgpath } dm_path_uevent(DM_UEVENT_PATH_REINSTATED, m->ti, - pgpath->path.dev->name, nr_valid_paths); + pgpath->path.pdev, nr_valid_paths); schedule_work(&m->trigger_event); @@ -1260,6 +1304,9 @@ static int action_dev(struct multipath * struct pgpath *pgpath; struct priority_group *pg; + if (!dev) + return 0; + list_for_each_entry(pg, &m->priority_groups, list) { list_for_each_entry(pgpath, &pg->pgpaths, list) { if (pgpath->path.dev == dev) @@ -1455,12 +1502,15 @@ out: static void activate_or_offline_path(struct pgpath *pgpath) { - struct request_queue *q = bdev_get_queue(pgpath->path.dev->bdev); - if (pgpath->is_active && !blk_queue_dying(q)) - scsi_dh_activate(q, pg_init_done, pgpath); - else - pg_init_done(pgpath, SCSI_DH_DEV_OFFLINED); + if (pgpath->path.dev) { + struct request_queue *q = bdev_get_queue(pgpath->path.dev->bdev); + if (pgpath->is_active && !blk_queue_dying(q)) { + scsi_dh_activate(q, pg_init_done, pgpath); + return; + } + } + pg_init_done(pgpath, SCSI_DH_DEV_OFFLINED); } static void activate_path_work(struct work_struct *work) @@ -1701,7 +1751,7 @@ static void multipath_status(struct dm_t pg->ps.type->info_args); list_for_each_entry(p, &pg->pgpaths, list) { - DMEMIT("%s %s %u ", p->path.dev->name, + DMEMIT("%s %s %u ", p->path.pdev, p->is_active ? "A" : "F", p->fail_count); if (pg->ps.type->status) @@ -1727,7 +1777,7 @@ static void multipath_status(struct dm_t pg->ps.type->table_args); list_for_each_entry(p, &pg->pgpaths, list) { - DMEMIT("%s ", p->path.dev->name); + DMEMIT("%s ", p->path.pdev); if (pg->ps.type->status) sz += pg->ps.type->status(&pg->ps, &p->path, type, result + sz, @@ -1814,7 +1864,7 @@ static int multipath_prepare_ioctl(struc if (!current_pgpath) current_pgpath = choose_pgpath(m, 0); - if (current_pgpath) { + if (current_pgpath && current_pgpath->path.dev) { if (!test_bit(MPATHF_QUEUE_IO, &m->flags)) { *bdev = current_pgpath->path.dev->bdev; *mode = current_pgpath->path.dev->mode; @@ -1860,6 +1910,8 @@ static int multipath_iterate_devices(str list_for_each_entry(pg, &m->priority_groups, list) { list_for_each_entry(p, &pg->pgpaths, list) { + if (!p->path.dev) + continue; ret = fn(ti, p->path.dev, ti->begin, ti->len, data); if (ret) goto out; @@ -1872,8 +1924,12 @@ out: static int pgpath_busy(struct pgpath *pgpath) { - struct request_queue *q = bdev_get_queue(pgpath->path.dev->bdev); + struct request_queue *q; + + if (!pgpath->path.dev) + return 0; + q = bdev_get_queue(pgpath->path.dev->bdev); return blk_lld_busy(q); } --- a/drivers/md/dm-mpath.h +++ b/drivers/md/dm-mpath.h @@ -12,6 +12,7 @@ struct dm_dev; struct dm_path { + char pdev[16]; /* Requested physical device */ struct dm_dev *dev; /* Read-only */ void *pscontext; /* For path-selector use */ }; --- a/drivers/md/dm-table.c +++ b/drivers/md/dm-table.c @@ -505,6 +505,9 @@ void dm_put_device(struct dm_target *ti, struct list_head *devices = &ti->table->devices; struct dm_dev_internal *dd; + if (!d) + return; + list_for_each_entry(dd, devices, list) { if (dd->dm_dev == d) { found = 1;