/* * 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 #include #include #include #include #include #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 "); MODULE_LICENSE("GPL"); MODULE_VERSION(DM_HP_HWH_VER);