GIT 6289865e3b0e2803e05450ace4d4850981ba1327 git+ssh://master.kernel.org/pub/scm/linux/kernel/git/jejb/aic94xx-sas-2.6.git commit Author: Darrick J. Wong Date: Tue Jan 30 01:18:58 2007 -0800 [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 commit a842cf6e285d8ef7bbd4441c463b6a19ea6aa6a2 Author: Darrick J. Wong Date: Tue Jan 30 01:18:55 2007 -0800 [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 commit 4d288727e605a6e4ccd7a6ca0550521ed9cd5762 Author: Darrick J. Wong Date: Tue Jan 30 01:18:52 2007 -0800 [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 commit c8927f58ea18003c4c2ce8793955d1c5d2bb2300 Author: Darrick J. Wong Date: Tue Jan 30 01:18:49 2007 -0800 [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 commit 9db6041a3ad7d411a8a95e21779791d5c97c99a7 Author: Darrick J. Wong Date: Tue Jan 30 01:18:44 2007 -0800 [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 commit 9c46855b0d6e31fcc74e0c19d059bdaebef2f331 Author: Darrick J. Wong Date: Tue Jan 30 01:18:41 2007 -0800 [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 commit 6754121fb45ae4615056c37ecae95505e4a28e5d Author: Darrick J. Wong Date: Tue Jan 30 01:18:38 2007 -0800 [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 commit 949ed59e7c907725f541a114e00bc2d781e1741b Author: Darrick J. Wong Date: Tue Jan 30 01:18:35 2007 -0800 [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 commit 697a6a6ef46c52bfa7f4dabd59e9af38c80fb3e4 Author: Darrick J. Wong Date: Fri Nov 10 16:59:24 2006 -0800 [SCSI] aic94xx: Don't call pci_map_sg for already-mapped scatterlists It turns out that libata has already dma_map_sg'd the scatterlist entries that go with an ata_queued_cmd by the time it calls sas_ata_qc_issue. sas_ata_qc_issue passes this scatterlist to aic94xx. Unfortunately, aic94xx assumes that any scatterlist passed to it needs to be pci_map_sg'd... which blows away the mapping that libata created! This causes (on a x260) Calgary IOMMU table leaks and duplicate frees when aic94xx and libata try to {pci,dma}_unmap_sg the scatterlist. Signed-off-by: Darrick J. Wong Key this check off ATA_PROTOCOL_STP Signed-off-by: James Bottomley commit cf7f2c1e3588755d5a0aaea65854ecb84ee534fe Author: Darrick J. Wong Date: Wed Oct 18 14:43:37 2006 -0700 [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 commit 19c277acd41162cf418d61ac62ff75f6fd811256 Author: James Bottomley Date: Mon Oct 16 13:25:30 2006 -0500 [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 commit 6ed355a263865ff81ee3703b4fcbac22783ec54d Author: James Bottomley Date: Mon Oct 16 10:57:05 2006 -0500 [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 commit 5ce0e16fa7caf769fc48b83cf5cc593e0bd3ea41 Author: James Bottomley Date: Sun Oct 15 20:24:35 2006 -0500 [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 commit 25dd73522d1f986329b0b086355d79a89e873ff4 Author: Darrick J. Wong Date: Fri Oct 13 16:56:25 2006 -0700 [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 commit 1edeb0d200a370faaf0a67739a7b9fdf1bf2c642 Author: Darrick J. Wong Date: Thu Oct 5 15:12:37 2006 -0700 [SCSI] aic94xx: SATA tag mask not set correctly The aic94xx controller has a bitmask establishing which tags are ok to use with a SATA NCQ disk. When the queue depth is 32, however, the expression that is used sets the mask to zero, not 0xFFFFFFFF. This patch widens the width of the integer so that this case is handled properly. Signed-off-by: Darrick J. Wong Signed-off-by: James Bottomley commit ba8730e3964de2415dcb07025b9be1d45431a944 Author: James Bottomley Date: Thu Sep 7 15:52:09 2006 -0500 [SCSI] 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. commit 0e61b1459b1a95114c7d2acf85d458f20822a3fe Author: James Bottomley Date: Tue Aug 22 12:39:19 2006 -0500 [PATCH] Add SATA support to libsas 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 commit af2d77b1ddc8505006f684bc12c0b6f86f6b8c59 Author: Darrick J. Wong Date: Thu Aug 10 19:19:47 2006 -0700 [PATCH] 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/aic94xx/aic94xx_dev.c | 2 drivers/scsi/aic94xx/aic94xx_init.c | 3 drivers/scsi/aic94xx/aic94xx_task.c | 20 +- drivers/scsi/libsas/Makefile | 3 drivers/scsi/libsas/sas_ata.c | 412 +++++++++++++++++++++++++++++++++++ drivers/scsi/libsas/sas_discover.c | 27 +- drivers/scsi/libsas/sas_expander.c | 81 ++++++- drivers/scsi/libsas/sas_internal.h | 3 drivers/scsi/libsas/sas_scsi_host.c | 74 ++++++ include/linux/libata.h | 1 include/scsi/libsas.h | 13 + include/scsi/sas_ata.h | 41 +++ 12 files changed, 637 insertions(+), 43 deletions(-) diff --git a/drivers/scsi/aic94xx/aic94xx_dev.c b/drivers/scsi/aic94xx/aic94xx_dev.c index c520e5b..3dce618 100644 --- a/drivers/scsi/aic94xx/aic94xx_dev.c +++ b/drivers/scsi/aic94xx/aic94xx_dev.c @@ -126,7 +126,7 @@ static inline int asd_init_sata(struct d if (w76 & 0x100) /* NCQ? */ qdepth = (w75 & 0x1F) + 1; asd_ddbsite_write_dword(asd_ha, ddb, SATA_TAG_ALLOC_MASK, - (1<dev_type == SATA_DEV || dev->dev_type == SATA_PM || diff --git a/drivers/scsi/aic94xx/aic94xx_init.c b/drivers/scsi/aic94xx/aic94xx_init.c index 27852b4..09bc9fc 100644 --- a/drivers/scsi/aic94xx/aic94xx_init.c +++ b/drivers/scsi/aic94xx/aic94xx_init.c @@ -81,6 +81,9 @@ static struct scsi_host_template aic94xx .use_clustering = ENABLE_CLUSTERING, .eh_device_reset_handler = sas_eh_device_reset_handler, .eh_bus_reset_handler = sas_eh_bus_reset_handler, + .slave_alloc = sas_slave_alloc, + .target_destroy = sas_target_destroy, + .ioctl = sas_ioctl, }; static int __devinit asd_map_memio(struct asd_ha_struct *asd_ha) diff --git a/drivers/scsi/aic94xx/aic94xx_task.c b/drivers/scsi/aic94xx/aic94xx_task.c index e2ad5be..6c12c0f 100644 --- a/drivers/scsi/aic94xx/aic94xx_task.c +++ b/drivers/scsi/aic94xx/aic94xx_task.c @@ -74,8 +74,13 @@ static inline int asd_map_scatterlist(st return 0; } - num_sg = pci_map_sg(asd_ha->pcidev, task->scatter, task->num_scatter, - task->data_dir); + /* STP tasks come from libata which has already mapped + * the SG list */ + if (task->task_proto == SAS_PROTOCOL_STP) + num_sg = task->num_scatter; + else + num_sg = pci_map_sg(asd_ha->pcidev, task->scatter, + task->num_scatter, task->data_dir); if (num_sg == 0) return -ENOMEM; @@ -120,8 +125,9 @@ static inline int asd_map_scatterlist(st return 0; err_unmap: - pci_unmap_sg(asd_ha->pcidev, task->scatter, task->num_scatter, - task->data_dir); + if (task->task_proto != SAS_PROTOCOL_STP) + pci_unmap_sg(asd_ha->pcidev, task->scatter, task->num_scatter, + task->data_dir); return res; } @@ -142,8 +148,9 @@ static inline void asd_unmap_scatterlist } asd_free_coherent(asd_ha, ascb->sg_arr); - pci_unmap_sg(asd_ha->pcidev, task->scatter, task->num_scatter, - task->data_dir); + if (task->task_proto != SAS_PROTOCOL_STP) + pci_unmap_sg(asd_ha->pcidev, task->scatter, task->num_scatter, + task->data_dir); } /* ---------- Task complete tasklet ---------- */ @@ -391,7 +398,6 @@ static int asd_build_ata_ascb(struct asd scb->ata_task.total_xfer_len = cpu_to_le32(task->total_xfer_len); scb->ata_task.fis = task->ata_task.fis; - scb->ata_task.fis.fis_type = 0x27; if (likely(!task->ata_task.device_control_reg_update)) scb->ata_task.fis.flags |= 0x80; /* C=1: update ATA cmd reg */ scb->ata_task.fis.flags &= 0xF0; /* PM_PORT field shall be 0 */ diff --git a/drivers/scsi/libsas/Makefile b/drivers/scsi/libsas/Makefile index 44d972a..6383eb5 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 0000000..2db2589 --- /dev/null +++ b/drivers/scsi/libsas/sas_ata.c @@ -0,0 +1,412 @@ +/* + * 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" +#include "../scsi_transport_api.h" +#include + +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; + 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; + + if (!qc) + 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) { + 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; + } 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; + } + } + + 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); + + /* + * 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); +} + +static unsigned int sas_ata_qc_issue(struct ata_queued_cmd *qc) +{ + int res; + 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) + return AC_ERR_SYSTEM; + 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; + qc->lldd_task = task; + + 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 (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 + res = sas_queue_up(task); + + /* Examine */ + 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; + } + + return 0; +} + +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_UNKNOWN; + 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) { + /* + * 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; + 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); + } + } +} + +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; +} + +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_discover.c b/drivers/scsi/libsas/sas_discover.c index a65598b..a18c0f6 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 as 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 as 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; @@ -371,6 +371,7 @@ static int sas_issue_ata_cmd(struct doma 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; @@ -480,6 +481,9 @@ cont1: present. sas_satl_register_dev(dev); */ + + sas_fill_in_rphy(dev, dev->rphy); + return 0; out_err: dev->sata_dev.identify_packet_device = NULL; @@ -548,7 +552,7 @@ int sas_discover_sata(struct domain_devi res = sas_notify_lldd_dev_found(dev); if (res) - goto out_err2; + return res; switch (dev->dev_type) { case SATA_DEV: @@ -560,23 +564,12 @@ int sas_discover_sata(struct domain_devi 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_expander.c b/drivers/scsi/libsas/sas_expander.c index e34442e..12ca57d 100644 --- a/drivers/scsi/libsas/sas_expander.c +++ b/drivers/scsi/libsas/sas_expander.c @@ -218,6 +218,36 @@ static void sas_set_ex_phy(struct domain #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, NULL); + /* 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; @@ -238,23 +268,15 @@ static int sas_ex_phy_discover(struct do 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: @@ -527,6 +549,7 @@ static int sas_get_report_phy_sata(struc { int res; u8 *rps_req = alloc_smp_req(RPS_REQ_SIZE); + u8 *resp = (u8 *)rps_resp; if (!rps_req) return -ENOMEM; @@ -537,8 +560,28 @@ static int sas_get_report_phy_sata(struc 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, @@ -623,14 +666,26 @@ static struct domain_device *sas_ex_disc } memcpy(child->frame_rcvd, &child->sata_dev.rps_resp.rps.fis, sizeof(struct dev_to_host_fis)); + + rphy = sas_end_device_alloc(phy->port); + if (unlikely(!rphy)) + goto out_free; + 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; diff --git a/drivers/scsi/libsas/sas_internal.h b/drivers/scsi/libsas/sas_internal.h index a78638d..2b8213b 100644 --- a/drivers/scsi/libsas/sas_internal.h +++ b/drivers/scsi/libsas/sas_internal.h @@ -39,6 +39,9 @@ #else #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 b4b5269..277b32e 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 #include "../scsi_sas_internal.h" #include "../scsi_transport_api.h" #include "../scsi_priv.h" @@ -41,12 +42,10 @@ #include "../scsi_priv.h" #include #include #include +#include /* ---------- 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; @@ -171,7 +170,7 @@ static struct sas_task *sas_create_task( 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; @@ -212,6 +211,16 @@ int sas_queuecommand(struct scsi_cmnd *c struct sas_ha_struct *sas_ha = dev->port->ha; 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; + } + res = -ENOMEM; task = sas_create_task(cmd, dev, GFP_ATOMIC); if (!task) @@ -683,6 +692,16 @@ enum scsi_eh_timer_return sas_scsi_timed return EH_NOT_HANDLED; } +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; +} + struct domain_device *sas_find_dev_by_rphy(struct sas_rphy *rphy) { struct Scsi_Host *shost = dev_to_shost(rphy->dev.parent); @@ -722,10 +741,17 @@ static inline struct domain_device *sas_ int sas_target_alloc(struct scsi_target *starget) { struct domain_device *found_dev = sas_find_target(starget); + int res; if (!found_dev) return -ENODEV; + if (dev_is_sata(found_dev)) { + res = sas_ata_init_host_and_port(found_dev, starget); + if (res) + return res; + } + starget->hostdata = found_dev; return 0; } @@ -740,6 +766,11 @@ int sas_slave_configure(struct scsi_devi 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); @@ -763,6 +794,10 @@ int sas_slave_configure(struct scsi_devi 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) @@ -981,10 +1016,38 @@ void sas_task_abort(struct sas_task *tas return; } + if (dev_is_sata(task->dev)) { + sas_ata_task_abort(task); + return; + } + scsi_req_abort_cmd(sc); 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); @@ -998,3 +1061,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); diff --git a/include/linux/libata.h b/include/linux/libata.h index d8cfc72..1347559 100644 --- a/include/linux/libata.h +++ b/include/linux/libata.h @@ -422,6 +422,7 @@ struct ata_queued_cmd { ata_qc_cb_t complete_fn; void *private_data; + void *lldd_task; }; struct ata_port_stats { diff --git a/include/scsi/libsas.h b/include/scsi/libsas.h index 2e6bdc4..9275a46 100644 --- a/include/scsi/libsas.h +++ b/include/scsi/libsas.h @@ -30,6 +30,7 @@ #define _LIBSAS_H_ #include #include #include +#include #include #include #include @@ -165,6 +166,13 @@ struct sata_device { u8 port_no; /* port number, if this is a PM (Port) */ struct list_head children; /* PM Ports if this is a PM */ + + struct ata_port *ap; + struct ata_host ata_host; + struct ata_taskfile tf; + u32 sstatus; + u32 serror; + u32 scontrol; }; /* ---------- Domain device ---------- */ @@ -624,6 +632,7 @@ int sas_set_phy_speed(struct sas_phy *ph struct sas_phy_linkrates *rates); int sas_phy_enable(struct sas_phy *phy, int enabled); int sas_phy_reset(struct sas_phy *phy, int hard_reset); +int sas_queue_up(struct sas_task *task); extern int sas_queuecommand(struct scsi_cmnd *, void (*scsi_done)(struct scsi_cmnd *)); extern int sas_target_alloc(struct scsi_target *); @@ -661,4 +670,8 @@ int __sas_task_abort(struct sas_task *); int sas_eh_device_reset_handler(struct scsi_cmnd *cmd); int sas_eh_bus_reset_handler(struct scsi_cmnd *cmd); +extern void sas_target_destroy(struct scsi_target *); +extern int sas_slave_alloc(struct scsi_device *); +extern int sas_ioctl(struct scsi_device *sdev, int cmd, void __user *arg); + #endif /* _SASLIB_H_ */ diff --git a/include/scsi/sas_ata.h b/include/scsi/sas_ata.h new file mode 100644 index 0000000..3407c81 --- /dev/null +++ b/include/scsi/sas_ata.h @@ -0,0 +1,41 @@ +/* + * 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 + * + */ + +#ifndef _SAS_ATA_H_ +#define _SAS_ATA_H_ + +#include +#include + +static inline int dev_is_sata(struct domain_device *dev) +{ + return (dev->rphy->identify.target_port_protocols & SAS_PROTOCOL_SATA); +} + +int sas_ata_init_host_and_port(struct domain_device *found_dev, + struct scsi_target *starget); + +void sas_ata_task_abort(struct sas_task *task); + +#endif /* _SAS_ATA_H_ */