linux-scsi.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [PATCH 4/7] scsi_dh: add EMC Clariion device handler
  2008-02-28  1:08 [PATCH 0/7] Move hardware handlers from dm layer to SCSI layer Chandra Seetharaman
@ 2008-02-28  1:08 ` Chandra Seetharaman
  0 siblings, 0 replies; 17+ messages in thread
From: Chandra Seetharaman @ 2008-02-28  1:08 UTC (permalink / raw)
  To: dm-devel, linux-scsi
  Cc: andmike, michaelc, Chandra Seetharaman, James.Bottomley,
	jens.axboe, agk

Subject: scsi_dh: add EMC Clariion device handler

From: Chandra Seetharaman <sekharan@us.ibm.com>

This adds support for EMC Clariions. This patch has the features that 
currently exists in mainline and advanced features from Ed's patches.

Signed-off-by: Chandra Seetharaman <sekharan@us.ibm.com>
Signed-off-by: Mike Christie <michaelc@cs.wisc.edu>
---

 drivers/scsi/device_handler/Kconfig       |    6 	6 +	0 -	0 !
 drivers/scsi/device_handler/Makefile      |    1 	1 +	0 -	0 !
 drivers/scsi/device_handler/scsi_dh_emc.c |  488 	488 +	0 -	0 !
 3 files changed, 495 insertions(+)

Index: linux-2.6.25-rc2-mm1/drivers/scsi/device_handler/Kconfig
===================================================================
--- linux-2.6.25-rc2-mm1.orig/drivers/scsi/device_handler/Kconfig
+++ linux-2.6.25-rc2-mm1/drivers/scsi/device_handler/Kconfig
@@ -24,3 +24,9 @@ config SCSI_DH_HP_SW
 	If you have a HP/COMPAQ MSA device that requires START_STOP to
 	be sent to start it and cannot upgrade the firmware then select y.
 	Otherwise, say N.
+
+config SCSI_DH_EMC
+	tristate "EMC CLARiiON Device Handler"
+	depends on SCSI_DH
+	help
+	If you have a EMC CLARiiON select y. Otherwise, say N.
Index: linux-2.6.25-rc2-mm1/drivers/scsi/device_handler/scsi_dh_emc.c
===================================================================
--- /dev/null
+++ linux-2.6.25-rc2-mm1/drivers/scsi/device_handler/scsi_dh_emc.c
@@ -0,0 +1,488 @@
+/*
+ * Target driver for EMC CLARiiON AX/CX-series hardware.
+ * Based on code from Lars Marowsky-Bree <lmb@suse.de>
+ * and Ed Goggin <egoggin@emc.com>.
+ *
+ * Copyright (C) 2006 Red Hat, Inc.  All rights reserved.
+ * Copyright (C) 2006 Mike Christie
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; see the file COPYING.  If not, write to
+ * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+#include <scsi/scsi.h>
+#include <scsi/scsi_eh.h>
+#include <scsi/scsi_dh.h>
+#include <scsi/scsi_device.h>
+
+#define CLARIION_NAME			"emc_clariion"
+
+#define CLARIION_TRESPASS_PAGE		0x22
+#define CLARIION_BUFFER_SIZE		0x80
+#define CLARIION_TIMEOUT		(60 * HZ)
+#define CLARIION_RETRIES		3
+#define CLARIION_UNBOUND_LU		-1
+
+static unsigned char long_trespass[] = {
+	0, 0, 0, 0,
+	CLARIION_TRESPASS_PAGE,	/* Page code */
+	0x09,			/* Page length - 2 */
+	0x81,			/* Trespass code + Honor reservation bit */
+	0xff, 0xff,		/* Trespass target */
+	0, 0, 0, 0, 0, 0	/* Reserved bytes / unknown */
+};
+
+static unsigned char long_trespass_hr[] = {
+	0, 0, 0, 0,
+	CLARIION_TRESPASS_PAGE,	/* Page code */
+	0x09,			/* Page length - 2 */
+	0x01,			/* Trespass code + Honor reservation bit */
+	0xff, 0xff,		/* Trespass target */
+	0, 0, 0, 0, 0, 0	/* Reserved bytes / unknown */
+};
+
+static unsigned char short_trespass[] = {
+	0, 0, 0, 0,
+	CLARIION_TRESPASS_PAGE,	/* Page code */
+	0x02,			/* Page length - 2 */
+	0x81,			/* Trespass code + Honor reservation bit */
+	0xff,			/* Trespass target */
+};
+
+static unsigned char short_trespass_hr[] = {
+	0, 0, 0, 0,
+	CLARIION_TRESPASS_PAGE,	/* Page code */
+	0x02,			/* Page length - 2 */
+	0x01,			/* Trespass code + Honor reservation bit */
+	0xff,			/* Trespass target */
+};
+
+struct clariion_dh_data {
+	/*
+	 * Use short trespass command (FC-series) or the long version
+	 * (default for AX/CX CLARiiON arrays).
+	 */
+	unsigned short_trespass;
+	/*
+	 * Whether or not (default) to honor SCSI reservations when
+	 * initiating a switch-over.
+	 */
+	unsigned hr;
+	/* I/O buffer for both MODE_SELECT and INQUIRY commands. */
+	char buffer[CLARIION_BUFFER_SIZE];
+	/*
+	 * SCSI sense buffer for commands -- assumes serial issuance
+	 * and completion sequence of all commands for same multipath.
+	 */
+	unsigned char sense[SCSI_SENSE_BUFFERSIZE];
+	/* which SP (A=0,B=1,UNBOUND=-1) is dflt SP for path's mapped dev */
+	int default_sp;
+	/* which SP (A=0,B=1,UNBOUND=-1) is active for path's mapped dev */
+	int current_sp;
+};
+
+/*
+ * Parse MODE_SELECT cmd reply.
+ */
+static int trespass_endio(struct scsi_device *sdev, int result)
+{
+	int err = SCSI_DH_OK;
+	struct scsi_sense_hdr sshdr;
+	struct clariion_dh_data *csdev = sdev->sdev_dh_data;
+	char *sense = csdev->sense;
+
+	if (status_byte(result) == CHECK_CONDITION &&
+	    scsi_normalize_sense(sense, SCSI_SENSE_BUFFERSIZE, &sshdr)) {
+		sdev_printk(KERN_ERR, sdev, "Found valid sense data 0x%2x, "
+			    "0x%2x, 0x%2x while sending CLARiiON trespass "
+			    "command.\n", sshdr.sense_key, sshdr.asc,
+			     sshdr.ascq);
+
+		if ((sshdr.sense_key == 0x05) && (sshdr.asc == 0x04) &&
+		     (sshdr.ascq == 0x00)) {
+			/*
+			 * Array based copy in progress -- do not send
+			 * mode_select or copy will be aborted mid-stream.
+			 */
+			sdev_printk(KERN_INFO, sdev, "Array Based Copy in "
+				    "progress while sending CLARiiON trespass "
+				    "command.\n");
+			err = SCSI_DH_DEV_TEMP_BUSY;
+		} else if ((sshdr.sense_key == 0x02) && (sshdr.asc == 0x04) &&
+			    (sshdr.ascq == 0x03)) {
+			/*
+			 * LUN Not Ready - Manual Intervention Required
+			 * indicates in-progress ucode upgrade (NDU).
+			 */
+			sdev_printk(KERN_INFO, sdev, "Detected in-progress "
+				    "ucode upgrade NDU operation while sending "
+				    "CLARiiON trespass command.\n");
+			err = SCSI_DH_DEV_TEMP_BUSY;
+		} else
+			err = SCSI_DH_DEV_FAILED;
+	} else if (result) {
+		sdev_printk(KERN_ERR, sdev, "Error 0x%x while sending "
+			    "CLARiiON trespass command.\n", result);
+		err = SCSI_DH_IO;
+	}
+
+	return err;
+}
+
+static int parse_sp_info_reply(struct scsi_device *sdev, int result,
+		int *default_sp, int *current_sp, int *new_current_sp)
+{
+	int err = SCSI_DH_OK;
+	struct clariion_dh_data *csdev = sdev->sdev_dh_data;
+
+	if (result == 0) {
+		/* check for in-progress ucode upgrade (NDU) */
+		if (csdev->buffer[48] != 0) {
+			sdev_printk(KERN_NOTICE, sdev, "Detected in-progress "
+			       "ucode upgrade NDU operation while finding "
+			       "current active SP.");
+			err = SCSI_DH_DEV_TEMP_BUSY;
+		} else {
+			*default_sp = csdev->buffer[5];
+
+			if (csdev->buffer[4] == 2)
+				/* SP for path is current */
+				*current_sp = csdev->buffer[8];
+			else {
+				if (csdev->buffer[4] == 1)
+					/* SP for this path is NOT current */
+					if (csdev->buffer[8] == 0)
+						*current_sp = 1;
+					else
+						*current_sp = 0;
+				else
+					/* unbound LU or LUNZ */
+					*current_sp = CLARIION_UNBOUND_LU;
+			}
+			*new_current_sp =  csdev->buffer[8];
+		}
+	} else {
+		struct scsi_sense_hdr sshdr;
+
+		err = SCSI_DH_IO;
+
+		if (scsi_normalize_sense(csdev->sense, SCSI_SENSE_BUFFERSIZE,
+							   &sshdr))
+			sdev_printk(KERN_ERR, sdev, "Found valid sense data "
+			      "0x%2x, 0x%2x, 0x%2x while finding current "
+			      "active SP.", sshdr.sense_key, sshdr.asc,
+			      sshdr.ascq);
+		else
+			sdev_printk(KERN_ERR, sdev, "Error 0x%x finding "
+			      "current active SP.", result);
+	}
+
+	return err;
+}
+
+static int sp_info_endio(struct scsi_device *sdev, int result,
+					int mode_select_sent, int *done)
+{
+	struct clariion_dh_data *csdev = sdev->sdev_dh_data;
+	int err_flags, default_sp, current_sp, new_current_sp;
+
+	err_flags = parse_sp_info_reply(sdev, result, &default_sp,
+					     &current_sp, &new_current_sp);
+
+	if (err_flags != SCSI_DH_OK)
+		goto done;
+
+	if (mode_select_sent) {
+		csdev->default_sp = default_sp;
+		csdev->current_sp = current_sp;
+	} else {
+		/*
+		 * Issue the actual module_selec request IFF either
+		 * (1) we do not know the identity of the current SP OR
+		 * (2) what we think we know is actually correct.
+		 */
+		if ((current_sp != CLARIION_UNBOUND_LU) &&
+		    (new_current_sp != current_sp)) {
+
+			csdev->default_sp = default_sp;
+			csdev->current_sp = current_sp;
+
+			sdev_printk(KERN_INFO, sdev, "Ignoring path group "
+			       "switch-over command for CLARiiON SP%s since "
+			       " mapped device is already initialized.",
+			       current_sp ? "B" : "A");
+			if (done)
+				*done = 1; /* as good as doing it */
+		}
+	}
+done:
+	return err_flags;
+}
+
+/*
+* Get block request for REQ_BLOCK_PC command issued to path.  Currently
+* limited to MODE_SELECT (trespass) and INQUIRY (VPD page 0xC0) commands.
+*
+* Uses data and sense buffers in hardware handler context structure and
+* assumes serial servicing of commands, both issuance and completion.
+*/
+static struct request *get_req(struct scsi_device *sdev, int cmd)
+{
+	struct clariion_dh_data *csdev = sdev->sdev_dh_data;
+	struct request *rq;
+	unsigned char *page22;
+	int len = 0;
+
+	rq = blk_get_request(sdev->request_queue,
+			(cmd == MODE_SELECT) ? WRITE : READ, GFP_ATOMIC);
+	if (!rq) {
+		sdev_printk(KERN_INFO, sdev, "get_req: blk_get_request failed");
+		return NULL;
+	}
+
+	memset(&rq->cmd, 0, BLK_MAX_CDB);
+	rq->cmd[0] = cmd;
+	rq->cmd_len = COMMAND_SIZE(rq->cmd[0]);
+
+	switch (cmd) {
+	case MODE_SELECT:
+		if (csdev->short_trespass) {
+			page22 = csdev->hr ? short_trespass_hr : short_trespass;
+			len = sizeof(short_trespass);
+		} else {
+			page22 = csdev->hr ? long_trespass_hr : long_trespass;
+			len = sizeof(long_trespass);
+		}
+		/*
+		 * Can't DMA from kernel BSS -- must copy selected trespass
+		 * command mode page contents to context buffer which is
+		 * allocated by kmalloc.
+		 */
+		BUG_ON((len > CLARIION_BUFFER_SIZE));
+		memcpy(csdev->buffer, page22, len);
+		rq->cmd_flags |= REQ_RW;
+		rq->cmd[1] = 0x10;
+		break;
+	case INQUIRY:
+		rq->cmd[1] = 0x1;
+		rq->cmd[2] = 0xC0;
+		len = CLARIION_BUFFER_SIZE;
+		memset(csdev->buffer, 0, CLARIION_BUFFER_SIZE);
+		break;
+	default:
+		BUG_ON(1);
+		break;
+	}
+
+	rq->cmd[4] = len;
+	rq->cmd_type = REQ_TYPE_BLOCK_PC;
+	rq->cmd_flags |= REQ_FAILFAST;
+	rq->timeout = CLARIION_TIMEOUT;
+	rq->retries = CLARIION_RETRIES;
+
+	rq->sense = csdev->sense;
+	memset(rq->sense, 0, SCSI_SENSE_BUFFERSIZE);
+	rq->sense_len = 0;
+
+	if (blk_rq_map_kern(sdev->request_queue, rq, csdev->buffer,
+							len, GFP_ATOMIC)) {
+		__blk_put_request(rq->q, rq);
+		return NULL;
+	}
+
+	return rq;
+}
+
+static int send_cmd(struct scsi_device *sdev, int cmd)
+{
+	struct request *rq = get_req(sdev, cmd);
+
+	if (!rq)
+		return SCSI_DH_RES_TEMP_UNAVAIL;
+
+	return blk_execute_rq(sdev->request_queue, NULL, rq, 1);
+}
+
+static int clariion_activate(struct scsi_device *sdev)
+{
+	int result, done = 0;
+
+	result = send_cmd(sdev, INQUIRY);
+	result = sp_info_endio(sdev, result, 0, &done);
+	if (result || done)
+		goto done;
+
+	result = send_cmd(sdev, MODE_SELECT);
+	result = trespass_endio(sdev, result);
+	if (result)
+		goto done;
+
+	result = send_cmd(sdev, INQUIRY);
+	result = sp_info_endio(sdev, result, 1, NULL);
+done:
+	return result;
+}
+
+static int clariion_check_sense(struct scsi_device *sdev,
+				struct scsi_sense_hdr *sense_hdr)
+{
+	switch (sense_hdr->sense_key) {
+	case NOT_READY:
+		if (sense_hdr->asc == 0x04 && sense_hdr->ascq == 0x03)
+			/*
+			 * LUN Not Ready - Manual Intervention Required
+			 * indicates this is a passive path.
+			 *
+			 * FIXME: However, if this is seen and EVPD C0
+			 * indicates that this is due to a NDU in
+			 * progress, we should set FAIL_PATH too.
+			 * This indicates we might have to do a SCSI
+			 * inquiry in the end_io path. Ugh.
+			 *
+			 * Can return FAILED only when we want the error
+			 * recovery process to kick in.
+			 */
+			return SUCCESS;
+		break;
+	case ILLEGAL_REQUEST:
+		if (sense_hdr->asc == 0x25 && sense_hdr->ascq == 0x01)
+			/*
+			 * An array based copy is in progress. Do not
+			 * fail the path, do not bypass to another PG,
+			 * do not retry. Fail the IO immediately.
+			 * (Actually this is the same conclusion as in
+			 * the default handler, but lets make sure.)
+			 *
+			 * Can return FAILED only when we want the error
+			 * recovery process to kick in.
+			 */
+			return SUCCESS;
+		break;
+	case UNIT_ATTENTION:
+		if (sense_hdr->asc == 0x29 && sense_hdr->ascq == 0x00)
+			/*
+			 * Unit Attention Code. This is the first IO
+			 * to the new path, so just retry.
+			 */
+			return NEEDS_RETRY;
+		break;
+	}
+
+	/* success just means we do not care what scsi-ml does */
+	return SUCCESS;
+}
+
+static const struct {
+	char *vendor;
+	char *model;
+} clariion_dev_list[] = {
+	{"DGC", "RAID"},
+	{"DGC", "DISK"},
+	{NULL, NULL},
+};
+
+static int clariion_bus_notify(struct notifier_block *, unsigned long, void *);
+
+static struct scsi_device_handler clariion_dh = {
+	.name		= CLARIION_NAME,
+	.module		= THIS_MODULE,
+	.nb.notifier_call = clariion_bus_notify,
+	.check_sense	= clariion_check_sense,
+	.activate	= clariion_activate,
+};
+
+/*
+ * TODO: need some interface so we can set trespass values
+ */
+static int clariion_bus_notify(struct notifier_block *nb,
+				unsigned long action, void *data)
+{
+	struct device *dev = data;
+	struct scsi_device *sdev = to_scsi_device(dev);
+	struct clariion_dh_data *dh_data;
+	int i, found = 0;
+	unsigned long flags;
+
+	if (action == BUS_NOTIFY_ADD_DEVICE) {
+		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))) {
+				found = 1;
+				break;
+			}
+		}
+		if (!found)
+			goto out;
+
+		dh_data = kzalloc(sizeof(*dh_data), GFP_KERNEL);
+		if (!dh_data) {
+			sdev_printk(KERN_ERR, sdev, "Attach failed %s.\n",
+				    CLARIION_NAME);
+			goto out;
+		}
+
+		dh_data->default_sp = CLARIION_UNBOUND_LU;
+		dh_data->current_sp = CLARIION_UNBOUND_LU;
+
+		spin_lock_irqsave(sdev->request_queue->queue_lock, flags);
+		sdev->sdev_dh_data = dh_data;
+		sdev->sdev_dh = &clariion_dh;
+		spin_unlock_irqrestore(sdev->request_queue->queue_lock, flags);
+
+		sdev_printk(KERN_NOTICE, sdev, "Attached %s.\n", CLARIION_NAME);
+		try_module_get(THIS_MODULE);
+
+	} else if (action == BUS_NOTIFY_DEL_DEVICE) {
+		if (sdev->sdev_dh != &clariion_dh)
+			goto out;
+
+		spin_lock_irqsave(sdev->request_queue->queue_lock, flags);
+		dh_data = sdev->sdev_dh_data;
+		sdev->sdev_dh_data = NULL;
+		sdev->sdev_dh = NULL;
+		spin_unlock_irqrestore(sdev->request_queue->queue_lock, flags);
+
+		sdev_printk(KERN_NOTICE, sdev, "Dettached %s.\n",
+			    CLARIION_NAME);
+
+		kfree(dh_data);
+		module_put(THIS_MODULE);
+	}
+
+out:
+	return 0;
+}
+
+static int __init clariion_init(void)
+{
+	int r;
+
+	r = scsi_register_device_handler(&clariion_dh);
+	if (r != 0)
+		printk(KERN_ERR "Failed to register scsi device handler.");
+	return r;
+}
+
+static void __exit clariion_exit(void)
+{
+	scsi_unregister_device_handler(&clariion_dh);
+}
+
+module_init(clariion_init);
+module_exit(clariion_exit);
+
+MODULE_DESCRIPTION("EMC CX/AX/FC-family driver");
+MODULE_AUTHOR("Mike Christie <michaelc@cs.wisc.edu");
+MODULE_LICENSE("GPL");
Index: linux-2.6.25-rc2-mm1/drivers/scsi/device_handler/Makefile
===================================================================
--- linux-2.6.25-rc2-mm1.orig/drivers/scsi/device_handler/Makefile
+++ linux-2.6.25-rc2-mm1/drivers/scsi/device_handler/Makefile
@@ -4,3 +4,4 @@
 obj-$(CONFIG_SCSI_DH)		+= scsi_dh.o
 obj-$(CONFIG_SCSI_DH_RDAC)	+= scsi_dh_rdac.o
 obj-$(CONFIG_SCSI_DH_HP_SW)	+= scsi_dh_hp_sw.o
+obj-$(CONFIG_SCSI_DH_EMC)	+= scsi_dh_emc.o

-- 

----------------------------------------------------------------------
    Chandra Seetharaman               | Be careful what you choose....
              - sekharan@us.ibm.com   |      .......you may get it.
----------------------------------------------------------------------

^ permalink raw reply	[flat|nested] 17+ messages in thread

* [PATCH 4/7] scsi_dh: add EMC Clariion device handler
  2008-03-11  1:33 [PATCH 0/7] scsi_dh: Move hardware handlers from dm to SCSI Chandra Seetharaman
@ 2008-03-11  1:33 ` Chandra Seetharaman
  0 siblings, 0 replies; 17+ messages in thread
From: Chandra Seetharaman @ 2008-03-11  1:33 UTC (permalink / raw)
  To: dm-devel, linux-scsi; +Cc: andmike, michaelc, James.Bottomley, jens.axboe, agk

Subject: scsi_dh: add EMC Clariion device handler

From: Chandra Seetharaman <sekharan@us.ibm.com>

This adds support for EMC Clariions. This patch has the features that 
currently exists in mainline and advanced features from Ed's patches.

Signed-off-by: Chandra Seetharaman <sekharan@us.ibm.com>
Signed-off-by: Mike Christie <michaelc@cs.wisc.edu>
---

 drivers/scsi/device_handler/Kconfig       |    6 	6 +	0 -	0 !
 drivers/scsi/device_handler/Makefile      |    1 	1 +	0 -	0 !
 drivers/scsi/device_handler/scsi_dh_emc.c |  499 	499 +	0 -	0 !
 3 files changed, 506 insertions(+)

Index: linux-2.6.25-rc2-mm1/drivers/scsi/device_handler/Kconfig
===================================================================
--- linux-2.6.25-rc2-mm1.orig/drivers/scsi/device_handler/Kconfig
+++ linux-2.6.25-rc2-mm1/drivers/scsi/device_handler/Kconfig
@@ -24,3 +24,9 @@ config SCSI_DH_HP_SW
 	If you have a HP/COMPAQ MSA device that requires START_STOP to
 	be sent to start it and cannot upgrade the firmware then select y.
 	Otherwise, say N.
+
+config SCSI_DH_EMC
+	tristate "EMC CLARiiON Device Handler"
+	depends on SCSI_DH
+	help
+	If you have a EMC CLARiiON select y. Otherwise, say N.
Index: linux-2.6.25-rc2-mm1/drivers/scsi/device_handler/scsi_dh_emc.c
===================================================================
--- /dev/null
+++ linux-2.6.25-rc2-mm1/drivers/scsi/device_handler/scsi_dh_emc.c
@@ -0,0 +1,499 @@
+/*
+ * Target driver for EMC CLARiiON AX/CX-series hardware.
+ * Based on code from Lars Marowsky-Bree <lmb@suse.de>
+ * and Ed Goggin <egoggin@emc.com>.
+ *
+ * Copyright (C) 2006 Red Hat, Inc.  All rights reserved.
+ * Copyright (C) 2006 Mike Christie
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; see the file COPYING.  If not, write to
+ * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+#include <scsi/scsi.h>
+#include <scsi/scsi_eh.h>
+#include <scsi/scsi_dh.h>
+#include <scsi/scsi_device.h>
+
+#define CLARIION_NAME			"emc_clariion"
+
+#define CLARIION_TRESPASS_PAGE		0x22
+#define CLARIION_BUFFER_SIZE		0x80
+#define CLARIION_TIMEOUT		(60 * HZ)
+#define CLARIION_RETRIES		3
+#define CLARIION_UNBOUND_LU		-1
+
+static unsigned char long_trespass[] = {
+	0, 0, 0, 0,
+	CLARIION_TRESPASS_PAGE,	/* Page code */
+	0x09,			/* Page length - 2 */
+	0x81,			/* Trespass code + Honor reservation bit */
+	0xff, 0xff,		/* Trespass target */
+	0, 0, 0, 0, 0, 0	/* Reserved bytes / unknown */
+};
+
+static unsigned char long_trespass_hr[] = {
+	0, 0, 0, 0,
+	CLARIION_TRESPASS_PAGE,	/* Page code */
+	0x09,			/* Page length - 2 */
+	0x01,			/* Trespass code + Honor reservation bit */
+	0xff, 0xff,		/* Trespass target */
+	0, 0, 0, 0, 0, 0	/* Reserved bytes / unknown */
+};
+
+static unsigned char short_trespass[] = {
+	0, 0, 0, 0,
+	CLARIION_TRESPASS_PAGE,	/* Page code */
+	0x02,			/* Page length - 2 */
+	0x81,			/* Trespass code + Honor reservation bit */
+	0xff,			/* Trespass target */
+};
+
+static unsigned char short_trespass_hr[] = {
+	0, 0, 0, 0,
+	CLARIION_TRESPASS_PAGE,	/* Page code */
+	0x02,			/* Page length - 2 */
+	0x01,			/* Trespass code + Honor reservation bit */
+	0xff,			/* Trespass target */
+};
+
+struct clariion_dh_data {
+	/*
+	 * Use short trespass command (FC-series) or the long version
+	 * (default for AX/CX CLARiiON arrays).
+	 */
+	unsigned short_trespass;
+	/*
+	 * Whether or not (default) to honor SCSI reservations when
+	 * initiating a switch-over.
+	 */
+	unsigned hr;
+	/* I/O buffer for both MODE_SELECT and INQUIRY commands. */
+	char buffer[CLARIION_BUFFER_SIZE];
+	/*
+	 * SCSI sense buffer for commands -- assumes serial issuance
+	 * and completion sequence of all commands for same multipath.
+	 */
+	unsigned char sense[SCSI_SENSE_BUFFERSIZE];
+	/* which SP (A=0,B=1,UNBOUND=-1) is dflt SP for path's mapped dev */
+	int default_sp;
+	/* which SP (A=0,B=1,UNBOUND=-1) is active for path's mapped dev */
+	int current_sp;
+};
+
+static inline struct clariion_dh_data
+			*get_clariion_data(struct scsi_device *sdev)
+{
+	struct scsi_dh_data *scsi_dh_data = sdev->scsi_dh_data;
+	BUG_ON(scsi_dh_data == NULL);
+	return ((struct clariion_dh_data *) scsi_dh_data->buf);
+}
+
+/*
+ * Parse MODE_SELECT cmd reply.
+ */
+static int trespass_endio(struct scsi_device *sdev, int result)
+{
+	int err = SCSI_DH_OK;
+	struct scsi_sense_hdr sshdr;
+	struct clariion_dh_data *csdev = get_clariion_data(sdev);
+	char *sense = csdev->sense;
+
+	if (status_byte(result) == CHECK_CONDITION &&
+	    scsi_normalize_sense(sense, SCSI_SENSE_BUFFERSIZE, &sshdr)) {
+		sdev_printk(KERN_ERR, sdev, "Found valid sense data 0x%2x, "
+			    "0x%2x, 0x%2x while sending CLARiiON trespass "
+			    "command.\n", sshdr.sense_key, sshdr.asc,
+			     sshdr.ascq);
+
+		if ((sshdr.sense_key == 0x05) && (sshdr.asc == 0x04) &&
+		     (sshdr.ascq == 0x00)) {
+			/*
+			 * Array based copy in progress -- do not send
+			 * mode_select or copy will be aborted mid-stream.
+			 */
+			sdev_printk(KERN_INFO, sdev, "Array Based Copy in "
+				    "progress while sending CLARiiON trespass "
+				    "command.\n");
+			err = SCSI_DH_DEV_TEMP_BUSY;
+		} else if ((sshdr.sense_key == 0x02) && (sshdr.asc == 0x04) &&
+			    (sshdr.ascq == 0x03)) {
+			/*
+			 * LUN Not Ready - Manual Intervention Required
+			 * indicates in-progress ucode upgrade (NDU).
+			 */
+			sdev_printk(KERN_INFO, sdev, "Detected in-progress "
+				    "ucode upgrade NDU operation while sending "
+				    "CLARiiON trespass command.\n");
+			err = SCSI_DH_DEV_TEMP_BUSY;
+		} else
+			err = SCSI_DH_DEV_FAILED;
+	} else if (result) {
+		sdev_printk(KERN_ERR, sdev, "Error 0x%x while sending "
+			    "CLARiiON trespass command.\n", result);
+		err = SCSI_DH_IO;
+	}
+
+	return err;
+}
+
+static int parse_sp_info_reply(struct scsi_device *sdev, int result,
+		int *default_sp, int *current_sp, int *new_current_sp)
+{
+	int err = SCSI_DH_OK;
+	struct clariion_dh_data *csdev = get_clariion_data(sdev);
+
+	if (result == 0) {
+		/* check for in-progress ucode upgrade (NDU) */
+		if (csdev->buffer[48] != 0) {
+			sdev_printk(KERN_NOTICE, sdev, "Detected in-progress "
+			       "ucode upgrade NDU operation while finding "
+			       "current active SP.");
+			err = SCSI_DH_DEV_TEMP_BUSY;
+		} else {
+			*default_sp = csdev->buffer[5];
+
+			if (csdev->buffer[4] == 2)
+				/* SP for path is current */
+				*current_sp = csdev->buffer[8];
+			else {
+				if (csdev->buffer[4] == 1)
+					/* SP for this path is NOT current */
+					if (csdev->buffer[8] == 0)
+						*current_sp = 1;
+					else
+						*current_sp = 0;
+				else
+					/* unbound LU or LUNZ */
+					*current_sp = CLARIION_UNBOUND_LU;
+			}
+			*new_current_sp =  csdev->buffer[8];
+		}
+	} else {
+		struct scsi_sense_hdr sshdr;
+
+		err = SCSI_DH_IO;
+
+		if (scsi_normalize_sense(csdev->sense, SCSI_SENSE_BUFFERSIZE,
+							   &sshdr))
+			sdev_printk(KERN_ERR, sdev, "Found valid sense data "
+			      "0x%2x, 0x%2x, 0x%2x while finding current "
+			      "active SP.", sshdr.sense_key, sshdr.asc,
+			      sshdr.ascq);
+		else
+			sdev_printk(KERN_ERR, sdev, "Error 0x%x finding "
+			      "current active SP.", result);
+	}
+
+	return err;
+}
+
+static int sp_info_endio(struct scsi_device *sdev, int result,
+					int mode_select_sent, int *done)
+{
+	struct clariion_dh_data *csdev = get_clariion_data(sdev);
+	int err_flags, default_sp, current_sp, new_current_sp;
+
+	err_flags = parse_sp_info_reply(sdev, result, &default_sp,
+					     &current_sp, &new_current_sp);
+
+	if (err_flags != SCSI_DH_OK)
+		goto done;
+
+	if (mode_select_sent) {
+		csdev->default_sp = default_sp;
+		csdev->current_sp = current_sp;
+	} else {
+		/*
+		 * Issue the actual module_selec request IFF either
+		 * (1) we do not know the identity of the current SP OR
+		 * (2) what we think we know is actually correct.
+		 */
+		if ((current_sp != CLARIION_UNBOUND_LU) &&
+		    (new_current_sp != current_sp)) {
+
+			csdev->default_sp = default_sp;
+			csdev->current_sp = current_sp;
+
+			sdev_printk(KERN_INFO, sdev, "Ignoring path group "
+			       "switch-over command for CLARiiON SP%s since "
+			       " mapped device is already initialized.",
+			       current_sp ? "B" : "A");
+			if (done)
+				*done = 1; /* as good as doing it */
+		}
+	}
+done:
+	return err_flags;
+}
+
+/*
+* Get block request for REQ_BLOCK_PC command issued to path.  Currently
+* limited to MODE_SELECT (trespass) and INQUIRY (VPD page 0xC0) commands.
+*
+* Uses data and sense buffers in hardware handler context structure and
+* assumes serial servicing of commands, both issuance and completion.
+*/
+static struct request *get_req(struct scsi_device *sdev, int cmd)
+{
+	struct clariion_dh_data *csdev = get_clariion_data(sdev);
+	struct request *rq;
+	unsigned char *page22;
+	int len = 0;
+
+	rq = blk_get_request(sdev->request_queue,
+			(cmd == MODE_SELECT) ? WRITE : READ, GFP_ATOMIC);
+	if (!rq) {
+		sdev_printk(KERN_INFO, sdev, "get_req: blk_get_request failed");
+		return NULL;
+	}
+
+	memset(&rq->cmd, 0, BLK_MAX_CDB);
+	rq->cmd[0] = cmd;
+	rq->cmd_len = COMMAND_SIZE(rq->cmd[0]);
+
+	switch (cmd) {
+	case MODE_SELECT:
+		if (csdev->short_trespass) {
+			page22 = csdev->hr ? short_trespass_hr : short_trespass;
+			len = sizeof(short_trespass);
+		} else {
+			page22 = csdev->hr ? long_trespass_hr : long_trespass;
+			len = sizeof(long_trespass);
+		}
+		/*
+		 * Can't DMA from kernel BSS -- must copy selected trespass
+		 * command mode page contents to context buffer which is
+		 * allocated by kmalloc.
+		 */
+		BUG_ON((len > CLARIION_BUFFER_SIZE));
+		memcpy(csdev->buffer, page22, len);
+		rq->cmd_flags |= REQ_RW;
+		rq->cmd[1] = 0x10;
+		break;
+	case INQUIRY:
+		rq->cmd[1] = 0x1;
+		rq->cmd[2] = 0xC0;
+		len = CLARIION_BUFFER_SIZE;
+		memset(csdev->buffer, 0, CLARIION_BUFFER_SIZE);
+		break;
+	default:
+		BUG_ON(1);
+		break;
+	}
+
+	rq->cmd[4] = len;
+	rq->cmd_type = REQ_TYPE_BLOCK_PC;
+	rq->cmd_flags |= REQ_FAILFAST;
+	rq->timeout = CLARIION_TIMEOUT;
+	rq->retries = CLARIION_RETRIES;
+
+	rq->sense = csdev->sense;
+	memset(rq->sense, 0, SCSI_SENSE_BUFFERSIZE);
+	rq->sense_len = 0;
+
+	if (blk_rq_map_kern(sdev->request_queue, rq, csdev->buffer,
+							len, GFP_ATOMIC)) {
+		__blk_put_request(rq->q, rq);
+		return NULL;
+	}
+
+	return rq;
+}
+
+static int send_cmd(struct scsi_device *sdev, int cmd)
+{
+	struct request *rq = get_req(sdev, cmd);
+
+	if (!rq)
+		return SCSI_DH_RES_TEMP_UNAVAIL;
+
+	return blk_execute_rq(sdev->request_queue, NULL, rq, 1);
+}
+
+static int clariion_activate(struct scsi_device *sdev)
+{
+	int result, done = 0;
+
+	result = send_cmd(sdev, INQUIRY);
+	result = sp_info_endio(sdev, result, 0, &done);
+	if (result || done)
+		goto done;
+
+	result = send_cmd(sdev, MODE_SELECT);
+	result = trespass_endio(sdev, result);
+	if (result)
+		goto done;
+
+	result = send_cmd(sdev, INQUIRY);
+	result = sp_info_endio(sdev, result, 1, NULL);
+done:
+	return result;
+}
+
+static int clariion_check_sense(struct scsi_device *sdev,
+				struct scsi_sense_hdr *sense_hdr)
+{
+	switch (sense_hdr->sense_key) {
+	case NOT_READY:
+		if (sense_hdr->asc == 0x04 && sense_hdr->ascq == 0x03)
+			/*
+			 * LUN Not Ready - Manual Intervention Required
+			 * indicates this is a passive path.
+			 *
+			 * FIXME: However, if this is seen and EVPD C0
+			 * indicates that this is due to a NDU in
+			 * progress, we should set FAIL_PATH too.
+			 * This indicates we might have to do a SCSI
+			 * inquiry in the end_io path. Ugh.
+			 *
+			 * Can return FAILED only when we want the error
+			 * recovery process to kick in.
+			 */
+			return SUCCESS;
+		break;
+	case ILLEGAL_REQUEST:
+		if (sense_hdr->asc == 0x25 && sense_hdr->ascq == 0x01)
+			/*
+			 * An array based copy is in progress. Do not
+			 * fail the path, do not bypass to another PG,
+			 * do not retry. Fail the IO immediately.
+			 * (Actually this is the same conclusion as in
+			 * the default handler, but lets make sure.)
+			 *
+			 * Can return FAILED only when we want the error
+			 * recovery process to kick in.
+			 */
+			return SUCCESS;
+		break;
+	case UNIT_ATTENTION:
+		if (sense_hdr->asc == 0x29 && sense_hdr->ascq == 0x00)
+			/*
+			 * Unit Attention Code. This is the first IO
+			 * to the new path, so just retry.
+			 */
+			return NEEDS_RETRY;
+		break;
+	}
+
+	/* success just means we do not care what scsi-ml does */
+	return SUCCESS;
+}
+
+static const struct {
+	char *vendor;
+	char *model;
+} clariion_dev_list[] = {
+	{"DGC", "RAID"},
+	{"DGC", "DISK"},
+	{NULL, NULL},
+};
+
+static int clariion_bus_notify(struct notifier_block *, unsigned long, void *);
+
+static struct scsi_device_handler clariion_dh = {
+	.name		= CLARIION_NAME,
+	.module		= THIS_MODULE,
+	.nb.notifier_call = clariion_bus_notify,
+	.check_sense	= clariion_check_sense,
+	.activate	= clariion_activate,
+};
+
+/*
+ * TODO: need some interface so we can set trespass values
+ */
+static int clariion_bus_notify(struct notifier_block *nb,
+				unsigned long action, void *data)
+{
+	struct device *dev = data;
+	struct scsi_device *sdev = to_scsi_device(dev);
+	struct scsi_dh_data *scsi_dh_data;
+	struct clariion_dh_data *h;
+	int i, found = 0;
+	unsigned long flags;
+
+	if (action == BUS_NOTIFY_ADD_DEVICE) {
+		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))) {
+				found = 1;
+				break;
+			}
+		}
+		if (!found)
+			goto out;
+
+		scsi_dh_data = kzalloc(sizeof(struct scsi_device_handler *)
+				+ sizeof(*h) , GFP_KERNEL);
+		if (!scsi_dh_data) {
+			sdev_printk(KERN_ERR, sdev, "Attach failed %s.\n",
+				    CLARIION_NAME);
+			goto out;
+		}
+
+		scsi_dh_data->scsi_dh = &clariion_dh;
+		h = (struct clariion_dh_data *) scsi_dh_data->buf;
+		h->default_sp = CLARIION_UNBOUND_LU;
+		h->current_sp = CLARIION_UNBOUND_LU;
+
+		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, "Attached %s.\n", CLARIION_NAME);
+		try_module_get(THIS_MODULE);
+
+	} else if (action == BUS_NOTIFY_DEL_DEVICE) {
+		BUG_ON(sdev->scsi_dh_data == NULL);
+		if (sdev->scsi_dh_data->scsi_dh != &clariion_dh)
+			goto out;
+
+		spin_lock_irqsave(sdev->request_queue->queue_lock, flags);
+		scsi_dh_data = sdev->scsi_dh_data;
+		sdev->scsi_dh_data = NULL;
+		spin_unlock_irqrestore(sdev->request_queue->queue_lock, flags);
+
+		sdev_printk(KERN_NOTICE, sdev, "Dettached %s.\n",
+			    CLARIION_NAME);
+
+		kfree(scsi_dh_data);
+		module_put(THIS_MODULE);
+	}
+
+out:
+	return 0;
+}
+
+static int __init clariion_init(void)
+{
+	int r;
+
+	r = scsi_register_device_handler(&clariion_dh);
+	if (r != 0)
+		printk(KERN_ERR "Failed to register scsi device handler.");
+	return r;
+}
+
+static void __exit clariion_exit(void)
+{
+	scsi_unregister_device_handler(&clariion_dh);
+}
+
+module_init(clariion_init);
+module_exit(clariion_exit);
+
+MODULE_DESCRIPTION("EMC CX/AX/FC-family driver");
+MODULE_AUTHOR("Mike Christie <michaelc@cs.wisc.edu");
+MODULE_LICENSE("GPL");
Index: linux-2.6.25-rc2-mm1/drivers/scsi/device_handler/Makefile
===================================================================
--- linux-2.6.25-rc2-mm1.orig/drivers/scsi/device_handler/Makefile
+++ linux-2.6.25-rc2-mm1/drivers/scsi/device_handler/Makefile
@@ -4,3 +4,4 @@
 obj-$(CONFIG_SCSI_DH)		+= scsi_dh.o
 obj-$(CONFIG_SCSI_DH_RDAC)	+= scsi_dh_rdac.o
 obj-$(CONFIG_SCSI_DH_HP_SW)	+= scsi_dh_hp_sw.o
+obj-$(CONFIG_SCSI_DH_EMC)	+= scsi_dh_emc.o

-- 

----------------------------------------------------------------------
    Chandra Seetharaman               | Be careful what you choose....
              - sekharan@us.ibm.com   |      .......you may get it.
----------------------------------------------------------------------

^ permalink raw reply	[flat|nested] 17+ messages in thread

* [PATCH 4/7] scsi_dh: add EMC Clariion device handler
  2008-04-01 22:51 [PATCH 0/7] scsi_dh: Move hardware handlers from dm to SCSI Chandra Seetharaman
@ 2008-04-01 22:51 ` Chandra Seetharaman
  0 siblings, 0 replies; 17+ messages in thread
From: Chandra Seetharaman @ 2008-04-01 22:51 UTC (permalink / raw)
  To: dm-devel, linux-scsi
  Cc: andmike, michaelc, asson_ronald, James.Bottomley, Benoit_Arthur,
	jens.axboe, agk

Subject: scsi_dh: add EMC Clariion device handler

From: Chandra Seetharaman <sekharan@us.ibm.com>

This adds support for EMC Clariions. This patch has the features that 
currently exists in mainline and advanced features from Ed's patches.

Signed-off-by: Chandra Seetharaman <sekharan@us.ibm.com>
Signed-off-by: Mike Christie <michaelc@cs.wisc.edu>
---

 drivers/scsi/device_handler/Kconfig       |    6 	6 +	0 -	0 !
 drivers/scsi/device_handler/Makefile      |    1 	1 +	0 -	0 !
 drivers/scsi/device_handler/scsi_dh_emc.c |  499 	499 +	0 -	0 !
 3 files changed, 506 insertions(+)

Index: linux-2.6.25-rc2-mm1/drivers/scsi/device_handler/Kconfig
===================================================================
--- linux-2.6.25-rc2-mm1.orig/drivers/scsi/device_handler/Kconfig	2008-03-14 13:44:27.000000000 -0700
+++ linux-2.6.25-rc2-mm1/drivers/scsi/device_handler/Kconfig	2008-03-14 13:44:38.000000000 -0700
@@ -24,3 +24,9 @@
 	If you have a HP/COMPAQ MSA device that requires START_STOP to
 	be sent to start it and cannot upgrade the firmware then select y.
 	Otherwise, say N.
+
+config SCSI_DH_EMC
+	tristate "EMC CLARiiON Device Handler"
+	depends on SCSI_DH
+	help
+	If you have a EMC CLARiiON select y. Otherwise, say N.
Index: linux-2.6.25-rc2-mm1/drivers/scsi/device_handler/scsi_dh_emc.c
===================================================================
--- /dev/null	1970-01-01 00:00:00.000000000 +0000
+++ linux-2.6.25-rc2-mm1/drivers/scsi/device_handler/scsi_dh_emc.c	2008-03-14 13:45:18.000000000 -0700
@@ -0,0 +1,499 @@
+/*
+ * Target driver for EMC CLARiiON AX/CX-series hardware.
+ * Based on code from Lars Marowsky-Bree <lmb@suse.de>
+ * and Ed Goggin <egoggin@emc.com>.
+ *
+ * Copyright (C) 2006 Red Hat, Inc.  All rights reserved.
+ * Copyright (C) 2006 Mike Christie
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; see the file COPYING.  If not, write to
+ * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+#include <scsi/scsi.h>
+#include <scsi/scsi_eh.h>
+#include <scsi/scsi_dh.h>
+#include <scsi/scsi_device.h>
+
+#define CLARIION_NAME			"emc_clariion"
+
+#define CLARIION_TRESPASS_PAGE		0x22
+#define CLARIION_BUFFER_SIZE		0x80
+#define CLARIION_TIMEOUT		(60 * HZ)
+#define CLARIION_RETRIES		3
+#define CLARIION_UNBOUND_LU		-1
+
+static unsigned char long_trespass[] = {
+	0, 0, 0, 0,
+	CLARIION_TRESPASS_PAGE,	/* Page code */
+	0x09,			/* Page length - 2 */
+	0x81,			/* Trespass code + Honor reservation bit */
+	0xff, 0xff,		/* Trespass target */
+	0, 0, 0, 0, 0, 0	/* Reserved bytes / unknown */
+};
+
+static unsigned char long_trespass_hr[] = {
+	0, 0, 0, 0,
+	CLARIION_TRESPASS_PAGE,	/* Page code */
+	0x09,			/* Page length - 2 */
+	0x01,			/* Trespass code + Honor reservation bit */
+	0xff, 0xff,		/* Trespass target */
+	0, 0, 0, 0, 0, 0	/* Reserved bytes / unknown */
+};
+
+static unsigned char short_trespass[] = {
+	0, 0, 0, 0,
+	CLARIION_TRESPASS_PAGE,	/* Page code */
+	0x02,			/* Page length - 2 */
+	0x81,			/* Trespass code + Honor reservation bit */
+	0xff,			/* Trespass target */
+};
+
+static unsigned char short_trespass_hr[] = {
+	0, 0, 0, 0,
+	CLARIION_TRESPASS_PAGE,	/* Page code */
+	0x02,			/* Page length - 2 */
+	0x01,			/* Trespass code + Honor reservation bit */
+	0xff,			/* Trespass target */
+};
+
+struct clariion_dh_data {
+	/*
+	 * Use short trespass command (FC-series) or the long version
+	 * (default for AX/CX CLARiiON arrays).
+	 */
+	unsigned short_trespass;
+	/*
+	 * Whether or not (default) to honor SCSI reservations when
+	 * initiating a switch-over.
+	 */
+	unsigned hr;
+	/* I/O buffer for both MODE_SELECT and INQUIRY commands. */
+	char buffer[CLARIION_BUFFER_SIZE];
+	/*
+	 * SCSI sense buffer for commands -- assumes serial issuance
+	 * and completion sequence of all commands for same multipath.
+	 */
+	unsigned char sense[SCSI_SENSE_BUFFERSIZE];
+	/* which SP (A=0,B=1,UNBOUND=-1) is dflt SP for path's mapped dev */
+	int default_sp;
+	/* which SP (A=0,B=1,UNBOUND=-1) is active for path's mapped dev */
+	int current_sp;
+};
+
+static inline struct clariion_dh_data
+			*get_clariion_data(struct scsi_device *sdev)
+{
+	struct scsi_dh_data *scsi_dh_data = sdev->scsi_dh_data;
+	BUG_ON(scsi_dh_data == NULL);
+	return ((struct clariion_dh_data *) scsi_dh_data->buf);
+}
+
+/*
+ * Parse MODE_SELECT cmd reply.
+ */
+static int trespass_endio(struct scsi_device *sdev, int result)
+{
+	int err = SCSI_DH_OK;
+	struct scsi_sense_hdr sshdr;
+	struct clariion_dh_data *csdev = get_clariion_data(sdev);
+	char *sense = csdev->sense;
+
+	if (status_byte(result) == CHECK_CONDITION &&
+	    scsi_normalize_sense(sense, SCSI_SENSE_BUFFERSIZE, &sshdr)) {
+		sdev_printk(KERN_ERR, sdev, "Found valid sense data 0x%2x, "
+			    "0x%2x, 0x%2x while sending CLARiiON trespass "
+			    "command.\n", sshdr.sense_key, sshdr.asc,
+			     sshdr.ascq);
+
+		if ((sshdr.sense_key == 0x05) && (sshdr.asc == 0x04) &&
+		     (sshdr.ascq == 0x00)) {
+			/*
+			 * Array based copy in progress -- do not send
+			 * mode_select or copy will be aborted mid-stream.
+			 */
+			sdev_printk(KERN_INFO, sdev, "Array Based Copy in "
+				    "progress while sending CLARiiON trespass "
+				    "command.\n");
+			err = SCSI_DH_DEV_TEMP_BUSY;
+		} else if ((sshdr.sense_key == 0x02) && (sshdr.asc == 0x04) &&
+			    (sshdr.ascq == 0x03)) {
+			/*
+			 * LUN Not Ready - Manual Intervention Required
+			 * indicates in-progress ucode upgrade (NDU).
+			 */
+			sdev_printk(KERN_INFO, sdev, "Detected in-progress "
+				    "ucode upgrade NDU operation while sending "
+				    "CLARiiON trespass command.\n");
+			err = SCSI_DH_DEV_TEMP_BUSY;
+		} else
+			err = SCSI_DH_DEV_FAILED;
+	} else if (result) {
+		sdev_printk(KERN_ERR, sdev, "Error 0x%x while sending "
+			    "CLARiiON trespass command.\n", result);
+		err = SCSI_DH_IO;
+	}
+
+	return err;
+}
+
+static int parse_sp_info_reply(struct scsi_device *sdev, int result,
+		int *default_sp, int *current_sp, int *new_current_sp)
+{
+	int err = SCSI_DH_OK;
+	struct clariion_dh_data *csdev = get_clariion_data(sdev);
+
+	if (result == 0) {
+		/* check for in-progress ucode upgrade (NDU) */
+		if (csdev->buffer[48] != 0) {
+			sdev_printk(KERN_NOTICE, sdev, "Detected in-progress "
+			       "ucode upgrade NDU operation while finding "
+			       "current active SP.");
+			err = SCSI_DH_DEV_TEMP_BUSY;
+		} else {
+			*default_sp = csdev->buffer[5];
+
+			if (csdev->buffer[4] == 2)
+				/* SP for path is current */
+				*current_sp = csdev->buffer[8];
+			else {
+				if (csdev->buffer[4] == 1)
+					/* SP for this path is NOT current */
+					if (csdev->buffer[8] == 0)
+						*current_sp = 1;
+					else
+						*current_sp = 0;
+				else
+					/* unbound LU or LUNZ */
+					*current_sp = CLARIION_UNBOUND_LU;
+			}
+			*new_current_sp =  csdev->buffer[8];
+		}
+	} else {
+		struct scsi_sense_hdr sshdr;
+
+		err = SCSI_DH_IO;
+
+		if (scsi_normalize_sense(csdev->sense, SCSI_SENSE_BUFFERSIZE,
+							   &sshdr))
+			sdev_printk(KERN_ERR, sdev, "Found valid sense data "
+			      "0x%2x, 0x%2x, 0x%2x while finding current "
+			      "active SP.", sshdr.sense_key, sshdr.asc,
+			      sshdr.ascq);
+		else
+			sdev_printk(KERN_ERR, sdev, "Error 0x%x finding "
+			      "current active SP.", result);
+	}
+
+	return err;
+}
+
+static int sp_info_endio(struct scsi_device *sdev, int result,
+					int mode_select_sent, int *done)
+{
+	struct clariion_dh_data *csdev = get_clariion_data(sdev);
+	int err_flags, default_sp, current_sp, new_current_sp;
+
+	err_flags = parse_sp_info_reply(sdev, result, &default_sp,
+					     &current_sp, &new_current_sp);
+
+	if (err_flags != SCSI_DH_OK)
+		goto done;
+
+	if (mode_select_sent) {
+		csdev->default_sp = default_sp;
+		csdev->current_sp = current_sp;
+	} else {
+		/*
+		 * Issue the actual module_selec request IFF either
+		 * (1) we do not know the identity of the current SP OR
+		 * (2) what we think we know is actually correct.
+		 */
+		if ((current_sp != CLARIION_UNBOUND_LU) &&
+		    (new_current_sp != current_sp)) {
+
+			csdev->default_sp = default_sp;
+			csdev->current_sp = current_sp;
+
+			sdev_printk(KERN_INFO, sdev, "Ignoring path group "
+			       "switch-over command for CLARiiON SP%s since "
+			       " mapped device is already initialized.",
+			       current_sp ? "B" : "A");
+			if (done)
+				*done = 1; /* as good as doing it */
+		}
+	}
+done:
+	return err_flags;
+}
+
+/*
+* Get block request for REQ_BLOCK_PC command issued to path.  Currently
+* limited to MODE_SELECT (trespass) and INQUIRY (VPD page 0xC0) commands.
+*
+* Uses data and sense buffers in hardware handler context structure and
+* assumes serial servicing of commands, both issuance and completion.
+*/
+static struct request *get_req(struct scsi_device *sdev, int cmd)
+{
+	struct clariion_dh_data *csdev = get_clariion_data(sdev);
+	struct request *rq;
+	unsigned char *page22;
+	int len = 0;
+
+	rq = blk_get_request(sdev->request_queue,
+			(cmd == MODE_SELECT) ? WRITE : READ, GFP_ATOMIC);
+	if (!rq) {
+		sdev_printk(KERN_INFO, sdev, "get_req: blk_get_request failed");
+		return NULL;
+	}
+
+	memset(&rq->cmd, 0, BLK_MAX_CDB);
+	rq->cmd[0] = cmd;
+	rq->cmd_len = COMMAND_SIZE(rq->cmd[0]);
+
+	switch (cmd) {
+	case MODE_SELECT:
+		if (csdev->short_trespass) {
+			page22 = csdev->hr ? short_trespass_hr : short_trespass;
+			len = sizeof(short_trespass);
+		} else {
+			page22 = csdev->hr ? long_trespass_hr : long_trespass;
+			len = sizeof(long_trespass);
+		}
+		/*
+		 * Can't DMA from kernel BSS -- must copy selected trespass
+		 * command mode page contents to context buffer which is
+		 * allocated by kmalloc.
+		 */
+		BUG_ON((len > CLARIION_BUFFER_SIZE));
+		memcpy(csdev->buffer, page22, len);
+		rq->cmd_flags |= REQ_RW;
+		rq->cmd[1] = 0x10;
+		break;
+	case INQUIRY:
+		rq->cmd[1] = 0x1;
+		rq->cmd[2] = 0xC0;
+		len = CLARIION_BUFFER_SIZE;
+		memset(csdev->buffer, 0, CLARIION_BUFFER_SIZE);
+		break;
+	default:
+		BUG_ON(1);
+		break;
+	}
+
+	rq->cmd[4] = len;
+	rq->cmd_type = REQ_TYPE_BLOCK_PC;
+	rq->cmd_flags |= REQ_FAILFAST;
+	rq->timeout = CLARIION_TIMEOUT;
+	rq->retries = CLARIION_RETRIES;
+
+	rq->sense = csdev->sense;
+	memset(rq->sense, 0, SCSI_SENSE_BUFFERSIZE);
+	rq->sense_len = 0;
+
+	if (blk_rq_map_kern(sdev->request_queue, rq, csdev->buffer,
+							len, GFP_ATOMIC)) {
+		__blk_put_request(rq->q, rq);
+		return NULL;
+	}
+
+	return rq;
+}
+
+static int send_cmd(struct scsi_device *sdev, int cmd)
+{
+	struct request *rq = get_req(sdev, cmd);
+
+	if (!rq)
+		return SCSI_DH_RES_TEMP_UNAVAIL;
+
+	return blk_execute_rq(sdev->request_queue, NULL, rq, 1);
+}
+
+static int clariion_activate(struct scsi_device *sdev)
+{
+	int result, done = 0;
+
+	result = send_cmd(sdev, INQUIRY);
+	result = sp_info_endio(sdev, result, 0, &done);
+	if (result || done)
+		goto done;
+
+	result = send_cmd(sdev, MODE_SELECT);
+	result = trespass_endio(sdev, result);
+	if (result)
+		goto done;
+
+	result = send_cmd(sdev, INQUIRY);
+	result = sp_info_endio(sdev, result, 1, NULL);
+done:
+	return result;
+}
+
+static int clariion_check_sense(struct scsi_device *sdev,
+				struct scsi_sense_hdr *sense_hdr)
+{
+	switch (sense_hdr->sense_key) {
+	case NOT_READY:
+		if (sense_hdr->asc == 0x04 && sense_hdr->ascq == 0x03)
+			/*
+			 * LUN Not Ready - Manual Intervention Required
+			 * indicates this is a passive path.
+			 *
+			 * FIXME: However, if this is seen and EVPD C0
+			 * indicates that this is due to a NDU in
+			 * progress, we should set FAIL_PATH too.
+			 * This indicates we might have to do a SCSI
+			 * inquiry in the end_io path. Ugh.
+			 *
+			 * Can return FAILED only when we want the error
+			 * recovery process to kick in.
+			 */
+			return SUCCESS;
+		break;
+	case ILLEGAL_REQUEST:
+		if (sense_hdr->asc == 0x25 && sense_hdr->ascq == 0x01)
+			/*
+			 * An array based copy is in progress. Do not
+			 * fail the path, do not bypass to another PG,
+			 * do not retry. Fail the IO immediately.
+			 * (Actually this is the same conclusion as in
+			 * the default handler, but lets make sure.)
+			 *
+			 * Can return FAILED only when we want the error
+			 * recovery process to kick in.
+			 */
+			return SUCCESS;
+		break;
+	case UNIT_ATTENTION:
+		if (sense_hdr->asc == 0x29 && sense_hdr->ascq == 0x00)
+			/*
+			 * Unit Attention Code. This is the first IO
+			 * to the new path, so just retry.
+			 */
+			return NEEDS_RETRY;
+		break;
+	}
+
+	/* success just means we do not care what scsi-ml does */
+	return SUCCESS;
+}
+
+static const struct {
+	char *vendor;
+	char *model;
+} clariion_dev_list[] = {
+	{"DGC", "RAID"},
+	{"DGC", "DISK"},
+	{NULL, NULL},
+};
+
+static int clariion_bus_notify(struct notifier_block *, unsigned long, void *);
+
+static struct scsi_device_handler clariion_dh = {
+	.name		= CLARIION_NAME,
+	.module		= THIS_MODULE,
+	.nb.notifier_call = clariion_bus_notify,
+	.check_sense	= clariion_check_sense,
+	.activate	= clariion_activate,
+};
+
+/*
+ * TODO: need some interface so we can set trespass values
+ */
+static int clariion_bus_notify(struct notifier_block *nb,
+				unsigned long action, void *data)
+{
+	struct device *dev = data;
+	struct scsi_device *sdev = to_scsi_device(dev);
+	struct scsi_dh_data *scsi_dh_data;
+	struct clariion_dh_data *h;
+	int i, found = 0;
+	unsigned long flags;
+
+	if (action == BUS_NOTIFY_ADD_DEVICE) {
+		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))) {
+				found = 1;
+				break;
+			}
+		}
+		if (!found)
+			goto out;
+
+		scsi_dh_data = kzalloc(sizeof(struct scsi_device_handler *)
+				+ sizeof(*h) , GFP_KERNEL);
+		if (!scsi_dh_data) {
+			sdev_printk(KERN_ERR, sdev, "Attach failed %s.\n",
+				    CLARIION_NAME);
+			goto out;
+		}
+
+		scsi_dh_data->scsi_dh = &clariion_dh;
+		h = (struct clariion_dh_data *) scsi_dh_data->buf;
+		h->default_sp = CLARIION_UNBOUND_LU;
+		h->current_sp = CLARIION_UNBOUND_LU;
+
+		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, "Attached %s.\n", CLARIION_NAME);
+		try_module_get(THIS_MODULE);
+
+	} else if (action == BUS_NOTIFY_DEL_DEVICE) {
+		BUG_ON(sdev->scsi_dh_data == NULL);
+		if (sdev->scsi_dh_data->scsi_dh != &clariion_dh)
+			goto out;
+
+		spin_lock_irqsave(sdev->request_queue->queue_lock, flags);
+		scsi_dh_data = sdev->scsi_dh_data;
+		sdev->scsi_dh_data = NULL;
+		spin_unlock_irqrestore(sdev->request_queue->queue_lock, flags);
+
+		sdev_printk(KERN_NOTICE, sdev, "Dettached %s.\n",
+			    CLARIION_NAME);
+
+		kfree(scsi_dh_data);
+		module_put(THIS_MODULE);
+	}
+
+out:
+	return 0;
+}
+
+static int __init clariion_init(void)
+{
+	int r;
+
+	r = scsi_register_device_handler(&clariion_dh);
+	if (r != 0)
+		printk(KERN_ERR "Failed to register scsi device handler.");
+	return r;
+}
+
+static void __exit clariion_exit(void)
+{
+	scsi_unregister_device_handler(&clariion_dh);
+}
+
+module_init(clariion_init);
+module_exit(clariion_exit);
+
+MODULE_DESCRIPTION("EMC CX/AX/FC-family driver");
+MODULE_AUTHOR("Mike Christie <michaelc@cs.wisc.edu>, Chandra Seetharaman <sekharan@us.ibm.com>");
+MODULE_LICENSE("GPL");
Index: linux-2.6.25-rc2-mm1/drivers/scsi/device_handler/Makefile
===================================================================
--- linux-2.6.25-rc2-mm1.orig/drivers/scsi/device_handler/Makefile	2008-03-14 13:44:27.000000000 -0700
+++ linux-2.6.25-rc2-mm1/drivers/scsi/device_handler/Makefile	2008-03-14 13:44:38.000000000 -0700
@@ -4,3 +4,4 @@
 obj-$(CONFIG_SCSI_DH)		+= scsi_dh.o
 obj-$(CONFIG_SCSI_DH_RDAC)	+= scsi_dh_rdac.o
 obj-$(CONFIG_SCSI_DH_HP_SW)	+= scsi_dh_hp_sw.o
+obj-$(CONFIG_SCSI_DH_EMC)	+= scsi_dh_emc.o

-- 

----------------------------------------------------------------------
    Chandra Seetharaman               | Be careful what you choose....
              - sekharan@us.ibm.com   |      .......you may get it.
----------------------------------------------------------------------

^ permalink raw reply	[flat|nested] 17+ messages in thread

* [PATCH 4/7] scsi_dh: add EMC Clariion device handler
  2008-04-16  1:18 [PATCH 0/7] scsi_dh: Move hardware handlers from dm to SCSI Chandra Seetharaman
@ 2008-04-16  1:18 ` Chandra Seetharaman
  2008-04-16 16:29   ` Mike Christie
  0 siblings, 1 reply; 17+ messages in thread
From: Chandra Seetharaman @ 2008-04-16  1:18 UTC (permalink / raw)
  To: dm-devel, linux-scsi
  Cc: andmike, michaelc, asson_ronald, James.Bottomley, Benoit_Arthur,
	jens.axboe, agk

Subject: scsi_dh: add EMC Clariion device handler

From: Chandra Seetharaman <sekharan@us.ibm.com>

This adds support for EMC Clariions. This patch has the features that 
currently exists in mainline and advanced features from Ed's patches.

Signed-off-by: Chandra Seetharaman <sekharan@us.ibm.com>
Signed-off-by: Mike Christie <michaelc@cs.wisc.edu>
---

 drivers/scsi/device_handler/Kconfig       |    6 	6 +	0 -	0 !
 drivers/scsi/device_handler/Makefile      |    1 	1 +	0 -	0 !
 drivers/scsi/device_handler/scsi_dh_emc.c |  499 	499 +	0 -	0 !
 3 files changed, 506 insertions(+)

Index: linux-2.6.25-rc8-mm2/drivers/scsi/device_handler/Kconfig
===================================================================
--- linux-2.6.25-rc8-mm2.orig/drivers/scsi/device_handler/Kconfig
+++ linux-2.6.25-rc8-mm2/drivers/scsi/device_handler/Kconfig
@@ -24,3 +24,9 @@ config SCSI_DH_HP_SW
 	If you have a HP/COMPAQ MSA device that requires START_STOP to
 	be sent to start it and cannot upgrade the firmware then select y.
 	Otherwise, say N.
+
+config SCSI_DH_EMC
+	tristate "EMC CLARiiON Device Handler"
+	depends on SCSI_DH
+	help
+	If you have a EMC CLARiiON select y. Otherwise, say N.
Index: linux-2.6.25-rc8-mm2/drivers/scsi/device_handler/scsi_dh_emc.c
===================================================================
--- /dev/null
+++ linux-2.6.25-rc8-mm2/drivers/scsi/device_handler/scsi_dh_emc.c
@@ -0,0 +1,499 @@
+/*
+ * Target driver for EMC CLARiiON AX/CX-series hardware.
+ * Based on code from Lars Marowsky-Bree <lmb@suse.de>
+ * and Ed Goggin <egoggin@emc.com>.
+ *
+ * Copyright (C) 2006 Red Hat, Inc.  All rights reserved.
+ * Copyright (C) 2006 Mike Christie
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; see the file COPYING.  If not, write to
+ * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+#include <scsi/scsi.h>
+#include <scsi/scsi_eh.h>
+#include <scsi/scsi_dh.h>
+#include <scsi/scsi_device.h>
+
+#define CLARIION_NAME			"emc_clariion"
+
+#define CLARIION_TRESPASS_PAGE		0x22
+#define CLARIION_BUFFER_SIZE		0x80
+#define CLARIION_TIMEOUT		(60 * HZ)
+#define CLARIION_RETRIES		3
+#define CLARIION_UNBOUND_LU		-1
+
+static unsigned char long_trespass[] = {
+	0, 0, 0, 0,
+	CLARIION_TRESPASS_PAGE,	/* Page code */
+	0x09,			/* Page length - 2 */
+	0x81,			/* Trespass code + Honor reservation bit */
+	0xff, 0xff,		/* Trespass target */
+	0, 0, 0, 0, 0, 0	/* Reserved bytes / unknown */
+};
+
+static unsigned char long_trespass_hr[] = {
+	0, 0, 0, 0,
+	CLARIION_TRESPASS_PAGE,	/* Page code */
+	0x09,			/* Page length - 2 */
+	0x01,			/* Trespass code + Honor reservation bit */
+	0xff, 0xff,		/* Trespass target */
+	0, 0, 0, 0, 0, 0	/* Reserved bytes / unknown */
+};
+
+static unsigned char short_trespass[] = {
+	0, 0, 0, 0,
+	CLARIION_TRESPASS_PAGE,	/* Page code */
+	0x02,			/* Page length - 2 */
+	0x81,			/* Trespass code + Honor reservation bit */
+	0xff,			/* Trespass target */
+};
+
+static unsigned char short_trespass_hr[] = {
+	0, 0, 0, 0,
+	CLARIION_TRESPASS_PAGE,	/* Page code */
+	0x02,			/* Page length - 2 */
+	0x01,			/* Trespass code + Honor reservation bit */
+	0xff,			/* Trespass target */
+};
+
+struct clariion_dh_data {
+	/*
+	 * Use short trespass command (FC-series) or the long version
+	 * (default for AX/CX CLARiiON arrays).
+	 */
+	unsigned short_trespass;
+	/*
+	 * Whether or not (default) to honor SCSI reservations when
+	 * initiating a switch-over.
+	 */
+	unsigned hr;
+	/* I/O buffer for both MODE_SELECT and INQUIRY commands. */
+	char buffer[CLARIION_BUFFER_SIZE];
+	/*
+	 * SCSI sense buffer for commands -- assumes serial issuance
+	 * and completion sequence of all commands for same multipath.
+	 */
+	unsigned char sense[SCSI_SENSE_BUFFERSIZE];
+	/* which SP (A=0,B=1,UNBOUND=-1) is dflt SP for path's mapped dev */
+	int default_sp;
+	/* which SP (A=0,B=1,UNBOUND=-1) is active for path's mapped dev */
+	int current_sp;
+};
+
+static inline struct clariion_dh_data
+			*get_clariion_data(struct scsi_device *sdev)
+{
+	struct scsi_dh_data *scsi_dh_data = sdev->scsi_dh_data;
+	BUG_ON(scsi_dh_data == NULL);
+	return ((struct clariion_dh_data *) scsi_dh_data->buf);
+}
+
+/*
+ * Parse MODE_SELECT cmd reply.
+ */
+static int trespass_endio(struct scsi_device *sdev, int result)
+{
+	int err = SCSI_DH_OK;
+	struct scsi_sense_hdr sshdr;
+	struct clariion_dh_data *csdev = get_clariion_data(sdev);
+	char *sense = csdev->sense;
+
+	if (status_byte(result) == CHECK_CONDITION &&
+	    scsi_normalize_sense(sense, SCSI_SENSE_BUFFERSIZE, &sshdr)) {
+		sdev_printk(KERN_ERR, sdev, "Found valid sense data 0x%2x, "
+			    "0x%2x, 0x%2x while sending CLARiiON trespass "
+			    "command.\n", sshdr.sense_key, sshdr.asc,
+			     sshdr.ascq);
+
+		if ((sshdr.sense_key == 0x05) && (sshdr.asc == 0x04) &&
+		     (sshdr.ascq == 0x00)) {
+			/*
+			 * Array based copy in progress -- do not send
+			 * mode_select or copy will be aborted mid-stream.
+			 */
+			sdev_printk(KERN_INFO, sdev, "Array Based Copy in "
+				    "progress while sending CLARiiON trespass "
+				    "command.\n");
+			err = SCSI_DH_DEV_TEMP_BUSY;
+		} else if ((sshdr.sense_key == 0x02) && (sshdr.asc == 0x04) &&
+			    (sshdr.ascq == 0x03)) {
+			/*
+			 * LUN Not Ready - Manual Intervention Required
+			 * indicates in-progress ucode upgrade (NDU).
+			 */
+			sdev_printk(KERN_INFO, sdev, "Detected in-progress "
+				    "ucode upgrade NDU operation while sending "
+				    "CLARiiON trespass command.\n");
+			err = SCSI_DH_DEV_TEMP_BUSY;
+		} else
+			err = SCSI_DH_DEV_FAILED;
+	} else if (result) {
+		sdev_printk(KERN_ERR, sdev, "Error 0x%x while sending "
+			    "CLARiiON trespass command.\n", result);
+		err = SCSI_DH_IO;
+	}
+
+	return err;
+}
+
+static int parse_sp_info_reply(struct scsi_device *sdev, int result,
+		int *default_sp, int *current_sp, int *new_current_sp)
+{
+	int err = SCSI_DH_OK;
+	struct clariion_dh_data *csdev = get_clariion_data(sdev);
+
+	if (result == 0) {
+		/* check for in-progress ucode upgrade (NDU) */
+		if (csdev->buffer[48] != 0) {
+			sdev_printk(KERN_NOTICE, sdev, "Detected in-progress "
+			       "ucode upgrade NDU operation while finding "
+			       "current active SP.");
+			err = SCSI_DH_DEV_TEMP_BUSY;
+		} else {
+			*default_sp = csdev->buffer[5];
+
+			if (csdev->buffer[4] == 2)
+				/* SP for path is current */
+				*current_sp = csdev->buffer[8];
+			else {
+				if (csdev->buffer[4] == 1)
+					/* SP for this path is NOT current */
+					if (csdev->buffer[8] == 0)
+						*current_sp = 1;
+					else
+						*current_sp = 0;
+				else
+					/* unbound LU or LUNZ */
+					*current_sp = CLARIION_UNBOUND_LU;
+			}
+			*new_current_sp =  csdev->buffer[8];
+		}
+	} else {
+		struct scsi_sense_hdr sshdr;
+
+		err = SCSI_DH_IO;
+
+		if (scsi_normalize_sense(csdev->sense, SCSI_SENSE_BUFFERSIZE,
+							   &sshdr))
+			sdev_printk(KERN_ERR, sdev, "Found valid sense data "
+			      "0x%2x, 0x%2x, 0x%2x while finding current "
+			      "active SP.", sshdr.sense_key, sshdr.asc,
+			      sshdr.ascq);
+		else
+			sdev_printk(KERN_ERR, sdev, "Error 0x%x finding "
+			      "current active SP.", result);
+	}
+
+	return err;
+}
+
+static int sp_info_endio(struct scsi_device *sdev, int result,
+					int mode_select_sent, int *done)
+{
+	struct clariion_dh_data *csdev = get_clariion_data(sdev);
+	int err_flags, default_sp, current_sp, new_current_sp;
+
+	err_flags = parse_sp_info_reply(sdev, result, &default_sp,
+					     &current_sp, &new_current_sp);
+
+	if (err_flags != SCSI_DH_OK)
+		goto done;
+
+	if (mode_select_sent) {
+		csdev->default_sp = default_sp;
+		csdev->current_sp = current_sp;
+	} else {
+		/*
+		 * Issue the actual module_selec request IFF either
+		 * (1) we do not know the identity of the current SP OR
+		 * (2) what we think we know is actually correct.
+		 */
+		if ((current_sp != CLARIION_UNBOUND_LU) &&
+		    (new_current_sp != current_sp)) {
+
+			csdev->default_sp = default_sp;
+			csdev->current_sp = current_sp;
+
+			sdev_printk(KERN_INFO, sdev, "Ignoring path group "
+			       "switch-over command for CLARiiON SP%s since "
+			       " mapped device is already initialized.",
+			       current_sp ? "B" : "A");
+			if (done)
+				*done = 1; /* as good as doing it */
+		}
+	}
+done:
+	return err_flags;
+}
+
+/*
+* Get block request for REQ_BLOCK_PC command issued to path.  Currently
+* limited to MODE_SELECT (trespass) and INQUIRY (VPD page 0xC0) commands.
+*
+* Uses data and sense buffers in hardware handler context structure and
+* assumes serial servicing of commands, both issuance and completion.
+*/
+static struct request *get_req(struct scsi_device *sdev, int cmd)
+{
+	struct clariion_dh_data *csdev = get_clariion_data(sdev);
+	struct request *rq;
+	unsigned char *page22;
+	int len = 0;
+
+	rq = blk_get_request(sdev->request_queue,
+			(cmd == MODE_SELECT) ? WRITE : READ, GFP_ATOMIC);
+	if (!rq) {
+		sdev_printk(KERN_INFO, sdev, "get_req: blk_get_request failed");
+		return NULL;
+	}
+
+	memset(&rq->cmd, 0, BLK_MAX_CDB);
+	rq->cmd[0] = cmd;
+	rq->cmd_len = COMMAND_SIZE(rq->cmd[0]);
+
+	switch (cmd) {
+	case MODE_SELECT:
+		if (csdev->short_trespass) {
+			page22 = csdev->hr ? short_trespass_hr : short_trespass;
+			len = sizeof(short_trespass);
+		} else {
+			page22 = csdev->hr ? long_trespass_hr : long_trespass;
+			len = sizeof(long_trespass);
+		}
+		/*
+		 * Can't DMA from kernel BSS -- must copy selected trespass
+		 * command mode page contents to context buffer which is
+		 * allocated by kmalloc.
+		 */
+		BUG_ON((len > CLARIION_BUFFER_SIZE));
+		memcpy(csdev->buffer, page22, len);
+		rq->cmd_flags |= REQ_RW;
+		rq->cmd[1] = 0x10;
+		break;
+	case INQUIRY:
+		rq->cmd[1] = 0x1;
+		rq->cmd[2] = 0xC0;
+		len = CLARIION_BUFFER_SIZE;
+		memset(csdev->buffer, 0, CLARIION_BUFFER_SIZE);
+		break;
+	default:
+		BUG_ON(1);
+		break;
+	}
+
+	rq->cmd[4] = len;
+	rq->cmd_type = REQ_TYPE_BLOCK_PC;
+	rq->cmd_flags |= REQ_FAILFAST;
+	rq->timeout = CLARIION_TIMEOUT;
+	rq->retries = CLARIION_RETRIES;
+
+	rq->sense = csdev->sense;
+	memset(rq->sense, 0, SCSI_SENSE_BUFFERSIZE);
+	rq->sense_len = 0;
+
+	if (blk_rq_map_kern(sdev->request_queue, rq, csdev->buffer,
+							len, GFP_ATOMIC)) {
+		__blk_put_request(rq->q, rq);
+		return NULL;
+	}
+
+	return rq;
+}
+
+static int send_cmd(struct scsi_device *sdev, int cmd)
+{
+	struct request *rq = get_req(sdev, cmd);
+
+	if (!rq)
+		return SCSI_DH_RES_TEMP_UNAVAIL;
+
+	return blk_execute_rq(sdev->request_queue, NULL, rq, 1);
+}
+
+static int clariion_activate(struct scsi_device *sdev)
+{
+	int result, done = 0;
+
+	result = send_cmd(sdev, INQUIRY);
+	result = sp_info_endio(sdev, result, 0, &done);
+	if (result || done)
+		goto done;
+
+	result = send_cmd(sdev, MODE_SELECT);
+	result = trespass_endio(sdev, result);
+	if (result)
+		goto done;
+
+	result = send_cmd(sdev, INQUIRY);
+	result = sp_info_endio(sdev, result, 1, NULL);
+done:
+	return result;
+}
+
+static int clariion_check_sense(struct scsi_device *sdev,
+				struct scsi_sense_hdr *sense_hdr)
+{
+	switch (sense_hdr->sense_key) {
+	case NOT_READY:
+		if (sense_hdr->asc == 0x04 && sense_hdr->ascq == 0x03)
+			/*
+			 * LUN Not Ready - Manual Intervention Required
+			 * indicates this is a passive path.
+			 *
+			 * FIXME: However, if this is seen and EVPD C0
+			 * indicates that this is due to a NDU in
+			 * progress, we should set FAIL_PATH too.
+			 * This indicates we might have to do a SCSI
+			 * inquiry in the end_io path. Ugh.
+			 *
+			 * Can return FAILED only when we want the error
+			 * recovery process to kick in.
+			 */
+			return SUCCESS;
+		break;
+	case ILLEGAL_REQUEST:
+		if (sense_hdr->asc == 0x25 && sense_hdr->ascq == 0x01)
+			/*
+			 * An array based copy is in progress. Do not
+			 * fail the path, do not bypass to another PG,
+			 * do not retry. Fail the IO immediately.
+			 * (Actually this is the same conclusion as in
+			 * the default handler, but lets make sure.)
+			 *
+			 * Can return FAILED only when we want the error
+			 * recovery process to kick in.
+			 */
+			return SUCCESS;
+		break;
+	case UNIT_ATTENTION:
+		if (sense_hdr->asc == 0x29 && sense_hdr->ascq == 0x00)
+			/*
+			 * Unit Attention Code. This is the first IO
+			 * to the new path, so just retry.
+			 */
+			return NEEDS_RETRY;
+		break;
+	}
+
+	/* success just means we do not care what scsi-ml does */
+	return SUCCESS;
+}
+
+static const struct {
+	char *vendor;
+	char *model;
+} clariion_dev_list[] = {
+	{"DGC", "RAID"},
+	{"DGC", "DISK"},
+	{NULL, NULL},
+};
+
+static int clariion_bus_notify(struct notifier_block *, unsigned long, void *);
+
+static struct scsi_device_handler clariion_dh = {
+	.name		= CLARIION_NAME,
+	.module		= THIS_MODULE,
+	.nb.notifier_call = clariion_bus_notify,
+	.check_sense	= clariion_check_sense,
+	.activate	= clariion_activate,
+};
+
+/*
+ * TODO: need some interface so we can set trespass values
+ */
+static int clariion_bus_notify(struct notifier_block *nb,
+				unsigned long action, void *data)
+{
+	struct device *dev = data;
+	struct scsi_device *sdev = to_scsi_device(dev);
+	struct scsi_dh_data *scsi_dh_data;
+	struct clariion_dh_data *h;
+	int i, found = 0;
+	unsigned long flags;
+
+	if (action == BUS_NOTIFY_ADD_DEVICE) {
+		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))) {
+				found = 1;
+				break;
+			}
+		}
+		if (!found)
+			goto out;
+
+		scsi_dh_data = kzalloc(sizeof(struct scsi_device_handler *)
+				+ sizeof(*h) , GFP_KERNEL);
+		if (!scsi_dh_data) {
+			sdev_printk(KERN_ERR, sdev, "Attach failed %s.\n",
+				    CLARIION_NAME);
+			goto out;
+		}
+
+		scsi_dh_data->scsi_dh = &clariion_dh;
+		h = (struct clariion_dh_data *) scsi_dh_data->buf;
+		h->default_sp = CLARIION_UNBOUND_LU;
+		h->current_sp = CLARIION_UNBOUND_LU;
+
+		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, "Attached %s.\n", CLARIION_NAME);
+		try_module_get(THIS_MODULE);
+
+	} else if (action == BUS_NOTIFY_DEL_DEVICE) {
+		BUG_ON(sdev->scsi_dh_data == NULL);
+		if (sdev->scsi_dh_data->scsi_dh != &clariion_dh)
+			goto out;
+
+		spin_lock_irqsave(sdev->request_queue->queue_lock, flags);
+		scsi_dh_data = sdev->scsi_dh_data;
+		sdev->scsi_dh_data = NULL;
+		spin_unlock_irqrestore(sdev->request_queue->queue_lock, flags);
+
+		sdev_printk(KERN_NOTICE, sdev, "Dettached %s.\n",
+			    CLARIION_NAME);
+
+		kfree(scsi_dh_data);
+		module_put(THIS_MODULE);
+	}
+
+out:
+	return 0;
+}
+
+static int __init clariion_init(void)
+{
+	int r;
+
+	r = scsi_register_device_handler(&clariion_dh);
+	if (r != 0)
+		printk(KERN_ERR "Failed to register scsi device handler.");
+	return r;
+}
+
+static void __exit clariion_exit(void)
+{
+	scsi_unregister_device_handler(&clariion_dh);
+}
+
+module_init(clariion_init);
+module_exit(clariion_exit);
+
+MODULE_DESCRIPTION("EMC CX/AX/FC-family driver");
+MODULE_AUTHOR("Mike Christie <michaelc@cs.wisc.edu>, Chandra Seetharaman <sekharan@us.ibm.com>");
+MODULE_LICENSE("GPL");
Index: linux-2.6.25-rc8-mm2/drivers/scsi/device_handler/Makefile
===================================================================
--- linux-2.6.25-rc8-mm2.orig/drivers/scsi/device_handler/Makefile
+++ linux-2.6.25-rc8-mm2/drivers/scsi/device_handler/Makefile
@@ -4,3 +4,4 @@
 obj-$(CONFIG_SCSI_DH)		+= scsi_dh.o
 obj-$(CONFIG_SCSI_DH_RDAC)	+= scsi_dh_rdac.o
 obj-$(CONFIG_SCSI_DH_HP_SW)	+= scsi_dh_hp_sw.o
+obj-$(CONFIG_SCSI_DH_EMC)	+= scsi_dh_emc.o

^ permalink raw reply	[flat|nested] 17+ messages in thread

* Re: [PATCH 4/7] scsi_dh: add EMC Clariion device handler
  2008-04-16  1:18 ` [PATCH 4/7] scsi_dh: add EMC Clariion device handler Chandra Seetharaman
