diff options
Diffstat (limited to 'drivers/scsi/device_handler')
-rw-r--r-- | drivers/scsi/device_handler/scsi_dh.c | 28 | ||||
-rw-r--r-- | drivers/scsi/device_handler/scsi_dh_alua.c | 139 | ||||
-rw-r--r-- | drivers/scsi/device_handler/scsi_dh_emc.c | 7 | ||||
-rw-r--r-- | drivers/scsi/device_handler/scsi_dh_hp_sw.c | 94 | ||||
-rw-r--r-- | drivers/scsi/device_handler/scsi_dh_rdac.c | 115 |
5 files changed, 263 insertions, 120 deletions
diff --git a/drivers/scsi/device_handler/scsi_dh.c b/drivers/scsi/device_handler/scsi_dh.c index 3ee1cbc89479..e19a1a55270c 100644 --- a/drivers/scsi/device_handler/scsi_dh.c +++ b/drivers/scsi/device_handler/scsi_dh.c @@ -226,7 +226,7 @@ store_dh_state(struct device *dev, struct device_attribute *attr, * Activate a device handler */ if (scsi_dh->activate) - err = scsi_dh->activate(sdev); + err = scsi_dh->activate(sdev, NULL, NULL); else err = 0; } @@ -304,18 +304,15 @@ static int scsi_dh_notifier(struct notifier_block *nb, sdev = to_scsi_device(dev); if (action == BUS_NOTIFY_ADD_DEVICE) { + err = device_create_file(dev, &scsi_dh_state_attr); + /* don't care about err */ devinfo = device_handler_match(NULL, sdev); - if (!devinfo) - goto out; - - err = scsi_dh_handler_attach(sdev, devinfo); - if (!err) - err = device_create_file(dev, &scsi_dh_state_attr); + if (devinfo) + err = scsi_dh_handler_attach(sdev, devinfo); } else if (action == BUS_NOTIFY_DEL_DEVICE) { device_remove_file(dev, &scsi_dh_state_attr); scsi_dh_handler_detach(sdev, NULL); } -out: return err; } @@ -423,10 +420,17 @@ 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. + * Returns immediately without waiting for activation to be completed. + * @q - Request queue that is associated with the scsi_device to be + * activated. + * @fn - Function to be called upon completion of the activation. + * Function fn is called with data (below) and the error code. + * Function fn may be called from the same calling context. So, + * do not hold the lock in the caller which may be needed in fn. + * @data - data passed to the function fn upon completion. + * */ -int scsi_dh_activate(struct request_queue *q) +int scsi_dh_activate(struct request_queue *q, activate_complete fn, void *data) { int err = 0; unsigned long flags; @@ -445,7 +449,7 @@ int scsi_dh_activate(struct request_queue *q) return err; if (scsi_dh->activate) - err = scsi_dh->activate(sdev); + err = scsi_dh->activate(sdev, fn, data); put_device(&sdev->sdev_gendev); return err; } diff --git a/drivers/scsi/device_handler/scsi_dh_alua.c b/drivers/scsi/device_handler/scsi_dh_alua.c index b5cdefaf2608..4f0d0138f48b 100644 --- a/drivers/scsi/device_handler/scsi_dh_alua.c +++ b/drivers/scsi/device_handler/scsi_dh_alua.c @@ -60,11 +60,17 @@ struct alua_dh_data { int bufflen; unsigned char sense[SCSI_SENSE_BUFFERSIZE]; int senselen; + struct scsi_device *sdev; + activate_complete callback_fn; + void *callback_data; }; #define ALUA_POLICY_SWITCH_CURRENT 0 #define ALUA_POLICY_SWITCH_ALL 1 +static char print_alua_state(int); +static int alua_check_sense(struct scsi_device *, struct scsi_sense_hdr *); + static inline struct alua_dh_data *get_alua_data(struct scsi_device *sdev) { struct scsi_dh_data *scsi_dh_data = sdev->scsi_dh_data; @@ -231,18 +237,71 @@ done: } /* + * alua_stpg - Evaluate SET TARGET GROUP STATES + * @sdev: the device to be evaluated + * @state: the new target group state + * + * Send a SET TARGET GROUP STATES command to the device. + * We only have to test here if we should resubmit the command; + * any other error is assumed as a failure. + */ +static void stpg_endio(struct request *req, int error) +{ + struct alua_dh_data *h = req->end_io_data; + struct scsi_sense_hdr sense_hdr; + unsigned err = SCSI_DH_IO; + + if (error || host_byte(req->errors) != DID_OK || + msg_byte(req->errors) != COMMAND_COMPLETE) + goto done; + + if (err == SCSI_DH_IO && h->senselen > 0) { + err = scsi_normalize_sense(h->sense, SCSI_SENSE_BUFFERSIZE, + &sense_hdr); + if (!err) { + err = SCSI_DH_IO; + goto done; + } + err = alua_check_sense(h->sdev, &sense_hdr); + if (err == ADD_TO_MLQUEUE) { + err = SCSI_DH_RETRY; + goto done; + } + sdev_printk(KERN_INFO, h->sdev, + "%s: stpg sense code: %02x/%02x/%02x\n", + ALUA_DH_NAME, sense_hdr.sense_key, + sense_hdr.asc, sense_hdr.ascq); + err = SCSI_DH_IO; + } + if (err == SCSI_DH_OK) { + h->state = TPGS_STATE_OPTIMIZED; + sdev_printk(KERN_INFO, h->sdev, + "%s: port group %02x switched to state %c\n", + ALUA_DH_NAME, h->group_id, + print_alua_state(h->state)); + } +done: + blk_put_request(req); + if (h->callback_fn) { + h->callback_fn(h->callback_data, err); + h->callback_fn = h->callback_data = NULL; + } + return; +} + +/* * submit_stpg - Issue a SET TARGET GROUP STATES command - * @sdev: sdev the command should be sent to * * Currently we're only setting the current target port group state * to 'active/optimized' and let the array firmware figure out * the states of the remaining groups. */ -static unsigned submit_stpg(struct scsi_device *sdev, struct alua_dh_data *h) +static unsigned submit_stpg(struct alua_dh_data *h) { struct request *rq; int err = SCSI_DH_RES_TEMP_UNAVAIL; int stpg_len = 8; + struct scsi_device *sdev = h->sdev; /* Prepare the data buffer */ memset(h->buff, 0, stpg_len); @@ -252,7 +311,7 @@ static unsigned submit_stpg(struct scsi_device *sdev, struct alua_dh_data *h) rq = get_alua_req(sdev, h->buff, stpg_len, WRITE); if (!rq) - goto done; + return SCSI_DH_RES_TEMP_UNAVAIL; /* Prepare the command. */ rq->cmd[0] = MAINTENANCE_OUT; @@ -266,17 +325,9 @@ static unsigned submit_stpg(struct scsi_device *sdev, struct alua_dh_data *h) rq->sense = h->sense; memset(rq->sense, 0, SCSI_SENSE_BUFFERSIZE); rq->sense_len = h->senselen = 0; + rq->end_io_data = h; - err = blk_execute_rq(rq->q, NULL, rq, 1); - if (err == -EIO) { - sdev_printk(KERN_INFO, sdev, - "%s: stpg failed with %x\n", - ALUA_DH_NAME, rq->errors); - h->senselen = rq->sense_len; - err = SCSI_DH_IO; - } - blk_put_request(rq); -done: + blk_execute_rq_nowait(rq->q, NULL, rq, 1, stpg_endio); return err; } @@ -477,50 +528,6 @@ static int alua_check_sense(struct scsi_device *sdev, } /* - * alua_stpg - Evaluate SET TARGET GROUP STATES - * @sdev: the device to be evaluated - * @state: the new target group state - * - * Send a SET TARGET GROUP STATES command to the device. - * We only have to test here if we should resubmit the command; - * any other error is assumed as a failure. - */ -static int alua_stpg(struct scsi_device *sdev, int state, - struct alua_dh_data *h) -{ - struct scsi_sense_hdr sense_hdr; - unsigned err; - int retry = ALUA_FAILOVER_RETRIES; - - retry: - err = submit_stpg(sdev, h); - if (err == SCSI_DH_IO && h->senselen > 0) { - err = scsi_normalize_sense(h->sense, SCSI_SENSE_BUFFERSIZE, - &sense_hdr); - if (!err) - return SCSI_DH_IO; - err = alua_check_sense(sdev, &sense_hdr); - if (retry > 0 && err == ADD_TO_MLQUEUE) { - retry--; - goto retry; - } - sdev_printk(KERN_INFO, sdev, - "%s: stpg sense code: %02x/%02x/%02x\n", - ALUA_DH_NAME, sense_hdr.sense_key, - sense_hdr.asc, sense_hdr.ascq); - err = SCSI_DH_IO; - } - if (err == SCSI_DH_OK) { - h->state = state; - sdev_printk(KERN_INFO, sdev, - "%s: port group %02x switched to state %c\n", - ALUA_DH_NAME, h->group_id, - print_alua_state(h->state) ); - } - return err; -} - -/* * alua_rtpg - Evaluate REPORT TARGET GROUP STATES * @sdev: the device to be evaluated. * @@ -652,7 +659,8 @@ out: * based on a certain policy. But until we actually encounter them it * should be okay. */ -static int alua_activate(struct scsi_device *sdev) +static int alua_activate(struct scsi_device *sdev, + activate_complete fn, void *data) { struct alua_dh_data *h = get_alua_data(sdev); int err = SCSI_DH_OK; @@ -663,11 +671,19 @@ static int alua_activate(struct scsi_device *sdev) goto out; } - if (h->tpgs & TPGS_MODE_EXPLICIT && h->state != TPGS_STATE_OPTIMIZED) - err = alua_stpg(sdev, TPGS_STATE_OPTIMIZED, h); + if (h->tpgs & TPGS_MODE_EXPLICIT && h->state != TPGS_STATE_OPTIMIZED) { + h->callback_fn = fn; + h->callback_data = data; + err = submit_stpg(h); + if (err == SCSI_DH_OK) + return 0; + h->callback_fn = h->callback_data = NULL; + } out: - return err; + if (fn) + fn(data, err); + return 0; } /* @@ -745,6 +761,7 @@ static int alua_bus_attach(struct scsi_device *sdev) h->rel_port = -1; h->buff = h->inq; h->bufflen = ALUA_INQUIRY_SIZE; + h->sdev = sdev; err = alua_initialize(sdev, h); if (err != SCSI_DH_OK) diff --git a/drivers/scsi/device_handler/scsi_dh_emc.c b/drivers/scsi/device_handler/scsi_dh_emc.c index 0cffe84976fe..61966750bd60 100644 --- a/drivers/scsi/device_handler/scsi_dh_emc.c +++ b/drivers/scsi/device_handler/scsi_dh_emc.c @@ -528,7 +528,8 @@ retry: return err; } -static int clariion_activate(struct scsi_device *sdev) +static int clariion_activate(struct scsi_device *sdev, + activate_complete fn, void *data) { struct clariion_dh_data *csdev = get_clariion_data(sdev); int result; @@ -559,7 +560,9 @@ done: csdev->port, lun_state[csdev->lun_state], csdev->default_sp + 'A'); - return result; + if (fn) + fn(data, result); + return 0; } /* * params - parameters in the following format diff --git a/drivers/scsi/device_handler/scsi_dh_hp_sw.c b/drivers/scsi/device_handler/scsi_dh_hp_sw.c index f7da7530875e..857fdd6032b2 100644 --- a/drivers/scsi/device_handler/scsi_dh_hp_sw.c +++ b/drivers/scsi/device_handler/scsi_dh_hp_sw.c @@ -39,8 +39,14 @@ struct hp_sw_dh_data { unsigned char sense[SCSI_SENSE_BUFFERSIZE]; int path_state; int retries; + int retry_cnt; + struct scsi_device *sdev; + activate_complete callback_fn; + void *callback_data; }; +static int hp_sw_start_stop(struct hp_sw_dh_data *); + static inline struct hp_sw_dh_data *get_hp_sw_data(struct scsi_device *sdev) { struct scsi_dh_data *scsi_dh_data = sdev->scsi_dh_data; @@ -191,19 +197,53 @@ static int start_done(struct scsi_device *sdev, unsigned char *sense) return rc; } +static void start_stop_endio(struct request *req, int error) +{ + struct hp_sw_dh_data *h = req->end_io_data; + unsigned err = SCSI_DH_OK; + + if (error || host_byte(req->errors) != DID_OK || + msg_byte(req->errors) != COMMAND_COMPLETE) { + sdev_printk(KERN_WARNING, h->sdev, + "%s: sending start_stop_unit failed with %x\n", + HP_SW_NAME, req->errors); + err = SCSI_DH_IO; + goto done; + } + + if (req->sense_len > 0) { + err = start_done(h->sdev, h->sense); + if (err == SCSI_DH_RETRY) { + err = SCSI_DH_IO; + if (--h->retry_cnt) { + blk_put_request(req); + err = hp_sw_start_stop(h); + if (err == SCSI_DH_OK) + return; + } + } + } +done: + blk_put_request(req); + if (h->callback_fn) { + h->callback_fn(h->callback_data, err); + h->callback_fn = h->callback_data = NULL; + } + return; + +} + /* * hp_sw_start_stop - Send START STOP UNIT command * @sdev: sdev command should be sent to * * Sending START STOP UNIT activates the SP. */ -static int hp_sw_start_stop(struct scsi_device *sdev, struct hp_sw_dh_data *h) +static int hp_sw_start_stop(struct hp_sw_dh_data *h) { struct request *req; - int ret, retry; -retry: - req = blk_get_request(sdev->request_queue, WRITE, GFP_NOIO); + req = blk_get_request(h->sdev->request_queue, WRITE, GFP_ATOMIC); if (!req) return SCSI_DH_RES_TEMP_UNAVAIL; @@ -217,32 +257,10 @@ retry: req->sense = h->sense; memset(req->sense, 0, SCSI_SENSE_BUFFERSIZE); req->sense_len = 0; - retry = h->retries; - - ret = blk_execute_rq(req->q, NULL, req, 1); - if (ret == -EIO) { - if (req->sense_len > 0) { - ret = start_done(sdev, h->sense); - } else { - sdev_printk(KERN_WARNING, sdev, - "%s: sending start_stop_unit failed with %x\n", - HP_SW_NAME, req->errors); - ret = SCSI_DH_IO; - } - } else - ret = SCSI_DH_OK; + req->end_io_data = h; - if (ret == SCSI_DH_RETRY) { - if (--retry) { - blk_put_request(req); - goto retry; - } - ret = SCSI_DH_IO; - } - - blk_put_request(req); - - return ret; + blk_execute_rq_nowait(req->q, NULL, req, 1, start_stop_endio); + return SCSI_DH_OK; } static int hp_sw_prep_fn(struct scsi_device *sdev, struct request *req) @@ -268,7 +286,8 @@ static int hp_sw_prep_fn(struct scsi_device *sdev, struct request *req) * activate the passive path (and deactivate the * previously active one). */ -static int hp_sw_activate(struct scsi_device *sdev) +static int hp_sw_activate(struct scsi_device *sdev, + activate_complete fn, void *data) { int ret = SCSI_DH_OK; struct hp_sw_dh_data *h = get_hp_sw_data(sdev); @@ -276,14 +295,18 @@ static int hp_sw_activate(struct scsi_device *sdev) ret = hp_sw_tur(sdev, h); if (ret == SCSI_DH_OK && h->path_state == HP_SW_PATH_PASSIVE) { - ret = hp_sw_start_stop(sdev, h); + h->retry_cnt = h->retries; + h->callback_fn = fn; + h->callback_data = data; + ret = hp_sw_start_stop(h); if (ret == SCSI_DH_OK) - sdev_printk(KERN_INFO, sdev, - "%s: activated path\n", - HP_SW_NAME); + return 0; + h->callback_fn = h->callback_data = NULL; } - return ret; + if (fn) + fn(data, ret); + return 0; } static const struct scsi_dh_devlist hp_sw_dh_data_list[] = { @@ -326,6 +349,7 @@ static int hp_sw_bus_attach(struct scsi_device *sdev) h = (struct hp_sw_dh_data *) scsi_dh_data->buf; h->path_state = HP_SW_PATH_UNINITIALIZED; h->retries = HP_SW_RETRIES; + h->sdev = sdev; ret = hp_sw_tur(sdev, h); if (ret != SCSI_DH_OK || h->path_state == HP_SW_PATH_UNINITIALIZED) diff --git a/drivers/scsi/device_handler/scsi_dh_rdac.c b/drivers/scsi/device_handler/scsi_dh_rdac.c index 268189d31d9c..47cfe1c49c3e 100644 --- a/drivers/scsi/device_handler/scsi_dh_rdac.c +++ b/drivers/scsi/device_handler/scsi_dh_rdac.c @@ -22,6 +22,7 @@ #include <scsi/scsi.h> #include <scsi/scsi_eh.h> #include <scsi/scsi_dh.h> +#include <linux/workqueue.h> #define RDAC_NAME "rdac" #define RDAC_RETRY_COUNT 5 @@ -138,7 +139,13 @@ struct rdac_controller { } 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; }; + struct c8_inquiry { u8 peripheral_info; u8 page_code; /* 0xC8 */ @@ -198,8 +205,17 @@ static const char *lun_state[] = "owned (AVT mode)", }; +struct rdac_queue_data { + struct list_head entry; + struct rdac_dh_data *h; + activate_complete callback_fn; + void *callback_data; +}; + static LIST_HEAD(ctlr_list); static DEFINE_SPINLOCK(list_lock); +static struct workqueue_struct *kmpath_rdacd; +static void send_mode_select(struct work_struct *work); /* * module parameter to enable rdac debug logging. @@ -281,7 +297,6 @@ static struct request *rdac_failover_get(struct scsi_device *sdev, rdac_pg->subpage_code = 0x1; rdac_pg->page_len[0] = 0x01; rdac_pg->page_len[1] = 0x28; - rdac_pg->lun_table[h->lun] = 0x81; } else { struct rdac_pg_legacy *rdac_pg; @@ -291,7 +306,6 @@ static struct request *rdac_failover_get(struct scsi_device *sdev, common = &rdac_pg->common; rdac_pg->page_code = RDAC_PAGE_CODE_REDUNDANT_CONTROLLER; rdac_pg->page_len = 0x68; - rdac_pg->lun_table[h->lun] = 0x81; } common->rdac_mode[1] = RDAC_MODE_TRANSFER_SPECIFIED_LUNS; common->quiescence_timeout = RDAC_QUIESCENCE_TIME; @@ -325,6 +339,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); @@ -363,6 +378,11 @@ static struct rdac_controller *get_controller(u8 *subsys_id, u8 *slot_id, kref_init(&ctlr->kref); ctlr->use_ms10 = -1; + ctlr->ms_queued = 0; + ctlr->ms_sdev = NULL; + spin_lock_init(&ctlr->ms_lock); + 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); @@ -490,7 +510,7 @@ static int set_mode_select(struct scsi_device *sdev, struct rdac_dh_data *h) } static int mode_select_handle_sense(struct scsi_device *sdev, - unsigned char *sensebuf) + unsigned char *sensebuf) { struct scsi_sense_hdr sense_hdr; int err = SCSI_DH_IO, ret; @@ -533,11 +553,29 @@ done: return err; } -static int send_mode_select(struct scsi_device *sdev, struct rdac_dh_data *h) +static void send_mode_select(struct work_struct *work) { + struct rdac_controller *ctlr = + container_of(work, struct rdac_controller, ms_work); struct request *rq; + struct scsi_device *sdev = ctlr->ms_sdev; + struct rdac_dh_data *h = get_rdac_data(sdev); struct request_queue *q = sdev->request_queue; int err, retry_cnt = RDAC_RETRY_COUNT; + struct rdac_queue_data *tmp, *qdata; + LIST_HEAD(list); + u8 *lun_table; + + spin_lock(&ctlr->ms_lock); + list_splice_init(&ctlr->ms_head, &list); + ctlr->ms_queued = 0; + ctlr->ms_sdev = NULL; + spin_unlock(&ctlr->ms_lock); + + if (ctlr->use_ms10) + lun_table = ctlr->mode_select.expanded.lun_table; + else + lun_table = ctlr->mode_select.legacy.lun_table; retry: err = SCSI_DH_RES_TEMP_UNAVAIL; @@ -545,6 +583,10 @@ retry: if (!rq) goto done; + list_for_each_entry(qdata, &list, entry) { + lun_table[qdata->h->lun] = 0x81; + } + RDAC_LOG(RDAC_LOG_FAILOVER, sdev, "array %s, ctlr %d, " "%s MODE_SELECT command", (char *) h->ctlr->array_name, h->ctlr->index, @@ -565,10 +607,45 @@ retry: } done: - return err; + list_for_each_entry_safe(qdata, tmp, &list, entry) { + list_del(&qdata->entry); + if (err == SCSI_DH_OK) + qdata->h->state = RDAC_STATE_ACTIVE; + if (qdata->callback_fn) + qdata->callback_fn(qdata->callback_data, err); + kfree(qdata); + } + return; +} + +static int queue_mode_select(struct scsi_device *sdev, + activate_complete fn, void *data) +{ + struct rdac_queue_data *qdata; + struct rdac_controller *ctlr; + + qdata = kzalloc(sizeof(*qdata), GFP_KERNEL); + if (!qdata) + return SCSI_DH_RETRY; + + qdata->h = get_rdac_data(sdev); + qdata->callback_fn = fn; + qdata->callback_data = data; + + ctlr = qdata->h->ctlr; + spin_lock(&ctlr->ms_lock); + list_add_tail(&qdata->entry, &ctlr->ms_head); + if (!ctlr->ms_queued) { + ctlr->ms_queued = 1; + ctlr->ms_sdev = sdev; + queue_work(kmpath_rdacd, &ctlr->ms_work); + } + spin_unlock(&ctlr->ms_lock); + return SCSI_DH_OK; } -static int rdac_activate(struct scsi_device *sdev) +static int rdac_activate(struct scsi_device *sdev, + activate_complete fn, void *data) { struct rdac_dh_data *h = get_rdac_data(sdev); int err = SCSI_DH_OK; @@ -577,10 +654,15 @@ static int rdac_activate(struct scsi_device *sdev) if (err != SCSI_DH_OK) goto done; - if (h->lun_state == RDAC_LUN_UNOWNED) - err = send_mode_select(sdev, h); + if (h->lun_state == RDAC_LUN_UNOWNED) { + err = queue_mode_select(sdev, fn, data); + if (err == SCSI_DH_OK) + return 0; + } done: - return err; + if (fn) + fn(data, err); + return 0; } static int rdac_prep_fn(struct scsi_device *sdev, struct request *req) @@ -790,13 +872,26 @@ static int __init rdac_init(void) int r; r = scsi_register_device_handler(&rdac_dh); - if (r != 0) + if (r != 0) { printk(KERN_ERR "Failed to register scsi device handler."); + goto done; + } + + /* + * Create workqueue to handle mode selects for rdac + */ + kmpath_rdacd = create_singlethread_workqueue("kmpath_rdacd"); + if (!kmpath_rdacd) { + scsi_unregister_device_handler(&rdac_dh); + printk(KERN_ERR "kmpath_rdacd creation failed.\n"); + } +done: return r; } static void __exit rdac_exit(void) { + destroy_workqueue(kmpath_rdacd); scsi_unregister_device_handler(&rdac_dh); } |