source "drivers/scsi/pcmcia/Kconfig"
 
+source "drivers/scsi/device_handler/Kconfig"
+
 endmenu
 
 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
+#
+# 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
+#
+# SCSI Device Handler
+#
+obj-$(CONFIG_SCSI_DH)          += scsi_dh.o
 
--- /dev/null
+/*
+ * 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);
+
+/*
+ * scsi_dh_handler_exist - Return TRUE(1) if a device handler exists for
+ *     the given name. FALSE(0) otherwise.
+ * @name - name of the device handler.
+ */
+int scsi_dh_handler_exist(const char *name)
+{
+       return (get_device_handler(name) != NULL);
+}
+EXPORT_SYMBOL_GPL(scsi_dh_handler_exist);
+
+MODULE_DESCRIPTION("SCSI device handler");
+MODULE_AUTHOR("Chandra Seetharaman <sekharan@us.ibm.com>");
+MODULE_LICENSE("GPL");
 
  */
 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))
        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.
 
 
        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.
         */
 
        .resume         = scsi_bus_resume,
        .remove         = scsi_bus_remove,
 };
+EXPORT_SYMBOL_GPL(scsi_bus_type);
 
 int scsi_sysfs_register(void)
 {
 
 #define SOFT_ERROR      0x2005
 #define ADD_TO_MLQUEUE  0x2006
 #define TIMEOUT_ERROR   0x2007
+#define SCSI_RETURN_NOT_HANDLED   0x2008
 
 /*
  * Midlevel queue return values.
 
 
        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)        \
                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
+/*
+ * 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 *);
+extern int scsi_dh_handler_exist(const char *);