aboutsummaryrefslogtreecommitdiff
path: root/drivers/scsi/libsas/sas_ata.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/scsi/libsas/sas_ata.c')
-rw-r--r--drivers/scsi/libsas/sas_ata.c127
1 files changed, 107 insertions, 20 deletions
diff --git a/drivers/scsi/libsas/sas_ata.c b/drivers/scsi/libsas/sas_ata.c
index d109cc3a17b..766098af4eb 100644
--- a/drivers/scsi/libsas/sas_ata.c
+++ b/drivers/scsi/libsas/sas_ata.c
@@ -211,7 +211,7 @@ static unsigned int sas_ata_qc_issue(struct ata_queued_cmd *qc)
qc->tf.nsect = 0;
}
- ata_tf_to_fis(&qc->tf, 1, 0, (u8*)&task->ata_task.fis);
+ ata_tf_to_fis(&qc->tf, qc->dev->link->pmp, 1, (u8 *)&task->ata_task.fis);
task->uldd_task = qc;
if (ata_is_atapi(qc->tf.protocol)) {
memcpy(task->ata_task.atapi_packet, qc->cdb, qc->dev->cdb_len);
@@ -285,14 +285,14 @@ int sas_get_ata_info(struct domain_device *dev, struct ex_phy *phy)
if (phy->attached_tproto & SAS_PROTOCOL_STP)
dev->tproto = phy->attached_tproto;
if (phy->attached_sata_dev)
- dev->tproto |= SATA_DEV;
+ dev->tproto |= SAS_SATA_DEV;
- if (phy->attached_dev_type == SATA_PENDING)
- dev->dev_type = SATA_PENDING;
+ if (phy->attached_dev_type == SAS_SATA_PENDING)
+ dev->dev_type = SAS_SATA_PENDING;
else {
int res;
- dev->dev_type = SATA_DEV;
+ dev->dev_type = SAS_SATA_DEV;
res = sas_get_report_phy_sata(dev->parent, phy->phy_id,
&dev->sata_dev.rps_resp);
if (res) {
@@ -314,7 +314,7 @@ static int sas_ata_clear_pending(struct domain_device *dev, struct ex_phy *phy)
int res;
/* we weren't pending, so successfully end the reset sequence now */
- if (dev->dev_type != SATA_PENDING)
+ if (dev->dev_type != SAS_SATA_PENDING)
return 1;
/* hmmm, if this succeeds do we need to repost the domain_device to the
@@ -348,9 +348,9 @@ static int smp_ata_check_ready(struct ata_link *link)
return 0;
switch (ex_phy->attached_dev_type) {
- case SATA_PENDING:
+ case SAS_SATA_PENDING:
return 0;
- case SAS_END_DEV:
+ case SAS_END_DEVICE:
if (ex_phy->attached_sata_dev)
return sas_ata_clear_pending(dev, ex_phy);
default:
@@ -523,6 +523,31 @@ static void sas_ata_set_dmamode(struct ata_port *ap, struct ata_device *ata_dev)
i->dft->lldd_ata_set_dmamode(dev);
}
+static void sas_ata_sched_eh(struct ata_port *ap)
+{
+ struct domain_device *dev = ap->private_data;
+ struct sas_ha_struct *ha = dev->port->ha;
+ unsigned long flags;
+
+ spin_lock_irqsave(&ha->lock, flags);
+ if (!test_and_set_bit(SAS_DEV_EH_PENDING, &dev->state))
+ ha->eh_active++;
+ ata_std_sched_eh(ap);
+ spin_unlock_irqrestore(&ha->lock, flags);
+}
+
+void sas_ata_end_eh(struct ata_port *ap)
+{
+ struct domain_device *dev = ap->private_data;
+ struct sas_ha_struct *ha = dev->port->ha;
+ unsigned long flags;
+
+ spin_lock_irqsave(&ha->lock, flags);
+ if (test_and_clear_bit(SAS_DEV_EH_PENDING, &dev->state))
+ ha->eh_active--;
+ spin_unlock_irqrestore(&ha->lock, flags);
+}
+
static struct ata_port_operations sas_sata_ops = {
.prereset = ata_std_prereset,
.hardreset = sas_ata_hard_reset,
@@ -536,6 +561,8 @@ static struct ata_port_operations sas_sata_ops = {
.port_start = ata_sas_port_start,
.port_stop = ata_sas_port_stop,
.set_dmamode = sas_ata_set_dmamode,
+ .sched_eh = sas_ata_sched_eh,
+ .end_eh = sas_ata_end_eh,
};
static struct ata_port_info sata_port_info = {
@@ -553,10 +580,7 @@ int sas_ata_init(struct domain_device *found_dev)
struct ata_port *ap;
int rc;
- ata_host_init(&found_dev->sata_dev.ata_host,
- ha->dev,
- sata_port_info.flags,
- &sas_sata_ops);
+ ata_host_init(&found_dev->sata_dev.ata_host, ha->dev, &sas_sata_ops);
ap = ata_sas_port_alloc(&found_dev->sata_dev.ata_host,
&sata_port_info,
shost);
@@ -591,7 +615,6 @@ void sas_ata_task_abort(struct sas_task *task)
spin_lock_irqsave(q->queue_lock, flags);
blk_abort_request(qc->scsicmd->request);
spin_unlock_irqrestore(q->queue_lock, flags);
- scsi_schedule_eh(qc->scsicmd->device->host);
return;
}
@@ -608,7 +631,7 @@ static void sas_get_ata_command_set(struct domain_device *dev)
struct dev_to_host_fis *fis =
(struct dev_to_host_fis *) dev->frame_rcvd;
- if (dev->dev_type == SATA_PENDING)
+ if (dev->dev_type == SAS_SATA_PENDING)
return;
if ((fis->sector_count == 1 && /* ATA */
@@ -674,6 +697,67 @@ void sas_probe_sata(struct asd_sas_port *port)
if (ata_dev_disabled(sas_to_ata_dev(dev)))
sas_fail_probe(dev, __func__, -ENODEV);
}
+
+}
+
+static void sas_ata_flush_pm_eh(struct asd_sas_port *port, const char *func)
+{
+ struct domain_device *dev, *n;
+
+ list_for_each_entry_safe(dev, n, &port->dev_list, dev_list_node) {
+ if (!dev_is_sata(dev))
+ continue;
+
+ sas_ata_wait_eh(dev);
+
+ /* if libata failed to power manage the device, tear it down */
+ if (ata_dev_disabled(sas_to_ata_dev(dev)))
+ sas_fail_probe(dev, func, -ENODEV);
+ }
+}
+
+void sas_suspend_sata(struct asd_sas_port *port)
+{
+ struct domain_device *dev;
+
+ mutex_lock(&port->ha->disco_mutex);
+ list_for_each_entry(dev, &port->dev_list, dev_list_node) {
+ struct sata_device *sata;
+
+ if (!dev_is_sata(dev))
+ continue;
+
+ sata = &dev->sata_dev;
+ if (sata->ap->pm_mesg.event == PM_EVENT_SUSPEND)
+ continue;
+
+ ata_sas_port_suspend(sata->ap);
+ }
+ mutex_unlock(&port->ha->disco_mutex);
+
+ sas_ata_flush_pm_eh(port, __func__);
+}
+
+void sas_resume_sata(struct asd_sas_port *port)
+{
+ struct domain_device *dev;
+
+ mutex_lock(&port->ha->disco_mutex);
+ list_for_each_entry(dev, &port->dev_list, dev_list_node) {
+ struct sata_device *sata;
+
+ if (!dev_is_sata(dev))
+ continue;
+
+ sata = &dev->sata_dev;
+ if (sata->ap->pm_mesg.event == PM_EVENT_ON)
+ continue;
+
+ ata_sas_port_resume(sata->ap);
+ }
+ mutex_unlock(&port->ha->disco_mutex);
+
+ sas_ata_flush_pm_eh(port, __func__);
}
/**
@@ -688,7 +772,7 @@ int sas_discover_sata(struct domain_device *dev)
{
int res;
- if (dev->dev_type == SATA_PM)
+ if (dev->dev_type == SAS_SATA_PM)
return -ENODEV;
sas_get_ata_command_set(dev);
@@ -708,10 +792,6 @@ static void async_sas_ata_eh(void *data, async_cookie_t cookie)
struct ata_port *ap = dev->sata_dev.ap;
struct sas_ha_struct *ha = dev->port->ha;
- /* hold a reference over eh since we may be racing with final
- * remove once all commands are completed
- */
- kref_get(&dev->kref);
sas_ata_printk(KERN_DEBUG, dev, "dev error handler\n");
ata_scsi_port_error_handler(ha->core.shost, ap);
sas_put_device(dev);
@@ -720,7 +800,7 @@ static void async_sas_ata_eh(void *data, async_cookie_t cookie)
void sas_ata_strategy_handler(struct Scsi_Host *shost)
{
struct sas_ha_struct *sas_ha = SHOST_TO_SAS_HA(shost);
- LIST_HEAD(async);
+ ASYNC_DOMAIN_EXCLUSIVE(async);
int i;
/* it's ok to defer revalidation events during ata eh, these
@@ -742,6 +822,13 @@ void sas_ata_strategy_handler(struct Scsi_Host *shost)
list_for_each_entry(dev, &port->dev_list, dev_list_node) {
if (!dev_is_sata(dev))
continue;
+
+ /* hold a reference over eh since we may be
+ * racing with final remove once all commands
+ * are completed
+ */
+ kref_get(&dev->kref);
+
async_schedule_domain(async_sas_ata_eh, dev, &async);
}
spin_unlock(&port->dev_list_lock);