]> git.kernelconcepts.de Git - karo-tx-linux.git/blobdiff - drivers/scsi/device_handler/scsi_dh_alua.c
[SCSI] scsi_dh_alua: implement 'implied transition timeout'
[karo-tx-linux.git] / drivers / scsi / device_handler / scsi_dh_alua.c
index fda9cdea0e60aa6d78c399b38e8d1ed635fb034f..ac6fddf99e998a4e9f4c1e5fc7f2fa9db7034bf1 100644 (file)
 #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;
 }
@@ -185,7 +189,7 @@ 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;
+       rq->cmd[1] = MI_REPORT_TARGET_PGS | MI_EXT_HDR_PARAM_FMT;
        rq->cmd[6] = (h->bufflen >> 24) & 0xff;
        rq->cmd[7] = (h->bufflen >> 16) & 0xff;
        rq->cmd[8] = (h->bufflen >>  8) & 0xff;
@@ -519,8 +523,14 @@ static int alua_rtpg(struct scsi_device *sdev, struct alua_dh_data *h)
        unsigned char *ucp;
        unsigned err;
        unsigned long expiry, interval = 1000;
+       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);
 
@@ -556,7 +566,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;