Commit a6a8d9f8 authored by Chandra Seetharaman's avatar Chandra Seetharaman Committed by James Bottomley

[SCSI] scsi_dh: add infrastructure for SCSI Device Handlers

Some of the storage devices (that can be accessed through multiple paths),
do need some special handling for
	1. Activating the passive path of the storage access.
	2. Decode and handle the special sense codes returned by the devices.
	3. Handle the I/Os being sent to the passive path, especially
           during the device probe time.
when accessed through multiple paths.

As of today this special device handling is done at the dm-multipath
layer using dm-handlers. That works well for (1); for (2) to be handled
at dm layer, scsi sense information need to be exported from SCSI to dm-layer,
which is not very attractive; (3) cannot be done at all at the dm layer.

Device handler has been moved to SCSI mainly to handle (2) and (3) properly.
Signed-off-by: default avatarChandra Seetharaman <sekharan@us.ibm.com>
Signed-off-by: default avatarMike Anderson <andmike@linux.vnet.ibm.com>
Signed-off-by: default avatarMike Christie <michaelc@cs.wisc.edu>
Signed-off-by: default avatarJames Bottomley <James.Bottomley@HansenPartnership.com>
parent 53c8ba95
...@@ -1771,4 +1771,6 @@ endif # SCSI_LOWLEVEL ...@@ -1771,4 +1771,6 @@ endif # SCSI_LOWLEVEL
source "drivers/scsi/pcmcia/Kconfig" source "drivers/scsi/pcmcia/Kconfig"
source "drivers/scsi/device_handler/Kconfig"
endmenu endmenu
...@@ -34,6 +34,7 @@ obj-$(CONFIG_SCSI_ISCSI_ATTRS) += scsi_transport_iscsi.o ...@@ -34,6 +34,7 @@ obj-$(CONFIG_SCSI_ISCSI_ATTRS) += scsi_transport_iscsi.o
obj-$(CONFIG_SCSI_SAS_ATTRS) += scsi_transport_sas.o obj-$(CONFIG_SCSI_SAS_ATTRS) += scsi_transport_sas.o
obj-$(CONFIG_SCSI_SAS_LIBSAS) += libsas/ obj-$(CONFIG_SCSI_SAS_LIBSAS) += libsas/
obj-$(CONFIG_SCSI_SRP_ATTRS) += scsi_transport_srp.o 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_ISCSI_TCP) += libiscsi.o iscsi_tcp.o
obj-$(CONFIG_INFINIBAND_ISER) += libiscsi.o obj-$(CONFIG_INFINIBAND_ISER) += libiscsi.o
......
#
# 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.
#
# SCSI Device Handler
#
obj-$(CONFIG_SCSI_DH) += scsi_dh.o
/*
* SCSI device handler infrastruture.
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation; either version 2 of the License, or (at your
* option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*
* Copyright IBM Corporation, 2007
* Authors:
* Chandra Seetharaman <sekharan@us.ibm.com>
* Mike Anderson <andmike@linux.vnet.ibm.com>
*/
#include <scsi/scsi_dh.h>
#include "../scsi_priv.h"
static DEFINE_SPINLOCK(list_lock);
static LIST_HEAD(scsi_dh_list);
static struct scsi_device_handler *get_device_handler(const char *name)
{
struct scsi_device_handler *tmp, *found = NULL;
spin_lock(&list_lock);
list_for_each_entry(tmp, &scsi_dh_list, list) {
if (!strcmp(tmp->name, name)) {
found = tmp;
break;
}
}
spin_unlock(&list_lock);
return found;
}
static int scsi_dh_notifier_add(struct device *dev, void *data)
{
struct scsi_device_handler *scsi_dh = data;
scsi_dh->nb.notifier_call(&scsi_dh->nb, BUS_NOTIFY_ADD_DEVICE, dev);
return 0;
}
/*
* scsi_register_device_handler - register a device handler personality
* module.
* @scsi_dh - device handler to be registered.
*
* Returns 0 on success, -EBUSY if handler already registered.
*/
int scsi_register_device_handler(struct scsi_device_handler *scsi_dh)
{
int ret = -EBUSY;
struct scsi_device_handler *tmp;
tmp = get_device_handler(scsi_dh->name);
if (tmp)
goto done;
ret = bus_register_notifier(&scsi_bus_type, &scsi_dh->nb);
bus_for_each_dev(&scsi_bus_type, NULL, scsi_dh, scsi_dh_notifier_add);
spin_lock(&list_lock);
list_add(&scsi_dh->list, &scsi_dh_list);
spin_unlock(&list_lock);
done:
return ret;
}
EXPORT_SYMBOL_GPL(scsi_register_device_handler);
static int scsi_dh_notifier_remove(struct device *dev, void *data)
{
struct scsi_device_handler *scsi_dh = data;
scsi_dh->nb.notifier_call(&scsi_dh->nb, BUS_NOTIFY_DEL_DEVICE, dev);
return 0;
}
/*
* scsi_unregister_device_handler - register a device handler personality
* module.
* @scsi_dh - device handler to be unregistered.
*
* Returns 0 on success, -ENODEV if handler not registered.
*/
int scsi_unregister_device_handler(struct scsi_device_handler *scsi_dh)
{
int ret = -ENODEV;
struct scsi_device_handler *tmp;
tmp = get_device_handler(scsi_dh->name);
if (!tmp)
goto done;
ret = bus_unregister_notifier(&scsi_bus_type, &scsi_dh->nb);
bus_for_each_dev(&scsi_bus_type, NULL, scsi_dh,
scsi_dh_notifier_remove);
spin_lock(&list_lock);
list_del(&scsi_dh->list);
spin_unlock(&list_lock);
done:
return ret;
}
EXPORT_SYMBOL_GPL(scsi_unregister_device_handler);
/*
* scsi_dh_activate - activate the path associated with the scsi_device
* corresponding to the given request queue.
* @q - Request queue that is associated with the scsi_device to be
* activated.
*/
int scsi_dh_activate(struct request_queue *q)
{
int err = 0;
unsigned long flags;
struct scsi_device *sdev;
struct scsi_device_handler *scsi_dh = NULL;
spin_lock_irqsave(q->queue_lock, flags);
sdev = q->queuedata;
if (sdev && sdev->scsi_dh_data)
scsi_dh = sdev->scsi_dh_data->scsi_dh;
if (!scsi_dh || !get_device(&sdev->sdev_gendev))
err = SCSI_DH_NOSYS;
spin_unlock_irqrestore(q->queue_lock, flags);
if (err)
return err;
if (scsi_dh->activate)
err = scsi_dh->activate(sdev);
put_device(&sdev->sdev_gendev);
return err;
}
EXPORT_SYMBOL_GPL(scsi_dh_activate);
/*
* scsi_dh_handler_exist - Return TRUE(1) if a device handler exists for
* the given name. FALSE(0) otherwise.
* @name - name of the device handler.
*/
int scsi_dh_handler_exist(const char *name)
{
return (get_device_handler(name) != NULL);
}
EXPORT_SYMBOL_GPL(scsi_dh_handler_exist);
MODULE_DESCRIPTION("SCSI device handler");
MODULE_AUTHOR("Chandra Seetharaman <sekharan@us.ibm.com>");
MODULE_LICENSE("GPL");
...@@ -298,6 +298,7 @@ static inline void scsi_eh_prt_fail_stats(struct Scsi_Host *shost, ...@@ -298,6 +298,7 @@ static inline void scsi_eh_prt_fail_stats(struct Scsi_Host *shost,
*/ */
static int scsi_check_sense(struct scsi_cmnd *scmd) static int scsi_check_sense(struct scsi_cmnd *scmd)
{ {
struct scsi_device *sdev = scmd->device;
struct scsi_sense_hdr sshdr; struct scsi_sense_hdr sshdr;
if (! scsi_command_normalize_sense(scmd, &sshdr)) if (! scsi_command_normalize_sense(scmd, &sshdr))
...@@ -306,6 +307,16 @@ static int scsi_check_sense(struct scsi_cmnd *scmd) ...@@ -306,6 +307,16 @@ static int scsi_check_sense(struct scsi_cmnd *scmd)
if (scsi_sense_is_deferred(&sshdr)) if (scsi_sense_is_deferred(&sshdr))
return NEEDS_RETRY; 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 * Previous logic looked for FILEMARK, EOM or ILI which are
* mainly associated with tapes and returned SUCCESS. * mainly associated with tapes and returned SUCCESS.
......
...@@ -1160,6 +1160,14 @@ int scsi_setup_fs_cmnd(struct scsi_device *sdev, struct request *req) ...@@ -1160,6 +1160,14 @@ int scsi_setup_fs_cmnd(struct scsi_device *sdev, struct request *req)
if (ret != BLKPREP_OK) if (ret != BLKPREP_OK)
return ret; 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. * Filesystem requests must transfer data.
*/ */
......
...@@ -439,6 +439,7 @@ struct bus_type scsi_bus_type = { ...@@ -439,6 +439,7 @@ struct bus_type scsi_bus_type = {
.resume = scsi_bus_resume, .resume = scsi_bus_resume,
.remove = scsi_bus_remove, .remove = scsi_bus_remove,
}; };
EXPORT_SYMBOL_GPL(scsi_bus_type);
int scsi_sysfs_register(void) int scsi_sysfs_register(void)
{ {
......
...@@ -400,6 +400,7 @@ struct scsi_lun { ...@@ -400,6 +400,7 @@ struct scsi_lun {
#define SOFT_ERROR 0x2005 #define SOFT_ERROR 0x2005
#define ADD_TO_MLQUEUE 0x2006 #define ADD_TO_MLQUEUE 0x2006
#define TIMEOUT_ERROR 0x2007 #define TIMEOUT_ERROR 0x2007
#define SCSI_RETURN_NOT_HANDLED 0x2008
/* /*
* Midlevel queue return values. * Midlevel queue return values.
......
...@@ -161,9 +161,29 @@ struct scsi_device { ...@@ -161,9 +161,29 @@ struct scsi_device {
struct execute_work ew; /* used to get process context on put */ struct execute_work ew; /* used to get process context on put */
struct scsi_dh_data *scsi_dh_data;
enum scsi_device_state sdev_state; enum scsi_device_state sdev_state;
unsigned long sdev_data[0]; unsigned long sdev_data[0];
} __attribute__((aligned(sizeof(unsigned long)))); } __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) \ #define to_scsi_device(d) \
container_of(d, struct scsi_device, sdev_gendev) container_of(d, struct scsi_device, sdev_gendev)
#define class_to_sdev(d) \ #define class_to_sdev(d) \
...@@ -230,7 +250,9 @@ extern struct scsi_device *__scsi_add_device(struct Scsi_Host *, ...@@ -230,7 +250,9 @@ extern struct scsi_device *__scsi_add_device(struct Scsi_Host *,
uint, uint, uint, void *hostdata); uint, uint, uint, void *hostdata);
extern int scsi_add_device(struct Scsi_Host *host, uint channel, extern int scsi_add_device(struct Scsi_Host *host, uint channel,
uint target, uint lun); 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 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 int scsi_device_get(struct scsi_device *);
extern void scsi_device_put(struct scsi_device *); extern void scsi_device_put(struct scsi_device *);
......
/*
* Header file for SCSI device handler infrastruture.
*
* Modified version of patches posted by Mike Christie <michaelc@cs.wisc.edu>
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation; either version 2 of the License, or (at your
* option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*
* Copyright IBM Corporation, 2007
* Authors:
* Chandra Seetharaman <sekharan@us.ibm.com>
* Mike Anderson <andmike@linux.vnet.ibm.com>
*/
#include <scsi/scsi_device.h>
enum {
SCSI_DH_OK = 0,
/*
* device errors
*/
SCSI_DH_DEV_FAILED, /* generic device error */
SCSI_DH_DEV_TEMP_BUSY,
SCSI_DH_DEVICE_MAX, /* max device blkerr definition */
/*
* transport errors
*/
SCSI_DH_NOTCONN = SCSI_DH_DEVICE_MAX + 1,
SCSI_DH_CONN_FAILURE,
SCSI_DH_TRANSPORT_MAX, /* max transport blkerr definition */
/*
* driver and generic errors
*/
SCSI_DH_IO = SCSI_DH_TRANSPORT_MAX + 1, /* generic error */
SCSI_DH_INVALID_IO,
SCSI_DH_RETRY, /* retry the req, but not immediately */
SCSI_DH_IMM_RETRY, /* immediately retry the req */
SCSI_DH_TIMED_OUT,
SCSI_DH_RES_TEMP_UNAVAIL,
SCSI_DH_DEV_OFFLINED,
SCSI_DH_NOSYS,
SCSI_DH_DRIVER_MAX,
};
extern int scsi_dh_activate(struct request_queue *);
extern int scsi_dh_handler_exist(const char *);
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment