Outline EMC hw_handler.
--- diff/drivers/md/Kconfig	2004-10-29 15:37:28.000000000 +0100
+++ source/drivers/md/Kconfig	2004-10-29 15:40:09.000000000 +0100
@@ -224,5 +224,11 @@
 	---help---
 	  Allow volume managers to support multipath hardware.
 
+config DM_MULTIPATH_EMC
+	tristate "EMC multipath support (EXPERIMENTAL)"
+	depends on DM_MULTIPATH && BLK_DEV_DM && EXPERIMENTAL
+	---help---
+	  Multipath support for EMC hardware.
+
 endmenu
 
--- diff/drivers/md/Makefile	2004-10-29 15:37:46.000000000 +0100
+++ source/drivers/md/Makefile	2004-10-29 15:40:09.000000000 +0100
@@ -29,6 +29,7 @@
 obj-$(CONFIG_BLK_DEV_DM)	+= dm-mod.o
 obj-$(CONFIG_DM_CRYPT)		+= dm-crypt.o
 obj-$(CONFIG_DM_MULTIPATH)	+= dm-multipath.o dm-round-robin.o
+obj-$(CONFIG_DM_MULTIPATH_EMC)	+= dm-emc.o
 obj-$(CONFIG_DM_SNAPSHOT)	+= dm-snapshot.o
 obj-$(CONFIG_DM_MIRROR)		+= dm-mirror.o
 obj-$(CONFIG_DM_ZERO)		+= dm-zero.o
--- diff/drivers/md/dm-emc.c	1970-01-01 01:00:00.000000000 +0100
+++ source/drivers/md/dm-emc.c	2004-10-29 15:40:09.000000000 +0100
@@ -0,0 +1,125 @@
+/*
+ * Copyright (C) 2004 SUSE LINUX AG. 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"
+
+struct emc_handler {
+	spinlock_t lock;
+
+	/* FIXME Add state variables */
+	unsigned short_trespass;
+};
+
+static struct emc_handler *alloc_emc_handler(void)
+{
+	struct emc_handler *h = kmalloc(sizeof(*h), GFP_KERNEL);
+
+	if (h) {
+		h->lock = SPIN_LOCK_UNLOCKED;
+	}
+
+	return h;
+}
+
+static int emc_ctr(struct hw_handler *hwh, unsigned argc, char **argv)
+{
+	struct emc_handler *h;
+
+	h = alloc_emc_handler();
+	if (!h)
+		return -ENOMEM;
+
+	/* FIXME: Parse [0|1] parameter to send short trespass command for
+	 * FC series */
+
+	hwh->context = h;
+	return 0;
+}
+
+static void emc_dtr(struct hw_handler *hwh)
+{
+	struct emc_handler *h = (struct emc_handler *) hwh->context;
+
+	kfree(h);
+	hwh->context = NULL;
+}
+
+static unsigned emc_err(struct hw_handler *hwh, struct bio *bio)
+{
+	int sense;
+
+	/* FIXME: Patch from axboe still missing */
+#if 0
+	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,
+	.ctr = emc_ctr,
+	.dtr = emc_dtr,
+	.err = emc_err,
+};
+
+static int __init dm_emc_init(void)
+{
+	int r = dm_register_hw_handler(&emc_hwh);
+
+	if (r < 0)
+		DMERR("emc: register failed %d", r);
+
+	DMINFO("dm-emc version 0.0.1 loaded");
+
+	return r;
+}
+
+static void __exit dm_emc_exit(void)
+{
+	int r = dm_unregister_hw_handler(&emc_hwh);
+
+	if (r < 0)
+		DMERR("emc: 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");
