Skip to content

Commit 5ca72c4

Browse files
Alexander GordeevIngo Molnar
authored andcommitted
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>
1 parent 08261d8 commit 5ca72c4

File tree

3 files changed

+207
-10
lines changed

3 files changed

+207
-10
lines changed

drivers/ata/ahci.c

Lines changed: 89 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1061,6 +1061,86 @@ static inline void ahci_gtf_filter_workaround(struct ata_host *host)
10611061
{}
10621062
#endif
10631063

1064+
int 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+
*/
1106+
int 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+
1135+
out_free_all_irqs:
1136+
i = host->n_ports;
1137+
out_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+
10641144
static int ahci_init_one(struct pci_dev *pdev, const struct pci_device_id *ent)
10651145
{
10661146
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)
10691149
struct device *dev = &pdev->dev;
10701150
struct ahci_host_priv *hpriv;
10711151
struct ata_host *host;
1072-
int n_ports, i, rc;
1152+
int n_ports, n_msis, i, rc;
10731153
int ahci_pci_bar = AHCI_PCI_BAR_STANDARD;
10741154

10751155
VPRINTK("ENTER\n");
@@ -1156,11 +1236,12 @@ static int ahci_init_one(struct pci_dev *pdev, const struct pci_device_id *ent)
11561236
if (ahci_sb600_enable_64bit(pdev))
11571237
hpriv->flags &= ~AHCI_HFLAG_32BIT_ONLY;
11581238

1159-
if ((hpriv->flags & AHCI_HFLAG_NO_MSI) || pci_enable_msi(pdev))
1160-
pci_intx(pdev, 1);
1161-
11621239
hpriv->mmio = pcim_iomap_table(pdev)[ahci_pci_bar];
11631240

1241+
n_msis = ahci_init_interrupts(pdev, hpriv);
1242+
if (n_msis > 1)
1243+
hpriv->flags |= AHCI_HFLAG_MULTI_MSI;
1244+
11641245
/* save initial config */
11651246
ahci_pci_save_initial_config(pdev, hpriv);
11661247

@@ -1256,6 +1337,10 @@ static int ahci_init_one(struct pci_dev *pdev, const struct pci_device_id *ent)
12561337
ahci_pci_print_info(host);
12571338

12581339
pci_set_master(pdev);
1340+
1341+
if (hpriv->flags & AHCI_HFLAG_MULTI_MSI)
1342+
return ahci_host_activate(host, pdev->irq, n_msis);
1343+
12591344
return ata_host_activate(host, pdev->irq, ahci_interrupt, IRQF_SHARED,
12601345
&ahci_sht);
12611346
}

drivers/ata/ahci.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -231,6 +231,7 @@ enum {
231231
AHCI_HFLAG_DELAY_ENGINE = (1 << 15), /* do not start engine on
232232
port start (wait until
233233
error-handling stage) */
234+
AHCI_HFLAG_MULTI_MSI = (1 << 16), /* multiple PCI MSIs */
234235

235236
/* ap->flags bits */
236237

@@ -297,6 +298,8 @@ struct ahci_port_priv {
297298
unsigned int ncq_saw_d2h:1;
298299
unsigned int ncq_saw_dmas:1;
299300
unsigned int ncq_saw_sdb:1;
301+
u32 intr_status; /* interrupts to handle */
302+
spinlock_t lock; /* protects parent ata_port */
300303
u32 intr_mask; /* interrupts to enable */
301304
bool fbs_supported; /* set iff FBS is supported */
302305
bool fbs_enabled; /* set iff FBS is enabled */
@@ -359,7 +362,10 @@ void ahci_set_em_messages(struct ahci_host_priv *hpriv,
359362
struct ata_port_info *pi);
360363
int ahci_reset_em(struct ata_host *host);
361364
irqreturn_t ahci_interrupt(int irq, void *dev_instance);
365+
irqreturn_t ahci_hw_interrupt(int irq, void *dev_instance);
366+
irqreturn_t ahci_thread_fn(int irq, void *dev_instance);
362367
void ahci_print_info(struct ata_host *host, const char *scc_s);
368+
int ahci_host_activate(struct ata_host *host, int irq, unsigned int n_msis);
363369

364370
static inline void __iomem *__ahci_port_base(struct ata_host *host,
365371
unsigned int port_no)

drivers/ata/libahci.c

Lines changed: 112 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1655,19 +1655,16 @@ static void ahci_error_intr(struct ata_port *ap, u32 irq_stat)
16551655
ata_port_abort(ap);
16561656
}
16571657

1658-
static void ahci_port_intr(struct ata_port *ap)
1658+
static void ahci_handle_port_interrupt(struct ata_port *ap,
1659+
void __iomem *port_mmio, u32 status)
16591660
{
1660-
void __iomem *port_mmio = ahci_port_base(ap);
16611661
struct ata_eh_info *ehi = &ap->link.eh_info;
16621662
struct ahci_port_priv *pp = ap->private_data;
16631663
struct ahci_host_priv *hpriv = ap->host->private_data;
16641664
int resetting = !!(ap->pflags & ATA_PFLAG_RESETTING);
1665-
u32 status, qc_active = 0;
1665+
u32 qc_active = 0;
16661666
int rc;
16671667

1668-
status = readl(port_mmio + PORT_IRQ_STAT);
1669-
writel(status, port_mmio + PORT_IRQ_STAT);
1670-
16711668
/* ignore BAD_PMP while resetting */
16721669
if (unlikely(resetting))
16731670
status &= ~PORT_IRQ_BAD_PMP;
@@ -1743,6 +1740,107 @@ static void ahci_port_intr(struct ata_port *ap)
17431740
}
17441741
}
17451742

1743+
void 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+
1754+
irqreturn_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+
}
1774+
EXPORT_SYMBOL_GPL(ahci_thread_fn);
1775+
1776+
void 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+
1788+
irqreturn_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+
}
1842+
EXPORT_SYMBOL_GPL(ahci_hw_interrupt);
1843+
17461844
irqreturn_t ahci_interrupt(int irq, void *dev_instance)
17471845
{
17481846
struct ata_host *host = dev_instance;
@@ -2196,6 +2294,14 @@ static int ahci_port_start(struct ata_port *ap)
21962294
*/
21972295
pp->intr_mask = DEF_PORT_IRQ;
21982296

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+
21992305
ap->private_data = pp;
22002306

22012307
/* engage engines, captain */

0 commit comments

Comments
 (0)