* [PATCH 1/7] scsi_dh: add skeleton for SCSI Device Handlers
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
2008-03-04 21:37 ` James Bottomley
0 siblings, 1 reply; 15+ 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 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 | 146 146 + 0 - 0 !
drivers/scsi/scsi_error.c | 10 10 + 0 - 0 !
drivers/scsi/scsi_lib.c | 5 5 + 0 - 0 !
include/scsi/scsi_device.h | 18 18 + 0 - 0 !
include/scsi/scsi_dh.h | 58 58 + 0 - 0 !
9 files changed, 256 insertions(+)
Index: linux-2.6.25-rc2-mm1/drivers/scsi/Kconfig
===================================================================
--- linux-2.6.25-rc2-mm1.orig/drivers/scsi/Kconfig
+++ linux-2.6.25-rc2-mm1/drivers/scsi/Kconfig
@@ -1749,4 +1749,6 @@ endif # SCSI_LOWLEVEL
source "drivers/scsi/pcmcia/Kconfig"
+source "drivers/scsi/device_handler/Kconfig"
+
endmenu
Index: linux-2.6.25-rc2-mm1/drivers/scsi/Makefile
===================================================================
--- linux-2.6.25-rc2-mm1.orig/drivers/scsi/Makefile
+++ linux-2.6.25-rc2-mm1/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
Index: linux-2.6.25-rc2-mm1/drivers/scsi/device_handler/Kconfig
===================================================================
--- /dev/null
+++ linux-2.6.25-rc2-mm1/drivers/scsi/device_handler/Kconfig
@@ -0,0 +1,12 @@
+#
+# SCSI Device Handler configuration
+#
+
+menuconfig SCSI_DH
+ bool "SCSI Device Handlers"
+ depends on SCSI!=n
+ 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.
Index: linux-2.6.25-rc2-mm1/drivers/scsi/device_handler/Makefile
===================================================================
--- /dev/null
+++ linux-2.6.25-rc2-mm1/drivers/scsi/device_handler/Makefile
@@ -0,0 +1,4 @@
+#
+# SCSI Device Handler
+#
+obj-$(CONFIG_SCSI_DH) += scsi_dh.o
Index: linux-2.6.25-rc2-mm1/drivers/scsi/scsi_error.c
===================================================================
--- linux-2.6.25-rc2-mm1.orig/drivers/scsi/scsi_error.c
+++ linux-2.6.25-rc2-mm1/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,15 @@ static int scsi_check_sense(struct scsi_
if (scsi_sense_is_deferred(&sshdr))
return NEEDS_RETRY;
+ if (sdev->sdev_dh && sdev->sdev_dh->check_sense) {
+ int rc;
+
+ rc = sdev->sdev_dh->check_sense(sdev, &sshdr);
+ if (rc != SUCCESS)
+ 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.
Index: linux-2.6.25-rc2-mm1/include/scsi/scsi_device.h
===================================================================
--- linux-2.6.25-rc2-mm1.orig/include/scsi/scsi_device.h
+++ linux-2.6.25-rc2-mm1/include/scsi/scsi_device.h
@@ -161,9 +161,25 @@ struct scsi_device {
struct execute_work ew; /* used to get process context on put */
+ struct scsi_device_handler *sdev_dh;
+ void *sdev_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 *);
+};
+
#define to_scsi_device(d) \
container_of(d, struct scsi_device, sdev_gendev)
#define class_to_sdev(d) \
@@ -229,7 +245,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 *sdev_dh);
extern void scsi_remove_device(struct scsi_device *);
+extern int scsi_unregister_device_handler(struct scsi_device_handler *sdev_dh);
extern int scsi_device_get(struct scsi_device *);
extern void scsi_device_put(struct scsi_device *);
Index: linux-2.6.25-rc2-mm1/drivers/scsi/device_handler/scsi_dh.c
===================================================================
--- /dev/null
+++ linux-2.6.25-rc2-mm1/drivers/scsi/device_handler/scsi_dh.c
@@ -0,0 +1,146 @@
+/*
+ * 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 *sdev_dh = data;
+
+ sdev_dh->nb.notifier_call(&sdev_dh->nb, BUS_NOTIFY_ADD_DEVICE, dev);
+ return 0;
+}
+
+/*
+ * scsi_register_device_handler - register a device handler personality
+ * module.
+ * @sdev_dh - device handler to be registered.
+ *
+ * Returns 0 on success, -EBUSY if handler already registered.
+ */
+int scsi_register_device_handler(struct scsi_device_handler *sdev_dh)
+{
+ int ret = -EBUSY;
+ struct scsi_device_handler *tmp;
+
+ tmp = get_device_handler(sdev_dh->name);
+ if (tmp)
+ goto done;
+
+ ret = bus_register_notifier(&scsi_bus_type, &sdev_dh->nb);
+
+ bus_for_each_dev(&scsi_bus_type, NULL, sdev_dh, scsi_dh_notifier_add);
+ spin_lock(&list_lock);
+ list_add(&sdev_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 *sdev_dh = data;
+
+ sdev_dh->nb.notifier_call(&sdev_dh->nb, BUS_NOTIFY_DEL_DEVICE, dev);
+ return 0;
+}
+
+/*
+ * scsi_unregister_device_handler - register a device handler personality
+ * module.
+ * @sdev_dh - device handler to be unregistered.
+ *
+ * Returns 0 on success, -ENODEV if handler not registered.
+ */
+int scsi_unregister_device_handler(struct scsi_device_handler *sdev_dh)
+{
+ int ret = -ENODEV;
+ struct scsi_device_handler *tmp;
+
+ tmp = get_device_handler(sdev_dh->name);
+ if (!tmp)
+ goto done;
+
+ ret = bus_unregister_notifier(&scsi_bus_type, &sdev_dh->nb);
+
+ bus_for_each_dev(&scsi_bus_type, NULL, sdev_dh,
+ scsi_dh_notifier_remove);
+ spin_lock(&list_lock);
+ list_del(&sdev_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 = SCSI_DH_NOSYS;
+ struct scsi_device *sdev;
+ struct scsi_device_handler *sdev_dh;
+ unsigned long flags;
+
+ spin_lock_irqsave(q->queue_lock, flags);
+ sdev = q->queuedata;
+ sdev_dh = sdev->sdev_dh;
+ if (!sdev || !sdev_dh || !get_device(&sdev->sdev_gendev)) {
+ spin_unlock_irqrestore(q->queue_lock, flags);
+ goto done;
+ }
+ spin_unlock_irqrestore(q->queue_lock, flags);
+
+ if (sdev_dh->activate)
+ err = sdev_dh->activate(sdev);
+ put_device(&sdev->sdev_gendev);
+done:
+ return err;
+}
+EXPORT_SYMBOL_GPL(scsi_dh_activate);
Index: linux-2.6.25-rc2-mm1/include/scsi/scsi_dh.h
===================================================================
--- /dev/null
+++ linux-2.6.25-rc2-mm1/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 *);
Index: linux-2.6.25-rc2-mm1/drivers/scsi/scsi_lib.c
===================================================================
--- linux-2.6.25-rc2-mm1.orig/drivers/scsi/scsi_lib.c
+++ linux-2.6.25-rc2-mm1/drivers/scsi/scsi_lib.c
@@ -1156,6 +1156,11 @@ int scsi_setup_fs_cmnd(struct scsi_devic
struct scsi_cmnd *cmd;
int ret = scsi_prep_state_check(sdev, req);
+ if ((ret == BLKPREP_OK) && (req->cmd_type == REQ_TYPE_FS)) {
+ if (sdev->sdev_dh && sdev->sdev_dh->prep_fn)
+ ret = sdev->sdev_dh->prep_fn(sdev);
+ }
+
if (ret != BLKPREP_OK)
return ret;
/*
--
----------------------------------------------------------------------
Chandra Seetharaman | Be careful what you choose....
- sekharan@us.ibm.com | .......you may get it.
----------------------------------------------------------------------
^ permalink raw reply [flat|nested] 15+ messages in thread
* Re: [PATCH 1/7] scsi_dh: add skeleton for SCSI Device Handlers
2008-02-28 1:08 ` [PATCH 1/7] scsi_dh: add skeleton for SCSI Device Handlers Chandra Seetharaman
@ 2008-03-04 21:37 ` James Bottomley
2008-03-04 22:25 ` Chandra Seetharaman
0 siblings, 1 reply; 15+ messages in thread
From: James Bottomley @ 2008-03-04 21:37 UTC (permalink / raw)
To: Chandra Seetharaman
Cc: dm-devel, linux-scsi, andmike, michaelc, jens.axboe, agk
On Wed, 2008-02-27 at 17:08 -0800, Chandra Seetharaman wrote:
> 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.
Generally, this is much cleaner and an easier implementation to follow,
thanks.
> 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 | 146 146 + 0 - 0 !
> drivers/scsi/scsi_error.c | 10 10 + 0 - 0 !
> drivers/scsi/scsi_lib.c | 5 5 + 0 - 0 !
> include/scsi/scsi_device.h | 18 18 + 0 - 0 !
> include/scsi/scsi_dh.h | 58 58 + 0 - 0 !
> 9 files changed, 256 insertions(+)
>
> Index: linux-2.6.25-rc2-mm1/drivers/scsi/Kconfig
> ===================================================================
> --- linux-2.6.25-rc2-mm1.orig/drivers/scsi/Kconfig
> +++ linux-2.6.25-rc2-mm1/drivers/scsi/Kconfig
> @@ -1749,4 +1749,6 @@ endif # SCSI_LOWLEVEL
>
> source "drivers/scsi/pcmcia/Kconfig"
>
> +source "drivers/scsi/device_handler/Kconfig"
> +
> endmenu
> Index: linux-2.6.25-rc2-mm1/drivers/scsi/Makefile
> ===================================================================
> --- linux-2.6.25-rc2-mm1.orig/drivers/scsi/Makefile
> +++ linux-2.6.25-rc2-mm1/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
> Index: linux-2.6.25-rc2-mm1/drivers/scsi/device_handler/Kconfig
> ===================================================================
> --- /dev/null
> +++ linux-2.6.25-rc2-mm1/drivers/scsi/device_handler/Kconfig
> @@ -0,0 +1,12 @@
> +#
> +# SCSI Device Handler configuration
> +#
> +
> +menuconfig SCSI_DH
> + bool "SCSI Device Handlers"
> + depends on SCSI!=n
> + 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.
> Index: linux-2.6.25-rc2-mm1/drivers/scsi/device_handler/Makefile
> ===================================================================
> --- /dev/null
> +++ linux-2.6.25-rc2-mm1/drivers/scsi/device_handler/Makefile
> @@ -0,0 +1,4 @@
> +#
> +# SCSI Device Handler
> +#
> +obj-$(CONFIG_SCSI_DH) += scsi_dh.o
> Index: linux-2.6.25-rc2-mm1/drivers/scsi/scsi_error.c
> ===================================================================
> --- linux-2.6.25-rc2-mm1.orig/drivers/scsi/scsi_error.c
> +++ linux-2.6.25-rc2-mm1/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,15 @@ static int scsi_check_sense(struct scsi_
> if (scsi_sense_is_deferred(&sshdr))
> return NEEDS_RETRY;
>
> + if (sdev->sdev_dh && sdev->sdev_dh->check_sense) {
> + int rc;
> +
> + rc = sdev->sdev_dh->check_sense(sdev, &sshdr);
> + if (rc != SUCCESS)
I can see reasons for a sense handler to return SUCCESS to trigger a
fast failure (say not ready needs initialising command or something, so
this should be a specific sense handler doesn't care return code.
> + 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.
> Index: linux-2.6.25-rc2-mm1/include/scsi/scsi_device.h
> ===================================================================
> --- linux-2.6.25-rc2-mm1.orig/include/scsi/scsi_device.h
> +++ linux-2.6.25-rc2-mm1/include/scsi/scsi_device.h
> @@ -161,9 +161,25 @@ struct scsi_device {
>
> struct execute_work ew; /* used to get process context on put */
>
> + struct scsi_device_handler *sdev_dh;
> + void *sdev_dh_data;
This is a bit wasteful of 8 bytes ... why not simply move the
sdev_dh_data inside the sdev_dh structure, then if there's no handler
it's not occupying space?
> 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 *);
> +};
> +
> #define to_scsi_device(d) \
> container_of(d, struct scsi_device, sdev_gendev)
> #define class_to_sdev(d) \
> @@ -229,7 +245,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 *sdev_dh);
> extern void scsi_remove_device(struct scsi_device *);
> +extern int scsi_unregister_device_handler(struct scsi_device_handler *sdev_dh);
>
> extern int scsi_device_get(struct scsi_device *);
> extern void scsi_device_put(struct scsi_device *);
> Index: linux-2.6.25-rc2-mm1/drivers/scsi/device_handler/scsi_dh.c
> ===================================================================
> --- /dev/null
> +++ linux-2.6.25-rc2-mm1/drivers/scsi/device_handler/scsi_dh.c
> @@ -0,0 +1,146 @@
> +/*
> + * 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 *sdev_dh = data;
> +
> + sdev_dh->nb.notifier_call(&sdev_dh->nb, BUS_NOTIFY_ADD_DEVICE, dev);
> + return 0;
> +}
> +
> +/*
> + * scsi_register_device_handler - register a device handler personality
> + * module.
> + * @sdev_dh - device handler to be registered.
> + *
> + * Returns 0 on success, -EBUSY if handler already registered.
> + */
> +int scsi_register_device_handler(struct scsi_device_handler *sdev_dh)
> +{
> + int ret = -EBUSY;
> + struct scsi_device_handler *tmp;
> +
> + tmp = get_device_handler(sdev_dh->name);
> + if (tmp)
> + goto done;
> +
> + ret = bus_register_notifier(&scsi_bus_type, &sdev_dh->nb);
> +
> + bus_for_each_dev(&scsi_bus_type, NULL, sdev_dh, scsi_dh_notifier_add);
> + spin_lock(&list_lock);
> + list_add(&sdev_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 *sdev_dh = data;
> +
> + sdev_dh->nb.notifier_call(&sdev_dh->nb, BUS_NOTIFY_DEL_DEVICE, dev);
> + return 0;
> +}
> +
> +/*
> + * scsi_unregister_device_handler - register a device handler personality
> + * module.
> + * @sdev_dh - device handler to be unregistered.
> + *
> + * Returns 0 on success, -ENODEV if handler not registered.
> + */
> +int scsi_unregister_device_handler(struct scsi_device_handler *sdev_dh)
> +{
> + int ret = -ENODEV;
> + struct scsi_device_handler *tmp;
> +
> + tmp = get_device_handler(sdev_dh->name);
> + if (!tmp)
> + goto done;
> +
> + ret = bus_unregister_notifier(&scsi_bus_type, &sdev_dh->nb);
> +
> + bus_for_each_dev(&scsi_bus_type, NULL, sdev_dh,
> + scsi_dh_notifier_remove);
> + spin_lock(&list_lock);
> + list_del(&sdev_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 = SCSI_DH_NOSYS;
> + struct scsi_device *sdev;
> + struct scsi_device_handler *sdev_dh;
> + unsigned long flags;
> +
> + spin_lock_irqsave(q->queue_lock, flags);
> + sdev = q->queuedata;
> + sdev_dh = sdev->sdev_dh;
> + if (!sdev || !sdev_dh || !get_device(&sdev->sdev_gendev)) {
> + spin_unlock_irqrestore(q->queue_lock, flags);
> + goto done;
> + }
> + spin_unlock_irqrestore(q->queue_lock, flags);
just on programming style, this is marginally better expressed as
err = (!sdev || !sdev_dh || !get_device(&sdev->sdev_gendev)) ? SCSI_DH_NOSYS : 0;
spin_unlock_irqrestore(q->queue_lock, flags);
if (err)
goto done; (or even return err)
Just to eliminate the duplicate unlocks.
> +
> + if (sdev_dh->activate)
> + err = sdev_dh->activate(sdev);
> + put_device(&sdev->sdev_gendev);
> +done:
> + return err;
> +}
> +EXPORT_SYMBOL_GPL(scsi_dh_activate);
> Index: linux-2.6.25-rc2-mm1/include/scsi/scsi_dh.h
> ===================================================================
> --- /dev/null
> +++ linux-2.6.25-rc2-mm1/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 *);
> Index: linux-2.6.25-rc2-mm1/drivers/scsi/scsi_lib.c
> ===================================================================
> --- linux-2.6.25-rc2-mm1.orig/drivers/scsi/scsi_lib.c
> +++ linux-2.6.25-rc2-mm1/drivers/scsi/scsi_lib.c
> @@ -1156,6 +1156,11 @@ int scsi_setup_fs_cmnd(struct scsi_devic
> struct scsi_cmnd *cmd;
> int ret = scsi_prep_state_check(sdev, req);
>
> + if ((ret == BLKPREP_OK) && (req->cmd_type == REQ_TYPE_FS)) {
> + if (sdev->sdev_dh && sdev->sdev_dh->prep_fn)
> + ret = sdev->sdev_dh->prep_fn(sdev);
> + }
> +
> if (ret != BLKPREP_OK)
> return ret;
This is the fastpath and we need to be careful. We already checked
cmd_type == REQ_TYPE before calling scsi_setup_fs_cmnd(), so there's no
need to check it again, surely.
Plus, since we're doing if (ret != BLKPREP_OK) just below this, if you
move the if down below that, it can simply become
if (unlikely(sdev->sdev_dh && sdev->sdev_dh->prep_fn)) {
ret = sdev->sdev_dh->prep_fn(sdev);
if (ret != BLKPREP_OK)
return ret;
}
Because the two outer gates have already been checked. The unlikely
expresses the fact that having device handlers isn't currently the very
common case.
Presumably the gcc optimiser would see all of this, but it never hurts
to make sure.
James
^ permalink raw reply [flat|nested] 15+ messages in thread
* Re: [PATCH 1/7] scsi_dh: add skeleton for SCSI Device Handlers
2008-03-04 21:37 ` James Bottomley
@ 2008-03-04 22:25 ` Chandra Seetharaman
0 siblings, 0 replies; 15+ messages in thread
From: Chandra Seetharaman @ 2008-03-04 22:25 UTC (permalink / raw)
To: James Bottomley
Cc: dm-devel, linux-scsi, Mike Anderson, michaelc, jens.axboe, agk
Thanks for your comments, James.
On Tue, 2008-03-04 at 15:37 -0600, James Bottomley wrote:
> On Wed, 2008-02-27 at 17:08 -0800, Chandra Seetharaman wrote:
> > 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.
>
> Generally, this is much cleaner and an easier implementation to follow,
> thanks.
Cool, thanks.
>
> > 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>
<snip>
> if (! scsi_command_normalize_sense(scmd, &sshdr))
> > @@ -306,6 +307,15 @@ static int scsi_check_sense(struct scsi_
> > if (scsi_sense_is_deferred(&sshdr))
> > return NEEDS_RETRY;
> >
> > + if (sdev->sdev_dh && sdev->sdev_dh->check_sense) {
> > + int rc;
> > +
> > + rc = sdev->sdev_dh->check_sense(sdev, &sshdr);
> > + if (rc != SUCCESS)
>
> I can see reasons for a sense handler to return SUCCESS to trigger a
> fast failure (say not ready needs initialising command or something, so
> this should be a specific sense handler doesn't care return code.
understand. I can return a NOT_HANDLED. Is it ok to add it as 0x2008 to
scsi.h ? or any suggestions.
>
> > + 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.
> > Index: linux-2.6.25-rc2-mm1/include/scsi/scsi_device.h
> > ===================================================================
> > --- linux-2.6.25-rc2-mm1.orig/include/scsi/scsi_device.h
> > +++ linux-2.6.25-rc2-mm1/include/scsi/scsi_device.h
> > @@ -161,9 +161,25 @@ struct scsi_device {
> >
> > struct execute_work ew; /* used to get process context on put */
> >
> > + struct scsi_device_handler *sdev_dh;
> > + void *sdev_dh_data;
>
> This is a bit wasteful of 8 bytes ... why not simply move the
> sdev_dh_data inside the sdev_dh structure, then if there's no handler
> it's not occupying space?
There is one sdev_dh per handler, and one sdev_dh_data per device.
But, structures can be redefined to save space when there is no hardware
handler present. But, there will be a additional pointer dereference
while invoking the functions, is it ok ?
Here is what I am thinking:
--------
struct scsi_device_handler { }; /* same as now */
struct scsi_dh_data {
struct scsi_device_handler *sdev_dh;
char buf[0];
};
and the handlers will allocate appropriate size for this data structure.
struct scsi_device {
:
:
struct scsi_dh_data *scsi_dh_data;
};
all the function invocations will be like
sdev->scsi_dh_data->sdev_dh->activate,prep_fn,check_sense
------------
>
> > enum scsi_device_state sdev_state;
> > unsigned long sdev_data[0];
> > } __attribute__((aligned(sizeof(unsigned long))));
> >
<snip>
> +int scsi_dh_activate(struct request_queue *q)
> > +{
> > + int err = SCSI_DH_NOSYS;
> > + struct scsi_device *sdev;
> > + struct scsi_device_handler *sdev_dh;
> > + unsigned long flags;
> > +
> > + spin_lock_irqsave(q->queue_lock, flags);
> > + sdev = q->queuedata;
> > + sdev_dh = sdev->sdev_dh;
> > + if (!sdev || !sdev_dh || !get_device(&sdev->sdev_gendev)) {
> > + spin_unlock_irqrestore(q->queue_lock, flags);
> > + goto done;
> > + }
> > + spin_unlock_irqrestore(q->queue_lock, flags);
>
> just on programming style, this is marginally better expressed as
>
> err = (!sdev || !sdev_dh || !get_device(&sdev->sdev_gendev)) ? SCSI_DH_NOSYS : 0;
> spin_unlock_irqrestore(q->queue_lock, flags);
>
> if (err)
> goto done; (or even return err)
>
> Just to eliminate the duplicate unlocks.
will do.
>
> > +
> > + if (sdev_dh->activate)
> > + err = sdev_dh->activate(sdev);
> > + put_device(&sdev->sdev_gendev);
> > +done:
> > + return err;
> > +}
> > +EXPORT_SYMBOL_GPL(scsi_dh_activate);
> >
<snip>
> +
> > +extern int scsi_dh_activate(struct request_queue *);
> > Index: linux-2.6.25-rc2-mm1/drivers/scsi/scsi_lib.c
> > ===================================================================
> > --- linux-2.6.25-rc2-mm1.orig/drivers/scsi/scsi_lib.c
> > +++ linux-2.6.25-rc2-mm1/drivers/scsi/scsi_lib.c
> > @@ -1156,6 +1156,11 @@ int scsi_setup_fs_cmnd(struct scsi_devic
> > struct scsi_cmnd *cmd;
> > int ret = scsi_prep_state_check(sdev, req);
> >
> > + if ((ret == BLKPREP_OK) && (req->cmd_type == REQ_TYPE_FS)) {
> > + if (sdev->sdev_dh && sdev->sdev_dh->prep_fn)
> > + ret = sdev->sdev_dh->prep_fn(sdev);
> > + }
> > +
> > if (ret != BLKPREP_OK)
> > return ret;
>
> This is the fastpath and we need to be careful. We already checked
> cmd_type == REQ_TYPE before calling scsi_setup_fs_cmnd(), so there's no
> need to check it again, surely.
Yeah, this was a cut-n-paste problem. I had this block at the higher
level, moved it here to get rid of that check, but forgot to remove
that :)
>
> Plus, since we're doing if (ret != BLKPREP_OK) just below this, if you
> move the if down below that, it can simply become
>
> if (unlikely(sdev->sdev_dh && sdev->sdev_dh->prep_fn)) {
> ret = sdev->sdev_dh->prep_fn(sdev);
> if (ret != BLKPREP_OK)
> return ret;
> }
>
> Because the two outer gates have already been checked. The unlikely
> expresses the fact that having device handlers isn't currently the very
> common case.
will do.
>
> Presumably the gcc optimiser would see all of this, but it never hurts
> to make sure.
>
> James
>
>
--
----------------------------------------------------------------------
Chandra Seetharaman | Be careful what you choose....
- sekharan@us.ibm.com | .......you may get it.
----------------------------------------------------------------------
^ permalink raw reply [flat|nested] 15+ messages in thread
* [PATCH 1/7] scsi_dh: add skeleton for SCSI Device Handlers
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; 15+ 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 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 | 147 147 + 0 - 0 !
drivers/scsi/scsi_error.c | 11 11 + 0 - 0 !
drivers/scsi/scsi_lib.c | 8 8 + 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 !
10 files changed, 266 insertions(+)
Index: linux-2.6.25-rc3-mm1/drivers/scsi/Kconfig
===================================================================
--- linux-2.6.25-rc3-mm1.orig/drivers/scsi/Kconfig
+++ linux-2.6.25-rc3-mm1/drivers/scsi/Kconfig
@@ -1759,4 +1759,6 @@ endif # SCSI_LOWLEVEL
source "drivers/scsi/pcmcia/Kconfig"
+source "drivers/scsi/device_handler/Kconfig"
+
endmenu
Index: linux-2.6.25-rc3-mm1/drivers/scsi/Makefile
===================================================================
--- linux-2.6.25-rc3-mm1.orig/drivers/scsi/Makefile
+++ linux-2.6.25-rc3-mm1/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
Index: linux-2.6.25-rc3-mm1/drivers/scsi/device_handler/Kconfig
===================================================================
--- /dev/null
+++ linux-2.6.25-rc3-mm1/drivers/scsi/device_handler/Kconfig
@@ -0,0 +1,12 @@
+#
+# SCSI Device Handler configuration
+#
+
+menuconfig SCSI_DH
+ bool "SCSI Device Handlers"
+ depends on SCSI!=n
+ 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.
Index: linux-2.6.25-rc3-mm1/drivers/scsi/device_handler/Makefile
===================================================================
--- /dev/null
+++ linux-2.6.25-rc3-mm1/drivers/scsi/device_handler/Makefile
@@ -0,0 +1,4 @@
+#
+# SCSI Device Handler
+#
+obj-$(CONFIG_SCSI_DH) += scsi_dh.o
Index: linux-2.6.25-rc3-mm1/drivers/scsi/scsi_error.c
===================================================================
--- linux-2.6.25-rc3-mm1.orig/drivers/scsi/scsi_error.c
+++ linux-2.6.25-rc3-mm1/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.
Index: linux-2.6.25-rc3-mm1/include/scsi/scsi_device.h
===================================================================
--- linux-2.6.25-rc3-mm1.orig/include/scsi/scsi_device.h
+++ linux-2.6.25-rc3-mm1/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 *);
Index: linux-2.6.25-rc3-mm1/drivers/scsi/device_handler/scsi_dh.c
===================================================================
--- /dev/null
+++ linux-2.6.25-rc3-mm1/drivers/scsi/device_handler/scsi_dh.c
@@ -0,0 +1,147 @@
+/*
+ * 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);
Index: linux-2.6.25-rc3-mm1/include/scsi/scsi_dh.h
===================================================================
--- /dev/null
+++ linux-2.6.25-rc3-mm1/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 *);
Index: linux-2.6.25-rc3-mm1/drivers/scsi/scsi_lib.c
===================================================================
--- linux-2.6.25-rc3-mm1.orig/drivers/scsi/scsi_lib.c
+++ linux-2.6.25-rc3-mm1/drivers/scsi/scsi_lib.c
@@ -1158,6 +1158,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.
*/
Index: linux-2.6.25-rc3-mm1/include/scsi/scsi.h
===================================================================
--- linux-2.6.25-rc3-mm1.orig/include/scsi/scsi.h
+++ linux-2.6.25-rc3-mm1/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.
--
----------------------------------------------------------------------
Chandra Seetharaman | Be careful what you choose....
- sekharan@us.ibm.com | .......you may get it.
----------------------------------------------------------------------
^ permalink raw reply [flat|nested] 15+ messages in thread
* [PATCH 1/7] scsi_dh: add skeleton for SCSI Device Handlers
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; 15+ 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 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 | 147 147 + 0 - 0 !
drivers/scsi/scsi_error.c | 11 11 + 0 - 0 !
drivers/scsi/scsi_lib.c | 8 8 + 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 !
10 files changed, 266 insertions(+)
Index: linux-2.6.25-rc2-mm1/drivers/scsi/Kconfig
===================================================================
--- linux-2.6.25-rc2-mm1.orig/drivers/scsi/Kconfig 2008-03-14 10:48:26.000000000 -0700
+++ linux-2.6.25-rc2-mm1/drivers/scsi/Kconfig 2008-03-14 10:49:21.000000000 -0700
@@ -1749,4 +1749,6 @@
source "drivers/scsi/pcmcia/Kconfig"
+source "drivers/scsi/device_handler/Kconfig"
+
endmenu
Index: linux-2.6.25-rc2-mm1/drivers/scsi/Makefile
===================================================================
--- linux-2.6.25-rc2-mm1.orig/drivers/scsi/Makefile 2008-03-14 10:48:26.000000000 -0700
+++ linux-2.6.25-rc2-mm1/drivers/scsi/Makefile 2008-03-14 10:49:21.000000000 -0700
@@ -34,6 +34,7 @@
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
Index: linux-2.6.25-rc2-mm1/drivers/scsi/device_handler/Kconfig
===================================================================
--- /dev/null 1970-01-01 00:00:00.000000000 +0000
+++ linux-2.6.25-rc2-mm1/drivers/scsi/device_handler/Kconfig 2008-03-14 13:40:57.000000000 -0700
@@ -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.
Index: linux-2.6.25-rc2-mm1/drivers/scsi/device_handler/Makefile
===================================================================
--- /dev/null 1970-01-01 00:00:00.000000000 +0000
+++ linux-2.6.25-rc2-mm1/drivers/scsi/device_handler/Makefile 2008-03-14 13:40:57.000000000 -0700
@@ -0,0 +1,4 @@
+#
+# SCSI Device Handler
+#
+obj-$(CONFIG_SCSI_DH) += scsi_dh.o
Index: linux-2.6.25-rc2-mm1/drivers/scsi/scsi_error.c
===================================================================
--- linux-2.6.25-rc2-mm1.orig/drivers/scsi/scsi_error.c 2008-03-14 10:48:27.000000000 -0700
+++ linux-2.6.25-rc2-mm1/drivers/scsi/scsi_error.c 2008-03-14 10:49:21.000000000 -0700
@@ -298,6 +298,7 @@
*/
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 @@
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.
Index: linux-2.6.25-rc2-mm1/include/scsi/scsi_device.h
===================================================================
--- linux-2.6.25-rc2-mm1.orig/include/scsi/scsi_device.h 2008-03-14 10:48:31.000000000 -0700
+++ linux-2.6.25-rc2-mm1/include/scsi/scsi_device.h 2008-03-14 10:49:21.000000000 -0700
@@ -161,9 +161,29 @@
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 @@
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 *);
Index: linux-2.6.25-rc2-mm1/drivers/scsi/device_handler/scsi_dh.c
===================================================================
--- /dev/null 1970-01-01 00:00:00.000000000 +0000
+++ linux-2.6.25-rc2-mm1/drivers/scsi/device_handler/scsi_dh.c 2008-03-14 13:42:24.000000000 -0700
@@ -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");
Index: linux-2.6.25-rc2-mm1/include/scsi/scsi_dh.h
===================================================================
--- /dev/null 1970-01-01 00:00:00.000000000 +0000
+++ linux-2.6.25-rc2-mm1/include/scsi/scsi_dh.h 2008-03-14 10:49:21.000000000 -0700
@@ -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 *);
Index: linux-2.6.25-rc2-mm1/drivers/scsi/scsi_lib.c
===================================================================
--- linux-2.6.25-rc2-mm1.orig/drivers/scsi/scsi_lib.c 2008-03-14 10:48:27.000000000 -0700
+++ linux-2.6.25-rc2-mm1/drivers/scsi/scsi_lib.c 2008-03-14 10:49:21.000000000 -0700
@@ -1158,6 +1158,14 @@
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.
*/
Index: linux-2.6.25-rc2-mm1/include/scsi/scsi.h
===================================================================
--- linux-2.6.25-rc2-mm1.orig/include/scsi/scsi.h 2008-03-14 10:48:31.000000000 -0700
+++ linux-2.6.25-rc2-mm1/include/scsi/scsi.h 2008-03-14 10:49:21.000000000 -0700
@@ -374,6 +374,7 @@
#define SOFT_ERROR 0x2005
#define ADD_TO_MLQUEUE 0x2006
#define TIMEOUT_ERROR 0x2007
+#define SCSI_RETURN_NOT_HANDLED 0x2008
/*
* Midlevel queue return values.
Index: linux-2.6.25-rc2-mm1/drivers/scsi/scsi_sysfs.c
===================================================================
--- linux-2.6.25-rc2-mm1.orig/drivers/scsi/scsi_sysfs.c 2008-03-14 10:48:27.000000000 -0700
+++ linux-2.6.25-rc2-mm1/drivers/scsi/scsi_sysfs.c 2008-03-14 11:11:38.000000000 -0700
@@ -397,6 +397,7 @@
.resume = scsi_bus_resume,
.remove = scsi_bus_remove,
};
+EXPORT_SYMBOL_GPL(scsi_bus_type);
int scsi_sysfs_register(void)
{
--
----------------------------------------------------------------------
Chandra Seetharaman | Be careful what you choose....
- sekharan@us.ibm.com | .......you may get it.
----------------------------------------------------------------------
^ permalink raw reply [flat|nested] 15+ messages in thread
* [PATCH 1/7] scsi_dh: add skeleton for SCSI Device Handlers
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
0 siblings, 0 replies; 15+ 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 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] 15+ messages in thread
* [PATCH 1/7] scsi_dh: add skeleton for SCSI Device Handlers
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; 15+ 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 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] 15+ 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; 15+ 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] 15+ 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; 15+ 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] 15+ 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; 15+ 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] 15+ 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; 15+ 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] 15+ 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; 15+ 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,
+ ¤t_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] 15+ 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; 15+ 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] 15+ 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; 15+ 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] 15+ 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; 15+ 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] 15+ messages in thread
end of thread, other threads:[~2008-04-17 22:23 UTC | newest]
Thread overview: 15+ 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 1/7] scsi_dh: add skeleton for SCSI Device Handlers 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 1/7] scsi_dh: add skeleton for SCSI Device Handlers Chandra Seetharaman
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 1/7] scsi_dh: add skeleton for SCSI Device Handlers 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 1/7] scsi_dh: add skeleton for SCSI Device Handlers 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 1/7] scsi_dh: add skeleton for SCSI Device Handlers Chandra Seetharaman
2008-03-04 21:37 ` James Bottomley
2008-03-04 22:25 ` 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).