RE: [PATCH #upstream, v2] ahci: Implement SATA AHCI FIS-based switching support

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



Hi Tejun, 


> -----Original Message-----
> From: Tejun Heo [mailto:tj@xxxxxxxxxx] 
> 
> 
> Why does fbs_need_dec need to be in ahci_port_priv?  Can't it be a
> local variable of error_intr()?

Yes, it should be replaced by one local variable.


> > +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);

OK.

 
> > +		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);
> 	}

OK.


> > @@ -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;
> 	}

PORT_FBS_SDE also need check, because(ahci v1.2  3.3.16):
Device With Error (DWE): Set by hardware to the value of the
Port Multiplier port number of the device that experienced a fatal
error condition. This field is only valid when PxFBS.SDE = '1'.

So I'll update the code into:
	u32 fbs = readl(port_mmio + PORT_FBS);
	int pmp = fbs >> PORT_FBS_DWE_OFFSET;

	if ((fbs & PORT_FBS_SDE) && (pmp < ap->nr_pmp_links) &&
	    ata_link_online(&ap->pmp_link[pmp])) {
		link = &ap->pmp_link[pmp];
		fbs_need_dec = true;
	}

 
> >  	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?

Because I referred to the AHCI spec v1.2  9.3.6:
An interface fatal error may be localized to a particular device or may
be fatal to the entire port.....If the error is fatal to the entire
port, then
PxFBS.SDE shall be cleared to '0' by the hardware. Software should
follow either the device specific or non-device specific error procedure
depending on whether PxFBS.SDE is set to '1'.


> > @@ -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?

Yes, I'll update that.


> > +	} 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.

OK, updated as below, and returns "void" instead of "int":
static void ahci_enable_fbs(struct ata_port *ap)
{
	struct ahci_port_priv *pp = ap->private_data;
	void __iomem *port_mmio = ahci_port_base(ap);
	u32 fbs;
	int rc;

	if (!pp->fbs_supported)
		return;

	WARN_ON(pp->fbs_enabled);

	rc = ahci_stop_engine(ap);
	if (rc)
		return;

	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);
}


> > +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 void ahci_disable_fbs(struct ata_port *ap)
{
	struct ahci_port_priv *pp = ap->private_data;
	void __iomem *port_mmio = ahci_port_base(ap);
	u32 fbs;
	int rc;

	if (!pp->fbs_supported)
		return;

	WARN_ON(!pp->fbs_enabled);

	rc = ahci_stop_engine(ap);
	if (rc)
		return;

	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");
	else {
		dev_printk(KERN_INFO, ap->host->dev, "FBS is
disabled.\n");
		pp->fbs_enabled = false;
	}

	ahci_start_engine(ap);
}


> >  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?

Sure, updated as below:
	if (cmd & PORT_CMD_FBSCP)
		pp->fbs_supported = true;
	else
		WARN_ON(1);


> Other than the above, looks great to me.

OK, thanks for your nice suggestions, I'll have to submit v3 later...


Best Regards,
Shane

--
To unsubscribe from this list: send the line "unsubscribe linux-ide" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at  http://vger.kernel.org/majordomo-info.html

[Index of Archives]     [Linux Filesystems]     [Linux SCSI]     [Linux RAID]     [Git]     [Kernel Newbies]     [Linux Newbie]     [Security]     [Netfilter]     [Bugtraq]     [Yosemite News]     [MIPS Linux]     [ARM Linux]     [Linux Security]     [Samba]     [Device Mapper]

  Powered by Linux