From: Hannes Reinecke Subject: Accept failed paths for multipath maps References: bnc#458037,bnc#458393 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 diff --git a/drivers/md/dm-mpath.c b/drivers/md/dm-mpath.c index 510fd26..95feacd 100644 --- a/drivers/md/dm-mpath.c +++ b/drivers/md/dm-mpath.c @@ -141,7 +141,8 @@ static void deactivate_path(struct work_struct *work) struct pgpath *pgpath = container_of(work, struct pgpath, deactivate_path); - blk_abort_queue(pgpath->path.dev->bdev->bd_disk->queue); + if (pgpath->path.dev) + blk_abort_queue(pgpath->path.dev->bdev->bd_disk->queue); } static struct priority_group *alloc_priority_group(void) @@ -253,6 +254,11 @@ static int __choose_path_in_pg(struct multipath *m, struct priority_group *pg, m->current_pgpath = path_to_pgpath(path); + if (!m->current_pgpath->path.dev) { + m->current_pgpath = NULL; + return -ENODEV; + } + if (m->current_pg != pg) __switch_pg(m, m->current_pgpath); @@ -570,6 +576,7 @@ static struct pgpath *parse_path(struct arg_set *as, struct path_selector *ps, { int r; struct pgpath *p; + char *path; struct multipath *m = ti->private; /* we need at least a path arg */ @@ -582,14 +589,37 @@ static struct pgpath *parse_path(struct arg_set *as, struct path_selector *ps, if (!p) return NULL; - r = dm_get_device(ti, shift(as), ti->begin, ti->len, + path = shift(as); + r = dm_get_device(ti, path, ti->begin, ti->len, 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 (m->hw_handler_name) { + if (m->hw_handler_name && p->path.dev) { struct request_queue *q = bdev_get_queue(p->path.dev->bdev); r = scsi_dh_attach(q, m->hw_handler_name); @@ -880,7 +910,7 @@ static int fail_path(struct pgpath *pgpath) if (!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 = 0; @@ -892,7 +922,7 @@ static int fail_path(struct pgpath *pgpath) m->current_pgpath = NULL; dm_path_uevent(DM_UEVENT_PATH_FAILED, m->ti, - pgpath->path.dev->name, m->nr_valid_paths); + pgpath->path.pdev, m->nr_valid_paths); queue_work(kmultipathd, &m->trigger_event); queue_work(kmultipathd, &pgpath->deactivate_path); @@ -917,6 +947,12 @@ static int reinstate_path(struct pgpath *pgpath) if (pgpath->is_active) goto out; + if (!pgpath->path.dev) { + DMWARN("Cannot reinstate disabled path %s", pgpath->path.pdev); + r = -ENODEV; + goto out; + } + if (!pgpath->pg->ps.type->reinstate_path) { DMWARN("Reinstate path not supported by path selector %s", pgpath->pg->ps.type->name); @@ -935,7 +971,7 @@ static int reinstate_path(struct pgpath *pgpath) queue_work(kmultipathd, &m->process_queued_ios); dm_path_uevent(DM_UEVENT_PATH_REINSTATED, m->ti, - pgpath->path.dev->name, m->nr_valid_paths); + pgpath->path.pdev, m->nr_valid_paths); queue_work(kmultipathd, &m->trigger_event); @@ -955,6 +991,9 @@ static int action_dev(struct multipath *m, struct dm_dev *dev, 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) @@ -1124,11 +1163,12 @@ static void pg_init_done(struct dm_path *path, int errors) static void activate_path(struct work_struct *work) { - int ret; + int ret = SCSI_DH_DEV_OFFLINED; struct pgpath *pgpath = container_of(work, struct pgpath, activate_path); - ret = scsi_dh_activate(bdev_get_queue(pgpath->path.dev->bdev)); + if (pgpath->path.dev) + ret = scsi_dh_activate(bdev_get_queue(pgpath->path.dev->bdev)); pg_init_done(&pgpath->path, ret); } @@ -1295,7 +1335,7 @@ static int multipath_status(struct dm_target *ti, status_type_t type, 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) @@ -1321,7 +1361,7 @@ static int multipath_status(struct dm_target *ti, status_type_t type, 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, @@ -1403,7 +1443,7 @@ static int multipath_ioctl(struct dm_target *ti, struct inode *inode, if (!m->current_pgpath) __choose_pgpath(m, 1 << 19); /* Assume 512KB */ - if (m->current_pgpath) { + if (m->current_pgpath && m->current_pgpath->path.dev) { bdev = m->current_pgpath->path.dev->bdev; fake_dentry.d_inode = bdev->bd_inode; fake_file.f_mode = m->current_pgpath->path.dev->mode; diff --git a/drivers/md/dm-mpath.h b/drivers/md/dm-mpath.h index e230f71..f97388d 100644 --- 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 */ }; diff --git a/drivers/md/dm-table.c b/drivers/md/dm-table.c index 1514e84..ee61b82 100644 --- a/drivers/md/dm-table.c +++ b/drivers/md/dm-table.c @@ -566,6 +566,9 @@ int dm_get_device(struct dm_target *ti, const char *path, sector_t start, */ void dm_put_device(struct dm_target *ti, struct dm_dev *dd) { + if (!dd) + return; + if (atomic_dec_and_test(&dd->count)) { close_dev(dd, ti->table->md); list_del(&dd->list); diff --git a/drivers/md/dm.c b/drivers/md/dm.c index 6783f91..134995e 100644 --- a/drivers/md/dm.c +++ b/drivers/md/dm.c @@ -1503,6 +1503,9 @@ static void map_request(struct dm_target *ti, struct request *rq, tio->ti = ti; atomic_inc(&md->pending); +#if 0 + /* This might trigger accidentally */ + /* * Although submitted requests to the md->queue are checked against * the table/queue limitations at the submission time, the limitations @@ -1525,6 +1528,7 @@ static void map_request(struct dm_target *ti, struct request *rq, dm_kill_request(clone, r); return; } +#endif r = ti->type->map_rq(ti, clone, &tio->info); switch (r) {