@ 2008-04-16 16:29   ` Mike Christie
  2008-04-16 23:59     ` [dm-devel] " Chandra Seetharaman
  0 siblings, 1 reply; 17+ messages in thread
From: Mike Christie @ 2008-04-16 16:29 UTC (permalink / raw)
  To: device-mapper development
  Cc: andmike, linux-scsi, asson_ronald, James.Bottomley, Benoit_Arthur,
	jens.axboe, agk

Chandra Seetharaman wrote:
> +
> +static int send_cmd(struct scsi_device *sdev, int cmd)
> +{
> +	struct request *rq = get_req(sdev, cmd);
> +
> +	if (!rq)
> +		return SCSI_DH_RES_TEMP_UNAVAIL;
> +
> +	return blk_execute_rq(sdev->request_queue, NULL, rq, 1);
> +}
> +

My only concerns are:

1. EMC and HP need to send a command to every device to transition them. 
Because we do blk_execute_rq from the dm multipath workqueue we can now 
only failover/failback for a couple devices at a time.

I am not sure if this is a big deal, because this the error handler path 
so it is going to be slower than the normal path. But it seems like 
customers are really picky about failover times and want them as fast as 
possible. If they have a couple hundred devices doing a couple at a time 
might be something that users see as a regression from the old code. I 
am not sure though. I just do not want the bugzillas when they come to 
work :)

2. EMC guys did you test the short vs long tress pass stuff? Do we need 
to add some code to handle the case where we send a short one, but we 
needed to send a long one. Is there some sense error or some inquiry 
data we can base this off of?

^ permalink raw reply	[flat|nested] 17+ messages in thread

* Re: [PATCH 4/7] scsi_dh: add EMC Clariion device handler
  2008-04-16 23:59     ` [dm-devel] " Chandra Seetharaman
@ 2008-04-17 17:14       ` Mike Christie
  2008-04-22 21:09         ` [dm-devel] " Chandra Seetharaman
  0 siblings, 1 reply; 17+ messages in thread
From: Mike Christie @ 2008-04-17 17:14 UTC (permalink / raw)
  To: sekharan, device-mapper development
  Cc: andmike, linux-scsi, asson_ronald, James.Bottomley, Benoit_Arthur,
	jens.axboe, agk

Chandra Seetharaman wrote:
> On Wed, 2008-04-16 at 11:29 -0500, Mike Christie wrote:
>> Chandra Seetharaman wrote:
>>> +
>>> +static int send_cmd(struct scsi_device *sdev, int cmd)
>>> +{
>>> +	struct request *rq = get_req(sdev, cmd);
>>> +
>>> +	if (!rq)
>>> +		return SCSI_DH_RES_TEMP_UNAVAIL;
>>> +
>>> +	return blk_execute_rq(sdev->request_queue, NULL, rq, 1);
>>> +}
>>> +
>> My only concerns are:
>>
>> 1. EMC and HP need to send a command to every device to transition them. 
>> Because we do blk_execute_rq from the dm multipath workqueue we can now 
>> only failover/failback for a couple devices at a time.
>>
> 
>> I am not sure if this is a big deal, because this the error handler path 
>> so it is going to be slower than the normal path. But it seems like 
> 
> Yes. But...
> 
> pg_init() due to failover/failback will be sent only when I/O is
> sent/resent to a multipath device, isn't it ? and we don't expect I/Os
> to be sent to all the devices at the same time (all the time), do we ?
> 

I am not sure what you mean by all the time, because I am talking about 
failover times above. And for failover I think I said yes in the 
previous mail. For EMC we are currently sending failover commands to all 
the devices at the same time, because EMC does not do the controller 
failover RDAC does.

> So, as you pointed, is it a big deal ? :)
> 

In the previous mail I specifically said users might care, because they 
are picky about failover times, so the answer is to your question is 
what I said before, maybe :) I said I am not sure, because I do not have 
any numbers for the failover times.

> BTW, As you know, it was originally coded that way(patchset posted on
> Jan 23, 2008) and later changed as per James comments (through you) that
> the code was overusing blk_execute_rq_nowait().
>  

Yes, I know, and as you know I did not agree with James. The reason I 
bring it up again is that Ed is not doing dm-multipath stuff, so EMC 
does not have a good reviewer right now, and I want to make sure these 
issues are raised on the list during the review so we can all discuss 
them together.

If James thinks it is a big enough problem he can offer some of this 
alternatives. If not then he will merge it and we can see if people even 
notice and handle it later. I just want to make sure we all know what is 
going on, because Alasdair is not a scsi guy. James does not know all 
the fun details of every box. And the EMC guys are not up to speed on 
linux. I am just worried we are going to get a bad review.

^ permalink raw reply	[flat|nested] 17+ messages in thread

* [PATCH 4/7] scsi_dh: add EMC Clariion device handler
  2008-04-17 21:18 [PATCH 0/7] scsi_dh: Move hardware handlers from dm to SCSI Chandra Seetharaman
@ 2008-04-17 21:19 ` Chandra Seetharaman
  0 siblings, 0 replies; 17+ messages in thread
From: Chandra Seetharaman @ 2008-04-17 21:19 UTC (permalink / raw)
  To: dm-devel, linux-scsi
  Cc: andmike, michaelc, asson_ronald, James.Bottomley, Benoit_Arthur,
	jens.axboe, agk

Subject: scsi_dh: add EMC Clariion device handler

From: Chandra Seetharaman <sekharan@us.ibm.com>

This adds support for EMC Clariions. This patch has the features that 
currently exists in mainline and advanced features from Ed's patches.

Signed-off-by: Chandra Seetharaman <sekharan@us.ibm.com>
Signed-off-by: Mike Christie <michaelc@cs.wisc.edu>
---

 drivers/scsi/device_handler/Kconfig       |    6 	6 +	0 -	0 !
 drivers/scsi/device_handler/Makefile      |    1 	1 +	0 -	0 !
 drivers/scsi/device_handler/scsi_dh_emc.c |  499 	499 +	0 -	0 !
 3 files changed, 506 insertions(+)

Index: linux-2.6.25-rc8-mm2/drivers/scsi/device_handler/Kconfig
===================================================================
--- linux-2.6.25-rc8-mm2.orig/drivers/scsi/device_handler/Kconfig
+++ linux-2.6.25-rc8-mm2/drivers/scsi/device_handler/Kconfig
@@ -24,3 +24,9 @@ config SCSI_DH_HP_SW
 	If you have a HP/COMPAQ MSA device that requires START_STOP to
 	be sent to start it and cannot upgrade the firmware then select y.
 	Otherwise, say N.
+
+config SCSI_DH_EMC
+	tristate "EMC CLARiiON Device Handler"
+	depends on SCSI_DH
+	help
+	If you have a EMC CLARiiON select y. Otherwise, say N.
Index: linux-2.6.25-rc8-mm2/drivers/scsi/device_handler/scsi_dh_emc.c
===================================================================
--- /dev/null
+++ linux-2.6.25-rc8-mm2/drivers/scsi/device_handler/scsi_dh_emc.c
@@ -0,0 +1,499 @@
+/*
+ * Target driver for EMC CLARiiON AX/CX-series hardware.
+ * Based on code from Lars Marowsky-Bree <lmb@suse.de>
+ * and Ed Goggin <egoggin@emc.com>.
+ *
+ * Copyright (C) 2006 Red Hat, Inc.  All rights reserved.
+ * Copyright (C) 2006 Mike Christie
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; see the file COPYING.  If not, write to
+ * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+#include <scsi/scsi.h>
+#include <scsi/scsi_eh.h>
+#include <scsi/scsi_dh.h>
+#include <scsi/scsi_device.h>
+
+#define CLARIION_NAME			"emc_clariion"
+
+#define CLARIION_TRESPASS_PAGE		0x22
+#define CLARIION_BUFFER_SIZE		0x80
+#define CLARIION_TIMEOUT		(60 * HZ)
+#define CLARIION_RETRIES		3
+#define CLARIION_UNBOUND_LU		-1
+
+static unsigned char long_trespass[] = {
+	0, 0, 0, 0,
+	CLARIION_TRESPASS_PAGE,	/* Page code */
+	0x09,			/* Page length - 2 */
+	0x81,			/* Trespass code + Honor reservation bit */
+	0xff, 0xff,		/* Trespass target */
+	0, 0, 0, 0, 0, 0	/* Reserved bytes / unknown */
+};
+
+static unsigned char long_trespass_hr[] = {
+	0, 0, 0, 0,
+	CLARIION_TRESPASS_PAGE,	/* Page code */
+	0x09,			/* Page length - 2 */
+	0x01,			/* Trespass code + Honor reservation bit */
+	0xff, 0xff,		/* Trespass target */
+	0, 0, 0, 0, 0, 0	/* Reserved bytes / unknown */
+};
+
+static unsigned char short_trespass[] = {
+	0, 0, 0, 0,
+	CLARIION_TRESPASS_PAGE,	/* Page code */
+	0x02,			/* Page length - 2 */
+	0x81,			/* Trespass code + Honor reservation bit */
+	0xff,			/* Trespass target */
+};
+
+static unsigned char short_trespass_hr[] = {
+	0, 0, 0, 0,
+	CLARIION_TRESPASS_PAGE,	/* Page code */
+	0x02,			/* Page length - 2 */
+	0x01,			/* Trespass code + Honor reservation bit */
+	0xff,			/* Trespass target */
+};
+
+struct clariion_dh_data {
+	/*
+	 * Use short trespass command (FC-series) or the long version
+	 * (default for AX/CX CLARiiON arrays).
+	 */
+	unsigned short_trespass;
+	/*
+	 * Whether or not (default) to honor SCSI reservations when
+	 * initiating a switch-over.
+	 */
+	unsigned hr;
+	/* I/O buffer for both MODE_SELECT and INQUIRY commands. */
+	char buffer[CLARIION_BUFFER_SIZE];
+	/*
+	 * SCSI sense buffer for commands -- assumes serial issuance
+	 * and completion sequence of all commands for same multipath.
+	 */
+	unsigned char sense[SCSI_SENSE_BUFFERSIZE];
+	/* which SP (A=0,B=1,UNBOUND=-1) is dflt SP for path's mapped dev */
+	int default_sp;
+	/* which SP (A=0,B=1,UNBOUND=-1) is active for path's mapped dev */
+	int current_sp;
+};
+
+static inline struct clariion_dh_data
+			*get_clariion_data(struct scsi_device *sdev)
+{
+	struct scsi_dh_data *scsi_dh_data = sdev->scsi_dh_data;
+	BUG_ON(scsi_dh_data == NULL);
+	return ((struct clariion_dh_data *) scsi_dh_data->buf);
+}
+
+/*
+ * Parse MODE_SELECT cmd reply.
+ */
+static int trespass_endio(struct scsi_device *sdev, int result)
+{
+	int err = SCSI_DH_OK;
+	struct scsi_sense_hdr sshdr;
+	struct clariion_dh_data *csdev = get_clariion_data(sdev);
+	char *sense = csdev->sense;
+
+	if (status_byte(result) == CHECK_CONDITION &&
+	    scsi_normalize_sense(sense, SCSI_SENSE_BUFFERSIZE, &sshdr)) {
+		sdev_printk(KERN_ERR, sdev, "Found valid sense data 0x%2x, "
+			    "0x%2x, 0x%2x while sending CLARiiON trespass "
+			    "command.\n", sshdr.sense_key, sshdr.asc,
+			     sshdr.ascq);
+
+		if ((sshdr.sense_key == 0x05) && (sshdr.asc == 0x04) &&
+		     (sshdr.ascq == 0x00)) {
+			/*
+			 * Array based copy in progress -- do not send
+			 * mode_select or copy will be aborted mid-stream.
+			 */
+			sdev_printk(KERN_INFO, sdev, "Array Based Copy in "
+				    "progress while sending CLARiiON trespass "
+				    "command.\n");
+			err = SCSI_DH_DEV_TEMP_BUSY;
+		} else if ((sshdr.sense_key == 0x02) && (sshdr.asc == 0x04) &&
+			    (sshdr.ascq == 0x03)) {
+			/*
+			 * LUN Not Ready - Manual Intervention Required
+			 * indicates in-progress ucode upgrade (NDU).
+			 */
+			sdev_printk(KERN_INFO, sdev, "Detected in-progress "
+				    "ucode upgrade NDU operation while sending "
+				    "CLARiiON trespass command.\n");
+			err = SCSI_DH_DEV_TEMP_BUSY;
+		} else
+			err = SCSI_DH_DEV_FAILED;
+	} else if (result) {
+		sdev_printk(KERN_ERR, sdev, "Error 0x%x while sending "
+			    "CLARiiON trespass command.\n", result);
+		err = SCSI_DH_IO;
+	}
+
+	return err;
+}
+
+static int parse_sp_info_reply(struct scsi_device *sdev, int result,
+		int *default_sp, int *current_sp, int *new_current_sp)
+{
+	int err = SCSI_DH_OK;
+	struct clariion_dh_data *csdev = get_clariion_data(sdev);
+
+	if (result == 0) {
+		/* check for in-progress ucode upgrade (NDU) */
+		if (csdev->buffer[48] != 0) {
+			sdev_printk(KERN_NOTICE, sdev, "Detected in-progress "
+			       "ucode upgrade NDU operation while finding "
+			       "current active SP.");
+			err = SCSI_DH_DEV_TEMP_BUSY;
+		} else {
+			*default_sp = csdev->buffer[5];
+
+			if (csdev->buffer[4] == 2)
+				/* SP for path is current */
+				*current_sp = csdev->buffer[8];
+			else {
+				if (csdev->buffer[4] == 1)
+					/* SP for this path is NOT current */
+					if (csdev->buffer[8] == 0)
+						*current_sp = 1;
+					else
+						*current_sp = 0;
+				else
+					/* unbound LU or LUNZ */
+					*current_sp = CLARIION_UNBOUND_LU;
+			}
+			*new_current_sp =  csdev->buffer[8];
+		}
+	} else {
+		struct scsi_sense_hdr sshdr;
+
+		err = SCSI_DH_IO;
+
+		if (scsi_normalize_sense(csdev->sense, SCSI_SENSE_BUFFERSIZE,
+							   &sshdr))
+			sdev_printk(KERN_ERR, sdev, "Found valid sense data "
+			      "0x%2x, 0x%2x, 0x%2x while finding current "
+			      "active SP.", sshdr.sense_key, sshdr.asc,
+			      sshdr.ascq);
+		else
+			sdev_printk(KERN_ERR, sdev, "Error 0x%x finding "
+			      "current active SP.", result);
+	}
+
+	return err;
+}
+
+static int sp_info_endio(struct scsi_device *sdev, int result,
+					int mode_select_sent, int *done)
+{
+	struct clariion_dh_data *csdev = get_clariion_data(sdev);
+	int err_flags, default_sp, current_sp, new_current_sp;
+
+	err_flags = parse_sp_info_reply(sdev, result, &default_sp,
+					     &current_sp, &new_current_sp);
+
+	if (err_flags != SCSI_DH_OK)
+		goto done;
+
+	if (mode_select_sent) {
+		csdev->default_sp = default_sp;
+		csdev->current_sp = current_sp;
+	} else {
+		/*
+		 * Issue the actual module_selec request IFF either
+		 * (1) we do not know the identity of the current SP OR
+		 * (2) what we think we know is actually correct.
+		 */
+		if ((current_sp != CLARIION_UNBOUND_LU) &&
+		    (new_current_sp != current_sp)) {
+
+			csdev->default_sp = default_sp;
+			csdev->current_sp = current_sp;
+
+			sdev_printk(KERN_INFO, sdev, "Ignoring path group "
+			       "switch-over command for CLARiiON SP%s since "
+			       " mapped device is already initialized.",
+			       current_sp ? "B" : "A");
+			if (done)
+				*done = 1; /* as good as doing it */
+		}
+	}
+done:
+	return err_flags;
+}
+
+/*
+* Get block request for REQ_BLOCK_PC command issued to path.  Currently
+* limited to MODE_SELECT (trespass) and INQUIRY (VPD page 0xC0) commands.
+*
+* Uses data and sense buffers in hardware handler context structure and
+* assumes serial servicing of commands, both issuance and completion.
+*/
+static struct request *get_req(struct scsi_device *sdev, int cmd)
+{
+	struct clariion_dh_data *csdev = get_clariion_data(sdev);
+	struct request *rq;
+	unsigned char *page22;
+	int len = 0;
+
+	rq = blk_get_request(sdev->request_queue,
+			(cmd == MODE_SELECT) ? WRITE : READ, GFP_ATOMIC);
+	if (!rq) {
+		sdev_printk(KERN_INFO, sdev, "get_req: blk_get_request failed");
+		return NULL;
+	}
+
+	memset(&rq->cmd, 0, BLK_MAX_CDB);
+	rq->cmd[0] = cmd;
+	rq->cmd_len = COMMAND_SIZE(rq->cmd[0]);
+
+	switch (cmd) {
+	case MODE_SELECT:
+		if (csdev->short_trespass) {
+			page22 = csdev->hr ? short_trespass_hr : short_trespass;
+			len = sizeof(short_trespass);
+		} else {
+			page22 = csdev->hr ? long_trespass_hr : long_trespass;
+			len = sizeof(long_trespass);
+		}
+		/*
+		 * Can't DMA from kernel BSS -- must copy selected trespass
+		 * command mode page contents to context buffer which is
+		 * allocated by kmalloc.
+		 */
+		BUG_ON((len > CLARIION_BUFFER_SIZE));
+		memcpy(csdev->buffer, page22, len);
+		rq->cmd_flags |= REQ_RW;
+		rq->cmd[1] = 0x10;
+		break;
+	case INQUIRY:
+		rq->cmd[1] = 0x1;
+		rq->cmd[2] = 0xC0;
+		len = CLARIION_BUFFER_SIZE;
+		memset(csdev->buffer, 0, CLARIION_BUFFER_SIZE);
+		break;
+	default:
+		BUG_ON(1);
+		break;
+	}
+
+	rq->cmd[4] = len;
+	rq->cmd_type = REQ_TYPE_BLOCK_PC;
+	rq->cmd_flags |= REQ_FAILFAST;
+	rq->timeout = CLARIION_TIMEOUT;
+	rq->retries = CLARIION_RETRIES;
+
+	rq->sense = csdev->sense;
+	memset(rq->sense, 0, SCSI_SENSE_BUFFERSIZE);
+	rq->sense_len = 0;
+
+	if (blk_rq_map_kern(sdev->request_queue, rq, csdev->buffer,
+							len, GFP_ATOMIC)) {
+		__blk_put_request(rq->q, rq);
+		return NULL;
+	}
+
+	return rq;
+}
+
+static int send_cmd(struct scsi_device *sdev, int cmd)
+{
+	struct request *rq = get_req(sdev, cmd);
+
+	if (!rq)
+		return SCSI_DH_RES_TEMP_UNAVAIL;
+
+	return blk_execute_rq(sdev->request_queue, NULL, rq, 1);
+}
+
+static int clariion_activate(struct scsi_device *sdev)
+{
+	int result, done = 0;
+
+	result = send_cmd(sdev, INQUIRY);
+	result = sp_info_endio(sdev, result, 0, &done);
+	if (result || done)
+		goto done;
+
+	result = send_cmd(sdev, MODE_SELECT);
+	result = trespass_endio(sdev, result);
+	if (result)
+		goto done;
+
+	result = send_cmd(sdev, INQUIRY);
+	result = sp_info_endio(sdev, result, 1, NULL);
+done:
+	return result;
+}
+
+static int clariion_check_sense(struct scsi_device *sdev,
+				struct scsi_sense_hdr *sense_hdr)
+{
+	switch (sense_hdr->sense_key) {
+	case NOT_READY:
+		if (sense_hdr->asc == 0x04 && sense_hdr->ascq == 0x03)
+			/*
+			 * LUN Not Ready - Manual Intervention Required
+			 * indicates this is a passive path.
+			 *
+			 * FIXME: However, if this is seen and EVPD C0
+			 * indicates that this is due to a NDU in
+			 * progress, we should set FAIL_PATH too.
+			 * This indicates we might have to do a SCSI
+			 * inquiry in the end_io path. Ugh.
+			 *
+			 * Can return FAILED only when we want the error
+			 * recovery process to kick in.
+			 */
+			return SUCCESS;
+		break;
+	case ILLEGAL_REQUEST:
+		if (sense_hdr->asc == 0x25 && sense_hdr->ascq == 0x01)
+			/*
+			 * An array based copy is in progress. Do not
+			 * fail the path, do not bypass to another PG,
+			 * do not retry. Fail the IO immediately.
+			 * (Actually this is the same conclusion as in
+			 * the default handler, but lets make sure.)
+			 *
+			 * Can return FAILED only when we want the error
+			 * recovery process to kick in.
+			 */
+			return SUCCESS;
+		break;
+	case UNIT_ATTENTION:
+		if (sense_hdr->asc == 0x29 && sense_hdr->ascq == 0x00)
+			/*
+			 * Unit Attention Code. This is the first IO
+			 * to the new path, so just retry.
+			 */
+			return NEEDS_RETRY;
+		break;
+	}
+
+	/* success just means we do not care what scsi-ml does */
+	return SUCCESS;
+}
+
+static const struct {
+	char *vendor;
+	char *model;
+} clariion_dev_list[] = {
+	{"DGC", "RAID"},
+	{"DGC", "DISK"},
+	{NULL, NULL},
+};
+
+static int clariion_bus_notify(struct notifier_block *, unsigned long, void *);
+
+static struct scsi_device_handler clariion_dh = {
+	.name		= CLARIION_NAME,
+	.module		= THIS_MODULE,
+	.nb.notifier_call = clariion_bus_notify,
+	.check_sense	= clariion_check_sense,
+	.activate	= clariion_activate,
+};
+
+/*
+ * TODO: need some interface so we can set trespass values
+ */
+static int clariion_bus_notify(struct notifier_block *nb,
+				unsigned long action, void *data)
+{
+	struct device *dev = data;
+	struct scsi_device *sdev = to_scsi_device(dev);
+	struct scsi_dh_data *scsi_dh_data;
+	struct clariion_dh_data *h;
+	int i, found = 0;
+	unsigned long flags;
+
+	if (action == BUS_NOTIFY_ADD_DEVICE) {
+		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))) {
+				found = 1;
+				break;
+			}
+		}
+		if (!found)
+			goto out;
+
+		scsi_dh_data = kzalloc(sizeof(struct scsi_device_handler *)
+				+ sizeof(*h) , GFP_KERNEL);
+		if (!scsi_dh_data) {
+			sdev_printk(KERN_ERR, sdev, "Attach failed %s.\n",
+				    CLARIION_NAME);
+			goto out;
+		}
+
+		scsi_dh_data->scsi_dh = &clariion_dh;
+		h = (struct clariion_dh_data *) scsi_dh_data->buf;
+		h->default_sp = CLARIION_UNBOUND_LU;
+		h->current_sp = CLARIION_UNBOUND_LU;
+
+		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, "Attached %s.\n", CLARIION_NAME);
+		try_module_get(THIS_MODULE);
+
+	} else if (action == BUS_NOTIFY_DEL_DEVICE) {
+		BUG_ON(sdev->scsi_dh_data == NULL);
+		if (sdev->scsi_dh_data->scsi_dh != &clariion_dh)
+			goto out;
+
+		spin_lock_irqsave(sdev->request_queue->queue_lock, flags);
+		scsi_dh_data = sdev->scsi_dh_data;
+		sdev->scsi_dh_data = NULL;
+		spin_unlock_irqrestore(sdev->request_queue->queue_lock, flags);
+
+		sdev_printk(KERN_NOTICE, sdev, "Dettached %s.\n",
+			    CLARIION_NAME);
+
+		kfree(scsi_dh_data);
+		module_put(THIS_MODULE);
+	}
+
+out:
+	return 0;
+}
+
+static int __init clariion_init(void)
+{
+	int r;
+
+	r = scsi_register_device_handler(&clariion_dh);
+	if (r != 0)
+		printk(KERN_ERR "Failed to register scsi device handler.");
+	return r;
+}
+
+static void __exit clariion_exit(void)
+{
+	scsi_unregister_device_handler(&clariion_dh);
+}
+
+module_init(clariion_init);
+module_exit(clariion_exit);
+
+MODULE_DESCRIPTION("EMC CX/AX/FC-family driver");
+MODULE_AUTHOR("Mike Christie <michaelc@cs.wisc.edu>, Chandra Seetharaman <sekharan@us.ibm.com>");
+MODULE_LICENSE("GPL");
Index: linux-2.6.25-rc8-mm2/drivers/scsi/device_handler/Makefile
===================================================================
--- linux-2.6.25-rc8-mm2.orig/drivers/scsi/device_handler/Makefile
+++ linux-2.6.25-rc8-mm2/drivers/scsi/device_handler/Makefile
@@ -4,3 +4,4 @@
 obj-$(CONFIG_SCSI_DH)		+= scsi_dh.o
 obj-$(CONFIG_SCSI_DH_RDAC)	+= scsi_dh_rdac.o
 obj-$(CONFIG_SCSI_DH_HP_SW)	+= scsi_dh_hp_sw.o
