summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--drivers/scsi/aic94xx/aic94xx_scb.c51
-rw-r--r--drivers/scsi/libsas/sas_init.c2
-rw-r--r--drivers/scsi/libsas/sas_scsi_host.c1
-rw-r--r--include/scsi/libsas.h1
-rw-r--r--include/scsi/scsi_transport_sas.h2
5 files changed, 49 insertions, 8 deletions
diff --git a/drivers/scsi/aic94xx/aic94xx_scb.c b/drivers/scsi/aic94xx/aic94xx_scb.c
index 1911c5d1787..a014418d670 100644
--- a/drivers/scsi/aic94xx/aic94xx_scb.c
+++ b/drivers/scsi/aic94xx/aic94xx_scb.c
@@ -343,6 +343,27 @@ void asd_invalidate_edb(struct asd_ascb *ascb, int edb_id)
}
}
+/* hard reset a phy later */
+static void do_phy_reset_later(void *data)
+{
+ struct sas_phy *sas_phy = data;
+ int error;
+
+ ASD_DPRINTK("%s: About to hard reset phy %d\n", __FUNCTION__,
+ sas_phy->identify.phy_identifier);
+ /* Reset device port */
+ error = sas_phy_reset(sas_phy, 1);
+ if (error)
+ ASD_DPRINTK("%s: Hard reset of phy %d failed (%d).\n",
+ __FUNCTION__, sas_phy->identify.phy_identifier, error);
+}
+
+static void phy_reset_later(struct sas_phy *sas_phy, struct Scsi_Host *shost)
+{
+ INIT_WORK(&sas_phy->reset_work, do_phy_reset_later, sas_phy);
+ queue_work(shost->work_q, &sas_phy->reset_work);
+}
+
/* start up the ABORT TASK tmf... */
static void task_kill_later(struct asd_ascb *ascb)
{
@@ -402,7 +423,9 @@ static void escb_tasklet_complete(struct asd_ascb *ascb,
goto out;
}
case REQ_DEVICE_RESET: {
- struct asd_ascb *a, *b;
+ struct Scsi_Host *shost = sas_ha->core.shost;
+ struct sas_phy *dev_phy;
+ struct asd_ascb *a;
u16 conn_handle;
conn_handle = *((u16*)(&dl->status_block[1]));
@@ -412,17 +435,31 @@ static void escb_tasklet_complete(struct asd_ascb *ascb,
dl->status_block[3]);
/* Kill all pending tasks and reset the device */
- list_for_each_entry_safe(a, b, &asd_ha->seq.pend_q, list) {
- struct sas_task *task = a->uldd_task;
- struct domain_device *dev = task->dev;
+ dev_phy = NULL;
+ list_for_each_entry(a, &asd_ha->seq.pend_q, list) {
+ struct sas_task *task;
+ struct domain_device *dev;
u16 x;
- x = *((u16*)(&dev->lldd_dev));
- if (x == conn_handle)
+ task = a->uldd_task;
+ if (!task)
+ continue;
+ dev = task->dev;
+
+ x = (u16)dev->lldd_dev;
+ if (x == conn_handle) {
+ dev_phy = dev->port->phy;
task_kill_later(a);
+ }
}
- /* FIXME: Reset device port (huh?) */
+ /* Reset device port */
+ if (!dev_phy) {
+ ASD_DPRINTK("%s: No pending commands; can't reset.\n",
+ __FUNCTION__);
+ goto out;
+ }
+ phy_reset_later(dev_phy, shost);
goto out;
}
case SIGNAL_NCQ_ERROR:
diff --git a/drivers/scsi/libsas/sas_init.c b/drivers/scsi/libsas/sas_init.c
index a2b479a6590..0fb347b4b1a 100644
--- a/drivers/scsi/libsas/sas_init.c
+++ b/drivers/scsi/libsas/sas_init.c
@@ -144,7 +144,7 @@ static int sas_get_linkerrors(struct sas_phy *phy)
return sas_smp_get_phy_events(phy);
}
-static int sas_phy_reset(struct sas_phy *phy, int hard_reset)
+int sas_phy_reset(struct sas_phy *phy, int hard_reset)
{
int ret;
enum phy_func reset_type;
diff --git a/drivers/scsi/libsas/sas_scsi_host.c b/drivers/scsi/libsas/sas_scsi_host.c
index c5fd3752272..e064aac06b9 100644
--- a/drivers/scsi/libsas/sas_scsi_host.c
+++ b/drivers/scsi/libsas/sas_scsi_host.c
@@ -865,3 +865,4 @@ 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_phy_reset);
diff --git a/include/scsi/libsas.h b/include/scsi/libsas.h
index a1fc20a47c5..29f6e1af1bf 100644
--- a/include/scsi/libsas.h
+++ b/include/scsi/libsas.h
@@ -597,6 +597,7 @@ 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_phy_reset(struct sas_phy *phy, int hard_reset);
extern int sas_queuecommand(struct scsi_cmnd *,
void (*scsi_done)(struct scsi_cmnd *));
extern int sas_target_alloc(struct scsi_target *);
diff --git a/include/scsi/scsi_transport_sas.h b/include/scsi/scsi_transport_sas.h
index 53024377f3b..59633a82de4 100644
--- a/include/scsi/scsi_transport_sas.h
+++ b/include/scsi/scsi_transport_sas.h
@@ -73,6 +73,8 @@ struct sas_phy {
/* for the list of phys belonging to a port */
struct list_head port_siblings;
+
+ struct work_struct reset_work;
};
#define dev_to_phy(d) \