aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAlexander Gordeev <agordeev@redhat.com>2014-10-06 11:24:45 -0400
committerTejun Heo <tj@kernel.org>2014-10-06 11:43:36 -0400
commit18dcf433f3ded61eb140a55e7048ec2fef79e723 (patch)
treee25ea1e7b82c6a43d76b7f44aa6c130ad12cca90
parent227dfb4dbf109596d76a9b842856c4ff68e4efb2 (diff)
AHCI: Optimize single IRQ interrupt processing
Split interrupt service routine into hardware context handler and threaded context handler. That allows to protect ports with individual locks rather than with a single host-wide lock and move port interrupts handling out of the hardware interrupt context. Testing was done by transferring 8GB on two hard drives in parallel using command 'dd if=/dev/sd{a,b} of=/dev/null'. With lock_stat statistics I measured access times to ata_host::lock spinlock (since interrupt handler code is fully embraced with this lock). The average lock's holdtime decreased eight times while average waittime decreased two times. Both before and after the change the transfer time is the same, while 'perf record -e cycles:k ...' shows 1%-4% CPU time spent in ahci_single_irq_intr() routine before the update and not even sampled/shown ahci_single_irq_intr() after the update. Signed-off-by: Alexander Gordeev <agordeev@redhat.com> Signed-off-by: Tejun Heo <tj@kernel.org> Cc: linux-ide@vger.kernel.org
-rw-r--r--drivers/ata/libahci.c74
1 files changed, 61 insertions, 13 deletions
diff --git a/drivers/ata/libahci.c b/drivers/ata/libahci.c
index 97683e45ab04..3ce3d23e4f97 100644
--- a/drivers/ata/libahci.c
+++ b/drivers/ata/libahci.c
@@ -1778,15 +1778,16 @@ static void ahci_handle_port_interrupt(struct ata_port *ap,
1778 } 1778 }
1779} 1779}
1780 1780
1781static void ahci_port_intr(struct ata_port *ap) 1781static void ahci_update_intr_status(struct ata_port *ap)
1782{ 1782{
1783 void __iomem *port_mmio = ahci_port_base(ap); 1783 void __iomem *port_mmio = ahci_port_base(ap);
1784 struct ahci_port_priv *pp = ap->private_data;
1784 u32 status; 1785 u32 status;
1785 1786
1786 status = readl(port_mmio + PORT_IRQ_STAT); 1787 status = readl(port_mmio + PORT_IRQ_STAT);
1787 writel(status, port_mmio + PORT_IRQ_STAT); 1788 writel(status, port_mmio + PORT_IRQ_STAT);
1788 1789
1789 ahci_handle_port_interrupt(ap, port_mmio, status); 1790 atomic_or(status, &pp->intr_status);
1790} 1791}
1791 1792
1792static irqreturn_t ahci_port_thread_fn(int irq, void *dev_instance) 1793static irqreturn_t ahci_port_thread_fn(int irq, void *dev_instance)
@@ -1807,6 +1808,34 @@ static irqreturn_t ahci_port_thread_fn(int irq, void *dev_instance)
1807 return IRQ_HANDLED; 1808 return IRQ_HANDLED;
1808} 1809}
1809 1810
1811irqreturn_t ahci_thread_fn(int irq, void *dev_instance)
1812{
1813 struct ata_host *host = dev_instance;
1814 struct ahci_host_priv *hpriv = host->private_data;
1815 u32 irq_masked = hpriv->port_map;
1816 unsigned int i;
1817
1818 for (i = 0; i < host->n_ports; i++) {
1819 struct ata_port *ap;
1820
1821 if (!(irq_masked & (1 << i)))
1822 continue;
1823
1824 ap = host->ports[i];
1825 if (ap) {
1826 ahci_port_thread_fn(irq, ap);
1827 VPRINTK("port %u\n", i);
1828 } else {
1829 VPRINTK("port %u (no irq)\n", i);
1830 if (ata_ratelimit())
1831 dev_warn(host->dev,
1832 "interrupt on disabled port %u\n", i);
1833 }
1834 }
1835
1836 return IRQ_HANDLED;
1837}
1838
1810static irqreturn_t ahci_multi_irqs_intr(int irq, void *dev_instance) 1839static irqreturn_t ahci_multi_irqs_intr(int irq, void *dev_instance)
1811{ 1840{
1812 struct ata_port *ap = dev_instance; 1841 struct ata_port *ap = dev_instance;
@@ -1856,7 +1885,7 @@ static irqreturn_t ahci_single_irq_intr(int irq, void *dev_instance)
1856 1885
1857 ap = host->ports[i]; 1886 ap = host->ports[i];
1858 if (ap) { 1887 if (ap) {
1859 ahci_port_intr(ap); 1888 ahci_update_intr_status(ap);
1860 VPRINTK("port %u\n", i); 1889 VPRINTK("port %u\n", i);
1861 } else { 1890 } else {
1862 VPRINTK("port %u (no irq)\n", i); 1891 VPRINTK("port %u (no irq)\n", i);
@@ -1883,7 +1912,7 @@ static irqreturn_t ahci_single_irq_intr(int irq, void *dev_instance)
1883 1912
1884 VPRINTK("EXIT\n"); 1913 VPRINTK("EXIT\n");
1885 1914
1886 return IRQ_RETVAL(handled); 1915 return handled ? IRQ_WAKE_THREAD : IRQ_NONE;
1887} 1916}
1888 1917
1889unsigned int ahci_qc_issue(struct ata_queued_cmd *qc) 1918unsigned int ahci_qc_issue(struct ata_queued_cmd *qc)
@@ -2295,13 +2324,8 @@ static int ahci_port_start(struct ata_port *ap)
2295 */ 2324 */
2296 pp->intr_mask = DEF_PORT_IRQ; 2325 pp->intr_mask = DEF_PORT_IRQ;
2297 2326
2298 /* 2327 spin_lock_init(&pp->lock);
2299 * Switch to per-port locking in case each port has its own MSI vector. 2328 ap->lock = &pp->lock;
2300 */
2301 if ((hpriv->flags & AHCI_HFLAG_MULTI_MSI)) {
2302 spin_lock_init(&pp->lock);
2303 ap->lock = &pp->lock;
2304 }
2305 2329
2306 ap->private_data = pp; 2330 ap->private_data = pp;
2307 2331
@@ -2462,6 +2486,31 @@ out_free_irqs:
2462 return rc; 2486 return rc;
2463} 2487}
2464 2488
2489static int ahci_host_activate_single_irq(struct ata_host *host, int irq,
2490 struct scsi_host_template *sht)
2491{
2492 int i, rc;
2493
2494 rc = ata_host_start(host);
2495 if (rc)
2496 return rc;
2497
2498 rc = devm_request_threaded_irq(host->dev, irq, ahci_single_irq_intr,
2499 ahci_thread_fn, IRQF_SHARED,
2500 dev_driver_string(host->dev), host);
2501 if (rc)
2502 return rc;
2503
2504 for (i = 0; i < host->n_ports; i++)
2505 ata_port_desc(host->ports[i], "irq %d", irq);
2506
2507 rc = ata_host_register(host, sht);
2508 if (rc)
2509 devm_free_irq(host->dev, irq, host);
2510
2511 return rc;
2512}
2513
2465/** 2514/**
2466 * ahci_host_activate - start AHCI host, request IRQs and register it 2515 * ahci_host_activate - start AHCI host, request IRQs and register it
2467 * @host: target ATA host 2516 * @host: target ATA host
@@ -2487,8 +2536,7 @@ int ahci_host_activate(struct ata_host *host, int irq,
2487 if (hpriv->flags & AHCI_HFLAG_MULTI_MSI) 2536 if (hpriv->flags & AHCI_HFLAG_MULTI_MSI)
2488 rc = ahci_host_activate_multi_irqs(host, irq, sht); 2537 rc = ahci_host_activate_multi_irqs(host, irq, sht);
2489 else 2538 else
2490 rc = ata_host_activate(host, irq, ahci_single_irq_intr, 2539 rc = ahci_host_activate_single_irq(host, irq, sht);
2491 IRQF_SHARED, sht);
2492 return rc; 2540 return rc;
2493} 2541}
2494EXPORT_SYMBOL_GPL(ahci_host_activate); 2542EXPORT_SYMBOL_GPL(ahci_host_activate);