From fa1c1e8f1ece48c7baa3ba529bfd0d10a0bdf4eb Mon Sep 17 00:00:00 2001 From: "Darrick J. Wong" Date: Thu, 10 Aug 2006 19:19:47 -0700 Subject: [SCSI] Add SATA support to libsas Hook the scsi_host_template functions in libsas to delegate functionality to libata when appropriate. Signed-off-by: Darrick J. Wong Misc code changes and merge fixes and update for libata->drivers/ata move Signed-off-by: James Bottomley --- drivers/scsi/libsas/sas_discover.c | 11 +- drivers/scsi/libsas/sas_scsi_host.c | 342 ++++++++++++++++++++++++++++++++++++ 2 files changed, 351 insertions(+), 2 deletions(-) (limited to 'drivers/scsi/libsas') diff --git a/drivers/scsi/libsas/sas_discover.c b/drivers/scsi/libsas/sas_discover.c index a65598b1e53..5252143b629 100644 --- a/drivers/scsi/libsas/sas_discover.c +++ b/drivers/scsi/libsas/sas_discover.c @@ -255,6 +255,7 @@ static int sas_get_port_device(struct asd_sas_port *port) switch (dev->dev_type) { case SAS_END_DEV: + case SATA_DEV: rphy = sas_end_device_alloc(port->port); break; case EDGE_DEV: @@ -265,7 +266,6 @@ static int sas_get_port_device(struct asd_sas_port *port) rphy = sas_expander_alloc(port->port, SAS_FANOUT_EXPANDER_DEVICE); break; - case SATA_DEV: default: printk("ERROR: Unidentified device type %d\n", dev->dev_type); rphy = NULL; @@ -480,7 +480,14 @@ cont1: present. sas_satl_register_dev(dev); */ - return 0; + + sas_fill_in_rphy(dev, dev->rphy); + + res = sas_rphy_add(dev->rphy); + if (res) + goto out_err; + + return res; out_err: dev->sata_dev.identify_packet_device = NULL; dev->sata_dev.identify_device = NULL; diff --git a/drivers/scsi/libsas/sas_scsi_host.c b/drivers/scsi/libsas/sas_scsi_host.c index 9c5342e7a69..3220b3fc6b2 100644 --- a/drivers/scsi/libsas/sas_scsi_host.c +++ b/drivers/scsi/libsas/sas_scsi_host.c @@ -42,6 +42,7 @@ #include #include #include +#include /* ---------- SCSI Host glue ---------- */ @@ -192,6 +193,11 @@ static int sas_queue_up(struct sas_task *task) return 0; } +static inline int dev_is_sata(struct domain_device *dev) +{ + return (dev->rphy->identify.target_port_protocols & SAS_PROTOCOL_SATA); +} + /** * sas_queuecommand -- Enqueue a command for processing * @parameters: See SCSI Core documentation @@ -213,6 +219,12 @@ int sas_queuecommand(struct scsi_cmnd *cmd, struct sas_ha_struct *sas_ha = dev->port->ha; struct sas_task *task; + if (dev_is_sata(dev)) { + res = ata_sas_queuecmd(cmd, scsi_done, + dev->sata_dev.ap); + goto out; + } + res = -ENOMEM; task = sas_create_task(cmd, dev, GFP_ATOMIC); if (!task) @@ -684,6 +696,279 @@ enum scsi_eh_timer_return sas_scsi_timed_out(struct scsi_cmnd *cmd) return EH_NOT_HANDLED; } + +static enum ata_completion_errors sas_to_ata_err(struct task_status_struct *ts) +{ + /* Cheesy attempt to translate SAS errors into ATA. Hah! */ + + /* transport error */ + if (ts->resp == SAS_TASK_UNDELIVERED) + return AC_ERR_ATA_BUS; + + /* ts->resp == SAS_TASK_COMPLETE */ + /* task delivered, what happened afterwards? */ + switch (ts->stat) { + case SAS_DEV_NO_RESPONSE: + return AC_ERR_TIMEOUT; + + case SAS_INTERRUPTED: + case SAS_PHY_DOWN: + case SAS_NAK_R_ERR: + return AC_ERR_ATA_BUS; + + + case SAS_DATA_UNDERRUN: + /* + * Some programs that use the taskfile interface + * (smartctl in particular) can cause underrun + * problems. Ignore these errors, perhaps at our + * peril. + */ + return 0; + + case SAS_DATA_OVERRUN: + case SAS_QUEUE_FULL: + case SAS_DEVICE_UNKNOWN: + case SAS_SG_ERR: + return AC_ERR_INVALID; + + case SAM_CHECK_COND: + case SAS_OPEN_TO: + case SAS_OPEN_REJECT: + case SAS_PROTO_RESPONSE: + SAS_DPRINTK("%s: Saw error %d. What to do?\n", + __FUNCTION__, ts->stat); + return AC_ERR_OTHER; + + case SAS_ABORTED_TASK: + return AC_ERR_DEV; + + default: + return 0; + } +} + +static void sas_ata_task_done(struct sas_task *task) +{ + struct ata_queued_cmd *qc = task->uldd_task; + struct domain_device *dev = qc->ap->private_data; + struct task_status_struct *stat = &task->task_status; + struct ata_task_resp *resp = (struct ata_task_resp *)stat->buf; + enum ata_completion_errors ac; + + ac = sas_to_ata_err(stat); + if (ac) { + SAS_DPRINTK("%s: SAS error %x\n", __FUNCTION__, stat->stat); + /* We saw a SAS error. Send a vague error. */ + qc->err_mask = ac; + dev->sata_dev.tf.feature = 0x04; /* status err */ + dev->sata_dev.tf.command = ATA_ERR; + goto end; + } + + ata_tf_from_fis(resp->ending_fis, &dev->sata_dev.tf); + qc->err_mask |= ac_err_mask(dev->sata_dev.tf.command); + dev->sata_dev.sstatus = resp->sstatus; + dev->sata_dev.serror = resp->serror; + dev->sata_dev.scontrol = resp->scontrol; + dev->sata_dev.ap->sactive = resp->sactive; +end: + ata_qc_complete(qc); + list_del_init(&task->list); + sas_free_task(task); +} + +int sas_ioctl(struct scsi_device *sdev, int cmd, void __user *arg) +{ + struct domain_device *dev = sdev_to_domain_dev(sdev); + + if (dev_is_sata(dev)) + return ata_scsi_ioctl(sdev, cmd, arg); + + return -EINVAL; +} + +static unsigned int sas_ata_qc_issue(struct ata_queued_cmd *qc) +{ + int res = -ENOMEM; + struct sas_task *task; + struct domain_device *dev = qc->ap->private_data; + struct sas_ha_struct *sas_ha = dev->port->ha; + struct Scsi_Host *host = sas_ha->core.shost; + struct sas_internal *i = to_sas_internal(host->transportt); + struct scatterlist *sg; + unsigned int num = 0; + unsigned int xfer = 0; + + task = sas_alloc_task(GFP_ATOMIC); + if (!task) + goto out; + task->dev = dev; + task->task_proto = SAS_PROTOCOL_STP; + task->task_done = sas_ata_task_done; + + ata_tf_to_fis(&qc->tf, (u8*)&task->ata_task.fis, 0); + task->uldd_task = qc; + if (is_atapi_taskfile(&qc->tf)) { + memcpy(task->ata_task.atapi_packet, qc->cdb, ATAPI_CDB_LEN); + task->total_xfer_len = qc->nbytes + qc->pad_len; + task->num_scatter = qc->pad_len ? qc->n_elem + 1 : qc->n_elem; + } else { + ata_for_each_sg(sg, qc) { + num++; + xfer += sg->length; + } + + task->total_xfer_len = xfer; + task->num_scatter = num; + } + + task->data_dir = qc->dma_dir; + task->scatter = qc->__sg; + task->ata_task.retry_count = 1; + task->task_state_flags = SAS_TASK_STATE_PENDING; + + if (qc->tf.protocol == ATA_PROT_DMA) + task->ata_task.dma_xfer = 1; + + if (sas_ha->lldd_max_execute_num < 2) + res = i->dft->lldd_execute_task(task, 1, GFP_ATOMIC); + else + res = sas_queue_up(task); + + /* Examine */ + if (res) { + SAS_DPRINTK("lldd_execute_task returned: %d\n", res); + + sas_free_task(task); + if (res == -SAS_QUEUE_FULL) + return -ENOMEM; + } + +out: + return res; +} + +static u8 sas_ata_check_status(struct ata_port *ap) +{ + struct domain_device *dev = ap->private_data; + return dev->sata_dev.tf.command; +} + +static void sas_ata_phy_reset(struct ata_port *ap) +{ + struct domain_device *dev = ap->private_data; + struct sas_internal *i = + to_sas_internal(dev->port->ha->core.shost->transportt); + int res = 0; + + if (i->dft->lldd_I_T_nexus_reset) + res = i->dft->lldd_I_T_nexus_reset(dev); + + if (res) + SAS_DPRINTK("%s: Unable to reset I T nexus?\n", __FUNCTION__); + + switch (dev->sata_dev.command_set) { + case ATA_COMMAND_SET: + SAS_DPRINTK("%s: Found ATA device.\n", __FUNCTION__); + ap->device[0].class = ATA_DEV_ATA; + break; + case ATAPI_COMMAND_SET: + SAS_DPRINTK("%s: Found ATAPI device.\n", __FUNCTION__); + ap->device[0].class = ATA_DEV_ATAPI; + break; + default: + SAS_DPRINTK("%s: Unknown SATA command set: %d.\n", + __FUNCTION__, + dev->sata_dev.command_set); + ap->device[0].class = ATA_DEV_ATA; + break; + } + + ap->cbl = ATA_CBL_SATA; +} + +static void sas_ata_post_internal(struct ata_queued_cmd *qc) +{ + if (qc->flags & ATA_QCFLAG_FAILED) + qc->err_mask |= AC_ERR_OTHER; + + if (qc->err_mask) + SAS_DPRINTK("%s: Failure; reset phy!\n", __FUNCTION__); +} + +static void sas_ata_tf_read(struct ata_port *ap, struct ata_taskfile *tf) +{ + struct domain_device *dev = ap->private_data; + memcpy(tf, &dev->sata_dev.tf, sizeof (*tf)); +} + +static void sas_ata_scr_write(struct ata_port *ap, unsigned int sc_reg_in, + u32 val) +{ + struct domain_device *dev = ap->private_data; + + SAS_DPRINTK("STUB %s\n", __FUNCTION__); + switch (sc_reg_in) { + case SCR_STATUS: + dev->sata_dev.sstatus = val; + break; + case SCR_CONTROL: + dev->sata_dev.scontrol = val; + break; + case SCR_ERROR: + dev->sata_dev.serror = val; + break; + case SCR_ACTIVE: + dev->sata_dev.ap->sactive = val; + break; + } +} + +static u32 sas_ata_scr_read(struct ata_port *ap, unsigned int sc_reg_in) +{ + struct domain_device *dev = ap->private_data; + + SAS_DPRINTK("STUB %s\n", __FUNCTION__); + switch (sc_reg_in) { + case SCR_STATUS: + return dev->sata_dev.sstatus; + case SCR_CONTROL: + return dev->sata_dev.scontrol; + case SCR_ERROR: + return dev->sata_dev.serror; + case SCR_ACTIVE: + return dev->sata_dev.ap->sactive; + default: + return 0xffffffffU; + } +} + +static struct ata_port_operations sas_sata_ops = { + .port_disable = ata_port_disable, + .check_status = sas_ata_check_status, + .check_altstatus = sas_ata_check_status, + .dev_select = ata_noop_dev_select, + .phy_reset = sas_ata_phy_reset, + .post_internal_cmd = sas_ata_post_internal, + .tf_read = sas_ata_tf_read, + .qc_prep = ata_noop_qc_prep, + .qc_issue = sas_ata_qc_issue, + .port_start = ata_sas_port_start, + .port_stop = ata_sas_port_stop, + .scr_read = sas_ata_scr_read, + .scr_write = sas_ata_scr_write +}; + +static struct ata_port_info sata_port_info = { + .flags = ATA_FLAG_SATA | ATA_FLAG_NO_LEGACY | ATA_FLAG_SATA_RESET | + ATA_FLAG_MMIO | ATA_FLAG_PIO_DMA, + .pio_mask = 0x1f, /* PIO0-4 */ + .mwdma_mask = 0x07, /* MWDMA0-2 */ + .udma_mask = ATA_UDMA6, + .port_ops = &sas_sata_ops +}; + struct domain_device *sas_find_dev_by_rphy(struct sas_rphy *rphy) { struct Scsi_Host *shost = dev_to_shost(rphy->dev.parent); @@ -722,11 +1007,33 @@ static inline struct domain_device *sas_find_target(struct scsi_target *starget) int sas_target_alloc(struct scsi_target *starget) { + struct Scsi_Host *shost = dev_to_shost(&starget->dev); + struct sas_ha_struct *ha = SHOST_TO_SAS_HA(shost); struct domain_device *found_dev = sas_find_target(starget); if (!found_dev) return -ENODEV; + if (dev_is_sata(found_dev)) { + struct ata_port *ap; + + ata_host_init(&found_dev->sata_dev.ata_host, + &ha->pcidev->dev, + sata_port_info.flags, + &sas_sata_ops); + ap = ata_sas_port_alloc(&found_dev->sata_dev.ata_host, + &sata_port_info, + shost); + if (!ap) { + SAS_DPRINTK("ata_sas_port_alloc failed.\n"); + return -ENODEV; + } + + ap->private_data = found_dev; + ap->cbl = ATA_CBL_SATA; + found_dev->sata_dev.ap = ap; + } + starget->hostdata = found_dev; return 0; } @@ -741,6 +1048,11 @@ int sas_slave_configure(struct scsi_device *scsi_dev) BUG_ON(dev->rphy->identify.device_type != SAS_END_DEVICE); + if (dev_is_sata(dev)) { + ata_sas_slave_configure(scsi_dev, dev->sata_dev.ap); + return 0; + } + sas_ha = dev->port->ha; sas_read_port_mode_page(scsi_dev); @@ -764,6 +1076,10 @@ int sas_slave_configure(struct scsi_device *scsi_dev) void sas_slave_destroy(struct scsi_device *scsi_dev) { + struct domain_device *dev = sdev_to_domain_dev(scsi_dev); + + if (dev_is_sata(dev)) + ata_port_disable(dev->sata_dev.ap); } int sas_change_queue_depth(struct scsi_device *scsi_dev, int new_depth) @@ -984,6 +1300,29 @@ void sas_task_abort(struct sas_task *task) scsi_schedule_eh(sc->device->host); } +int sas_slave_alloc(struct scsi_device *scsi_dev) +{ + struct domain_device *dev = sdev_to_domain_dev(scsi_dev); + + if (dev_is_sata(dev)) + return ata_sas_port_init(dev->sata_dev.ap); + + return 0; +} + +void sas_target_destroy(struct scsi_target *starget) +{ + struct domain_device *found_dev = sas_find_target(starget); + + if (!found_dev) + return; + + if (dev_is_sata(found_dev)) + ata_sas_port_destroy(found_dev->sata_dev.ap); + + return; +} + EXPORT_SYMBOL_GPL(sas_queuecommand); EXPORT_SYMBOL_GPL(sas_target_alloc); EXPORT_SYMBOL_GPL(sas_slave_configure); @@ -997,3 +1336,6 @@ 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); +EXPORT_SYMBOL_GPL(sas_slave_alloc); +EXPORT_SYMBOL_GPL(sas_target_destroy); +EXPORT_SYMBOL_GPL(sas_ioctl); -- cgit v1.2.3 From 1acce1942a32296f7c25ba82776c97e9c04c8e1e Mon Sep 17 00:00:00 2001 From: James Bottomley Date: Tue, 22 Aug 2006 12:39:19 -0500 Subject: [SCSI] libsas: Add SATA support to STP piece for SATA on SAS expanders This patch adds support for SATA over SAS expanders to the previous two SATA support in libsas patches. There were a couple of nasty non trivial things to sort out before this one could be made to work. Firstly, I'd like to thank Doug Gilbert for diagnosing a problem with the LSI expanders where the REPORT_SATA_PHY command was returning the D2H FIS in the wrong order (Although, here, I think I have to blame the SAS standards which specifies the FIS "shall be returned in little endian format" and later on "which means resp[24] shall be FIS type" The latter, of course, implying big endian format). Just to make sure, I put a check for the D2H FIS type being in the wrong position and reverse the FIS data if it is. The second is a problem outlined in Annex G of the SAS standard (again, a technical point with D2H FIS ... necessitating a phy reset on certain conditions). With the patch, I can now see my SATA-1 disk in a cascaded expander configuration. Signed-off-by: James Bottomley --- drivers/scsi/libsas/sas_expander.c | 81 ++++++++++++++++++++++++++++++++------ 1 file changed, 68 insertions(+), 13 deletions(-) (limited to 'drivers/scsi/libsas') diff --git a/drivers/scsi/libsas/sas_expander.c b/drivers/scsi/libsas/sas_expander.c index 23e90c5f8f3..0c4e3a97791 100644 --- a/drivers/scsi/libsas/sas_expander.c +++ b/drivers/scsi/libsas/sas_expander.c @@ -220,6 +220,36 @@ static void sas_set_ex_phy(struct domain_device *dev, int phy_id, #define DISCOVER_REQ_SIZE 16 #define DISCOVER_RESP_SIZE 56 +static int sas_ex_phy_discover_helper(struct domain_device *dev, u8 *disc_req, + u8 *disc_resp, int single) +{ + int i, res; + + disc_req[9] = single; + for (i = 1 ; i < 3; i++) { + struct discover_resp *dr; + + res = smp_execute_task(dev, disc_req, DISCOVER_REQ_SIZE, + disc_resp, DISCOVER_RESP_SIZE); + if (res) + return res; + /* This is detecting a failure to transmit inital + * dev to host FIS as described in section G.5 of + * sas-2 r 04b */ + dr = &((struct smp_resp *)disc_resp)->disc; + if (!(dr->attached_dev_type == 0 && + dr->attached_sata_dev)) + break; + /* In order to generate the dev to host FIS, we + * send a link reset to the expander port */ + sas_smp_phy_control(dev, single, PHY_FUNC_LINK_RESET); + /* Wait for the reset to trigger the negotiation */ + msleep(500); + } + sas_set_ex_phy(dev, single, disc_resp); + return 0; +} + static int sas_ex_phy_discover(struct domain_device *dev, int single) { struct expander_device *ex = &dev->ex_dev; @@ -240,23 +270,15 @@ static int sas_ex_phy_discover(struct domain_device *dev, int single) disc_req[1] = SMP_DISCOVER; if (0 <= single && single < ex->num_phys) { - disc_req[9] = single; - res = smp_execute_task(dev, disc_req, DISCOVER_REQ_SIZE, - disc_resp, DISCOVER_RESP_SIZE); - if (res) - goto out_err; - sas_set_ex_phy(dev, single, disc_resp); + res = sas_ex_phy_discover_helper(dev, disc_req, disc_resp, single); } else { int i; for (i = 0; i < ex->num_phys; i++) { - disc_req[9] = i; - res = smp_execute_task(dev, disc_req, - DISCOVER_REQ_SIZE, disc_resp, - DISCOVER_RESP_SIZE); + res = sas_ex_phy_discover_helper(dev, disc_req, + disc_resp, i); if (res) goto out_err; - sas_set_ex_phy(dev, i, disc_resp); } } out_err: @@ -529,6 +551,7 @@ static int sas_get_report_phy_sata(struct domain_device *dev, { int res; u8 *rps_req = alloc_smp_req(RPS_REQ_SIZE); + u8 *resp = (u8 *)rps_resp; if (!rps_req) return -ENOMEM; @@ -539,8 +562,28 @@ static int sas_get_report_phy_sata(struct domain_device *dev, res = smp_execute_task(dev, rps_req, RPS_REQ_SIZE, rps_resp, RPS_RESP_SIZE); + /* 0x34 is the FIS type for the D2H fis. There's a potential + * standards cockup here. sas-2 explicitly specifies the FIS + * should be encoded so that FIS type is in resp[24]. + * However, some expanders endian reverse this. Undo the + * reversal here */ + if (!res && resp[27] == 0x34 && resp[24] != 0x34) { + int i; + + for (i = 0; i < 5; i++) { + int j = 24 + (i*4); + u8 a, b; + a = resp[j + 0]; + b = resp[j + 1]; + resp[j + 0] = resp[j + 3]; + resp[j + 1] = resp[j + 2]; + resp[j + 2] = b; + resp[j + 3] = a; + } + } + kfree(rps_req); - return 0; + return res; } static void sas_ex_get_linkrate(struct domain_device *parent, @@ -625,14 +668,26 @@ static struct domain_device *sas_ex_discover_end_dev( } memcpy(child->frame_rcvd, &child->sata_dev.rps_resp.rps.fis, sizeof(struct dev_to_host_fis)); + + rphy = sas_end_device_alloc(phy->port); + /* FIXME: error handling */ + BUG_ON(!rphy); + sas_init_dev(child); + + child->rphy = rphy; + + spin_lock(&parent->port->dev_list_lock); + list_add_tail(&child->dev_list_node, &parent->port->dev_list); + spin_unlock(&parent->port->dev_list_lock); + res = sas_discover_sata(child); if (res) { SAS_DPRINTK("sas_discover_sata() for device %16llx at " "%016llx:0x%x returned 0x%x\n", SAS_ADDR(child->sas_addr), SAS_ADDR(parent->sas_addr), phy_id, res); - goto out_free; + goto out_list_del; } } else if (phy->attached_tproto & SAS_PROTO_SSP) { child->dev_type = SAS_END_DEV; -- cgit v1.2.3 From 38e2f035587b0674b3168931c8402f4d719fdd76 Mon Sep 17 00:00:00 2001 From: James Bottomley Date: Thu, 7 Sep 2006 15:52:09 -0500 Subject: [SCSI] libsas: fix up sas_smp_phy_control() The prototype of this has changed for the link speed setting patch. Need to update the SATA use of this. Signed-off-by: James Bottomley --- drivers/scsi/libsas/sas_expander.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/scsi/libsas') diff --git a/drivers/scsi/libsas/sas_expander.c b/drivers/scsi/libsas/sas_expander.c index 0c4e3a97791..07464873ab8 100644 --- a/drivers/scsi/libsas/sas_expander.c +++ b/drivers/scsi/libsas/sas_expander.c @@ -242,7 +242,7 @@ static int sas_ex_phy_discover_helper(struct domain_device *dev, u8 *disc_req, break; /* In order to generate the dev to host FIS, we * send a link reset to the expander port */ - sas_smp_phy_control(dev, single, PHY_FUNC_LINK_RESET); + sas_smp_phy_control(dev, single, PHY_FUNC_LINK_RESET, NULL); /* Wait for the reset to trigger the negotiation */ msleep(500); } -- cgit v1.2.3 From bdab4e877819cc8b682797901c8b37567fec3c5e Mon Sep 17 00:00:00 2001 From: "Darrick J. Wong" Date: Fri, 13 Oct 2006 16:56:25 -0700 Subject: [SCSI] libsas: support NCQ for SATA disks This patch adds SATAII NCQ support to libsas. Both the use_ncq and the dma_xfer flags in ata_task must be set for NCQ to work correctly on the Adaptec SAS controller. The rest of the patch adds ATA_FLAG_NCQ to sata_port_info and sets up ap->scsi_host so that ata_setup_ncq doesn't crash. Please note that this patch is against the aic94xx-sas git tree, not scsi-misc. Thanks also to James Bottomley for providing an earlier version of this patch from which to work. I've tested this patch on a x206m with a ST380819AS SATA2 disk plugged into the Adaptec SAS controller. The drive came up with a queue depth of 31, and I successfully ran an I/O flood test to coerce libata into sending multiple commands simultaneously. A kernel probe recorded the maximum tag number that had been seen before and after the flood test; before the test it was 2 and after it was 30, as I expected. Signed-off-by: Darrick J. Wong Signed-off-by: James Bottomley --- drivers/scsi/libsas/sas_scsi_host.c | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) (limited to 'drivers/scsi/libsas') diff --git a/drivers/scsi/libsas/sas_scsi_host.c b/drivers/scsi/libsas/sas_scsi_host.c index 3220b3fc6b2..274e7eb47f3 100644 --- a/drivers/scsi/libsas/sas_scsi_host.c +++ b/drivers/scsi/libsas/sas_scsi_host.c @@ -828,8 +828,14 @@ static unsigned int sas_ata_qc_issue(struct ata_queued_cmd *qc) task->ata_task.retry_count = 1; task->task_state_flags = SAS_TASK_STATE_PENDING; - if (qc->tf.protocol == ATA_PROT_DMA) + switch (qc->tf.protocol) { + case ATA_PROT_NCQ: + task->ata_task.use_ncq = 1; + /* fall through */ + case ATA_PROT_DMA: task->ata_task.dma_xfer = 1; + break; + } if (sas_ha->lldd_max_execute_num < 2) res = i->dft->lldd_execute_task(task, 1, GFP_ATOMIC); @@ -962,7 +968,7 @@ static struct ata_port_operations sas_sata_ops = { static struct ata_port_info sata_port_info = { .flags = ATA_FLAG_SATA | ATA_FLAG_NO_LEGACY | ATA_FLAG_SATA_RESET | - ATA_FLAG_MMIO | ATA_FLAG_PIO_DMA, + ATA_FLAG_MMIO | ATA_FLAG_PIO_DMA | ATA_FLAG_NCQ, .pio_mask = 0x1f, /* PIO0-4 */ .mwdma_mask = 0x07, /* MWDMA0-2 */ .udma_mask = ATA_UDMA6, @@ -1031,6 +1037,7 @@ int sas_target_alloc(struct scsi_target *starget) ap->private_data = found_dev; ap->cbl = ATA_CBL_SATA; + ap->scsi_host = shost; found_dev->sata_dev.ap = ap; } -- cgit v1.2.3 From 27e92471b5d8b3e70646dfaf9369d96773972efd Mon Sep 17 00:00:00 2001 From: James Bottomley Date: Sun, 15 Oct 2006 20:24:35 -0500 Subject: [SCSI] aic94xx: add SATAPI support It turns out this is fairly easy to plumb in by recognising the three command types and copying the CDB. The protocol response path needs to be amended to cope with SAS_PROTO_RESPONSE. Signed-off-by: James Bottomley --- drivers/scsi/libsas/sas_scsi_host.c | 40 +++++++++++++++++++++---------------- 1 file changed, 23 insertions(+), 17 deletions(-) (limited to 'drivers/scsi/libsas') diff --git a/drivers/scsi/libsas/sas_scsi_host.c b/drivers/scsi/libsas/sas_scsi_host.c index 274e7eb47f3..5ff14ed9baf 100644 --- a/drivers/scsi/libsas/sas_scsi_host.c +++ b/drivers/scsi/libsas/sas_scsi_host.c @@ -735,7 +735,6 @@ static enum ata_completion_errors sas_to_ata_err(struct task_status_struct *ts) case SAM_CHECK_COND: case SAS_OPEN_TO: case SAS_OPEN_REJECT: - case SAS_PROTO_RESPONSE: SAS_DPRINTK("%s: Saw error %d. What to do?\n", __FUNCTION__, ts->stat); return AC_ERR_OTHER; @@ -743,6 +742,10 @@ static enum ata_completion_errors sas_to_ata_err(struct task_status_struct *ts) case SAS_ABORTED_TASK: return AC_ERR_DEV; + case SAS_PROTO_RESPONSE: + /* This means the ending_fis has the error + * value; return 0 here to collect it */ + return 0; default: return 0; } @@ -756,23 +759,25 @@ static void sas_ata_task_done(struct sas_task *task) struct ata_task_resp *resp = (struct ata_task_resp *)stat->buf; enum ata_completion_errors ac; - ac = sas_to_ata_err(stat); - if (ac) { - SAS_DPRINTK("%s: SAS error %x\n", __FUNCTION__, stat->stat); - /* We saw a SAS error. Send a vague error. */ - qc->err_mask = ac; - dev->sata_dev.tf.feature = 0x04; /* status err */ - dev->sata_dev.tf.command = ATA_ERR; - goto end; + if (stat->stat == SAS_PROTO_RESPONSE) { + ata_tf_from_fis(resp->ending_fis, &dev->sata_dev.tf); + qc->err_mask |= ac_err_mask(dev->sata_dev.tf.command); + dev->sata_dev.sstatus = resp->sstatus; + dev->sata_dev.serror = resp->serror; + dev->sata_dev.scontrol = resp->scontrol; + dev->sata_dev.ap->sactive = resp->sactive; + } else if (stat->stat != SAM_STAT_GOOD) { + ac = sas_to_ata_err(stat); + if (ac) { + SAS_DPRINTK("%s: SAS error %x\n", __FUNCTION__, + stat->stat); + /* We saw a SAS error. Send a vague error. */ + qc->err_mask = ac; + dev->sata_dev.tf.feature = 0x04; /* status err */ + dev->sata_dev.tf.command = ATA_ERR; + } } - ata_tf_from_fis(resp->ending_fis, &dev->sata_dev.tf); - qc->err_mask |= ac_err_mask(dev->sata_dev.tf.command); - dev->sata_dev.sstatus = resp->sstatus; - dev->sata_dev.serror = resp->serror; - dev->sata_dev.scontrol = resp->scontrol; - dev->sata_dev.ap->sactive = resp->sactive; -end: ata_qc_complete(qc); list_del_init(&task->list); sas_free_task(task); @@ -810,7 +815,7 @@ static unsigned int sas_ata_qc_issue(struct ata_queued_cmd *qc) ata_tf_to_fis(&qc->tf, (u8*)&task->ata_task.fis, 0); task->uldd_task = qc; if (is_atapi_taskfile(&qc->tf)) { - memcpy(task->ata_task.atapi_packet, qc->cdb, ATAPI_CDB_LEN); + memcpy(task->ata_task.atapi_packet, qc->cdb, qc->dev->cdb_len); task->total_xfer_len = qc->nbytes + qc->pad_len; task->num_scatter = qc->pad_len ? qc->n_elem + 1 : qc->n_elem; } else { @@ -832,6 +837,7 @@ static unsigned int sas_ata_qc_issue(struct ata_queued_cmd *qc) case ATA_PROT_NCQ: task->ata_task.use_ncq = 1; /* fall through */ + case ATA_PROT_ATAPI_DMA: case ATA_PROT_DMA: task->ata_task.dma_xfer = 1; break; -- cgit v1.2.3 From 528fd55200ec135548e71aee43650bca92a041aa Mon Sep 17 00:00:00 2001 From: James Bottomley Date: Mon, 16 Oct 2006 10:57:05 -0500 Subject: [SCSI] libsas: better error handling in sas_ex_discover_end_dev() This replaces a few BUG_ON() statements with the correct failure error handling. There are still many more to do. Signed-off-by: James Bottomley --- drivers/scsi/libsas/sas_expander.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers/scsi/libsas') diff --git a/drivers/scsi/libsas/sas_expander.c b/drivers/scsi/libsas/sas_expander.c index 07464873ab8..d05fc23b4d5 100644 --- a/drivers/scsi/libsas/sas_expander.c +++ b/drivers/scsi/libsas/sas_expander.c @@ -670,8 +670,8 @@ static struct domain_device *sas_ex_discover_end_dev( sizeof(struct dev_to_host_fis)); rphy = sas_end_device_alloc(phy->port); - /* FIXME: error handling */ - BUG_ON(!rphy); + if (unlikely(!rphy)) + goto out_free; sas_init_dev(child); -- cgit v1.2.3 From 0281e02c5671f50701924465744edd3e2feb5d6f Mon Sep 17 00:00:00 2001 From: James Bottomley Date: Mon, 16 Oct 2006 13:25:30 -0500 Subject: [SCSI] libsas: fixup NCQ for SATA disks We actually had two problems: the one with the tag (which is fixed by zeroing the tag before sending the taskfile to the sequencer) but the other with the fact that we sent our first NCQ command to the device before the sequencer had been informed of the NCQ tagging capabilities. I fixed the latter by moving the rphy_add() to the correct point in the code after the NCQ capabilities are set up. Signed-off-by: James Bottomley --- drivers/scsi/libsas/sas_discover.c | 28 +++++++--------------------- drivers/scsi/libsas/sas_scsi_host.c | 6 ++++++ 2 files changed, 13 insertions(+), 21 deletions(-) (limited to 'drivers/scsi/libsas') diff --git a/drivers/scsi/libsas/sas_discover.c b/drivers/scsi/libsas/sas_discover.c index 5252143b629..a18c0f6d666 100644 --- a/drivers/scsi/libsas/sas_discover.c +++ b/drivers/scsi/libsas/sas_discover.c @@ -371,6 +371,7 @@ static int sas_issue_ata_cmd(struct domain_device *dev, u8 command, task->dev = dev; + task->ata_task.fis.fis_type = 0x27; task->ata_task.fis.command = command; task->ata_task.fis.features = features; task->ata_task.fis.device = d2h_fis->device; @@ -483,11 +484,7 @@ cont1: sas_fill_in_rphy(dev, dev->rphy); - res = sas_rphy_add(dev->rphy); - if (res) - goto out_err; - - return res; + return 0; out_err: dev->sata_dev.identify_packet_device = NULL; dev->sata_dev.identify_device = NULL; @@ -555,7 +552,7 @@ int sas_discover_sata(struct domain_device *dev) res = sas_notify_lldd_dev_found(dev); if (res) - goto out_err2; + return res; switch (dev->dev_type) { case SATA_DEV: @@ -567,23 +564,12 @@ int sas_discover_sata(struct domain_device *dev) default: break; } - if (res) - goto out_err; - sas_notify_lldd_dev_gone(dev); - res = sas_notify_lldd_dev_found(dev); - if (res) - goto out_err2; - - res = sas_rphy_add(dev->rphy); - if (res) - goto out_err; - - return res; + if (!res) { + sas_notify_lldd_dev_found(dev); + res = sas_rphy_add(dev->rphy); + } -out_err: - sas_notify_lldd_dev_gone(dev); -out_err2: return res; } diff --git a/drivers/scsi/libsas/sas_scsi_host.c b/drivers/scsi/libsas/sas_scsi_host.c index 5ff14ed9baf..0dc7c02b383 100644 --- a/drivers/scsi/libsas/sas_scsi_host.c +++ b/drivers/scsi/libsas/sas_scsi_host.c @@ -812,6 +812,12 @@ static unsigned int sas_ata_qc_issue(struct ata_queued_cmd *qc) task->task_proto = SAS_PROTOCOL_STP; task->task_done = sas_ata_task_done; + if (qc->tf.command == ATA_CMD_FPDMA_WRITE || + qc->tf.command == ATA_CMD_FPDMA_READ) { + /* Need to zero out the tag libata assigned us */ + qc->tf.nsect = 0; + } + ata_tf_to_fis(&qc->tf, (u8*)&task->ata_task.fis, 0); task->uldd_task = qc; if (is_atapi_taskfile(&qc->tf)) { -- cgit v1.2.3 From 338ec57003ff9d7bc1471677e61872455977a5de Mon Sep 17 00:00:00 2001 From: "Darrick J. Wong" Date: Wed, 18 Oct 2006 14:43:37 -0700 Subject: [SCSI] Migrate libsas ATA code into a separate file This is a respin of my earlier patch that migrates the ATA support code into a separate file. For now, the controversial linking bits have been removed per James Bottomley's request for a patch that contains only the migration diffs, which means that libsas continues to require libata. I intend to address that problem in a separate patch. This patch is against the aic94xx-sas-2.6 git tree, and it has been sanity tested on my x206m with Seagate SATA and SAS disks without uncovering any new problems. Signed-off-by: Darrick J. Wong Signed-off-by: James Bottomley --- drivers/scsi/libsas/Makefile | 3 +- drivers/scsi/libsas/sas_ata.c | 339 ++++++++++++++++++++++++++++++++++++ drivers/scsi/libsas/sas_scsi_host.c | 313 +-------------------------------- 3 files changed, 347 insertions(+), 308 deletions(-) create mode 100644 drivers/scsi/libsas/sas_ata.c (limited to 'drivers/scsi/libsas') diff --git a/drivers/scsi/libsas/Makefile b/drivers/scsi/libsas/Makefile index 44d972a3b4b..6383eb58d89 100644 --- a/drivers/scsi/libsas/Makefile +++ b/drivers/scsi/libsas/Makefile @@ -33,4 +33,5 @@ libsas-y += sas_init.o \ sas_dump.o \ sas_discover.o \ sas_expander.o \ - sas_scsi_host.o + sas_scsi_host.o \ + sas_ata.o diff --git a/drivers/scsi/libsas/sas_ata.c b/drivers/scsi/libsas/sas_ata.c new file mode 100644 index 00000000000..de42b5b801c --- /dev/null +++ b/drivers/scsi/libsas/sas_ata.c @@ -0,0 +1,339 @@ +/* + * Support for SATA devices on Serial Attached SCSI (SAS) controllers + * + * Copyright (C) 2006 IBM Corporation + * + * Written by: Darrick J. Wong , IBM Corporation + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + * USA + */ + +#include +#include "sas_internal.h" +#include +#include +#include +#include +#include +#include +#include "../scsi_sas_internal.h" + +static enum ata_completion_errors sas_to_ata_err(struct task_status_struct *ts) +{ + /* Cheesy attempt to translate SAS errors into ATA. Hah! */ + + /* transport error */ + if (ts->resp == SAS_TASK_UNDELIVERED) + return AC_ERR_ATA_BUS; + + /* ts->resp == SAS_TASK_COMPLETE */ + /* task delivered, what happened afterwards? */ + switch (ts->stat) { + case SAS_DEV_NO_RESPONSE: + return AC_ERR_TIMEOUT; + + case SAS_INTERRUPTED: + case SAS_PHY_DOWN: + case SAS_NAK_R_ERR: + return AC_ERR_ATA_BUS; + + + case SAS_DATA_UNDERRUN: + /* + * Some programs that use the taskfile interface + * (smartctl in particular) can cause underrun + * problems. Ignore these errors, perhaps at our + * peril. + */ + return 0; + + case SAS_DATA_OVERRUN: + case SAS_QUEUE_FULL: + case SAS_DEVICE_UNKNOWN: + case SAS_SG_ERR: + return AC_ERR_INVALID; + + case SAM_CHECK_COND: + case SAS_OPEN_TO: + case SAS_OPEN_REJECT: + SAS_DPRINTK("%s: Saw error %d. What to do?\n", + __FUNCTION__, ts->stat); + return AC_ERR_OTHER; + + case SAS_ABORTED_TASK: + return AC_ERR_DEV; + + case SAS_PROTO_RESPONSE: + /* This means the ending_fis has the error + * value; return 0 here to collect it */ + return 0; + default: + return 0; + } +} + +static void sas_ata_task_done(struct sas_task *task) +{ + struct ata_queued_cmd *qc = task->uldd_task; + struct domain_device *dev = qc->ap->private_data; + struct task_status_struct *stat = &task->task_status; + struct ata_task_resp *resp = (struct ata_task_resp *)stat->buf; + enum ata_completion_errors ac; + + if (stat->stat == SAS_PROTO_RESPONSE) { + ata_tf_from_fis(resp->ending_fis, &dev->sata_dev.tf); + qc->err_mask |= ac_err_mask(dev->sata_dev.tf.command); + dev->sata_dev.sstatus = resp->sstatus; + dev->sata_dev.serror = resp->serror; + dev->sata_dev.scontrol = resp->scontrol; + dev->sata_dev.ap->sactive = resp->sactive; + } else if (stat->stat != SAM_STAT_GOOD) { + ac = sas_to_ata_err(stat); + if (ac) { + SAS_DPRINTK("%s: SAS error %x\n", __FUNCTION__, + stat->stat); + /* We saw a SAS error. Send a vague error. */ + qc->err_mask = ac; + dev->sata_dev.tf.feature = 0x04; /* status err */ + dev->sata_dev.tf.command = ATA_ERR; + } + } + + ata_qc_complete(qc); + list_del_init(&task->list); + sas_free_task(task); +} + +static unsigned int sas_ata_qc_issue(struct ata_queued_cmd *qc) +{ + int res = -ENOMEM; + struct sas_task *task; + struct domain_device *dev = qc->ap->private_data; + struct sas_ha_struct *sas_ha = dev->port->ha; + struct Scsi_Host *host = sas_ha->core.shost; + struct sas_internal *i = to_sas_internal(host->transportt); + struct scatterlist *sg; + unsigned int num = 0; + unsigned int xfer = 0; + + task = sas_alloc_task(GFP_ATOMIC); + if (!task) + goto out; + task->dev = dev; + task->task_proto = SAS_PROTOCOL_STP; + task->task_done = sas_ata_task_done; + + if (qc->tf.command == ATA_CMD_FPDMA_WRITE || + qc->tf.command == ATA_CMD_FPDMA_READ) { + /* Need to zero out the tag libata assigned us */ + qc->tf.nsect = 0; + } + + ata_tf_to_fis(&qc->tf, (u8*)&task->ata_task.fis, 0); + task->uldd_task = qc; + if (is_atapi_taskfile(&qc->tf)) { + memcpy(task->ata_task.atapi_packet, qc->cdb, qc->dev->cdb_len); + task->total_xfer_len = qc->nbytes + qc->pad_len; + task->num_scatter = qc->pad_len ? qc->n_elem + 1 : qc->n_elem; + } else { + ata_for_each_sg(sg, qc) { + num++; + xfer += sg->length; + } + + task->total_xfer_len = xfer; + task->num_scatter = num; + } + + task->data_dir = qc->dma_dir; + task->scatter = qc->__sg; + task->ata_task.retry_count = 1; + task->task_state_flags = SAS_TASK_STATE_PENDING; + + switch (qc->tf.protocol) { + case ATA_PROT_NCQ: + task->ata_task.use_ncq = 1; + /* fall through */ + case ATA_PROT_ATAPI_DMA: + case ATA_PROT_DMA: + task->ata_task.dma_xfer = 1; + break; + } + + if (sas_ha->lldd_max_execute_num < 2) + res = i->dft->lldd_execute_task(task, 1, GFP_ATOMIC); + else + res = sas_queue_up(task); + + /* Examine */ + if (res) { + SAS_DPRINTK("lldd_execute_task returned: %d\n", res); + + sas_free_task(task); + if (res == -SAS_QUEUE_FULL) + return -ENOMEM; + } + +out: + return res; +} + +static u8 sas_ata_check_status(struct ata_port *ap) +{ + struct domain_device *dev = ap->private_data; + return dev->sata_dev.tf.command; +} + +static void sas_ata_phy_reset(struct ata_port *ap) +{ + struct domain_device *dev = ap->private_data; + struct sas_internal *i = + to_sas_internal(dev->port->ha->core.shost->transportt); + int res = 0; + + if (i->dft->lldd_I_T_nexus_reset) + res = i->dft->lldd_I_T_nexus_reset(dev); + + if (res) + SAS_DPRINTK("%s: Unable to reset I T nexus?\n", __FUNCTION__); + + switch (dev->sata_dev.command_set) { + case ATA_COMMAND_SET: + SAS_DPRINTK("%s: Found ATA device.\n", __FUNCTION__); + ap->device[0].class = ATA_DEV_ATA; + break; + case ATAPI_COMMAND_SET: + SAS_DPRINTK("%s: Found ATAPI device.\n", __FUNCTION__); + ap->device[0].class = ATA_DEV_ATAPI; + break; + default: + SAS_DPRINTK("%s: Unknown SATA command set: %d.\n", + __FUNCTION__, + dev->sata_dev.command_set); + ap->device[0].class = ATA_DEV_ATA; + break; + } + + ap->cbl = ATA_CBL_SATA; +} + +static void sas_ata_post_internal(struct ata_queued_cmd *qc) +{ + if (qc->flags & ATA_QCFLAG_FAILED) + qc->err_mask |= AC_ERR_OTHER; + + if (qc->err_mask) + SAS_DPRINTK("%s: Failure; reset phy!\n", __FUNCTION__); +} + +static void sas_ata_tf_read(struct ata_port *ap, struct ata_taskfile *tf) +{ + struct domain_device *dev = ap->private_data; + memcpy(tf, &dev->sata_dev.tf, sizeof (*tf)); +} + +static void sas_ata_scr_write(struct ata_port *ap, unsigned int sc_reg_in, + u32 val) +{ + struct domain_device *dev = ap->private_data; + + SAS_DPRINTK("STUB %s\n", __FUNCTION__); + switch (sc_reg_in) { + case SCR_STATUS: + dev->sata_dev.sstatus = val; + break; + case SCR_CONTROL: + dev->sata_dev.scontrol = val; + break; + case SCR_ERROR: + dev->sata_dev.serror = val; + break; + case SCR_ACTIVE: + dev->sata_dev.ap->sactive = val; + break; + } +} + +static u32 sas_ata_scr_read(struct ata_port *ap, unsigned int sc_reg_in) +{ + struct domain_device *dev = ap->private_data; + + SAS_DPRINTK("STUB %s\n", __FUNCTION__); + switch (sc_reg_in) { + case SCR_STATUS: + return dev->sata_dev.sstatus; + case SCR_CONTROL: + return dev->sata_dev.scontrol; + case SCR_ERROR: + return dev->sata_dev.serror; + case SCR_ACTIVE: + return dev->sata_dev.ap->sactive; + default: + return 0xffffffffU; + } +} + +static struct ata_port_operations sas_sata_ops = { + .port_disable = ata_port_disable, + .check_status = sas_ata_check_status, + .check_altstatus = sas_ata_check_status, + .dev_select = ata_noop_dev_select, + .phy_reset = sas_ata_phy_reset, + .post_internal_cmd = sas_ata_post_internal, + .tf_read = sas_ata_tf_read, + .qc_prep = ata_noop_qc_prep, + .qc_issue = sas_ata_qc_issue, + .port_start = ata_sas_port_start, + .port_stop = ata_sas_port_stop, + .scr_read = sas_ata_scr_read, + .scr_write = sas_ata_scr_write +}; + +static struct ata_port_info sata_port_info = { + .flags = ATA_FLAG_SATA | ATA_FLAG_NO_LEGACY | ATA_FLAG_SATA_RESET | + ATA_FLAG_MMIO | ATA_FLAG_PIO_DMA | ATA_FLAG_NCQ, + .pio_mask = 0x1f, /* PIO0-4 */ + .mwdma_mask = 0x07, /* MWDMA0-2 */ + .udma_mask = ATA_UDMA6, + .port_ops = &sas_sata_ops +}; + +int sas_ata_init_host_and_port(struct domain_device *found_dev, + struct scsi_target *starget) +{ + struct Scsi_Host *shost = dev_to_shost(&starget->dev); + struct sas_ha_struct *ha = SHOST_TO_SAS_HA(shost); + struct ata_port *ap; + + ata_host_init(&found_dev->sata_dev.ata_host, + &ha->pcidev->dev, + sata_port_info.flags, + &sas_sata_ops); + ap = ata_sas_port_alloc(&found_dev->sata_dev.ata_host, + &sata_port_info, + shost); + if (!ap) { + SAS_DPRINTK("ata_sas_port_alloc failed.\n"); + return -ENODEV; + } + + ap->private_data = found_dev; + ap->cbl = ATA_CBL_SATA; + ap->scsi_host = shost; + found_dev->sata_dev.ap = ap; + + return 0; +} diff --git a/drivers/scsi/libsas/sas_scsi_host.c b/drivers/scsi/libsas/sas_scsi_host.c index 0dc7c02b383..dbc2a912114 100644 --- a/drivers/scsi/libsas/sas_scsi_host.c +++ b/drivers/scsi/libsas/sas_scsi_host.c @@ -34,6 +34,7 @@ #include #include #include +#include #include "../scsi_sas_internal.h" #include "../scsi_transport_api.h" #include "../scsi_priv.h" @@ -173,7 +174,7 @@ static struct sas_task *sas_create_task(struct scsi_cmnd *cmd, return task; } -static int sas_queue_up(struct sas_task *task) +int sas_queue_up(struct sas_task *task) { struct sas_ha_struct *sas_ha = task->dev->port->ha; struct scsi_core *core = &sas_ha->core; @@ -193,11 +194,6 @@ static int sas_queue_up(struct sas_task *task) return 0; } -static inline int dev_is_sata(struct domain_device *dev) -{ - return (dev->rphy->identify.target_port_protocols & SAS_PROTOCOL_SATA); -} - /** * sas_queuecommand -- Enqueue a command for processing * @parameters: See SCSI Core documentation @@ -696,93 +692,6 @@ enum scsi_eh_timer_return sas_scsi_timed_out(struct scsi_cmnd *cmd) return EH_NOT_HANDLED; } - -static enum ata_completion_errors sas_to_ata_err(struct task_status_struct *ts) -{ - /* Cheesy attempt to translate SAS errors into ATA. Hah! */ - - /* transport error */ - if (ts->resp == SAS_TASK_UNDELIVERED) - return AC_ERR_ATA_BUS; - - /* ts->resp == SAS_TASK_COMPLETE */ - /* task delivered, what happened afterwards? */ - switch (ts->stat) { - case SAS_DEV_NO_RESPONSE: - return AC_ERR_TIMEOUT; - - case SAS_INTERRUPTED: - case SAS_PHY_DOWN: - case SAS_NAK_R_ERR: - return AC_ERR_ATA_BUS; - - - case SAS_DATA_UNDERRUN: - /* - * Some programs that use the taskfile interface - * (smartctl in particular) can cause underrun - * problems. Ignore these errors, perhaps at our - * peril. - */ - return 0; - - case SAS_DATA_OVERRUN: - case SAS_QUEUE_FULL: - case SAS_DEVICE_UNKNOWN: - case SAS_SG_ERR: - return AC_ERR_INVALID; - - case SAM_CHECK_COND: - case SAS_OPEN_TO: - case SAS_OPEN_REJECT: - SAS_DPRINTK("%s: Saw error %d. What to do?\n", - __FUNCTION__, ts->stat); - return AC_ERR_OTHER; - - case SAS_ABORTED_TASK: - return AC_ERR_DEV; - - case SAS_PROTO_RESPONSE: - /* This means the ending_fis has the error - * value; return 0 here to collect it */ - return 0; - default: - return 0; - } -} - -static void sas_ata_task_done(struct sas_task *task) -{ - struct ata_queued_cmd *qc = task->uldd_task; - struct domain_device *dev = qc->ap->private_data; - struct task_status_struct *stat = &task->task_status; - struct ata_task_resp *resp = (struct ata_task_resp *)stat->buf; - enum ata_completion_errors ac; - - if (stat->stat == SAS_PROTO_RESPONSE) { - ata_tf_from_fis(resp->ending_fis, &dev->sata_dev.tf); - qc->err_mask |= ac_err_mask(dev->sata_dev.tf.command); - dev->sata_dev.sstatus = resp->sstatus; - dev->sata_dev.serror = resp->serror; - dev->sata_dev.scontrol = resp->scontrol; - dev->sata_dev.ap->sactive = resp->sactive; - } else if (stat->stat != SAM_STAT_GOOD) { - ac = sas_to_ata_err(stat); - if (ac) { - SAS_DPRINTK("%s: SAS error %x\n", __FUNCTION__, - stat->stat); - /* We saw a SAS error. Send a vague error. */ - qc->err_mask = ac; - dev->sata_dev.tf.feature = 0x04; /* status err */ - dev->sata_dev.tf.command = ATA_ERR; - } - } - - ata_qc_complete(qc); - list_del_init(&task->list); - sas_free_task(task); -} - int sas_ioctl(struct scsi_device *sdev, int cmd, void __user *arg) { struct domain_device *dev = sdev_to_domain_dev(sdev); @@ -793,200 +702,6 @@ int sas_ioctl(struct scsi_device *sdev, int cmd, void __user *arg) return -EINVAL; } -static unsigned int sas_ata_qc_issue(struct ata_queued_cmd *qc) -{ - int res = -ENOMEM; - struct sas_task *task; - struct domain_device *dev = qc->ap->private_data; - struct sas_ha_struct *sas_ha = dev->port->ha; - struct Scsi_Host *host = sas_ha->core.shost; - struct sas_internal *i = to_sas_internal(host->transportt); - struct scatterlist *sg; - unsigned int num = 0; - unsigned int xfer = 0; - - task = sas_alloc_task(GFP_ATOMIC); - if (!task) - goto out; - task->dev = dev; - task->task_proto = SAS_PROTOCOL_STP; - task->task_done = sas_ata_task_done; - - if (qc->tf.command == ATA_CMD_FPDMA_WRITE || - qc->tf.command == ATA_CMD_FPDMA_READ) { - /* Need to zero out the tag libata assigned us */ - qc->tf.nsect = 0; - } - - ata_tf_to_fis(&qc->tf, (u8*)&task->ata_task.fis, 0); - task->uldd_task = qc; - if (is_atapi_taskfile(&qc->tf)) { - memcpy(task->ata_task.atapi_packet, qc->cdb, qc->dev->cdb_len); - task->total_xfer_len = qc->nbytes + qc->pad_len; - task->num_scatter = qc->pad_len ? qc->n_elem + 1 : qc->n_elem; - } else { - ata_for_each_sg(sg, qc) { - num++; - xfer += sg->length; - } - - task->total_xfer_len = xfer; - task->num_scatter = num; - } - - task->data_dir = qc->dma_dir; - task->scatter = qc->__sg; - task->ata_task.retry_count = 1; - task->task_state_flags = SAS_TASK_STATE_PENDING; - - switch (qc->tf.protocol) { - case ATA_PROT_NCQ: - task->ata_task.use_ncq = 1; - /* fall through */ - case ATA_PROT_ATAPI_DMA: - case ATA_PROT_DMA: - task->ata_task.dma_xfer = 1; - break; - } - - if (sas_ha->lldd_max_execute_num < 2) - res = i->dft->lldd_execute_task(task, 1, GFP_ATOMIC); - else - res = sas_queue_up(task); - - /* Examine */ - if (res) { - SAS_DPRINTK("lldd_execute_task returned: %d\n", res); - - sas_free_task(task); - if (res == -SAS_QUEUE_FULL) - return -ENOMEM; - } - -out: - return res; -} - -static u8 sas_ata_check_status(struct ata_port *ap) -{ - struct domain_device *dev = ap->private_data; - return dev->sata_dev.tf.command; -} - -static void sas_ata_phy_reset(struct ata_port *ap) -{ - struct domain_device *dev = ap->private_data; - struct sas_internal *i = - to_sas_internal(dev->port->ha->core.shost->transportt); - int res = 0; - - if (i->dft->lldd_I_T_nexus_reset) - res = i->dft->lldd_I_T_nexus_reset(dev); - - if (res) - SAS_DPRINTK("%s: Unable to reset I T nexus?\n", __FUNCTION__); - - switch (dev->sata_dev.command_set) { - case ATA_COMMAND_SET: - SAS_DPRINTK("%s: Found ATA device.\n", __FUNCTION__); - ap->device[0].class = ATA_DEV_ATA; - break; - case ATAPI_COMMAND_SET: - SAS_DPRINTK("%s: Found ATAPI device.\n", __FUNCTION__); - ap->device[0].class = ATA_DEV_ATAPI; - break; - default: - SAS_DPRINTK("%s: Unknown SATA command set: %d.\n", - __FUNCTION__, - dev->sata_dev.command_set); - ap->device[0].class = ATA_DEV_ATA; - break; - } - - ap->cbl = ATA_CBL_SATA; -} - -static void sas_ata_post_internal(struct ata_queued_cmd *qc) -{ - if (qc->flags & ATA_QCFLAG_FAILED) - qc->err_mask |= AC_ERR_OTHER; - - if (qc->err_mask) - SAS_DPRINTK("%s: Failure; reset phy!\n", __FUNCTION__); -} - -static void sas_ata_tf_read(struct ata_port *ap, struct ata_taskfile *tf) -{ - struct domain_device *dev = ap->private_data; - memcpy(tf, &dev->sata_dev.tf, sizeof (*tf)); -} - -static void sas_ata_scr_write(struct ata_port *ap, unsigned int sc_reg_in, - u32 val) -{ - struct domain_device *dev = ap->private_data; - - SAS_DPRINTK("STUB %s\n", __FUNCTION__); - switch (sc_reg_in) { - case SCR_STATUS: - dev->sata_dev.sstatus = val; - break; - case SCR_CONTROL: - dev->sata_dev.scontrol = val; - break; - case SCR_ERROR: - dev->sata_dev.serror = val; - break; - case SCR_ACTIVE: - dev->sata_dev.ap->sactive = val; - break; - } -} - -static u32 sas_ata_scr_read(struct ata_port *ap, unsigned int sc_reg_in) -{ - struct domain_device *dev = ap->private_data; - - SAS_DPRINTK("STUB %s\n", __FUNCTION__); - switch (sc_reg_in) { - case SCR_STATUS: - return dev->sata_dev.sstatus; - case SCR_CONTROL: - return dev->sata_dev.scontrol; - case SCR_ERROR: - return dev->sata_dev.serror; - case SCR_ACTIVE: - return dev->sata_dev.ap->sactive; - default: - return 0xffffffffU; - } -} - -static struct ata_port_operations sas_sata_ops = { - .port_disable = ata_port_disable, - .check_status = sas_ata_check_status, - .check_altstatus = sas_ata_check_status, - .dev_select = ata_noop_dev_select, - .phy_reset = sas_ata_phy_reset, - .post_internal_cmd = sas_ata_post_internal, - .tf_read = sas_ata_tf_read, - .qc_prep = ata_noop_qc_prep, - .qc_issue = sas_ata_qc_issue, - .port_start = ata_sas_port_start, - .port_stop = ata_sas_port_stop, - .scr_read = sas_ata_scr_read, - .scr_write = sas_ata_scr_write -}; - -static struct ata_port_info sata_port_info = { - .flags = ATA_FLAG_SATA | ATA_FLAG_NO_LEGACY | ATA_FLAG_SATA_RESET | - ATA_FLAG_MMIO | ATA_FLAG_PIO_DMA | ATA_FLAG_NCQ, - .pio_mask = 0x1f, /* PIO0-4 */ - .mwdma_mask = 0x07, /* MWDMA0-2 */ - .udma_mask = ATA_UDMA6, - .port_ops = &sas_sata_ops -}; - struct domain_device *sas_find_dev_by_rphy(struct sas_rphy *rphy) { struct Scsi_Host *shost = dev_to_shost(rphy->dev.parent); @@ -1025,32 +740,16 @@ static inline struct domain_device *sas_find_target(struct scsi_target *starget) int sas_target_alloc(struct scsi_target *starget) { - struct Scsi_Host *shost = dev_to_shost(&starget->dev); - struct sas_ha_struct *ha = SHOST_TO_SAS_HA(shost); struct domain_device *found_dev = sas_find_target(starget); + int res; if (!found_dev) return -ENODEV; if (dev_is_sata(found_dev)) { - struct ata_port *ap; - - ata_host_init(&found_dev->sata_dev.ata_host, - &ha->pcidev->dev, - sata_port_info.flags, - &sas_sata_ops); - ap = ata_sas_port_alloc(&found_dev->sata_dev.ata_host, - &sata_port_info, - shost); - if (!ap) { - SAS_DPRINTK("ata_sas_port_alloc failed.\n"); - return -ENODEV; - } - - ap->private_data = found_dev; - ap->cbl = ATA_CBL_SATA; - ap->scsi_host = shost; - found_dev->sata_dev.ap = ap; + res = sas_ata_init_host_and_port(found_dev, starget); + if (res) + return res; } starget->hostdata = found_dev; -- cgit v1.2.3 From 3eb7a51a3ae0ed0227e051ecf75199fccbb4cc73 Mon Sep 17 00:00:00 2001 From: "Darrick J. Wong" Date: Tue, 30 Jan 2007 01:18:35 -0800 Subject: [SCSI] sas_ata: Satisfy libata qc function locking requirements ata_qc_complete and ata_sas_queuecmd require that the port lock be held when they are called. sas_ata doesn't do this, leading to BUG messages about qc tags newly allocated qc tags already being in use. This patch fixes the locking, which should clean up the rest of those messages. So far I've tested this against an IBM x206m with two SATA disks with no BUG messages and no other signs of things going wrong, and the machine finally passed the pounder stress test. Signed-off-by: Darrick J. Wong Signed-off-by: James Bottomley --- drivers/scsi/libsas/sas_ata.c | 4 ++++ drivers/scsi/libsas/sas_scsi_host.c | 4 ++++ 2 files changed, 8 insertions(+) (limited to 'drivers/scsi/libsas') diff --git a/drivers/scsi/libsas/sas_ata.c b/drivers/scsi/libsas/sas_ata.c index de42b5b801c..0bb1a149a9c 100644 --- a/drivers/scsi/libsas/sas_ata.c +++ b/drivers/scsi/libsas/sas_ata.c @@ -92,7 +92,9 @@ static void sas_ata_task_done(struct sas_task *task) struct task_status_struct *stat = &task->task_status; struct ata_task_resp *resp = (struct ata_task_resp *)stat->buf; enum ata_completion_errors ac; + unsigned long flags; + spin_lock_irqsave(dev->sata_dev.ap->lock, flags); if (stat->stat == SAS_PROTO_RESPONSE) { ata_tf_from_fis(resp->ending_fis, &dev->sata_dev.tf); qc->err_mask |= ac_err_mask(dev->sata_dev.tf.command); @@ -113,6 +115,8 @@ static void sas_ata_task_done(struct sas_task *task) } ata_qc_complete(qc); + spin_unlock_irqrestore(dev->sata_dev.ap->lock, flags); + list_del_init(&task->list); sas_free_task(task); } diff --git a/drivers/scsi/libsas/sas_scsi_host.c b/drivers/scsi/libsas/sas_scsi_host.c index dbc2a912114..ba5c91b8131 100644 --- a/drivers/scsi/libsas/sas_scsi_host.c +++ b/drivers/scsi/libsas/sas_scsi_host.c @@ -216,8 +216,12 @@ int sas_queuecommand(struct scsi_cmnd *cmd, struct sas_task *task; if (dev_is_sata(dev)) { + unsigned long flags; + + spin_lock_irqsave(dev->sata_dev.ap->lock, flags); res = ata_sas_queuecmd(cmd, scsi_done, dev->sata_dev.ap); + spin_unlock_irqrestore(dev->sata_dev.ap->lock, flags); goto out; } -- cgit v1.2.3 From 35a7f2f698d309cc50d98e56312dd907427b7ba4 Mon Sep 17 00:00:00 2001 From: "Darrick J. Wong" Date: Tue, 30 Jan 2007 01:18:38 -0800 Subject: [SCSI] sas_ata: sas_ata_qc_issue should return AC_ERR_* The sas_ata_qc_issue function was incorrectly written to return error codes such as -ENOMEM. Since libata OR's qc->err_mask with the return value, It is necessary to make my code return one of the AC_ERR_ codes instead. For now, use AC_ERR_SYSTEM because an error here means that the OS couldn't send the command to the controller. If anybody has a suggestion for a better AC_ERR_ code to use, please suggest it. Signed-off-by: Darrick J. Wong Signed-off-by: James Bottomley --- drivers/scsi/libsas/sas_ata.c | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) (limited to 'drivers/scsi/libsas') diff --git a/drivers/scsi/libsas/sas_ata.c b/drivers/scsi/libsas/sas_ata.c index 0bb1a149a9c..46e1dbe1b84 100644 --- a/drivers/scsi/libsas/sas_ata.c +++ b/drivers/scsi/libsas/sas_ata.c @@ -123,7 +123,7 @@ static void sas_ata_task_done(struct sas_task *task) static unsigned int sas_ata_qc_issue(struct ata_queued_cmd *qc) { - int res = -ENOMEM; + int res; struct sas_task *task; struct domain_device *dev = qc->ap->private_data; struct sas_ha_struct *sas_ha = dev->port->ha; @@ -135,7 +135,7 @@ static unsigned int sas_ata_qc_issue(struct ata_queued_cmd *qc) task = sas_alloc_task(GFP_ATOMIC); if (!task) - goto out; + return AC_ERR_SYSTEM; task->dev = dev; task->task_proto = SAS_PROTOCOL_STP; task->task_done = sas_ata_task_done; @@ -187,12 +187,10 @@ static unsigned int sas_ata_qc_issue(struct ata_queued_cmd *qc) SAS_DPRINTK("lldd_execute_task returned: %d\n", res); sas_free_task(task); - if (res == -SAS_QUEUE_FULL) - return -ENOMEM; + return AC_ERR_SYSTEM; } -out: - return res; + return 0; } static u8 sas_ata_check_status(struct ata_port *ap) -- cgit v1.2.3 From 1c50dc83f9ca752b1e1b985f1ce33d2695103ffa Mon Sep 17 00:00:00 2001 From: "Darrick J. Wong" Date: Tue, 30 Jan 2007 01:18:41 -0800 Subject: [SCSI] sas_ata: ata_post_internal should abort the sas_task This patch adds a new field, lldd_task, to ata_queued_cmd so that libata users such as libsas can associate some data with a qc. The particular ambition with this patch is to associate a sas_task with a qc; that way, if libata decides to timeout a command, we can come back (in sas_ata_post_internal) and abort the sas task. One question remains: Is it necessary to reset the phy on error, or will the libata error handler take care of it? (Assuming that one is written, of course.) This patch, as it is today, works well enough to clean things up when an ATA device probe attempt fails halfway through the probe, though I'm not sure this is always the right thing to do. Signed-off-by: Darrick J. Wong Signed-off-by: James Bottomley --- drivers/scsi/libsas/sas_ata.c | 30 +++++++++++++++++++++++++++--- 1 file changed, 27 insertions(+), 3 deletions(-) (limited to 'drivers/scsi/libsas') diff --git a/drivers/scsi/libsas/sas_ata.c b/drivers/scsi/libsas/sas_ata.c index 46e1dbe1b84..c8af884abe1 100644 --- a/drivers/scsi/libsas/sas_ata.c +++ b/drivers/scsi/libsas/sas_ata.c @@ -88,12 +88,17 @@ static enum ata_completion_errors sas_to_ata_err(struct task_status_struct *ts) static void sas_ata_task_done(struct sas_task *task) { struct ata_queued_cmd *qc = task->uldd_task; - struct domain_device *dev = qc->ap->private_data; + struct domain_device *dev; struct task_status_struct *stat = &task->task_status; struct ata_task_resp *resp = (struct ata_task_resp *)stat->buf; enum ata_completion_errors ac; unsigned long flags; + if (!qc) + goto qc_already_gone; + + dev = qc->ap->private_data; + spin_lock_irqsave(dev->sata_dev.ap->lock, flags); if (stat->stat == SAS_PROTO_RESPONSE) { ata_tf_from_fis(resp->ending_fis, &dev->sata_dev.tf); @@ -114,9 +119,11 @@ static void sas_ata_task_done(struct sas_task *task) } } + qc->lldd_task = NULL; ata_qc_complete(qc); spin_unlock_irqrestore(dev->sata_dev.ap->lock, flags); +qc_already_gone: list_del_init(&task->list); sas_free_task(task); } @@ -166,6 +173,7 @@ static unsigned int sas_ata_qc_issue(struct ata_queued_cmd *qc) task->scatter = qc->__sg; task->ata_task.retry_count = 1; task->task_state_flags = SAS_TASK_STATE_PENDING; + qc->lldd_task = task; switch (qc->tf.protocol) { case ATA_PROT_NCQ: @@ -237,8 +245,24 @@ static void sas_ata_post_internal(struct ata_queued_cmd *qc) if (qc->flags & ATA_QCFLAG_FAILED) qc->err_mask |= AC_ERR_OTHER; - if (qc->err_mask) - SAS_DPRINTK("%s: Failure; reset phy!\n", __FUNCTION__); + if (qc->err_mask) { + /* + * Find the sas_task and kill it. By this point, + * libata has decided to kill the qc, so we needn't + * bother with sas_ata_task_done. But we still + * ought to abort the task. + */ + struct sas_task *task = qc->lldd_task; + struct domain_device *dev = qc->ap->private_data; + + qc->lldd_task = NULL; + if (task) { + task->uldd_task = NULL; + __sas_task_abort(task); + } + + sas_phy_reset(dev->port->phy, 1); + } } static void sas_ata_tf_read(struct ata_port *ap, struct ata_taskfile *tf) -- cgit v1.2.3 From 38691593cda9674f41d8708eaa73b0b7e14e95c3 Mon Sep 17 00:00:00 2001 From: "Darrick J. Wong" Date: Tue, 30 Jan 2007 01:18:44 -0800 Subject: [SCSI] sas_ata: Don't copy aic94xx's sactive to ata_port Since the aic94xx sequencer assigns its own NCQ tags to ATA commands, it no longer makes any sense to copy the sactive field in the STP response to ata_port->sactive, as that will confuse libata. Also, libata seems to be capable of managing sactive on its own. The attached patch gets rid of one of the causes of the BUG messages in ata_qc_new, and seems to work without problems on an IBM x206m. Signed-off-by: Darrick J. Wong Signed-off-by: James Bottomley --- drivers/scsi/libsas/sas_ata.c | 1 - 1 file changed, 1 deletion(-) (limited to 'drivers/scsi/libsas') diff --git a/drivers/scsi/libsas/sas_ata.c b/drivers/scsi/libsas/sas_ata.c index c8af884abe1..16c3e5a581a 100644 --- a/drivers/scsi/libsas/sas_ata.c +++ b/drivers/scsi/libsas/sas_ata.c @@ -106,7 +106,6 @@ static void sas_ata_task_done(struct sas_task *task) dev->sata_dev.sstatus = resp->sstatus; dev->sata_dev.serror = resp->serror; dev->sata_dev.scontrol = resp->scontrol; - dev->sata_dev.ap->sactive = resp->sactive; } else if (stat->stat != SAM_STAT_GOOD) { ac = sas_to_ata_err(stat); if (ac) { -- cgit v1.2.3 From d97db63f8dd22e7b669982e47db0c5e3f569a6b5 Mon Sep 17 00:00:00 2001 From: "Darrick J. Wong" Date: Tue, 30 Jan 2007 01:18:49 -0800 Subject: [SCSI] libsas: Accept SAM_GOOD for ATAPI devices in sas_ata_task_done A sas_task sent to an ATAPI devices returns SAM_GOOD if successful. Therefore, we should treat this the same way we treat ATA commands that succeed. Signed-off-by: Darrick J. Wong Signed-off-by: James Bottomley --- drivers/scsi/libsas/sas_ata.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/scsi/libsas') diff --git a/drivers/scsi/libsas/sas_ata.c b/drivers/scsi/libsas/sas_ata.c index 16c3e5a581a..004b463a1c4 100644 --- a/drivers/scsi/libsas/sas_ata.c +++ b/drivers/scsi/libsas/sas_ata.c @@ -100,7 +100,7 @@ static void sas_ata_task_done(struct sas_task *task) dev = qc->ap->private_data; spin_lock_irqsave(dev->sata_dev.ap->lock, flags); - if (stat->stat == SAS_PROTO_RESPONSE) { + if (stat->stat == SAS_PROTO_RESPONSE || stat->stat == SAM_GOOD) { ata_tf_from_fis(resp->ending_fis, &dev->sata_dev.tf); qc->err_mask |= ac_err_mask(dev->sata_dev.tf.command); dev->sata_dev.sstatus = resp->sstatus; -- cgit v1.2.3 From 5986c3d305f497d3ff33d65e4d9ff6d00121407b Mon Sep 17 00:00:00 2001 From: "Darrick J. Wong" Date: Tue, 30 Jan 2007 01:18:52 -0800 Subject: [SCSI] libsas: Unknown STP devices should be reported to libata as unknown. When libsas encounters a STP device whose protocol isn't recognized (i.e. not ATA or ATAPI), we should set the ata_device's class to ATA_DEV_UNKNOWN instead of ATA_DEV_ATA. Signed-off-by: Darrick J. Wong Signed-off-by: James Bottomley --- drivers/scsi/libsas/sas_ata.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/scsi/libsas') diff --git a/drivers/scsi/libsas/sas_ata.c b/drivers/scsi/libsas/sas_ata.c index 004b463a1c4..e164f58f385 100644 --- a/drivers/scsi/libsas/sas_ata.c +++ b/drivers/scsi/libsas/sas_ata.c @@ -232,7 +232,7 @@ static void sas_ata_phy_reset(struct ata_port *ap) SAS_DPRINTK("%s: Unknown SATA command set: %d.\n", __FUNCTION__, dev->sata_dev.command_set); - ap->device[0].class = ATA_DEV_ATA; + ap->device[0].class = ATA_DEV_UNKNOWN; break; } -- cgit v1.2.3 From fe059f122fb9d1bd3a629d4215a4dde11df66f98 Mon Sep 17 00:00:00 2001 From: "Darrick J. Wong" Date: Tue, 30 Jan 2007 01:18:55 -0800 Subject: [SCSI] sas_ata: Assign sas_task to scsi_cmnd to enable EH for ATA devices The SATL should connect the scsi_cmnd to the sas_task (despite the presence of libata) so that requests to abort scsi_cmnds headed to the ATA device can be processed by the EH and aborted correctly. The abort status should still be propagated from sas -> ata -> scsi. Signed-off-by: Darrick J. Wong Signed-off-by: James Bottomley --- drivers/scsi/libsas/sas_ata.c | 7 +++++++ 1 file changed, 7 insertions(+) (limited to 'drivers/scsi/libsas') diff --git a/drivers/scsi/libsas/sas_ata.c b/drivers/scsi/libsas/sas_ata.c index e164f58f385..b6535b073bf 100644 --- a/drivers/scsi/libsas/sas_ata.c +++ b/drivers/scsi/libsas/sas_ata.c @@ -119,6 +119,8 @@ static void sas_ata_task_done(struct sas_task *task) } qc->lldd_task = NULL; + if (qc->scsicmd) + ASSIGN_SAS_TASK(qc->scsicmd, NULL); ata_qc_complete(qc); spin_unlock_irqrestore(dev->sata_dev.ap->lock, flags); @@ -184,6 +186,9 @@ static unsigned int sas_ata_qc_issue(struct ata_queued_cmd *qc) break; } + if (qc->scsicmd) + ASSIGN_SAS_TASK(qc->scsicmd, task); + if (sas_ha->lldd_max_execute_num < 2) res = i->dft->lldd_execute_task(task, 1, GFP_ATOMIC); else @@ -193,6 +198,8 @@ static unsigned int sas_ata_qc_issue(struct ata_queued_cmd *qc) if (res) { SAS_DPRINTK("lldd_execute_task returned: %d\n", res); + if (qc->scsicmd) + ASSIGN_SAS_TASK(qc->scsicmd, NULL); sas_free_task(task); return AC_ERR_SYSTEM; } -- cgit v1.2.3 From 3a2755af37b317d47fdc3dd15178adaf5d47263e Mon Sep 17 00:00:00 2001 From: "Darrick J. Wong" Date: Tue, 30 Jan 2007 01:18:58 -0800 Subject: [SCSI] sas_ata: Implement sas_task_abort for ATA devices ATA devices need special handling for sas_task_abort. If the ATA command came from SCSI, then we merely need to tell SCSI to abort the scsi_cmnd. However, internal commands require a bit more work--we need to fill the qc with the appropriate error status and complete the command, and eventually post_internal will issue the actual ABORT TASK. Signed-off-by: James Bottomley --- drivers/scsi/libsas/sas_ata.c | 47 ++++++++++++++++++++++++++++++++++--- drivers/scsi/libsas/sas_internal.h | 3 +++ drivers/scsi/libsas/sas_scsi_host.c | 8 ++++--- 3 files changed, 52 insertions(+), 6 deletions(-) (limited to 'drivers/scsi/libsas') diff --git a/drivers/scsi/libsas/sas_ata.c b/drivers/scsi/libsas/sas_ata.c index b6535b073bf..2db25899675 100644 --- a/drivers/scsi/libsas/sas_ata.c +++ b/drivers/scsi/libsas/sas_ata.c @@ -30,6 +30,8 @@ #include #include #include "../scsi_sas_internal.h" +#include "../scsi_transport_api.h" +#include static enum ata_completion_errors sas_to_ata_err(struct task_status_struct *ts) { @@ -91,6 +93,7 @@ static void sas_ata_task_done(struct sas_task *task) struct domain_device *dev; struct task_status_struct *stat = &task->task_status; struct ata_task_resp *resp = (struct ata_task_resp *)stat->buf; + struct sas_ha_struct *sas_ha; enum ata_completion_errors ac; unsigned long flags; @@ -98,6 +101,7 @@ static void sas_ata_task_done(struct sas_task *task) goto qc_already_gone; dev = qc->ap->private_data; + sas_ha = dev->port->ha; spin_lock_irqsave(dev->sata_dev.ap->lock, flags); if (stat->stat == SAS_PROTO_RESPONSE || stat->stat == SAM_GOOD) { @@ -124,6 +128,20 @@ static void sas_ata_task_done(struct sas_task *task) ata_qc_complete(qc); spin_unlock_irqrestore(dev->sata_dev.ap->lock, flags); + /* + * If the sas_task has an ata qc, a scsi_cmnd and the aborted + * flag is set, then we must have come in via the libsas EH + * functions. When we exit this function, we need to put the + * scsi_cmnd on the list of finished errors. The ata_qc_complete + * call cleans up the libata side of things but we're protected + * from the scsi_cmnd going away because the scsi_cmnd is owned + * by the EH, making libata's call to scsi_done a NOP. + */ + spin_lock_irqsave(&task->task_state_lock, flags); + if (qc->scsicmd && task->task_state_flags & SAS_TASK_STATE_ABORTED) + scsi_eh_finish_cmd(qc->scsicmd, &sas_ha->eh_done_q); + spin_unlock_irqrestore(&task->task_state_lock, flags); + qc_already_gone: list_del_init(&task->list); sas_free_task(task); @@ -259,15 +277,18 @@ static void sas_ata_post_internal(struct ata_queued_cmd *qc) * ought to abort the task. */ struct sas_task *task = qc->lldd_task; - struct domain_device *dev = qc->ap->private_data; + unsigned long flags; qc->lldd_task = NULL; if (task) { + /* Should this be a AT(API) device reset? */ + spin_lock_irqsave(&task->task_state_lock, flags); + task->task_state_flags |= SAS_TASK_NEED_DEV_RESET; + spin_unlock_irqrestore(&task->task_state_lock, flags); + task->uldd_task = NULL; __sas_task_abort(task); } - - sas_phy_reset(dev->port->phy, 1); } } @@ -369,3 +390,23 @@ int sas_ata_init_host_and_port(struct domain_device *found_dev, return 0; } + +void sas_ata_task_abort(struct sas_task *task) +{ + struct ata_queued_cmd *qc = task->uldd_task; + struct completion *waiting; + + /* Bounce SCSI-initiated commands to the SCSI EH */ + if (qc->scsicmd) { + scsi_req_abort_cmd(qc->scsicmd); + scsi_schedule_eh(qc->scsicmd->device->host); + return; + } + + /* Internal command, fake a timeout and complete. */ + qc->flags &= ~ATA_QCFLAG_ACTIVE; + qc->flags |= ATA_QCFLAG_FAILED; + qc->err_mask |= AC_ERR_TIMEOUT; + waiting = qc->private_data; + complete(waiting); +} diff --git a/drivers/scsi/libsas/sas_internal.h b/drivers/scsi/libsas/sas_internal.h index a78638df201..2b8213b1832 100644 --- a/drivers/scsi/libsas/sas_internal.h +++ b/drivers/scsi/libsas/sas_internal.h @@ -39,6 +39,9 @@ #define SAS_DPRINTK(fmt, ...) #endif +#define TO_SAS_TASK(_scsi_cmd) ((void *)(_scsi_cmd)->host_scribble) +#define ASSIGN_SAS_TASK(_sc, _t) do { (_sc)->host_scribble = (void *) _t; } while (0) + void sas_scsi_recover_host(struct Scsi_Host *shost); int sas_show_class(enum sas_class class, char *buf); diff --git a/drivers/scsi/libsas/sas_scsi_host.c b/drivers/scsi/libsas/sas_scsi_host.c index ba5c91b8131..7663841eb4c 100644 --- a/drivers/scsi/libsas/sas_scsi_host.c +++ b/drivers/scsi/libsas/sas_scsi_host.c @@ -47,9 +47,6 @@ /* ---------- SCSI Host glue ---------- */ -#define TO_SAS_TASK(_scsi_cmd) ((void *)(_scsi_cmd)->host_scribble) -#define ASSIGN_SAS_TASK(_sc, _t) do { (_sc)->host_scribble = (void *) _t; } while (0) - static void sas_scsi_task_done(struct sas_task *task) { struct task_status_struct *ts = &task->task_status; @@ -1018,6 +1015,11 @@ void sas_task_abort(struct sas_task *task) return; } + if (dev_is_sata(task->dev)) { + sas_ata_task_abort(task); + return; + } + scsi_req_abort_cmd(sc); scsi_schedule_eh(sc->device->host); } -- cgit v1.2.3 From 0f05df8b3b41bc258bdf520b72e8cf7c524048b7 Mon Sep 17 00:00:00 2001 From: James Bottomley Date: Mon, 16 Jul 2007 13:41:04 -0500 Subject: [SCSI] libsas, aic94xx: fix dma mapping cockups with ATA This one was noticed by Gilbert Wu of Adaptec: The libata core actually does the DMA mapping for you, so there has to be an exception in the device drivers that *don't* do dma mapping for ATA commands. However, since we've already done this, libsas must now dma map any ATA commands that it wishes to issue ... and yes, this is a horrible mess. Additionally, the test in aic94xx for ATA protocols isn't quite right. Signed-off-by: James Bottomley --- drivers/scsi/libsas/sas_discover.c | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) (limited to 'drivers/scsi/libsas') diff --git a/drivers/scsi/libsas/sas_discover.c b/drivers/scsi/libsas/sas_discover.c index a18c0f6d666..4d768db1b56 100644 --- a/drivers/scsi/libsas/sas_discover.c +++ b/drivers/scsi/libsas/sas_discover.c @@ -110,6 +110,13 @@ static int sas_execute_task(struct sas_task *task, void *buffer, int size, task->total_xfer_len = size; task->data_dir = pci_dma_dir; task->task_done = sas_disc_task_done; + if (pci_dma_dir != PCI_DMA_NONE && + sas_protocol_ata(task->task_proto)) { + task->num_scatter = pci_map_sg(task->dev->port->ha->pcidev, + task->scatter, + task->num_scatter, + task->data_dir); + } for (retries = 0; retries < 5; retries++) { task->task_state_flags = SAS_TASK_STATE_PENDING; @@ -192,8 +199,13 @@ static int sas_execute_task(struct sas_task *task, void *buffer, int size, } } ex_err: - if (pci_dma_dir != PCI_DMA_NONE) + if (pci_dma_dir != PCI_DMA_NONE) { + if (sas_protocol_ata(task->task_proto)) + pci_unmap_sg(task->dev->port->ha->pcidev, + task->scatter, task->num_scatter, + task->data_dir); kfree(scatter); + } out: return res; } -- cgit v1.2.3 From 9d720d82dc295521d70939c3f5edd54050730f09 Mon Sep 17 00:00:00 2001 From: James Bottomley Date: Mon, 16 Jul 2007 13:15:51 -0500 Subject: [SCSI] libsas: fix lockdep issue with ATA lockdep noticed that with ATA support the port->dev_list_lock was entangled at irq context, so it now needs to become IRQ safe Signed-off-by: James Bottomley --- drivers/scsi/libsas/sas_discover.c | 8 ++++---- drivers/scsi/libsas/sas_expander.c | 12 ++++++------ 2 files changed, 10 insertions(+), 10 deletions(-) (limited to 'drivers/scsi/libsas') diff --git a/drivers/scsi/libsas/sas_discover.c b/drivers/scsi/libsas/sas_discover.c index 4d768db1b56..328a78ad6aa 100644 --- a/drivers/scsi/libsas/sas_discover.c +++ b/drivers/scsi/libsas/sas_discover.c @@ -304,9 +304,9 @@ static int sas_get_port_device(struct asd_sas_port *port) port->disc.max_level = 0; dev->rphy = rphy; - spin_lock(&port->dev_list_lock); + spin_lock_irq(&port->dev_list_lock); list_add_tail(&dev->dev_list_node, &port->dev_list); - spin_unlock(&port->dev_list_lock); + spin_unlock_irq(&port->dev_list_lock); return 0; } @@ -703,9 +703,9 @@ static void sas_discover_domain(struct work_struct *work) sas_rphy_free(dev->rphy); dev->rphy = NULL; - spin_lock(&port->dev_list_lock); + spin_lock_irq(&port->dev_list_lock); list_del_init(&dev->dev_list_node); - spin_unlock(&port->dev_list_lock); + spin_unlock_irq(&port->dev_list_lock); 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 d05fc23b4d5..969fd3eb494 100644 --- a/drivers/scsi/libsas/sas_expander.c +++ b/drivers/scsi/libsas/sas_expander.c @@ -677,9 +677,9 @@ static struct domain_device *sas_ex_discover_end_dev( child->rphy = rphy; - spin_lock(&parent->port->dev_list_lock); + spin_lock_irq(&parent->port->dev_list_lock); list_add_tail(&child->dev_list_node, &parent->port->dev_list); - spin_unlock(&parent->port->dev_list_lock); + spin_unlock_irq(&parent->port->dev_list_lock); res = sas_discover_sata(child); if (res) { @@ -701,9 +701,9 @@ static struct domain_device *sas_ex_discover_end_dev( child->rphy = rphy; sas_fill_in_rphy(child, rphy); - spin_lock(&parent->port->dev_list_lock); + spin_lock_irq(&parent->port->dev_list_lock); list_add_tail(&child->dev_list_node, &parent->port->dev_list); - spin_unlock(&parent->port->dev_list_lock); + spin_unlock_irq(&parent->port->dev_list_lock); res = sas_discover_end_dev(child); if (res) { @@ -816,9 +816,9 @@ static struct domain_device *sas_ex_discover_expander( sas_fill_in_rphy(child, rphy); sas_rphy_add(rphy); - spin_lock(&parent->port->dev_list_lock); + spin_lock_irq(&parent->port->dev_list_lock); list_add_tail(&child->dev_list_node, &parent->port->dev_list); - spin_unlock(&parent->port->dev_list_lock); + spin_unlock_irq(&parent->port->dev_list_lock); res = sas_discover_expander(child); if (res) { -- cgit v1.2.3 From ba1fc175cc6c0af7e78241e50160344f0f198282 Mon Sep 17 00:00:00 2001 From: FUJITA Tomonori Date: Mon, 9 Jul 2007 12:52:08 +0900 Subject: [SCSI] libsas: add SAS management protocol handler This patch adds support for SAS Management Protocol (SMP) passthrough support via bsg. aic94xx can use this. Signed-off-by: FUJITA Tomonori Signed-off-by: James Bottomley --- drivers/scsi/libsas/sas_expander.c | 48 ++++++++++++++++++++++++++++++++++++++ drivers/scsi/libsas/sas_init.c | 1 + 2 files changed, 49 insertions(+) (limited to 'drivers/scsi/libsas') diff --git a/drivers/scsi/libsas/sas_expander.c b/drivers/scsi/libsas/sas_expander.c index 969fd3eb494..a81195354b9 100644 --- a/drivers/scsi/libsas/sas_expander.c +++ b/drivers/scsi/libsas/sas_expander.c @@ -23,6 +23,7 @@ */ #include +#include #include "sas_internal.h" @@ -1972,3 +1973,50 @@ out: return res; } #endif + +int sas_smp_handler(struct Scsi_Host *shost, struct sas_rphy *rphy, + struct request *req) +{ + struct domain_device *dev; + int ret, type = rphy->identify.device_type; + struct request *rsp = req->next_rq; + + if (!rsp) { + printk("%s: space for a smp response is missing\n", + __FUNCTION__); + return -EINVAL; + } + + /* seems aic94xx doesn't support */ + if (!rphy) { + printk("%s: can we send a smp request to a host?\n", + __FUNCTION__); + return -EINVAL; + } + + if (type != SAS_EDGE_EXPANDER_DEVICE && + type != SAS_FANOUT_EXPANDER_DEVICE) { + printk("%s: can we send a smp request to a device?\n", + __FUNCTION__); + return -EINVAL; + } + + dev = sas_find_dev_by_rphy(rphy); + if (!dev) { + printk("%s: fail to find a domain_device?\n", __FUNCTION__); + return -EINVAL; + } + + /* do we need to support multiple segments? */ + if (req->bio->bi_vcnt > 1 || rsp->bio->bi_vcnt > 1) { + printk("%s: multiple segments req %u %u, rsp %u %u\n", + __FUNCTION__, req->bio->bi_vcnt, req->data_len, + rsp->bio->bi_vcnt, rsp->data_len); + return -EINVAL; + } + + ret = smp_execute_task(dev, bio_data(req->bio), req->data_len, + bio_data(rsp->bio), rsp->data_len); + + return ret; +} diff --git a/drivers/scsi/libsas/sas_init.c b/drivers/scsi/libsas/sas_init.c index 965698c8b7b..98360272f40 100644 --- a/drivers/scsi/libsas/sas_init.c +++ b/drivers/scsi/libsas/sas_init.c @@ -259,6 +259,7 @@ static struct sas_function_template sft = { .phy_reset = sas_phy_reset, .set_phy_speed = sas_set_phy_speed, .get_linkerrors = sas_get_linkerrors, + .smp_handler = sas_smp_handler, }; struct scsi_transport_template * -- cgit v1.2.3 From 08547354c17a50a54906b7936d6ecb05ea39bedd Mon Sep 17 00:00:00 2001 From: FUJITA Tomonori Date: Sat, 7 Jul 2007 18:11:35 +0900 Subject: [SCSI] libsas: kill unused smp_portal code Signed-off-by: FUJITA Tomonori Signed-off-by: James Bottomley --- drivers/scsi/libsas/sas_expander.c | 106 ------------------------------------- 1 file changed, 106 deletions(-) (limited to 'drivers/scsi/libsas') diff --git a/drivers/scsi/libsas/sas_expander.c b/drivers/scsi/libsas/sas_expander.c index a81195354b9..eca83e8d8c0 100644 --- a/drivers/scsi/libsas/sas_expander.c +++ b/drivers/scsi/libsas/sas_expander.c @@ -37,14 +37,6 @@ static int sas_configure_phy(struct domain_device *dev, int phy_id, u8 *sas_addr, int include); static int sas_disable_routing(struct domain_device *dev, u8 *sas_addr); -#if 0 -/* FIXME: smp needs to migrate into the sas class */ -static ssize_t smp_portal_read(struct kobject *, struct bin_attribute *, - char *, loff_t, size_t); -static ssize_t smp_portal_write(struct kobject *, struct bin_attribute *, - char *, loff_t, size_t); -#endif - /* ---------- SMP task management ---------- */ static void smp_task_timedout(unsigned long _task) @@ -1415,30 +1407,6 @@ static int sas_disable_routing(struct domain_device *dev, u8 *sas_addr) return 0; } -#if 0 -#define SMP_BIN_ATTR_NAME "smp_portal" - -static void sas_ex_smp_hook(struct domain_device *dev) -{ - struct expander_device *ex_dev = &dev->ex_dev; - struct bin_attribute *bin_attr = &ex_dev->smp_bin_attr; - - memset(bin_attr, 0, sizeof(*bin_attr)); - - bin_attr->attr.name = SMP_BIN_ATTR_NAME; - bin_attr->attr.mode = 0600; - - bin_attr->size = 0; - bin_attr->private = NULL; - bin_attr->read = smp_portal_read; - bin_attr->write= smp_portal_write; - bin_attr->mmap = NULL; - - ex_dev->smp_portal_pid = -1; - init_MUTEX(&ex_dev->smp_sema); -} -#endif - /** * sas_discover_expander -- expander discovery * @ex: pointer to expander domain device @@ -1900,80 +1868,6 @@ out: return res; } -#if 0 -/* ---------- SMP portal ---------- */ - -static ssize_t smp_portal_write(struct kobject *kobj, - struct bin_attribute *bin_attr, - char *buf, loff_t offs, size_t size) -{ - struct domain_device *dev = to_dom_device(kobj); - struct expander_device *ex = &dev->ex_dev; - - if (offs != 0) - return -EFBIG; - else if (size == 0) - return 0; - - down_interruptible(&ex->smp_sema); - if (ex->smp_req) - kfree(ex->smp_req); - ex->smp_req = kzalloc(size, GFP_USER); - if (!ex->smp_req) { - up(&ex->smp_sema); - return -ENOMEM; - } - memcpy(ex->smp_req, buf, size); - ex->smp_req_size = size; - ex->smp_portal_pid = current->pid; - up(&ex->smp_sema); - - return size; -} - -static ssize_t smp_portal_read(struct kobject *kobj, - struct bin_attribute *bin_attr, - char *buf, loff_t offs, size_t size) -{ - struct domain_device *dev = to_dom_device(kobj); - struct expander_device *ex = &dev->ex_dev; - u8 *smp_resp; - int res = -EINVAL; - - /* XXX: sysfs gives us an offset of 0x10 or 0x8 while in fact - * it should be 0. - */ - - down_interruptible(&ex->smp_sema); - if (!ex->smp_req || ex->smp_portal_pid != current->pid) - goto out; - - res = 0; - if (size == 0) - goto out; - - res = -ENOMEM; - smp_resp = alloc_smp_resp(size); - if (!smp_resp) - goto out; - res = smp_execute_task(dev, ex->smp_req, ex->smp_req_size, - smp_resp, size); - if (!res) { - memcpy(buf, smp_resp, size); - res = size; - } - - kfree(smp_resp); -out: - kfree(ex->smp_req); - ex->smp_req = NULL; - ex->smp_req_size = 0; - ex->smp_portal_pid = -1; - up(&ex->smp_sema); - return res; -} -#endif - int sas_smp_handler(struct Scsi_Host *shost, struct sas_rphy *rphy, struct request *req) { -- cgit v1.2.3 From 110dd8f19df534b5e464bd1d8f491195a7e62a26 Mon Sep 17 00:00:00 2001 From: James Bottomley Date: Fri, 20 Jul 2007 13:11:44 -0500 Subject: [SCSI] libsas: fix scr_read/write users and update the libata documentation This fixes up the usage in libsas (which are easy to miss, since they're only in the scsi-misc tree) ... and also corrects the documentation on the point of what these two function pointers actually return. Signed-off-by: James Bottomley --- drivers/scsi/libsas/sas_ata.c | 24 ++++++++++++++++-------- 1 file changed, 16 insertions(+), 8 deletions(-) (limited to 'drivers/scsi/libsas') diff --git a/drivers/scsi/libsas/sas_ata.c b/drivers/scsi/libsas/sas_ata.c index 2db25899675..359391f5735 100644 --- a/drivers/scsi/libsas/sas_ata.c +++ b/drivers/scsi/libsas/sas_ata.c @@ -172,7 +172,7 @@ static unsigned int sas_ata_qc_issue(struct ata_queued_cmd *qc) qc->tf.nsect = 0; } - ata_tf_to_fis(&qc->tf, (u8*)&task->ata_task.fis, 0); + ata_tf_to_fis(&qc->tf, 1, 0, (u8*)&task->ata_task.fis); task->uldd_task = qc; if (is_atapi_taskfile(&qc->tf)) { memcpy(task->ata_task.atapi_packet, qc->cdb, qc->dev->cdb_len); @@ -298,7 +298,7 @@ static void sas_ata_tf_read(struct ata_port *ap, struct ata_taskfile *tf) memcpy(tf, &dev->sata_dev.tf, sizeof (*tf)); } -static void sas_ata_scr_write(struct ata_port *ap, unsigned int sc_reg_in, +static int sas_ata_scr_write(struct ata_port *ap, unsigned int sc_reg_in, u32 val) { struct domain_device *dev = ap->private_data; @@ -317,25 +317,33 @@ static void sas_ata_scr_write(struct ata_port *ap, unsigned int sc_reg_in, case SCR_ACTIVE: dev->sata_dev.ap->sactive = val; break; + default: + return -EINVAL; } + return 0; } -static u32 sas_ata_scr_read(struct ata_port *ap, unsigned int sc_reg_in) +static int sas_ata_scr_read(struct ata_port *ap, unsigned int sc_reg_in, + u32 *val) { struct domain_device *dev = ap->private_data; SAS_DPRINTK("STUB %s\n", __FUNCTION__); switch (sc_reg_in) { case SCR_STATUS: - return dev->sata_dev.sstatus; + *val = dev->sata_dev.sstatus; + return 0; case SCR_CONTROL: - return dev->sata_dev.scontrol; + *val = dev->sata_dev.scontrol; + return 0; case SCR_ERROR: - return dev->sata_dev.serror; + *val = dev->sata_dev.serror; + return 0; case SCR_ACTIVE: - return dev->sata_dev.ap->sactive; + *val = dev->sata_dev.ap->sactive; + return 0; default: - return 0xffffffffU; + return -EINVAL; } } -- cgit v1.2.3 From b91421749a1840148d8c81637c03c0ace3f35269 Mon Sep 17 00:00:00 2001 From: James Bottomley Date: Sun, 22 Jul 2007 13:15:55 -0500 Subject: [SCSI] libsas: make ATA functions selectable by a config option Not everyone wants libsas automatically to pull in libata. This patch makes the behaviour configurable, so you can build libsas with or without ATA support. Signed-off-by: James Bottomley --- drivers/scsi/libsas/Kconfig | 7 + drivers/scsi/libsas/Makefile | 4 +- drivers/scsi/libsas/sas_ata.c | 397 +++++++++++++++++++++++++++++++++++++ drivers/scsi/libsas/sas_discover.c | 397 +------------------------------------ drivers/scsi/libsas/sas_expander.c | 9 +- 5 files changed, 417 insertions(+), 397 deletions(-) (limited to 'drivers/scsi/libsas') diff --git a/drivers/scsi/libsas/Kconfig b/drivers/scsi/libsas/Kconfig index aafdc92f831..3a3c1ac9c6c 100644 --- a/drivers/scsi/libsas/Kconfig +++ b/drivers/scsi/libsas/Kconfig @@ -30,6 +30,13 @@ config SCSI_SAS_LIBSAS This provides transport specific helpers for SAS drivers which use the domain device construct (like the aic94xxx). +config SCSI_SAS_ATA + bool "ATA support for libsas (requires libata)" + depends on SCSI_SAS_LIBSAS && ATA + help + Builds in ATA support into libsas. Will necessitate + the loading of libata along with libsas. + config SCSI_SAS_LIBSAS_DEBUG bool "Compile the SAS Domain Transport Attributes in debug mode" default y diff --git a/drivers/scsi/libsas/Makefile b/drivers/scsi/libsas/Makefile index 6383eb58d89..fd387b91856 100644 --- a/drivers/scsi/libsas/Makefile +++ b/drivers/scsi/libsas/Makefile @@ -33,5 +33,5 @@ libsas-y += sas_init.o \ sas_dump.o \ sas_discover.o \ sas_expander.o \ - sas_scsi_host.o \ - sas_ata.o + sas_scsi_host.o +libsas-$(CONFIG_SCSI_SAS_ATA) += sas_ata.o diff --git a/drivers/scsi/libsas/sas_ata.c b/drivers/scsi/libsas/sas_ata.c index 359391f5735..ced2de32c51 100644 --- a/drivers/scsi/libsas/sas_ata.c +++ b/drivers/scsi/libsas/sas_ata.c @@ -21,6 +21,8 @@ * USA */ +#include + #include #include "sas_internal.h" #include @@ -418,3 +420,398 @@ void sas_ata_task_abort(struct sas_task *task) waiting = qc->private_data; complete(waiting); } + +static void sas_task_timedout(unsigned long _task) +{ + struct sas_task *task = (void *) _task; + unsigned long flags; + + 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); + + complete(&task->completion); +} + +static void sas_disc_task_done(struct sas_task *task) +{ + if (!del_timer(&task->timer)) + return; + complete(&task->completion); +} + +#define SAS_DEV_TIMEOUT 10 + +/** + * sas_execute_task -- Basic task processing for discovery + * @task: the task to be executed + * @buffer: pointer to buffer to do I/O + * @size: size of @buffer + * @pci_dma_dir: PCI_DMA_... + */ +static int sas_execute_task(struct sas_task *task, void *buffer, int size, + int pci_dma_dir) +{ + int res = 0; + struct scatterlist *scatter = NULL; + struct task_status_struct *ts = &task->task_status; + int num_scatter = 0; + int retries = 0; + struct sas_internal *i = + to_sas_internal(task->dev->port->ha->core.shost->transportt); + + if (pci_dma_dir != PCI_DMA_NONE) { + scatter = kzalloc(sizeof(*scatter), GFP_KERNEL); + if (!scatter) + goto out; + + sg_init_one(scatter, buffer, size); + num_scatter = 1; + } + + task->task_proto = task->dev->tproto; + task->scatter = scatter; + task->num_scatter = num_scatter; + task->total_xfer_len = size; + task->data_dir = pci_dma_dir; + task->task_done = sas_disc_task_done; + if (pci_dma_dir != PCI_DMA_NONE && + sas_protocol_ata(task->task_proto)) { + task->num_scatter = pci_map_sg(task->dev->port->ha->pcidev, + task->scatter, + task->num_scatter, + task->data_dir); + } + + for (retries = 0; retries < 5; retries++) { + task->task_state_flags = SAS_TASK_STATE_PENDING; + init_completion(&task->completion); + + task->timer.data = (unsigned long) task; + task->timer.function = sas_task_timedout; + task->timer.expires = jiffies + SAS_DEV_TIMEOUT*HZ; + add_timer(&task->timer); + + res = i->dft->lldd_execute_task(task, 1, GFP_KERNEL); + if (res) { + del_timer(&task->timer); + SAS_DPRINTK("executing SAS discovery task failed:%d\n", + res); + goto ex_err; + } + wait_for_completion(&task->completion); + res = -ETASK; + if (task->task_state_flags & SAS_TASK_STATE_ABORTED) { + int res2; + SAS_DPRINTK("task aborted, flags:0x%x\n", + task->task_state_flags); + res2 = i->dft->lldd_abort_task(task); + SAS_DPRINTK("came back from abort task\n"); + if (!(task->task_state_flags & SAS_TASK_STATE_DONE)) { + if (res2 == TMF_RESP_FUNC_COMPLETE) + continue; /* Retry the task */ + else + goto ex_err; + } + } + if (task->task_status.stat == SAM_BUSY || + task->task_status.stat == SAM_TASK_SET_FULL || + task->task_status.stat == SAS_QUEUE_FULL) { + SAS_DPRINTK("task: q busy, sleeping...\n"); + schedule_timeout_interruptible(HZ); + } else if (task->task_status.stat == SAM_CHECK_COND) { + struct scsi_sense_hdr shdr; + + if (!scsi_normalize_sense(ts->buf, ts->buf_valid_size, + &shdr)) { + SAS_DPRINTK("couldn't normalize sense\n"); + continue; + } + if ((shdr.sense_key == 6 && shdr.asc == 0x29) || + (shdr.sense_key == 2 && shdr.asc == 4 && + shdr.ascq == 1)) { + SAS_DPRINTK("device %016llx LUN: %016llx " + "powering up or not ready yet, " + "sleeping...\n", + SAS_ADDR(task->dev->sas_addr), + SAS_ADDR(task->ssp_task.LUN)); + + schedule_timeout_interruptible(5*HZ); + } else if (shdr.sense_key == 1) { + res = 0; + break; + } else if (shdr.sense_key == 5) { + break; + } else { + SAS_DPRINTK("dev %016llx LUN: %016llx " + "sense key:0x%x ASC:0x%x ASCQ:0x%x" + "\n", + SAS_ADDR(task->dev->sas_addr), + SAS_ADDR(task->ssp_task.LUN), + shdr.sense_key, + shdr.asc, shdr.ascq); + } + } else if (task->task_status.resp != SAS_TASK_COMPLETE || + task->task_status.stat != SAM_GOOD) { + SAS_DPRINTK("task finished with resp:0x%x, " + "stat:0x%x\n", + task->task_status.resp, + task->task_status.stat); + goto ex_err; + } else { + res = 0; + break; + } + } +ex_err: + if (pci_dma_dir != PCI_DMA_NONE) { + if (sas_protocol_ata(task->task_proto)) + pci_unmap_sg(task->dev->port->ha->pcidev, + task->scatter, task->num_scatter, + task->data_dir); + kfree(scatter); + } +out: + return res; +} + +/* ---------- SATA ---------- */ + +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 ((fis->sector_count == 1 && /* ATA */ + fis->lbal == 1 && + fis->lbam == 0 && + fis->lbah == 0 && + fis->device == 0) + || + (fis->sector_count == 0 && /* CE-ATA (mATA) */ + fis->lbal == 0 && + fis->lbam == 0xCE && + fis->lbah == 0xAA && + (fis->device & ~0x10) == 0)) + + dev->sata_dev.command_set = ATA_COMMAND_SET; + + else if ((fis->interrupt_reason == 1 && /* ATAPI */ + fis->lbal == 1 && + fis->byte_count_low == 0x14 && + fis->byte_count_high == 0xEB && + (fis->device & ~0x10) == 0)) + + dev->sata_dev.command_set = ATAPI_COMMAND_SET; + + else if ((fis->sector_count == 1 && /* SEMB */ + fis->lbal == 1 && + fis->lbam == 0x3C && + fis->lbah == 0xC3 && + fis->device == 0) + || + (fis->interrupt_reason == 1 && /* SATA PM */ + fis->lbal == 1 && + fis->byte_count_low == 0x69 && + fis->byte_count_high == 0x96 && + (fis->device & ~0x10) == 0)) + + /* Treat it as a superset? */ + dev->sata_dev.command_set = ATAPI_COMMAND_SET; +} + +/** + * sas_issue_ata_cmd -- Basic SATA command processing for discovery + * @dev: the device to send the command to + * @command: the command register + * @features: the features register + * @buffer: pointer to buffer to do I/O + * @size: size of @buffer + * @pci_dma_dir: PCI_DMA_... + */ +static int sas_issue_ata_cmd(struct domain_device *dev, u8 command, + u8 features, void *buffer, int size, + int pci_dma_dir) +{ + int res = 0; + struct sas_task *task; + struct dev_to_host_fis *d2h_fis = (struct dev_to_host_fis *) + &dev->frame_rcvd[0]; + + res = -ENOMEM; + task = sas_alloc_task(GFP_KERNEL); + if (!task) + goto out; + + task->dev = dev; + + task->ata_task.fis.fis_type = 0x27; + task->ata_task.fis.command = command; + task->ata_task.fis.features = features; + task->ata_task.fis.device = d2h_fis->device; + task->ata_task.retry_count = 1; + + res = sas_execute_task(task, buffer, size, pci_dma_dir); + + sas_free_task(task); +out: + return res; +} + +static void sas_sata_propagate_sas_addr(struct domain_device *dev) +{ + unsigned long flags; + struct asd_sas_port *port = dev->port; + struct asd_sas_phy *phy; + + BUG_ON(dev->parent); + + memcpy(port->attached_sas_addr, dev->sas_addr, SAS_ADDR_SIZE); + spin_lock_irqsave(&port->phy_list_lock, flags); + list_for_each_entry(phy, &port->phy_list, port_phy_el) + memcpy(phy->attached_sas_addr, dev->sas_addr, SAS_ADDR_SIZE); + spin_unlock_irqrestore(&port->phy_list_lock, flags); +} + +#define ATA_IDENTIFY_DEV 0xEC +#define ATA_IDENTIFY_PACKET_DEV 0xA1 +#define ATA_SET_FEATURES 0xEF +#define ATA_FEATURE_PUP_STBY_SPIN_UP 0x07 + +/** + * sas_discover_sata_dev -- discover a STP/SATA device (SATA_DEV) + * @dev: STP/SATA device of interest (ATA/ATAPI) + * + * The LLDD has already been notified of this device, so that we can + * send FISes to it. Here we try to get IDENTIFY DEVICE or IDENTIFY + * PACKET DEVICE, if ATAPI device, so that the LLDD can fine-tune its + * performance for this device. + */ +static int sas_discover_sata_dev(struct domain_device *dev) +{ + int res; + __le16 *identify_x; + u8 command; + + identify_x = kzalloc(512, GFP_KERNEL); + if (!identify_x) + return -ENOMEM; + + if (dev->sata_dev.command_set == ATA_COMMAND_SET) { + dev->sata_dev.identify_device = identify_x; + command = ATA_IDENTIFY_DEV; + } else { + dev->sata_dev.identify_packet_device = identify_x; + command = ATA_IDENTIFY_PACKET_DEV; + } + + res = sas_issue_ata_cmd(dev, command, 0, identify_x, 512, + PCI_DMA_FROMDEVICE); + if (res) + goto out_err; + + /* lives on the media? */ + if (le16_to_cpu(identify_x[0]) & 4) { + /* incomplete response */ + SAS_DPRINTK("sending SET FEATURE/PUP_STBY_SPIN_UP to " + "dev %llx\n", SAS_ADDR(dev->sas_addr)); + if (!le16_to_cpu(identify_x[83] & (1<<6))) + goto cont1; + res = sas_issue_ata_cmd(dev, ATA_SET_FEATURES, + ATA_FEATURE_PUP_STBY_SPIN_UP, + NULL, 0, PCI_DMA_NONE); + if (res) + goto cont1; + + schedule_timeout_interruptible(5*HZ); /* More time? */ + res = sas_issue_ata_cmd(dev, command, 0, identify_x, 512, + PCI_DMA_FROMDEVICE); + if (res) + goto out_err; + } +cont1: + /* Get WWN */ + if (dev->port->oob_mode != SATA_OOB_MODE) { + memcpy(dev->sas_addr, dev->sata_dev.rps_resp.rps.stp_sas_addr, + SAS_ADDR_SIZE); + } else if (dev->sata_dev.command_set == ATA_COMMAND_SET && + (le16_to_cpu(dev->sata_dev.identify_device[108]) & 0xF000) + == 0x5000) { + int i; + + for (i = 0; i < 4; i++) { + dev->sas_addr[2*i] = + (le16_to_cpu(dev->sata_dev.identify_device[108+i]) & 0xFF00) >> 8; + dev->sas_addr[2*i+1] = + le16_to_cpu(dev->sata_dev.identify_device[108+i]) & 0x00FF; + } + } + sas_hash_addr(dev->hashed_sas_addr, dev->sas_addr); + if (!dev->parent) + sas_sata_propagate_sas_addr(dev); + + /* XXX Hint: register this SATA device with SATL. + When this returns, dev->sata_dev->lu is alive and + present. + sas_satl_register_dev(dev); + */ + + sas_fill_in_rphy(dev, dev->rphy); + + return 0; +out_err: + dev->sata_dev.identify_packet_device = NULL; + dev->sata_dev.identify_device = NULL; + kfree(identify_x); + return res; +} + +static int sas_discover_sata_pm(struct domain_device *dev) +{ + return -ENODEV; +} + +/** + * sas_discover_sata -- discover an STP/SATA domain device + * @dev: pointer to struct domain_device of interest + * + * First we notify the LLDD of this device, so we can send frames to + * it. Then depending on the type of device we call the appropriate + * discover functions. Once device discover is done, we notify the + * LLDD so that it can fine-tune its parameters for the device, by + * removing it and then adding it. That is, the second time around, + * the driver would have certain fields, that it is looking at, set. + * Finally we initialize the kobj so that the device can be added to + * the system at registration time. Devices directly attached to a HA + * port, have no parents. All other devices do, and should have their + * "parent" pointer set appropriately before calling this function. + */ +int sas_discover_sata(struct domain_device *dev) +{ + int res; + + sas_get_ata_command_set(dev); + + res = sas_notify_lldd_dev_found(dev); + if (res) + return res; + + switch (dev->dev_type) { + case SATA_DEV: + res = sas_discover_sata_dev(dev); + break; + case SATA_PM: + res = sas_discover_sata_pm(dev); + break; + default: + break; + } + sas_notify_lldd_dev_gone(dev); + if (!res) { + sas_notify_lldd_dev_found(dev); + res = sas_rphy_add(dev->rphy); + } + + return res; +} diff --git a/drivers/scsi/libsas/sas_discover.c b/drivers/scsi/libsas/sas_discover.c index 328a78ad6aa..6ac9f61d006 100644 --- a/drivers/scsi/libsas/sas_discover.c +++ b/drivers/scsi/libsas/sas_discover.c @@ -55,161 +55,6 @@ void sas_init_dev(struct domain_device *dev) } } -static void sas_task_timedout(unsigned long _task) -{ - struct sas_task *task = (void *) _task; - unsigned long flags; - - 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); - - complete(&task->completion); -} - -static void sas_disc_task_done(struct sas_task *task) -{ - if (!del_timer(&task->timer)) - return; - complete(&task->completion); -} - -#define SAS_DEV_TIMEOUT 10 - -/** - * sas_execute_task -- Basic task processing for discovery - * @task: the task to be executed - * @buffer: pointer to buffer to do I/O - * @size: size of @buffer - * @pci_dma_dir: PCI_DMA_... - */ -static int sas_execute_task(struct sas_task *task, void *buffer, int size, - int pci_dma_dir) -{ - int res = 0; - struct scatterlist *scatter = NULL; - struct task_status_struct *ts = &task->task_status; - int num_scatter = 0; - int retries = 0; - struct sas_internal *i = - to_sas_internal(task->dev->port->ha->core.shost->transportt); - - if (pci_dma_dir != PCI_DMA_NONE) { - scatter = kzalloc(sizeof(*scatter), GFP_KERNEL); - if (!scatter) - goto out; - - sg_init_one(scatter, buffer, size); - num_scatter = 1; - } - - task->task_proto = task->dev->tproto; - task->scatter = scatter; - task->num_scatter = num_scatter; - task->total_xfer_len = size; - task->data_dir = pci_dma_dir; - task->task_done = sas_disc_task_done; - if (pci_dma_dir != PCI_DMA_NONE && - sas_protocol_ata(task->task_proto)) { - task->num_scatter = pci_map_sg(task->dev->port->ha->pcidev, - task->scatter, - task->num_scatter, - task->data_dir); - } - - for (retries = 0; retries < 5; retries++) { - task->task_state_flags = SAS_TASK_STATE_PENDING; - init_completion(&task->completion); - - task->timer.data = (unsigned long) task; - task->timer.function = sas_task_timedout; - task->timer.expires = jiffies + SAS_DEV_TIMEOUT*HZ; - add_timer(&task->timer); - - res = i->dft->lldd_execute_task(task, 1, GFP_KERNEL); - if (res) { - del_timer(&task->timer); - SAS_DPRINTK("executing SAS discovery task failed:%d\n", - res); - goto ex_err; - } - wait_for_completion(&task->completion); - res = -ETASK; - if (task->task_state_flags & SAS_TASK_STATE_ABORTED) { - int res2; - SAS_DPRINTK("task aborted, flags:0x%x\n", - task->task_state_flags); - res2 = i->dft->lldd_abort_task(task); - SAS_DPRINTK("came back from abort task\n"); - if (!(task->task_state_flags & SAS_TASK_STATE_DONE)) { - if (res2 == TMF_RESP_FUNC_COMPLETE) - continue; /* Retry the task */ - else - goto ex_err; - } - } - if (task->task_status.stat == SAM_BUSY || - task->task_status.stat == SAM_TASK_SET_FULL || - task->task_status.stat == SAS_QUEUE_FULL) { - SAS_DPRINTK("task: q busy, sleeping...\n"); - schedule_timeout_interruptible(HZ); - } else if (task->task_status.stat == SAM_CHECK_COND) { - struct scsi_sense_hdr shdr; - - if (!scsi_normalize_sense(ts->buf, ts->buf_valid_size, - &shdr)) { - SAS_DPRINTK("couldn't normalize sense\n"); - continue; - } - if ((shdr.sense_key == 6 && shdr.asc == 0x29) || - (shdr.sense_key == 2 && shdr.asc == 4 && - shdr.ascq == 1)) { - SAS_DPRINTK("device %016llx LUN: %016llx " - "powering up or not ready yet, " - "sleeping...\n", - SAS_ADDR(task->dev->sas_addr), - SAS_ADDR(task->ssp_task.LUN)); - - schedule_timeout_interruptible(5*HZ); - } else if (shdr.sense_key == 1) { - res = 0; - break; - } else if (shdr.sense_key == 5) { - break; - } else { - SAS_DPRINTK("dev %016llx LUN: %016llx " - "sense key:0x%x ASC:0x%x ASCQ:0x%x" - "\n", - SAS_ADDR(task->dev->sas_addr), - SAS_ADDR(task->ssp_task.LUN), - shdr.sense_key, - shdr.asc, shdr.ascq); - } - } else if (task->task_status.resp != SAS_TASK_COMPLETE || - task->task_status.stat != SAM_GOOD) { - SAS_DPRINTK("task finished with resp:0x%x, " - "stat:0x%x\n", - task->task_status.resp, - task->task_status.stat); - goto ex_err; - } else { - res = 0; - break; - } - } -ex_err: - if (pci_dma_dir != PCI_DMA_NONE) { - if (sas_protocol_ata(task->task_proto)) - pci_unmap_sg(task->dev->port->ha->pcidev, - task->scatter, task->num_scatter, - task->data_dir); - kfree(scatter); - } -out: - return res; -} - /* ---------- Domain device discovery ---------- */ /** @@ -313,202 +158,6 @@ static int sas_get_port_device(struct asd_sas_port *port) /* ---------- Discover and Revalidate ---------- */ -/* ---------- SATA ---------- */ - -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 ((fis->sector_count == 1 && /* ATA */ - fis->lbal == 1 && - fis->lbam == 0 && - fis->lbah == 0 && - fis->device == 0) - || - (fis->sector_count == 0 && /* CE-ATA (mATA) */ - fis->lbal == 0 && - fis->lbam == 0xCE && - fis->lbah == 0xAA && - (fis->device & ~0x10) == 0)) - - dev->sata_dev.command_set = ATA_COMMAND_SET; - - else if ((fis->interrupt_reason == 1 && /* ATAPI */ - fis->lbal == 1 && - fis->byte_count_low == 0x14 && - fis->byte_count_high == 0xEB && - (fis->device & ~0x10) == 0)) - - dev->sata_dev.command_set = ATAPI_COMMAND_SET; - - else if ((fis->sector_count == 1 && /* SEMB */ - fis->lbal == 1 && - fis->lbam == 0x3C && - fis->lbah == 0xC3 && - fis->device == 0) - || - (fis->interrupt_reason == 1 && /* SATA PM */ - fis->lbal == 1 && - fis->byte_count_low == 0x69 && - fis->byte_count_high == 0x96 && - (fis->device & ~0x10) == 0)) - - /* Treat it as a superset? */ - dev->sata_dev.command_set = ATAPI_COMMAND_SET; -} - -/** - * sas_issue_ata_cmd -- Basic SATA command processing for discovery - * @dev: the device to send the command to - * @command: the command register - * @features: the features register - * @buffer: pointer to buffer to do I/O - * @size: size of @buffer - * @pci_dma_dir: PCI_DMA_... - */ -static int sas_issue_ata_cmd(struct domain_device *dev, u8 command, - u8 features, void *buffer, int size, - int pci_dma_dir) -{ - int res = 0; - struct sas_task *task; - struct dev_to_host_fis *d2h_fis = (struct dev_to_host_fis *) - &dev->frame_rcvd[0]; - - res = -ENOMEM; - task = sas_alloc_task(GFP_KERNEL); - if (!task) - goto out; - - task->dev = dev; - - task->ata_task.fis.fis_type = 0x27; - task->ata_task.fis.command = command; - task->ata_task.fis.features = features; - task->ata_task.fis.device = d2h_fis->device; - task->ata_task.retry_count = 1; - - res = sas_execute_task(task, buffer, size, pci_dma_dir); - - sas_free_task(task); -out: - return res; -} - -static void sas_sata_propagate_sas_addr(struct domain_device *dev) -{ - unsigned long flags; - struct asd_sas_port *port = dev->port; - struct asd_sas_phy *phy; - - BUG_ON(dev->parent); - - memcpy(port->attached_sas_addr, dev->sas_addr, SAS_ADDR_SIZE); - spin_lock_irqsave(&port->phy_list_lock, flags); - list_for_each_entry(phy, &port->phy_list, port_phy_el) - memcpy(phy->attached_sas_addr, dev->sas_addr, SAS_ADDR_SIZE); - spin_unlock_irqrestore(&port->phy_list_lock, flags); -} - -#define ATA_IDENTIFY_DEV 0xEC -#define ATA_IDENTIFY_PACKET_DEV 0xA1 -#define ATA_SET_FEATURES 0xEF -#define ATA_FEATURE_PUP_STBY_SPIN_UP 0x07 - -/** - * sas_discover_sata_dev -- discover a STP/SATA device (SATA_DEV) - * @dev: STP/SATA device of interest (ATA/ATAPI) - * - * The LLDD has already been notified of this device, so that we can - * send FISes to it. Here we try to get IDENTIFY DEVICE or IDENTIFY - * PACKET DEVICE, if ATAPI device, so that the LLDD can fine-tune its - * performance for this device. - */ -static int sas_discover_sata_dev(struct domain_device *dev) -{ - int res; - __le16 *identify_x; - u8 command; - - identify_x = kzalloc(512, GFP_KERNEL); - if (!identify_x) - return -ENOMEM; - - if (dev->sata_dev.command_set == ATA_COMMAND_SET) { - dev->sata_dev.identify_device = identify_x; - command = ATA_IDENTIFY_DEV; - } else { - dev->sata_dev.identify_packet_device = identify_x; - command = ATA_IDENTIFY_PACKET_DEV; - } - - res = sas_issue_ata_cmd(dev, command, 0, identify_x, 512, - PCI_DMA_FROMDEVICE); - if (res) - goto out_err; - - /* lives on the media? */ - if (le16_to_cpu(identify_x[0]) & 4) { - /* incomplete response */ - SAS_DPRINTK("sending SET FEATURE/PUP_STBY_SPIN_UP to " - "dev %llx\n", SAS_ADDR(dev->sas_addr)); - if (!le16_to_cpu(identify_x[83] & (1<<6))) - goto cont1; - res = sas_issue_ata_cmd(dev, ATA_SET_FEATURES, - ATA_FEATURE_PUP_STBY_SPIN_UP, - NULL, 0, PCI_DMA_NONE); - if (res) - goto cont1; - - schedule_timeout_interruptible(5*HZ); /* More time? */ - res = sas_issue_ata_cmd(dev, command, 0, identify_x, 512, - PCI_DMA_FROMDEVICE); - if (res) - goto out_err; - } -cont1: - /* Get WWN */ - if (dev->port->oob_mode != SATA_OOB_MODE) { - memcpy(dev->sas_addr, dev->sata_dev.rps_resp.rps.stp_sas_addr, - SAS_ADDR_SIZE); - } else if (dev->sata_dev.command_set == ATA_COMMAND_SET && - (le16_to_cpu(dev->sata_dev.identify_device[108]) & 0xF000) - == 0x5000) { - int i; - - for (i = 0; i < 4; i++) { - dev->sas_addr[2*i] = - (le16_to_cpu(dev->sata_dev.identify_device[108+i]) & 0xFF00) >> 8; - dev->sas_addr[2*i+1] = - le16_to_cpu(dev->sata_dev.identify_device[108+i]) & 0x00FF; - } - } - sas_hash_addr(dev->hashed_sas_addr, dev->sas_addr); - if (!dev->parent) - sas_sata_propagate_sas_addr(dev); - - /* XXX Hint: register this SATA device with SATL. - When this returns, dev->sata_dev->lu is alive and - present. - sas_satl_register_dev(dev); - */ - - sas_fill_in_rphy(dev, dev->rphy); - - return 0; -out_err: - dev->sata_dev.identify_packet_device = NULL; - dev->sata_dev.identify_device = NULL; - kfree(identify_x); - return res; -} - -static int sas_discover_sata_pm(struct domain_device *dev) -{ - return -ENODEV; -} - int sas_notify_lldd_dev_found(struct domain_device *dev) { int res = 0; @@ -541,49 +190,6 @@ void sas_notify_lldd_dev_gone(struct domain_device *dev) /* ---------- Common/dispatchers ---------- */ -/** - * sas_discover_sata -- discover an STP/SATA domain device - * @dev: pointer to struct domain_device of interest - * - * First we notify the LLDD of this device, so we can send frames to - * it. Then depending on the type of device we call the appropriate - * discover functions. Once device discover is done, we notify the - * LLDD so that it can fine-tune its parameters for the device, by - * removing it and then adding it. That is, the second time around, - * the driver would have certain fields, that it is looking at, set. - * Finally we initialize the kobj so that the device can be added to - * the system at registration time. Devices directly attached to a HA - * port, have no parents. All other devices do, and should have their - * "parent" pointer set appropriately before calling this function. - */ -int sas_discover_sata(struct domain_device *dev) -{ - int res; - - sas_get_ata_command_set(dev); - - res = sas_notify_lldd_dev_found(dev); - if (res) - return res; - - switch (dev->dev_type) { - case SATA_DEV: - res = sas_discover_sata_dev(dev); - break; - case SATA_PM: - res = sas_discover_sata_pm(dev); - break; - default: - break; - } - sas_notify_lldd_dev_gone(dev); - if (!res) { - sas_notify_lldd_dev_found(dev); - res = sas_rphy_add(dev->rphy); - } - - return res; -} /** * sas_discover_end_dev -- discover an end device (SSP, etc) @@ -690,11 +296,14 @@ static void sas_discover_domain(struct work_struct *work) case FANOUT_DEV: error = sas_discover_root_expander(dev); break; +#ifdef CONFIG_SCSI_SAS_ATA case SATA_DEV: case SATA_PM: error = sas_discover_sata(dev); break; +#endif default: + error = -ENXIO; SAS_DPRINTK("unhandled device %d\n", dev->dev_type); break; } diff --git a/drivers/scsi/libsas/sas_expander.c b/drivers/scsi/libsas/sas_expander.c index eca83e8d8c0..b500f0c1449 100644 --- a/drivers/scsi/libsas/sas_expander.c +++ b/drivers/scsi/libsas/sas_expander.c @@ -535,6 +535,8 @@ int sas_smp_get_phy_events(struct sas_phy *phy) } +#ifdef CONFIG_SCSI_SAS_ATA + #define RPS_REQ_SIZE 16 #define RPS_RESP_SIZE 60 @@ -578,6 +580,7 @@ static int sas_get_report_phy_sata(struct domain_device *dev, kfree(rps_req); return res; } +#endif static void sas_ex_get_linkrate(struct domain_device *parent, struct domain_device *child, @@ -645,6 +648,7 @@ static struct domain_device *sas_ex_discover_end_dev( } sas_ex_get_linkrate(parent, child, phy); +#ifdef CONFIG_SCSI_SAS_ATA if ((phy->attached_tproto & SAS_PROTO_STP) || phy->attached_sata_dev) { child->dev_type = SATA_DEV; if (phy->attached_tproto & SAS_PROTO_STP) @@ -682,7 +686,9 @@ static struct domain_device *sas_ex_discover_end_dev( SAS_ADDR(parent->sas_addr), phy_id, res); goto out_list_del; } - } else if (phy->attached_tproto & SAS_PROTO_SSP) { + } else +#endif + if (phy->attached_tproto & SAS_PROTO_SSP) { child->dev_type = SAS_END_DEV; rphy = sas_end_device_alloc(phy->port); /* FIXME: error handling */ @@ -710,6 +716,7 @@ static struct domain_device *sas_ex_discover_end_dev( SAS_DPRINTK("target proto 0x%x at %016llx:0x%x not handled\n", phy->attached_tproto, SAS_ADDR(parent->sas_addr), phy_id); + goto out_free; } list_add_tail(&child->siblings, &parent_ex->children); -- cgit v1.2.3