diff options
Diffstat (limited to 'drivers/scsi/device_handler')
| -rw-r--r-- | drivers/scsi/device_handler/Kconfig | 4 | ||||
| -rw-r--r-- | drivers/scsi/device_handler/scsi_dh.c | 117 | ||||
| -rw-r--r-- | drivers/scsi/device_handler/scsi_dh_alua.c | 296 | ||||
| -rw-r--r-- | drivers/scsi/device_handler/scsi_dh_emc.c | 22 | ||||
| -rw-r--r-- | drivers/scsi/device_handler/scsi_dh_hp_sw.c | 24 | ||||
| -rw-r--r-- | drivers/scsi/device_handler/scsi_dh_rdac.c | 236 |
6 files changed, 456 insertions, 243 deletions
diff --git a/drivers/scsi/device_handler/Kconfig b/drivers/scsi/device_handler/Kconfig index 67070257919..69abd0ad48e 100644 --- a/drivers/scsi/device_handler/Kconfig +++ b/drivers/scsi/device_handler/Kconfig @@ -32,8 +32,8 @@ config SCSI_DH_EMC If you have a EMC CLARiiON select y. Otherwise, say N. config SCSI_DH_ALUA - tristate "SPC-3 ALUA Device Handler (EXPERIMENTAL)" - depends on SCSI_DH && EXPERIMENTAL + tristate "SPC-3 ALUA Device Handler" + depends on SCSI_DH help SCSI Device handler for generic SPC-3 Asymmetric Logical Unit Access (ALUA). diff --git a/drivers/scsi/device_handler/scsi_dh.c b/drivers/scsi/device_handler/scsi_dh.c index 564e6ecd17c..33e422e7583 100644 --- a/drivers/scsi/device_handler/scsi_dh.c +++ b/drivers/scsi/device_handler/scsi_dh.c @@ -22,12 +22,12 @@ */ #include <linux/slab.h> +#include <linux/module.h> #include <scsi/scsi_dh.h> #include "../scsi_priv.h" static DEFINE_SPINLOCK(list_lock); static LIST_HEAD(scsi_dh_list); -static int scsi_dh_list_idx = 1; static struct scsi_device_handler *get_device_handler(const char *name) { @@ -44,19 +44,27 @@ static struct scsi_device_handler *get_device_handler(const char *name) return found; } -static struct scsi_device_handler *get_device_handler_by_idx(int idx) +/* + * device_handler_match_function - Match a device handler to a device + * @sdev - SCSI device to be tested + * + * Tests @sdev against the match function of all registered device_handler. + * Returns the found device handler or NULL if not found. + */ +static struct scsi_device_handler * +device_handler_match_function(struct scsi_device *sdev) { - struct scsi_device_handler *tmp, *found = NULL; + struct scsi_device_handler *tmp_dh, *found_dh = NULL; spin_lock(&list_lock); - list_for_each_entry(tmp, &scsi_dh_list, list) { - if (tmp->idx == idx) { - found = tmp; + list_for_each_entry(tmp_dh, &scsi_dh_list, list) { + if (tmp_dh->match && tmp_dh->match(sdev)) { + found_dh = tmp_dh; break; } } spin_unlock(&list_lock); - return found; + return found_dh; } /* @@ -72,12 +80,9 @@ static struct scsi_device_handler * device_handler_match(struct scsi_device_handler *scsi_dh, struct scsi_device *sdev) { - struct scsi_device_handler *found_dh = NULL; - int idx; + struct scsi_device_handler *found_dh; - idx = scsi_get_device_flags_keyed(sdev, sdev->vendor, sdev->model, - SCSI_DEVINFO_DH); - found_dh = get_device_handler_by_idx(idx); + found_dh = device_handler_match_function(sdev); if (scsi_dh && found_dh != scsi_dh) found_dh = NULL; @@ -151,6 +156,10 @@ store_dh_state(struct device *dev, struct device_attribute *attr, struct scsi_device_handler *scsi_dh; int err = -EINVAL; + if (sdev->sdev_state == SDEV_CANCEL || + sdev->sdev_state == SDEV_DEL) + return -ENODEV; + if (!sdev->scsi_dh_data) { /* * Attach to a device handler @@ -317,25 +326,14 @@ static int scsi_dh_notifier_remove(struct device *dev, void *data) */ int scsi_register_device_handler(struct scsi_device_handler *scsi_dh) { - int i; if (get_device_handler(scsi_dh->name)) return -EBUSY; spin_lock(&list_lock); - scsi_dh->idx = scsi_dh_list_idx++; list_add(&scsi_dh->list, &scsi_dh_list); spin_unlock(&list_lock); - for (i = 0; scsi_dh->devlist[i].vendor; i++) { - scsi_dev_info_list_add_keyed(0, - scsi_dh->devlist[i].vendor, - scsi_dh->devlist[i].model, - NULL, - scsi_dh->idx, - SCSI_DEVINFO_DH); - } - bus_for_each_dev(&scsi_bus_type, NULL, scsi_dh, scsi_dh_notifier_add); printk(KERN_INFO "%s: device handler registered\n", scsi_dh->name); @@ -352,7 +350,6 @@ EXPORT_SYMBOL_GPL(scsi_register_device_handler); */ int scsi_unregister_device_handler(struct scsi_device_handler *scsi_dh) { - int i; if (!get_device_handler(scsi_dh->name)) return -ENODEV; @@ -360,12 +357,6 @@ int scsi_unregister_device_handler(struct scsi_device_handler *scsi_dh) bus_for_each_dev(&scsi_bus_type, NULL, scsi_dh, scsi_dh_notifier_remove); - for (i = 0; scsi_dh->devlist[i].vendor; i++) { - scsi_dev_info_list_del_keyed(scsi_dh->devlist[i].vendor, - scsi_dh->devlist[i].model, - SCSI_DEVINFO_DH); - } - spin_lock(&list_lock); list_del(&scsi_dh->list); spin_unlock(&list_lock); @@ -394,12 +385,22 @@ int scsi_dh_activate(struct request_queue *q, activate_complete fn, void *data) unsigned long flags; struct scsi_device *sdev; struct scsi_device_handler *scsi_dh = NULL; + struct device *dev = NULL; spin_lock_irqsave(q->queue_lock, flags); sdev = q->queuedata; - if (sdev && sdev->scsi_dh_data) + if (!sdev) { + spin_unlock_irqrestore(q->queue_lock, flags); + err = SCSI_DH_NOSYS; + if (fn) + fn(data, err); + return err; + } + + if (sdev->scsi_dh_data) scsi_dh = sdev->scsi_dh_data->scsi_dh; - if (!scsi_dh || !get_device(&sdev->sdev_gendev) || + dev = get_device(&sdev->sdev_gendev); + if (!scsi_dh || !dev || sdev->sdev_state == SDEV_CANCEL || sdev->sdev_state == SDEV_DEL) err = SCSI_DH_NOSYS; @@ -410,12 +411,13 @@ int scsi_dh_activate(struct request_queue *q, activate_complete fn, void *data) if (err) { if (fn) fn(data, err); - return err; + goto out; } if (scsi_dh->activate) err = scsi_dh->activate(sdev, fn, data); - put_device(&sdev->sdev_gendev); +out: + put_device(dev); return err; } EXPORT_SYMBOL_GPL(scsi_dh_activate); @@ -465,8 +467,9 @@ int scsi_dh_handler_exist(const char *name) EXPORT_SYMBOL_GPL(scsi_dh_handler_exist); /* - * scsi_dh_handler_attach - Attach device handler - * @sdev - sdev the handler should be attached to + * scsi_dh_attach - Attach device handler + * @q - Request queue that is associated with the scsi_device + * the handler should be attached to * @name - name of the handler to attach */ int scsi_dh_attach(struct request_queue *q, const char *name) @@ -495,8 +498,9 @@ int scsi_dh_attach(struct request_queue *q, const char *name) EXPORT_SYMBOL_GPL(scsi_dh_attach); /* - * scsi_dh_handler_detach - Detach device handler - * @sdev - sdev the handler should be detached from + * scsi_dh_detach - Detach device handler + * @q - Request queue that is associated with the scsi_device + * the handler should be detached from * * This function will detach the device handler only * if the sdev is not part of the internal list, ie @@ -525,6 +529,38 @@ void scsi_dh_detach(struct request_queue *q) } EXPORT_SYMBOL_GPL(scsi_dh_detach); +/* + * scsi_dh_attached_handler_name - Get attached device handler's name + * @q - Request queue that is associated with the scsi_device + * that may have a device handler attached + * @gfp - the GFP mask used in the kmalloc() call when allocating memory + * + * Returns name of attached handler, NULL if no handler is attached. + * Caller must take care to free the returned string. + */ +const char *scsi_dh_attached_handler_name(struct request_queue *q, gfp_t gfp) +{ + unsigned long flags; + struct scsi_device *sdev; + const char *handler_name = NULL; + + spin_lock_irqsave(q->queue_lock, flags); + sdev = q->queuedata; + if (!sdev || !get_device(&sdev->sdev_gendev)) + sdev = NULL; + spin_unlock_irqrestore(q->queue_lock, flags); + + if (!sdev) + return NULL; + + if (sdev->scsi_dh_data) + handler_name = kstrdup(sdev->scsi_dh_data->scsi_dh->name, gfp); + + put_device(&sdev->sdev_gendev); + return handler_name; +} +EXPORT_SYMBOL_GPL(scsi_dh_attached_handler_name); + static struct notifier_block scsi_dh_nb = { .notifier_call = scsi_dh_notifier }; @@ -533,10 +569,6 @@ static int __init scsi_dh_init(void) { int r; - r = scsi_dev_info_add_list(SCSI_DEVINFO_DH, "SCSI Device Handler"); - if (r) - return r; - r = bus_register_notifier(&scsi_bus_type, &scsi_dh_nb); if (!r) @@ -551,7 +583,6 @@ static void __exit scsi_dh_exit(void) bus_for_each_dev(&scsi_bus_type, NULL, NULL, scsi_dh_sysfs_attr_remove); bus_unregister_notifier(&scsi_bus_type, &scsi_dh_nb); - scsi_dev_info_remove_list(SCSI_DEVINFO_DH); } module_init(scsi_dh_init); diff --git a/drivers/scsi/device_handler/scsi_dh_alua.c b/drivers/scsi/device_handler/scsi_dh_alua.c index 42fe52902ad..7bcf67eec92 100644 --- a/drivers/scsi/device_handler/scsi_dh_alua.c +++ b/drivers/scsi/device_handler/scsi_dh_alua.c @@ -21,6 +21,7 @@ */ #include <linux/slab.h> #include <linux/delay.h> +#include <linux/module.h> #include <scsi/scsi.h> #include <scsi/scsi_eh.h> #include <scsi/scsi_dh.h> @@ -45,23 +46,32 @@ #define TPGS_SUPPORT_OFFLINE 0x40 #define TPGS_SUPPORT_TRANSITION 0x80 +#define RTPG_FMT_MASK 0x70 +#define RTPG_FMT_EXT_HDR 0x10 + #define TPGS_MODE_UNINITIALIZED -1 #define TPGS_MODE_NONE 0x0 #define TPGS_MODE_IMPLICIT 0x1 #define TPGS_MODE_EXPLICIT 0x2 #define ALUA_INQUIRY_SIZE 36 -#define ALUA_FAILOVER_TIMEOUT (60 * HZ) +#define ALUA_FAILOVER_TIMEOUT 60 #define ALUA_FAILOVER_RETRIES 5 +/* flags passed from user level */ +#define ALUA_OPTIMIZE_STPG 1 + struct alua_dh_data { int group_id; int rel_port; int tpgs; int state; + int pref; + unsigned flags; /* used for optimizing STPG */ unsigned char inq[ALUA_INQUIRY_SIZE]; unsigned char *buff; int bufflen; + unsigned char transition_tmo; unsigned char sense[SCSI_SENSE_BUFFERSIZE]; int senselen; struct scsi_device *sdev; @@ -110,6 +120,7 @@ static struct request *get_alua_req(struct scsi_device *sdev, "%s: blk_get_request failed\n", __func__); return NULL; } + blk_rq_set_block_pc(rq); if (buflen && blk_rq_map_kern(q, rq, buffer, buflen, GFP_NOIO)) { blk_put_request(rq); @@ -118,53 +129,15 @@ static struct request *get_alua_req(struct scsi_device *sdev, return NULL; } - rq->cmd_type = REQ_TYPE_BLOCK_PC; rq->cmd_flags |= REQ_FAILFAST_DEV | REQ_FAILFAST_TRANSPORT | REQ_FAILFAST_DRIVER; rq->retries = ALUA_FAILOVER_RETRIES; - rq->timeout = ALUA_FAILOVER_TIMEOUT; + rq->timeout = ALUA_FAILOVER_TIMEOUT * HZ; return rq; } /* - * submit_std_inquiry - Issue a standard INQUIRY command - * @sdev: sdev the command should be send to - */ -static int submit_std_inquiry(struct scsi_device *sdev, struct alua_dh_data *h) -{ - struct request *rq; - int err = SCSI_DH_RES_TEMP_UNAVAIL; - - rq = get_alua_req(sdev, h->inq, ALUA_INQUIRY_SIZE, READ); - if (!rq) - goto done; - - /* Prepare the command. */ - rq->cmd[0] = INQUIRY; - rq->cmd[1] = 0; - rq->cmd[2] = 0; - rq->cmd[4] = ALUA_INQUIRY_SIZE; - rq->cmd_len = COMMAND_SIZE(INQUIRY); - - rq->sense = h->sense; - memset(rq->sense, 0, SCSI_SENSE_BUFFERSIZE); - rq->sense_len = h->senselen = 0; - - err = blk_execute_rq(rq->q, NULL, rq, 1); - if (err == -EIO) { - sdev_printk(KERN_INFO, sdev, - "%s: std inquiry failed with %x\n", - ALUA_DH_NAME, rq->errors); - h->senselen = rq->sense_len; - err = SCSI_DH_IO; - } - blk_put_request(rq); -done: - return err; -} - -/* * submit_vpd_inquiry - Issue an INQUIRY VPD page 0x83 command * @sdev: sdev the command should be sent to */ @@ -205,7 +178,8 @@ done: * submit_rtpg - Issue a REPORT TARGET GROUP STATES command * @sdev: sdev the command should be sent to */ -static unsigned submit_rtpg(struct scsi_device *sdev, struct alua_dh_data *h) +static unsigned submit_rtpg(struct scsi_device *sdev, struct alua_dh_data *h, + bool rtpg_ext_hdr_req) { struct request *rq; int err = SCSI_DH_RES_TEMP_UNAVAIL; @@ -216,7 +190,10 @@ static unsigned submit_rtpg(struct scsi_device *sdev, struct alua_dh_data *h) /* Prepare the command. */ rq->cmd[0] = MAINTENANCE_IN; - rq->cmd[1] = MI_REPORT_TARGET_PGS; + if (rtpg_ext_hdr_req) + rq->cmd[1] = MI_REPORT_TARGET_PGS | MI_EXT_HDR_PARAM_FMT; + else + rq->cmd[1] = MI_REPORT_TARGET_PGS; rq->cmd[6] = (h->bufflen >> 24) & 0xff; rq->cmd[7] = (h->bufflen >> 16) & 0xff; rq->cmd[8] = (h->bufflen >> 8) & 0xff; @@ -255,13 +232,13 @@ static void stpg_endio(struct request *req, int error) struct scsi_sense_hdr sense_hdr; unsigned err = SCSI_DH_OK; - if (error || host_byte(req->errors) != DID_OK || - msg_byte(req->errors) != COMMAND_COMPLETE) { + if (host_byte(req->errors) != DID_OK || + msg_byte(req->errors) != COMMAND_COMPLETE) { err = SCSI_DH_IO; goto done; } - if (h->senselen > 0) { + if (req->sense_len > 0) { err = scsi_normalize_sense(h->sense, SCSI_SENSE_BUFFERSIZE, &sense_hdr); if (!err) { @@ -278,7 +255,9 @@ static void stpg_endio(struct request *req, int error) ALUA_DH_NAME, sense_hdr.sense_key, sense_hdr.asc, sense_hdr.ascq); err = SCSI_DH_IO; - } + } else if (error) + err = SCSI_DH_IO; + if (err == SCSI_DH_OK) { h->state = TPGS_STATE_OPTIMIZED; sdev_printk(KERN_INFO, h->sdev, @@ -338,23 +317,17 @@ static unsigned submit_stpg(struct alua_dh_data *h) } /* - * alua_std_inquiry - Evaluate standard INQUIRY command + * alua_check_tpgs - Evaluate TPGS setting * @sdev: device to be checked * - * Just extract the TPGS setting to find out if ALUA + * Examine the TPGS setting of the sdev to find out if ALUA * is supported. */ -static int alua_std_inquiry(struct scsi_device *sdev, struct alua_dh_data *h) +static int alua_check_tpgs(struct scsi_device *sdev, struct alua_dh_data *h) { - int err; - - err = submit_std_inquiry(sdev, h); - - if (err != SCSI_DH_OK) - return err; + int err = SCSI_DH_OK; - /* Check TPGS setting */ - h->tpgs = (h->inq[5] >> 4) & 0x3; + h->tpgs = scsi_device_tpgs(sdev); switch (h->tpgs) { case TPGS_MODE_EXPLICIT|TPGS_MODE_IMPLICIT: sdev_printk(KERN_INFO, sdev, @@ -508,27 +481,38 @@ static int alua_check_sense(struct scsi_device *sdev, * Power On, Reset, or Bus Device Reset, just retry. */ return ADD_TO_MLQUEUE; - if (sense_hdr->asc == 0x2a && sense_hdr->ascq == 0x06) { + if (sense_hdr->asc == 0x29 && sense_hdr->ascq == 0x04) + /* + * Device internal reset + */ + return ADD_TO_MLQUEUE; + if (sense_hdr->asc == 0x2a && sense_hdr->ascq == 0x01) + /* + * Mode Parameters Changed + */ + return ADD_TO_MLQUEUE; + if (sense_hdr->asc == 0x2a && sense_hdr->ascq == 0x06) /* * ALUA state changed */ return ADD_TO_MLQUEUE; - } - if (sense_hdr->asc == 0x2a && sense_hdr->ascq == 0x07) { + if (sense_hdr->asc == 0x2a && sense_hdr->ascq == 0x07) /* * Implicit ALUA state transition failed */ return ADD_TO_MLQUEUE; - } - if (sense_hdr->asc == 0x3f && sense_hdr->ascq == 0x0e) { + if (sense_hdr->asc == 0x3f && sense_hdr->ascq == 0x03) + /* + * Inquiry data has changed + */ + return ADD_TO_MLQUEUE; + if (sense_hdr->asc == 0x3f && sense_hdr->ascq == 0x0e) /* * REPORTED_LUNS_DATA_HAS_CHANGED is reported * when switching controllers on targets like * Intel Multi-Flex. We can just retry. */ return ADD_TO_MLQUEUE; - } - break; } @@ -538,22 +522,30 @@ static int alua_check_sense(struct scsi_device *sdev, /* * alua_rtpg - Evaluate REPORT TARGET GROUP STATES * @sdev: the device to be evaluated. + * @wait_for_transition: if nonzero, wait ALUA_FAILOVER_TIMEOUT seconds for device to exit transitioning state * * Evaluate the Target Port Group State. * Returns SCSI_DH_DEV_OFFLINED if the path is * found to be unusable. */ -static int alua_rtpg(struct scsi_device *sdev, struct alua_dh_data *h) +static int alua_rtpg(struct scsi_device *sdev, struct alua_dh_data *h, int wait_for_transition) { struct scsi_sense_hdr sense_hdr; int len, k, off, valid_states = 0; - char *ucp; + unsigned char *ucp; unsigned err; - unsigned long expiry, interval = 10; + bool rtpg_ext_hdr_req = 1; + unsigned long expiry, interval = 0; + unsigned int tpg_desc_tbl_off; + unsigned char orig_transition_tmo; + + if (!h->transition_tmo) + expiry = round_jiffies_up(jiffies + ALUA_FAILOVER_TIMEOUT * HZ); + else + expiry = round_jiffies_up(jiffies + h->transition_tmo * HZ); - expiry = round_jiffies_up(jiffies + ALUA_FAILOVER_TIMEOUT); retry: - err = submit_rtpg(sdev, h); + err = submit_rtpg(sdev, h, rtpg_ext_hdr_req); if (err == SCSI_DH_IO && h->senselen > 0) { err = scsi_normalize_sense(h->sense, SCSI_SENSE_BUFFERSIZE, @@ -561,6 +553,21 @@ static int alua_rtpg(struct scsi_device *sdev, struct alua_dh_data *h) if (!err) return SCSI_DH_IO; + /* + * submit_rtpg() has failed on existing arrays + * when requesting extended header info, and + * the array doesn't support extended headers, + * even though it shouldn't according to T10. + * The retry without rtpg_ext_hdr_req set + * handles this. + */ + if (rtpg_ext_hdr_req == 1 && + sense_hdr.sense_key == ILLEGAL_REQUEST && + sense_hdr.asc == 0x24 && sense_hdr.ascq == 0) { + rtpg_ext_hdr_req = 0; + goto retry; + } + err = alua_check_sense(sdev, &sense_hdr); if (err == ADD_TO_MLQUEUE && time_before(jiffies, expiry)) goto retry; @@ -587,17 +594,40 @@ static int alua_rtpg(struct scsi_device *sdev, struct alua_dh_data *h) goto retry; } - for (k = 4, ucp = h->buff + 4; k < len; k += off, ucp += off) { + orig_transition_tmo = h->transition_tmo; + if ((h->buff[4] & RTPG_FMT_MASK) == RTPG_FMT_EXT_HDR && h->buff[5] != 0) + h->transition_tmo = h->buff[5]; + else + h->transition_tmo = ALUA_FAILOVER_TIMEOUT; + + if (wait_for_transition && (orig_transition_tmo != h->transition_tmo)) { + sdev_printk(KERN_INFO, sdev, + "%s: transition timeout set to %d seconds\n", + ALUA_DH_NAME, h->transition_tmo); + expiry = jiffies + h->transition_tmo * HZ; + } + + if ((h->buff[4] & RTPG_FMT_MASK) == RTPG_FMT_EXT_HDR) + tpg_desc_tbl_off = 8; + else + tpg_desc_tbl_off = 4; + + for (k = tpg_desc_tbl_off, ucp = h->buff + tpg_desc_tbl_off; + k < len; + k += off, ucp += off) { + if (h->group_id == (ucp[2] << 8) + ucp[3]) { h->state = ucp[0] & 0x0f; + h->pref = ucp[0] >> 7; valid_states = ucp[1]; } off = 8 + (ucp[7] * 4); } sdev_printk(KERN_INFO, sdev, - "%s: port group %02x state %c supports %c%c%c%c%c%c%c\n", + "%s: port group %02x state %c %s supports %c%c%c%c%c%c%c\n", ALUA_DH_NAME, h->group_id, print_alua_state(h->state), + h->pref ? "preferred" : "non-preferred", valid_states&TPGS_SUPPORT_TRANSITION?'T':'t', valid_states&TPGS_SUPPORT_OFFLINE?'O':'o', valid_states&TPGS_SUPPORT_LBA_DEPENDENT?'L':'l', @@ -608,19 +638,23 @@ static int alua_rtpg(struct scsi_device *sdev, struct alua_dh_data *h) switch (h->state) { case TPGS_STATE_TRANSITIONING: - if (time_before(jiffies, expiry)) { - /* State transition, retry */ - interval *= 10; - msleep(interval); - goto retry; + if (wait_for_transition) { + if (time_before(jiffies, expiry)) { + /* State transition, retry */ + interval += 2000; + msleep(interval); + goto retry; + } + err = SCSI_DH_RETRY; + } else { + err = SCSI_DH_OK; } + /* Transitioning time exceeded, set port to standby */ - err = SCSI_DH_RETRY; h->state = TPGS_STATE_STANDBY; break; case TPGS_STATE_OFFLINE: - case TPGS_STATE_UNAVAILABLE: - /* Path unusable for unavailable/offline */ + /* Path unusable */ err = SCSI_DH_DEV_OFFLINED; break; default: @@ -642,7 +676,7 @@ static int alua_initialize(struct scsi_device *sdev, struct alua_dh_data *h) { int err; - err = alua_std_inquiry(sdev, h); + err = alua_check_tpgs(sdev, h); if (err != SCSI_DH_OK) goto out; @@ -650,13 +684,48 @@ static int alua_initialize(struct scsi_device *sdev, struct alua_dh_data *h) if (err != SCSI_DH_OK) goto out; - err = alua_rtpg(sdev, h); + err = alua_rtpg(sdev, h, 0); if (err != SCSI_DH_OK) goto out; out: return err; } +/* + * alua_set_params - set/unset the optimize flag + * @sdev: device on the path to be activated + * params - parameters in the following format + * "no_of_params\0param1\0param2\0param3\0...\0" + * For example, to set the flag pass the following parameters + * from multipath.conf + * hardware_handler "2 alua 1" + */ +static int alua_set_params(struct scsi_device *sdev, const char *params) +{ + struct alua_dh_data *h = get_alua_data(sdev); + unsigned int optimize = 0, argc; + const char *p = params; + int result = SCSI_DH_OK; + + if ((sscanf(params, "%u", &argc) != 1) || (argc != 1)) + return -EINVAL; + + while (*p++) + ; + if ((sscanf(p, "%u", &optimize) != 1) || (optimize > 1)) + return -EINVAL; + + if (optimize) + h->flags |= ALUA_OPTIMIZE_STPG; + else + h->flags &= ~ALUA_OPTIMIZE_STPG; + + return result; +} + +static uint optimize_stpg; +module_param(optimize_stpg, uint, S_IRUGO|S_IWUSR); +MODULE_PARM_DESC(optimize_stpg, "Allow use of a non-optimized path, rather than sending a STPG, when implicit TPGS is supported (0=No,1=Yes). Default is 0."); /* * alua_activate - activate a path @@ -673,16 +742,40 @@ static int alua_activate(struct scsi_device *sdev, { struct alua_dh_data *h = get_alua_data(sdev); int err = SCSI_DH_OK; + int stpg = 0; + + err = alua_rtpg(sdev, h, 1); + if (err != SCSI_DH_OK) + goto out; - if (h->group_id != -1) { - err = alua_rtpg(sdev, h); - if (err != SCSI_DH_OK) - goto out; + if (optimize_stpg) + h->flags |= ALUA_OPTIMIZE_STPG; + + if (h->tpgs & TPGS_MODE_EXPLICIT) { + switch (h->state) { + case TPGS_STATE_NONOPTIMIZED: + stpg = 1; + if ((h->flags & ALUA_OPTIMIZE_STPG) && + (!h->pref) && + (h->tpgs & TPGS_MODE_IMPLICIT)) + stpg = 0; + break; + case TPGS_STATE_STANDBY: + case TPGS_STATE_UNAVAILABLE: + stpg = 1; + break; + case TPGS_STATE_OFFLINE: + err = SCSI_DH_IO; + break; + case TPGS_STATE_TRANSITIONING: + err = SCSI_DH_RETRY; + break; + default: + break; + } } - if (h->tpgs & TPGS_MODE_EXPLICIT && - h->state != TPGS_STATE_OPTIMIZED && - h->state != TPGS_STATE_LBA_DEPENDENT) { + if (stpg) { h->callback_fn = fn; h->callback_data = data; err = submit_stpg(h); @@ -720,23 +813,10 @@ static int alua_prep_fn(struct scsi_device *sdev, struct request *req) } -static const struct scsi_dh_devlist alua_dev_list[] = { - {"HP", "MSA VOLUME" }, - {"HP", "HSV101" }, - {"HP", "HSV111" }, - {"HP", "HSV200" }, - {"HP", "HSV210" }, - {"HP", "HSV300" }, - {"IBM", "2107900" }, - {"IBM", "2145" }, - {"Pillar", "Axiom" }, - {"Intel", "Multi-Flex"}, - {"NETAPP", "LUN"}, - {"NETAPP", "LUN C-Mode"}, - {"AIX", "NVDISK"}, - {"Promise", "VTrak"}, - {NULL, NULL} -}; +static bool alua_match(struct scsi_device *sdev) +{ + return (scsi_device_tpgs(sdev) != 0); +} static int alua_bus_attach(struct scsi_device *sdev); static void alua_bus_detach(struct scsi_device *sdev); @@ -744,12 +824,13 @@ static void alua_bus_detach(struct scsi_device *sdev); static struct scsi_device_handler alua_dh = { .name = ALUA_DH_NAME, .module = THIS_MODULE, - .devlist = alua_dev_list, .attach = alua_bus_attach, .detach = alua_bus_detach, .prep_fn = alua_prep_fn, .check_sense = alua_check_sense, .activate = alua_activate, + .set_params = alua_set_params, + .match = alua_match, }; /* @@ -782,7 +863,7 @@ static int alua_bus_attach(struct scsi_device *sdev) h->sdev = sdev; err = alua_initialize(sdev, h); - if (err != SCSI_DH_OK) + if ((err != SCSI_DH_OK) && (err != SCSI_DH_DEV_OFFLINED)) goto failed; if (!try_module_get(THIS_MODULE)) @@ -791,6 +872,7 @@ static int alua_bus_attach(struct scsi_device *sdev) 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, "%s: Attached\n", ALUA_DH_NAME); return 0; diff --git a/drivers/scsi/device_handler/scsi_dh_emc.c b/drivers/scsi/device_handler/scsi_dh_emc.c index 48441f6908a..6f07f7fe3aa 100644 --- a/drivers/scsi/device_handler/scsi_dh_emc.c +++ b/drivers/scsi/device_handler/scsi_dh_emc.c @@ -21,6 +21,7 @@ * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */ #include <linux/slab.h> +#include <linux/module.h> #include <scsi/scsi.h> #include <scsi/scsi_eh.h> #include <scsi/scsi_dh.h> @@ -279,6 +280,7 @@ static struct request *get_req(struct scsi_device *sdev, int cmd, return NULL; } + blk_rq_set_block_pc(rq); rq->cmd_len = COMMAND_SIZE(cmd); rq->cmd[0] = cmd; @@ -303,7 +305,6 @@ static struct request *get_req(struct scsi_device *sdev, int cmd, break; } - rq->cmd_type = REQ_TYPE_BLOCK_PC; rq->cmd_flags |= REQ_FAILFAST_DEV | REQ_FAILFAST_TRANSPORT | REQ_FAILFAST_DRIVER; rq->timeout = CLARIION_TIMEOUT; @@ -628,6 +629,24 @@ static const struct scsi_dh_devlist clariion_dev_list[] = { {NULL, NULL}, }; +static bool clariion_match(struct scsi_device *sdev) +{ + int i; + + if (scsi_device_tpgs(sdev)) + return false; + + 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))) { + return true; + } + } + return false; +} + static int clariion_bus_attach(struct scsi_device *sdev); static void clariion_bus_detach(struct scsi_device *sdev); @@ -641,6 +660,7 @@ static struct scsi_device_handler clariion_dh = { .activate = clariion_activate, .prep_fn = clariion_prep_fn, .set_params = clariion_set_params, + .match = clariion_match, }; static int clariion_bus_attach(struct scsi_device *sdev) diff --git a/drivers/scsi/device_handler/scsi_dh_hp_sw.c b/drivers/scsi/device_handler/scsi_dh_hp_sw.c index b479f1eef96..e9d9fea9e27 100644 --- a/drivers/scsi/device_handler/scsi_dh_hp_sw.c +++ b/drivers/scsi/device_handler/scsi_dh_hp_sw.c @@ -22,6 +22,7 @@ */ #include <linux/slab.h> +#include <linux/module.h> #include <scsi/scsi.h> #include <scsi/scsi_dbg.h> #include <scsi/scsi_eh.h> @@ -119,7 +120,7 @@ retry: if (!req) return SCSI_DH_RES_TEMP_UNAVAIL; - req->cmd_type = REQ_TYPE_BLOCK_PC; + blk_rq_set_block_pc(req); req->cmd_flags |= REQ_FAILFAST_DEV | REQ_FAILFAST_TRANSPORT | REQ_FAILFAST_DRIVER; req->cmd_len = COMMAND_SIZE(TEST_UNIT_READY); @@ -249,7 +250,7 @@ static int hp_sw_start_stop(struct hp_sw_dh_data *h) if (!req) return SCSI_DH_RES_TEMP_UNAVAIL; - req->cmd_type = REQ_TYPE_BLOCK_PC; + blk_rq_set_block_pc(req); req->cmd_flags |= REQ_FAILFAST_DEV | REQ_FAILFAST_TRANSPORT | REQ_FAILFAST_DRIVER; req->cmd_len = COMMAND_SIZE(START_STOP); @@ -319,6 +320,24 @@ static const struct scsi_dh_devlist hp_sw_dh_data_list[] = { {NULL, NULL}, }; +static bool hp_sw_match(struct scsi_device *sdev) +{ + int i; + + if (scsi_device_tpgs(sdev)) + return false; + + 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))) { + return true; + } + } + return false; +} + static int hp_sw_bus_attach(struct scsi_device *sdev); static void hp_sw_bus_detach(struct scsi_device *sdev); @@ -330,6 +349,7 @@ static struct scsi_device_handler hp_sw_dh = { .detach = hp_sw_bus_detach, .activate = hp_sw_activate, .prep_fn = hp_sw_prep_fn, + .match = hp_sw_match, }; static int hp_sw_bus_attach(struct scsi_device *sdev) diff --git a/drivers/scsi/device_handler/scsi_dh_rdac.c b/drivers/scsi/device_handler/scsi_dh_rdac.c index 293c183dfe6..826069db984 100644 --- a/drivers/scsi/device_handler/scsi_dh_rdac.c +++ b/drivers/scsi/device_handler/scsi_dh_rdac.c @@ -1,5 +1,5 @@ /* - * Engenio/LSI RDAC SCSI Device Handler + * LSI/Engenio/NetApp E-Series RDAC SCSI Device Handler * * Copyright (C) 2005 Mike Christie. All rights reserved. * Copyright (C) Chandra Seetharaman, IBM Corp. 2007 @@ -24,6 +24,7 @@ #include <scsi/scsi_dh.h> #include <linux/workqueue.h> #include <linux/slab.h> +#include <linux/module.h> #define RDAC_NAME "rdac" #define RDAC_RETRY_COUNT 5 @@ -35,7 +36,7 @@ * mode page were taken from the LSI RDAC 2.4 GPL'd * driver, and then converted to Linux conventions. */ -#define RDAC_QUIESCENCE_TIME 20; +#define RDAC_QUIESCENCE_TIME 20 /* * Page Codes */ @@ -128,25 +129,7 @@ struct c4_inquiry { 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; - u8 index; - u8 array_name[ARRAY_LABEL_LEN]; - spinlock_t ms_lock; - int ms_queued; - struct work_struct ms_work; - struct scsi_device *ms_sdev; - struct list_head ms_head; -}; - +#define UNIQUE_ID_LEN 16 struct c8_inquiry { u8 peripheral_info; u8 page_code; /* 0xC8 */ @@ -159,12 +142,31 @@ struct c8_inquiry { u8 vol_user_label_len; u8 vol_user_label[60]; u8 array_uniq_id_len; - u8 array_unique_id[16]; + u8 array_unique_id[UNIQUE_ID_LEN]; u8 array_user_label_len; u8 array_user_label[60]; u8 lun[8]; }; +struct rdac_controller { + u8 array_id[UNIQUE_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; + u8 index; + u8 array_name[ARRAY_LABEL_LEN]; + struct Scsi_Host *host; + spinlock_t ms_lock; + int ms_queued; + struct work_struct ms_work; + struct scsi_device *ms_sdev; + struct list_head ms_head; +}; + struct c2_inquiry { u8 peripheral_info; u8 page_code; /* 0xC2 */ @@ -182,14 +184,24 @@ struct rdac_dh_data { struct rdac_controller *ctlr; #define UNINITIALIZED_LUN (1 << 8) unsigned lun; + +#define RDAC_MODE 0 +#define RDAC_MODE_AVT 1 +#define RDAC_MODE_IOSHIP 2 + unsigned char mode; + #define RDAC_STATE_ACTIVE 0 #define RDAC_STATE_PASSIVE 1 unsigned char state; #define RDAC_LUN_UNOWNED 0 #define RDAC_LUN_OWNED 1 -#define RDAC_LUN_AVT 2 char lun_state; + +#define RDAC_PREFERRED 0 +#define RDAC_NON_PREFERRED 1 + char preferred; + unsigned char sense[SCSI_SENSE_BUFFERSIZE]; union { struct c2_inquiry c2; @@ -199,11 +211,15 @@ struct rdac_dh_data { } inq; }; +static const char *mode[] = { + "RDAC", + "AVT", + "IOSHIP", +}; static const char *lun_state[] = { "unowned", "owned", - "owned (AVT mode)", }; struct rdac_queue_data { @@ -263,6 +279,7 @@ static struct request *get_rdac_req(struct scsi_device *sdev, "get_rdac_req: blk_get_request failed.\n"); return NULL; } + blk_rq_set_block_pc(rq); if (buflen && blk_rq_map_kern(q, rq, buffer, buflen, GFP_NOIO)) { blk_put_request(rq); @@ -271,7 +288,6 @@ static struct request *get_rdac_req(struct scsi_device *sdev, return NULL; } - rq->cmd_type = REQ_TYPE_BLOCK_PC; rq->cmd_flags |= REQ_FAILFAST_DEV | REQ_FAILFAST_TRANSPORT | REQ_FAILFAST_DRIVER; rq->retries = RDAC_RETRIES; @@ -348,43 +364,33 @@ static void release_controller(struct kref *kref) struct rdac_controller *ctlr; ctlr = container_of(kref, struct rdac_controller, kref); - flush_workqueue(kmpath_rdacd); - 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, - char *array_name) +static struct rdac_controller *get_controller(int index, char *array_name, + u8 *array_id, struct scsi_device *sdev) { 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)) { + if ((memcmp(tmp->array_id, array_id, UNIQUE_ID_LEN) == 0) && + (tmp->index == index) && + (tmp->host == sdev->host)) { kref_get(&tmp->kref); - spin_unlock(&list_lock); return tmp; } } ctlr = kmalloc(sizeof(*ctlr), GFP_ATOMIC); if (!ctlr) - goto done; + return NULL; /* initialize fields of controller */ - memcpy(ctlr->subsys_id, subsys_id, SUBSYS_ID_LEN); - memcpy(ctlr->slot_id, slot_id, SLOT_ID_LEN); + memcpy(ctlr->array_id, array_id, UNIQUE_ID_LEN); + ctlr->index = index; + ctlr->host = sdev->host; memcpy(ctlr->array_name, array_name, ARRAY_LABEL_LEN); - /* update the controller index */ - if (slot_id[1] == 0x31) - ctlr->index = 0; - else - ctlr->index = 1; - kref_init(&ctlr->kref); ctlr->use_ms10 = -1; ctlr->ms_queued = 0; @@ -393,8 +399,7 @@ static struct rdac_controller *get_controller(u8 *subsys_id, u8 *slot_id, INIT_WORK(&ctlr->ms_work, send_mode_select); INIT_LIST_HEAD(&ctlr->ms_head); list_add(&ctlr->node, &ctlr_list); -done: - spin_unlock(&list_lock); + return ctlr; } @@ -430,7 +435,7 @@ done: } static int get_lun_info(struct scsi_device *sdev, struct rdac_dh_data *h, - char *array_name) + char *array_name, u8 *array_id) { int err, i; struct c8_inquiry *inqp; @@ -449,6 +454,8 @@ static int get_lun_info(struct scsi_device *sdev, struct rdac_dh_data *h, *(array_name+i) = inqp->array_user_label[(2*i)+1]; *(array_name+ARRAY_LABEL_LEN-1) = '\0'; + memset(array_id, 0, UNIQUE_ID_LEN); + memcpy(array_id, inqp->array_unique_id, inqp->array_uniq_id_len); } return err; } @@ -458,42 +465,57 @@ static int check_ownership(struct scsi_device *sdev, struct rdac_dh_data *h) int err; struct c9_inquiry *inqp; - h->lun_state = RDAC_LUN_UNOWNED; h->state = RDAC_STATE_ACTIVE; err = submit_inquiry(sdev, 0xC9, sizeof(struct c9_inquiry), h); if (err == SCSI_DH_OK) { inqp = &h->inq.c9; - if ((inqp->avte_cvp >> 7) == 0x1) { - /* LUN in AVT mode */ - sdev_printk(KERN_NOTICE, sdev, - "%s: AVT mode detected\n", - RDAC_NAME); - h->lun_state = RDAC_LUN_AVT; - } else if ((inqp->avte_cvp & 0x1) != 0) { - /* LUN was owned by the controller */ + /* detect the operating mode */ + if ((inqp->avte_cvp >> 5) & 0x1) + h->mode = RDAC_MODE_IOSHIP; /* LUN in IOSHIP mode */ + else if (inqp->avte_cvp >> 7) + h->mode = RDAC_MODE_AVT; /* LUN in AVT mode */ + else + h->mode = RDAC_MODE; /* LUN in RDAC mode */ + + /* Update ownership */ + if (inqp->avte_cvp & 0x1) h->lun_state = RDAC_LUN_OWNED; + else { + h->lun_state = RDAC_LUN_UNOWNED; + if (h->mode == RDAC_MODE) + h->state = RDAC_STATE_PASSIVE; } - } - if (h->lun_state == RDAC_LUN_UNOWNED) - h->state = RDAC_STATE_PASSIVE; + /* Update path prio*/ + if (inqp->path_prio & 0x1) + h->preferred = RDAC_PREFERRED; + else + h->preferred = RDAC_NON_PREFERRED; + } return err; } static int initialize_controller(struct scsi_device *sdev, - struct rdac_dh_data *h, char *array_name) + struct rdac_dh_data *h, char *array_name, u8 *array_id) { - int err; + int err, index; struct c4_inquiry *inqp; err = submit_inquiry(sdev, 0xC4, sizeof(struct c4_inquiry), h); if (err == SCSI_DH_OK) { inqp = &h->inq.c4; - h->ctlr = get_controller(inqp->subsys_id, inqp->slot_id, - array_name); + /* get the controller index */ + if (inqp->slot_id[1] == 0x31) + index = 0; + else + index = 1; + + spin_lock(&list_lock); + h->ctlr = get_controller(index, array_name, array_id, sdev); if (!h->ctlr) err = SCSI_DH_RES_TEMP_UNAVAIL; + spin_unlock(&list_lock); } return err; } @@ -648,12 +670,27 @@ static int rdac_activate(struct scsi_device *sdev, { struct rdac_dh_data *h = get_rdac_data(sdev); int err = SCSI_DH_OK; + int act = 0; err = check_ownership(sdev, h); if (err != SCSI_DH_OK) goto done; - if (h->lun_state == RDAC_LUN_UNOWNED) { + switch (h->mode) { + case RDAC_MODE: + if (h->lun_state == RDAC_LUN_UNOWNED) + act = 1; + break; + case RDAC_MODE_IOSHIP: + if ((h->lun_state == RDAC_LUN_UNOWNED) && + (h->preferred == RDAC_PREFERRED)) + act = 1; + break; + default: + break; + } + + if (act) { err = queue_mode_select(sdev, fn, data); if (err == SCSI_DH_OK) return 0; @@ -749,34 +786,45 @@ static const struct scsi_dh_devlist rdac_dev_list[] = { {"IBM", "1742"}, {"IBM", "1745"}, {"IBM", "1746"}, + {"IBM", "1813"}, {"IBM", "1814"}, {"IBM", "1815"}, {"IBM", "1818"}, {"IBM", "3526"}, - {"SGI", "TP9400"}, - {"SGI", "TP9500"}, + {"SGI", "TP9"}, {"SGI", "IS"}, {"STK", "OPENstorage D280"}, - {"SUN", "CSM200_R"}, - {"SUN", "LCSM100_I"}, - {"SUN", "LCSM100_S"}, - {"SUN", "LCSM100_E"}, - {"SUN", "LCSM100_F"}, - {"DELL", "MD3000"}, - {"DELL", "MD3000i"}, - {"DELL", "MD32xx"}, - {"DELL", "MD32xxi"}, - {"DELL", "MD36xxi"}, - {"DELL", "MD36xxf"}, - {"LSI", "INF-01-00"}, - {"ENGENIO", "INF-01-00"}, {"STK", "FLEXLINE 380"}, - {"SUN", "CSM100_R_FC"}, + {"SUN", "CSM"}, + {"SUN", "LCSM100"}, {"SUN", "STK6580_6780"}, {"SUN", "SUN_6180"}, + {"SUN", "ArrayStorage"}, + {"DELL", "MD3"}, + {"NETAPP", "INF-01-00"}, + {"LSI", "INF-01-00"}, + {"ENGENIO", "INF-01-00"}, {NULL, NULL}, }; +static bool rdac_match(struct scsi_device *sdev) +{ + int i; + + if (scsi_device_tpgs(sdev)) + return false; + + 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))) { + return true; + } + } + return false; +} + static int rdac_bus_attach(struct scsi_device *sdev); static void rdac_bus_detach(struct scsi_device *sdev); @@ -789,6 +837,7 @@ static struct scsi_device_handler rdac_dh = { .attach = rdac_bus_attach, .detach = rdac_bus_detach, .activate = rdac_activate, + .match = rdac_match, }; static int rdac_bus_attach(struct scsi_device *sdev) @@ -798,13 +847,14 @@ static int rdac_bus_attach(struct scsi_device *sdev) unsigned long flags; int err; char array_name[ARRAY_LABEL_LEN]; + char array_id[UNIQUE_ID_LEN]; scsi_dh_data = kzalloc(sizeof(*scsi_dh_data) + sizeof(*h) , GFP_KERNEL); if (!scsi_dh_data) { sdev_printk(KERN_ERR, sdev, "%s: Attach failed\n", RDAC_NAME); - return 0; + return -ENOMEM; } scsi_dh_data->scsi_dh = &rdac_dh; @@ -812,11 +862,11 @@ static int rdac_bus_attach(struct scsi_device *sdev) h->lun = UNINITIALIZED_LUN; h->state = RDAC_STATE_ACTIVE; - err = get_lun_info(sdev, h, array_name); + err = get_lun_info(sdev, h, array_name, array_id); if (err != SCSI_DH_OK) goto failed; - err = initialize_controller(sdev, h, array_name); + err = initialize_controller(sdev, h, array_name, array_id); if (err != SCSI_DH_OK) goto failed; @@ -836,13 +886,16 @@ static int rdac_bus_attach(struct scsi_device *sdev) spin_unlock_irqrestore(sdev->request_queue->queue_lock, flags); sdev_printk(KERN_NOTICE, sdev, - "%s: LUN %d (%s)\n", - RDAC_NAME, h->lun, lun_state[(int)h->lun_state]); + "%s: LUN %d (%s) (%s)\n", + RDAC_NAME, h->lun, mode[(int)h->mode], + lun_state[(int)h->lun_state]); return 0; clean_ctlr: + spin_lock(&list_lock); kref_put(&h->ctlr->kref, release_controller); + spin_unlock(&list_lock); failed: kfree(scsi_dh_data); @@ -857,14 +910,19 @@ static void rdac_bus_detach( struct scsi_device *sdev ) struct rdac_dh_data *h; unsigned long flags; - spin_lock_irqsave(sdev->request_queue->queue_lock, flags); scsi_dh_data = sdev->scsi_dh_data; + h = (struct rdac_dh_data *) scsi_dh_data->buf; + if (h->ctlr && h->ctlr->ms_queued) + flush_workqueue(kmpath_rdacd); + + spin_lock_irqsave(sdev->request_queue->queue_lock, flags); sdev->scsi_dh_data = NULL; spin_unlock_irqrestore(sdev->request_queue->queue_lock, flags); - h = (struct rdac_dh_data *) scsi_dh_data->buf; + spin_lock(&list_lock); if (h->ctlr) kref_put(&h->ctlr->kref, release_controller); + spin_unlock(&list_lock); kfree(scsi_dh_data); module_put(THIS_MODULE); sdev_printk(KERN_NOTICE, sdev, "%s: Detached\n", RDAC_NAME); @@ -889,6 +947,8 @@ static int __init rdac_init(void) if (!kmpath_rdacd) { scsi_unregister_device_handler(&rdac_dh); printk(KERN_ERR "kmpath_rdacd creation failed.\n"); + + r = -EINVAL; } done: return r; @@ -903,7 +963,7 @@ static void __exit rdac_exit(void) module_init(rdac_init); module_exit(rdac_exit); -MODULE_DESCRIPTION("Multipath LSI/Engenio RDAC driver"); +MODULE_DESCRIPTION("Multipath LSI/Engenio/NetApp E-Series RDAC driver"); MODULE_AUTHOR("Mike Christie, Chandra Seetharaman"); MODULE_VERSION("01.00.0000.0000"); MODULE_LICENSE("GPL"); |