+obj-$(CONFIG_SCSI_DH_EMC)	+= scsi_dh_emc.o

^ permalink raw reply	[flat|nested] 17+ messages in thread

* [PATCH 0/7] scsi_dh: Move hardware handlers from dm to SCSI
@ 2008-04-17 22:22 Chandra Seetharaman
  2008-04-17 22:22 ` [PATCH 1/7] scsi_dh: add skeleton for SCSI Device Handlers Chandra Seetharaman
                   ` (6 more replies)
  0 siblings, 7 replies; 17+ messages in thread
From: Chandra Seetharaman @ 2008-04-17 22:22 UTC (permalink / raw)
  To: linux-scsi
  Cc: andmike, michaelc, asson_ronald, James.Bottomley, dm-devel,
	Benoit_Arthur, jens.axboe, agk

NOTE: This is a resend (again) of the patchset sent on Apr 15 as it did not
reach linux-scsi mailing list

Hello,

This is the same set of patches that was posted on Apr 01:
http://marc.info/?l=dm-devel&m=120716401627550&w=2

Only change is that these patches are created on top of 2.6.25-rc8-mm2.

Testing has been done with the following storage devices:
 - IBM DS4800 storage device for the lsi_rdac hardware handler
 - HP storage device for the hp_sw hardware handler.
   Thanks to Dave(dwysocha@redhat.com)
 - EMC storage device for the EMC hardware handler.
   Thanks to Arthur(Benoit_Arthur@emc.com) and Ronald(asson_ronald@emc.com)
In effect all hardware handlers are tested.

These patches need an ACK from Alasdair(agk@redhat.com).

Thanks,

chandra

^ permalink raw reply	[flat|nested] 17+ messages in thread

* [PATCH 1/7] scsi_dh: add skeleton for SCSI Device Handlers
  2008-04-17 22:22 [PATCH 0/7] scsi_dh: Move hardware handlers from dm to SCSI Chandra Seetharaman
@ 2008-04-17 22:22 ` Chandra Seetharaman
  2008-04-17 22:22 ` [PATCH 2/7] scsi_dh: add lsi rdac device handler Chandra Seetharaman
                   ` (5 subsequent siblings)
  6 siblings, 0 replies; 17+ messages in thread
From: Chandra Seetharaman @ 2008-04-17 22:22 UTC (permalink / raw)
  To: linux-scsi
  Cc: dm-devel, andmike, michaelc, agk, James.Bottomley, jens.axboe,
	dwysocha, Benoit_Arthur, asson_ronald, Chandra Seetharaman

Subject: scsi_dh: add skeleton for SCSI Device Handlers

From: Chandra Seetharaman <sekharan@us.ibm.com>

Add base support to the SCSI subsystem for SCSI device handlers.

Signed-off-by: Chandra Seetharaman <sekharan@us.ibm.com>
Signed-off-by: Mike Anderson <andmike@linux.vnet.ibm.com>
Signed-off-by: Mike Christie <michaelc@cs.wisc.edu>
---

 drivers/scsi/Kconfig                  |    2 	2 +	0 -	0 !
 drivers/scsi/Makefile                 |    1 	1 +	0 -	0 !
 drivers/scsi/device_handler/Kconfig   |   12 	12 +	0 -	0 !
 drivers/scsi/device_handler/Makefile  |    4 	4 +	0 -	0 !
 drivers/scsi/device_handler/scsi_dh.c |  151 	151 +	0 -	0 !
 drivers/scsi/scsi_error.c             |   11 	11 +	0 -	0 !
 drivers/scsi/scsi_lib.c               |    8 	8 +	0 -	0 !
 drivers/scsi/scsi_sysfs.c             |    1 	1 +	0 -	0 !
 include/scsi/scsi.h                   |    1 	1 +	0 -	0 !
 include/scsi/scsi_device.h            |   22 	22 +	0 -	0 !
 include/scsi/scsi_dh.h                |   58 	58 +	0 -	0 !
 11 files changed, 271 insertions(+)

--- linux-2.6.25-rc8-mm2.orig/drivers/scsi/Kconfig
+++ linux-2.6.25-rc8-mm2/drivers/scsi/Kconfig
@@ -1761,4 +1761,6 @@ endif # SCSI_LOWLEVEL
 
 source "drivers/scsi/pcmcia/Kconfig"
 
+source "drivers/scsi/device_handler/Kconfig"
+
 endmenu
--- linux-2.6.25-rc8-mm2.orig/drivers/scsi/Makefile
+++ linux-2.6.25-rc8-mm2/drivers/scsi/Makefile
@@ -34,6 +34,7 @@ obj-$(CONFIG_SCSI_ISCSI_ATTRS)	+= scsi_t
 obj-$(CONFIG_SCSI_SAS_ATTRS)	+= scsi_transport_sas.o
 obj-$(CONFIG_SCSI_SAS_LIBSAS)	+= libsas/
 obj-$(CONFIG_SCSI_SRP_ATTRS)	+= scsi_transport_srp.o
+obj-$(CONFIG_SCSI_DH)		+= device_handler/
 
 obj-$(CONFIG_ISCSI_TCP) 	+= libiscsi.o	iscsi_tcp.o
 obj-$(CONFIG_INFINIBAND_ISER) 	+= libiscsi.o
--- /dev/null
+++ linux-2.6.25-rc8-mm2/drivers/scsi/device_handler/Kconfig
@@ -0,0 +1,12 @@
+#
+# SCSI Device Handler configuration
+#
+
+menuconfig SCSI_DH
+	tristate "SCSI Device Handlers"
+	depends on SCSI
+	default n
+	help
+	  SCSI Device Handlers provide device specific support for
+	  devices utilized in multipath configurations. Say Y here to
+	  select support for specific hardware.
--- /dev/null
+++ linux-2.6.25-rc8-mm2/drivers/scsi/device_handler/Makefile
@@ -0,0 +1,4 @@
+#
+# SCSI Device Handler
+#
+obj-$(CONFIG_SCSI_DH)		+= scsi_dh.o
--- linux-2.6.25-rc8-mm2.orig/drivers/scsi/scsi_error.c
+++ linux-2.6.25-rc8-mm2/drivers/scsi/scsi_error.c
@@ -298,6 +298,7 @@ static inline void scsi_eh_prt_fail_stat
  */
 static int scsi_check_sense(struct scsi_cmnd *scmd)
 {
+	struct scsi_device *sdev = scmd->device;
 	struct scsi_sense_hdr sshdr;
 
 	if (! scsi_command_normalize_sense(scmd, &sshdr))
@@ -306,6 +307,16 @@ static int scsi_check_sense(struct scsi_
 	if (scsi_sense_is_deferred(&sshdr))
 		return NEEDS_RETRY;
 
+	if (sdev->scsi_dh_data && sdev->scsi_dh_data->scsi_dh &&
+			sdev->scsi_dh_data->scsi_dh->check_sense) {
+		int rc;
+
+		rc = sdev->scsi_dh_data->scsi_dh->check_sense(sdev, &sshdr);
+		if (rc != SCSI_RETURN_NOT_HANDLED)
+			return rc;
+		/* handler does not care. Drop down to default handling */
+	}
+
 	/*
 	 * Previous logic looked for FILEMARK, EOM or ILI which are
 	 * mainly associated with tapes and returned SUCCESS.
--- linux-2.6.25-rc8-mm2.orig/include/scsi/scsi_device.h
+++ linux-2.6.25-rc8-mm2/include/scsi/scsi_device.h
@@ -161,9 +161,29 @@ struct scsi_device {
 
 	struct execute_work	ew; /* used to get process context on put */
 
+	struct scsi_dh_data	*scsi_dh_data;
 	enum scsi_device_state sdev_state;
 	unsigned long		sdev_data[0];
 } __attribute__((aligned(sizeof(unsigned long))));
+
+struct scsi_device_handler {
+	/* Used by the infrastructure */
+	struct list_head list; /* list of scsi_device_handlers */
+	struct notifier_block nb;
+
+	/* Filled by the hardware handler */
+	struct module *module;
+	const char *name;
+	int (*check_sense)(struct scsi_device *, struct scsi_sense_hdr *);
+	int (*activate)(struct scsi_device *);
+	int (*prep_fn)(struct scsi_device *, struct request *);
+};
+
+struct scsi_dh_data {
+	struct scsi_device_handler *scsi_dh;
+	char buf[0];
+};
+
 #define	to_scsi_device(d)	\
 	container_of(d, struct scsi_device, sdev_gendev)
 #define	class_to_sdev(d)	\
@@ -229,7 +249,9 @@ extern struct scsi_device *__scsi_add_de
 		uint, uint, uint, void *hostdata);
 extern int scsi_add_device(struct Scsi_Host *host, uint channel,
 			   uint target, uint lun);
+extern int scsi_register_device_handler(struct scsi_device_handler *scsi_dh);
 extern void scsi_remove_device(struct scsi_device *);
+extern int scsi_unregister_device_handler(struct scsi_device_handler *scsi_dh);
 
 extern int scsi_device_get(struct scsi_device *);
 extern void scsi_device_put(struct scsi_device *);
--- /dev/null
+++ linux-2.6.25-rc8-mm2/drivers/scsi/device_handler/scsi_dh.c
@@ -0,0 +1,151 @@
+/*
+ * SCSI device handler infrastruture.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * Copyright IBM Corporation, 2007
+ *      Authors:
+ *               Chandra Seetharaman <sekharan@us.ibm.com>
+ *               Mike Anderson <andmike@linux.vnet.ibm.com>
+ */
+
+#include <scsi/scsi_dh.h>
+#include "../scsi_priv.h"
+
+static DEFINE_SPINLOCK(list_lock);
+static LIST_HEAD(scsi_dh_list);
+
+static struct scsi_device_handler *get_device_handler(const char *name)
+{
+	struct scsi_device_handler *tmp, *found = NULL;
+
+	spin_lock(&list_lock);
+	list_for_each_entry(tmp, &scsi_dh_list, list) {
+		if (!strcmp(tmp->name, name)) {
+			found = tmp;
+			break;
+		}
+	}
+	spin_unlock(&list_lock);
+	return found;
+}
+
+static int scsi_dh_notifier_add(struct device *dev, void *data)
+{
+	struct scsi_device_handler *scsi_dh = data;
+
+	scsi_dh->nb.notifier_call(&scsi_dh->nb, BUS_NOTIFY_ADD_DEVICE, dev);
+	return 0;
+}
+
+/*
+ * scsi_register_device_handler - register a device handler personality
+ *      module.
+ * @scsi_dh - device handler to be registered.
+ *
+ * Returns 0 on success, -EBUSY if handler already registered.
+ */
+int scsi_register_device_handler(struct scsi_device_handler *scsi_dh)
+{
+	int ret = -EBUSY;
+	struct scsi_device_handler *tmp;
+
+	tmp = get_device_handler(scsi_dh->name);
+	if (tmp)
+		goto done;
+
+	ret = bus_register_notifier(&scsi_bus_type, &scsi_dh->nb);
+
+	bus_for_each_dev(&scsi_bus_type, NULL, scsi_dh, scsi_dh_notifier_add);
+	spin_lock(&list_lock);
+	list_add(&scsi_dh->list, &scsi_dh_list);
+	spin_unlock(&list_lock);
+
+done:
+	return ret;
+}
+EXPORT_SYMBOL_GPL(scsi_register_device_handler);
+
+static int scsi_dh_notifier_remove(struct device *dev, void *data)
+{
+	struct scsi_device_handler *scsi_dh = data;
+
+	scsi_dh->nb.notifier_call(&scsi_dh->nb, BUS_NOTIFY_DEL_DEVICE, dev);
+	return 0;
+}
+
+/*
+ * scsi_unregister_device_handler - register a device handler personality
+ *      module.
+ * @scsi_dh - device handler to be unregistered.
+ *
+ * Returns 0 on success, -ENODEV if handler not registered.
+ */
+int scsi_unregister_device_handler(struct scsi_device_handler *scsi_dh)
+{
+	int ret = -ENODEV;
+	struct scsi_device_handler *tmp;
+
+	tmp = get_device_handler(scsi_dh->name);
+	if (!tmp)
+		goto done;
+
+	ret = bus_unregister_notifier(&scsi_bus_type, &scsi_dh->nb);
+
+	bus_for_each_dev(&scsi_bus_type, NULL, scsi_dh,
+					scsi_dh_notifier_remove);
+	spin_lock(&list_lock);
+	list_del(&scsi_dh->list);
+	spin_unlock(&list_lock);
+
+done:
+	return ret;
+}
+EXPORT_SYMBOL_GPL(scsi_unregister_device_handler);
+
+/*
+ * scsi_dh_activate - activate the path associated with the scsi_device
+ *      corresponding to the given request queue.
+ * @q - Request queue that is associated with the scsi_device to be
+ *      activated.
+ */
+int scsi_dh_activate(struct request_queue *q)
+{
+	int err = 0;
+	unsigned long flags;
+	struct scsi_device *sdev;
+	struct scsi_device_handler *scsi_dh = NULL;
+
+	spin_lock_irqsave(q->queue_lock, flags);
+	sdev = q->queuedata;
+	if (sdev && sdev->scsi_dh_data)
+		scsi_dh = sdev->scsi_dh_data->scsi_dh;
+	if (!scsi_dh || !get_device(&sdev->sdev_gendev))
+		err = SCSI_DH_NOSYS;
+	spin_unlock_irqrestore(q->queue_lock, flags);
+
+	if (err)
+		return err;
+
+	if (scsi_dh->activate)
+		err = scsi_dh->activate(sdev);
+	put_device(&sdev->sdev_gendev);
+	return err;
+}
+EXPORT_SYMBOL_GPL(scsi_dh_activate);
+
+MODULE_DESCRIPTION("SCSI device handler");
+MODULE_AUTHOR("Chandra Seetharaman <sekharan@us.ibm.com>");
+MODULE_LICENSE("GPL");
--- /dev/null
+++ linux-2.6.25-rc8-mm2/include/scsi/scsi_dh.h
@@ -0,0 +1,58 @@
+/*
+ * Header file for SCSI device handler infrastruture.
+ *
+ * Modified version of patches posted by Mike Christie <michaelc@cs.wisc.edu>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * Copyright IBM Corporation, 2007
+ *      Authors:
+ *               Chandra Seetharaman <sekharan@us.ibm.com>
+ *               Mike Anderson <andmike@linux.vnet.ibm.com>
+ */
+
+#include <scsi/scsi_device.h>
+
+enum {
+	SCSI_DH_OK = 0,
+	/*
+	 * device errors
+	 */
+	SCSI_DH_DEV_FAILED,	/* generic device error */
+	SCSI_DH_DEV_TEMP_BUSY,
+	SCSI_DH_DEVICE_MAX,	/* max device blkerr definition */
+
+	/*
+	 * transport errors
+	 */
+	SCSI_DH_NOTCONN = SCSI_DH_DEVICE_MAX + 1,
+	SCSI_DH_CONN_FAILURE,
+	SCSI_DH_TRANSPORT_MAX,	/* max transport blkerr definition */
+
+	/*
+	 * driver and generic errors
+	 */
+	SCSI_DH_IO = SCSI_DH_TRANSPORT_MAX + 1,	/* generic error */
+	SCSI_DH_INVALID_IO,
+	SCSI_DH_RETRY,		/* retry the req, but not immediately */
+	SCSI_DH_IMM_RETRY,	/* immediately retry the req */
+	SCSI_DH_TIMED_OUT,
+	SCSI_DH_RES_TEMP_UNAVAIL,
+	SCSI_DH_DEV_OFFLINED,
+	SCSI_DH_NOSYS,
+	SCSI_DH_DRIVER_MAX,
+};
+
+extern int scsi_dh_activate(struct request_queue *);
--- linux-2.6.25-rc8-mm2.orig/drivers/scsi/scsi_lib.c
+++ linux-2.6.25-rc8-mm2/drivers/scsi/scsi_lib.c
@@ -1156,6 +1156,14 @@ int scsi_setup_fs_cmnd(struct scsi_devic
 
 	if (ret != BLKPREP_OK)
 		return ret;
+
+	if (unlikely(sdev->scsi_dh_data && sdev->scsi_dh_data->scsi_dh
+			 && sdev->scsi_dh_data->scsi_dh->prep_fn)) {
+		ret = sdev->scsi_dh_data->scsi_dh->prep_fn(sdev, req);
+		if (ret != BLKPREP_OK)
+			return ret;
+	}
+
 	/*
 	 * Filesystem requests must transfer data.
 	 */
--- linux-2.6.25-rc8-mm2.orig/include/scsi/scsi.h
+++ linux-2.6.25-rc8-mm2/include/scsi/scsi.h
@@ -374,6 +374,7 @@ struct scsi_lun {
 #define SOFT_ERROR      0x2005
 #define ADD_TO_MLQUEUE  0x2006
 #define TIMEOUT_ERROR   0x2007
+#define SCSI_RETURN_NOT_HANDLED   0x2008
 
 /*
  * Midlevel queue return values.
--- linux-2.6.25-rc8-mm2.orig/drivers/scsi/scsi_sysfs.c
+++ linux-2.6.25-rc8-mm2/drivers/scsi/scsi_sysfs.c
@@ -406,6 +406,7 @@ struct bus_type scsi_bus_type = {
 	.resume		= scsi_bus_resume,
 	.remove		= scsi_bus_remove,
 };
+EXPORT_SYMBOL_GPL(scsi_bus_type);
 
 int scsi_sysfs_register(void)
 {

^ permalink raw reply	[flat|nested] 17+ messages in thread

* [PATCH 2/7] scsi_dh: add lsi rdac device handler
  2008-04-17 22:22 [PATCH 0/7] scsi_dh: Move hardware handlers from dm to SCSI Chandra Seetharaman
  2008-04-17 22:22 ` [PATCH 1/7] scsi_dh: add skeleton for SCSI Device Handlers Chandra Seetharaman
