diff options
Diffstat (limited to 'drivers/scsi/device_handler')
| -rw-r--r-- | drivers/scsi/device_handler/Kconfig | 4 | ||||
| -rw-r--r-- | drivers/scsi/device_handler/scsi_dh.c | 168 | ||||
| -rw-r--r-- | drivers/scsi/device_handler/scsi_dh_alua.c | 308 | ||||
| -rw-r--r-- | drivers/scsi/device_handler/scsi_dh_emc.c | 24 | ||||
| -rw-r--r-- | drivers/scsi/device_handler/scsi_dh_hp_sw.c | 31 | ||||
| -rw-r--r-- | drivers/scsi/device_handler/scsi_dh_rdac.c | 260 | 
6 files changed, 491 insertions, 304 deletions
diff --git a/drivers/scsi/device_handler/Kconfig b/drivers/scsi/device_handler/Kconfig index 67070257919..69abd0ad48e 100644 --- a/drivers/scsi/device_handler/Kconfig +++ b/drivers/scsi/device_handler/Kconfig @@ -32,8 +32,8 @@ config SCSI_DH_EMC  	If you have a EMC CLARiiON select y. Otherwise, say N.  config SCSI_DH_ALUA -	tristate "SPC-3 ALUA Device Handler (EXPERIMENTAL)" -	depends on SCSI_DH && EXPERIMENTAL +	tristate "SPC-3 ALUA Device Handler" +	depends on SCSI_DH  	help  	  SCSI Device handler for generic SPC-3 Asymmetric Logical Unit  	  Access (ALUA). diff --git a/drivers/scsi/device_handler/scsi_dh.c b/drivers/scsi/device_handler/scsi_dh.c index 6fae3d285ae..33e422e7583 100644 --- a/drivers/scsi/device_handler/scsi_dh.c +++ b/drivers/scsi/device_handler/scsi_dh.c @@ -22,19 +22,12 @@   */  #include <linux/slab.h> +#include <linux/module.h>  #include <scsi/scsi_dh.h>  #include "../scsi_priv.h" -struct scsi_dh_devinfo_list { -	struct list_head node; -	char vendor[9]; -	char model[17]; -	struct scsi_device_handler *handler; -}; -  static DEFINE_SPINLOCK(list_lock);  static LIST_HEAD(scsi_dh_list); -static LIST_HEAD(scsi_dh_dev_list);  static struct scsi_device_handler *get_device_handler(const char *name)  { @@ -51,43 +44,29 @@ static struct scsi_device_handler *get_device_handler(const char *name)  	return found;  } - +/* + * device_handler_match_function - Match a device handler to a device + * @sdev - SCSI device to be tested + * + * Tests @sdev against the match function of all registered device_handler. + * Returns the found device handler or NULL if not found. + */  static struct scsi_device_handler * -scsi_dh_cache_lookup(struct scsi_device *sdev) +device_handler_match_function(struct scsi_device *sdev)  { -	struct scsi_dh_devinfo_list *tmp; -	struct scsi_device_handler *found_dh = NULL; +	struct scsi_device_handler *tmp_dh, *found_dh = NULL;  	spin_lock(&list_lock); -	list_for_each_entry(tmp, &scsi_dh_dev_list, node) { -		if (!strncmp(sdev->vendor, tmp->vendor, strlen(tmp->vendor)) && -		    !strncmp(sdev->model, tmp->model, strlen(tmp->model))) { -			found_dh = tmp->handler; +	list_for_each_entry(tmp_dh, &scsi_dh_list, list) { +		if (tmp_dh->match && tmp_dh->match(sdev)) { +			found_dh = tmp_dh;  			break;  		}  	}  	spin_unlock(&list_lock); -  	return found_dh;  } -static int scsi_dh_handler_lookup(struct scsi_device_handler *scsi_dh, -				  struct scsi_device *sdev) -{ -	int i, found = 0; - -	for(i = 0; scsi_dh->devlist[i].vendor; i++) { -		if (!strncmp(sdev->vendor, scsi_dh->devlist[i].vendor, -			     strlen(scsi_dh->devlist[i].vendor)) && -		    !strncmp(sdev->model, scsi_dh->devlist[i].model, -			     strlen(scsi_dh->devlist[i].model))) { -			found = 1; -			break; -		} -	} -	return found; -} -  /*   * device_handler_match - Attach a device handler to a device   * @scsi_dh - The device handler to match against or NULL @@ -101,42 +80,12 @@ static struct scsi_device_handler *  device_handler_match(struct scsi_device_handler *scsi_dh,  		     struct scsi_device *sdev)  { -	struct scsi_device_handler *found_dh = NULL; -	struct scsi_dh_devinfo_list *tmp; +	struct scsi_device_handler *found_dh; -	found_dh = scsi_dh_cache_lookup(sdev); -	if (found_dh) -		return found_dh; +	found_dh = device_handler_match_function(sdev); -	if (scsi_dh) { -		if (scsi_dh_handler_lookup(scsi_dh, sdev)) -			found_dh = scsi_dh; -	} else { -		struct scsi_device_handler *tmp_dh; - -		spin_lock(&list_lock); -		list_for_each_entry(tmp_dh, &scsi_dh_list, list) { -			if (scsi_dh_handler_lookup(tmp_dh, sdev)) -				found_dh = tmp_dh; -		} -		spin_unlock(&list_lock); -	} - -	if (found_dh) { /* If device is found, add it to the cache */ -		tmp = kmalloc(sizeof(*tmp), GFP_KERNEL); -		if (tmp) { -			strncpy(tmp->vendor, sdev->vendor, 8); -			strncpy(tmp->model, sdev->model, 16); -			tmp->vendor[8] = '\0'; -			tmp->model[16] = '\0'; -			tmp->handler = found_dh; -			spin_lock(&list_lock); -			list_add(&tmp->node, &scsi_dh_dev_list); -			spin_unlock(&list_lock); -		} else { -			found_dh = NULL; -		} -	} +	if (scsi_dh && found_dh != scsi_dh) +		found_dh = NULL;  	return found_dh;  } @@ -207,6 +156,10 @@ store_dh_state(struct device *dev, struct device_attribute *attr,  	struct scsi_device_handler *scsi_dh;  	int err = -EINVAL; +	if (sdev->sdev_state == SDEV_CANCEL || +	    sdev->sdev_state == SDEV_DEL) +		return -ENODEV; +  	if (!sdev->scsi_dh_data) {  		/*  		 * Attach to a device handler @@ -373,12 +326,14 @@ static int scsi_dh_notifier_remove(struct device *dev, void *data)   */  int scsi_register_device_handler(struct scsi_device_handler *scsi_dh)  { +  	if (get_device_handler(scsi_dh->name))  		return -EBUSY;  	spin_lock(&list_lock);  	list_add(&scsi_dh->list, &scsi_dh_list);  	spin_unlock(&list_lock); +  	bus_for_each_dev(&scsi_bus_type, NULL, scsi_dh, scsi_dh_notifier_add);  	printk(KERN_INFO "%s: device handler registered\n", scsi_dh->name); @@ -395,7 +350,6 @@ EXPORT_SYMBOL_GPL(scsi_register_device_handler);   */  int scsi_unregister_device_handler(struct scsi_device_handler *scsi_dh)  { -	struct scsi_dh_devinfo_list *tmp, *pos;  	if (!get_device_handler(scsi_dh->name))  		return -ENODEV; @@ -405,12 +359,6 @@ int scsi_unregister_device_handler(struct scsi_device_handler *scsi_dh)  	spin_lock(&list_lock);  	list_del(&scsi_dh->list); -	list_for_each_entry_safe(pos, tmp, &scsi_dh_dev_list, node) { -		if (pos->handler == scsi_dh) { -			list_del(&pos->node); -			kfree(pos); -		} -	}  	spin_unlock(&list_lock);  	printk(KERN_INFO "%s: device handler unregistered\n", scsi_dh->name); @@ -437,21 +385,39 @@ int scsi_dh_activate(struct request_queue *q, activate_complete fn, void *data)  	unsigned long flags;  	struct scsi_device *sdev;  	struct scsi_device_handler *scsi_dh = NULL; +	struct device *dev = NULL;  	spin_lock_irqsave(q->queue_lock, flags);  	sdev = q->queuedata; -	if (sdev && sdev->scsi_dh_data) +	if (!sdev) { +		spin_unlock_irqrestore(q->queue_lock, flags); +		err = SCSI_DH_NOSYS; +		if (fn) +			fn(data, err); +		return err; +	} + +	if (sdev->scsi_dh_data)  		scsi_dh = sdev->scsi_dh_data->scsi_dh; -	if (!scsi_dh || !get_device(&sdev->sdev_gendev)) +	dev = get_device(&sdev->sdev_gendev); +	if (!scsi_dh || !dev || +	    sdev->sdev_state == SDEV_CANCEL || +	    sdev->sdev_state == SDEV_DEL)  		err = SCSI_DH_NOSYS; +	if (sdev->sdev_state == SDEV_OFFLINE) +		err = SCSI_DH_DEV_OFFLINED;  	spin_unlock_irqrestore(q->queue_lock, flags); -	if (err) -		return err; +	if (err) { +		if (fn) +			fn(data, err); +		goto out; +	}  	if (scsi_dh->activate)  		err = scsi_dh->activate(sdev, fn, data); -	put_device(&sdev->sdev_gendev); +out: +	put_device(dev);  	return err;  }  EXPORT_SYMBOL_GPL(scsi_dh_activate); @@ -501,8 +467,9 @@ int scsi_dh_handler_exist(const char *name)  EXPORT_SYMBOL_GPL(scsi_dh_handler_exist);  /* - * scsi_dh_handler_attach - Attach device handler - * @sdev - sdev the handler should be attached to + * scsi_dh_attach - Attach device handler + * @q - Request queue that is associated with the scsi_device + *      the handler should be attached to   * @name - name of the handler to attach   */  int scsi_dh_attach(struct request_queue *q, const char *name) @@ -531,8 +498,9 @@ int scsi_dh_attach(struct request_queue *q, const char *name)  EXPORT_SYMBOL_GPL(scsi_dh_attach);  /* - * scsi_dh_handler_detach - Detach device handler - * @sdev - sdev the handler should be detached from + * scsi_dh_detach - Detach device handler + * @q - Request queue that is associated with the scsi_device + *      the handler should be detached from   *   * This function will detach the device handler only   * if the sdev is not part of the internal list, ie @@ -561,6 +529,38 @@ void scsi_dh_detach(struct request_queue *q)  }  EXPORT_SYMBOL_GPL(scsi_dh_detach); +/* + * scsi_dh_attached_handler_name - Get attached device handler's name + * @q - Request queue that is associated with the scsi_device + *      that may have a device handler attached + * @gfp - the GFP mask used in the kmalloc() call when allocating memory + * + * Returns name of attached handler, NULL if no handler is attached. + * Caller must take care to free the returned string. + */ +const char *scsi_dh_attached_handler_name(struct request_queue *q, gfp_t gfp) +{ +	unsigned long flags; +	struct scsi_device *sdev; +	const char *handler_name = NULL; + +	spin_lock_irqsave(q->queue_lock, flags); +	sdev = q->queuedata; +	if (!sdev || !get_device(&sdev->sdev_gendev)) +		sdev = NULL; +	spin_unlock_irqrestore(q->queue_lock, flags); + +	if (!sdev) +		return NULL; + +	if (sdev->scsi_dh_data) +		handler_name = kstrdup(sdev->scsi_dh_data->scsi_dh->name, gfp); + +	put_device(&sdev->sdev_gendev); +	return handler_name; +} +EXPORT_SYMBOL_GPL(scsi_dh_attached_handler_name); +  static struct notifier_block scsi_dh_nb = {  	.notifier_call = scsi_dh_notifier  }; diff --git a/drivers/scsi/device_handler/scsi_dh_alua.c b/drivers/scsi/device_handler/scsi_dh_alua.c index 6b729324b8d..7bcf67eec92 100644 --- a/drivers/scsi/device_handler/scsi_dh_alua.c +++ b/drivers/scsi/device_handler/scsi_dh_alua.c @@ -21,6 +21,7 @@   */  #include <linux/slab.h>  #include <linux/delay.h> +#include <linux/module.h>  #include <scsi/scsi.h>  #include <scsi/scsi_eh.h>  #include <scsi/scsi_dh.h> @@ -45,23 +46,32 @@  #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 */ +#define ALUA_OPTIMIZE_STPG		1 +  struct alua_dh_data {  	int			group_id;  	int			rel_port;  	int			tpgs;  	int			state; +	int			pref; +	unsigned		flags; /* used for optimizing STPG */  	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; @@ -110,6 +120,7 @@ static struct request *get_alua_req(struct scsi_device *sdev,  			    "%s: blk_get_request failed\n", __func__);  		return NULL;  	} +	blk_rq_set_block_pc(rq);  	if (buflen && blk_rq_map_kern(q, rq, buffer, buflen, GFP_NOIO)) {  		blk_put_request(rq); @@ -118,53 +129,15 @@ static struct request *get_alua_req(struct scsi_device *sdev,  		return NULL;  	} -	rq->cmd_type = REQ_TYPE_BLOCK_PC;  	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;  }  /* - * submit_std_inquiry - Issue a standard INQUIRY command - * @sdev: sdev the command should be send to - */ -static int submit_std_inquiry(struct scsi_device *sdev, struct alua_dh_data *h) -{ -	struct request *rq; -	int err = SCSI_DH_RES_TEMP_UNAVAIL; - -	rq = get_alua_req(sdev, h->inq, ALUA_INQUIRY_SIZE, READ); -	if (!rq) -		goto done; - -	/* Prepare the command. */ -	rq->cmd[0] = INQUIRY; -	rq->cmd[1] = 0; -	rq->cmd[2] = 0; -	rq->cmd[4] = ALUA_INQUIRY_SIZE; -	rq->cmd_len = COMMAND_SIZE(INQUIRY); - -	rq->sense = h->sense; -	memset(rq->sense, 0, SCSI_SENSE_BUFFERSIZE); -	rq->sense_len = h->senselen = 0; - -	err = blk_execute_rq(rq->q, NULL, rq, 1); -	if (err == -EIO) { -		sdev_printk(KERN_INFO, sdev, -			    "%s: std inquiry failed with %x\n", -			    ALUA_DH_NAME, rq->errors); -		h->senselen = rq->sense_len; -		err = SCSI_DH_IO; -	} -	blk_put_request(rq); -done: -	return err; -} - -/*   * submit_vpd_inquiry - Issue an INQUIRY VPD page 0x83 command   * @sdev: sdev the command should be sent to   */ @@ -205,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; @@ -216,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; @@ -253,13 +230,15 @@ static void stpg_endio(struct request *req, int error)  {  	struct alua_dh_data *h = req->end_io_data;  	struct scsi_sense_hdr sense_hdr; -	unsigned err = SCSI_DH_IO; +	unsigned err = SCSI_DH_OK; -	if (error || host_byte(req->errors) != DID_OK || -			msg_byte(req->errors) != COMMAND_COMPLETE) +	if (host_byte(req->errors) != DID_OK || +	    msg_byte(req->errors) != COMMAND_COMPLETE) { +		err = SCSI_DH_IO;  		goto done; +	} -	if (err == SCSI_DH_IO && h->senselen > 0) { +	if (req->sense_len > 0) {  		err = scsi_normalize_sense(h->sense, SCSI_SENSE_BUFFERSIZE,  					   &sense_hdr);  		if (!err) { @@ -276,7 +255,9 @@ static void stpg_endio(struct request *req, int error)  			    ALUA_DH_NAME, sense_hdr.sense_key,  			    sense_hdr.asc, sense_hdr.ascq);  		err = SCSI_DH_IO; -	} +	} else if (error) +		err = SCSI_DH_IO; +  	if (err == SCSI_DH_OK) {  		h->state = TPGS_STATE_OPTIMIZED;  		sdev_printk(KERN_INFO, h->sdev, @@ -285,7 +266,8 @@ static void stpg_endio(struct request *req, int error)  			    print_alua_state(h->state));  	}  done: -	blk_put_request(req); +	req->end_io_data = NULL; +	__blk_put_request(req->q, req);  	if (h->callback_fn) {  		h->callback_fn(h->callback_data, err);  		h->callback_fn = h->callback_data = NULL; @@ -303,7 +285,6 @@ done:  static unsigned submit_stpg(struct alua_dh_data *h)  {  	struct request *rq; -	int err = SCSI_DH_RES_TEMP_UNAVAIL;  	int stpg_len = 8;  	struct scsi_device *sdev = h->sdev; @@ -332,27 +313,21 @@ static unsigned submit_stpg(struct alua_dh_data *h)  	rq->end_io_data = h;  	blk_execute_rq_nowait(rq->q, NULL, rq, 1, stpg_endio); -	return err; +	return SCSI_DH_OK;  }  /* - * alua_std_inquiry - Evaluate standard INQUIRY command + * alua_check_tpgs - Evaluate TPGS setting   * @sdev: device to be checked   * - * Just extract the TPGS setting to find out if ALUA + * Examine the TPGS setting of the sdev to find out if ALUA   * is supported.   */ -static int alua_std_inquiry(struct scsi_device *sdev, struct alua_dh_data *h) +static int alua_check_tpgs(struct scsi_device *sdev, struct alua_dh_data *h)  { -	int err; - -	err = submit_std_inquiry(sdev, h); - -	if (err != SCSI_DH_OK) -		return err; +	int err = SCSI_DH_OK; -	/* Check TPGS setting */ -	h->tpgs = (h->inq[5] >> 4) & 0x3; +	h->tpgs = scsi_device_tpgs(sdev);  	switch (h->tpgs) {  	case TPGS_MODE_EXPLICIT|TPGS_MODE_IMPLICIT:  		sdev_printk(KERN_INFO, sdev, @@ -506,27 +481,38 @@ static int alua_check_sense(struct scsi_device *sdev,  			 * Power On, Reset, or Bus Device Reset, just retry.  			 */  			return ADD_TO_MLQUEUE; -		if (sense_hdr->asc == 0x2a && sense_hdr->ascq == 0x06) { +		if (sense_hdr->asc == 0x29 && sense_hdr->ascq == 0x04) +			/* +			 * Device internal reset +			 */ +			return ADD_TO_MLQUEUE; +		if (sense_hdr->asc == 0x2a && sense_hdr->ascq == 0x01) +			/* +			 * Mode Parameters Changed +			 */ +			return ADD_TO_MLQUEUE; +		if (sense_hdr->asc == 0x2a && sense_hdr->ascq == 0x06)  			/*  			 * ALUA state changed  			 */  			return ADD_TO_MLQUEUE; -		} -		if (sense_hdr->asc == 0x2a && sense_hdr->ascq == 0x07) { +		if (sense_hdr->asc == 0x2a && sense_hdr->ascq == 0x07)  			/*  			 * Implicit ALUA state transition failed  			 */  			return ADD_TO_MLQUEUE; -		} -		if (sense_hdr->asc == 0x3f && sense_hdr->ascq == 0x0e) { +		if (sense_hdr->asc == 0x3f && sense_hdr->ascq == 0x03) +			/* +			 * Inquiry data has changed +			 */ +			return ADD_TO_MLQUEUE; +		if (sense_hdr->asc == 0x3f && sense_hdr->ascq == 0x0e)  			/*  			 * REPORTED_LUNS_DATA_HAS_CHANGED is reported  			 * when switching controllers on targets like  			 * Intel Multi-Flex. We can just retry.  			 */  			return ADD_TO_MLQUEUE; -		} -  		break;  	} @@ -536,22 +522,30 @@ static int alua_check_sense(struct scsi_device *sdev,  /*   * alua_rtpg - Evaluate REPORT TARGET GROUP STATES   * @sdev: the device to be evaluated. + * @wait_for_transition: if nonzero, wait ALUA_FAILOVER_TIMEOUT seconds for device to exit transitioning state   *   * Evaluate the Target Port Group State.   * Returns SCSI_DH_DEV_OFFLINED if the path is - * found to be unuseable. + * found to be unusable.   */ -static int alua_rtpg(struct scsi_device *sdev, struct alua_dh_data *h) +static int alua_rtpg(struct scsi_device *sdev, struct alua_dh_data *h, int wait_for_transition)  {  	struct scsi_sense_hdr sense_hdr;  	int len, k, off, valid_states = 0; -	char *ucp; +	unsigned char *ucp;  	unsigned err; -	unsigned long expiry, interval = 10; +	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, @@ -559,6 +553,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; @@ -585,17 +594,40 @@ 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 (wait_for_transition && (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;  			valid_states = ucp[1];  		}  		off = 8 + (ucp[7] * 4);  	}  	sdev_printk(KERN_INFO, sdev, -		    "%s: port group %02x state %c supports %c%c%c%c%c%c%c\n", +		    "%s: port group %02x state %c %s supports %c%c%c%c%c%c%c\n",  		    ALUA_DH_NAME, h->group_id, print_alua_state(h->state), +		    h->pref ? "preferred" : "non-preferred",  		    valid_states&TPGS_SUPPORT_TRANSITION?'T':'t',  		    valid_states&TPGS_SUPPORT_OFFLINE?'O':'o',  		    valid_states&TPGS_SUPPORT_LBA_DEPENDENT?'L':'l', @@ -606,19 +638,23 @@ static int alua_rtpg(struct scsi_device *sdev, struct alua_dh_data *h)  	switch (h->state) {  	case TPGS_STATE_TRANSITIONING: -		if (time_before(jiffies, expiry)) { -			/* State transition, retry */ -			interval *= 10; -			msleep(interval); -			goto retry; +		if (wait_for_transition) { +			if (time_before(jiffies, expiry)) { +				/* State transition, retry */ +				interval += 2000; +				msleep(interval); +				goto retry; +			} +			err = SCSI_DH_RETRY; +		} else { +			err = SCSI_DH_OK;  		} +  		/* 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 */ +		/* Path unusable */  		err = SCSI_DH_DEV_OFFLINED;  		break;  	default: @@ -640,7 +676,7 @@ static int alua_initialize(struct scsi_device *sdev, struct alua_dh_data *h)  {  	int err; -	err = alua_std_inquiry(sdev, h); +	err = alua_check_tpgs(sdev, h);  	if (err != SCSI_DH_OK)  		goto out; @@ -648,13 +684,48 @@ static int alua_initialize(struct scsi_device *sdev, struct alua_dh_data *h)  	if (err != SCSI_DH_OK)  		goto out; -	err = alua_rtpg(sdev, h); +	err = alua_rtpg(sdev, h, 0);  	if (err != SCSI_DH_OK)  		goto out;  out:  	return err;  } +/* + * alua_set_params - set/unset the optimize flag + * @sdev: device on the path to be activated + * params - parameters in the following format + *      "no_of_params\0param1\0param2\0param3\0...\0" + * For example, to set the flag pass the following parameters + * from multipath.conf + *     hardware_handler        "2 alua 1" + */ +static int alua_set_params(struct scsi_device *sdev, const char *params) +{ +	struct alua_dh_data *h = get_alua_data(sdev); +	unsigned int optimize = 0, argc; +	const char *p = params; +	int result = SCSI_DH_OK; + +	if ((sscanf(params, "%u", &argc) != 1) || (argc != 1)) +		return -EINVAL; + +	while (*p++) +		; +	if ((sscanf(p, "%u", &optimize) != 1) || (optimize > 1)) +		return -EINVAL; + +	if (optimize) +		h->flags |= ALUA_OPTIMIZE_STPG; +	else +		h->flags &= ~ALUA_OPTIMIZE_STPG; + +	return result; +} + +static uint optimize_stpg; +module_param(optimize_stpg, uint, S_IRUGO|S_IWUSR); +MODULE_PARM_DESC(optimize_stpg, "Allow use of a non-optimized path, rather than sending a STPG, when implicit TPGS is supported (0=No,1=Yes). Default is 0.");  /*   * alua_activate - activate a path @@ -671,16 +742,40 @@ static int alua_activate(struct scsi_device *sdev,  {  	struct alua_dh_data *h = get_alua_data(sdev);  	int err = SCSI_DH_OK; +	int stpg = 0; + +	err = alua_rtpg(sdev, h, 1); +	if (err != SCSI_DH_OK) +		goto out; -	if (h->group_id != -1) { -		err = alua_rtpg(sdev, h); -		if (err != SCSI_DH_OK) -			goto out; +	if (optimize_stpg) +		h->flags |= ALUA_OPTIMIZE_STPG; + +	if (h->tpgs & TPGS_MODE_EXPLICIT) { +		switch (h->state) { +		case TPGS_STATE_NONOPTIMIZED: +			stpg = 1; +			if ((h->flags & ALUA_OPTIMIZE_STPG) && +			    (!h->pref) && +			    (h->tpgs & TPGS_MODE_IMPLICIT)) +				stpg = 0; +			break; +		case TPGS_STATE_STANDBY: +		case TPGS_STATE_UNAVAILABLE: +			stpg = 1; +			break; +		case TPGS_STATE_OFFLINE: +			err = SCSI_DH_IO; +			break; +		case TPGS_STATE_TRANSITIONING: +			err = SCSI_DH_RETRY; +			break; +		default: +			break; +		}  	} -	if (h->tpgs & TPGS_MODE_EXPLICIT && -	    h->state != TPGS_STATE_OPTIMIZED && -	    h->state != TPGS_STATE_LBA_DEPENDENT) { +	if (stpg) {  		h->callback_fn = fn;  		h->callback_data = data;  		err = submit_stpg(h); @@ -718,21 +813,10 @@ static int alua_prep_fn(struct scsi_device *sdev, struct request *req)  } -static const struct scsi_dh_devlist alua_dev_list[] = { -	{"HP", "MSA VOLUME" }, -	{"HP", "HSV101" }, -	{"HP", "HSV111" }, -	{"HP", "HSV200" }, -	{"HP", "HSV210" }, -	{"HP", "HSV300" }, -	{"IBM", "2107900" }, -	{"IBM", "2145" }, -	{"Pillar", "Axiom" }, -	{"Intel", "Multi-Flex"}, -	{"NETAPP", "LUN"}, -	{"AIX", "NVDISK"}, -	{NULL, NULL} -}; +static bool alua_match(struct scsi_device *sdev) +{ +	return (scsi_device_tpgs(sdev) != 0); +}  static int alua_bus_attach(struct scsi_device *sdev);  static void alua_bus_detach(struct scsi_device *sdev); @@ -740,12 +824,13 @@ static void alua_bus_detach(struct scsi_device *sdev);  static struct scsi_device_handler alua_dh = {  	.name = ALUA_DH_NAME,  	.module = THIS_MODULE, -	.devlist = alua_dev_list,  	.attach = alua_bus_attach,  	.detach = alua_bus_detach,  	.prep_fn = alua_prep_fn,  	.check_sense = alua_check_sense,  	.activate = alua_activate, +	.set_params = alua_set_params, +	.match = alua_match,  };  /* @@ -759,7 +844,7 @@ static int alua_bus_attach(struct scsi_device *sdev)  	unsigned long flags;  	int err = SCSI_DH_OK; -	scsi_dh_data = kzalloc(sizeof(struct scsi_device_handler *) +	scsi_dh_data = kzalloc(sizeof(*scsi_dh_data)  			       + sizeof(*h) , GFP_KERNEL);  	if (!scsi_dh_data) {  		sdev_printk(KERN_ERR, sdev, "%s: Attach failed\n", @@ -778,7 +863,7 @@ static int alua_bus_attach(struct scsi_device *sdev)  	h->sdev = sdev;  	err = alua_initialize(sdev, h); -	if (err != SCSI_DH_OK) +	if ((err != SCSI_DH_OK) && (err != SCSI_DH_DEV_OFFLINED))  		goto failed;  	if (!try_module_get(THIS_MODULE)) @@ -787,6 +872,7 @@ static int alua_bus_attach(struct scsi_device *sdev)  	spin_lock_irqsave(sdev->request_queue->queue_lock, flags);  	sdev->scsi_dh_data = scsi_dh_data;  	spin_unlock_irqrestore(sdev->request_queue->queue_lock, flags); +	sdev_printk(KERN_NOTICE, sdev, "%s: Attached\n", ALUA_DH_NAME);  	return 0; diff --git a/drivers/scsi/device_handler/scsi_dh_emc.c b/drivers/scsi/device_handler/scsi_dh_emc.c index 6faf472f753..6f07f7fe3aa 100644 --- a/drivers/scsi/device_handler/scsi_dh_emc.c +++ b/drivers/scsi/device_handler/scsi_dh_emc.c @@ -21,6 +21,7 @@   * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.   */  #include <linux/slab.h> +#include <linux/module.h>  #include <scsi/scsi.h>  #include <scsi/scsi_eh.h>  #include <scsi/scsi_dh.h> @@ -279,6 +280,7 @@ static struct request *get_req(struct scsi_device *sdev, int cmd,  		return NULL;  	} +	blk_rq_set_block_pc(rq);  	rq->cmd_len = COMMAND_SIZE(cmd);  	rq->cmd[0] = cmd; @@ -303,7 +305,6 @@ static struct request *get_req(struct scsi_device *sdev, int cmd,  		break;  	} -	rq->cmd_type = REQ_TYPE_BLOCK_PC;  	rq->cmd_flags |= REQ_FAILFAST_DEV | REQ_FAILFAST_TRANSPORT |  			 REQ_FAILFAST_DRIVER;  	rq->timeout = CLARIION_TIMEOUT; @@ -628,6 +629,24 @@ static const struct scsi_dh_devlist clariion_dev_list[] = {  	{NULL, NULL},  }; +static bool clariion_match(struct scsi_device *sdev) +{ +	int i; + +	if (scsi_device_tpgs(sdev)) +		return false; + +	for (i = 0; clariion_dev_list[i].vendor; i++) { +		if (!strncmp(sdev->vendor, clariion_dev_list[i].vendor, +			strlen(clariion_dev_list[i].vendor)) && +		    !strncmp(sdev->model, clariion_dev_list[i].model, +			strlen(clariion_dev_list[i].model))) { +			return true; +		} +	} +	return false; +} +  static int clariion_bus_attach(struct scsi_device *sdev);  static void clariion_bus_detach(struct scsi_device *sdev); @@ -641,6 +660,7 @@ static struct scsi_device_handler clariion_dh = {  	.activate	= clariion_activate,  	.prep_fn	= clariion_prep_fn,  	.set_params	= clariion_set_params, +	.match		= clariion_match,  };  static int clariion_bus_attach(struct scsi_device *sdev) @@ -650,7 +670,7 @@ static int clariion_bus_attach(struct scsi_device *sdev)  	unsigned long flags;  	int err; -	scsi_dh_data = kzalloc(sizeof(struct scsi_device_handler *) +	scsi_dh_data = kzalloc(sizeof(*scsi_dh_data)  			       + sizeof(*h) , GFP_KERNEL);  	if (!scsi_dh_data) {  		sdev_printk(KERN_ERR, sdev, "%s: Attach failed\n", diff --git a/drivers/scsi/device_handler/scsi_dh_hp_sw.c b/drivers/scsi/device_handler/scsi_dh_hp_sw.c index e3916641e62..e9d9fea9e27 100644 --- a/drivers/scsi/device_handler/scsi_dh_hp_sw.c +++ b/drivers/scsi/device_handler/scsi_dh_hp_sw.c @@ -22,6 +22,7 @@   */  #include <linux/slab.h> +#include <linux/module.h>  #include <scsi/scsi.h>  #include <scsi/scsi_dbg.h>  #include <scsi/scsi_eh.h> @@ -119,7 +120,7 @@ retry:  	if (!req)  		return SCSI_DH_RES_TEMP_UNAVAIL; -	req->cmd_type = REQ_TYPE_BLOCK_PC; +	blk_rq_set_block_pc(req);  	req->cmd_flags |= REQ_FAILFAST_DEV | REQ_FAILFAST_TRANSPORT |  			  REQ_FAILFAST_DRIVER;  	req->cmd_len = COMMAND_SIZE(TEST_UNIT_READY); @@ -225,7 +226,8 @@ static void start_stop_endio(struct request *req, int error)  		}  	}  done: -	blk_put_request(req); +	req->end_io_data = NULL; +	__blk_put_request(req->q, req);  	if (h->callback_fn) {  		h->callback_fn(h->callback_data, err);  		h->callback_fn = h->callback_data = NULL; @@ -248,7 +250,7 @@ static int hp_sw_start_stop(struct hp_sw_dh_data *h)  	if (!req)  		return SCSI_DH_RES_TEMP_UNAVAIL; -	req->cmd_type = REQ_TYPE_BLOCK_PC; +	blk_rq_set_block_pc(req);  	req->cmd_flags |= REQ_FAILFAST_DEV | REQ_FAILFAST_TRANSPORT |  			  REQ_FAILFAST_DRIVER;  	req->cmd_len = COMMAND_SIZE(START_STOP); @@ -318,6 +320,24 @@ static const struct scsi_dh_devlist hp_sw_dh_data_list[] = {  	{NULL, NULL},  }; +static bool hp_sw_match(struct scsi_device *sdev) +{ +	int i; + +	if (scsi_device_tpgs(sdev)) +		return false; + +	for (i = 0; hp_sw_dh_data_list[i].vendor; i++) { +		if (!strncmp(sdev->vendor, hp_sw_dh_data_list[i].vendor, +			strlen(hp_sw_dh_data_list[i].vendor)) && +		    !strncmp(sdev->model, hp_sw_dh_data_list[i].model, +			strlen(hp_sw_dh_data_list[i].model))) { +			return true; +		} +	} +	return false; +} +  static int hp_sw_bus_attach(struct scsi_device *sdev);  static void hp_sw_bus_detach(struct scsi_device *sdev); @@ -329,6 +349,7 @@ static struct scsi_device_handler hp_sw_dh = {  	.detach		= hp_sw_bus_detach,  	.activate	= hp_sw_activate,  	.prep_fn	= hp_sw_prep_fn, +	.match		= hp_sw_match,  };  static int hp_sw_bus_attach(struct scsi_device *sdev) @@ -338,8 +359,8 @@ static int hp_sw_bus_attach(struct scsi_device *sdev)  	unsigned long flags;  	int ret; -	scsi_dh_data = kzalloc(sizeof(struct scsi_device_handler *) -			       + sizeof(struct hp_sw_dh_data) , GFP_KERNEL); +	scsi_dh_data = kzalloc(sizeof(*scsi_dh_data) +			       + sizeof(*h) , GFP_KERNEL);  	if (!scsi_dh_data) {  		sdev_printk(KERN_ERR, sdev, "%s: Attach Failed\n",  			    HP_SW_NAME); diff --git a/drivers/scsi/device_handler/scsi_dh_rdac.c b/drivers/scsi/device_handler/scsi_dh_rdac.c index 5be3ae15cb7..826069db984 100644 --- a/drivers/scsi/device_handler/scsi_dh_rdac.c +++ b/drivers/scsi/device_handler/scsi_dh_rdac.c @@ -1,5 +1,5 @@  /* - * Engenio/LSI RDAC SCSI Device Handler + * LSI/Engenio/NetApp E-Series RDAC SCSI Device Handler   *   * Copyright (C) 2005 Mike Christie. All rights reserved.   * Copyright (C) Chandra Seetharaman, IBM Corp. 2007 @@ -24,6 +24,7 @@  #include <scsi/scsi_dh.h>  #include <linux/workqueue.h>  #include <linux/slab.h> +#include <linux/module.h>  #define RDAC_NAME "rdac"  #define RDAC_RETRY_COUNT 5 @@ -35,7 +36,7 @@   * mode page were taken from the LSI RDAC 2.4 GPL'd   * driver, and then converted to Linux conventions.   */ -#define RDAC_QUIESCENCE_TIME 20; +#define RDAC_QUIESCENCE_TIME 20  /*   * Page Codes   */ @@ -128,25 +129,7 @@ struct c4_inquiry {  	u8	reserved[2];  }; -struct rdac_controller { -	u8			subsys_id[SUBSYS_ID_LEN]; -	u8			slot_id[SLOT_ID_LEN]; -	int			use_ms10; -	struct kref		kref; -	struct list_head	node; /* list of all controllers */ -	union			{ -		struct rdac_pg_legacy legacy; -		struct rdac_pg_expanded expanded; -	} mode_select; -	u8	index; -	u8	array_name[ARRAY_LABEL_LEN]; -	spinlock_t		ms_lock; -	int			ms_queued; -	struct work_struct	ms_work; -	struct scsi_device	*ms_sdev; -	struct list_head	ms_head; -}; - +#define UNIQUE_ID_LEN 16  struct c8_inquiry {  	u8	peripheral_info;  	u8	page_code; /* 0xC8 */ @@ -159,12 +142,31 @@ struct c8_inquiry {  	u8	vol_user_label_len;  	u8	vol_user_label[60];  	u8	array_uniq_id_len; -	u8	array_unique_id[16]; +	u8	array_unique_id[UNIQUE_ID_LEN];  	u8	array_user_label_len;  	u8	array_user_label[60];  	u8	lun[8];  }; +struct rdac_controller { +	u8			array_id[UNIQUE_ID_LEN]; +	int			use_ms10; +	struct kref		kref; +	struct list_head	node; /* list of all controllers */ +	union			{ +		struct rdac_pg_legacy legacy; +		struct rdac_pg_expanded expanded; +	} mode_select; +	u8	index; +	u8	array_name[ARRAY_LABEL_LEN]; +	struct Scsi_Host	*host; +	spinlock_t		ms_lock; +	int			ms_queued; +	struct work_struct	ms_work; +	struct scsi_device	*ms_sdev; +	struct list_head	ms_head; +}; +  struct c2_inquiry {  	u8	peripheral_info;  	u8	page_code;	/* 0xC2 */ @@ -182,14 +184,24 @@ struct rdac_dh_data {  	struct rdac_controller	*ctlr;  #define UNINITIALIZED_LUN	(1 << 8)  	unsigned		lun; + +#define RDAC_MODE		0 +#define RDAC_MODE_AVT		1 +#define RDAC_MODE_IOSHIP	2 +	unsigned char		mode; +  #define RDAC_STATE_ACTIVE	0  #define RDAC_STATE_PASSIVE	1  	unsigned char		state;  #define RDAC_LUN_UNOWNED	0  #define RDAC_LUN_OWNED		1 -#define RDAC_LUN_AVT		2  	char			lun_state; + +#define RDAC_PREFERRED		0 +#define RDAC_NON_PREFERRED	1 +	char			preferred; +  	unsigned char		sense[SCSI_SENSE_BUFFERSIZE];  	union			{  		struct c2_inquiry c2; @@ -199,11 +211,15 @@ struct rdac_dh_data {  	} inq;  }; +static const char *mode[] = { +	"RDAC", +	"AVT", +	"IOSHIP", +};  static const char *lun_state[] =  {  	"unowned",  	"owned", -	"owned (AVT mode)",  };  struct rdac_queue_data { @@ -263,6 +279,7 @@ static struct request *get_rdac_req(struct scsi_device *sdev,  				"get_rdac_req: blk_get_request failed.\n");  		return NULL;  	} +	blk_rq_set_block_pc(rq);  	if (buflen && blk_rq_map_kern(q, rq, buffer, buflen, GFP_NOIO)) {  		blk_put_request(rq); @@ -271,7 +288,6 @@ static struct request *get_rdac_req(struct scsi_device *sdev,  		return NULL;  	} -	rq->cmd_type = REQ_TYPE_BLOCK_PC;  	rq->cmd_flags |= REQ_FAILFAST_DEV | REQ_FAILFAST_TRANSPORT |  			 REQ_FAILFAST_DRIVER;  	rq->retries = RDAC_RETRIES; @@ -281,11 +297,13 @@ static struct request *get_rdac_req(struct scsi_device *sdev,  }  static struct request *rdac_failover_get(struct scsi_device *sdev, -					 struct rdac_dh_data *h) +			struct rdac_dh_data *h, struct list_head *list)  {  	struct request *rq;  	struct rdac_mode_common *common;  	unsigned data_size; +	struct rdac_queue_data *qdata; +	u8 *lun_table;  	if (h->ctlr->use_ms10) {  		struct rdac_pg_expanded *rdac_pg; @@ -298,6 +316,7 @@ static struct request *rdac_failover_get(struct scsi_device *sdev,  		rdac_pg->subpage_code = 0x1;  		rdac_pg->page_len[0] = 0x01;  		rdac_pg->page_len[1] = 0x28; +		lun_table = rdac_pg->lun_table;  	} else {  		struct rdac_pg_legacy *rdac_pg; @@ -307,11 +326,16 @@ static struct request *rdac_failover_get(struct scsi_device *sdev,  		common = &rdac_pg->common;  		rdac_pg->page_code = RDAC_PAGE_CODE_REDUNDANT_CONTROLLER;  		rdac_pg->page_len = 0x68; +		lun_table = rdac_pg->lun_table;  	}  	common->rdac_mode[1] = RDAC_MODE_TRANSFER_SPECIFIED_LUNS;  	common->quiescence_timeout = RDAC_QUIESCENCE_TIME;  	common->rdac_options = RDAC_FORCED_QUIESENCE; +	list_for_each_entry(qdata, list, entry) { +		lun_table[qdata->h->lun] = 0x81; +	} +  	/* get request for block layer packet command */  	rq = get_rdac_req(sdev, &h->ctlr->mode_select, data_size, WRITE);  	if (!rq) @@ -340,43 +364,33 @@ static void release_controller(struct kref *kref)  	struct rdac_controller *ctlr;  	ctlr = container_of(kref, struct rdac_controller, kref); -	flush_workqueue(kmpath_rdacd); -	spin_lock(&list_lock);  	list_del(&ctlr->node); -	spin_unlock(&list_lock);  	kfree(ctlr);  } -static struct rdac_controller *get_controller(u8 *subsys_id, u8 *slot_id, -						char *array_name) +static struct rdac_controller *get_controller(int index, char *array_name, +			u8 *array_id, struct scsi_device *sdev)  {  	struct rdac_controller *ctlr, *tmp; -	spin_lock(&list_lock); -  	list_for_each_entry(tmp, &ctlr_list, node) { -		if ((memcmp(tmp->subsys_id, subsys_id, SUBSYS_ID_LEN) == 0) && -			  (memcmp(tmp->slot_id, slot_id, SLOT_ID_LEN) == 0)) { +		if ((memcmp(tmp->array_id, array_id, UNIQUE_ID_LEN) == 0) && +			  (tmp->index == index) && +			  (tmp->host == sdev->host)) {  			kref_get(&tmp->kref); -			spin_unlock(&list_lock);  			return tmp;  		}  	}  	ctlr = kmalloc(sizeof(*ctlr), GFP_ATOMIC);  	if (!ctlr) -		goto done; +		return NULL;  	/* initialize fields of controller */ -	memcpy(ctlr->subsys_id, subsys_id, SUBSYS_ID_LEN); -	memcpy(ctlr->slot_id, slot_id, SLOT_ID_LEN); +	memcpy(ctlr->array_id, array_id, UNIQUE_ID_LEN); +	ctlr->index = index; +	ctlr->host = sdev->host;  	memcpy(ctlr->array_name, array_name, ARRAY_LABEL_LEN); -	/* update the controller index */ -	if (slot_id[1] == 0x31) -		ctlr->index = 0; -	else -		ctlr->index = 1; -  	kref_init(&ctlr->kref);  	ctlr->use_ms10 = -1;  	ctlr->ms_queued = 0; @@ -385,8 +399,7 @@ static struct rdac_controller *get_controller(u8 *subsys_id, u8 *slot_id,  	INIT_WORK(&ctlr->ms_work, send_mode_select);  	INIT_LIST_HEAD(&ctlr->ms_head);  	list_add(&ctlr->node, &ctlr_list); -done: -	spin_unlock(&list_lock); +  	return ctlr;  } @@ -422,7 +435,7 @@ done:  }  static int get_lun_info(struct scsi_device *sdev, struct rdac_dh_data *h, -			char *array_name) +			char *array_name, u8 *array_id)  {  	int err, i;  	struct c8_inquiry *inqp; @@ -441,6 +454,8 @@ static int get_lun_info(struct scsi_device *sdev, struct rdac_dh_data *h,  			*(array_name+i) = inqp->array_user_label[(2*i)+1];  		*(array_name+ARRAY_LABEL_LEN-1) = '\0'; +		memset(array_id, 0, UNIQUE_ID_LEN); +		memcpy(array_id, inqp->array_unique_id, inqp->array_uniq_id_len);  	}  	return err;  } @@ -450,42 +465,57 @@ static int check_ownership(struct scsi_device *sdev, struct rdac_dh_data *h)  	int err;  	struct c9_inquiry *inqp; -	h->lun_state = RDAC_LUN_UNOWNED;  	h->state = RDAC_STATE_ACTIVE;  	err = submit_inquiry(sdev, 0xC9, sizeof(struct c9_inquiry), h);  	if (err == SCSI_DH_OK) {  		inqp = &h->inq.c9; -		if ((inqp->avte_cvp >> 7) == 0x1) { -			/* LUN in AVT mode */ -			sdev_printk(KERN_NOTICE, sdev, -				    "%s: AVT mode detected\n", -				    RDAC_NAME); -			h->lun_state = RDAC_LUN_AVT; -		} else if ((inqp->avte_cvp & 0x1) != 0) { -			/* LUN was owned by the controller */ +		/* detect the operating mode */ +		if ((inqp->avte_cvp >> 5) & 0x1) +			h->mode = RDAC_MODE_IOSHIP; /* LUN in IOSHIP mode */ +		else if (inqp->avte_cvp >> 7) +			h->mode = RDAC_MODE_AVT; /* LUN in AVT mode */ +		else +			h->mode = RDAC_MODE; /* LUN in RDAC mode */ + +		/* Update ownership */ +		if (inqp->avte_cvp & 0x1)  			h->lun_state = RDAC_LUN_OWNED; +		else { +			h->lun_state = RDAC_LUN_UNOWNED; +			if (h->mode == RDAC_MODE) +				h->state = RDAC_STATE_PASSIVE;  		} -	} -	if (h->lun_state == RDAC_LUN_UNOWNED) -		h->state = RDAC_STATE_PASSIVE; +		/* Update path prio*/ +		if (inqp->path_prio & 0x1) +			h->preferred = RDAC_PREFERRED; +		else +			h->preferred = RDAC_NON_PREFERRED; +	}  	return err;  }  static int initialize_controller(struct scsi_device *sdev, -				 struct rdac_dh_data *h, char *array_name) +		struct rdac_dh_data *h, char *array_name, u8 *array_id)  { -	int err; +	int err, index;  	struct c4_inquiry *inqp;  	err = submit_inquiry(sdev, 0xC4, sizeof(struct c4_inquiry), h);  	if (err == SCSI_DH_OK) {  		inqp = &h->inq.c4; -		h->ctlr = get_controller(inqp->subsys_id, inqp->slot_id, -					array_name); +		/* get the controller index */ +		if (inqp->slot_id[1] == 0x31) +			index = 0; +		else +			index = 1; + +		spin_lock(&list_lock); +		h->ctlr = get_controller(index, array_name, array_id, sdev);  		if (!h->ctlr)  			err = SCSI_DH_RES_TEMP_UNAVAIL; +		spin_unlock(&list_lock);  	}  	return err;  } @@ -565,7 +595,6 @@ static void send_mode_select(struct work_struct *work)  	int err, retry_cnt = RDAC_RETRY_COUNT;  	struct rdac_queue_data *tmp, *qdata;  	LIST_HEAD(list); -	u8 *lun_table;  	spin_lock(&ctlr->ms_lock);  	list_splice_init(&ctlr->ms_head, &list); @@ -573,21 +602,12 @@ static void send_mode_select(struct work_struct *work)  	ctlr->ms_sdev = NULL;  	spin_unlock(&ctlr->ms_lock); -	if (ctlr->use_ms10) -		lun_table = ctlr->mode_select.expanded.lun_table; -	else -		lun_table = ctlr->mode_select.legacy.lun_table; -  retry:  	err = SCSI_DH_RES_TEMP_UNAVAIL; -	rq = rdac_failover_get(sdev, h); +	rq = rdac_failover_get(sdev, h, &list);  	if (!rq)  		goto done; -	list_for_each_entry(qdata, &list, entry) { -		lun_table[qdata->h->lun] = 0x81; -	} -  	RDAC_LOG(RDAC_LOG_FAILOVER, sdev, "array %s, ctlr %d, "  		"%s MODE_SELECT command",  		(char *) h->ctlr->array_name, h->ctlr->index, @@ -650,12 +670,27 @@ static int rdac_activate(struct scsi_device *sdev,  {  	struct rdac_dh_data *h = get_rdac_data(sdev);  	int err = SCSI_DH_OK; +	int act = 0;  	err = check_ownership(sdev, h);  	if (err != SCSI_DH_OK)  		goto done; -	if (h->lun_state == RDAC_LUN_UNOWNED) { +	switch (h->mode) { +	case RDAC_MODE: +		if (h->lun_state == RDAC_LUN_UNOWNED) +			act = 1; +		break; +	case RDAC_MODE_IOSHIP: +		if ((h->lun_state == RDAC_LUN_UNOWNED) && +		    (h->preferred == RDAC_PREFERRED)) +			act = 1; +		break; +	default: +		break; +	} + +	if (act) {  		err = queue_mode_select(sdev, fn, data);  		if (err == SCSI_DH_OK)  			return 0; @@ -751,33 +786,45 @@ static const struct scsi_dh_devlist rdac_dev_list[] = {  	{"IBM", "1742"},  	{"IBM", "1745"},  	{"IBM", "1746"}, +	{"IBM", "1813"},  	{"IBM", "1814"},  	{"IBM", "1815"},  	{"IBM", "1818"},  	{"IBM", "3526"}, -	{"SGI", "TP9400"}, -	{"SGI", "TP9500"}, +	{"SGI", "TP9"},  	{"SGI", "IS"},  	{"STK", "OPENstorage D280"}, -	{"SUN", "CSM200_R"}, -	{"SUN", "LCSM100_I"}, -	{"SUN", "LCSM100_S"}, -	{"SUN", "LCSM100_E"}, -	{"SUN", "LCSM100_F"}, -	{"DELL", "MD3000"}, -	{"DELL", "MD3000i"}, -	{"DELL", "MD32xx"}, -	{"DELL", "MD32xxi"}, -	{"DELL", "MD36xxi"}, -	{"LSI", "INF-01-00"}, -	{"ENGENIO", "INF-01-00"},  	{"STK", "FLEXLINE 380"}, -	{"SUN", "CSM100_R_FC"}, +	{"SUN", "CSM"}, +	{"SUN", "LCSM100"},  	{"SUN", "STK6580_6780"},  	{"SUN", "SUN_6180"}, +	{"SUN", "ArrayStorage"}, +	{"DELL", "MD3"}, +	{"NETAPP", "INF-01-00"}, +	{"LSI", "INF-01-00"}, +	{"ENGENIO", "INF-01-00"},  	{NULL, NULL},  }; +static bool rdac_match(struct scsi_device *sdev) +{ +	int i; + +	if (scsi_device_tpgs(sdev)) +		return false; + +	for (i = 0; rdac_dev_list[i].vendor; i++) { +		if (!strncmp(sdev->vendor, rdac_dev_list[i].vendor, +			strlen(rdac_dev_list[i].vendor)) && +		    !strncmp(sdev->model, rdac_dev_list[i].model, +			strlen(rdac_dev_list[i].model))) { +			return true; +		} +	} +	return false; +} +  static int rdac_bus_attach(struct scsi_device *sdev);  static void rdac_bus_detach(struct scsi_device *sdev); @@ -790,6 +837,7 @@ static struct scsi_device_handler rdac_dh = {  	.attach = rdac_bus_attach,  	.detach = rdac_bus_detach,  	.activate = rdac_activate, +	.match = rdac_match,  };  static int rdac_bus_attach(struct scsi_device *sdev) @@ -799,13 +847,14 @@ static int rdac_bus_attach(struct scsi_device *sdev)  	unsigned long flags;  	int err;  	char array_name[ARRAY_LABEL_LEN]; +	char array_id[UNIQUE_ID_LEN]; -	scsi_dh_data = kzalloc(sizeof(struct scsi_device_handler *) +	scsi_dh_data = kzalloc(sizeof(*scsi_dh_data)  			       + sizeof(*h) , GFP_KERNEL);  	if (!scsi_dh_data) {  		sdev_printk(KERN_ERR, sdev, "%s: Attach failed\n",  			    RDAC_NAME); -		return 0; +		return -ENOMEM;  	}  	scsi_dh_data->scsi_dh = &rdac_dh; @@ -813,11 +862,11 @@ static int rdac_bus_attach(struct scsi_device *sdev)  	h->lun = UNINITIALIZED_LUN;  	h->state = RDAC_STATE_ACTIVE; -	err = get_lun_info(sdev, h, array_name); +	err = get_lun_info(sdev, h, array_name, array_id);  	if (err != SCSI_DH_OK)  		goto failed; -	err = initialize_controller(sdev, h, array_name); +	err = initialize_controller(sdev, h, array_name, array_id);  	if (err != SCSI_DH_OK)  		goto failed; @@ -837,13 +886,16 @@ static int rdac_bus_attach(struct scsi_device *sdev)  	spin_unlock_irqrestore(sdev->request_queue->queue_lock, flags);  	sdev_printk(KERN_NOTICE, sdev, -		    "%s: LUN %d (%s)\n", -		    RDAC_NAME, h->lun, lun_state[(int)h->lun_state]); +		    "%s: LUN %d (%s) (%s)\n", +		    RDAC_NAME, h->lun, mode[(int)h->mode], +		    lun_state[(int)h->lun_state]);  	return 0;  clean_ctlr: +	spin_lock(&list_lock);  	kref_put(&h->ctlr->kref, release_controller); +	spin_unlock(&list_lock);  failed:  	kfree(scsi_dh_data); @@ -858,14 +910,19 @@ static void rdac_bus_detach( struct scsi_device *sdev )  	struct rdac_dh_data *h;  	unsigned long flags; -	spin_lock_irqsave(sdev->request_queue->queue_lock, flags);  	scsi_dh_data = sdev->scsi_dh_data; +	h = (struct rdac_dh_data *) scsi_dh_data->buf; +	if (h->ctlr && h->ctlr->ms_queued) +		flush_workqueue(kmpath_rdacd); + +	spin_lock_irqsave(sdev->request_queue->queue_lock, flags);  	sdev->scsi_dh_data = NULL;  	spin_unlock_irqrestore(sdev->request_queue->queue_lock, flags); -	h = (struct rdac_dh_data *) scsi_dh_data->buf; +	spin_lock(&list_lock);  	if (h->ctlr)  		kref_put(&h->ctlr->kref, release_controller); +	spin_unlock(&list_lock);  	kfree(scsi_dh_data);  	module_put(THIS_MODULE);  	sdev_printk(KERN_NOTICE, sdev, "%s: Detached\n", RDAC_NAME); @@ -890,6 +947,8 @@ static int __init rdac_init(void)  	if (!kmpath_rdacd) {  		scsi_unregister_device_handler(&rdac_dh);  		printk(KERN_ERR "kmpath_rdacd creation failed.\n"); + +		r = -EINVAL;  	}  done:  	return r; @@ -904,6 +963,7 @@ static void __exit rdac_exit(void)  module_init(rdac_init);  module_exit(rdac_exit); -MODULE_DESCRIPTION("Multipath LSI/Engenio RDAC driver"); +MODULE_DESCRIPTION("Multipath LSI/Engenio/NetApp E-Series RDAC driver");  MODULE_AUTHOR("Mike Christie, Chandra Seetharaman"); +MODULE_VERSION("01.00.0000.0000");  MODULE_LICENSE("GPL");  | 
