|
@@ -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) {
|
|
|
|
- struct request_queue *q = bdev_get_queue(p->path.dev->bdev);
|
|
|
|
|
|
+ if (m->retain_attached_hw_handler || m->hw_handler_name)
|
|
|
|
+ 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,
|