aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/ata
diff options
context:
space:
mode:
authorAlexander Gordeev <agordeev@redhat.com>2012-11-19 10:02:48 -0500
committerIngo Molnar <mingo@kernel.org>2013-01-24 11:25:13 -0500
commit5ca72c4f7c412c2002363218901eba5516c476b1 (patch)
treec87592b1249a5fa97e8e4cd62e1e72d9b6db389e /drivers/ata
parent08261d87f7d1b6253ab3223756625a5c74532293 (diff)
AHCI: Support multiple MSIs
Take advantage of multiple MSIs implementation on x86 - on systems with IRQ remapping AHCI ports not only get assigned separate MSI vectors - but also separate IRQs. As result, interrupts generated by different ports could be serviced on different CPUs rather than on a single one. In cases when number of allocated MSIs is less than requested the Sharing Last MSI mode does not get used, no matter implemented in hardware or not. Instead, the driver assumes the advantage of multiple MSIs is negated and falls back to the single MSI mode as if MRSM bit was set (some Intel chips implement this strategy anyway - MRSM bit gets set even if the number of allocated MSIs exceeds the number of implemented ports). Signed-off-by: Alexander Gordeev <agordeev@redhat.com> Acked-by: Jeff Garzik <jgarzik@redhat.com> Cc: Bjorn Helgaas <bhelgaas@google.com> Cc: Suresh Siddha <suresh.b.siddha@intel.com> Cc: Yinghai Lu <yinghai@kernel.org> Cc: Matthew Wilcox <willy@linux.intel.com> Cc: Linus Torvalds <torvalds@linux-foundation.org> Cc: Andrew Morton <akpm@linux-foundation.org> Cc: Peter Zijlstra <a.p.zijlstra@chello.nl> Cc: Thomas Gleixner <tglx@linutronix.de> Link: http://lkml.kernel.org/r/15bf7ee314dd55f21ec7d2a01c47613cd8190a7c.1353324359.git.agordeev@redhat.com Signed-off-by: Ingo Molnar <mingo@kernel.org>
Diffstat (limited to 'drivers/ata')
-rw-r--r--drivers/ata/ahci.c93
-rw-r--r--drivers/ata/ahci.h6
-rw-r--r--drivers/ata/libahci.c118
3 files changed, 207 insertions, 10 deletions
diff --git a/drivers/ata/ahci.c b/drivers/ata/ahci.c
index 497912732566..495aeed26779 100644
--- a/drivers/ata/ahci.c
+++ b/drivers/ata/ahci.c
@@ -1061,6 +1061,86 @@ static inline void ahci_gtf_filter_workaround(struct ata_host *host)
1061{} 1061{}
1062#endif 1062#endif
1063 1063
1064int ahci_init_interrupts(struct pci_dev *pdev, struct ahci_host_priv *hpriv)
1065{
1066 int rc;
1067 unsigned int maxvec;
1068
1069 if (!(hpriv->flags & AHCI_HFLAG_NO_MSI)) {
1070 rc = pci_enable_msi_block_auto(pdev, &maxvec);
1071 if (rc > 0) {
1072 if ((rc == maxvec) || (rc == 1))
1073 return rc;
1074 /*
1075 * Assume that advantage of multipe MSIs is negated,
1076 * so fallback to single MSI mode to save resources
1077 */
1078 pci_disable_msi(pdev);
1079 if (!pci_enable_msi(pdev))
1080 return 1;
1081 }
1082 }
1083
1084 pci_intx(pdev, 1);
1085 return 0;
1086}
1087
1088/**
1089 * ahci_host_activate - start AHCI host, request IRQs and register it
1090 * @host: target ATA host
1091 * @irq: base IRQ number to request
1092 * @n_msis: number of MSIs allocated for this host
1093 * @irq_handler: irq_handler used when requesting IRQs
1094 * @irq_flags: irq_flags used when requesting IRQs
1095 *
1096 * Similar to ata_host_activate, but requests IRQs according to AHCI-1.1
1097 * when multiple MSIs were allocated. That is one MSI per port, starting
1098 * from @irq.
1099 *
1100 * LOCKING:
1101 * Inherited from calling layer (may sleep).
1102 *
1103 * RETURNS:
1104 * 0 on success, -errno otherwise.
1105 */
1106int ahci_host_activate(struct ata_host *host, int irq, unsigned int n_msis)
1107{
1108 int i, rc;
1109
1110 /* Sharing Last Message among several ports is not supported */
1111 if (n_msis < host->n_ports)
1112 return -EINVAL;
1113
1114 rc = ata_host_start(host);
1115 if (rc)
1116 return rc;
1117
1118 for (i = 0; i < host->n_ports; i++) {
1119 rc = devm_request_threaded_irq(host->dev,
1120 irq + i, ahci_hw_interrupt, ahci_thread_fn, IRQF_SHARED,
1121 dev_driver_string(host->dev), host->ports[i]);
1122 if (rc)
1123 goto out_free_irqs;
1124 }
1125
1126 for (i = 0; i < host->n_ports; i++)
1127 ata_port_desc(host->ports[i], "irq %d", irq + i);
1128
1129 rc = ata_host_register(host, &ahci_sht);
1130 if (rc)
1131 goto out_free_all_irqs;
1132
1133 return 0;
1134
1135out_free_all_irqs:
1136 i = host->n_ports;
1137out_free_irqs:
1138 for (i--; i >= 0; i--)
1139 devm_free_irq(host->dev, irq + i, host->ports[i]);
1140
1141 return rc;
1142}
1143
1064static int ahci_init_one(struct pci_dev *pdev, const struct pci_device_id *ent) 1144static int ahci_init_one(struct pci_dev *pdev, const struct pci_device_id *ent)
1065{ 1145{
1066 unsigned int board_id = ent->driver_data; 1146 unsigned int board_id = ent->driver_data;
@@ -1069,7 +1149,7 @@ static int ahci_init_one(struct pci_dev *pdev, const struct pci_device_id *ent)
1069 struct device *dev = &pdev->dev; 1149 struct device *dev = &pdev->dev;
1070 struct ahci_host_priv *hpriv; 1150 struct ahci_host_priv *hpriv;
1071 struct ata_host *host; 1151 struct ata_host *host;
1072 int n_ports, i, rc; 1152 int n_ports, n_msis, i, rc;
1073 int ahci_pci_bar = AHCI_PCI_BAR_STANDARD; 1153 int ahci_pci_bar = AHCI_PCI_BAR_STANDARD;
1074 1154
1075 VPRINTK("ENTER\n"); 1155 VPRINTK("ENTER\n");
@@ -1156,11 +1236,12 @@ static int ahci_init_one(struct pci_dev *pdev, const struct pci_device_id *ent)
1156 if (ahci_sb600_enable_64bit(pdev)) 1236 if (ahci_sb600_enable_64bit(pdev))
1157 hpriv->flags &= ~AHCI_HFLAG_32BIT_ONLY; 1237 hpriv->flags &= ~AHCI_HFLAG_32BIT_ONLY;
1158 1238
1159 if ((hpriv->flags & AHCI_HFLAG_NO_MSI) || pci_enable_msi(pdev))
1160 pci_intx(pdev, 1);
1161
1162 hpriv->mmio = pcim_iomap_table(pdev)[ahci_pci_bar]; 1239 hpriv->mmio = pcim_iomap_table(pdev)[ahci_pci_bar];
1163 1240
1241 n_msis = ahci_init_interrupts(pdev, hpriv);
1242 if (n_msis > 1)
1243 hpriv->flags |= AHCI_HFLAG_MULTI_MSI;
1244
1164 /* save initial config */ 1245 /* save initial config */
1165 ahci_pci_save_initial_config(pdev, hpriv); 1246 ahci_pci_save_initial_config(pdev, hpriv);
1166 1247
@@ -1256,6 +1337,10 @@ static int ahci_init_one(struct pci_dev *pdev, const struct pci_device_id *ent)
1256 ahci_pci_print_info(host); 1337 ahci_pci_print_info(host);
1257 1338
1258 pci_set_master(pdev); 1339 pci_set_master(pdev);
1340
1341 if (hpriv->flags & AHCI_HFLAG_MULTI_MSI)
1342 return ahci_host_activate(host, pdev->irq, n_msis);
1343
1259 return ata_host_activate(host, pdev->irq, ahci_interrupt, IRQF_SHARED, 1344 return ata_host_activate(host, pdev->irq, ahci_interrupt, IRQF_SHARED,
1260 &ahci_sht); 1345 &ahci_sht);
1261} 1346}
diff --git a/drivers/ata/ahci.h b/drivers/ata/ahci.h
index 9be471200a07..b830e6c9fe49 100644
--- a/drivers/ata/ahci.h
+++ b/drivers/ata/ahci.h
@@ -231,6 +231,7 @@ enum {
231 AHCI_HFLAG_DELAY_ENGINE = (1 << 15), /* do not start engine on 231 AHCI_HFLAG_DELAY_ENGINE = (1 << 15), /* do not start engine on
232 port start (wait until 232 port start (wait until
233 error-handling stage) */ 233 error-handling stage) */
234 AHCI_HFLAG_MULTI_MSI = (1 << 16), /* multiple PCI MSIs */
234 235
235 /* ap->flags bits */ 236 /* ap->flags bits */
236 237
@@ -297,6 +298,8 @@ struct ahci_port_priv {
297 unsigned int ncq_saw_d2h:1; 298 unsigned int ncq_saw_d2h:1;
298 unsigned int ncq_saw_dmas:1; 299 unsigned int ncq_saw_dmas:1;
299 unsigned int ncq_saw_sdb:1; 300 unsigned int ncq_saw_sdb:1;
301 u32 intr_status; /* interrupts to handle */
302 spinlock_t lock; /* protects parent ata_port */
300 u32 intr_mask; /* interrupts to enable */ 303 u32 intr_mask; /* interrupts to enable */
301 bool fbs_supported; /* set iff FBS is supported */ 304 bool fbs_supported; /* set iff FBS is supported */
302 bool fbs_enabled; /* set iff FBS is enabled */ 305 bool fbs_enabled; /* set iff FBS is enabled */
@@ -359,7 +362,10 @@ void ahci_set_em_messages(struct ahci_host_priv *hpriv,
359 struct ata_port_info *pi); 362 struct ata_port_info *pi);
360int ahci_reset_em(struct ata_host *host); 363int ahci_reset_em(struct ata_host *host);
361irqreturn_t ahci_interrupt(int irq, void *dev_instance); 364irqreturn_t ahci_interrupt(int irq, void *dev_instance);
365irqreturn_t ahci_hw_interrupt(int irq, void *dev_instance);
366irqreturn_t ahci_thread_fn(int irq, void *dev_instance);
362void ahci_print_info(struct ata_host *host, const char *scc_s); 367void ahci_print_info(struct ata_host *host, const char *scc_s);
368int ahci_host_activate(struct ata_host *host, int irq, unsigned int n_msis);
363 369
364static inline void __iomem *__ahci_port_base(struct ata_host *host, 370static inline void __iomem *__ahci_port_base(struct ata_host *host,
365 unsigned int port_no) 371 unsigned int port_no)
diff --git a/drivers/ata/libahci.c b/drivers/ata/libahci.c
index 6cd7805e47ca..34c82167b962 100644
--- a/drivers/ata/libahci.c
+++ b/drivers/ata/libahci.c
@@ -1655,19 +1655,16 @@ static void ahci_error_intr(struct ata_port *ap, u32 irq_stat)
1655 ata_port_abort(ap); 1655 ata_port_abort(ap);
1656} 1656}
1657 1657
1658static void ahci_port_intr(struct ata_port *ap) 1658static void ahci_handle_port_interrupt(struct ata_port *ap,
1659 void __iomem *port_mmio, u32 status)
1659{ 1660{
1660 void __iomem *port_mmio = ahci_port_base(ap);
1661 struct ata_eh_info *ehi = &ap->link.eh_info; 1661 struct ata_eh_info *ehi = &ap->link.eh_info;
1662 struct ahci_port_priv *pp = ap->private_data; 1662 struct ahci_port_priv *pp = ap->private_data;
1663 struct ahci_host_priv *hpriv = ap->host->private_data; 1663 struct ahci_host_priv *hpriv = ap->host->private_data;
1664 int resetting = !!(ap->pflags & ATA_PFLAG_RESETTING); 1664 int resetting = !!(ap->pflags & ATA_PFLAG_RESETTING);
1665 u32 status, qc_active = 0; 1665 u32 qc_active = 0;
1666 int rc; 1666 int rc;
1667 1667
1668 status = readl(port_mmio + PORT_IRQ_STAT);
1669 writel(status, port_mmio + PORT_IRQ_STAT);
1670
1671 /* ignore BAD_PMP while resetting */ 1668 /* ignore BAD_PMP while resetting */
1672 if (unlikely(resetting)) 1669 if (unlikely(resetting))
1673 status &= ~PORT_IRQ_BAD_PMP; 1670 status &= ~PORT_IRQ_BAD_PMP;
@@ -1743,6 +1740,107 @@ static void ahci_port_intr(struct ata_port *ap)
1743 } 1740 }
1744} 1741}
1745 1742
1743void ahci_port_intr(struct ata_port *ap)
1744{
1745 void __iomem *port_mmio = ahci_port_base(ap);
1746 u32 status;
1747
1748 status = readl(port_mmio + PORT_IRQ_STAT);
1749 writel(status, port_mmio + PORT_IRQ_STAT);
1750
1751 ahci_handle_port_interrupt(ap, port_mmio, status);
1752}
1753
1754irqreturn_t ahci_thread_fn(int irq, void *dev_instance)
1755{
1756 struct ata_port *ap = dev_instance;
1757 struct ahci_port_priv *pp = ap->private_data;
1758 void __iomem *port_mmio = ahci_port_base(ap);
1759 unsigned long flags;
1760 u32 status;
1761
1762 spin_lock_irqsave(&ap->host->lock, flags);
1763 status = pp->intr_status;
1764 if (status)
1765 pp->intr_status = 0;
1766 spin_unlock_irqrestore(&ap->host->lock, flags);
1767
1768 spin_lock_bh(ap->lock);
1769 ahci_handle_port_interrupt(ap, port_mmio, status);
1770 spin_unlock_bh(ap->lock);
1771
1772 return IRQ_HANDLED;
1773}
1774EXPORT_SYMBOL_GPL(ahci_thread_fn);
1775
1776void ahci_hw_port_interrupt(struct ata_port *ap)
1777{
1778 void __iomem *port_mmio = ahci_port_base(ap);
1779 struct ahci_port_priv *pp = ap->private_data;
1780 u32 status;
1781
1782 status = readl(port_mmio + PORT_IRQ_STAT);
1783 writel(status, port_mmio + PORT_IRQ_STAT);
1784
1785 pp->intr_status |= status;
1786}
1787
1788irqreturn_t ahci_hw_interrupt(int irq, void *dev_instance)
1789{
1790 struct ata_port *ap_this = dev_instance;
1791 struct ahci_port_priv *pp = ap_this->private_data;
1792 struct ata_host *host = ap_this->host;
1793 struct ahci_host_priv *hpriv = host->private_data;
1794 void __iomem *mmio = hpriv->mmio;
1795 unsigned int i;
1796 u32 irq_stat, irq_masked;
1797
1798 VPRINTK("ENTER\n");
1799
1800 spin_lock(&host->lock);
1801
1802 irq_stat = readl(mmio + HOST_IRQ_STAT);
1803
1804 if (!irq_stat) {
1805 u32 status = pp->intr_status;
1806
1807 spin_unlock(&host->lock);
1808
1809 VPRINTK("EXIT\n");
1810
1811 return status ? IRQ_WAKE_THREAD : IRQ_NONE;
1812 }
1813
1814 irq_masked = irq_stat & hpriv->port_map;
1815
1816 for (i = 0; i < host->n_ports; i++) {
1817 struct ata_port *ap;
1818
1819 if (!(irq_masked & (1 << i)))
1820 continue;
1821
1822 ap = host->ports[i];
1823 if (ap) {
1824 ahci_hw_port_interrupt(ap);
1825 VPRINTK("port %u\n", i);
1826 } else {
1827 VPRINTK("port %u (no irq)\n", i);
1828 if (ata_ratelimit())
1829 dev_warn(host->dev,
1830 "interrupt on disabled port %u\n", i);
1831 }
1832 }
1833
1834 writel(irq_stat, mmio + HOST_IRQ_STAT);
1835
1836 spin_unlock(&host->lock);
1837
1838 VPRINTK("EXIT\n");
1839
1840 return IRQ_WAKE_THREAD;
1841}
1842EXPORT_SYMBOL_GPL(ahci_hw_interrupt);
1843
1746irqreturn_t ahci_interrupt(int irq, void *dev_instance) 1844irqreturn_t ahci_interrupt(int irq, void *dev_instance)
1747{ 1845{
1748 struct ata_host *host = dev_instance; 1846 struct ata_host *host = dev_instance;
@@ -2196,6 +2294,14 @@ static int ahci_port_start(struct ata_port *ap)
2196 */ 2294 */
2197 pp->intr_mask = DEF_PORT_IRQ; 2295 pp->intr_mask = DEF_PORT_IRQ;
2198 2296
2297 /*
2298 * Switch to per-port locking in case each port has its own MSI vector.
2299 */
2300 if ((hpriv->flags & AHCI_HFLAG_MULTI_MSI)) {
2301 spin_lock_init(&pp->lock);
2302 ap->lock = &pp->lock;
2303 }
2304
2199 ap->private_data = pp; 2305 ap->private_data = pp;
2200 2306
2201 /* engage engines, captain */ 2307 /* engage engines, captain */