From: Mike Snitzer <snitzer@redhat.com> Date: Thu, 23 Sep 2010 21:40:36 -0400 Subject: [scsi] scsi_dh_alua: handle transitioning state correctly Message-id: <20100923214035.GA13376@redhat.com> Patchwork-id: 28360 O-Subject: [RHEL5.6 PATCH v3] scsi_dh_alua: Handle transitioning state correctly Bugzilla: 619361 RH-Acked-by: Mike Christie <mchristi@redhat.com> BZ 619361 Brew build: http://brewweb.devel.redhat.com/brew/taskinfo?taskID=2778175 For ALUA we should be handling all states, independent of whether is explicit or implicit. For 'Transitioning' we should be retry for a certain amount of time; after that we're setting the port to 'Standby' and return SCSI_DH_RETRY to signal upper layers a retry is in order here. Signed-off-by: Hannes Reinecke <hare@suse.de> NOTE: This patch is the result of upstream review on linux-scsi. I did not backport handling of the TPGS_STATE_LBA_DEPENDENT because it would require further changes which are outside scope of this BZ -- so backporting that isn't worth the risk. Signed-off-by: Mike Snitzer <snitzer@redhat.com> diff --git a/drivers/scsi/device_handler/scsi_dh_alua.c b/drivers/scsi/device_handler/scsi_dh_alua.c index a78aaa6..05a012e 100644 --- a/drivers/scsi/device_handler/scsi_dh_alua.c +++ b/drivers/scsi/device_handler/scsi_dh_alua.c @@ -20,6 +20,7 @@ * */ #include <linux/list.h> +#include <linux/delay.h> #include <scsi/scsi.h> #include <scsi/scsi_eh.h> #include <scsi/scsi_cmnd.h> @@ -536,7 +537,9 @@ static int alua_rtpg(struct scsi_device *sdev, struct alua_dh_data *h) int len, k, off, valid_states = 0; char *ucp; unsigned err; + unsigned long expiry, interval = 10; + expiry = round_jiffies_up(jiffies + ALUA_FAILOVER_TIMEOUT); retry: err = submit_rtpg(sdev, h); @@ -547,7 +550,7 @@ static int alua_rtpg(struct scsi_device *sdev, struct alua_dh_data *h) return SCSI_DH_IO; err = alua_check_sense(sdev, &sense_hdr); - if (err == SCSI_MLQUEUE_IMM_RETRY) + if (err == SCSI_MLQUEUE_IMM_RETRY && time_before(jiffies, expiry)) goto retry; sdev_printk(KERN_INFO, sdev, "%s: rtpg sense code %02x/%02x/%02x\n", @@ -590,29 +593,27 @@ static int alua_rtpg(struct scsi_device *sdev, struct alua_dh_data *h) valid_states&TPGS_SUPPORT_NONOPTIMIZED?'N':'n', valid_states&TPGS_SUPPORT_OPTIMIZED?'A':'a'); - if (h->tpgs & TPGS_MODE_EXPLICIT) { - switch (h->state) { - case TPGS_STATE_TRANSITIONING: + switch (h->state) { + case TPGS_STATE_TRANSITIONING: + if (time_before(jiffies, expiry)) { /* State transition, retry */ + interval *= 10; + msleep(interval); goto retry; - break; - case TPGS_STATE_OFFLINE: - /* Path is offline, fail */ - err = SCSI_DH_DEV_OFFLINED; - break; - default: - break; } - } else { - /* Only Implicit ALUA support */ - if (h->state == TPGS_STATE_OPTIMIZED || - h->state == TPGS_STATE_NONOPTIMIZED || - h->state == TPGS_STATE_STANDBY) - /* Useable path if active */ - err = SCSI_DH_OK; - else - /* Path unuseable for unavailable/offline */ - err = SCSI_DH_DEV_OFFLINED; + /* Transitioning time exceeded, set port to standby */ + err = SCSI_DH_RETRY; + h->state = TPGS_STATE_STANDBY; + break; + case TPGS_STATE_OFFLINE: + case TPGS_STATE_UNAVAILABLE: + /* Path unuseable for unavailable/offline */ + err = SCSI_DH_DEV_OFFLINED; + break; + default: + /* Useable path if active */ + err = SCSI_DH_OK; + break; } return err; } @@ -686,8 +687,10 @@ static int alua_prep_fn(struct scsi_device *sdev, struct request *req) struct alua_dh_data *h = get_alua_data(sdev); int ret = BLKPREP_OK; - if (h->state != TPGS_STATE_OPTIMIZED && - h->state != TPGS_STATE_NONOPTIMIZED) { + if (h->state == TPGS_STATE_TRANSITIONING) + ret = BLKPREP_DEFER; + else if (h->state != TPGS_STATE_OPTIMIZED && + h->state != TPGS_STATE_NONOPTIMIZED) { ret = BLKPREP_KILL; req->flags |= REQ_QUIET; }