dm mpath: add retain_attached_hw_handler feature

A SCSI device handler might get attached to a device during the
initial device scan.  We do not necessarily want to override
this when loading a multipath table, so this patch adds a new
multipath feature argument "retain_attached_hw_handler".

During SCSI device scan all loaded SCSI device handlers will be
consulted for a match (via scsi_dh's provided .match).  If a match is
found that device handler will be attached.  We need a way to have
userspace multipathd's provided 'hw_handler' not override the already
attached hardware handler.

When specifying the new feature 'retain_attached_hw_handler' multipath
will use the currently attached hardware handler instead of trying to
attach the one specified during table load.  If no hardware handler is
attached the specified hardware handler will still be used.

Leverages scsi_dh_attach's ability to increment the scsi_dh's reference
count if the same scsi_dh name is provided when attaching - currently
attached scsi_dh name is determined with scsi_dh_attached_handler_name.

Depends upon commit 7e8a74b177
("[SCSI] scsi_dh: add scsi_dh_attached_handler_name").

Signed-off-by: Mike Snitzer <snitzer@redhat.com>
Tested-by: Babu Moger <babu.moger@netapp.com>
Reviewed-by: Chandra Seetharaman <sekharan@us.ibm.com>
Acked-by: Hannes Reinecke <hare@suse.de>
Signed-off-by: Alasdair G Kergon <agk@redhat.com>
This commit is contained in:
Mike Snitzer 2012-07-27 15:08:04 +01:00 committed by Alasdair G Kergon
parent f9a8e0cd26
commit a58a935d5a

View File

@ -85,6 +85,7 @@ struct multipath {
unsigned queue_io:1; /* Must we queue all I/O? */ unsigned queue_io:1; /* Must we queue all I/O? */
unsigned queue_if_no_path:1; /* Queue I/O if last path fails? */ unsigned queue_if_no_path:1; /* Queue I/O if last path fails? */
unsigned saved_queue_if_no_path:1; /* Saved state during suspension */ unsigned saved_queue_if_no_path:1; /* Saved state during suspension */
unsigned retain_attached_hw_handler:1; /* If there's already a hw_handler present, don't change it. */
unsigned pg_init_retries; /* Number of times to retry pg_init */ unsigned pg_init_retries; /* Number of times to retry pg_init */
unsigned pg_init_count; /* Number of times pg_init called */ unsigned pg_init_count; /* Number of times pg_init called */
@ -568,6 +569,8 @@ static struct pgpath *parse_path(struct dm_arg_set *as, struct path_selector *ps
int r; int r;
struct pgpath *p; struct pgpath *p;
struct multipath *m = ti->private; struct multipath *m = ti->private;
struct request_queue *q = NULL;
const char *attached_handler_name;
/* we need at least a path arg */ /* we need at least a path arg */
if (as->argc < 1) { if (as->argc < 1) {
@ -586,13 +589,37 @@ static struct pgpath *parse_path(struct dm_arg_set *as, struct path_selector *ps
goto bad; goto bad;
} }
if (m->hw_handler_name) { if (m->retain_attached_hw_handler || m->hw_handler_name)
struct request_queue *q = bdev_get_queue(p->path.dev->bdev); q = bdev_get_queue(p->path.dev->bdev);
if (m->retain_attached_hw_handler) {
attached_handler_name = scsi_dh_attached_handler_name(q, GFP_KERNEL);
if (attached_handler_name) {
/*
* Reset hw_handler_name to match the attached handler
* and clear any hw_handler_params associated with the
* ignored handler.
*
* NB. This modifies the table line to show the actual
* handler instead of the original table passed in.
*/
kfree(m->hw_handler_name);
m->hw_handler_name = attached_handler_name;
kfree(m->hw_handler_params);
m->hw_handler_params = NULL;
}
}
if (m->hw_handler_name) {
/*
* Increments scsi_dh reference, even when using an
* already-attached handler.
*/
r = scsi_dh_attach(q, m->hw_handler_name); r = scsi_dh_attach(q, m->hw_handler_name);
if (r == -EBUSY) { if (r == -EBUSY) {
/* /*
* Already attached to different hw_handler, * Already attached to different hw_handler:
* try to reattach with correct one. * try to reattach with correct one.
*/ */
scsi_dh_detach(q); scsi_dh_detach(q);
@ -760,7 +787,7 @@ static int parse_features(struct dm_arg_set *as, struct multipath *m)
const char *arg_name; const char *arg_name;
static struct dm_arg _args[] = { static struct dm_arg _args[] = {
{0, 5, "invalid number of feature args"}, {0, 6, "invalid number of feature args"},
{1, 50, "pg_init_retries must be between 1 and 50"}, {1, 50, "pg_init_retries must be between 1 and 50"},
{0, 60000, "pg_init_delay_msecs must be between 0 and 60000"}, {0, 60000, "pg_init_delay_msecs must be between 0 and 60000"},
}; };
@ -781,6 +808,11 @@ static int parse_features(struct dm_arg_set *as, struct multipath *m)
continue; continue;
} }
if (!strcasecmp(arg_name, "retain_attached_hw_handler")) {
m->retain_attached_hw_handler = 1;
continue;
}
if (!strcasecmp(arg_name, "pg_init_retries") && if (!strcasecmp(arg_name, "pg_init_retries") &&
(argc >= 1)) { (argc >= 1)) {
r = dm_read_arg(_args + 1, as, &m->pg_init_retries, &ti->error); r = dm_read_arg(_args + 1, as, &m->pg_init_retries, &ti->error);
@ -1364,13 +1396,16 @@ static int multipath_status(struct dm_target *ti, status_type_t type,
else { else {
DMEMIT("%u ", m->queue_if_no_path + DMEMIT("%u ", m->queue_if_no_path +
(m->pg_init_retries > 0) * 2 + (m->pg_init_retries > 0) * 2 +
(m->pg_init_delay_msecs != DM_PG_INIT_DELAY_DEFAULT) * 2); (m->pg_init_delay_msecs != DM_PG_INIT_DELAY_DEFAULT) * 2 +
m->retain_attached_hw_handler);
if (m->queue_if_no_path) if (m->queue_if_no_path)
DMEMIT("queue_if_no_path "); DMEMIT("queue_if_no_path ");
if (m->pg_init_retries) if (m->pg_init_retries)
DMEMIT("pg_init_retries %u ", m->pg_init_retries); DMEMIT("pg_init_retries %u ", m->pg_init_retries);
if (m->pg_init_delay_msecs != DM_PG_INIT_DELAY_DEFAULT) if (m->pg_init_delay_msecs != DM_PG_INIT_DELAY_DEFAULT)
DMEMIT("pg_init_delay_msecs %u ", m->pg_init_delay_msecs); DMEMIT("pg_init_delay_msecs %u ", m->pg_init_delay_msecs);
if (m->retain_attached_hw_handler)
DMEMIT("retain_attached_hw_handler ");
} }
if (!m->hw_handler_name || type == STATUSTYPE_INFO) if (!m->hw_handler_name || type == STATUSTYPE_INFO)
@ -1656,7 +1691,7 @@ out:
*---------------------------------------------------------------*/ *---------------------------------------------------------------*/
static struct target_type multipath_target = { static struct target_type multipath_target = {
.name = "multipath", .name = "multipath",
.version = {1, 4, 0}, .version = {1, 5, 0},
.module = THIS_MODULE, .module = THIS_MODULE,
.ctr = multipath_ctr, .ctr = multipath_ctr,
.dtr = multipath_dtr, .dtr = multipath_dtr,