Subject: scsi_dh: add scsi device handler to dm From: Mike Anderson <andmike@xxxxxxxxxxxxxxxxxx> This patch adds a dm hardware handler that can control SCSI device handlers. SCSI Hardware handler for a specific device type can be invokes by using this handler. For example, to use the lsi_rdac SCSI hardware handler, one would specify hardware_handler "2 scsi_dh lsi_rdac" in the device section of /etc/multipath.conf. Signed-off-by: Mike Anderson <andmike@xxxxxxxxxxxxxxxxxx> Signed-off-by: Chandra Seetharaman <sekharan@xxxxxxxxxx> --- drivers/md/Kconfig | 6 6 + 0 - 0 ! drivers/md/Makefile | 2 2 + 0 - 0 ! drivers/md/dm-mpath-scsi-dh.c | 185 185 + 0 - 0 ! 3 files changed, 193 insertions(+) Index: linux-2.6.24-rc8/drivers/md/Kconfig =================================================================== --- linux-2.6.24-rc8.orig/drivers/md/Kconfig +++ linux-2.6.24-rc8/drivers/md/Kconfig @@ -273,6 +273,12 @@ config DM_MULTIPATH_HP ---help--- Multipath support for HP MSA (Active/Passive) series hardware. +config DM_MULTIPATH_SCSI_DH + tristate "SCSI Device Handler support (EXPERIMENTAL)" + depends on DM_MULTIPATH && BLK_DEV_DM && EXPERIMENTAL + ---help--- + Multipath support for SCSI Device Handlers. + config DM_DELAY tristate "I/O delaying target (EXPERIMENTAL)" depends on BLK_DEV_DM && EXPERIMENTAL Index: linux-2.6.24-rc8/drivers/md/Makefile =================================================================== --- linux-2.6.24-rc8.orig/drivers/md/Makefile +++ linux-2.6.24-rc8/drivers/md/Makefile @@ -9,6 +9,7 @@ dm-snapshot-objs := dm-snap.o dm-excepti dm-mirror-objs := dm-log.o dm-raid1.o dm-rdac-objs := dm-mpath-rdac.o dm-hp-sw-objs := dm-mpath-hp-sw.o +dm-scsi-dh-objs := dm-mpath-scsi-dh.o md-mod-objs := md.o bitmap.o raid456-objs := raid5.o raid6algos.o raid6recov.o raid6tables.o \ raid6int1.o raid6int2.o raid6int4.o \ @@ -38,6 +39,7 @@ obj-$(CONFIG_DM_MULTIPATH) += dm-multipa 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_MULTIPATH_SCSI_DH) += dm-scsi-dh.o obj-$(CONFIG_DM_SNAPSHOT) += dm-snapshot.o obj-$(CONFIG_DM_MIRROR) += dm-mirror.o obj-$(CONFIG_DM_ZERO) += dm-zero.o Index: linux-2.6.24-rc8/drivers/md/dm-mpath-scsi-dh.c =================================================================== --- /dev/null +++ linux-2.6.24-rc8/drivers/md/dm-mpath-scsi-dh.c @@ -0,0 +1,185 @@ +/* + * SCSI Device handler + * + * 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 + * Author: Mike Anderson <andmike@xxxxxxxxxxxxxxxxxx> + */ + +#define DM_MSG_PREFIX "multipath scsi_dh" + +#include "dm.h" +#include "dm-hw-handler.h" + +struct scsi_dh_context { + char *hw_handler_name; + struct dm_path *path; +}; + +static int scsi_dh_create(struct hw_handler *hwh, unsigned argc, char **argv) +{ + struct scsi_dh_context *c; + + c = kzalloc(sizeof(*c), GFP_KERNEL); + if (!c) + return -ENOMEM; + if (argc == 1) { + c->hw_handler_name = kstrdup(argv[0], GFP_KERNEL); + if (c->hw_handler_name) + request_module("scsi_dh_%s", c->hw_handler_name); + } + + hwh->context = c; + + return 0; +} + +static void scsi_dh_destroy(struct hw_handler *hwh) +{ + struct scsi_dh_context *c = hwh->context; + kfree(c->hw_handler_name); + kfree(c); + hwh->context = NULL; + return; +} + +static unsigned scsi_dh_error(struct hw_handler *hwh, struct bio *bio) +{ + /* Try default handler */ + return dm_scsi_err_handler(hwh, bio); +} + + +static void pg_init_done(struct request *req, int err) +{ + struct scsi_dh_context *c = req->end_io_data; + int ret = 0; + + if (blkerr_transport_err(req->errors)) { + /* + * Old dm behavior had us fail a path on any error. + * In future patches, since we have finer grained errors now, + * we do not have to fail the path on the first transient + * error. + */ + ret = MP_FAIL_PATH; + goto out; + } + + /* device or driver problems */ + switch (req->errors) { + case BLKERR_OK: + break; + case BLKERR_NOSYS: + if (!c->hw_handler_name) + break; + DMERR("Cannot failover device because hw-%s may not be " + "loaded.", c->hw_handler_name); + /* + * Fail path for now, so we do not ping poing + */ + ret = MP_FAIL_PATH; + break; + case BLKERR_DEV_TEMP_BUSY: + /* + * Probably doing something like FW upgrade on the + * controller so try the other pg. + */ + ret = MP_BYPASS_PG; + break; + /* TODO: For BLKERR_RETRY we should wait a couple seconds */ + case BLKERR_RETRY: + case BLKERR_IMM_RETRY: + case BLKERR_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. + */ + ret = MP_FAIL_PATH; + } + +out: + dm_pg_init_complete(c->path, ret); + __blk_put_request(req->q, req); + return; +} + +static void scsi_dh_pg_init(struct hw_handler *hwh, unsigned bypassed, + struct dm_path *path) +{ + struct scsi_dh_context *c = hwh->context; + struct request *req; + + req = blk_get_request(bdev_get_queue(path->dev->bdev), 1, GFP_NOIO); + if (!req) { + /* FIXME: Add retry */ + dm_pg_init_complete(path, MP_FAIL_PATH); + return; + } + + req->cmd[0] = REQ_LB_OP_TRANSITION; + req->cmd_type = REQ_TYPE_LINUX_BLOCK; + c->path = path; + req->end_io_data = c; + /* TODO: does this need to be configurable or is it HW specific? */ + req->retries = 5; + blk_execute_rq_nowait(req->q, NULL, req, 1, pg_init_done); +} + +#define SCSI_DH_NAME "scsi_dh" +#define SCSI_DH_VER "0.1" + +static struct hw_handler_type scsi_dh_handler = { + .name = SCSI_DH_NAME, + .module = THIS_MODULE, + .create = scsi_dh_create, + .destroy = scsi_dh_destroy, + .pg_init = scsi_dh_pg_init, + .error = scsi_dh_error, +}; + +static int __init scsi_dh_init(void) +{ + int r; + + r = dm_register_hw_handler(&scsi_dh_handler); + if (r < 0) { + DMERR("%s: register failed %d", SCSI_DH_NAME, r); + return r; + } + + DMINFO("%s: version %s loaded", SCSI_DH_NAME, SCSI_DH_VER); + return 0; +} + +static void __exit scsi_dh_exit(void) +{ + int r = dm_unregister_hw_handler(&scsi_dh_handler); + + if (r < 0) + DMERR("%s: unregister failed %d", SCSI_DH_NAME, r); +} + +module_init(scsi_dh_init); +module_exit(scsi_dh_exit); + +MODULE_DESCRIPTION("DM Multipath SCSI Device Handler support"); +MODULE_AUTHOR("Mike Anderson <andmike@xxxxxxxxxxxxxxxxxx"); +MODULE_LICENSE("GPL"); +MODULE_VERSION(SCSI_DH_VER); -- ---------------------------------------------------------------------- Chandra Seetharaman | Be careful what you choose.... - sekharan@xxxxxxxxxx | .......you may get it. ---------------------------------------------------------------------- - To unsubscribe from this list: send the line "unsubscribe linux-scsi" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html