@ 2008-04-17 22:22 ` Chandra Seetharaman
  2008-04-17 22:23 ` [PATCH 3/7] scsi_dh: add hp sw " Chandra Seetharaman
                   ` (4 subsequent siblings)
  6 siblings, 0 replies; 17+ messages in thread
From: Chandra Seetharaman @ 2008-04-17 22:22 UTC (permalink / raw)
  To: linux-scsi
  Cc: dm-devel, andmike, michaelc, agk, James.Bottomley, jens.axboe,
	dwysocha, Benoit_Arthur, asson_ronald, Chandra Seetharaman

Subject: scsi_dh: add lsi rdac device handler

From: Chandra Seetharaman <sekharan@us.ibm.com>

add LSI RDAC SCSI device handler

Signed-off-by: Chandra Seetharaman <sekharan@us.ibm.com>
---

 drivers/scsi/device_handler/Kconfig        |    6 	6 +	0 -	0 !
 drivers/scsi/device_handler/Makefile       |    1 	1 +	0 -	0 !
 drivers/scsi/device_handler/scsi_dh_rdac.c |  678 	678 +	0 -	0 !
 3 files changed, 685 insertions(+)

Index: linux-2.6.25-rc8-mm2/drivers/scsi/device_handler/Kconfig
===================================================================
--- linux-2.6.25-rc8-mm2.orig/drivers/scsi/device_handler/Kconfig
+++ linux-2.6.25-rc8-mm2/drivers/scsi/device_handler/Kconfig
@@ -10,3 +10,9 @@ menuconfig SCSI_DH
 	  SCSI Device Handlers provide device specific support for
 	  devices utilized in multipath configurations. Say Y here to
 	  select support for specific hardware.
+
+config SCSI_DH_RDAC
+	tristate "LSI RDAC Device Handler"
+	depends on SCSI_DH
+	help
+	If you have a LSI RDAC select y. Otherwise, say N.
Index: linux-2.6.25-rc8-mm2/drivers/scsi/device_handler/scsi_dh_rdac.c
===================================================================
--- /dev/null
+++ linux-2.6.25-rc8-mm2/drivers/scsi/device_handler/scsi_dh_rdac.c
@@ -0,0 +1,678 @@
+/*
+ * Engenio/LSI RDAC SCSI Device Handler
+ *
+ * Copyright (C) 2005 Mike Christie. All rights reserved.
+ * Copyright (C) Chandra Seetharaman, IBM Corp. 2007
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ */
+#include <scsi/scsi.h>
+#include <scsi/scsi_eh.h>
+#include <scsi/scsi_dh.h>
+
+#define RDAC_NAME "rdac"
+
+/*
+ * LSI mode page stuff
+ *
+ * These struct definitions and the forming of the
+ * mode page were taken from the LSI RDAC 2.4 GPL'd
+ * driver, and then converted to Linux conventions.
+ */
+#define RDAC_QUIESCENCE_TIME 20;
+/*
+ * Page Codes
+ */
+#define RDAC_PAGE_CODE_REDUNDANT_CONTROLLER 0x2c
+
+/*
+ * Controller modes definitions
+ */
+#define RDAC_MODE_TRANSFER_SPECIFIED_LUNS	0x02
+
+/*
+ * RDAC Options field
+ */
+#define RDAC_FORCED_QUIESENCE 0x02
+
+#define RDAC_TIMEOUT	(60 * HZ)
+#define RDAC_RETRIES	3
+
+struct rdac_mode_6_hdr {
+	u8	data_len;
+	u8	medium_type;
+	u8	device_params;
+	u8	block_desc_len;
+};
+
+struct rdac_mode_10_hdr {
+	u16	data_len;
+	u8	medium_type;
+	u8	device_params;
+	u16	reserved;
+	u16	block_desc_len;
+};
+
+struct rdac_mode_common {
+	u8	controller_serial[16];
+	u8	alt_controller_serial[16];
+	u8	rdac_mode[2];
+	u8	alt_rdac_mode[2];
+	u8	quiescence_timeout;
+	u8	rdac_options;
+};
+
+struct rdac_pg_legacy {
+	struct rdac_mode_6_hdr hdr;
+	u8	page_code;
+	u8	page_len;
+	struct rdac_mode_common common;
+#define MODE6_MAX_LUN	32
+	u8	lun_table[MODE6_MAX_LUN];
+	u8	reserved2[32];
+	u8	reserved3;
+	u8	reserved4;
+};
+
+struct rdac_pg_expanded {
+	struct rdac_mode_10_hdr hdr;
+	u8	page_code;
+	u8	subpage_code;
+	u8	page_len[2];
+	struct rdac_mode_common common;
+	u8	lun_table[256];
+	u8	reserved3;
+	u8	reserved4;
+};
+
+struct c9_inquiry {
+	u8	peripheral_info;
+	u8	page_code;	/* 0xC9 */
+	u8	reserved1;
+	u8	page_len;
+	u8	page_id[4];	/* "vace" */
+	u8	avte_cvp;
+	u8	path_prio;
+	u8	reserved2[38];
+};
+
+#define SUBSYS_ID_LEN	16
+#define SLOT_ID_LEN	2
+
+struct c4_inquiry {
+	u8	peripheral_info;
+	u8	page_code;	/* 0xC4 */
+	u8	reserved1;
+	u8	page_len;
+	u8	page_id[4];	/* "subs" */
+	u8	subsys_id[SUBSYS_ID_LEN];
+	u8	revision[4];
+	u8	slot_id[SLOT_ID_LEN];
+	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;
+};
+struct c8_inquiry {
+	u8	peripheral_info;
+	u8	page_code; /* 0xC8 */
+	u8	reserved1;
+	u8	page_len;
+	u8	page_id[4]; /* "edid" */
+	u8	reserved2[3];
+	u8	vol_uniq_id_len;
+	u8	vol_uniq_id[16];
+	u8	vol_user_label_len;
+	u8	vol_user_label[60];
+	u8	array_uniq_id_len;
+	u8	array_unique_id[16];
+	u8	array_user_label_len;
+	u8	array_user_label[60];
+	u8	lun[8];
+};
+
+struct c2_inquiry {
+	u8	peripheral_info;
+	u8	page_code;	/* 0xC2 */
+	u8	reserved1;
+	u8	page_len;
+	u8	page_id[4];	/* "swr4" */
+	u8	sw_version[3];
+	u8	sw_date[3];
+	u8	features_enabled;
+	u8	max_lun_supported;
+	u8	partitions[239]; /* Total allocation length should be 0xFF */
+};
+
+struct rdac_dh_data {
+	struct rdac_controller	*ctlr;
+#define UNINITIALIZED_LUN	(1 << 8)
+	unsigned		lun;
+#define RDAC_STATE_ACTIVE	0
+#define RDAC_STATE_PASSIVE	1
+	unsigned char		state;
+	unsigned char		sense[SCSI_SENSE_BUFFERSIZE];
+	union			{
+		struct c2_inquiry c2;
+		struct c4_inquiry c4;
+		struct c8_inquiry c8;
+		struct c9_inquiry c9;
+	} inq;
+};
+
+static LIST_HEAD(ctlr_list);
+static DEFINE_SPINLOCK(list_lock);
+
+static inline struct rdac_dh_data *get_rdac_data(struct scsi_device *sdev)
+{
+	struct scsi_dh_data *scsi_dh_data = sdev->scsi_dh_data;
+	BUG_ON(scsi_dh_data == NULL);
+	return ((struct rdac_dh_data *) scsi_dh_data->buf);
+}
+
+static struct request *get_rdac_req(struct scsi_device *sdev,
+			void *buffer, unsigned buflen, int rw)
+{
+	struct request *rq;
+	struct request_queue *q = sdev->request_queue;
+	struct rdac_dh_data *h = get_rdac_data(sdev);
+
+	rq = blk_get_request(q, rw, GFP_KERNEL);
+
+	if (!rq) {
+		sdev_printk(KERN_INFO, sdev,
+				"get_rdac_req: blk_get_request failed.\n");
+		return NULL;
+	}
+
+	if (buflen && blk_rq_map_kern(q, rq, buffer, buflen, GFP_KERNEL)) {
+		blk_put_request(rq);
+		sdev_printk(KERN_INFO, sdev,
+				"get_rdac_req: blk_rq_map_kern failed.\n");
+		return NULL;
+	}
+
+	memset(&rq->cmd, 0, BLK_MAX_CDB);
+	rq->sense = h->sense;
+	memset(rq->sense, 0, SCSI_SENSE_BUFFERSIZE);
+	rq->sense_len = 0;
+
+	rq->cmd_type = REQ_TYPE_BLOCK_PC;
+	rq->cmd_flags |= REQ_FAILFAST | REQ_NOMERGE;
+	rq->retries = RDAC_RETRIES;
+	rq->timeout = RDAC_TIMEOUT;
+
+	return rq;
+}
+
+static struct request *rdac_failover_get(struct scsi_device *sdev)
+{
+	struct request *rq;
+	struct rdac_mode_common *common;
+	unsigned data_size;
+	struct rdac_dh_data *h = get_rdac_data(sdev);
+
+	if (h->ctlr->use_ms10) {
+		struct rdac_pg_expanded *rdac_pg;
+
+		data_size = sizeof(struct rdac_pg_expanded);
+		rdac_pg = &h->ctlr->mode_select.expanded;
+		memset(rdac_pg, 0, data_size);
+		common = &rdac_pg->common;
+		rdac_pg->page_code = RDAC_PAGE_CODE_REDUNDANT_CONTROLLER + 0x40;
+		rdac_pg->subpage_code = 0x1;
+		rdac_pg->page_len[0] = 0x01;
+		rdac_pg->page_len[1] = 0x28;
+		rdac_pg->lun_table[h->lun] = 0x81;
+	} else {
+		struct rdac_pg_legacy *rdac_pg;
+
+		data_size = sizeof(struct rdac_pg_legacy);
+		rdac_pg = &h->ctlr->mode_select.legacy;
+		memset(rdac_pg, 0, data_size);
+		common = &rdac_pg->common;
+		rdac_pg->page_code = RDAC_PAGE_CODE_REDUNDANT_CONTROLLER;
+		rdac_pg->page_len = 0x68;
+		rdac_pg->lun_table[h->lun] = 0x81;
+	}
+	common->rdac_mode[1] = RDAC_MODE_TRANSFER_SPECIFIED_LUNS;
+	common->quiescence_timeout = RDAC_QUIESCENCE_TIME;
+	common->rdac_options = RDAC_FORCED_QUIESENCE;
+
+	/* get request for block layer packet command */
+	rq = get_rdac_req(sdev, &h->ctlr->mode_select, data_size, WRITE);
+	if (!rq)
+		return NULL;
+
+	/* Prepare the command. */
+	if (h->ctlr->use_ms10) {
+		rq->cmd[0] = MODE_SELECT_10;
+		rq->cmd[7] = data_size >> 8;
+		rq->cmd[8] = data_size & 0xff;
+	} else {
+		rq->cmd[0] = MODE_SELECT;
+		rq->cmd[4] = data_size;
+	}
+	rq->cmd_len = COMMAND_SIZE(rq->cmd[0]);
+
+	return rq;
+}
+
+static void release_controller(struct kref *kref)
+{
+	struct rdac_controller *ctlr;
+	ctlr = container_of(kref, struct rdac_controller, kref);
+
+	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)
+{
+	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)) {
+			kref_get(&tmp->kref);
+			spin_unlock(&list_lock);
+			return tmp;
+		}
+	}
+	ctlr = kmalloc(sizeof(*ctlr), GFP_ATOMIC);
+	if (!ctlr)
+		goto done;
+
+	/* initialize fields of controller */
+	memcpy(ctlr->subsys_id, subsys_id, SUBSYS_ID_LEN);
+	memcpy(ctlr->slot_id, slot_id, SLOT_ID_LEN);
+	kref_init(&ctlr->kref);
+	ctlr->use_ms10 = -1;
+	list_add(&ctlr->node, &ctlr_list);
+done:
+	spin_unlock(&list_lock);
+	return ctlr;
+}
+
+static int submit_inquiry(struct scsi_device *sdev, int page_code,
+		unsigned int len)
+{
+	struct request *rq;
+	struct request_queue *q = sdev->request_queue;
+	struct rdac_dh_data *h = get_rdac_data(sdev);
+	int err = SCSI_DH_RES_TEMP_UNAVAIL;
+
+	rq = get_rdac_req(sdev, &h->inq, len, READ);
+	if (!rq)
+		goto done;
+
+	/* Prepare the command. */
+	rq->cmd[0] = INQUIRY;
+	rq->cmd[1] = 1;
+	rq->cmd[2] = page_code;
+	rq->cmd[4] = len;
+	rq->cmd_len = COMMAND_SIZE(INQUIRY);
+	err = blk_execute_rq(q, NULL, rq, 1);
+	if (err == -EIO)
+		err = SCSI_DH_IO;
+done:
+	return err;
+}
+
+static int get_lun(struct scsi_device *sdev)
+{
+	int err;
+	struct c8_inquiry *inqp;
+	struct rdac_dh_data *h = get_rdac_data(sdev);
+
+	err = submit_inquiry(sdev, 0xC8, sizeof(struct c8_inquiry));
+	if (err == SCSI_DH_OK) {
+		inqp = &h->inq.c8;
+		h->lun = inqp->lun[7]; /* currently it uses only one byte */
+	}
+	return err;
+}
+
+#define RDAC_OWNED	0
+#define RDAC_UNOWNED	1
+#define RDAC_FAILED	2
+static int check_ownership(struct scsi_device *sdev)
+{
+	int err;
+	struct c9_inquiry *inqp;
+	struct rdac_dh_data *h = get_rdac_data(sdev);
+
+	err = submit_inquiry(sdev, 0xC9, sizeof(struct c9_inquiry));
+	if (err == SCSI_DH_OK) {
+		err = RDAC_UNOWNED;
+		inqp = &h->inq.c9;
+		/*
+		 * If in AVT mode or if the path already owns the LUN,
+		 * return RDAC_OWNED;
+		 */
+		if (((inqp->avte_cvp >> 7) == 0x1) ||
+				 ((inqp->avte_cvp & 0x1) != 0))
+			err = RDAC_OWNED;
+	} else
+		err = RDAC_FAILED;
+	return err;
+}
+
+static int initialize_controller(struct scsi_device *sdev)
+{
+	int err;
+	struct c4_inquiry *inqp;
+	struct rdac_dh_data *h = get_rdac_data(sdev);
+
+	err = submit_inquiry(sdev, 0xC4, sizeof(struct c4_inquiry));
+	if (err == SCSI_DH_OK) {
+		inqp = &h->inq.c4;
+		h->ctlr = get_controller(inqp->subsys_id, inqp->slot_id);
+		if (!h->ctlr)
+			err = SCSI_DH_RES_TEMP_UNAVAIL;
+	}
+	return err;
+}
+
+static int set_mode_select(struct scsi_device *sdev)
+{
+	int err;
+	struct c2_inquiry *inqp;
+	struct rdac_dh_data *h = get_rdac_data(sdev);
+
+	err = submit_inquiry(sdev, 0xC2, sizeof(struct c2_inquiry));
+	if (err == SCSI_DH_OK) {
+		inqp = &h->inq.c2;
+		/*
+		 * If more than MODE6_MAX_LUN luns are supported, use
+		 * mode select 10
+		 */
+		if (inqp->max_lun_supported >= MODE6_MAX_LUN)
+			h->ctlr->use_ms10 = 1;
+		else
+			h->ctlr->use_ms10 = 0;
+	}
+	return err;
+}
+
+static int mode_select_handle_sense(struct scsi_device *sdev)
+{
+	struct scsi_sense_hdr sense_hdr;
+	struct rdac_dh_data *h = get_rdac_data(sdev);
+	int sense, err = SCSI_DH_IO, ret;
+
+	ret = scsi_normalize_sense(h->sense, SCSI_SENSE_BUFFERSIZE, &sense_hdr);
+	if (!ret)
+		goto done;
+
+	err = SCSI_DH_OK;
+	sense = (sense_hdr.sense_key << 16) | (sense_hdr.asc << 8) |
+			sense_hdr.ascq;
+	/* If it is retryable failure, submit the c9 inquiry again */
+	if (sense == 0x59136 || sense == 0x68b02 || sense == 0xb8b02 ||
+			    sense == 0x62900) {
+		/* 0x59136    - Command lock contention
+		 * 0x[6b]8b02 - Quiesense in progress or achieved
+		 * 0x62900    - Power On, Reset, or Bus Device Reset
+		 */
+		err = SCSI_DH_DEV_TEMP_BUSY;
+	}
+
+	if (sense)
+		sdev_printk(KERN_INFO, sdev,
+			"MODE_SELECT failed with sense 0x%x.\n", sense);
+done:
+	return err;
+}
+
+static int send_mode_select(struct scsi_device *sdev)
+{
+	struct request *rq;
+	struct request_queue *q = sdev->request_queue;
+	struct rdac_dh_data *h = get_rdac_data(sdev);
+	int err = SCSI_DH_RES_TEMP_UNAVAIL;
+
+	rq = rdac_failover_get(sdev);
+	if (!rq)
+		goto done;
+
+	sdev_printk(KERN_INFO, sdev, "queueing MODE_SELECT command.\n");
+
+	err = blk_execute_rq(q, NULL, rq, 1);
+	if (err != SCSI_DH_OK)
+		err = mode_select_handle_sense(sdev);
+	if (err == SCSI_DH_OK)
+		h->state = RDAC_STATE_ACTIVE;
+done:
+	return err;
+}
+
+static int rdac_activate(struct scsi_device *sdev)
+{
+	struct rdac_dh_data *h = get_rdac_data(sdev);
+	int err = SCSI_DH_OK;
+
+	if (h->lun == UNINITIALIZED_LUN) {
+		err = get_lun(sdev);
+		if (err != SCSI_DH_OK)
+			goto done;
+	}
+
+	err = check_ownership(sdev);
+	switch (err) {
+	case RDAC_UNOWNED:
+		break;
+	case RDAC_OWNED:
+		err = SCSI_DH_OK;
+		goto done;
+	case RDAC_FAILED:
+	default:
+		err = SCSI_DH_IO;
+		goto done;
+	}
+
+	if (!h->ctlr) {
+		err = initialize_controller(sdev);
+		if (err != SCSI_DH_OK)
+			goto done;
+	}
+
+	if (h->ctlr->use_ms10 == -1) {
+		err = set_mode_select(sdev);
+		if (err != SCSI_DH_OK)
+			goto done;
+	}
+
+	err = send_mode_select(sdev);
+done:
+	return err;
+}
+
+static int rdac_prep_fn(struct scsi_device *sdev, struct request *req)
+{
+	struct rdac_dh_data *h = get_rdac_data(sdev);
+	int ret = BLKPREP_OK;
+
+	if (h->state != RDAC_STATE_ACTIVE) {
+		ret = BLKPREP_KILL;
+		req->cmd_flags |= REQ_QUIET;
+	}
+	return ret;
+
+}
+
+static int rdac_check_sense(struct scsi_device *sdev,
+				struct scsi_sense_hdr *sense_hdr)
+{
+	struct rdac_dh_data *h = get_rdac_data(sdev);
+	switch (sense_hdr->sense_key) {
+	case NOT_READY:
+		if (sense_hdr->asc == 0x04 && sense_hdr->ascq == 0x81)
+			/* LUN Not Ready - Storage firmware incompatible
+			 * Manual code synchonisation required.
+			 *
+			 * Nothing we can do here. Try to bypass the path.
+			 */
+			return SUCCESS;
+		if (sense_hdr->asc == 0x04 && sense_hdr->ascq == 0xA1)
+			/* LUN Not Ready - Quiescense in progress
+			 *
+			 * Just retry and wait.
+			 */
+			return NEEDS_RETRY;
+		break;
+	case ILLEGAL_REQUEST:
+		if (sense_hdr->asc == 0x94 && sense_hdr->ascq == 0x01) {
+			/* Invalid Request - Current Logical Unit Ownership.
+			 * Controller is not the current owner of the LUN,
+			 * Fail the path, so that the other path be used.
+			 */
+			h->state = RDAC_STATE_PASSIVE;
+			return SUCCESS;
+		}
+		break;
+	case UNIT_ATTENTION:
+		if (sense_hdr->asc == 0x29 && sense_hdr->ascq == 0x00)
+			/*
+			 * Power On, Reset, or Bus Device Reset, just retry.
+			 */
+			return NEEDS_RETRY;
+		break;
+	}
+	/* success just means we do not care what scsi-ml does */
+	return SCSI_RETURN_NOT_HANDLED;
+}
+
+static const struct {
+	char *vendor;
+	char *model;
+} rdac_dev_list[] = {
+	{"IBM", "1815"},
+	{NULL, NULL},
+};
+
+static int rdac_bus_notify(struct notifier_block *, unsigned long, void *);
+
+static struct scsi_device_handler rdac_dh = {
+	.name = RDAC_NAME,
+	.module = THIS_MODULE,
+	.nb.notifier_call = rdac_bus_notify,
+	.prep_fn = rdac_prep_fn,
+	.check_sense = rdac_check_sense,
+	.activate = rdac_activate,
+};
+
+/*
+ * TODO: need some interface so we can set trespass values
+ */
+static int rdac_bus_notify(struct notifier_block *nb,
+			    unsigned long action, void *data)
+{
+	struct device *dev = data;
+	struct scsi_device *sdev = to_scsi_device(dev);
+	struct scsi_dh_data *scsi_dh_data;
+	struct rdac_dh_data *h;
+	int i, found = 0;
+	unsigned long flags;
+
+	if (action == BUS_NOTIFY_ADD_DEVICE) {
+		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))) {
+				found = 1;
+				break;
+			}
+		}
+		if (!found)
+			goto out;
+
+		scsi_dh_data = kzalloc(sizeof(struct scsi_device_handler *)
+				+ sizeof(*h) , GFP_KERNEL);
+		if (!scsi_dh_data) {
+			sdev_printk(KERN_ERR, sdev, "Attach failed %s.\n",
+				    RDAC_NAME);
+			goto out;
+		}
+
+		scsi_dh_data->scsi_dh = &rdac_dh;
+		h = (struct rdac_dh_data *) scsi_dh_data->buf;
+		h->lun = UNINITIALIZED_LUN;
+		h->state = RDAC_STATE_ACTIVE;
+		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);
+		try_module_get(THIS_MODULE);
+
+		sdev_printk(KERN_NOTICE, sdev, "Attached %s.\n", RDAC_NAME);
+
+	} else if (action == BUS_NOTIFY_DEL_DEVICE) {
+		BUG_ON(sdev->scsi_dh_data == NULL);
+		if (sdev->scsi_dh_data->scsi_dh != &rdac_dh)
+			goto out;
+
+		spin_lock_irqsave(sdev->request_queue->queue_lock, flags);
+		scsi_dh_data = sdev->scsi_dh_data;
+		sdev->scsi_dh_data = NULL;
+		spin_unlock_irqrestore(sdev->request_queue->queue_lock, flags);
+
+		h = (struct rdac_dh_data *) scsi_dh_data->buf;
+		if (h->ctlr)
+			kref_put(&h->ctlr->kref, release_controller);
+		kfree(scsi_dh_data);
+		module_put(THIS_MODULE);
+		sdev_printk(KERN_NOTICE, sdev, "Dettached %s.\n", RDAC_NAME);
+	}
+
+out:
+	return 0;
+}
+
+static int __init rdac_init(void)
+{
+	int r;
+
+	r = scsi_register_device_handler(&rdac_dh);
+	if (r != 0)
+		printk(KERN_ERR "Failed to register scsi device handler.");
+	return r;
+}
+
+static void __exit rdac_exit(void)
+{
+	scsi_unregister_device_handler(&rdac_dh);
+}
+
+module_init(rdac_init);
+module_exit(rdac_exit);
+
+MODULE_DESCRIPTION("Multipath LSI/Engenio RDAC driver");
+MODULE_AUTHOR("Mike Christie, Chandra Seetharaman");
+MODULE_LICENSE("GPL");
Index: linux-2.6.25-rc8-mm2/drivers/scsi/device_handler/Makefile
===================================================================
--- linux-2.6.25-rc8-mm2.orig/drivers/scsi/device_handler/Makefile
+++ linux-2.6.25-rc8-mm2/drivers/scsi/device_handler/Makefile
@@ -2,3 +2,4 @@
 # SCSI Device Handler
 #
 obj-$(CONFIG_SCSI_DH)		+= scsi_dh.o
+obj-$(CONFIG_SCSI_DH_RDAC)	+= scsi_dh_rdac.o

^ permalink raw reply	[flat|nested] 17+ messages in thread

* [PATCH 3/7] scsi_dh: add hp sw device handler
  2008-04-17 22:22 [PATCH 0/7] scsi_dh: Move hardware handlers from dm to SCSI Chandra Seetharaman
  2008-04-17 22:22 ` [PATCH 1/7] scsi_dh: add skeleton for SCSI Device Handlers Chandra Seetharaman
  2008-04-17 22:22 ` [PATCH 2/7] scsi_dh: add lsi rdac device handler Chandra Seetharaman
@ 2008-04-17 22:23 ` Chandra Seetharaman
  2008-04-17 22:23 ` [PATCH 4/7] scsi_dh: add EMC Clariion " Chandra Seetharaman
                   ` (3 subsequent siblings)
  6 siblings, 0 replies; 17+ messages in thread
From: Chandra Seetharaman @ 2008-04-17 22:23 UTC (permalink / raw)
  To: linux-scsi
  Cc: andmike, michaelc, asson_ronald, James.Bottomley, dm-devel,
	Benoit_Arthur, jens.axboe, agk

Subject: scsi_dh: add hp sw device handler

From: Mike Christie <michaelc@cs.wisc.edu>

This patch adds a very basic scsi device handler for older hp boxes which
cannot be upgraded.

Signed-off-by: Mike Christie <michaelc@cs.wisc.edu>
Signed-off-by: Chandra Seetharaman <sekharan@us.ibm.com>
---

 drivers/scsi/device_handler/Kconfig         |    8 	8 +	0 -	0 !
 drivers/scsi/device_handler/Makefile        |    1 	1 +	0 -	0 !
 drivers/scsi/device_handler/scsi_dh_hp_sw.c |  201 	201 +	0 -	0 !
 3 files changed, 210 insertions(+)

Index: linux-2.6.25-rc8-mm2/drivers/scsi/device_handler/Kconfig
===================================================================
--- linux-2.6.25-rc8-mm2.orig/drivers/scsi/device_handler/Kconfig
+++ linux-2.6.25-rc8-mm2/drivers/scsi/device_handler/Kconfig
@@ -16,3 +16,11 @@ config SCSI_DH_RDAC
 	depends on SCSI_DH
 	help
 	If you have a LSI RDAC select y. Otherwise, say N.
+
+config SCSI_DH_HP_SW
+	tristate "HP/COMPAQ MSA Device Handler"
+	depends on SCSI_DH
+	help
+	If you have a HP/COMPAQ MSA device that requires START_STOP to
+	be sent to start it and cannot upgrade the firmware then select y.
+	Otherwise, say N.
Index: linux-2.6.25-rc8-mm2/drivers/scsi/device_handler/Makefile
===================================================================
--- linux-2.6.25-rc8-mm2.orig/drivers/scsi/device_handler/Makefile
+++ linux-2.6.25-rc8-mm2/drivers/scsi/device_handler/Makefile
@@ -3,3 +3,4 @@
 #
 obj-$(CONFIG_SCSI_DH)		+= scsi_dh.o
 obj-$(CONFIG_SCSI_DH_RDAC)	+= scsi_dh_rdac.o
+obj-$(CONFIG_SCSI_DH_HP_SW)	+= scsi_dh_hp_sw.o
Index: linux-2.6.25-rc8-mm2/drivers/scsi/device_handler/scsi_dh_hp_sw.c
===================================================================
--- /dev/null
+++ linux-2.6.25-rc8-mm2/drivers/scsi/device_handler/scsi_dh_hp_sw.c
@@ -0,0 +1,201 @@
+/*
+ * Basic HP/COMPAQ MSA 1000 support. This is only needed if your HW cannot be
+ * upgraded.
+ *
+ * Copyright (C) 2006 Red Hat, Inc.  All rights reserved.
+ * Copyright (C) 2006 Mike Christie
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; see the file COPYING.  If not, write to
+ * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <scsi/scsi.h>
+#include <scsi/scsi_dbg.h>
+#include <scsi/scsi_eh.h>
+#include <scsi/scsi_dh.h>
+
+#define HP_SW_NAME	"hp_sw"
+
+#define HP_SW_TIMEOUT (60 * HZ)
+#define HP_SW_RETRIES 3
+
+struct hp_sw_dh_data {
+	unsigned char sense[SCSI_SENSE_BUFFERSIZE];
+	int retries;
+};
+
+static inline struct hp_sw_dh_data *get_hp_sw_data(struct scsi_device *sdev)
+{
+	struct scsi_dh_data *scsi_dh_data = sdev->scsi_dh_data;
+	BUG_ON(scsi_dh_data == NULL);
+	return ((struct hp_sw_dh_data *) scsi_dh_data->buf);
+}
+
+static int hp_sw_done(struct scsi_device *sdev)
+{
+	struct hp_sw_dh_data *h = get_hp_sw_data(sdev);
+	struct scsi_sense_hdr sshdr;
+	int rc;
+
+	sdev_printk(KERN_INFO, sdev, "hp_sw_done\n");
+
+	rc = scsi_normalize_sense(h->sense, SCSI_SENSE_BUFFERSIZE, &sshdr);
+	if (!rc)
+		goto done;
+	switch (sshdr.sense_key) {
+	case NOT_READY:
+		if ((sshdr.asc == 0x04) && (sshdr.ascq == 3)) {
+			rc = SCSI_DH_RETRY;
+			h->retries++;
+			break;
+		}
+		/* fall through */
+	default:
+		h->retries++;
+		rc = SCSI_DH_IMM_RETRY;
+	}
+
+done:
+	if (rc == SCSI_DH_OK || rc == SCSI_DH_IO)
+		h->retries = 0;
+	else if (h->retries > HP_SW_RETRIES) {
+		h->retries = 0;
+		rc = SCSI_DH_IO;
+	}
+	return rc;
+}
+
+static int hp_sw_activate(struct scsi_device *sdev)
+{
+	struct hp_sw_dh_data *h = get_hp_sw_data(sdev);
+	struct request *req;
+	int ret = SCSI_DH_RES_TEMP_UNAVAIL;
+
+	req = blk_get_request(sdev->request_queue, WRITE, GFP_ATOMIC);
+	if (!req)
+		goto done;
+
+	sdev_printk(KERN_INFO, sdev, "sending START_STOP.");
+
+	req->cmd_type = REQ_TYPE_BLOCK_PC;
+	req->cmd_flags |= REQ_FAILFAST;
+	req->cmd_len = COMMAND_SIZE(START_STOP);
+	memset(req->cmd, 0, MAX_COMMAND_SIZE);
+	req->cmd[0] = START_STOP;
+	req->cmd[4] = 1;	/* Start spin cycle */
+	req->timeout = HP_SW_TIMEOUT;
+	req->sense = h->sense;
+	memset(req->sense, 0, SCSI_SENSE_BUFFERSIZE);
+	req->sense_len = 0;
+
+	ret = blk_execute_rq(req->q, NULL, req, 1);
+	if (!ret) /* SUCCESS */
+		ret = hp_sw_done(sdev);
+	else
+		ret = SCSI_DH_IO;
+done:
+	return ret;
+}
+
+static const struct {
+	char *vendor;
+	char *model;
+} hp_sw_dh_data_list[] = {
+	{"COMPAQ", "MSA1000"},
+	{"HP", "HSV100"},
+	{NULL, NULL},
+};
+
+static int hp_sw_bus_notify(struct notifier_block *, unsigned long, void *);
+
+static struct scsi_device_handler hp_sw_dh = {
+	.name		= HP_SW_NAME,
+	.module		= THIS_MODULE,
+	.nb.notifier_call = hp_sw_bus_notify,
+	.activate	= hp_sw_activate,
+};
+
+static int hp_sw_bus_notify(struct notifier_block *nb,
+			    unsigned long action, void *data)
+{
+	struct device *dev = data;
+	struct scsi_device *sdev = to_scsi_device(dev);
+	struct scsi_dh_data *scsi_dh_data;
+	int i, found = 0;
+	unsigned long flags;
+
+	if (action == BUS_NOTIFY_ADD_DEVICE) {
+		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))) {
+				found = 1;
+				break;
+			}
+		}
+		if (!found)
+			goto out;
+
+		scsi_dh_data = kzalloc(sizeof(struct scsi_device_handler *)
+				+ sizeof(struct hp_sw_dh_data) , GFP_KERNEL);
+		if (!scsi_dh_data) {
+			sdev_printk(KERN_ERR, sdev, "Attach Failed %s.\n",
+				    HP_SW_NAME);
+			goto out;
+		}
+
+		scsi_dh_data->scsi_dh = &hp_sw_dh;
+		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);
+		try_module_get(THIS_MODULE);
+
+		sdev_printk(KERN_NOTICE, sdev, "Attached %s.\n", HP_SW_NAME);
+	} else if (action == BUS_NOTIFY_DEL_DEVICE) {
+		BUG_ON(sdev->scsi_dh_data == NULL);
+		if (sdev->scsi_dh_data->scsi_dh != &hp_sw_dh)
+			goto out;
+
+		spin_lock_irqsave(sdev->request_queue->queue_lock, flags);
+		scsi_dh_data = sdev->scsi_dh_data;
+		sdev->scsi_dh_data = NULL;
+		spin_unlock_irqrestore(sdev->request_queue->queue_lock, flags);
+		module_put(THIS_MODULE);
+
+		sdev_printk(KERN_NOTICE, sdev, "Dettached %s.\n", HP_SW_NAME);
+
+		kfree(scsi_dh_data);
+	}
+
+out:
+	return 0;
+}
+
+static int __init hp_sw_init(void)
+{
+	return scsi_register_device_handler(&hp_sw_dh);
+}
+
+static void __exit hp_sw_exit(void)
+{
+	scsi_unregister_device_handler(&hp_sw_dh);
+}
+
+module_init(hp_sw_init);
+module_exit(hp_sw_exit);
+
+MODULE_DESCRIPTION("HP MSA 1000");
+MODULE_AUTHOR("Mike Christie <michaelc@cs.wisc.edu");
+MODULE_LICENSE("GPL");

^ permalink raw reply	[flat|nested] 17+ messages in thread

* [PATCH 4/7] scsi_dh: add EMC Clariion device handler
  2008-04-17 22:22 [PATCH 0/7] scsi_dh: Move hardware handlers from dm to SCSI Chandra Seetharaman
                   ` (2 preceding siblings ...)
  2008-04-17 22:23 ` [PATCH 3/7] scsi_dh: add hp sw " Chandra Seetharaman
@ 2008-04-17 22:23 ` Chandra Seetharaman
  2008-04-17 22:23 ` [PATCH 5/7] scsi_dh: Use SCSI device handler in dm-multipath Chandra Seetharaman
                   ` (2 subsequent siblings)
  6 siblings, 0 replies; 17+ messages in thread
From: Chandra Seetharaman @ 2008-04-17 22:23 UTC (permalink / raw)
  To: linux-scsi
  Cc: andmike, michaelc, asson_ronald, James.Bottomley, dm-devel,
	Benoit_Arthur, jens.axboe, agk

Subject: scsi_dh: add EMC Clariion device handler

From: Chandra Seetharaman <sekharan@us.ibm.com>

This adds support for EMC Clariions. This patch has the features that 
currently exists in mainline and advanced features from Ed's patches.

Signed-off-by: Chandra Seetharaman <sekharan@us.ibm.com>
Signed-off-by: Mike Christie <michaelc@cs.wisc.edu>
---

 drivers/scsi/device_handler/Kconfig       |    6 	6 +	0 -	0 !
 drivers/scsi/device_handler/Makefile      |    1 	1 +	0 -	0 !
 drivers/scsi/device_handler/scsi_dh_emc.c |  499 	499 +	0 -	0 !
 3 files changed, 506 insertions(+)

Index: linux-2.6.25-rc8-mm2/drivers/scsi/device_handler/Kconfig
===================================================================
--- linux-2.6.25-rc8-mm2.orig/drivers/scsi/device_handler/Kconfig
+++ linux-2.6.25-rc8-mm2/drivers/scsi/device_handler/Kconfig
@@ -24,3 +24,9 @@ config SCSI_DH_HP_SW
 	If you have a HP/COMPAQ MSA device that requires START_STOP to
 	be sent to start it and cannot upgrade the firmware then select y.
 	Otherwise, say N.
+
+config SCSI_DH_EMC
+	tristate "EMC CLARiiON Device Handler"
+	depends on SCSI_DH
+	help
+	If you have a EMC CLARiiON select y. Otherwise, say N.
Index: linux-2.6.25-rc8-mm2/drivers/scsi/device_handler/scsi_dh_emc.c
===================================================================
--- /dev/null
+++ linux-2.6.25-rc8-mm2/drivers/scsi/device_handler/scsi_dh_emc.c
@@ -0,0 +1,499 @@
+/*
+ * Target driver for EMC CLARiiON AX/CX-series hardware.
+ * Based on code from Lars Marowsky-Bree <lmb@suse.de>
+ * and Ed Goggin <egoggin@emc.com>.
+ *
+ * Copyright (C) 2006 Red Hat, Inc.  All rights reserved.
+ * Copyright (C) 2006 Mike Christie
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; see the file COPYING.  If not, write to
+ * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+#include <scsi/scsi.h>
+#include <scsi/scsi_eh.h>
+#include <scsi/scsi_dh.h>
+#include <scsi/scsi_device.h>
+
+#define CLARIION_NAME			"emc_clariion"
+
+#define CLARIION_TRESPASS_PAGE		0x22
+#define CLARIION_BUFFER_SIZE		0x80
+#define CLARIION_TIMEOUT		(60 * HZ)
+#define CLARIION_RETRIES		3
+#define CLARIION_UNBOUND_LU		-1
+
+static unsigned char long_trespass[] = {
+	0, 0, 0, 0,
+	CLARIION_TRESPASS_PAGE,	/* Page code */
+	0x09,			/* Page length - 2 */
+	0x81,			/* Trespass code + Honor reservation bit */
+	0xff, 0xff,		/* Trespass target */
+	0, 0, 0, 0, 0, 0	/* Reserved bytes / unknown */
+};
+
+static unsigned char long_trespass_hr[] = {
+	0, 0, 0, 0,
+	CLARIION_TRESPASS_PAGE,	/* Page code */
+	0x09,			/* Page length - 2 */
+	0x01,			/* Trespass code + Honor reservation bit */
+	0xff, 0xff,		/* Trespass target */
+	0, 0, 0, 0, 0, 0	/* Reserved bytes / unknown */
+};
+
+static unsigned char short_trespass[] = {
+	0, 0, 0, 0,
+	CLARIION_TRESPASS_PAGE,	/* Page code */
+	0x02,			/* Page length - 2 */
+	0x81,			/* Trespass code + Honor reservation bit */
+	0xff,			/* Trespass target */
+};
+
+static unsigned char short_trespass_hr[] = {
+	0, 0, 0, 0,
+	CLARIION_TRESPASS_PAGE,	/* Page code */
+	0x02,			/* Page length - 2 */
+	0x01,			/* Trespass code + Honor reservation bit */
+	0xff,			/* Trespass target */
+};
+
+struct clariion_dh_data {
+	/*
+	 * Use short trespass command (FC-series) or the long version
+	 * (default for AX/CX CLARiiON arrays).
+	 */
+	unsigned short_trespass;
+	/*
+	 * Whether or not (default) to honor SCSI reservations when
+	 * initiating a switch-over.
+	 */
+	unsigned hr;
+	/* I/O buffer for both MODE_SELECT and INQUIRY commands. */
+	char buffer[CLARIION_BUFFER_SIZE];
+	/*
+	 * SCSI sense buffer for commands -- assumes serial issuance
+	 * and completion sequence of all commands for same multipath.
+	 */
+	unsigned char sense[SCSI_SENSE_BUFFERSIZE];
+	/* which SP (A=0,B=1,UNBOUND=-1) is dflt SP for path's mapped dev */
+	int default_sp;
+	/* which SP (A=0,B=1,UNBOUND=-1) is active for path's mapped dev */
+	int current_sp;
+};
+
+static inline struct clariion_dh_data
+			*get_clariion_data(struct scsi_device *sdev)
+{
+	struct scsi_dh_data *scsi_dh_data = sdev->scsi_dh_data;
+	BUG_ON(scsi_dh_data == NULL);
+	return ((struct clariion_dh_data *) scsi_dh_data->buf);
+}
+
+/*
+ * Parse MODE_SELECT cmd reply.
+ */
+static int trespass_endio(struct scsi_device *sdev, int result)
+{
+	int err = SCSI_DH_OK;
+	struct scsi_sense_hdr sshdr;
+	struct clariion_dh_data *csdev = get_clariion_data(sdev);
+	char *sense = csdev->sense;
+
+	if (status_byte(result) == CHECK_CONDITION &&
+	    scsi_normalize_sense(sense, SCSI_SENSE_BUFFERSIZE, &sshdr)) {
+		sdev_printk(KERN_ERR, sdev, "Found valid sense data 0x%2x, "
+			    "0x%2x, 0x%2x while sending CLARiiON trespass "
+			    "command.\n", sshdr.sense_key, sshdr.asc,
+			     sshdr.ascq);
+
+		if ((sshdr.sense_key == 0x05) && (sshdr.asc == 0x04) &&
+		     (sshdr.ascq == 0x00)) {
+			/*
+			 * Array based copy in progress -- do not send
+			 * mode_select or copy will be aborted mid-stream.
+			 */
+			sdev_printk(KERN_INFO, sdev, "Array Based Copy in "
+				    "progress while sending CLARiiON trespass "
+				    "command.\n");
+			err = SCSI_DH_DEV_TEMP_BUSY;
+		} else if ((sshdr.sense_key == 0x02) && (sshdr.asc == 0x04) &&
+			    (sshdr.ascq == 0x03)) {
+			/*
+			 * LUN Not Ready - Manual Intervention Required
+			 * indicates in-progress ucode upgrade (NDU).
+			 */
+			sdev_printk(KERN_INFO, sdev, "Detected in-progress "
+				    "ucode upgrade NDU operation while sending "
+				    "CLARiiON trespass command.\n");
+			err = SCSI_DH_DEV_TEMP_BUSY;
+		} else
+			err = SCSI_DH_DEV_FAILED;
+	} else if (result) {
+		sdev_printk(KERN_ERR, sdev, "Error 0x%x while sending "
+			    "CLARiiON trespass command.\n", result);
+		err = SCSI_DH_IO;
+	}
+
+	return err;
+}
+
+static int parse_sp_info_reply(struct scsi_device *sdev, int result,
+		int *default_sp, int *current_sp, int *new_current_sp)
+{
+	int err = SCSI_DH_OK;
+	struct clariion_dh_data *csdev = get_clariion_data(sdev);
+
+	if (result == 0) {
+		/* check for in-progress ucode upgrade (NDU) */
+		if (csdev->buffer[48] != 0) {
+			sdev_printk(KERN_NOTICE, sdev, "Detected in-progress "
+			       "ucode upgrade NDU operation while finding "
+			       "current active SP.");
+			err = SCSI_DH_DEV_TEMP_BUSY;
+		} else {
+			*default_sp = csdev->buffer[5];
+
+			if (csdev->buffer[4] == 2)
+				/* SP for path is current */
+				*current_sp = csdev->buffer[8];
+			else {
+				if (csdev->buffer[4] == 1)
+					/* SP for this path is NOT current */
+					if (csdev->buffer[8] == 0)
+						*current_sp = 1;
+					else
+						*current_sp = 0;
+				else
+					/* unbound LU or LUNZ */
+					*current_sp = CLARIION_UNBOUND_LU;
+			}
+			*new_current_sp =  csdev->buffer[8];
+		}
+	} else {
+		struct scsi_sense_hdr sshdr;
+
+		err = SCSI_DH_IO;
+
+		if (scsi_normalize_sense(csdev->sense, SCSI_SENSE_BUFFERSIZE,
+							   &sshdr))
+			sdev_printk(KERN_ERR, sdev, "Found valid sense data "
+			      "0x%2x, 0x%2x, 0x%2x while finding current "
+			      "active SP.", sshdr.sense_key, sshdr.asc,
+			      sshdr.ascq);
+		else
+			sdev_printk(KERN_ERR, sdev, "Error 0x%x finding "
+			      "current active SP.", result);
+	}
+
+	return err;
+}
+
+static int sp_info_endio(struct scsi_device *sdev, int result,
+					int mode_select_sent, int *done)
+{
+	struct clariion_dh_data *csdev = get_clariion_data(sdev);
+	int err_flags, default_sp, current_sp, new_current_sp;
+
+	err_flags = parse_sp_info_reply(sdev, result, &default_sp,
+					     &current_sp, &new_current_sp);
+
+	if (err_flags != SCSI_DH_OK)
+		goto done;
+
+	if (mode_select_sent) {
+		csdev->default_sp = default_sp;
+		csdev->current_sp = current_sp;
+	} else {
+		/*
+		 * Issue the actual module_selec request IFF either
+		 * (1) we do not know the identity of the current SP OR
+		 * (2) what we think we know is actually correct.
+		 */
+		if ((current_sp != CLARIION_UNBOUND_LU) &&
+		    (new_current_sp != current_sp)) {
+
+			csdev->default_sp = default_sp;
+			csdev->current_sp = current_sp;
+
+			sdev_printk(KERN_INFO, sdev, "Ignoring path group "
+			       "switch-over command for CLARiiON SP%s since "
+			       " mapped device is already initialized.",
+			       current_sp ? "B" : "A");
+			if (done)
+				*done = 1; /* as good as doing it */
+		}
+	}
+done:
+	return err_flags;
+}
+
+/*
+* Get block request for REQ_BLOCK_PC command issued to path.  Currently
+* limited to MODE_SELECT (trespass) and INQUIRY (VPD page 0xC0) commands.
+*
+* Uses data and sense buffers in hardware handler context structure and
+* assumes serial servicing of commands, both issuance and completion.
+*/
+static struct request *get_req(struct scsi_device *sdev, int cmd)
+{
+	struct clariion_dh_data *csdev = get_clariion_data(sdev);
+	struct request *rq;
+	unsigned char *page22;
+	int len = 0;
+
+	rq = blk_get_request(sdev->request_queue,
+			(cmd == MODE_SELECT) ? WRITE : READ, GFP_ATOMIC);
+	if (!rq) {
+		sdev_printk(KERN_INFO, sdev, "get_req: blk_get_request failed");
+		return NULL;
+	}
+
+	memset(&rq->cmd, 0, BLK_MAX_CDB);
+	rq->cmd[0] = cmd;
+	rq->cmd_len = COMMAND_SIZE(rq->cmd[0]);
+
+	switch (cmd) {
+	case MODE_SELECT:
+		if (csdev->short_trespass) {
+			page22 = csdev->hr ? short_trespass_hr : short_trespass;
+			len = sizeof(short_trespass);
+		} else {
+			page22 = csdev->hr ? long_trespass_hr : long_trespass;
+			len = sizeof(long_trespass);
+		}
+		/*
+		 * Can't DMA from kernel BSS -- must copy selected trespass
+		 * command mode page contents to context buffer which is
+		 * allocated by kmalloc.
+		 */
+		BUG_ON((len > CLARIION_BUFFER_SIZE));
+		memcpy(csdev->buffer, page22, len);
+		rq->cmd_flags |= REQ_RW;
+		rq->cmd[1] = 0x10;
+		break;
+	case INQUIRY:
+		rq->cmd[1] = 0x1;
+		rq->cmd[2] = 0xC0;
+		len = CLARIION_BUFFER_SIZE;
+		memset(csdev->buffer, 0, CLARIION_BUFFER_SIZE);
+		break;
+	default:
+		BUG_ON(1);
+		break;
+	}
+
+	rq->cmd[4] = len;
+	rq->cmd_type = REQ_TYPE_BLOCK_PC;
+	rq->cmd_flags |= REQ_FAILFAST;
+	rq->timeout = CLARIION_TIMEOUT;
+	rq->retries = CLARIION_RETRIES;
+
+	rq->sense = csdev->sense;
+	memset(rq->sense, 0, SCSI_SENSE_BUFFERSIZE);
+	rq->sense_len = 0;
+
+	if (blk_rq_map_kern(sdev->request_queue, rq, csdev->buffer,
+							len, GFP_ATOMIC)) {
+		__blk_put_request(rq->q, rq);
+		return NULL;
+	}
+
+	return rq;
+}
+
+static int send_cmd(struct scsi_device *sdev, int cmd)
+{
+	struct request *rq = get_req(sdev, cmd);
+
+	if (!rq)
+		return SCSI_DH_RES_TEMP_UNAVAIL;
+
+	return blk_execute_rq(sdev->request_queue, NULL, rq, 1);
+}
+
+static int clariion_activate(struct scsi_device *sdev)
+{
+	int result, done = 0;
+
+	result = send_cmd(sdev, INQUIRY);
+	result = sp_info_endio(sdev, result, 0, &done);
+	if (result || done)
+		goto done;
+
+	result = send_cmd(sdev, MODE_SELECT);
+	result = trespass_endio(sdev, result);
+	if (result)
+		goto done;
+
+	result = send_cmd(sdev, INQUIRY);
+	result = sp_info_endio(sdev, result, 1, NULL);
+done:
+	return result;
+}
+
+static int clariion_check_sense(struct scsi_device *sdev,
+				struct scsi_sense_hdr *sense_hdr)
+{
+	switch (sense_hdr->sense_key) {
+	case NOT_READY:
+		if (sense_hdr->asc == 0x04 && sense_hdr->ascq == 0x03)
+			/*
+			 * LUN Not Ready - Manual Intervention Required
+			 * indicates this is a passive path.
+			 *
+			 * FIXME: However, if this is seen and EVPD C0
+			 * indicates that this is due to a NDU in
+			 * progress, we should set FAIL_PATH too.
+			 * This indicates we might have to do a SCSI
+			 * inquiry in the end_io path. Ugh.
+			 *
+			 * Can return FAILED only when we want the error
+			 * recovery process to kick in.
+			 */
+			return SUCCESS;
+		break;
+	case ILLEGAL_REQUEST:
+		if (sense_hdr->asc == 0x25 && sense_hdr->ascq == 0x01)
+			/*
+			 * An array based copy is in progress. Do not
+			 * fail the path, do not bypass to another PG,
+			 * do not retry. Fail the IO immediately.
+			 * (Actually this is the same conclusion as in
+			 * the default handler, but lets make sure.)
+			 *
+			 * Can return FAILED only when we want the error
+			 * recovery process to kick in.
+			 */
+			return SUCCESS;
+		break;
+	case UNIT_ATTENTION:
+		if (sense_hdr->asc == 0x29 && sense_hdr->ascq == 0x00)
+			/*
+			 * Unit Attention Code. This is the first IO
+			 * to the new path, so just retry.
+			 */
+			return NEEDS_RETRY;
+		break;
+	}
+
+	/* success just means we do not care what scsi-ml does */
+	return SUCCESS;
+}
+
+static const struct {
+	char *vendor;
+	char *model;
+} clariion_dev_list[] = {
+	{"DGC", "RAID"},
+	{"DGC", "DISK"},
+	{NULL, NULL},
+};
+
+static int clariion_bus_notify(struct notifier_block *, unsigned long, void *);
+
+static struct scsi_device_handler clariion_dh = {
+	.name		= CLARIION_NAME,
+	.module		= THIS_MODULE,
+	.nb.notifier_call = clariion_bus_notify,
+	.check_sense	= clariion_check_sense,
+	.activate	= clariion_activate,
+};
+
+/*
+ * TODO: need some interface so we can set trespass values
+ */
+static int clariion_bus_notify(struct notifier_block *nb,
+				unsigned long action, void *data)
+{
+	struct device *dev = data;
+	struct scsi_device *sdev = to_scsi_device(dev);
+	struct scsi_dh_data *scsi_dh_data;
+	struct clariion_dh_data *h;
+	int i, found = 0;
+	unsigned long flags;
+
+	if (action == BUS_NOTIFY_ADD_DEVICE) {
+		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))) {
+				found = 1;
+				break;
+			}
+		}
+		if (!found)
+			goto out;
+
+		scsi_dh_data = kzalloc(sizeof(struct scsi_device_handler *)
+				+ sizeof(*h) , GFP_KERNEL);
+		if (!scsi_dh_data) {
+			sdev_printk(KERN_ERR, sdev, "Attach failed %s.\n",
+				    CLARIION_NAME);
+			goto out;
+		}
+
+		scsi_dh_data->scsi_dh = &clariion_dh;
+		h = (struct clariion_dh_data *) scsi_dh_data->buf;
+		h->default_sp = CLARIION_UNBOUND_LU;
+		h->current_sp = CLARIION_UNBOUND_LU;
+
+		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, "Attached %s.\n", CLARIION_NAME);
+		try_module_get(THIS_MODULE);
+
+	} else if (action == BUS_NOTIFY_DEL_DEVICE) {
+		BUG_ON(sdev->scsi_dh_data == NULL);
+		if (sdev->scsi_dh_data->scsi_dh != &clariion_dh)
+			goto out;
+
+		spin_lock_irqsave(sdev->request_queue->queue_lock, flags);
+		scsi_dh_data = sdev->scsi_dh_data;
+		sdev->scsi_dh_data = NULL;
+		spin_unlock_irqrestore(sdev->request_queue->queue_lock, flags);
+
+		sdev_printk(KERN_NOTICE, sdev, "Dettached %s.\n",
+			    CLARIION_NAME);
+
+		kfree(scsi_dh_data);
+		module_put(THIS_MODULE);
+	}
+
+out:
+	return 0;
+}
+
+static int __init clariion_init(void)
+{
+	int r;
+
+	r = scsi_register_device_handler(&clariion_dh);
+	if (r != 0)
+		printk(KERN_ERR "Failed to register scsi device handler.");
+	return r;
+}
+
+static void __exit clariion_exit(void)
+{
+	scsi_unregister_device_handler(&clariion_dh);
+}
+
+module_init(clariion_init);
+module_exit(clariion_exit);
+
+MODULE_DESCRIPTION("EMC CX/AX/FC-family driver");
+MODULE_AUTHOR("Mike Christie <michaelc@cs.wisc.edu>, Chandra Seetharaman <sekharan@us.ibm.com>");
+MODULE_LICENSE("GPL");
Index: linux-2.6.25-rc8-mm2/drivers/scsi/device_handler/Makefile
===================================================================
--- linux-2.6.25-rc8-mm2.orig/drivers/scsi/device_handler/Makefile
+++ linux-2.6.25-rc8-mm2/drivers/scsi/device_handler/Makefile
@@ -4,3 +4,4 @@
 obj-$(CONFIG_SCSI_DH)		+= scsi_dh.o
 obj-$(CONFIG_SCSI_DH_RDAC)	+= scsi_dh_rdac.o
 obj-$(CONFIG_SCSI_DH_HP_SW)	+= scsi_dh_hp_sw.o
+obj-$(CONFIG_SCSI_DH_EMC)	+= scsi_dh_emc.o

^ permalink raw reply	[flat|nested] 17+ messages in thread

* [PATCH 5/7] scsi_dh: Use SCSI device handler in dm-multipath
  2008-04-17 22:22 [PATCH 0/7] scsi_dh: Move hardware handlers from dm to SCSI Chandra Seetharaman
                   ` (3 preceding siblings ...)
  2008-04-17 22:23 ` [PATCH 4/7] scsi_dh: add EMC Clariion " Chandra Seetharaman
@ 2008-04-17 22:23 ` Chandra Seetharaman
  2008-04-17 22:23 ` [PATCH 6/7] scsi_dh: Remove hardware handlers from dm Chandra Seetharaman
  2008-04-17 22:23 ` [PATCH 7/7] scsi_dh: Remove hardware handler infrastructure " Chandra Seetharaman
  6 siblings, 0 replies; 17+ messages in thread
From: Chandra Seetharaman @ 2008-04-17 22:23 UTC (permalink / raw)
  To: linux-scsi
  Cc: andmike, michaelc, asson_ronald, James.Bottomley, dm-devel,
	Benoit_Arthur, jens.axboe, agk

Subject: scsi_dh: Use SCSI device handler in dm-multipath

From: Chandra Seetharaman <sekharan@us.ibm.com>

This patch converts dm-mpath to scsi hw handlers. It does
not add any new functionality and old behaviors and userspace
tools work as is except we use the safe clariion default instead
of using the userspace setting.

Signed-off-by: Chandra Seetharaman <sekharan@us.ibm.com>
Signed-off-by: Mike Christie <michaelc@cs.wisc.edu>
---
 drivers/md/Kconfig    |    1 	1 +	0 -	0 !
 drivers/md/dm-mpath.c |  226 	111 +	115 -	0 !
 2 files changed, 112 insertions(+), 115 deletions(-)

Index: linux-2.6.25-rc8-mm2/drivers/md/dm-mpath.c
===================================================================
--- linux-2.6.25-rc8-mm2.orig/drivers/md/dm-mpath.c
+++ linux-2.6.25-rc8-mm2/drivers/md/dm-mpath.c
@@ -7,7 +7,6 @@
 
 #include "dm.h"
 #include "dm-path-selector.h"
-#include "dm-hw-handler.h"
 #include "dm-bio-list.h"
 #include "dm-bio-record.h"
 #include "dm-uevent.h"
@@ -20,6 +19,7 @@
 #include <linux/slab.h>
 #include <linux/time.h>
 #include <linux/workqueue.h>
+#include <scsi/scsi_dh.h>
 #include <asm/atomic.h>
 
 #define DM_MSG_PREFIX "multipath"
@@ -61,9 +61,14 @@ struct multipath {
 
 	spinlock_t lock;
 
-	struct hw_handler hw_handler;
 	unsigned nr_priority_groups;
 	struct list_head priority_groups;
+
+	/* Fields used by hardware handler usage */
+	char *hw_handler_name;
+	struct work_struct activate_passive_path;
+	struct dm_path *path_to_activate;
+
 	unsigned pg_init_required;	/* pg_init needs calling? */
 	unsigned pg_init_in_progress;	/* Only one pg_init allowed at once */
 
@@ -106,9 +111,13 @@ typedef int (*action_fn) (struct pgpath 
 
 static struct kmem_cache *_mpio_cache;
 
-static struct workqueue_struct *kmultipathd;
+static struct workqueue_struct *kmultipathd, *hw_handlerd;
 static void process_queued_ios(struct work_struct *work);
 static void trigger_event(struct work_struct *work);
+static void activate_passive_path(struct work_struct *work);
+static void bypass_pg(struct multipath *m, struct priority_group *pg,
+		      int bypassed);
+static int fail_path(struct pgpath *pgpath);
 
 
 /*-----------------------------------------------
@@ -178,6 +187,7 @@ static struct multipath *alloc_multipath
 		m->queue_io = 1;
 		INIT_WORK(&m->process_queued_ios, process_queued_ios);
 		INIT_WORK(&m->trigger_event, trigger_event);
+		INIT_WORK(&m->activate_passive_path, activate_passive_path);
 		m->mpio_pool = mempool_create_slab_pool(MIN_IOS, _mpio_cache);
 		if (!m->mpio_pool) {
 			kfree(m);
@@ -193,18 +203,13 @@ static struct multipath *alloc_multipath
 static void free_multipath(struct multipath *m)
 {
 	struct priority_group *pg, *tmp;
-	struct hw_handler *hwh = &m->hw_handler;
 
 	list_for_each_entry_safe(pg, tmp, &m->priority_groups, list) {
 		list_del(&pg->list);
 		free_priority_group(pg, m->ti);
 	}
 
-	if (hwh->type) {
-		hwh->type->destroy(hwh);
-		dm_put_hw_handler(hwh->type);
-	}
-
+	kfree(m->hw_handler_name);
 	mempool_destroy(m->mpio_pool);
 	kfree(m);
 }
@@ -216,12 +221,10 @@ static void free_multipath(struct multip
 
 static void __switch_pg(struct multipath *m, struct pgpath *pgpath)
 {
-	struct hw_handler *hwh = &m->hw_handler;
-
 	m->current_pg = pgpath->pg;
 
 	/* Must we initialise the PG first, and queue I/O till it's ready? */
-	if (hwh->type && hwh->type->pg_init) {
+	if (m->hw_handler_name) {
 		m->pg_init_required = 1;
 		m->queue_io = 1;
 	} else {
@@ -405,11 +408,86 @@ static void dispatch_queued_ios(struct m
 	}
 }
 
+static void pg_init_done(struct dm_path *path, int errors)
+{
+	struct pgpath *pgpath = path_to_pgpath(path);
+	struct priority_group *pg = pgpath->pg;
+	struct multipath *m = pg->m;
+	unsigned long flags;
+
+	/* device or driver problems */
+	switch (errors) {
+	case SCSI_DH_OK:
+		break;
+	case SCSI_DH_NOSYS:
+		if (!m->hw_handler_name) {
+			errors = 0;
+			break;
+		}
+		DMERR("Cannot failover device because hw-%s may not be "
+		      "loaded.", m->hw_handler_name);
+		/*
+		 * Fail path for now, so we do not ping poing
+		 */
+		fail_path(pgpath);
+		break;
+	case SCSI_DH_DEV_TEMP_BUSY:
+		/*
+		 * Probably doing something like FW upgrade on the
+		 * controller so try the other pg.
+		 */
+		bypass_pg(m, pg, 1);
+		break;
+	/* TODO: For SCSI_DH_RETRY we should wait a couple seconds */
+	case SCSI_DH_RETRY:
+	case SCSI_DH_IMM_RETRY:
+	case SCSI_DH_RES_TEMP_UNAVAIL:
+		break;
+	default:
+		/*
+		 * We probably do not want to fail the path for a device
+		 * error, but this is what the old dm did. In future
+		 * patches we can do more advanced handling.
+		 */
+		fail_path(pgpath);
+	}
+
+	spin_lock_irqsave(&m->lock, flags);
+	if (errors) {
+		DMERR("Could not failover device. Error %d.", errors);
+		m->current_pgpath = NULL;
+		m->current_pg = NULL;
+	} else if (!m->pg_init_required) {
+		m->queue_io = 0;
+		pg->bypassed = 0;
+	}
+
+	m->pg_init_in_progress = 0;
+	queue_work(kmultipathd, &m->process_queued_ios);
+	spin_unlock_irqrestore(&m->lock, flags);
+}
+
+static void activate_passive_path(struct work_struct *work)
+{
+	int ret;
+	struct multipath *m =
+		container_of(work, struct multipath, activate_passive_path);
+	struct dm_path *path = m->path_to_activate;
+
+	ret = scsi_dh_activate(bdev_get_queue(path->dev->bdev));
+	pg_init_done(path, ret);
+}
+
+static void pg_init(struct multipath *m, struct dm_path *path)
+{
+	m->path_to_activate = path;
+	queue_work(hw_handlerd, &m->activate_passive_path);
+}
+
 static void process_queued_ios(struct work_struct *work)
 {
 	struct multipath *m =
 		container_of(work, struct multipath, process_queued_ios);
-	struct hw_handler *hwh = &m->hw_handler;
 	struct pgpath *pgpath = NULL;
 	unsigned init_required = 0, must_queue = 1;
 	unsigned long flags;
@@ -439,7 +517,7 @@ out:
 	spin_unlock_irqrestore(&m->lock, flags);
 
 	if (init_required)
-		hwh->type->pg_init(hwh, pgpath->pg->bypassed, &pgpath->path);
+		pg_init(m, &pgpath->path);
 
 	if (!must_queue)
 		dispatch_queued_ios(m);
@@ -652,10 +730,9 @@ static struct priority_group *parse_prio
 
 static int parse_hw_handler(struct arg_set *as, struct multipath *m)
 {
+	struct dm_target *ti = m->ti;
 	int r;
-	struct hw_handler_type *hwht;
 	unsigned hw_argc;
-	struct dm_target *ti = m->ti;
 
 	static struct param _params[] = {
 		{0, 1024, "invalid number of hardware handler args"},
@@ -668,25 +745,9 @@ static int parse_hw_handler(struct arg_s
 	if (!hw_argc)
 		return 0;
 
-	hwht = dm_get_hw_handler(shift(as));
-	if (!hwht) {
-		ti->error = "unknown hardware handler type";
-		return -EINVAL;
-	}
-
-	m->hw_handler.md = dm_table_get_md(ti->table);
-	dm_put(m->hw_handler.md);
-
-	r = hwht->create(&m->hw_handler, hw_argc - 1, as->argv);
-	if (r) {
-		dm_put_hw_handler(hwht);
-		ti->error = "hardware handler constructor failed";
-		return r;
-	}
-
-	m->hw_handler.type = hwht;
+	m->hw_handler_name = kstrdup(shift(as), GFP_KERNEL);
+	request_module("scsi_dh_%s", m->hw_handler_name);
 	consume(as, hw_argc - 1);
-
 	return 0;
 }
 
@@ -808,6 +869,7 @@ static void multipath_dtr(struct dm_targ
 {
 	struct multipath *m = (struct multipath *) ti->private;
 
+	flush_workqueue(hw_handlerd);
 	flush_workqueue(kmultipathd);
 	free_multipath(m);
 }
@@ -1006,71 +1068,11 @@ static int bypass_pg_num(struct multipat
 }
 
 /*
- * Should we retry pg_init immediately?
- */
-static int pg_init_limit_reached(struct multipath *m, struct pgpath *pgpath)
-{
-	unsigned long flags;
-	int limit_reached = 0;
-
-	spin_lock_irqsave(&m->lock, flags);
-
-	if (m->pg_init_count <= m->pg_init_retries)
-		m->pg_init_required = 1;
-	else
-		limit_reached = 1;
-
-	spin_unlock_irqrestore(&m->lock, flags);
-
-	return limit_reached;
-}
-
-/*
- * pg_init must call this when it has completed its initialisation
- */
-void dm_pg_init_complete(struct dm_path *path, unsigned err_flags)
-{
-	struct pgpath *pgpath = path_to_pgpath(path);
-	struct priority_group *pg = pgpath->pg;
-	struct multipath *m = pg->m;
-	unsigned long flags;
-
-	/*
-	 * If requested, retry pg_init until maximum number of retries exceeded.
-	 * If retry not requested and PG already bypassed, always fail the path.
-	 */
-	if (err_flags & MP_RETRY) {
-		if (pg_init_limit_reached(m, pgpath))
-			err_flags |= MP_FAIL_PATH;
-	} else if (err_flags && pg->bypassed)
-		err_flags |= MP_FAIL_PATH;
-
-	if (err_flags & MP_FAIL_PATH)
-		fail_path(pgpath);
-
-	if (err_flags & MP_BYPASS_PG)
-		bypass_pg(m, pg, 1);
-
-	spin_lock_irqsave(&m->lock, flags);
-	if (err_flags & ~MP_RETRY) {
-		m->current_pgpath = NULL;
-		m->current_pg = NULL;
-	} else if (!m->pg_init_required)
-		m->queue_io = 0;
-
-	m->pg_init_in_progress = 0;
-	queue_work(kmultipathd, &m->process_queued_ios);
-	spin_unlock_irqrestore(&m->lock, flags);
-}
-
-/*
  * end_io handling
  */
 static int do_end_io(struct multipath *m, struct bio *bio,
 		     int error, struct dm_mpath_io *mpio)
 {
-	struct hw_handler *hwh = &m->hw_handler;
-	unsigned err_flags = MP_FAIL_PATH;	/* Default behavior */
 	unsigned long flags;
 
 	if (!error)
@@ -1097,19 +1099,8 @@ static int do_end_io(struct multipath *m
 	}
 	spin_unlock_irqrestore(&m->lock, flags);
 
-	if (hwh->type && hwh->type->error)
-		err_flags = hwh->type->error(hwh, bio);
-
-	if (mpio->pgpath) {
-		if (err_flags & MP_FAIL_PATH)
-			fail_path(mpio->pgpath);
-
-		if (err_flags & MP_BYPASS_PG)
-			bypass_pg(m, mpio->pgpath->pg, 1);
-	}
-
-	if (err_flags & MP_ERROR_IO)
-		return -EIO;
+	if (mpio->pgpath)
+		fail_path(mpio->pgpath);
 
       requeue:
 	dm_bio_restore(&mpio->details, bio);
@@ -1194,7 +1185,6 @@ static int multipath_status(struct dm_ta
 	int sz = 0;
 	unsigned long flags;
 	struct multipath *m = (struct multipath *) ti->private;
-	struct hw_handler *hwh = &m->hw_handler;
 	struct priority_group *pg;
 	struct pgpath *p;
 	unsigned pg_num;
@@ -1214,12 +1204,10 @@ static int multipath_status(struct dm_ta
 			DMEMIT("pg_init_retries %u ", m->pg_init_retries);
 	}
 
-	if (hwh->type && hwh->type->status)
-		sz += hwh->type->status(hwh, type, result + sz, maxlen - sz);
-	else if (!hwh->type || type == STATUSTYPE_INFO)
-		DMEMIT("0 ");
+	if (m->hw_handler_name)
+		DMEMIT("1 %s ", m->hw_handler_name);
 	else
-		DMEMIT("1 %s ", hwh->type->name);
+		DMEMIT("0 ");
 
 	DMEMIT("%u ", m->nr_priority_groups);
 
@@ -1422,6 +1410,15 @@ static int __init dm_multipath_init(void
 		return -ENOMEM;
 	}
 
+	hw_handlerd = create_workqueue("khwhandlerd");
+	if (!hw_handlerd) {
+		DMERR("failed to create workqueue khwhandlerd");
+		destroy_workqueue(kmultipathd);
+		dm_unregister_target(&multipath_target);
+		kmem_cache_destroy(_mpio_cache);
+		return -ENOMEM;
+	}
+
 	DMINFO("version %u.%u.%u loaded",
 	       multipath_target.version[0], multipath_target.version[1],
 	       multipath_target.version[2]);
@@ -1433,6 +1430,7 @@ static void __exit dm_multipath_exit(voi
 {
 	int r;
 
+	destroy_workqueue(hw_handlerd);
 	destroy_workqueue(kmultipathd);
 
 	r = dm_unregister_target(&multipath_target);
@@ -1441,8 +1439,6 @@ static void __exit dm_multipath_exit(voi
 	kmem_cache_destroy(_mpio_cache);
 }
 
-EXPORT_SYMBOL_GPL(dm_pg_init_complete);
-
 module_init(dm_multipath_init);
 module_exit(dm_multipath_exit);
 
Index: linux-2.6.25-rc8-mm2/drivers/md/Kconfig
===================================================================
--- linux-2.6.25-rc8-mm2.orig/drivers/md/Kconfig
+++ linux-2.6.25-rc8-mm2/drivers/md/Kconfig
@@ -252,6 +252,7 @@ config DM_ZERO
 config DM_MULTIPATH
 	tristate "Multipath target"
 	depends on BLK_DEV_DM
+	select SCSI_DH
 	---help---
 	  Allow volume managers to support multipath hardware.
 

^ permalink raw reply	[flat|nested] 17+ messages in thread

* [PATCH 6/7] scsi_dh: Remove hardware handlers from dm
  2008-04-17 22:22 [PATCH 0/7] scsi_dh: Move hardware handlers from dm to SCSI Chandra Seetharaman
                   ` (4 preceding siblings ...)
  2008-04-17 22:23 ` [PATCH 5/7] scsi_dh: Use SCSI device handler in dm-multipath Chandra Seetharaman
@ 2008-04-17 22:23 ` Chandra Seetharaman
  2008-04-17 22:23 ` [PATCH 7/7] scsi_dh: Remove hardware handler infrastructure " Chandra Seetharaman
  6 siblings, 0 replies; 17+ messages in thread
From: Chandra Seetharaman @ 2008-04-17 22:23 UTC (permalink / raw)
  To: linux-scsi
  Cc: dm-devel, andmike, michaelc, agk, James.Bottomley, jens.axboe,
	dwysocha, Benoit_Arthur, asson_ronald, Chandra Seetharaman

Subject: scsi_dh: Remove hardware handlers from dm

From: Chandra Seetharaman <sekharan@us.ibm.com>

This patch removes the 3 hardware handlers that currently exist 
under dm as they are moved to SCSI layer in the previous patches.

Signed-off-by: Chandra Seetharaman <sekharan@us.ibm.com>
---
 drivers/md/Kconfig          |   18 	0 +	18 -	0 !
 drivers/md/Makefile         |    3 	0 +	3 -	0 !
 drivers/md/dm-emc.c         |  347 	0 +	347 -	0 !
 drivers/md/dm-mpath-hp-sw.c |  248 	0 +	248 -	0 !
 drivers/md/dm-mpath-rdac.c  |  701 	0 +	701 -	0 !
 5 files changed, 1317 deletions(-)

Index: linux-2.6.25-rc8-mm2/drivers/md/Kconfig
===================================================================
--- linux-2.6.25-rc8-mm2.orig/drivers/md/Kconfig
+++ linux-2.6.25-rc8-mm2/drivers/md/Kconfig
@@ -256,24 +256,6 @@ config DM_MULTIPATH
 	---help---
 	  Allow volume managers to support multipath hardware.
 
-config DM_MULTIPATH_EMC
-	tristate "EMC CX/AX multipath support"
-	depends on DM_MULTIPATH && BLK_DEV_DM
-	---help---
-	  Multipath support for EMC CX/AX series hardware.
-
-config DM_MULTIPATH_RDAC
-	tristate "LSI/Engenio RDAC multipath support (EXPERIMENTAL)"
-	depends on DM_MULTIPATH && BLK_DEV_DM && SCSI && EXPERIMENTAL
-	---help---
-	  Multipath support for LSI/Engenio RDAC.
-
-config DM_MULTIPATH_HP
-        tristate "HP MSA multipath support (EXPERIMENTAL)"
-        depends on DM_MULTIPATH && BLK_DEV_DM && SCSI && EXPERIMENTAL
-        ---help---
-          Multipath support for HP MSA (Active/Passive) series hardware.
-
 config DM_DELAY
 	tristate "I/O delaying target (EXPERIMENTAL)"
 	depends on BLK_DEV_DM && EXPERIMENTAL
Index: linux-2.6.25-rc8-mm2/drivers/md/Makefile
===================================================================
--- linux-2.6.25-rc8-mm2.orig/drivers/md/Makefile
+++ linux-2.6.25-rc8-mm2/drivers/md/Makefile
@@ -35,9 +35,6 @@ obj-$(CONFIG_BLK_DEV_DM)	+= dm-mod.o
 obj-$(CONFIG_DM_CRYPT)		+= dm-crypt.o
 obj-$(CONFIG_DM_DELAY)		+= dm-delay.o
 obj-$(CONFIG_DM_MULTIPATH)	+= dm-multipath.o dm-round-robin.o
-obj-$(CONFIG_DM_MULTIPATH_EMC)	+= dm-emc.o
-obj-$(CONFIG_DM_MULTIPATH_HP)	+= dm-hp-sw.o
-obj-$(CONFIG_DM_MULTIPATH_RDAC)	+= dm-rdac.o
 obj-$(CONFIG_DM_SNAPSHOT)	+= dm-snapshot.o
 obj-$(CONFIG_DM_MIRROR)		+= dm-mirror.o dm-log.o
 obj-$(CONFIG_DM_ZERO)		+= dm-zero.o
Index: linux-2.6.25-rc8-mm2/drivers/md/dm-emc.c
===================================================================
--- linux-2.6.25-rc8-mm2.orig/drivers/md/dm-emc.c
+++ /dev/null
@@ -1,347 +0,0 @@
-/*
- * Copyright (C) 2004 SUSE LINUX Products GmbH. All rights reserved.
- * Copyright (C) 2004 Red Hat, Inc. All rights reserved.
- *
- * This file is released under the GPL.
- *
- * Multipath support for EMC CLARiiON AX/CX-series hardware.
- */
-
-#include "dm.h"
-#include "dm-hw-handler.h"
-#include <scsi/scsi.h>
-#include <scsi/scsi_cmnd.h>
-
-#define DM_MSG_PREFIX "multipath emc"
-
-struct emc_handler {
-	spinlock_t lock;
-
-	/* Whether we should send the short trespass command (FC-series)
-	 * or the long version (default for AX/CX CLARiiON arrays). */
-	unsigned short_trespass;
-	/* Whether or not to honor SCSI reservations when initiating a
-	 * switch-over. Default: Don't. */
-	unsigned hr;
-
-	unsigned char sense[SCSI_SENSE_BUFFERSIZE];
-};
-
-#define TRESPASS_PAGE 0x22
-#define EMC_FAILOVER_TIMEOUT (60 * HZ)
-
-/* Code borrowed from dm-lsi-rdac by Mike Christie */
-
-static inline void free_bio(struct bio *bio)
-{
-	__free_page(bio->bi_io_vec[0].bv_page);
-	bio_put(bio);
-}
-
-static void emc_endio(struct bio *bio, int error)
-{
-	struct dm_path *path = bio->bi_private;
-
-	/* We also need to look at the sense keys here whether or not to
-	 * switch to the next PG etc.
-	 *
-	 * For now simple logic: either it works or it doesn't.
-	 */
-	if (error)
-		dm_pg_init_complete(path, MP_FAIL_PATH);
-	else
-		dm_pg_init_complete(path, 0);
-
-	/* request is freed in block layer */
-	free_bio(bio);
-}
-
-static struct bio *get_failover_bio(struct dm_path *path, unsigned data_size)
-{
-	struct bio *bio;
-	struct page *page;
-
-	bio = bio_alloc(GFP_ATOMIC, 1);
-	if (!bio) {
-		DMERR("get_failover_bio: bio_alloc() failed.");
-		return NULL;
-	}
-
-	bio->bi_rw |= (1 << BIO_RW);
-	bio->bi_bdev = path->dev->bdev;
-	bio->bi_sector = 0;
-	bio->bi_private = path;
-	bio->bi_end_io = emc_endio;
-
-	page = alloc_page(GFP_ATOMIC);
-	if (!page) {
-		DMERR("get_failover_bio: alloc_page() failed.");
-		bio_put(bio);
-		return NULL;
-	}
-
-	if (bio_add_page(bio, page, data_size, 0) != data_size) {
-		DMERR("get_failover_bio: bio_add_page() failed.");
-		__free_page(page);
-		bio_put(bio);
-		return NULL;
-	}
-
-	return bio;
-}
-
-static struct request *get_failover_req(struct emc_handler *h,
-					struct bio *bio, struct dm_path *path)
-{
-	struct request *rq;
-	struct block_device *bdev = bio->bi_bdev;
-	struct request_queue *q = bdev_get_queue(bdev);
-
-	/* FIXME: Figure out why it fails with GFP_ATOMIC. */
-	rq = blk_get_request(q, WRITE, __GFP_WAIT);
-	if (!rq) {
-		DMERR("get_failover_req: blk_get_request failed");
-		return NULL;
-	}
-
-	blk_rq_append_bio(q, rq, bio);
-
-	rq->sense = h->sense;
-	memset(rq->sense, 0, SCSI_SENSE_BUFFERSIZE);
-	rq->sense_len = 0;
-
-	memset(&rq->cmd, 0, BLK_MAX_CDB);
-
-	rq->timeout = EMC_FAILOVER_TIMEOUT;
-	rq->cmd_type = REQ_TYPE_BLOCK_PC;
-	rq->cmd_flags |= REQ_FAILFAST | REQ_NOMERGE;
-
-	return rq;
-}
-
-static struct request *emc_trespass_get(struct emc_handler *h,
-					struct dm_path *path)
-{
-	struct bio *bio;
-	struct request *rq;
-	unsigned char *page22;
-	unsigned char long_trespass_pg[] = {
-		0, 0, 0, 0,
-		TRESPASS_PAGE,        /* Page code */
-		0x09,                 /* Page length - 2 */
-		h->hr ? 0x01 : 0x81,  /* Trespass code + Honor reservation bit */
-		0xff, 0xff,           /* Trespass target */
-		0, 0, 0, 0, 0, 0      /* Reserved bytes / unknown */
-		};
-	unsigned char short_trespass_pg[] = {
-		0, 0, 0, 0,
-		TRESPASS_PAGE,        /* Page code */
-		0x02,                 /* Page length - 2 */
-		h->hr ? 0x01 : 0x81,  /* Trespass code + Honor reservation bit */
-		0xff,                 /* Trespass target */
-		};
-	unsigned data_size = h->short_trespass ? sizeof(short_trespass_pg) :
-				sizeof(long_trespass_pg);
-
-	/* get bio backing */
-	if (data_size > PAGE_SIZE)
-		/* this should never happen */
-		return NULL;
-
-	bio = get_failover_bio(path, data_size);
-	if (!bio) {
-		DMERR("emc_trespass_get: no bio");
-		return NULL;
-	}
-
-	page22 = (unsigned char *)bio_data(bio);
-	memset(page22, 0, data_size);
-
-	memcpy(page22, h->short_trespass ?
-		short_trespass_pg : long_trespass_pg, data_size);
-
-	/* get request for block layer packet command */
-	rq = get_failover_req(h, bio, path);
-	if (!rq) {
-		DMERR("emc_trespass_get: no rq");
-		free_bio(bio);
-		return NULL;
-	}
-
-	/* Prepare the command. */
-	rq->cmd[0] = MODE_SELECT;
-	rq->cmd[1] = 0x10;
-	rq->cmd[4] = data_size;
-	rq->cmd_len = COMMAND_SIZE(rq->cmd[0]);
-
-	return rq;
-}
-
-static void emc_pg_init(struct hw_handler *hwh, unsigned bypassed,
-			struct dm_path *path)
-{
-	struct request *rq;
-	struct request_queue *q = bdev_get_queue(path->dev->bdev);
-
-	/*
-	 * We can either blindly init the pg (then look at the sense),
-	 * or we can send some commands to get the state here (then
-	 * possibly send the fo cmnd), or we can also have the
-	 * initial state passed into us and then get an update here.
-	 */
-	if (!q) {
-		DMINFO("emc_pg_init: no queue");
-		goto fail_path;
-	}
-
-	/* FIXME: The request should be pre-allocated. */
-	rq = emc_trespass_get(hwh->context, path);
-	if (!rq) {
-		DMERR("emc_pg_init: no rq");
-		goto fail_path;
-	}
-
-	DMINFO("emc_pg_init: sending switch-over command");
-	elv_add_request(q, rq, ELEVATOR_INSERT_FRONT, 1);
-	return;
-
-fail_path:
-	dm_pg_init_complete(path, MP_FAIL_PATH);
-}
-
-static struct emc_handler *alloc_emc_handler(void)
-{
-	struct emc_handler *h = kzalloc(sizeof(*h), GFP_KERNEL);
-
-	if (h)
-		spin_lock_init(&h->lock);
-
-	return h;
-}
-
-static int emc_create(struct hw_handler *hwh, unsigned argc, char **argv)
-{
-	struct emc_handler *h;
-	unsigned hr, short_trespass;
-
-	if (argc == 0) {
-		/* No arguments: use defaults */
-		hr = 0;
-		short_trespass = 0;
-	} else if (argc != 2) {
-		DMWARN("incorrect number of arguments");
-		return -EINVAL;
-	} else {
-		if ((sscanf(argv[0], "%u", &short_trespass) != 1)
-			|| (short_trespass > 1)) {
-			DMWARN("invalid trespass mode selected");
-			return -EINVAL;
-		}
-
-		if ((sscanf(argv[1], "%u", &hr) != 1)
-			|| (hr > 1)) {
-			DMWARN("invalid honor reservation flag selected");
-			return -EINVAL;
-		}
-	}
-
-	h = alloc_emc_handler();
-	if (!h)
-		return -ENOMEM;
-
-	hwh->context = h;
-
-	if ((h->short_trespass = short_trespass))
-		DMWARN("short trespass command will be send");
-	else
-		DMWARN("long trespass command will be send");
-
-	if ((h->hr = hr))
-		DMWARN("honor reservation bit will be set");
-	else
-		DMWARN("honor reservation bit will not be set (default)");
-
-	return 0;
-}
-
-static void emc_destroy(struct hw_handler *hwh)
-{
-	struct emc_handler *h = (struct emc_handler *) hwh->context;
-
-	kfree(h);
-	hwh->context = NULL;
-}
-
-static unsigned emc_error(struct hw_handler *hwh, struct bio *bio)
-{
-	/* FIXME: Patch from axboe still missing */
-#if 0
-	int sense;
-
-	if (bio->bi_error & BIO_SENSE) {
-		sense = bio->bi_error & 0xffffff; /* sense key / asc / ascq */
-
-		if (sense == 0x020403) {
-			/* LUN Not Ready - Manual Intervention Required
-			 * indicates this is a passive path.
-			 *
-			 * FIXME: However, if this is seen and EVPD C0
-			 * indicates that this is due to a NDU in
-			 * progress, we should set FAIL_PATH too.
-			 * This indicates we might have to do a SCSI
-			 * inquiry in the end_io path. Ugh. */
-			return MP_BYPASS_PG | MP_RETRY_IO;
-		} else if (sense == 0x052501) {
-			/* An array based copy is in progress. Do not
-			 * fail the path, do not bypass to another PG,
-			 * do not retry. Fail the IO immediately.
-			 * (Actually this is the same conclusion as in
-			 * the default handler, but lets make sure.) */
-			return 0;
-		} else if (sense == 0x062900) {
-			/* Unit Attention Code. This is the first IO
-			 * to the new path, so just retry. */
-			return MP_RETRY_IO;
-		}
-	}
-#endif
-
-	/* Try default handler */
-	return dm_scsi_err_handler(hwh, bio);
-}
-
-static struct hw_handler_type emc_hwh = {
-	.name = "emc",
-	.module = THIS_MODULE,
-	.create = emc_create,
-	.destroy = emc_destroy,
-	.pg_init = emc_pg_init,
-	.error = emc_error,
-};
-
-static int __init dm_emc_init(void)
-{
-	int r = dm_register_hw_handler(&emc_hwh);
-
-	if (r < 0)
-		DMERR("register failed %d", r);
-
-	DMINFO("version 0.0.3 loaded");
-
-	return r;
-}
-
-static void __exit dm_emc_exit(void)
-{
-	int r = dm_unregister_hw_handler(&emc_hwh);
-
-	if (r < 0)
-		DMERR("unregister failed %d", r);
-}
-
-module_init(dm_emc_init);
-module_exit(dm_emc_exit);
-
-MODULE_DESCRIPTION(DM_NAME " EMC CX/AX/FC-family multipath");
-MODULE_AUTHOR("Lars Marowsky-Bree <lmb@suse.de>");
-MODULE_LICENSE("GPL");
Index: linux-2.6.25-rc8-mm2/drivers/md/dm-mpath-hp-sw.c
===================================================================
--- linux-2.6.25-rc8-mm2.orig/drivers/md/dm-mpath-hp-sw.c
+++ /dev/null
@@ -1,248 +0,0 @@
-/*
- * Copyright (C) 2005 Mike Christie, All rights reserved.
- * Copyright (C) 2007 Red Hat, Inc. All rights reserved.
- * Authors: Mike Christie
- *          Dave Wysochanski
- *
- * This file is released under the GPL.
- *
- * This module implements the specific path activation code for
- * HP StorageWorks and FSC FibreCat Asymmetric (Active/Passive)
- * storage arrays.
- * These storage arrays have controller-based failover, not
- * LUN-based failover.  However, LUN-based failover is the design
- * of dm-multipath. Thus, this module is written for LUN-based failover.
- */
-#include <linux/blkdev.h>
-#include <linux/list.h>
-#include <linux/types.h>
-#include <scsi/scsi.h>
-#include <scsi/scsi_cmnd.h>
-#include <scsi/scsi_dbg.h>
-
-#include "dm.h"
-#include "dm-hw-handler.h"
-
-#define DM_MSG_PREFIX "multipath hp-sw"
-#define DM_HP_HWH_NAME "hp-sw"
-#define DM_HP_HWH_VER "1.0.0"
-
-struct hp_sw_context {
-	unsigned char sense[SCSI_SENSE_BUFFERSIZE];
-};
-
-/*
- * hp_sw_error_is_retryable - Is an HP-specific check condition retryable?
- * @req: path activation request
- *
- * Examine error codes of request and determine whether the error is retryable.
- * Some error codes are already retried by scsi-ml (see
- * scsi_decide_disposition), but some HP specific codes are not.
- * The intent of this routine is to supply the logic for the HP specific
- * check conditions.
- *
- * Returns:
- *  1 - command completed with retryable error
- *  0 - command completed with non-retryable error
- *
- * Possible optimizations
- * 1. More hardware-specific error codes
- */
-static int hp_sw_error_is_retryable(struct request *req)
-{
-	/*
-	 * NOT_READY is known to be retryable
-	 * For now we just dump out the sense data and call it retryable
-	 */
-	if (status_byte(req->errors) == CHECK_CONDITION)
-		__scsi_print_sense(DM_HP_HWH_NAME, req->sense, req->sense_len);
-
-	/*
-	 * At this point we don't have complete information about all the error
-	 * codes from this hardware, so we are just conservative and retry
-	 * when in doubt.
-	 */
-	return 1;
-}
-
-/*
- * hp_sw_end_io - Completion handler for HP path activation.
- * @req: path activation request
- * @error: scsi-ml error
- *
- *  Check sense data, free request structure, and notify dm that
- *  pg initialization has completed.
- *
- * Context: scsi-ml softirq
- *
- */
-static void hp_sw_end_io(struct request *req, int error)
-{
-	struct dm_path *path = req->end_io_data;
-	unsigned err_flags = 0;
-
-	if (!error) {
-		DMDEBUG("%s path activation command - success",
-			path->dev->name);
-		goto out;
-	}
-
-	if (hp_sw_error_is_retryable(req)) {
-		DMDEBUG("%s path activation command - retry",
-			path->dev->name);
-		err_flags = MP_RETRY;
-		goto out;
-	}
-
-	DMWARN("%s path activation fail - error=0x%x",
-	       path->dev->name, error);
-	err_flags = MP_FAIL_PATH;
-
-out:
-	req->end_io_data = NULL;
-	__blk_put_request(req->q, req);
-	dm_pg_init_complete(path, err_flags);
-}
-
-/*
- * hp_sw_get_request - Allocate an HP specific path activation request
- * @path: path on which request will be sent (needed for request queue)
- *
- * The START command is used for path activation request.
- * These arrays are controller-based failover, not LUN based.
- * One START command issued to a single path will fail over all
- * LUNs for the same controller.
- *
- * Possible optimizations
- * 1. Make timeout configurable
- * 2. Preallocate request
- */
-static struct request *hp_sw_get_request(struct dm_path *path)
-{
-	struct request *req;
-	struct block_device *bdev = path->dev->bdev;
-	struct request_queue *q = bdev_get_queue(bdev);
-	struct hp_sw_context *h = path->hwhcontext;
-
-	req = blk_get_request(q, WRITE, GFP_NOIO);
-	if (!req)
-		goto out;
-
-	req->timeout = 60 * HZ;
-
-	req->errors = 0;
-	req->cmd_type = REQ_TYPE_BLOCK_PC;
-	req->cmd_flags |= REQ_FAILFAST | REQ_NOMERGE;
-	req->end_io_data = path;
-	req->sense = h->sense;
-	memset(req->sense, 0, SCSI_SENSE_BUFFERSIZE);
-
-	memset(&req->cmd, 0, BLK_MAX_CDB);
-	req->cmd[0] = START_STOP;
-	req->cmd[4] = 1;
-	req->cmd_len = COMMAND_SIZE(req->cmd[0]);
-
-out:
-	return req;
-}
-
-/*
- * hp_sw_pg_init - HP path activation implementation.
- * @hwh: hardware handler specific data
- * @bypassed: unused; is the path group bypassed? (see dm-mpath.c)
- * @path: path to send initialization command
- *
- * Send an HP-specific path activation command on 'path'.
- * Do not try to optimize in any way, just send the activation command.
- * More than one path activation command may be sent to the same controller.
- * This seems to work fine for basic failover support.
- *
- * Possible optimizations
- * 1. Detect an in-progress activation request and avoid submitting another one
- * 2. Model the controller and only send a single activation request at a time
- * 3. Determine the state of a path before sending an activation request
- *
- * Context: kmpathd (see process_queued_ios() in dm-mpath.c)
- */
-static void hp_sw_pg_init(struct hw_handler *hwh, unsigned bypassed,
-			  struct dm_path *path)
-{
-	struct request *req;
-	struct hp_sw_context *h;
-
-	path->hwhcontext = hwh->context;
-	h = hwh->context;
-
-	req = hp_sw_get_request(path);
-	if (!req) {
-		DMERR("%s path activation command - allocation fail",
-		      path->dev->name);
-		goto retry;
-	}
-
-	DMDEBUG("%s path activation command - sent", path->dev->name);
-
-	blk_execute_rq_nowait(req->q, NULL, req, 1, hp_sw_end_io);
-	return;
-
-retry:
-	dm_pg_init_complete(path, MP_RETRY);
-}
-
-static int hp_sw_create(struct hw_handler *hwh, unsigned argc, char **argv)
-{
-	struct hp_sw_context *h;
-
-	h = kmalloc(sizeof(*h), GFP_KERNEL);
-	if (!h)
-		return -ENOMEM;
-
-	hwh->context = h;
-
-	return 0;
-}
-
-static void hp_sw_destroy(struct hw_handler *hwh)
-{
-	struct hp_sw_context *h = hwh->context;
-
-	kfree(h);
-}
-
-static struct hw_handler_type hp_sw_hwh = {
-	.name = DM_HP_HWH_NAME,
-	.module = THIS_MODULE,
-	.create = hp_sw_create,
-	.destroy = hp_sw_destroy,
-	.pg_init = hp_sw_pg_init,
-};
-
-static int __init hp_sw_init(void)
-{
-	int r;
-
-	r = dm_register_hw_handler(&hp_sw_hwh);
-	if (r < 0)
-		DMERR("register failed %d", r);
-	else
-		DMINFO("version " DM_HP_HWH_VER " loaded");
-
-	return r;
-}
-
-static void __exit hp_sw_exit(void)
-{
-	int r;
-
-	r = dm_unregister_hw_handler(&hp_sw_hwh);
-	if (r < 0)
-		DMERR("unregister failed %d", r);
-}
-
-module_init(hp_sw_init);
-module_exit(hp_sw_exit);
-
-MODULE_DESCRIPTION("DM Multipath HP StorageWorks / FSC FibreCat (A/P) support");
-MODULE_AUTHOR("Mike Christie, Dave Wysochanski <dm-devel@redhat.com>");
-MODULE_LICENSE("GPL");
-MODULE_VERSION(DM_HP_HWH_VER);
Index: linux-2.6.25-rc8-mm2/drivers/md/dm-mpath-rdac.c
===================================================================
--- linux-2.6.25-rc8-mm2.orig/drivers/md/dm-mpath-rdac.c
+++ /dev/null
@@ -1,701 +0,0 @@
-/*
- * Engenio/LSI RDAC DM HW handler
- *
- * Copyright (C) 2005 Mike Christie. All rights reserved.
- * Copyright (C) Chandra Seetharaman, IBM Corp. 2007
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
- *
- */
-#include <scsi/scsi.h>
-#include <scsi/scsi_cmnd.h>
-#include <scsi/scsi_eh.h>
-
-#define DM_MSG_PREFIX "multipath rdac"
-
-#include "dm.h"
-#include "dm-hw-handler.h"
-
-#define RDAC_DM_HWH_NAME "rdac"
-#define RDAC_DM_HWH_VER "0.4"
-
-/*
- * LSI mode page stuff
- *
- * These struct definitions and the forming of the
- * mode page were taken from the LSI RDAC 2.4 GPL'd
- * driver, and then converted to Linux conventions.
- */
-#define RDAC_QUIESCENCE_TIME 20;
-/*
- * Page Codes
- */
-#define RDAC_PAGE_CODE_REDUNDANT_CONTROLLER 0x2c
-
-/*
- * Controller modes definitions
- */
-#define RDAC_MODE_TRANSFER_ALL_LUNS		0x01
-#define RDAC_MODE_TRANSFER_SPECIFIED_LUNS	0x02
-
-/*
- * RDAC Options field
- */
-#define RDAC_FORCED_QUIESENCE 0x02
-
-#define RDAC_FAILOVER_TIMEOUT (60 * HZ)
-
-struct rdac_mode_6_hdr {
-	u8	data_len;
-	u8	medium_type;
-	u8	device_params;
-	u8	block_desc_len;
-};
-
-struct rdac_mode_10_hdr {
-	u16	data_len;
-	u8	medium_type;
-	u8	device_params;
-	u16	reserved;
-	u16	block_desc_len;
-};
-
-struct rdac_mode_common {
-	u8	controller_serial[16];
-	u8	alt_controller_serial[16];
-	u8	rdac_mode[2];
-	u8	alt_rdac_mode[2];
-	u8	quiescence_timeout;
-	u8	rdac_options;
-};
-
-struct rdac_pg_legacy {
-	struct rdac_mode_6_hdr hdr;
-	u8	page_code;
-	u8	page_len;
-	struct rdac_mode_common common;
-#define MODE6_MAX_LUN	32
-	u8	lun_table[MODE6_MAX_LUN];
-	u8	reserved2[32];
-	u8	reserved3;
-	u8	reserved4;
-};
-
-struct rdac_pg_expanded {
-	struct rdac_mode_10_hdr hdr;
-	u8	page_code;
-	u8	subpage_code;
-	u8	page_len[2];
-	struct rdac_mode_common common;
-	u8	lun_table[256];
-	u8	reserved3;
-	u8	reserved4;
-};
-
-struct c9_inquiry {
-	u8	peripheral_info;
-	u8	page_code;	/* 0xC9 */
-	u8	reserved1;
-	u8	page_len;
-	u8	page_id[4];	/* "vace" */
-	u8	avte_cvp;
-	u8	path_prio;
-	u8	reserved2[38];
-};
-
-#define SUBSYS_ID_LEN	16
-#define SLOT_ID_LEN	2
-
-struct c4_inquiry {
-	u8	peripheral_info;
-	u8	page_code;	/* 0xC4 */
-	u8	reserved1;
-	u8	page_len;
-	u8	page_id[4];	/* "subs" */
-	u8	subsys_id[SUBSYS_ID_LEN];
-	u8	revision[4];
-	u8	slot_id[SLOT_ID_LEN];
-	u8	reserved[2];
-};
-
-struct rdac_controller {
-	u8			subsys_id[SUBSYS_ID_LEN];
-	u8			slot_id[SLOT_ID_LEN];
-	int			use_10_ms;
-	struct kref		kref;
-	struct list_head	node; /* list of all controllers */
-	spinlock_t		lock;
-	int			submitted;
-	struct list_head	cmd_list; /* list of commands to be submitted */
-	union			{
-		struct rdac_pg_legacy legacy;
-		struct rdac_pg_expanded expanded;
-	} mode_select;
-};
-struct c8_inquiry {
-	u8	peripheral_info;
-	u8	page_code; /* 0xC8 */
-	u8	reserved1;
-	u8	page_len;
-	u8	page_id[4]; /* "edid" */
-	u8	reserved2[3];
-	u8	vol_uniq_id_len;
-	u8	vol_uniq_id[16];
-	u8	vol_user_label_len;
-	u8	vol_user_label[60];
-	u8	array_uniq_id_len;
-	u8	array_unique_id[16];
-	u8	array_user_label_len;
-	u8	array_user_label[60];
-	u8	lun[8];
-};
-
-struct c2_inquiry {
-	u8	peripheral_info;
-	u8	page_code;	/* 0xC2 */
-	u8	reserved1;
-	u8	page_len;
-	u8	page_id[4];	/* "swr4" */
-	u8	sw_version[3];
-	u8	sw_date[3];
-	u8	features_enabled;
-	u8	max_lun_supported;
-	u8	partitions[239]; /* Total allocation length should be 0xFF */
-};
-
-struct rdac_handler {
-	struct list_head	entry; /* list waiting to submit MODE SELECT */
-	unsigned		timeout;
-	struct rdac_controller	*ctlr;
-#define UNINITIALIZED_LUN	(1 << 8)
-	unsigned		lun;
-	unsigned char		sense[SCSI_SENSE_BUFFERSIZE];
-	struct dm_path		*path;
-	struct work_struct	work;
-#define	SEND_C2_INQUIRY		1
-#define	SEND_C4_INQUIRY		2
-#define	SEND_C8_INQUIRY		3
-#define	SEND_C9_INQUIRY		4
-#define	SEND_MODE_SELECT	5
-	int			cmd_to_send;
-	union			{
-		struct c2_inquiry c2;
-		struct c4_inquiry c4;
-		struct c8_inquiry c8;
-		struct c9_inquiry c9;
-	} inq;
-};
-
-static LIST_HEAD(ctlr_list);
-static DEFINE_SPINLOCK(list_lock);
-static struct workqueue_struct *rdac_wkqd;
-
-static inline int had_failures(struct request *req, int error)
-{
-	return (error || host_byte(req->errors) != DID_OK ||
-			msg_byte(req->errors) != COMMAND_COMPLETE);
-}
-
-static void rdac_resubmit_all(struct rdac_handler *h)
-{
-	struct rdac_controller *ctlr = h->ctlr;
-	struct rdac_handler *tmp, *h1;
-
-	spin_lock(&ctlr->lock);
-	list_for_each_entry_safe(h1, tmp, &ctlr->cmd_list, entry) {
-		h1->cmd_to_send = SEND_C9_INQUIRY;
-		queue_work(rdac_wkqd, &h1->work);
-		list_del(&h1->entry);
-	}
-	ctlr->submitted = 0;
-	spin_unlock(&ctlr->lock);
-}
-
-static void mode_select_endio(struct request *req, int error)
-{
-	struct rdac_handler *h = req->end_io_data;
-	struct scsi_sense_hdr sense_hdr;
-	int sense = 0, fail = 0;
-
-	if (had_failures(req, error)) {
-		fail = 1;
-		goto failed;
-	}
-
-	if (status_byte(req->errors) == CHECK_CONDITION) {
-		scsi_normalize_sense(req->sense, SCSI_SENSE_BUFFERSIZE,
-				&sense_hdr);
-		sense = (sense_hdr.sense_key << 16) | (sense_hdr.asc << 8) |
-				sense_hdr.ascq;
-		/* If it is retryable failure, submit the c9 inquiry again */
-		if (sense == 0x59136 || sense == 0x68b02 || sense == 0xb8b02 ||
-		    sense == 0x62900) {
-			/* 0x59136    - Command lock contention
-			 * 0x[6b]8b02 - Quiesense in progress or achieved
-			 * 0x62900    - Power On, Reset, or Bus Device Reset
-			 */
-			h->cmd_to_send = SEND_C9_INQUIRY;
-			queue_work(rdac_wkqd, &h->work);
-			goto done;
-		}
-		if (sense)
-			DMINFO("MODE_SELECT failed on %s with sense 0x%x",
-						h->path->dev->name, sense);
- 	}
-failed:
-	if (fail || sense)
-		dm_pg_init_complete(h->path, MP_FAIL_PATH);
-	else
-		dm_pg_init_complete(h->path, 0);
-
-done:
-	rdac_resubmit_all(h);
-	__blk_put_request(req->q, req);
-}
-
-static struct request *get_rdac_req(struct rdac_handler *h,
-			void *buffer, unsigned buflen, int rw)
-{
-	struct request *rq;
-	struct request_queue *q = bdev_get_queue(h->path->dev->bdev);
-
-	rq = blk_get_request(q, rw, GFP_KERNEL);
-
-	if (!rq) {
-		DMINFO("get_rdac_req: blk_get_request failed");
-		return NULL;
-	}
-
-	if (buflen && blk_rq_map_kern(q, rq, buffer, buflen, GFP_KERNEL)) {
-		blk_put_request(rq);
-		DMINFO("get_rdac_req: blk_rq_map_kern failed");
-		return NULL;
-	}
-
- 	memset(&rq->cmd, 0, BLK_MAX_CDB);
-	rq->sense = h->sense;
-	memset(rq->sense, 0, SCSI_SENSE_BUFFERSIZE);
-	rq->sense_len = 0;
-
-	rq->end_io_data = h;
-	rq->timeout = h->timeout;
-	rq->cmd_type = REQ_TYPE_BLOCK_PC;
-	rq->cmd_flags |= REQ_FAILFAST | REQ_NOMERGE;
-	return rq;
-}
-
-static struct request *rdac_failover_get(struct rdac_handler *h)
-{
-	struct request *rq;
-	struct rdac_mode_common *common;
-	unsigned data_size;
-
-	if (h->ctlr->use_10_ms) {
-		struct rdac_pg_expanded *rdac_pg;
-
-		data_size = sizeof(struct rdac_pg_expanded);
-		rdac_pg = &h->ctlr->mode_select.expanded;
-		memset(rdac_pg, 0, data_size);
-		common = &rdac_pg->common;
-		rdac_pg->page_code = RDAC_PAGE_CODE_REDUNDANT_CONTROLLER + 0x40;
-		rdac_pg->subpage_code = 0x1;
-		rdac_pg->page_len[0] = 0x01;
-		rdac_pg->page_len[1] = 0x28;
-		rdac_pg->lun_table[h->lun] = 0x81;
-	} else {
-		struct rdac_pg_legacy *rdac_pg;
-
-		data_size = sizeof(struct rdac_pg_legacy);
-		rdac_pg = &h->ctlr->mode_select.legacy;
-		memset(rdac_pg, 0, data_size);
-		common = &rdac_pg->common;
-		rdac_pg->page_code = RDAC_PAGE_CODE_REDUNDANT_CONTROLLER;
-		rdac_pg->page_len = 0x68;
-		rdac_pg->lun_table[h->lun] = 0x81;
-	}
-	common->rdac_mode[1] = RDAC_MODE_TRANSFER_SPECIFIED_LUNS;
-	common->quiescence_timeout = RDAC_QUIESCENCE_TIME;
-	common->rdac_options = RDAC_FORCED_QUIESENCE;
-
-	/* get request for block layer packet command */
-	rq = get_rdac_req(h, &h->ctlr->mode_select, data_size, WRITE);
-	if (!rq) {
-		DMERR("rdac_failover_get: no rq");
-		return NULL;
-	}
-
-	/* Prepare the command. */
-	if (h->ctlr->use_10_ms) {
-		rq->cmd[0] = MODE_SELECT_10;
-		rq->cmd[7] = data_size >> 8;
-		rq->cmd[8] = data_size & 0xff;
-	} else {
-		rq->cmd[0] = MODE_SELECT;
-		rq->cmd[4] = data_size;
-	}
-	rq->cmd_len = COMMAND_SIZE(rq->cmd[0]);
-
-	return rq;
-}
-
-/* Acquires h->ctlr->lock */
-static void submit_mode_select(struct rdac_handler *h)
-{
-	struct request *rq;
-	struct request_queue *q = bdev_get_queue(h->path->dev->bdev);
-
-	spin_lock(&h->ctlr->lock);
-	if (h->ctlr->submitted) {
-		list_add(&h->entry, &h->ctlr->cmd_list);
-		goto drop_lock;
-	}
-
-	if (!q) {
-		DMINFO("submit_mode_select: no queue");
-		goto fail_path;
-	}
-
-	rq = rdac_failover_get(h);
-	if (!rq) {
-		DMERR("submit_mode_select: no rq");
-		goto fail_path;
-	}
-
-	DMINFO("queueing MODE_SELECT command on %s", h->path->dev->name);
-
-	blk_execute_rq_nowait(q, NULL, rq, 1, mode_select_endio);
-	h->ctlr->submitted = 1;
-	goto drop_lock;
-fail_path:
-	dm_pg_init_complete(h->path, MP_FAIL_PATH);
-drop_lock:
-	spin_unlock(&h->ctlr->lock);
-}
-
-static void release_ctlr(struct kref *kref)
-{
-	struct rdac_controller *ctlr;
-	ctlr = container_of(kref, struct rdac_controller, kref);
-
-	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)
-{
-	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)) {
-			kref_get(&tmp->kref);
-			spin_unlock(&list_lock);
-			return tmp;
-		}
-	}
-	ctlr = kmalloc(sizeof(*ctlr), GFP_ATOMIC);
-	if (!ctlr)
-		goto done;
-
-	/* initialize fields of controller */
-	memcpy(ctlr->subsys_id, subsys_id, SUBSYS_ID_LEN);
-	memcpy(ctlr->slot_id, slot_id, SLOT_ID_LEN);
-	kref_init(&ctlr->kref);
-	spin_lock_init(&ctlr->lock);
-	ctlr->submitted = 0;
-	ctlr->use_10_ms = -1;
-	INIT_LIST_HEAD(&ctlr->cmd_list);
-	list_add(&ctlr->node, &ctlr_list);
-done:
-	spin_unlock(&list_lock);
-	return ctlr;
-}
-
-static void c4_endio(struct request *req, int error)
-{
-	struct rdac_handler *h = req->end_io_data;
-	struct c4_inquiry *sp;
-
-	if (had_failures(req, error)) {
-		dm_pg_init_complete(h->path, MP_FAIL_PATH);
-		goto done;
-	}
-
-	sp = &h->inq.c4;
-
-	h->ctlr = get_controller(sp->subsys_id, sp->slot_id);
-
-	if (h->ctlr) {
-		h->cmd_to_send = SEND_C9_INQUIRY;
-		queue_work(rdac_wkqd, &h->work);
-	} else
-		dm_pg_init_complete(h->path, MP_FAIL_PATH);
-done:
-	__blk_put_request(req->q, req);
-}
-
-static void c2_endio(struct request *req, int error)
-{
-	struct rdac_handler *h = req->end_io_data;
-	struct c2_inquiry *sp;
-
-	if (had_failures(req, error)) {
-		dm_pg_init_complete(h->path, MP_FAIL_PATH);
-		goto done;
-	}
-
-	sp = &h->inq.c2;
-
-	/* If more than MODE6_MAX_LUN luns are supported, use mode select 10 */
-	if (sp->max_lun_supported >= MODE6_MAX_LUN)
-		h->ctlr->use_10_ms = 1;
-	else
-		h->ctlr->use_10_ms = 0;
-
-	h->cmd_to_send = SEND_MODE_SELECT;
-	queue_work(rdac_wkqd, &h->work);
-done:
-	__blk_put_request(req->q, req);
-}
-
-static void c9_endio(struct request *req, int error)
-{
-	struct rdac_handler *h = req->end_io_data;
-	struct c9_inquiry *sp;
-
-	if (had_failures(req, error)) {
-		dm_pg_init_complete(h->path, MP_FAIL_PATH);
-		goto done;
-	}
-
-	/* We need to look at the sense keys here to take clear action.
-	 * For now simple logic: If the host is in AVT mode or if controller
-	 * owns the lun, return dm_pg_init_complete(), otherwise submit
-	 * MODE SELECT.
-	 */
-	sp = &h->inq.c9;
-
-	/* If in AVT mode, return success */
-	if ((sp->avte_cvp >> 7) == 0x1) {
-		dm_pg_init_complete(h->path, 0);
-		goto done;
-	}
-
-	/* If the controller on this path owns the LUN, return success */
-	if (sp->avte_cvp & 0x1) {
-		dm_pg_init_complete(h->path, 0);
-		goto done;
-	}
-
-	if (h->ctlr) {
-		if (h->ctlr->use_10_ms == -1)
-			h->cmd_to_send = SEND_C2_INQUIRY;
-		else
-			h->cmd_to_send = SEND_MODE_SELECT;
-	} else
-		h->cmd_to_send = SEND_C4_INQUIRY;
-	queue_work(rdac_wkqd, &h->work);
-done:
-	__blk_put_request(req->q, req);
-}
-
-static void c8_endio(struct request *req, int error)
-{
-	struct rdac_handler *h = req->end_io_data;
-	struct c8_inquiry *sp;
-
-	if (had_failures(req, error)) {
-		dm_pg_init_complete(h->path, MP_FAIL_PATH);
-		goto done;
-	}
-
-	/* We need to look at the sense keys here to take clear action.
-	 * For now simple logic: Get the lun from the inquiry page.
-	 */
-	sp = &h->inq.c8;
-	h->lun = sp->lun[7]; /* currently it uses only one byte */
-	h->cmd_to_send = SEND_C9_INQUIRY;
-	queue_work(rdac_wkqd, &h->work);
-done:
-	__blk_put_request(req->q, req);
-}
-
-static void submit_inquiry(struct rdac_handler *h, int page_code,
-		unsigned int len, rq_end_io_fn endio)
-{
-	struct request *rq;
-	struct request_queue *q = bdev_get_queue(h->path->dev->bdev);
-
-	if (!q)
-		goto fail_path;
-
-	rq = get_rdac_req(h, &h->inq, len, READ);
-	if (!rq)
-		goto fail_path;
-
-	/* Prepare the command. */
-	rq->cmd[0] = INQUIRY;
-	rq->cmd[1] = 1;
-	rq->cmd[2] = page_code;
-	rq->cmd[4] = len;
-	rq->cmd_len = COMMAND_SIZE(INQUIRY);
-	blk_execute_rq_nowait(q, NULL, rq, 1, endio);
-	return;
-
-fail_path:
-	dm_pg_init_complete(h->path, MP_FAIL_PATH);
-}
-
-static void service_wkq(struct work_struct *work)
-{
-	struct rdac_handler *h = container_of(work, struct rdac_handler, work);
-
-	switch (h->cmd_to_send) {
-	case SEND_C2_INQUIRY:
-		submit_inquiry(h, 0xC2, sizeof(struct c2_inquiry), c2_endio);
-		break;
-	case SEND_C4_INQUIRY:
-		submit_inquiry(h, 0xC4, sizeof(struct c4_inquiry), c4_endio);
-		break;
-	case SEND_C8_INQUIRY:
-		submit_inquiry(h, 0xC8, sizeof(struct c8_inquiry), c8_endio);
-		break;
-	case SEND_C9_INQUIRY:
-		submit_inquiry(h, 0xC9, sizeof(struct c9_inquiry), c9_endio);
-		break;
-	case SEND_MODE_SELECT:
-		submit_mode_select(h);
-		break;
-	default:
-		BUG();
-	}
-}
-/*
- * only support subpage2c until we confirm that this is just a matter of
- * of updating firmware or not, and RDAC (basic AVT works already) for now
- * but we can add these in in when we get time and testers
- */
-static int rdac_create(struct hw_handler *hwh, unsigned argc, char **argv)
-{
-	struct rdac_handler *h;
-	unsigned timeout;
-
-	if (argc == 0) {
-		/* No arguments: use defaults */
-		timeout = RDAC_FAILOVER_TIMEOUT;
-	} else if (argc != 1) {
-		DMWARN("incorrect number of arguments");
-		return -EINVAL;
-	} else {
-		if (sscanf(argv[1], "%u", &timeout) != 1) {
-			DMWARN("invalid timeout value");
-			return -EINVAL;
-		}
-	}
-
-	h = kzalloc(sizeof(*h), GFP_KERNEL);
-	if (!h)
-		return -ENOMEM;
-
-	hwh->context = h;
-	h->timeout = timeout;
-	h->lun = UNINITIALIZED_LUN;
-	INIT_WORK(&h->work, service_wkq);
-	DMWARN("using RDAC command with timeout %u", h->timeout);
-
-	return 0;
-}
-
-static void rdac_destroy(struct hw_handler *hwh)
-{
-	struct rdac_handler *h = hwh->context;
-
-	if (h->ctlr)
-		kref_put(&h->ctlr->kref, release_ctlr);
-	kfree(h);
-	hwh->context = NULL;
-}
-
-static unsigned rdac_error(struct hw_handler *hwh, struct bio *bio)
-{
-	/* Try default handler */
-	return dm_scsi_err_handler(hwh, bio);
-}
-
-static void rdac_pg_init(struct hw_handler *hwh, unsigned bypassed,
-			struct dm_path *path)
-{
-	struct rdac_handler *h = hwh->context;
-
-	h->path = path;
-	switch (h->lun) {
-	case UNINITIALIZED_LUN:
-		submit_inquiry(h, 0xC8, sizeof(struct c8_inquiry), c8_endio);
-		break;
-	default:
-		submit_inquiry(h, 0xC9, sizeof(struct c9_inquiry), c9_endio);
-	}
-}
-
-static struct hw_handler_type rdac_handler = {
-	.name = RDAC_DM_HWH_NAME,
-	.module = THIS_MODULE,
-	.create = rdac_create,
-	.destroy = rdac_destroy,
-	.pg_init = rdac_pg_init,
-	.error = rdac_error,
-};
-
-static int __init rdac_init(void)
-{
-	int r;
-
-	rdac_wkqd = create_singlethread_workqueue("rdac_wkqd");
-	if (!rdac_wkqd) {
-		DMERR("Failed to create workqueue rdac_wkqd.");
-		return -ENOMEM;
-	}
-
-	r = dm_register_hw_handler(&rdac_handler);
-	if (r < 0) {
-		DMERR("%s: register failed %d", RDAC_DM_HWH_NAME, r);
-		destroy_workqueue(rdac_wkqd);
-		return r;
-	}
-
-	DMINFO("%s: version %s loaded", RDAC_DM_HWH_NAME, RDAC_DM_HWH_VER);
-	return 0;
-}
-
-static void __exit rdac_exit(void)
-{
-	int r = dm_unregister_hw_handler(&rdac_handler);
-
-	destroy_workqueue(rdac_wkqd);
-	if (r < 0)
-		DMERR("%s: unregister failed %d", RDAC_DM_HWH_NAME, r);
-}
-
-module_init(rdac_init);
-module_exit(rdac_exit);
-
-MODULE_DESCRIPTION("DM Multipath LSI/Engenio RDAC support");
-MODULE_AUTHOR("Mike Christie, Chandra Seetharaman");
-MODULE_LICENSE("GPL");
-MODULE_VERSION(RDAC_DM_HWH_VER);

^ permalink raw reply	[flat|nested] 17+ messages in thread

* [PATCH 7/7] scsi_dh: Remove hardware handler infrastructure from dm
  2008-04-17 22:22 [PATCH 0/7] scsi_dh: Move hardware handlers from dm to SCSI Chandra Seetharaman
                   ` (5 preceding siblings ...)
  2008-04-17 22:23 ` [PATCH 6/7] scsi_dh: Remove hardware handlers from dm Chandra Seetharaman
@ 2008-04-17 22:23 ` Chandra Seetharaman
  6 siblings, 0 replies; 17+ messages in thread
From: Chandra Seetharaman @ 2008-04-17 22:23 UTC (permalink / raw)
  To: linux-scsi
  Cc: dm-devel, andmike, michaelc, agk, James.Bottomley, jens.axboe,
	dwysocha, Benoit_Arthur, asson_ronald, Chandra Seetharaman

Subject: scsi_dh: Remove hardware handler infrastructure from dm

From: Chandra Seetharaman <sekharan@us.ibm.com>

This patch just removes infrastructure that provide support to add
a hardware handler to dm.

Signed-off-by: Chandra Seetharaman <sekharan@us.ibm.com>
---
 drivers/md/Makefile        |    2 	1 +	1 -	0 !
 drivers/md/dm-hw-handler.c |  213 	0 +	213 -	0 !
 drivers/md/dm-hw-handler.h |   63 	0 +	63 -	0 !
 drivers/md/dm-mpath.h      |    1 	0 +	1 -	0 !
 4 files changed, 1 insertion(+), 278 deletions(-)

Index: linux-2.6.25-rc8-mm2/drivers/md/dm-hw-handler.c
===================================================================
--- linux-2.6.25-rc8-mm2.orig/drivers/md/dm-hw-handler.c
+++ /dev/null
@@ -1,213 +0,0 @@
-/*
- * Copyright (C) 2004 Red Hat, Inc. All rights reserved.
- *
- * This file is released under the GPL.
- *
- * Multipath hardware handler registration.
- */
-
-#include "dm.h"
-#include "dm-hw-handler.h"
-
-#include <linux/slab.h>
-
-struct hwh_internal {
-	struct hw_handler_type hwht;
-
-	struct list_head list;
-	long use;
-};
-
-#define hwht_to_hwhi(__hwht) container_of((__hwht), struct hwh_internal, hwht)
-
-static LIST_HEAD(_hw_handlers);
-static DECLARE_RWSEM(_hwh_lock);
-
-static struct hwh_internal *__find_hw_handler_type(const char *name)
-{
-	struct hwh_internal *hwhi;
-
-	list_for_each_entry(hwhi, &_hw_handlers, list) {
-		if (!strcmp(name, hwhi->hwht.name))
-			return hwhi;
-	}
-
-	return NULL;
-}
-
-static struct hwh_internal *get_hw_handler(const char *name)
-{
-	struct hwh_internal *hwhi;
-
-	down_read(&_hwh_lock);
-	hwhi = __find_hw_handler_type(name);
-	if (hwhi) {
-		if ((hwhi->use == 0) && !try_module_get(hwhi->hwht.module))
-			hwhi = NULL;
-		else
-			hwhi->use++;
-	}
-	up_read(&_hwh_lock);
-
-	return hwhi;
-}
-
-struct hw_handler_type *dm_get_hw_handler(const char *name)
-{
-	struct hwh_internal *hwhi;
-
-	if (!name)
-		return NULL;
-
-	hwhi = get_hw_handler(name);
-	if (!hwhi) {
-		request_module("dm-%s", name);
-		hwhi = get_hw_handler(name);
-	}
-
-	return hwhi ? &hwhi->hwht : NULL;
-}
-
-void dm_put_hw_handler(struct hw_handler_type *hwht)
-{
-	struct hwh_internal *hwhi;
-
-	if (!hwht)
-		return;
-
-	down_read(&_hwh_lock);
-	hwhi = __find_hw_handler_type(hwht->name);
-	if (!hwhi)
-		goto out;
-
-	if (--hwhi->use == 0)
-		module_put(hwhi->hwht.module);
-
-	BUG_ON(hwhi->use < 0);
-
-      out:
-	up_read(&_hwh_lock);
-}
-
-static struct hwh_internal *_alloc_hw_handler(struct hw_handler_type *hwht)
-{
-	struct hwh_internal *hwhi = kzalloc(sizeof(*hwhi), GFP_KERNEL);
-
-	if (hwhi)
-		hwhi->hwht = *hwht;
-
-	return hwhi;
-}
-
-int dm_register_hw_handler(struct hw_handler_type *hwht)
-{
-	int r = 0;
-	struct hwh_internal *hwhi = _alloc_hw_handler(hwht);
-
-	if (!hwhi)
-		return -ENOMEM;
-
-	down_write(&_hwh_lock);
-
-	if (__find_hw_handler_type(hwht->name)) {
-		kfree(hwhi);
-		r = -EEXIST;
-	} else
-		list_add(&hwhi->list, &_hw_handlers);
-
-	up_write(&_hwh_lock);
-
-	return r;
-}
-
-int dm_unregister_hw_handler(struct hw_handler_type *hwht)
-{
-	struct hwh_internal *hwhi;
-
-	down_write(&_hwh_lock);
-
-	hwhi = __find_hw_handler_type(hwht->name);
-	if (!hwhi) {
-		up_write(&_hwh_lock);
-		return -EINVAL;
-	}
-
-	if (hwhi->use) {
-		up_write(&_hwh_lock);
-		return -ETXTBSY;
-	}
-
-	list_del(&hwhi->list);
-
-	up_write(&_hwh_lock);
-
-	kfree(hwhi);
-
-	return 0;
-}
-
-unsigned dm_scsi_err_handler(struct hw_handler *hwh, struct bio *bio)
-{
-#if 0
-	int sense_key, asc, ascq;
-
-	if (bio->bi_error & BIO_SENSE) {
-		/* FIXME: This is just an initial guess. */
-		/* key / asc / ascq */
-		sense_key = (bio->bi_error >> 16) & 0xff;
-		asc = (bio->bi_error >> 8) & 0xff;
-		ascq = bio->bi_error & 0xff;
-
-		switch (sense_key) {
-			/* This block as a whole comes from the device.
-			 * So no point retrying on another path. */
-		case 0x03:	/* Medium error */
-		case 0x05:	/* Illegal request */
-		case 0x07:	/* Data protect */
-		case 0x08:	/* Blank check */
-		case 0x0a:	/* copy aborted */
-		case 0x0c:	/* obsolete - no clue ;-) */
-		case 0x0d:	/* volume overflow */
-		case 0x0e:	/* data miscompare */
-		case 0x0f:	/* reserved - no idea either. */
-			return MP_ERROR_IO;
-
-			/* For these errors it's unclear whether they
-			 * come from the device or the controller.
-			 * So just lets try a different path, and if
-			 * it eventually succeeds, user-space will clear
-			 * the paths again... */
-		case 0x02:	/* Not ready */
-		case 0x04:	/* Hardware error */
-		case 0x09:	/* vendor specific */
-		case 0x0b:	/* Aborted command */
-			return MP_FAIL_PATH;
-
-		case 0x06:	/* Unit attention - might want to decode */
-			if (asc == 0x04 && ascq == 0x01)
-				/* "Unit in the process of
-				 * becoming ready" */
-				return 0;
-			return MP_FAIL_PATH;
-
-			/* FIXME: For Unit Not Ready we may want
-			 * to have a generic pg activation
-			 * feature (START_UNIT). */
-
-			/* Should these two ever end up in the
-			 * error path? I don't think so. */
-		case 0x00:	/* No sense */
-		case 0x01:	/* Recovered error */
-			return 0;
-		}
-	}
-#endif
-
-	/* We got no idea how to decode the other kinds of errors ->
-	 * assume generic error condition. */
-	return MP_FAIL_PATH;
-}
-
-EXPORT_SYMBOL_GPL(dm_register_hw_handler);
-EXPORT_SYMBOL_GPL(dm_unregister_hw_handler);
-EXPORT_SYMBOL_GPL(dm_scsi_err_handler);
Index: linux-2.6.25-rc8-mm2/drivers/md/dm-hw-handler.h
===================================================================
--- linux-2.6.25-rc8-mm2.orig/drivers/md/dm-hw-handler.h
+++ /dev/null
@@ -1,63 +0,0 @@
-/*
- * Copyright (C) 2004 Red Hat, Inc. All rights reserved.
- *
- * This file is released under the GPL.
- *
- * Multipath hardware handler registration.
- */
-
-#ifndef	DM_HW_HANDLER_H
-#define	DM_HW_HANDLER_H
-
-#include <linux/device-mapper.h>
-
-#include "dm-mpath.h"
-
-struct hw_handler_type;
-struct hw_handler {
-	struct hw_handler_type *type;
-	struct mapped_device *md;
-	void *context;
-};
-
-/*
- * Constructs a hardware handler object, takes custom arguments
- */
-/* Information about a hardware handler type */
-struct hw_handler_type {
-	char *name;
-	struct module *module;
-
-	int (*create) (struct hw_handler *handler, unsigned int argc,
-		       char **argv);
-	void (*destroy) (struct hw_handler *hwh);
-
-	void (*pg_init) (struct hw_handler *hwh, unsigned bypassed,
-			 struct dm_path *path);
-	unsigned (*error) (struct hw_handler *hwh, struct bio *bio);
-	int (*status) (struct hw_handler *hwh, status_type_t type,
-		       char *result, unsigned int maxlen);
-};
-
-/* Register a hardware handler */
-int dm_register_hw_handler(struct hw_handler_type *type);
-
-/* Unregister a hardware handler */
-int dm_unregister_hw_handler(struct hw_handler_type *type);
-
-/* Returns a registered hardware handler type */
-struct hw_handler_type *dm_get_hw_handler(const char *name);
-
-/* Releases a hardware handler  */
-void dm_put_hw_handler(struct hw_handler_type *hwht);
-
-/* Default err function */
-unsigned dm_scsi_err_handler(struct hw_handler *hwh, struct bio *bio);
-
-/* Error flags for err and dm_pg_init_complete */
-#define MP_FAIL_PATH 1
-#define MP_BYPASS_PG 2
-#define MP_ERROR_IO  4	/* Don't retry this I/O */
-#define MP_RETRY 8
-
-#endif
Index: linux-2.6.25-rc8-mm2/drivers/md/Makefile
===================================================================
--- linux-2.6.25-rc8-mm2.orig/drivers/md/Makefile
+++ linux-2.6.25-rc8-mm2/drivers/md/Makefile
@@ -4,7 +4,7 @@
 
 dm-mod-objs	:= dm.o dm-table.o dm-target.o dm-linear.o dm-stripe.o \
 		   dm-ioctl.o dm-io.o dm-kcopyd.o
-dm-multipath-objs := dm-hw-handler.o dm-path-selector.o dm-mpath.o
+dm-multipath-objs := dm-path-selector.o dm-mpath.o
 dm-snapshot-objs := dm-snap.o dm-exception-store.o
 dm-mirror-objs	:= dm-raid1.o
 dm-rdac-objs	:= dm-mpath-rdac.o
Index: linux-2.6.25-rc8-mm2/drivers/md/dm-mpath.h
===================================================================
--- linux-2.6.25-rc8-mm2.orig/drivers/md/dm-mpath.h
+++ linux-2.6.25-rc8-mm2/drivers/md/dm-mpath.h
@@ -16,7 +16,6 @@ struct dm_path {
 	unsigned is_active;	/* Read-only */
 
 	void *pscontext;	/* For path-selector use */
-	void *hwhcontext;	/* For hw-handler use */
 };
 
 /* Callback for hwh_pg_init_fn to use when complete */

^ permalink raw reply	[flat|nested] 17+ messages in thread

* Re: [PATCH 4/7] scsi_dh: add EMC Clariion device handler
  2008-04-22 21:09         ` [dm-devel] " Chandra Seetharaman
@ 2008-04-22 21:47           ` Mike Christie
  2008-04-22 21:50             ` Mike Christie
  0 siblings, 1 reply; 17+ messages in thread
From: Mike Christie @ 2008-04-22 21:47 UTC (permalink / raw)
  To: sekharan
  Cc: andmike, linux-scsi, asson_ronald, James.Bottomley,
	device-mapper development, Benoit_Arthur, jens.axboe, agk

Chandra Seetharaman wrote:
> On Thu, 2008-04-17 at 12:14 -0500, Mike Christie wrote:
>> Chandra Seetharaman wrote:
>>> On Wed, 2008-04-16 at 11:29 -0500, Mike Christie wrote:
>>>> Chandra Seetharaman wrote:
>>>>> +
>>>>> +static int send_cmd(struct scsi_device *sdev, int cmd)
>>>>> +{
>>>>> +	struct request *rq = get_req(sdev, cmd);
>>>>> +
>>>>> +	if (!rq)
>>>>> +		return SCSI_DH_RES_TEMP_UNAVAIL;
>>>>> +
>>>>> +	return blk_execute_rq(sdev->request_queue, NULL, rq, 1);
>>>>> +}
>>>>> +
>>>> My only concerns are:
>>>>
>>>> 1. EMC and HP need to send a command to every device to transition them. 
>>>> Because we do blk_execute_rq from the dm multipath workqueue we can now 
>>>> only failover/failback for a couple devices at a time.
>>>>
>>>> I am not sure if this is a big deal, because this the error handler path 
>>>> so it is going to be slower than the normal path. But it seems like 
>>> Yes. But...
>>>
>>> pg_init() due to failover/failback will be sent only when I/O is
>>> sent/resent to a multipath device, isn't it ? and we don't expect I/Os
>>> to be sent to all the devices at the same time (all the time), do we ?
>>>
>> I am not sure what you mean by all the time, because I am talking about 
> 
> What I meant was that we do not expect I/Os to be sent to all the
> devices at all the times (pg_init will be sent only when I/Os fails on a
> path, right ?).
> 
> Sorry for not being clear.
> 

No problem.


>> failover times above. And for failover I think I said yes in the 
>> previous mail. For EMC we are currently sending failover commands to all 
>> the devices at the same time, because EMC does not do the controller 
>> failover RDAC does.
> 
> RDAC doesn't do controller failover. It also does per lun failover.
> 

Oh yeah, I forgot.


>>> So, as you pointed, is it a big deal ? :)
>>>
>> In the previous mail I specifically said users might care, because they 
>> are picky about failover times, real    3m39.728s
> user    0m4.135s
> sys     0m14.536s
> 
>> so the answer is to your question is 
>> what I said before, maybe :) I said I am not sure, because I do not have 
>> any numbers for the failover times.
> 
> Since RDAC also does the failover per device (as is the case with EMC),
> I ran tests on about 49 luns. I ran disktest on all the disks at the

Thanks.

> same time and disabled/enabled the port to the preferred path to
> generate failover and failback.
> 
> Let me know what do you think.
> 
> Here are the results:
> Tests run in an idle system. With 49 luns and the following script:
> ******************************************************
> for i in `ls -1 /dev/mapper/mpath*`
> do
> 	disktest $i -L 4000 -t 100 -P X &
> 	sleep 1
> done
> 
> wait
> ******************************************************
> Simple Run:
> 
> with patchset:		2.6.25-mm1:
> real    3m30.122s	real    3m29.746s
> user    0m4.069s	user    0m4.099s
> sys     0m14.876s	sys     0m14.535s
> -----------------------------------------------

Is this just a boot up test or a test just running IO but no 
failback/failover?

> 
> Failover Run:
> 
> with patchset:		2.6.25-mm1:
> real    5m18.875s	real    5m31.741s
> user    0m4.069s	user    0m3.883s
> sys     0m14.838s	sys     0m13.822s

Ehh, I have no idea if this is good or bad. Does it mean it is talking 
13 more seconds to complete?

Have you seen the type of thread on dm-devel or the iscsi list where 
people are concerned with getting the time the failure is detected to 
the time IO is running on a new path down from something like 10 to 5 
seconds. One time the iscsi driver did not implement time2wait correctly 
and by fixing it we shaved only 2 seconds off and users were very happy 
with the extra 2 seconds. We added the nop timer stuff so we could get 
faster failovers. We have the fast io fail tmo so we can speed up the 
process even more. Shaving off a second here or there is really nitpicky 
and if I were you I would give me the middle finger :) It just seems 
like people expect better performance from this type of error.

If my comment is too nitpicky then I am fine with ignoring this for now. 
We just have to fix the emc short/long tress pass code then. I added 
another EMC guy to the thread so he can ping the other EMC devs to get 
going (I had sent them questions on how to handle it and have not got a 
response).

^ permalink raw reply	[flat|nested] 17+ messages in thread

* Re: [PATCH 4/7] scsi_dh: add EMC Clariion device handler
  2008-04-22 21:47           ` Mike Christie
@ 2008-04-22 21:50             ` Mike Christie
  0 siblings, 0 replies; 17+ messages in thread
From: Mike Christie @ 2008-04-22 21:50 UTC (permalink / raw)
  To: sekharan
  Cc: andmike, linux-scsi, asson_ronald, James.Bottomley,
	device-mapper development, Benoit_Arthur, jens.axboe, agk

Mike Christie wrote:
> Chandra Seetharaman wrote:
>> On Thu, 2008-04-17 at 12:14 -0500, Mike Christie wrote:
>>> Chandra Seetharaman wrote:
>>>> On Wed, 2008-04-16 at 11:29 -0500, Mike Christie wrote:
>>>>> Chandra Seetharaman wrote:
>>>>>> +
>>>>>> +static int send_cmd(struct scsi_device *sdev, int cmd)
>>>>>> +{
>>>>>> +    struct request *rq = get_req(sdev, cmd);
>>>>>> +
>>>>>> +    if (!rq)
>>>>>> +        return SCSI_DH_RES_TEMP_UNAVAIL;
>>>>>> +
>>>>>> +    return blk_execute_rq(sdev->request_queue, NULL, rq, 1);
>>>>>> +}
>>>>>> +
>>>>> My only concerns are:
>>>>>
>>>>> 1. EMC and HP need to send a command to every device to transition 
>>>>> them. Because we do blk_execute_rq from the dm multipath workqueue 
>>>>> we can now only failover/failback for a couple devices at a time.
>>>>>
>>>>> I am not sure if this is a big deal, because this the error handler 
>>>>> path so it is going to be slower than the normal path. But it seems 
>>>>> like 
>>>> Yes. But...
>>>>
>>>> pg_init() due to failover/failback will be sent only when I/O is
>>>> sent/resent to a multipath device, isn't it ? and we don't expect I/Os
>>>> to be sent to all the devices at the same time (all the time), do we ?
>>>>
>>> I am not sure what you mean by all the time, because I am talking about 
>>
>> What I meant was that we do not expect I/Os to be sent to all the
>> devices at all the times (pg_init will be sent only when I/Os fails on a
>> path, right ?).
>>
>> Sorry for not being clear.
>>
> 
> No problem.
> 
> 
>>> failover times above. And for failover I think I said yes in the 
>>> previous mail. For EMC we are currently sending failover commands to 
>>> all the devices at the same time, because EMC does not do the 
>>> controller failover RDAC does.
>>
>> RDAC doesn't do controller failover. It also does per lun failover.
>>
> 
> Oh yeah, I forgot.
> 
> 
>>>> So, as you pointed, is it a big deal ? :)
>>>>
>>> In the previous mail I specifically said users might care, because 
>>> they are picky about failover times, real    3m39.728s
>> user    0m4.135s
>> sys     0m14.536s
>>
>>> so the answer is to your question is what I said before, maybe :) I 
>>> said I am not sure, because I do not have any numbers for the 
>>> failover times.
>>
>> Since RDAC also does the failover per device (as is the case with EMC),
>> I ran tests on about 49 luns. I ran disktest on all the disks at the
> 
> Thanks.
> 
>> same time and disabled/enabled the port to the preferred path to
>> generate failover and failback.
>>
>> Let me know what do you think.
>>
>> Here are the results:
>> Tests run in an idle system. With 49 luns and the following script:
>> ******************************************************
>> for i in `ls -1 /dev/mapper/mpath*`
>> do
>>     disktest $i -L 4000 -t 100 -P X &
>>     sleep 1
>> done
>>
>> wait
>> ******************************************************
>> Simple Run:
>>
>> with patchset:        2.6.25-mm1:
>> real    3m30.122s    real    3m29.746s
>> user    0m4.069s    user    0m4.099s
>> sys     0m14.876s    sys     0m14.535s
>> -----------------------------------------------
> 
> Is this just a boot up test or a test just running IO but no 
> failback/failover?
> 
>>
>> Failover Run:
>>
>> with patchset:        2.6.25-mm1:
>> real    5m18.875s    real    5m31.741s
>> user    0m4.069s    user    0m3.883s
>> sys     0m14.838s    sys     0m13.822s
> 
> Ehh, I have no idea if this is good or bad. Does it mean it is talking 
> 13 more seconds to complete?
> 

Oops, I read that wrong. With the new code it is 13 seconds faster. I 
have no concerns about that.

^ permalink raw reply	[flat|nested] 17+ messages in thread

end of thread, other threads:[~2008-04-22 21:50 UTC | newest]

Thread overview: 17+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2008-04-17 22:22 [PATCH 0/7] scsi_dh: Move hardware handlers from dm to SCSI Chandra Seetharaman
2008-04-17 22:22 ` [PATCH 1/7] scsi_dh: add skeleton for SCSI Device Handlers Chandra Seetharaman
2008-04-17 22:22 ` [PATCH 2/7] scsi_dh: add lsi rdac device handler Chandra Seetharaman
2008-04-17 22:23 ` [PATCH 3/7] scsi_dh: add hp sw " Chandra Seetharaman
2008-04-17 22:23 ` [PATCH 4/7] scsi_dh: add EMC Clariion " Chandra Seetharaman
2008-04-17 22:23 ` [PATCH 5/7] scsi_dh: Use SCSI device handler in dm-multipath Chandra Seetharaman
2008-04-17 22:23 ` [PATCH 6/7] scsi_dh: Remove hardware handlers from dm Chandra Seetharaman
2008-04-17 22:23 ` [PATCH 7/7] scsi_dh: Remove hardware handler infrastructure " Chandra Seetharaman
  -- strict thread matches above, loose matches on Subject: below --
2008-04-17 21:18 [PATCH 0/7] scsi_dh: Move hardware handlers from dm to SCSI Chandra Seetharaman
2008-04-17 21:19 ` [PATCH 4/7] scsi_dh: add EMC Clariion device handler Chandra Seetharaman
2008-04-16  1:18 [PATCH 0/7] scsi_dh: Move hardware handlers from dm to SCSI Chandra Seetharaman
2008-04-16  1:18 ` [PATCH 4/7] scsi_dh: add EMC Clariion device handler Chandra Seetharaman
2008-04-16 16:29   ` Mike Christie
2008-04-16 23:59     ` [dm-devel] " Chandra Seetharaman
2008-04-17 17:14       ` Mike Christie
2008-04-22 21:09         ` [dm-devel] " Chandra Seetharaman
2008-04-22 21:47           ` Mike Christie
2008-04-22 21:50             ` Mike Christie
2008-04-01 22:51 [PATCH 0/7] scsi_dh: Move hardware handlers from dm to SCSI Chandra Seetharaman
2008-04-01 22:51 ` [PATCH 4/7] scsi_dh: add EMC Clariion device handler Chandra Seetharaman
2008-03-11  1:33 [PATCH 0/7] scsi_dh: Move hardware handlers from dm to SCSI Chandra Seetharaman
2008-03-11  1:33 ` [PATCH 4/7] scsi_dh: add EMC Clariion device handler Chandra Seetharaman
2008-02-28  1:08 [PATCH 0/7] Move hardware handlers from dm layer to SCSI layer Chandra Seetharaman
2008-02-28  1:08 ` [PATCH 4/7] scsi_dh: add EMC Clariion device handler Chandra Seetharaman

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).