diff options
Diffstat (limited to 'drivers/scsi/device_handler/scsi_dh_alua.c')
-rw-r--r-- | drivers/scsi/device_handler/scsi_dh_alua.c | 71 |
1 files changed, 61 insertions, 10 deletions
diff --git a/drivers/scsi/device_handler/scsi_dh_alua.c b/drivers/scsi/device_handler/scsi_dh_alua.c index fda9cdea0e60..08d80a6d272a 100644 --- a/drivers/scsi/device_handler/scsi_dh_alua.c +++ b/drivers/scsi/device_handler/scsi_dh_alua.c @@ -46,13 +46,16 @@ #define TPGS_SUPPORT_OFFLINE 0x40 #define TPGS_SUPPORT_TRANSITION 0x80 +#define RTPG_FMT_MASK 0x70 +#define RTPG_FMT_EXT_HDR 0x10 + #define TPGS_MODE_UNINITIALIZED -1 #define TPGS_MODE_NONE 0x0 #define TPGS_MODE_IMPLICIT 0x1 #define TPGS_MODE_EXPLICIT 0x2 #define ALUA_INQUIRY_SIZE 36 -#define ALUA_FAILOVER_TIMEOUT (60 * HZ) +#define ALUA_FAILOVER_TIMEOUT 60 #define ALUA_FAILOVER_RETRIES 5 /* flags passed from user level */ @@ -68,6 +71,7 @@ struct alua_dh_data { unsigned char inq[ALUA_INQUIRY_SIZE]; unsigned char *buff; int bufflen; + unsigned char transition_tmo; unsigned char sense[SCSI_SENSE_BUFFERSIZE]; int senselen; struct scsi_device *sdev; @@ -128,7 +132,7 @@ static struct request *get_alua_req(struct scsi_device *sdev, rq->cmd_flags |= REQ_FAILFAST_DEV | REQ_FAILFAST_TRANSPORT | REQ_FAILFAST_DRIVER; rq->retries = ALUA_FAILOVER_RETRIES; - rq->timeout = ALUA_FAILOVER_TIMEOUT; + rq->timeout = ALUA_FAILOVER_TIMEOUT * HZ; return rq; } @@ -174,7 +178,8 @@ done: * submit_rtpg - Issue a REPORT TARGET GROUP STATES command * @sdev: sdev the command should be sent to */ -static unsigned submit_rtpg(struct scsi_device *sdev, struct alua_dh_data *h) +static unsigned submit_rtpg(struct scsi_device *sdev, struct alua_dh_data *h, + bool rtpg_ext_hdr_req) { struct request *rq; int err = SCSI_DH_RES_TEMP_UNAVAIL; @@ -185,7 +190,10 @@ static unsigned submit_rtpg(struct scsi_device *sdev, struct alua_dh_data *h) /* Prepare the command. */ rq->cmd[0] = MAINTENANCE_IN; - rq->cmd[1] = MI_REPORT_TARGET_PGS; + if (rtpg_ext_hdr_req) + rq->cmd[1] = MI_REPORT_TARGET_PGS | MI_EXT_HDR_PARAM_FMT; + else + rq->cmd[1] = MI_REPORT_TARGET_PGS; rq->cmd[6] = (h->bufflen >> 24) & 0xff; rq->cmd[7] = (h->bufflen >> 16) & 0xff; rq->cmd[8] = (h->bufflen >> 8) & 0xff; @@ -518,11 +526,18 @@ static int alua_rtpg(struct scsi_device *sdev, struct alua_dh_data *h) int len, k, off, valid_states = 0; unsigned char *ucp; unsigned err; - unsigned long expiry, interval = 1000; + bool rtpg_ext_hdr_req = 1; + unsigned long expiry, interval = 0; + unsigned int tpg_desc_tbl_off; + unsigned char orig_transition_tmo; + + if (!h->transition_tmo) + expiry = round_jiffies_up(jiffies + ALUA_FAILOVER_TIMEOUT * HZ); + else + expiry = round_jiffies_up(jiffies + h->transition_tmo * HZ); - expiry = round_jiffies_up(jiffies + ALUA_FAILOVER_TIMEOUT); retry: - err = submit_rtpg(sdev, h); + err = submit_rtpg(sdev, h, rtpg_ext_hdr_req); if (err == SCSI_DH_IO && h->senselen > 0) { err = scsi_normalize_sense(h->sense, SCSI_SENSE_BUFFERSIZE, @@ -530,6 +545,21 @@ static int alua_rtpg(struct scsi_device *sdev, struct alua_dh_data *h) if (!err) return SCSI_DH_IO; + /* + * submit_rtpg() has failed on existing arrays + * when requesting extended header info, and + * the array doesn't support extended headers, + * even though it shouldn't according to T10. + * The retry without rtpg_ext_hdr_req set + * handles this. + */ + if (rtpg_ext_hdr_req == 1 && + sense_hdr.sense_key == ILLEGAL_REQUEST && + sense_hdr.asc == 0x24 && sense_hdr.ascq == 0) { + rtpg_ext_hdr_req = 0; + goto retry; + } + err = alua_check_sense(sdev, &sense_hdr); if (err == ADD_TO_MLQUEUE && time_before(jiffies, expiry)) goto retry; @@ -556,7 +586,28 @@ static int alua_rtpg(struct scsi_device *sdev, struct alua_dh_data *h) goto retry; } - for (k = 4, ucp = h->buff + 4; k < len; k += off, ucp += off) { + orig_transition_tmo = h->transition_tmo; + if ((h->buff[4] & RTPG_FMT_MASK) == RTPG_FMT_EXT_HDR && h->buff[5] != 0) + h->transition_tmo = h->buff[5]; + else + h->transition_tmo = ALUA_FAILOVER_TIMEOUT; + + if (orig_transition_tmo != h->transition_tmo) { + sdev_printk(KERN_INFO, sdev, + "%s: transition timeout set to %d seconds\n", + ALUA_DH_NAME, h->transition_tmo); + expiry = jiffies + h->transition_tmo * HZ; + } + + if ((h->buff[4] & RTPG_FMT_MASK) == RTPG_FMT_EXT_HDR) + tpg_desc_tbl_off = 8; + else + tpg_desc_tbl_off = 4; + + for (k = tpg_desc_tbl_off, ucp = h->buff + tpg_desc_tbl_off; + k < len; + k += off, ucp += off) { + if (h->group_id == (ucp[2] << 8) + ucp[3]) { h->state = ucp[0] & 0x0f; h->pref = ucp[0] >> 7; @@ -581,7 +632,7 @@ static int alua_rtpg(struct scsi_device *sdev, struct alua_dh_data *h) case TPGS_STATE_TRANSITIONING: if (time_before(jiffies, expiry)) { /* State transition, retry */ - interval *= 2; + interval += 2000; msleep(interval); goto retry; } @@ -691,9 +742,9 @@ static int alua_activate(struct scsi_device *sdev, stpg = 0; break; case TPGS_STATE_STANDBY: + case TPGS_STATE_UNAVAILABLE: stpg = 1; break; - case TPGS_STATE_UNAVAILABLE: case TPGS_STATE_OFFLINE: err = SCSI_DH_IO; break; |