From: Tejun Heo <tj@kernel.org>
To: shane.huang@amd.com
Cc: jgarzik@pobox.com, linux-ide@vger.kernel.org
Subject: Re: [PATCH #upstream, v2] ahci: Implement SATA AHCI FIS-based switching support
Date: Mon, 31 Aug 2009 17:01:44 +0900 [thread overview]
Message-ID: <4A9B8368.1080807@kernel.org> (raw)
In-Reply-To: <1250570756.5207.15.camel@zm-desktop>
Hello, Shane.
Sorry about the delay.
Shane Huang wrote:
> Implement SATA AHCI FIS-based switching support. The patch has been updated
> after Tejun's review and suggestions to the 1st submission, which will not
> modify libata anymore.
Heh... nice that it's now contained in ahci.c proper.
> @@ -286,6 +300,10 @@ struct ahci_port_priv {
> unsigned int ncq_saw_dmas:1;
> unsigned int ncq_saw_sdb:1;
> u32 intr_mask; /* interrupts to enable */
> + bool fbs_supported; /* set iff FBS is supported */
> + bool fbs_enabled; /* set iff FBS is enabled */
> + int fbs_last_dev; /* save FBS.DEV of last FIS */
> + bool fbs_need_dec; /* need clear device error */
Why does fbs_need_dec need to be in ahci_port_priv? Can't it be a
local variable of error_intr()?
> /* enclosure management info per PM slot */
> struct ahci_em_priv em_priv[EM_MAX_SLOTS];
> };
> +static void ahci_fbs_dec_intr(struct ata_port *ap)
> +{
> + struct ahci_port_priv *pp = ap->private_data;
> + DPRINTK("ENTER\n");
> +
> + if (pp->fbs_enabled) {
Just do BUG_ON(!pp->fbs_enabled);
> + void __iomem *port_mmio = ahci_port_base(ap);
> + u32 fbs = readl(port_mmio + PORT_FBS);
> + int retries = 3;
> +
> + /* time to wait for DEC is not specified by AHCI spec,
> + * add a retry loop for safety */
> + do {
> + writel(fbs | PORT_FBS_DEC, port_mmio + PORT_FBS);
> + fbs = readl(port_mmio + PORT_FBS);
> + retries--;
> + } while ((fbs & PORT_FBS_DEC) && retries);
Hmmm... shouldn't it be more like the following?
writel(fbs | PORT_FBS_DEC, port_mmio + PORT_FBS);
fbs = readl(port_mmio + PORT_FBS);
while ((fbs & PORT_FBS_DEC) && retries--) {
udelay(1);
fbs = readl(port_mmio + PORT_FBS);
}
> + if (fbs & PORT_FBS_DEC)
> + dev_printk(KERN_ERR, ap->host->dev,
> + "failed to clear device error\n");
> + } else
> + dev_printk(KERN_ERR, ap->host->dev,
> + "FBS is disabled, no need to clear device error\n");
> +}
> +
> static void ahci_error_intr(struct ata_port *ap, u32 irq_stat)
> {
> struct ahci_host_priv *hpriv = ap->host->private_data;
> @@ -1984,10 +2042,22 @@ static void ahci_error_intr(struct ata_port *ap, u32 irq_stat)
> struct ata_eh_info *active_ehi;
> u32 serror;
>
> - /* determine active link */
> - ata_for_each_link(link, ap, EDGE)
> - if (ata_link_active(link))
> - break;
> + /* determine active link with error */
> + if (pp->fbs_enabled) {
> + void __iomem *port_mmio = ahci_port_base(ap);
> + u32 fbs = readl(port_mmio + PORT_FBS);
> +
> + ata_for_each_link(link, ap, EDGE)
> + if (ata_link_active(link) && (fbs & PORT_FBS_SDE) &&
> + (link->pmp == (fbs >> PORT_FBS_DWE_OFFSET))) {
> + pp->fbs_need_dec = true;
> + break;
> + }
You can do
pmp = fbs >> PORT_FBS_DWE_OFFSET;
if (pmp < ap->nr_pmp_links && ata_link_online(&ap->pmp_link[pmp])) {
link = &ap->pmp_link[pmp];
pp->fbs_need_dec = true;
}
> + } else
> + ata_for_each_link(link, ap, EDGE)
> + if (ata_link_active(link))
> + break;
> +
> if (!link)
> link = &ap->link;
>
> @@ -2044,8 +2114,13 @@ static void ahci_error_intr(struct ata_port *ap, u32 irq_stat)
> }
>
> if (irq_stat & PORT_IRQ_IF_ERR) {
> - host_ehi->err_mask |= AC_ERR_ATA_BUS;
> - host_ehi->action |= ATA_EH_RESET;
> + if (pp->fbs_need_dec)
> + active_ehi->err_mask |= AC_ERR_DEV;
> + else {
> + host_ehi->err_mask |= AC_ERR_ATA_BUS;
> + host_ehi->action |= ATA_EH_RESET;
> + }
> +
Are you sure IF_ERR is device specific and doesn't require host link
reset?
> @@ -2061,7 +2136,12 @@ static void ahci_error_intr(struct ata_port *ap, u32 irq_stat)
> if (irq_stat & PORT_IRQ_FREEZE)
> ata_port_freeze(ap);
> else
> - ata_port_abort(ap);
> + if (pp->fbs_enabled && pp->fbs_need_dec &&
else if (pp->fbs_need_dec) {
should be enough, right?
> + !ata_is_host_link(link)) {
> + ata_link_abort(link);
> + ahci_fbs_dec_intr(ap);
> + } else
> + ata_port_abort(ap);
> }
>
> static void ahci_port_intr(struct ata_port *ap)
> @@ -2113,12 +2193,19 @@ static void ahci_port_intr(struct ata_port *ap)
> /* If the 'N' bit in word 0 of the FIS is set,
> * we just received asynchronous notification.
> * Tell libata about it.
> + *
> + * Lack of SNotification should not appear in
> + * ahci 1.2, so the workaround is unnecessary
> + * when FBS is enabled.
> */
> - const __le32 *f = pp->rx_fis + RX_FIS_SDB;
> - u32 f0 = le32_to_cpu(f[0]);
> -
> - if (f0 & (1 << 15))
> - sata_async_notification(ap);
> + if (pp->fbs_enabled)
> + WARN_ON(1);
> + else {
> + const __le32 *f = pp->rx_fis + RX_FIS_SDB;
> + u32 f0 = le32_to_cpu(f[0]);
> + if (f0 & (1 << 15))
> + sata_async_notification(ap);
> + }
> }
> }
>
> @@ -2212,6 +2299,17 @@ static unsigned int ahci_qc_issue(struct ata_queued_cmd *qc)
>
> if (qc->tf.protocol == ATA_PROT_NCQ)
> writel(1 << qc->tag, port_mmio + PORT_SCR_ACT);
> +
> + if (pp->fbs_enabled) {
> + if (pp->fbs_last_dev != qc->dev->link->pmp) {
> + u32 fbs = readl(port_mmio + PORT_FBS);
> + fbs &= ~(PORT_FBS_DEV_MASK | PORT_FBS_DEC);
> + fbs |= qc->dev->link->pmp << PORT_FBS_DEV_OFFSET;
> + writel(fbs, port_mmio + PORT_FBS);
> + pp->fbs_last_dev = qc->dev->link->pmp;
> + }
> + }
> +
> writel(1 << qc->tag, port_mmio + PORT_CMD_ISSUE);
>
> ahci_sw_activity(qc->dev->link);
> @@ -2224,6 +2322,9 @@ static bool ahci_qc_fill_rtf(struct ata_queued_cmd *qc)
> struct ahci_port_priv *pp = qc->ap->private_data;
> u8 *d2h_fis = pp->rx_fis + RX_FIS_D2H_REG;
>
> + if (pp->fbs_enabled)
> + d2h_fis += (qc->dev->link->pmp) * AHCI_RX_FIS_SZ;
> +
> ata_tf_from_fis(d2h_fis, &qc->result_tf);
> return true;
> }
> @@ -2272,6 +2373,77 @@ static void ahci_post_internal_cmd(struct ata_queued_cmd *qc)
> ahci_kick_engine(ap);
> }
>
> +static int ahci_enable_fbs(struct ata_port *ap)
> +{
> + struct ahci_port_priv *pp = ap->private_data;
> +
> + if (pp->fbs_supported && !pp->fbs_enabled) {
> + void __iomem *port_mmio = ahci_port_base(ap);
> + u32 fbs;
> + int rc = ahci_stop_engine(ap);
> + if (rc)
> + return rc;
> +
> + fbs = readl(port_mmio + PORT_FBS);
> + writel(fbs | PORT_FBS_EN, port_mmio + PORT_FBS);
> + fbs = readl(port_mmio + PORT_FBS);
> + if (fbs & PORT_FBS_EN) {
> + dev_printk(KERN_INFO, ap->host->dev,
> + "FBS is enabled.\n");
> + pp->fbs_enabled = true;
> + pp->fbs_last_dev = -1; /* initialization */
> + } else {
> + dev_printk(KERN_ERR, ap->host->dev,
> + "Failed to enable FBS\n");
> + ahci_start_engine(ap);
> + return -EIO;
> + }
> +
> + ahci_start_engine(ap);
> + } else {
> + dev_printk(KERN_ERR, ap->host->dev,
> + "FBS is not supported or already enabled\n");
> + return -EINVAL;
The above message will be printed on every !FBS ahcis, right? It
would be better to do the following at the top of the function.
if (!pp->fbs_supported)
return;
WARN_ON(pp->fbs_enabled);
and drop the if/else.
> +static int ahci_disable_fbs(struct ata_port *ap)
> +{
> + struct ahci_port_priv *pp = ap->private_data;
> +
> + if (pp->fbs_enabled) {
> + void __iomem *port_mmio = ahci_port_base(ap);
> + u32 fbs;
> + int rc = ahci_stop_engine(ap);
> + if (rc)
> + return rc;
> +
> + fbs = readl(port_mmio + PORT_FBS);
> + writel(fbs & ~PORT_FBS_EN, port_mmio + PORT_FBS);
> + fbs = readl(port_mmio + PORT_FBS);
> + if (fbs & PORT_FBS_EN) {
> + dev_printk(KERN_ERR, ap->host->dev,
> + "Failed to disable FBS\n");
> + ahci_start_engine(ap);
> + return -EIO;
> + } else {
> + dev_printk(KERN_INFO, ap->host->dev,
> + "FBS is disabled.\n");
> + pp->fbs_enabled = false;
> + }
> +
> + ahci_start_engine(ap);
> + } else if (sata_pmp_attached(ap)) {
> + dev_printk(KERN_ERR, ap->host->dev,
> + "FBS is not supported or already disabled\n");
> + return -EINVAL;
> + }
Ditto. Just drop sanity checks. Upper layer should take care of them.
No need to check that this deep in the stack.
> static int ahci_port_start(struct ata_port *ap)
> {
> + struct ahci_host_priv *hpriv = ap->host->private_data;
> struct device *dev = ap->host->dev;
> struct ahci_port_priv *pp;
> void *mem;
> dma_addr_t mem_dma;
> + size_t dma_sz, rx_fis_sz;
>
> pp = devm_kzalloc(dev, sizeof(*pp), GFP_KERNEL);
> if (!pp)
> return -ENOMEM;
>
> - mem = dmam_alloc_coherent(dev, AHCI_PORT_PRIV_DMA_SZ, &mem_dma,
> - GFP_KERNEL);
> + /* check FBS capability */
> + if ((hpriv->cap & HOST_CAP_FBS) && sata_pmp_supported(ap)) {
> + void __iomem *port_mmio = ahci_port_base(ap);
> + u32 cmd = readl(port_mmio + PORT_CMD);
> + if (cmd & PORT_CMD_FBSCP)
> + pp->fbs_supported = true;
Maybe whine a bit if CAP indicates FBS but PORT_CMD doesn't?
Other than the above, looks great to me.
Thanks a lot.
--
tejun
next prev parent reply other threads:[~2009-08-31 8:01 UTC|newest]
Thread overview: 16+ messages / expand[flat|nested] mbox.gz Atom feed top
2009-08-18 4:45 [PATCH #upstream, v2] ahci: Implement SATA AHCI FIS-based switching support Shane Huang
2009-08-31 8:01 ` Tejun Heo [this message]
2009-09-01 12:22 ` Huang, Shane
2009-09-01 12:28 ` Tejun Heo
2009-09-01 13:08 ` Huang, Shane
2009-09-01 13:14 ` Tejun Heo
2009-09-02 1:55 ` Huang, Shane
2009-09-02 1:58 ` Tejun Heo
2009-09-02 2:32 ` Huang, Shane
2009-09-02 2:40 ` Tejun Heo
2009-09-02 6:42 ` Huang, Shane
2009-09-02 6:49 ` Tejun Heo
2009-09-02 6:58 ` Huang, Shane
2009-09-03 4:53 ` Huang, Shane
2009-09-03 6:04 ` Tejun Heo
2009-09-04 2:25 ` Huang, Shane
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=4A9B8368.1080807@kernel.org \
--to=tj@kernel.org \
--cc=jgarzik@pobox.com \
--cc=linux-ide@vger.kernel.org \
--cc=shane.huang@amd.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).