From b218a0d8e250e0ae8fd4d4e45bd66a588b380752 Mon Sep 17 00:00:00 2001 From: "Darrick J. Wong" Date: Thu, 11 Jan 2007 14:14:55 -0800 Subject: [SCSI] libsas: Don't give scsi_cmnds to the EH if they never made it to the SAS LLDD or have already returned On a system with many SAS targets, it appears possible that a scsi_cmnd can time out without ever making it to the SAS LLDD or at the same time that a completion is occurring. In both of these cases, telling the LLDD to abort the sas_task makes no sense because the LLDD won't know about the sas_task; what we really want to do is to increase the timer. Note that this involves creating another sas_task bit to indicate whether or not the task has been sent to the LLDD; I could have implemented this by slightly redefining SAS_TASK_STATE_PENDING, but this way seems cleaner. This second version amends the aic94xx portion to set the TASK_AT_INITIATOR flag for all sas_tasks that were passed to lldd_execute_task. Signed-off-by: Darrick J. Wong Signed-off-by: James Bottomley --- include/scsi/libsas.h | 1 + 1 file changed, 1 insertion(+) (limited to 'include/scsi') diff --git a/include/scsi/libsas.h b/include/scsi/libsas.h index 0c775fceb675..da96bcfb9e46 100644 --- a/include/scsi/libsas.h +++ b/include/scsi/libsas.h @@ -554,6 +554,7 @@ struct sas_task { #define SAS_TASK_STATE_DONE 2 #define SAS_TASK_STATE_ABORTED 4 #define SAS_TASK_INITIATOR_ABORTED 8 +#define SAS_TASK_AT_INITIATOR 16 static inline struct sas_task *sas_alloc_task(gfp_t flags) { -- cgit v1.2.3 From acbf167d4ad8c27f9743a4b539d51ae9535bf21c Mon Sep 17 00:00:00 2001 From: "Darrick J. Wong" Date: Thu, 11 Jan 2007 14:14:57 -0800 Subject: [SCSI] libsas: Add a sysfs knob to enable/disable a phy This patch lets a user arbitrarily enable or disable a phy via sysfs. Potential applications include shutting down a phy to replace one lane of wide port, and (more importantly) providing a method for the libata SATL to control the phy. Signed-off-by: Darrick J. Wong Signed-off-by: James Bottomley --- drivers/scsi/libsas/sas_init.c | 35 +++++++++++++++++++++++-- drivers/scsi/libsas/sas_scsi_host.c | 1 + drivers/scsi/scsi_transport_sas.c | 51 +++++++++++++++++++++++++++++++++++++ include/scsi/libsas.h | 3 +++ include/scsi/scsi_transport_sas.h | 2 ++ 5 files changed, 90 insertions(+), 2 deletions(-) (limited to 'include/scsi') diff --git a/drivers/scsi/libsas/sas_init.c b/drivers/scsi/libsas/sas_init.c index 2f0c07fc3f48..90cce34cb6f5 100644 --- a/drivers/scsi/libsas/sas_init.c +++ b/drivers/scsi/libsas/sas_init.c @@ -146,6 +146,36 @@ static int sas_get_linkerrors(struct sas_phy *phy) return sas_smp_get_phy_events(phy); } +int sas_phy_enable(struct sas_phy *phy, int enable) +{ + int ret; + enum phy_func command; + + if (enable) + command = PHY_FUNC_LINK_RESET; + else + command = PHY_FUNC_DISABLE; + + if (scsi_is_sas_phy_local(phy)) { + struct Scsi_Host *shost = dev_to_shost(phy->dev.parent); + struct sas_ha_struct *sas_ha = SHOST_TO_SAS_HA(shost); + struct asd_sas_phy *asd_phy = sas_ha->sas_phy[phy->number]; + struct sas_internal *i = + to_sas_internal(sas_ha->core.shost->transportt); + + if (!enable) { + sas_phy_disconnected(asd_phy); + sas_ha->notify_phy_event(asd_phy, PHYE_LOSS_OF_SIGNAL); + } + ret = i->dft->lldd_control_phy(asd_phy, command, NULL); + } else { + struct sas_rphy *rphy = dev_to_rphy(phy->dev.parent); + struct domain_device *ddev = sas_find_dev_by_rphy(rphy); + ret = sas_smp_phy_control(ddev, phy->number, command, NULL); + } + return ret; +} + int sas_phy_reset(struct sas_phy *phy, int hard_reset) { int ret; @@ -172,8 +202,8 @@ int sas_phy_reset(struct sas_phy *phy, int hard_reset) return ret; } -static int sas_set_phy_speed(struct sas_phy *phy, - struct sas_phy_linkrates *rates) +int sas_set_phy_speed(struct sas_phy *phy, + struct sas_phy_linkrates *rates) { int ret; @@ -212,6 +242,7 @@ static int sas_set_phy_speed(struct sas_phy *phy, } static struct sas_function_template sft = { + .phy_enable = sas_phy_enable, .phy_reset = sas_phy_reset, .set_phy_speed = sas_set_phy_speed, .get_linkerrors = sas_get_linkerrors, diff --git a/drivers/scsi/libsas/sas_scsi_host.c b/drivers/scsi/libsas/sas_scsi_host.c index 21bd24775980..7774eb3628ad 100644 --- a/drivers/scsi/libsas/sas_scsi_host.c +++ b/drivers/scsi/libsas/sas_scsi_host.c @@ -875,3 +875,4 @@ EXPORT_SYMBOL_GPL(sas_change_queue_type); EXPORT_SYMBOL_GPL(sas_bios_param); EXPORT_SYMBOL_GPL(sas_task_abort); EXPORT_SYMBOL_GPL(sas_phy_reset); +EXPORT_SYMBOL_GPL(sas_phy_enable); diff --git a/drivers/scsi/scsi_transport_sas.c b/drivers/scsi/scsi_transport_sas.c index 5c0b75bbfa10..9e38c1894bb2 100644 --- a/drivers/scsi/scsi_transport_sas.c +++ b/drivers/scsi/scsi_transport_sas.c @@ -336,6 +336,51 @@ show_sas_device_type(struct class_device *cdev, char *buf) } static CLASS_DEVICE_ATTR(device_type, S_IRUGO, show_sas_device_type, NULL); +static ssize_t do_sas_phy_enable(struct class_device *cdev, + size_t count, int enable) +{ + struct sas_phy *phy = transport_class_to_phy(cdev); + struct Scsi_Host *shost = dev_to_shost(phy->dev.parent); + struct sas_internal *i = to_sas_internal(shost->transportt); + int error; + + error = i->f->phy_enable(phy, enable); + if (error) + return error; + phy->enabled = enable; + return count; +}; + +static ssize_t store_sas_phy_enable(struct class_device *cdev, + const char *buf, size_t count) +{ + if (count < 1) + return -EINVAL; + + switch (buf[0]) { + case '0': + do_sas_phy_enable(cdev, count, 0); + break; + case '1': + do_sas_phy_enable(cdev, count, 1); + break; + default: + return -EINVAL; + } + + return count; +} + +static ssize_t show_sas_phy_enable(struct class_device *cdev, char *buf) +{ + struct sas_phy *phy = transport_class_to_phy(cdev); + + return snprintf(buf, 20, "%d", phy->enabled); +} + +static CLASS_DEVICE_ATTR(enable, S_IRUGO | S_IWUSR, show_sas_phy_enable, + store_sas_phy_enable); + static ssize_t do_sas_phy_reset(struct class_device *cdev, size_t count, int hard_reset) { @@ -435,6 +480,7 @@ struct sas_phy *sas_phy_alloc(struct device *parent, int number) return NULL; phy->number = number; + phy->enabled = 1; device_initialize(&phy->dev); phy->dev.parent = get_device(parent); @@ -1389,6 +1435,10 @@ static int sas_user_scan(struct Scsi_Host *shost, uint channel, SETUP_TEMPLATE_RW(phy_attrs, field, S_IRUGO | S_IWUSR, 1, \ !i->f->set_phy_speed, S_IRUGO) +#define SETUP_OPTIONAL_PHY_ATTRIBUTE_RW(field, func) \ + SETUP_TEMPLATE_RW(phy_attrs, field, S_IRUGO | S_IWUSR, 1, \ + !i->f->func, S_IRUGO) + #define SETUP_PORT_ATTRIBUTE(field) \ SETUP_TEMPLATE(port_attrs, field, S_IRUGO, 1) @@ -1479,6 +1529,7 @@ sas_attach_transport(struct sas_function_template *ft) SETUP_PHY_ATTRIBUTE(phy_reset_problem_count); SETUP_OPTIONAL_PHY_ATTRIBUTE_WRONLY(link_reset, phy_reset); SETUP_OPTIONAL_PHY_ATTRIBUTE_WRONLY(hard_reset, phy_reset); + SETUP_OPTIONAL_PHY_ATTRIBUTE_RW(enable, phy_enable); i->phy_attrs[count] = NULL; count = 0; diff --git a/include/scsi/libsas.h b/include/scsi/libsas.h index da96bcfb9e46..0689d6682aa3 100644 --- a/include/scsi/libsas.h +++ b/include/scsi/libsas.h @@ -614,6 +614,9 @@ struct sas_domain_function_template { extern int sas_register_ha(struct sas_ha_struct *); extern int sas_unregister_ha(struct sas_ha_struct *); +int sas_set_phy_speed(struct sas_phy *phy, + struct sas_phy_linkrates *rates); +int sas_phy_enable(struct sas_phy *phy, int enabled); int sas_phy_reset(struct sas_phy *phy, int hard_reset); extern int sas_queuecommand(struct scsi_cmnd *, void (*scsi_done)(struct scsi_cmnd *)); diff --git a/include/scsi/scsi_transport_sas.h b/include/scsi/scsi_transport_sas.h index 59633a82de47..45d58598c3ee 100644 --- a/include/scsi/scsi_transport_sas.h +++ b/include/scsi/scsi_transport_sas.h @@ -54,6 +54,7 @@ struct sas_identify { struct sas_phy { struct device dev; int number; + int enabled; /* phy identification */ struct sas_identify identify; @@ -163,6 +164,7 @@ struct sas_function_template { int (*get_enclosure_identifier)(struct sas_rphy *, u64 *); int (*get_bay_identifier)(struct sas_rphy *); int (*phy_reset)(struct sas_phy *, int); + int (*phy_enable)(struct sas_phy *, int); int (*set_phy_speed)(struct sas_phy *, struct sas_phy_linkrates *); }; -- cgit v1.2.3 From 37958fb040cf6f88b354b9fa7e846014ffbd3b73 Mon Sep 17 00:00:00 2001 From: "Darrick J. Wong" Date: Thu, 11 Jan 2007 14:15:15 -0800 Subject: [SCSI] libsas: Remove SAS_TASK_INITIATOR_ABORTED flag This flag is no longer necessary because we push tasks to be aborted into the EH as soon as we possibly can, and let the SCSI EH code take care of the coordination for which this flag was used. Signed-off-by: Darrick J. Wong Signed-off-by: James Bottomley --- drivers/scsi/libsas/sas_scsi_host.c | 17 ----------------- include/scsi/libsas.h | 1 - 2 files changed, 18 deletions(-) (limited to 'include/scsi') diff --git a/drivers/scsi/libsas/sas_scsi_host.c b/drivers/scsi/libsas/sas_scsi_host.c index 3f647c695da5..f90c332474c8 100644 --- a/drivers/scsi/libsas/sas_scsi_host.c +++ b/drivers/scsi/libsas/sas_scsi_host.c @@ -310,15 +310,6 @@ static enum task_disposition sas_scsi_find_task(struct sas_task *task) spin_unlock_irqrestore(&core->task_queue_lock, flags); } - spin_lock_irqsave(&task->task_state_lock, flags); - if (task->task_state_flags & SAS_TASK_INITIATOR_ABORTED) { - spin_unlock_irqrestore(&task->task_state_lock, flags); - SAS_DPRINTK("%s: task 0x%p already aborted\n", - __FUNCTION__, task); - return TASK_IS_ABORTED; - } - spin_unlock_irqrestore(&task->task_state_lock, flags); - for (i = 0; i < 5; i++) { SAS_DPRINTK("%s: aborting task 0x%p\n", __FUNCTION__, task); res = si->dft->lldd_abort_task(task); @@ -534,12 +525,6 @@ enum scsi_eh_timer_return sas_scsi_timed_out(struct scsi_cmnd *cmd) } spin_lock_irqsave(&task->task_state_lock, flags); - if (task->task_state_flags & SAS_TASK_INITIATOR_ABORTED) { - spin_unlock_irqrestore(&task->task_state_lock, flags); - SAS_DPRINTK("command 0x%p, task 0x%p, aborted by initiator: " - "EH_NOT_HANDLED\n", cmd, task); - return EH_NOT_HANDLED; - } if (task->task_state_flags & SAS_TASK_STATE_DONE) { spin_unlock_irqrestore(&task->task_state_lock, flags); SAS_DPRINTK("command 0x%p, task 0x%p, timed out: EH_HANDLED\n", @@ -826,7 +811,6 @@ static int do_sas_task_abort(struct sas_task *task) return 0; } - task->task_state_flags |= SAS_TASK_INITIATOR_ABORTED; if (!(task->task_state_flags & SAS_TASK_STATE_DONE)) task->task_state_flags |= SAS_TASK_STATE_ABORTED; spin_unlock_irqrestore(&task->task_state_lock, flags); @@ -849,7 +833,6 @@ static int do_sas_task_abort(struct sas_task *task) } spin_lock_irqsave(&task->task_state_lock, flags); - task->task_state_flags &= ~SAS_TASK_INITIATOR_ABORTED; if (!(task->task_state_flags & SAS_TASK_STATE_DONE)) task->task_state_flags &= ~SAS_TASK_STATE_ABORTED; spin_unlock_irqrestore(&task->task_state_lock, flags); diff --git a/include/scsi/libsas.h b/include/scsi/libsas.h index 0689d6682aa3..1d394855b366 100644 --- a/include/scsi/libsas.h +++ b/include/scsi/libsas.h @@ -553,7 +553,6 @@ struct sas_task { #define SAS_TASK_STATE_PENDING 1 #define SAS_TASK_STATE_DONE 2 #define SAS_TASK_STATE_ABORTED 4 -#define SAS_TASK_INITIATOR_ABORTED 8 #define SAS_TASK_AT_INITIATOR 16 static inline struct sas_task *sas_alloc_task(gfp_t flags) -- cgit v1.2.3 From 3ebf6922b0833807e54c73f4794c74baf9945fc8 Mon Sep 17 00:00:00 2001 From: "Darrick J. Wong" Date: Thu, 11 Jan 2007 14:15:17 -0800 Subject: [SCSI] libsas: Enable the EH strategy handler to reset a phy after a command When a SAS LLDD needs to request a device port reset, it needs to have all commands aborted before it can reset the port. Since commands are put on the EH's list in the order that they were queued, the LLDD can set a "need reset" flag in the last task to be aborted so that the EH can reset the port after all commands are aborted. Signed-off-by: Darrick J. Wong Signed-off-by: James Bottomley --- drivers/scsi/libsas/sas_scsi_host.c | 37 ++++++++++++++++++++++++++++++++++++- include/scsi/libsas.h | 1 + 2 files changed, 37 insertions(+), 1 deletion(-) (limited to 'include/scsi') diff --git a/drivers/scsi/libsas/sas_scsi_host.c b/drivers/scsi/libsas/sas_scsi_host.c index f90c332474c8..d0c04ebeb8b0 100644 --- a/drivers/scsi/libsas/sas_scsi_host.c +++ b/drivers/scsi/libsas/sas_scsi_host.c @@ -389,6 +389,19 @@ static int sas_recover_I_T(struct domain_device *dev) return res; } +static int eh_reset_phy_helper(struct sas_phy *phy) +{ + int tmf_resp; + + tmf_resp = sas_phy_reset(phy, 1); + if (tmf_resp) + SAS_DPRINTK("Hard reset of phy %d failed 0x%x\n", + phy->identify.phy_identifier, + tmf_resp); + + return tmf_resp; +} + void sas_scsi_recover_host(struct Scsi_Host *shost) { struct sas_ha_struct *ha = SHOST_TO_SAS_HA(shost); @@ -396,8 +409,9 @@ void sas_scsi_recover_host(struct Scsi_Host *shost) LIST_HEAD(error_q); struct scsi_cmnd *cmd, *n; enum task_disposition res = TASK_IS_DONE; - int tmf_resp; + int tmf_resp, need_reset; struct sas_internal *i = to_sas_internal(shost->transportt); + struct sas_phy *task_sas_phy = NULL; spin_lock_irqsave(shost->host_lock, flags); list_splice_init(&shost->eh_cmd_q, &error_q); @@ -418,6 +432,13 @@ Again: SAS_DPRINTK("%s: taskless cmd?!\n", __FUNCTION__); continue; } + + spin_lock_irqsave(&task->task_state_lock, flags); + need_reset = task->task_state_flags & SAS_TASK_NEED_DEV_RESET; + if (need_reset) + task_sas_phy = task->dev->port->phy; + spin_unlock_irqrestore(&task->task_state_lock, flags); + SAS_DPRINTK("trying to find task 0x%p\n", task); res = sas_scsi_find_task(task); @@ -428,11 +449,15 @@ Again: SAS_DPRINTK("%s: task 0x%p is done\n", __FUNCTION__, task); task->task_done(task); + if (need_reset) + eh_reset_phy_helper(task_sas_phy); continue; case TASK_IS_ABORTED: SAS_DPRINTK("%s: task 0x%p is aborted\n", __FUNCTION__, task); task->task_done(task); + if (need_reset) + eh_reset_phy_helper(task_sas_phy); continue; case TASK_IS_AT_LU: SAS_DPRINTK("task 0x%p is at LU: lu recover\n", task); @@ -443,6 +468,8 @@ Again: SAS_ADDR(task->dev), cmd->device->lun); task->task_done(task); + if (need_reset) + eh_reset_phy_helper(task_sas_phy); sas_scsi_clear_queue_lu(&error_q, cmd); goto Again; } @@ -455,6 +482,8 @@ Again: SAS_DPRINTK("I_T %016llx recovered\n", SAS_ADDR(task->dev->sas_addr)); task->task_done(task); + if (need_reset) + eh_reset_phy_helper(task_sas_phy); sas_scsi_clear_queue_I_T(&error_q, task->dev); goto Again; } @@ -468,6 +497,8 @@ Again: SAS_DPRINTK("clear nexus port:%d " "succeeded\n", port->id); task->task_done(task); + if (need_reset) + eh_reset_phy_helper(task_sas_phy); sas_scsi_clear_queue_port(&error_q, port); goto Again; @@ -480,6 +511,8 @@ Again: SAS_DPRINTK("clear nexus ha " "succeeded\n"); task->task_done(task); + if (need_reset) + eh_reset_phy_helper(task_sas_phy); goto out; } } @@ -493,6 +526,8 @@ Again: cmd->device->lun); task->task_done(task); + if (need_reset) + eh_reset_phy_helper(task_sas_phy); goto clear_q; } } diff --git a/include/scsi/libsas.h b/include/scsi/libsas.h index 1d394855b366..75df71f08ce1 100644 --- a/include/scsi/libsas.h +++ b/include/scsi/libsas.h @@ -553,6 +553,7 @@ struct sas_task { #define SAS_TASK_STATE_PENDING 1 #define SAS_TASK_STATE_DONE 2 #define SAS_TASK_STATE_ABORTED 4 +#define SAS_TASK_NEED_DEV_RESET 8 #define SAS_TASK_AT_INITIATOR 16 static inline struct sas_task *sas_alloc_task(gfp_t flags) -- cgit v1.2.3 From 396819fba821ad56f1b90090d256f0ab726c89c5 Mon Sep 17 00:00:00 2001 From: "Darrick J. Wong" Date: Thu, 11 Jan 2007 14:15:20 -0800 Subject: [SCSI] libsas: Delay issuing ABORT TASK TMF until the error handler sas_task_abort() should simply abort the upper-level SCSI command and wait until the error handler to send the actual ABORT TASK command. By deferring things to the EH we simplify the concurrency coordination and eliminate some race conditions. Note that sas_task_abort has a few hooks to handle libsas internal commands properly too. Also rename do_sas_task_abort to __sas_task_abort just in case we really want to abort the task *right now* and we don't have a scsi_cmnd attached to the command. This is a hook for libata internal commands to abort. Signed-off-by: Darrick J. Wong Signed-off-by: James Bottomley --- drivers/scsi/libsas/sas_scsi_host.c | 51 +++++++++++++++++++++---------------- include/scsi/libsas.h | 3 ++- 2 files changed, 31 insertions(+), 23 deletions(-) (limited to 'include/scsi') diff --git a/drivers/scsi/libsas/sas_scsi_host.c b/drivers/scsi/libsas/sas_scsi_host.c index d0c04ebeb8b0..f867455dd339 100644 --- a/drivers/scsi/libsas/sas_scsi_host.c +++ b/drivers/scsi/libsas/sas_scsi_host.c @@ -560,6 +560,7 @@ enum scsi_eh_timer_return sas_scsi_timed_out(struct scsi_cmnd *cmd) } spin_lock_irqsave(&task->task_state_lock, flags); + BUG_ON(task->task_state_flags & SAS_TASK_STATE_ABORTED); if (task->task_state_flags & SAS_TASK_STATE_DONE) { spin_unlock_irqrestore(&task->task_state_lock, flags); SAS_DPRINTK("command 0x%p, task 0x%p, timed out: EH_HANDLED\n", @@ -830,44 +831,42 @@ void sas_shutdown_queue(struct sas_ha_struct *sas_ha) spin_unlock_irqrestore(&core->task_queue_lock, flags); } -static int do_sas_task_abort(struct sas_task *task) +/* + * Call the LLDD task abort routine directly. This function is intended for + * use by upper layers that need to tell the LLDD to abort a task. + */ +int __sas_task_abort(struct sas_task *task) { - struct scsi_cmnd *sc = task->uldd_task; struct sas_internal *si = to_sas_internal(task->dev->port->ha->core.shost->transportt); unsigned long flags; int res; spin_lock_irqsave(&task->task_state_lock, flags); - if (task->task_state_flags & SAS_TASK_STATE_ABORTED) { + if (task->task_state_flags & SAS_TASK_STATE_ABORTED || + task->task_state_flags & SAS_TASK_STATE_DONE) { spin_unlock_irqrestore(&task->task_state_lock, flags); - SAS_DPRINTK("%s: Task %p already aborted.\n", __FUNCTION__, + SAS_DPRINTK("%s: Task %p already finished.\n", __FUNCTION__, task); return 0; } - - if (!(task->task_state_flags & SAS_TASK_STATE_DONE)) - task->task_state_flags |= SAS_TASK_STATE_ABORTED; + task->task_state_flags |= SAS_TASK_STATE_ABORTED; spin_unlock_irqrestore(&task->task_state_lock, flags); if (!si->dft->lldd_abort_task) return -ENODEV; res = si->dft->lldd_abort_task(task); + + spin_lock_irqsave(&task->task_state_lock, flags); if ((task->task_state_flags & SAS_TASK_STATE_DONE) || (res == TMF_RESP_FUNC_COMPLETE)) { - /* SMP commands don't have scsi_cmds(?) */ - if (!sc) { - task->task_done(task); - return 0; - } - scsi_req_abort_cmd(sc); - scsi_schedule_eh(sc->device->host); + spin_unlock_irqrestore(&task->task_state_lock, flags); + task->task_done(task); return 0; } - spin_lock_irqsave(&task->task_state_lock, flags); if (!(task->task_state_flags & SAS_TASK_STATE_DONE)) task->task_state_flags &= ~SAS_TASK_STATE_ABORTED; spin_unlock_irqrestore(&task->task_state_lock, flags); @@ -875,17 +874,24 @@ static int do_sas_task_abort(struct sas_task *task) return -EAGAIN; } -void sas_task_abort(struct work_struct *work) +/* + * Tell an upper layer that it needs to initiate an abort for a given task. + * This should only ever be called by an LLDD. + */ +void sas_task_abort(struct sas_task *task) { - struct sas_task *task = - container_of(work, struct sas_task, abort_work); - int i; + struct scsi_cmnd *sc = task->uldd_task; - for (i = 0; i < 5; i++) - if (!do_sas_task_abort(task)) + /* Escape for libsas internal commands */ + if (!sc) { + if (!del_timer(&task->timer)) return; + task->timer.function(task->timer.data); + return; + } - SAS_DPRINTK("%s: Could not kill task!\n", __FUNCTION__); + scsi_req_abort_cmd(sc); + scsi_schedule_eh(sc->device->host); } EXPORT_SYMBOL_GPL(sas_queuecommand); @@ -895,6 +901,7 @@ EXPORT_SYMBOL_GPL(sas_slave_destroy); EXPORT_SYMBOL_GPL(sas_change_queue_depth); EXPORT_SYMBOL_GPL(sas_change_queue_type); EXPORT_SYMBOL_GPL(sas_bios_param); +EXPORT_SYMBOL_GPL(__sas_task_abort); EXPORT_SYMBOL_GPL(sas_task_abort); EXPORT_SYMBOL_GPL(sas_phy_reset); EXPORT_SYMBOL_GPL(sas_phy_enable); diff --git a/include/scsi/libsas.h b/include/scsi/libsas.h index 75df71f08ce1..c83426344e8f 100644 --- a/include/scsi/libsas.h +++ b/include/scsi/libsas.h @@ -650,6 +650,7 @@ void sas_unregister_dev(struct domain_device *); void sas_init_dev(struct domain_device *); -void sas_task_abort(struct work_struct *); +void sas_task_abort(struct sas_task *); +int __sas_task_abort(struct sas_task *); #endif /* _SASLIB_H_ */ -- cgit v1.2.3 From 6b0efb8516a5298e12033df61f9e0c376a306adb Mon Sep 17 00:00:00 2001 From: "Darrick J. Wong" Date: Thu, 11 Jan 2007 14:15:43 -0800 Subject: [SCSI] libsas: Add SAS_HA state flags to avoid queueing events while unloading Track sas_ha_struct state so that we ignore events that come in while we're shutting things down. Signed-off-by: Malahal Naineni Signed-off-by: James Bottomley --- drivers/scsi/libsas/sas_discover.c | 2 +- drivers/scsi/libsas/sas_event.c | 6 +++--- drivers/scsi/libsas/sas_init.c | 12 ++++++++++++ drivers/scsi/libsas/sas_internal.h | 9 +++++++-- include/scsi/libsas.h | 8 ++++++++ 5 files changed, 31 insertions(+), 6 deletions(-) (limited to 'include/scsi') diff --git a/drivers/scsi/libsas/sas_discover.c b/drivers/scsi/libsas/sas_discover.c index 21f538e4a0b5..b6ba0e053d1a 100644 --- a/drivers/scsi/libsas/sas_discover.c +++ b/drivers/scsi/libsas/sas_discover.c @@ -753,7 +753,7 @@ int sas_discover_event(struct asd_sas_port *port, enum discover_event ev) BUG_ON(ev >= DISC_NUM_EVENTS); sas_queue_event(ev, &disc->disc_event_lock, &disc->pending, - &disc->disc_work[ev].work, port->ha->core.shost); + &disc->disc_work[ev].work, port->ha); return 0; } diff --git a/drivers/scsi/libsas/sas_event.c b/drivers/scsi/libsas/sas_event.c index d83392ee6823..9db30fb5caf2 100644 --- a/drivers/scsi/libsas/sas_event.c +++ b/drivers/scsi/libsas/sas_event.c @@ -31,7 +31,7 @@ static void notify_ha_event(struct sas_ha_struct *sas_ha, enum ha_event event) BUG_ON(event >= HA_NUM_EVENTS); sas_queue_event(event, &sas_ha->event_lock, &sas_ha->pending, - &sas_ha->ha_events[event].work, sas_ha->core.shost); + &sas_ha->ha_events[event].work, sas_ha); } static void notify_port_event(struct asd_sas_phy *phy, enum port_event event) @@ -41,7 +41,7 @@ static void notify_port_event(struct asd_sas_phy *phy, enum port_event event) BUG_ON(event >= PORT_NUM_EVENTS); sas_queue_event(event, &ha->event_lock, &phy->port_events_pending, - &phy->port_events[event].work, ha->core.shost); + &phy->port_events[event].work, ha); } static void notify_phy_event(struct asd_sas_phy *phy, enum phy_event event) @@ -51,7 +51,7 @@ static void notify_phy_event(struct asd_sas_phy *phy, enum phy_event event) BUG_ON(event >= PHY_NUM_EVENTS); sas_queue_event(event, &ha->event_lock, &phy->phy_events_pending, - &phy->phy_events[event].work, ha->core.shost); + &phy->phy_events[event].work, ha); } int sas_init_events(struct sas_ha_struct *sas_ha) diff --git a/drivers/scsi/libsas/sas_init.c b/drivers/scsi/libsas/sas_init.c index 4df73d69bb3c..965698c8b7bf 100644 --- a/drivers/scsi/libsas/sas_init.c +++ b/drivers/scsi/libsas/sas_init.c @@ -87,6 +87,9 @@ int sas_register_ha(struct sas_ha_struct *sas_ha) else if (sas_ha->lldd_queue_size == -1) sas_ha->lldd_queue_size = 128; /* Sanity */ + sas_ha->state = SAS_HA_REGISTERED; + spin_lock_init(&sas_ha->state_lock); + error = sas_register_phys(sas_ha); if (error) { printk(KERN_NOTICE "couldn't register sas phys:%d\n", error); @@ -127,6 +130,15 @@ Undo_phys: int sas_unregister_ha(struct sas_ha_struct *sas_ha) { + unsigned long flags; + + /* Set the state to unregistered to avoid further + * events to be queued */ + spin_lock_irqsave(&sas_ha->state_lock, flags); + sas_ha->state = SAS_HA_UNREGISTERED; + spin_unlock_irqrestore(&sas_ha->state_lock, flags); + scsi_flush_work(sas_ha->core.shost); + sas_unregister_ports(sas_ha); if (sas_ha->lldd_max_execute_num > 1) { diff --git a/drivers/scsi/libsas/sas_internal.h b/drivers/scsi/libsas/sas_internal.h index 137d7e496b6d..a78638df2018 100644 --- a/drivers/scsi/libsas/sas_internal.h +++ b/drivers/scsi/libsas/sas_internal.h @@ -80,7 +80,7 @@ void sas_hae_reset(struct work_struct *work); static inline void sas_queue_event(int event, spinlock_t *lock, unsigned long *pending, struct work_struct *work, - struct Scsi_Host *shost) + struct sas_ha_struct *sas_ha) { unsigned long flags; @@ -91,7 +91,12 @@ static inline void sas_queue_event(int event, spinlock_t *lock, } __set_bit(event, pending); spin_unlock_irqrestore(lock, flags); - scsi_queue_work(shost, work); + + spin_lock_irqsave(&sas_ha->state_lock, flags); + if (sas_ha->state != SAS_HA_UNREGISTERED) { + scsi_queue_work(sas_ha->core.shost, work); + } + spin_unlock_irqrestore(&sas_ha->state_lock, flags); } static inline void sas_begin_event(int event, spinlock_t *lock, diff --git a/include/scsi/libsas.h b/include/scsi/libsas.h index c83426344e8f..ca393929c10b 100644 --- a/include/scsi/libsas.h +++ b/include/scsi/libsas.h @@ -323,12 +323,20 @@ struct sas_ha_event { struct sas_ha_struct *ha; }; +enum sas_ha_state { + SAS_HA_REGISTERED, + SAS_HA_UNREGISTERED +}; + struct sas_ha_struct { /* private: */ spinlock_t event_lock; struct sas_ha_event ha_events[HA_NUM_EVENTS]; unsigned long pending; + enum sas_ha_state state; + spinlock_t state_lock; + struct scsi_core core; /* public: */ -- cgit v1.2.3 From 6f63caae2172e97e528b58319480217b0b36542e Mon Sep 17 00:00:00 2001 From: "Darrick J. Wong" Date: Fri, 26 Jan 2007 14:08:43 -0800 Subject: [SCSI] libsas: Clean up discovery failure handler code sas_rphy_delete does two things: it removes the sas_rphy from the transport layer and frees the sas_rphy. This can be broken down into two functions, sas_rphy_remove and sas_rphy_free; sas_rphy_remove is of interest to sas_discover_root_expander because it calls functions that require sas_rphy_add as a prerequisite and can fail (namely sas_discover_expander). In that case, sas_discover_root_expander needs to be able to undo the effects of sas_rphy_add yet leave the job of freeing the sas_rphy to the caller of sas_discover_root_expander. This patch also removes some unnecessary code from sas_discover_end_dev to eliminate an unnecessary cycle of sas_notify_lldd_gone/found for SAS devices, thus eliminating a sas_rphy_remove call (and fixing a race condition where a SCSI target scan can come in between the gone and found call). It also moves the sas_rphy_free calls into sas_discover_domain and sas_ex_discover_end_dev to complement the sas_rphy_allocation via sas_get_port_device. This patch does not change the semantics of sas_rphy_delete. Signed-off-by: Darrick J. Wong Signed-off-by: James Bottomley --- drivers/scsi/libsas/sas_discover.c | 45 ++++++++++++++------------------------ drivers/scsi/libsas/sas_expander.c | 8 +++---- drivers/scsi/scsi_transport_sas.c | 33 ++++++++++++++++------------ include/scsi/scsi_transport_sas.h | 1 + 4 files changed, 40 insertions(+), 47 deletions(-) (limited to 'include/scsi') diff --git a/drivers/scsi/libsas/sas_discover.c b/drivers/scsi/libsas/sas_discover.c index b6ba0e053d1a..a65598b1e536 100644 --- a/drivers/scsi/libsas/sas_discover.c +++ b/drivers/scsi/libsas/sas_discover.c @@ -577,8 +577,6 @@ int sas_discover_sata(struct domain_device *dev) out_err: sas_notify_lldd_dev_gone(dev); out_err2: - sas_rphy_free(dev->rphy); - dev->rphy = NULL; return res; } @@ -600,24 +598,11 @@ int sas_discover_end_dev(struct domain_device *dev) if (res) goto out_err; - /* do this to get the end device port attributes which will have - * been scanned in sas_rphy_add */ - sas_notify_lldd_dev_gone(dev); - res = sas_notify_lldd_dev_found(dev); - if (res) - goto out_err3; - return 0; out_err: sas_notify_lldd_dev_gone(dev); out_err2: - sas_rphy_free(dev->rphy); - dev->rphy = NULL; - return res; -out_err3: - sas_rphy_delete(dev->rphy); - dev->rphy = NULL; return res; } @@ -672,6 +657,7 @@ void sas_unregister_domain_devices(struct asd_sas_port *port) */ static void sas_discover_domain(struct work_struct *work) { + struct domain_device *dev; int error = 0; struct sas_discovery_event *ev = container_of(work, struct sas_discovery_event, work); @@ -681,39 +667,42 @@ static void sas_discover_domain(struct work_struct *work) &port->disc.pending); if (port->port_dev) - return ; - else { - error = sas_get_port_device(port); - if (error) - return; - } + return; + + error = sas_get_port_device(port); + if (error) + return; + dev = port->port_dev; SAS_DPRINTK("DOING DISCOVERY on port %d, pid:%d\n", port->id, current->pid); - switch (port->port_dev->dev_type) { + switch (dev->dev_type) { case SAS_END_DEV: - error = sas_discover_end_dev(port->port_dev); + error = sas_discover_end_dev(dev); break; case EDGE_DEV: case FANOUT_DEV: - error = sas_discover_root_expander(port->port_dev); + error = sas_discover_root_expander(dev); break; case SATA_DEV: case SATA_PM: - error = sas_discover_sata(port->port_dev); + error = sas_discover_sata(dev); break; default: - SAS_DPRINTK("unhandled device %d\n", port->port_dev->dev_type); + SAS_DPRINTK("unhandled device %d\n", dev->dev_type); break; } if (error) { + sas_rphy_free(dev->rphy); + dev->rphy = NULL; + spin_lock(&port->dev_list_lock); - list_del_init(&port->port_dev->dev_list_node); + list_del_init(&dev->dev_list_node); spin_unlock(&port->dev_list_lock); - kfree(port->port_dev); /* not kobject_register-ed yet */ + kfree(dev); /* not kobject_register-ed yet */ port->port_dev = NULL; } diff --git a/drivers/scsi/libsas/sas_expander.c b/drivers/scsi/libsas/sas_expander.c index 0dfd97e7e96c..d9b9a008d36d 100644 --- a/drivers/scsi/libsas/sas_expander.c +++ b/drivers/scsi/libsas/sas_expander.c @@ -667,6 +667,8 @@ static struct domain_device *sas_ex_discover_end_dev( return child; out_list_del: + sas_rphy_free(child->rphy); + child->rphy = NULL; list_del(&child->dev_list_node); out_free: sas_port_delete(phy->port); @@ -1444,12 +1446,8 @@ int sas_discover_root_expander(struct domain_device *dev) return res; out_err2: - sas_rphy_delete(dev->rphy); - dev->rphy = NULL; - return res; + sas_rphy_remove(dev->rphy); out_err: - sas_rphy_free(dev->rphy); - dev->rphy = NULL; return res; } diff --git a/drivers/scsi/scsi_transport_sas.c b/drivers/scsi/scsi_transport_sas.c index ce232803f433..010845fd2b85 100644 --- a/drivers/scsi/scsi_transport_sas.c +++ b/drivers/scsi/scsi_transport_sas.c @@ -1299,7 +1299,7 @@ EXPORT_SYMBOL(sas_rphy_add); * Note: * This function must only be called on a remote * PHY that has not sucessfully been added using - * sas_rphy_add(). + * sas_rphy_add() (or has been sas_rphy_remove()'d) */ void sas_rphy_free(struct sas_rphy *rphy) { @@ -1318,18 +1318,30 @@ void sas_rphy_free(struct sas_rphy *rphy) EXPORT_SYMBOL(sas_rphy_free); /** - * sas_rphy_delete -- remove SAS remote PHY - * @rphy: SAS remote PHY to remove + * sas_rphy_delete -- remove and free SAS remote PHY + * @rphy: SAS remote PHY to remove and free * - * Removes the specified SAS remote PHY. + * Removes the specified SAS remote PHY and frees it. */ void sas_rphy_delete(struct sas_rphy *rphy) +{ + sas_rphy_remove(rphy); + sas_rphy_free(rphy); +} +EXPORT_SYMBOL(sas_rphy_delete); + +/** + * sas_rphy_remove -- remove SAS remote PHY + * @rphy: SAS remote phy to remove + * + * Removes the specified SAS remote PHY. + */ +void +sas_rphy_remove(struct sas_rphy *rphy) { struct device *dev = &rphy->dev; struct sas_port *parent = dev_to_sas_port(dev->parent); - struct Scsi_Host *shost = dev_to_shost(parent->dev.parent); - struct sas_host_attrs *sas_host = to_sas_host_attrs(shost); switch (rphy->identify.device_type) { case SAS_END_DEVICE: @@ -1345,17 +1357,10 @@ sas_rphy_delete(struct sas_rphy *rphy) transport_remove_device(dev); device_del(dev); - transport_destroy_device(dev); - - mutex_lock(&sas_host->lock); - list_del(&rphy->list); - mutex_unlock(&sas_host->lock); parent->rphy = NULL; - - put_device(dev); } -EXPORT_SYMBOL(sas_rphy_delete); +EXPORT_SYMBOL(sas_rphy_remove); /** * scsi_is_sas_rphy -- check if a struct device represents a SAS remote PHY diff --git a/include/scsi/scsi_transport_sas.h b/include/scsi/scsi_transport_sas.h index 45d58598c3ee..9aedc19820b0 100644 --- a/include/scsi/scsi_transport_sas.h +++ b/include/scsi/scsi_transport_sas.h @@ -182,6 +182,7 @@ extern struct sas_rphy *sas_end_device_alloc(struct sas_port *); extern struct sas_rphy *sas_expander_alloc(struct sas_port *, enum sas_device_type); void sas_rphy_free(struct sas_rphy *); extern int sas_rphy_add(struct sas_rphy *); +extern void sas_rphy_remove(struct sas_rphy *); extern void sas_rphy_delete(struct sas_rphy *); extern int scsi_is_sas_rphy(const struct device *); -- cgit v1.2.3 From ad689233bee854dced741c91aff12a8771a22f6f Mon Sep 17 00:00:00 2001 From: "Darrick J. Wong" Date: Fri, 26 Jan 2007 14:08:52 -0800 Subject: [SCSI] libsas: Handle SCSI commands that complete with failure codes This patch moves the code that handles SAS failures out of the main EH function and into a separate function. It also detects commands that have no sas_task (i.e. they completed, but with error data) and sends them into scsi_error for processing. This allows us to handle SCSI errors (and enables auto-spinup as a side effect) instead of dropping them on the floor and falling into an infinite loop. It also requires the implementation of a device reset function, which the SAS failure code has been modified to employ for REQ_DEVICE_RESET. Signed-off-by: Darrick J. Wong Signed-off-by: James Bottomley --- drivers/scsi/libsas/sas_scsi_host.c | 148 +++++++++++++++++++++++++----------- include/scsi/libsas.h | 1 + 2 files changed, 105 insertions(+), 44 deletions(-) (limited to 'include/scsi') diff --git a/drivers/scsi/libsas/sas_scsi_host.c b/drivers/scsi/libsas/sas_scsi_host.c index 9ffe7605fb9d..eac1d2dde743 100644 --- a/drivers/scsi/libsas/sas_scsi_host.c +++ b/drivers/scsi/libsas/sas_scsi_host.c @@ -34,6 +34,7 @@ #include #include "../scsi_sas_internal.h" #include "../scsi_transport_api.h" +#include "../scsi_priv.h" #include #include @@ -396,54 +397,80 @@ static int sas_recover_I_T(struct domain_device *dev) return res; } -static int eh_reset_phy_helper(struct sas_phy *phy) +/* Find the sas_phy that's attached to this device */ +struct sas_phy *find_local_sas_phy(struct domain_device *dev) { - int tmf_resp; + struct domain_device *pdev = dev->parent; + struct ex_phy *exphy = NULL; + int i; + + /* Directly attached device */ + if (!pdev) + return dev->port->phy; - tmf_resp = sas_phy_reset(phy, 1); - if (tmf_resp) - SAS_DPRINTK("Hard reset of phy %d failed 0x%x\n", - phy->identify.phy_identifier, - tmf_resp); + /* Otherwise look in the expander */ + for (i = 0; i < pdev->ex_dev.num_phys; i++) + if (!memcmp(dev->sas_addr, + pdev->ex_dev.ex_phy[i].attached_sas_addr, + SAS_ADDR_SIZE)) { + exphy = &pdev->ex_dev.ex_phy[i]; + break; + } - return tmf_resp; + BUG_ON(!exphy); + return exphy->phy; } -void sas_scsi_recover_host(struct Scsi_Host *shost) +/* Attempt to send a target reset message to a device */ +int sas_eh_device_reset_handler(struct scsi_cmnd *cmd) +{ + struct domain_device *dev = cmd_to_domain_dev(cmd); + struct sas_phy *phy = find_local_sas_phy(dev); + int res; + + res = sas_phy_reset(phy, 1); + if (res) + SAS_DPRINTK("Device reset of %s failed 0x%x\n", + phy->dev.kobj.k_name, + res); + if (res == TMF_RESP_FUNC_SUCC || res == TMF_RESP_FUNC_COMPLETE) + return SUCCESS; + + return FAILED; +} + +/* Try to reset a device */ +static int try_to_reset_cmd_device(struct Scsi_Host *shost, + struct scsi_cmnd *cmd) +{ + if (!shost->hostt->eh_device_reset_handler) + return FAILED; + + return shost->hostt->eh_device_reset_handler(cmd); +} + +static int sas_eh_handle_sas_errors(struct Scsi_Host *shost, + struct list_head *work_q, + struct list_head *done_q) { - struct sas_ha_struct *ha = SHOST_TO_SAS_HA(shost); - unsigned long flags; - LIST_HEAD(error_q); struct scsi_cmnd *cmd, *n; enum task_disposition res = TASK_IS_DONE; int tmf_resp, need_reset; struct sas_internal *i = to_sas_internal(shost->transportt); - struct sas_phy *task_sas_phy = NULL; - - spin_lock_irqsave(shost->host_lock, flags); - list_splice_init(&shost->eh_cmd_q, &error_q); - spin_unlock_irqrestore(shost->host_lock, flags); - - SAS_DPRINTK("Enter %s\n", __FUNCTION__); + unsigned long flags; + struct sas_ha_struct *ha = SHOST_TO_SAS_HA(shost); - /* All tasks on this list were marked SAS_TASK_STATE_ABORTED - * by sas_scsi_timed_out() callback. - */ Again: - SAS_DPRINTK("going over list...\n"); - list_for_each_entry_safe(cmd, n, &error_q, eh_entry) { + list_for_each_entry_safe(cmd, n, work_q, eh_entry) { struct sas_task *task = TO_SAS_TASK(cmd); - list_del_init(&cmd->eh_entry); - if (!task) { - SAS_DPRINTK("%s: taskless cmd?!\n", __FUNCTION__); + if (!task) continue; - } + + list_del_init(&cmd->eh_entry); spin_lock_irqsave(&task->task_state_lock, flags); need_reset = task->task_state_flags & SAS_TASK_NEED_DEV_RESET; - if (need_reset) - task_sas_phy = task->dev->port->phy; spin_unlock_irqrestore(&task->task_state_lock, flags); SAS_DPRINTK("trying to find task 0x%p\n", task); @@ -457,14 +484,14 @@ Again: task); task->task_done(task); if (need_reset) - eh_reset_phy_helper(task_sas_phy); + try_to_reset_cmd_device(shost, cmd); continue; case TASK_IS_ABORTED: SAS_DPRINTK("%s: task 0x%p is aborted\n", __FUNCTION__, task); task->task_done(task); if (need_reset) - eh_reset_phy_helper(task_sas_phy); + try_to_reset_cmd_device(shost, cmd); continue; case TASK_IS_AT_LU: SAS_DPRINTK("task 0x%p is at LU: lu recover\n", task); @@ -476,8 +503,8 @@ Again: cmd->device->lun); task->task_done(task); if (need_reset) - eh_reset_phy_helper(task_sas_phy); - sas_scsi_clear_queue_lu(&error_q, cmd); + try_to_reset_cmd_device(shost, cmd); + sas_scsi_clear_queue_lu(work_q, cmd); goto Again; } /* fallthrough */ @@ -491,8 +518,8 @@ Again: SAS_ADDR(task->dev->sas_addr)); task->task_done(task); if (need_reset) - eh_reset_phy_helper(task_sas_phy); - sas_scsi_clear_queue_I_T(&error_q, task->dev); + try_to_reset_cmd_device(shost, cmd); + sas_scsi_clear_queue_I_T(work_q, task->dev); goto Again; } /* Hammer time :-) */ @@ -506,8 +533,8 @@ Again: "succeeded\n", port->id); task->task_done(task); if (need_reset) - eh_reset_phy_helper(task_sas_phy); - sas_scsi_clear_queue_port(&error_q, + try_to_reset_cmd_device(shost, cmd); + sas_scsi_clear_queue_port(work_q, port); goto Again; } @@ -520,7 +547,7 @@ Again: "succeeded\n"); task->task_done(task); if (need_reset) - eh_reset_phy_helper(task_sas_phy); + try_to_reset_cmd_device(shost, cmd); goto out; } } @@ -535,21 +562,53 @@ Again: task->task_done(task); if (need_reset) - eh_reset_phy_helper(task_sas_phy); + try_to_reset_cmd_device(shost, cmd); goto clear_q; } } out: - scsi_eh_flush_done_q(&ha->eh_done_q); - SAS_DPRINTK("--- Exit %s\n", __FUNCTION__); - return; + return list_empty(work_q); clear_q: SAS_DPRINTK("--- Exit %s -- clear_q\n", __FUNCTION__); - list_for_each_entry_safe(cmd, n, &error_q, eh_entry) { + list_for_each_entry_safe(cmd, n, work_q, eh_entry) { struct sas_task *task = TO_SAS_TASK(cmd); list_del_init(&cmd->eh_entry); task->task_done(task); } + return list_empty(work_q); +} + +void sas_scsi_recover_host(struct Scsi_Host *shost) +{ + struct sas_ha_struct *ha = SHOST_TO_SAS_HA(shost); + unsigned long flags; + LIST_HEAD(eh_work_q); + + spin_lock_irqsave(shost->host_lock, flags); + list_splice_init(&shost->eh_cmd_q, &eh_work_q); + spin_unlock_irqrestore(shost->host_lock, flags); + + SAS_DPRINTK("Enter %s\n", __FUNCTION__); + /* + * Deal with commands that still have SAS tasks (i.e. they didn't + * complete via the normal sas_task completion mechanism) + */ + if (sas_eh_handle_sas_errors(shost, &eh_work_q, &ha->eh_done_q)) + goto out; + + /* + * Now deal with SCSI commands that completed ok but have a an error + * code (and hopefully sense data) attached. This is roughly what + * scsi_unjam_host does, but we skip scsi_eh_abort_cmds because any + * command we see here has no sas_task and is thus unknown to the HA. + */ + if (!scsi_eh_get_sense(&eh_work_q, &ha->eh_done_q)) + scsi_eh_ready_devs(shost, &eh_work_q, &ha->eh_done_q); + +out: + scsi_eh_flush_done_q(&ha->eh_done_q); + SAS_DPRINTK("--- Exit %s\n", __FUNCTION__); + return; } enum scsi_eh_timer_return sas_scsi_timed_out(struct scsi_cmnd *cmd) @@ -914,3 +973,4 @@ EXPORT_SYMBOL_GPL(__sas_task_abort); EXPORT_SYMBOL_GPL(sas_task_abort); EXPORT_SYMBOL_GPL(sas_phy_reset); EXPORT_SYMBOL_GPL(sas_phy_enable); +EXPORT_SYMBOL_GPL(sas_eh_device_reset_handler); diff --git a/include/scsi/libsas.h b/include/scsi/libsas.h index ca393929c10b..b200233cc6f4 100644 --- a/include/scsi/libsas.h +++ b/include/scsi/libsas.h @@ -660,5 +660,6 @@ void sas_init_dev(struct domain_device *); void sas_task_abort(struct sas_task *); int __sas_task_abort(struct sas_task *); +int sas_eh_device_reset_handler(struct scsi_cmnd *cmd); #endif /* _SASLIB_H_ */ -- cgit v1.2.3 From 502c62f17aa7daa78d5da963305251b872885ff9 Mon Sep 17 00:00:00 2001 From: Eric Moore Date: Mon, 29 Jan 2007 09:41:12 -0700 Subject: [SCSI] spi transport class: export spi_dv_pending Signed-off-by: Eric Moore Signed-off-by: James Bottomley --- drivers/scsi/scsi_transport_spi.c | 1 - include/scsi/scsi_transport_spi.h | 1 + 2 files changed, 1 insertion(+), 1 deletion(-) (limited to 'include/scsi') diff --git a/drivers/scsi/scsi_transport_spi.c b/drivers/scsi/scsi_transport_spi.c index 014d7fea1ff3..6f56f8750635 100644 --- a/drivers/scsi/scsi_transport_spi.c +++ b/drivers/scsi/scsi_transport_spi.c @@ -46,7 +46,6 @@ * two cc/ua clears */ /* Private data accessors (keep these out of the header file) */ -#define spi_dv_pending(x) (((struct spi_transport_attrs *)&(x)->starget_data)->dv_pending) #define spi_dv_in_progress(x) (((struct spi_transport_attrs *)&(x)->starget_data)->dv_in_progress) #define spi_dv_mutex(x) (((struct spi_transport_attrs *)&(x)->starget_data)->dv_mutex) diff --git a/include/scsi/scsi_transport_spi.h b/include/scsi/scsi_transport_spi.h index da180f738477..286e9628ed8b 100644 --- a/include/scsi/scsi_transport_spi.h +++ b/include/scsi/scsi_transport_spi.h @@ -85,6 +85,7 @@ struct spi_host_attrs { #define spi_pcomp_en(x) (((struct spi_transport_attrs *)&(x)->starget_data)->pcomp_en) #define spi_hold_mcs(x) (((struct spi_transport_attrs *)&(x)->starget_data)->hold_mcs) #define spi_initial_dv(x) (((struct spi_transport_attrs *)&(x)->starget_data)->initial_dv) +#define spi_dv_pending(x) (((struct spi_transport_attrs *)&(x)->starget_data)->dv_pending) #define spi_support_sync(x) (((struct spi_transport_attrs *)&(x)->starget_data)->support_sync) #define spi_support_wide(x) (((struct spi_transport_attrs *)&(x)->starget_data)->support_wide) -- cgit v1.2.3 From a9344e68ac0a656475006737dbc258d69fe4f7b0 Mon Sep 17 00:00:00 2001 From: "Darrick J. Wong" Date: Mon, 29 Jan 2007 23:48:19 -0800 Subject: [SCSI] libsas: Add an LU reset mechanism to the error handler After discussion with andmike and dougg, it seems that the purpose of eh_device_reset_handler is to issue LU resets, and that eh_bus_reset_handler would be a more appropriate place for a phy reset. Signed-off-by: Darrick J. Wong Signed-off-by: James Bottomley --- drivers/scsi/libsas/sas_scsi_host.c | 40 +++++++++++++++++++++++++++++++++---- include/scsi/libsas.h | 1 + 2 files changed, 37 insertions(+), 4 deletions(-) (limited to 'include/scsi') diff --git a/drivers/scsi/libsas/sas_scsi_host.c b/drivers/scsi/libsas/sas_scsi_host.c index cd2378010c7e..897a5e2c55e4 100644 --- a/drivers/scsi/libsas/sas_scsi_host.c +++ b/drivers/scsi/libsas/sas_scsi_host.c @@ -421,8 +421,29 @@ struct sas_phy *find_local_sas_phy(struct domain_device *dev) return exphy->phy; } -/* Attempt to send a target reset message to a device */ +/* Attempt to send a LUN reset message to a device */ int sas_eh_device_reset_handler(struct scsi_cmnd *cmd) +{ + struct domain_device *dev = cmd_to_domain_dev(cmd); + struct sas_internal *i = + to_sas_internal(dev->port->ha->core.shost->transportt); + struct scsi_lun lun; + int res; + + int_to_scsilun(cmd->device->lun, &lun); + + if (!i->dft->lldd_lu_reset) + return FAILED; + + res = i->dft->lldd_lu_reset(dev, lun.scsi_lun); + if (res == TMF_RESP_FUNC_SUCC || res == TMF_RESP_FUNC_COMPLETE) + return SUCCESS; + + return FAILED; +} + +/* Attempt to send a phy (bus) reset */ +int sas_eh_bus_reset_handler(struct scsi_cmnd *cmd) { struct domain_device *dev = cmd_to_domain_dev(cmd); struct sas_phy *phy = find_local_sas_phy(dev); @@ -430,7 +451,7 @@ int sas_eh_device_reset_handler(struct scsi_cmnd *cmd) res = sas_phy_reset(phy, 1); if (res) - SAS_DPRINTK("Device reset of %s failed 0x%x\n", + SAS_DPRINTK("Bus reset of %s failed 0x%x\n", phy->dev.kobj.k_name, res); if (res == TMF_RESP_FUNC_SUCC || res == TMF_RESP_FUNC_COMPLETE) @@ -443,10 +464,20 @@ int sas_eh_device_reset_handler(struct scsi_cmnd *cmd) static int try_to_reset_cmd_device(struct Scsi_Host *shost, struct scsi_cmnd *cmd) { + int res; + if (!shost->hostt->eh_device_reset_handler) - return FAILED; + goto try_bus_reset; + + res = shost->hostt->eh_device_reset_handler(cmd); + if (res == SUCCESS) + return res; - return shost->hostt->eh_device_reset_handler(cmd); +try_bus_reset: + if (shost->hostt->eh_bus_reset_handler) + return shost->hostt->eh_bus_reset_handler(cmd); + + return FAILED; } static int sas_eh_handle_sas_errors(struct Scsi_Host *shost, @@ -976,3 +1007,4 @@ EXPORT_SYMBOL_GPL(sas_task_abort); EXPORT_SYMBOL_GPL(sas_phy_reset); EXPORT_SYMBOL_GPL(sas_phy_enable); EXPORT_SYMBOL_GPL(sas_eh_device_reset_handler); +EXPORT_SYMBOL_GPL(sas_eh_bus_reset_handler); diff --git a/include/scsi/libsas.h b/include/scsi/libsas.h index b200233cc6f4..8516ba68cc95 100644 --- a/include/scsi/libsas.h +++ b/include/scsi/libsas.h @@ -661,5 +661,6 @@ void sas_init_dev(struct domain_device *); void sas_task_abort(struct sas_task *); int __sas_task_abort(struct sas_task *); int sas_eh_device_reset_handler(struct scsi_cmnd *cmd); +int sas_eh_bus_reset_handler(struct scsi_cmnd *cmd); #endif /* _SASLIB_H_ */ -- cgit v1.2.3