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 | 96 | ||||
| -rw-r--r-- | drivers/scsi/device_handler/scsi_dh_alua.c | 190 | ||||
| -rw-r--r-- | drivers/scsi/device_handler/scsi_dh_emc.c | 21 | ||||
| -rw-r--r-- | drivers/scsi/device_handler/scsi_dh_hp_sw.c | 23 | ||||
| -rw-r--r-- | drivers/scsi/device_handler/scsi_dh_rdac.c | 75 |
6 files changed, 288 insertions, 121 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 23149b9e297..33e422e7583 100644 --- a/drivers/scsi/device_handler/scsi_dh.c +++ b/drivers/scsi/device_handler/scsi_dh.c @@ -28,7 +28,6 @@ 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) { @@ -45,21 +44,6 @@ 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) -{ - struct scsi_device_handler *tmp, *found = NULL; - - spin_lock(&list_lock); - list_for_each_entry(tmp, &scsi_dh_list, list) { - if (tmp->idx == idx) { - found = tmp; - break; - } - } - spin_unlock(&list_lock); - return found; -} - /* * device_handler_match_function - Match a device handler to a device * @sdev - SCSI device to be tested @@ -84,23 +68,6 @@ device_handler_match_function(struct scsi_device *sdev) } /* - * device_handler_match_devlist - Match a device handler to a device - * @sdev - SCSI device to be tested - * - * Tests @sdev against all device_handler registered in the devlist. - * Returns the found device handler or NULL if not found. - */ -static struct scsi_device_handler * -device_handler_match_devlist(struct scsi_device *sdev) -{ - int idx; - - idx = scsi_get_device_flags_keyed(sdev, sdev->vendor, sdev->model, - SCSI_DEVINFO_DH); - return get_device_handler_by_idx(idx); -} - -/* * device_handler_match - Attach a device handler to a device * @scsi_dh - The device handler to match against or NULL * @sdev - SCSI device to be tested against @scsi_dh @@ -116,8 +83,6 @@ device_handler_match(struct scsi_device_handler *scsi_dh, struct scsi_device_handler *found_dh; found_dh = device_handler_match_function(sdev); - if (!found_dh) - found_dh = device_handler_match_devlist(sdev); if (scsi_dh && found_dh != scsi_dh) found_dh = NULL; @@ -361,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 && 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); @@ -396,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; @@ -404,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 && 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); @@ -521,7 +468,8 @@ EXPORT_SYMBOL_GPL(scsi_dh_handler_exist); /* * scsi_dh_attach - Attach device handler - * @sdev - sdev the handler should be attached to + * @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) @@ -551,7 +499,8 @@ EXPORT_SYMBOL_GPL(scsi_dh_attach); /* * scsi_dh_detach - Detach device handler - * @sdev - sdev the handler should be detached from + * @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 @@ -580,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 }; @@ -588,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) @@ -606,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 e5a44235295..7bcf67eec92 100644 --- a/drivers/scsi/device_handler/scsi_dh_alua.c +++ b/drivers/scsi/device_handler/scsi_dh_alua.c @@ -46,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; @@ -111,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); @@ -119,11 +129,10 @@ 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; } @@ -169,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; @@ -180,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; @@ -219,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) { @@ -242,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, @@ -466,6 +481,16 @@ 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 == 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 @@ -497,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; unsigned char *ucp; unsigned err; - unsigned long expiry, interval = 1000; + 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, @@ -520,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; @@ -546,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', @@ -567,14 +638,19 @@ 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 *= 2; - 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: @@ -608,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 @@ -631,14 +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); + err = alua_rtpg(sdev, h, 1); if (err != SCSI_DH_OK) goto out; - if (h->tpgs & TPGS_MODE_EXPLICIT && - h->state != TPGS_STATE_OPTIMIZED && - h->state != TPGS_STATE_LBA_DEPENDENT) { + 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 (stpg) { h->callback_fn = fn; h->callback_data = data; err = submit_stpg(h); @@ -692,6 +829,7 @@ static struct scsi_device_handler alua_dh = { .prep_fn = alua_prep_fn, .check_sense = alua_check_sense, .activate = alua_activate, + .set_params = alua_set_params, .match = alua_match, }; diff --git a/drivers/scsi/device_handler/scsi_dh_emc.c b/drivers/scsi/device_handler/scsi_dh_emc.c index 591186cf189..6f07f7fe3aa 100644 --- a/drivers/scsi/device_handler/scsi_dh_emc.c +++ b/drivers/scsi/device_handler/scsi_dh_emc.c @@ -280,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; @@ -304,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; @@ -629,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); @@ -642,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 0f86a18b157..e9d9fea9e27 100644 --- a/drivers/scsi/device_handler/scsi_dh_hp_sw.c +++ b/drivers/scsi/device_handler/scsi_dh_hp_sw.c @@ -120,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); @@ -250,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); @@ -320,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); @@ -331,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 1d312792006..826069db984 100644 --- a/drivers/scsi/device_handler/scsi_dh_rdac.c +++ b/drivers/scsi/device_handler/scsi_dh_rdac.c @@ -279,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); @@ -287,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; @@ -364,10 +364,7 @@ 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); } @@ -376,20 +373,17 @@ static struct rdac_controller *get_controller(int index, char *array_name, { struct rdac_controller *ctlr, *tmp; - spin_lock(&list_lock); - list_for_each_entry(tmp, &ctlr_list, node) { 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->array_id, array_id, UNIQUE_ID_LEN); @@ -405,8 +399,7 @@ static struct rdac_controller *get_controller(int index, char *array_name, 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; } @@ -517,9 +510,12 @@ static int initialize_controller(struct scsi_device *sdev, 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; } @@ -790,36 +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", "TP9700"}, + {"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); @@ -832,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) @@ -848,7 +854,7 @@ static int rdac_bus_attach(struct scsi_device *sdev) 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; @@ -887,7 +893,9 @@ static int rdac_bus_attach(struct scsi_device *sdev) return 0; clean_ctlr: + spin_lock(&list_lock); kref_put(&h->ctlr->kref, release_controller); + spin_unlock(&list_lock); failed: kfree(scsi_dh_data); @@ -902,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); @@ -934,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; |
