diff options
author | Christoph Hellwig <hch@lst.de> | 2016-09-05 11:21:45 -0400 |
---|---|---|
committer | Tejun Heo <tj@kernel.org> | 2016-09-06 12:38:17 -0400 |
commit | 0b9e2988ab2261fd6d4a0039edf81ed1e3662be8 (patch) | |
tree | 75dbefd4076fca402d2c62b4e62bd0e4469439e0 /drivers/ata | |
parent | 2536524a91fe5c5a9fddd282fd4e79ee0976aefe (diff) |
ahci: use pci_alloc_irq_vectors
Use the new pci_alloc_irq_vectors API to allocate MSI-X and MSI vectors.
The big advantage over the old code is that we can use the same API for
MSI and MSI-X, and that we don't need to store the MSI-X vector mapping
in driver-private data structures.
This first conversion keeps the probe order as-is: MSI-X multi vector,
MSI multi vector, MSI single vector, MSI-X single vector and last a
single least legacy interrupt line. There is one small change of
behavior: we now check the "MSI Revert to Single Message" flag for
MSI-X in addition to MSI.
Because the API to find the Linux IRQ number for a MSI/MSI-X vector
is PCI specific, but libahaci is bus-agnostic I had to a
get_irq_vector function pointer to struct ahci_host_priv. The
alternative would be to move the multi-vector case of ahci_host_activate
to ahci.c and just call ata_host_activate directly from the others
users of ahci_host_activate.
Signed-off-by: Christoph Hellwig <hch@lst.de>
Signed-off-by: Tejun Heo <tj@kernel.org>
Diffstat (limited to 'drivers/ata')
-rw-r--r-- | drivers/ata/ahci.c | 149 | ||||
-rw-r--r-- | drivers/ata/ahci.h | 24 | ||||
-rw-r--r-- | drivers/ata/libahci.c | 11 |
3 files changed, 45 insertions, 139 deletions
diff --git a/drivers/ata/ahci.c b/drivers/ata/ahci.c index 90eabaf81215..ba5f11cebee2 100644 --- a/drivers/ata/ahci.c +++ b/drivers/ata/ahci.c | |||
@@ -1400,142 +1400,56 @@ static irqreturn_t ahci_thunderx_irq_handler(int irq, void *dev_instance) | |||
1400 | } | 1400 | } |
1401 | #endif | 1401 | #endif |
1402 | 1402 | ||
1403 | /* | 1403 | static int ahci_get_irq_vector(struct ata_host *host, int port) |
1404 | * ahci_init_msix() - optionally enable per-port MSI-X otherwise defer | ||
1405 | * to single msi. | ||
1406 | */ | ||
1407 | static int ahci_init_msix(struct pci_dev *pdev, unsigned int n_ports, | ||
1408 | struct ahci_host_priv *hpriv, unsigned long flags) | ||
1409 | { | 1404 | { |
1410 | int nvec, i, rc; | 1405 | return pci_irq_vector(to_pci_dev(host->dev), port); |
1411 | |||
1412 | /* Do not init MSI-X if MSI is disabled for the device */ | ||
1413 | if (hpriv->flags & AHCI_HFLAG_NO_MSI) | ||
1414 | return -ENODEV; | ||
1415 | |||
1416 | nvec = pci_msix_vec_count(pdev); | ||
1417 | if (nvec < 0) | ||
1418 | return nvec; | ||
1419 | |||
1420 | /* | ||
1421 | * Proper MSI-X implementations will have a vector per-port. | ||
1422 | * Barring that, we prefer single-MSI over single-MSIX. If this | ||
1423 | * check fails (not enough MSI-X vectors for all ports) we will | ||
1424 | * be called again with the flag clear iff ahci_init_msi() | ||
1425 | * fails. | ||
1426 | */ | ||
1427 | if (flags & AHCI_HFLAG_MULTI_MSIX) { | ||
1428 | if (nvec < n_ports) | ||
1429 | return -ENODEV; | ||
1430 | nvec = n_ports; | ||
1431 | } else if (nvec) { | ||
1432 | nvec = 1; | ||
1433 | } else { | ||
1434 | /* | ||
1435 | * Emit dev_err() since this was the non-legacy irq | ||
1436 | * method of last resort. | ||
1437 | */ | ||
1438 | rc = -ENODEV; | ||
1439 | goto fail; | ||
1440 | } | ||
1441 | |||
1442 | for (i = 0; i < nvec; i++) | ||
1443 | hpriv->msix[i].entry = i; | ||
1444 | rc = pci_enable_msix_exact(pdev, hpriv->msix, nvec); | ||
1445 | if (rc < 0) | ||
1446 | goto fail; | ||
1447 | |||
1448 | if (nvec > 1) | ||
1449 | hpriv->flags |= AHCI_HFLAG_MULTI_MSIX; | ||
1450 | hpriv->irq = hpriv->msix[0].vector; /* for single msi-x */ | ||
1451 | |||
1452 | return nvec; | ||
1453 | fail: | ||
1454 | dev_err(&pdev->dev, | ||
1455 | "failed to enable MSI-X with error %d, # of vectors: %d\n", | ||
1456 | rc, nvec); | ||
1457 | |||
1458 | return rc; | ||
1459 | } | 1406 | } |
1460 | 1407 | ||
1461 | static int ahci_init_msi(struct pci_dev *pdev, unsigned int n_ports, | 1408 | static int ahci_init_msi(struct pci_dev *pdev, unsigned int n_ports, |
1462 | struct ahci_host_priv *hpriv) | 1409 | struct ahci_host_priv *hpriv) |
1463 | { | 1410 | { |
1464 | int rc, nvec; | 1411 | int nvec; |
1465 | 1412 | ||
1466 | if (hpriv->flags & AHCI_HFLAG_NO_MSI) | 1413 | if (hpriv->flags & AHCI_HFLAG_NO_MSI) |
1467 | return -ENODEV; | 1414 | return -ENODEV; |
1468 | 1415 | ||
1469 | nvec = pci_msi_vec_count(pdev); | ||
1470 | if (nvec < 0) | ||
1471 | return nvec; | ||
1472 | |||
1473 | /* | 1416 | /* |
1474 | * If number of MSIs is less than number of ports then Sharing Last | 1417 | * If number of MSIs is less than number of ports then Sharing Last |
1475 | * Message mode could be enforced. In this case assume that advantage | 1418 | * Message mode could be enforced. In this case assume that advantage |
1476 | * of multipe MSIs is negated and use single MSI mode instead. | 1419 | * of multipe MSIs is negated and use single MSI mode instead. |
1477 | */ | 1420 | */ |
1478 | if (nvec < n_ports) | 1421 | nvec = pci_alloc_irq_vectors(pdev, n_ports, INT_MAX, |
1479 | goto single_msi; | 1422 | PCI_IRQ_MSIX | PCI_IRQ_MSI); |
1480 | 1423 | if (nvec > 0) { | |
1481 | rc = pci_enable_msi_exact(pdev, nvec); | 1424 | if (!(readl(hpriv->mmio + HOST_CTL) & HOST_MRSM)) { |
1482 | if (rc == -ENOSPC) | 1425 | hpriv->get_irq_vector = ahci_get_irq_vector; |
1483 | goto single_msi; | 1426 | hpriv->flags |= AHCI_HFLAG_MULTI_MSI; |
1484 | if (rc < 0) | 1427 | return nvec; |
1485 | return rc; | 1428 | } |
1486 | 1429 | ||
1487 | /* fallback to single MSI mode if the controller enforced MRSM mode */ | 1430 | /* |
1488 | if (readl(hpriv->mmio + HOST_CTL) & HOST_MRSM) { | 1431 | * Fallback to single MSI mode if the controller enforced MRSM |
1489 | pci_disable_msi(pdev); | 1432 | * mode. |
1433 | */ | ||
1490 | printk(KERN_INFO "ahci: MRSM is on, fallback to single MSI\n"); | 1434 | printk(KERN_INFO "ahci: MRSM is on, fallback to single MSI\n"); |
1491 | goto single_msi; | 1435 | pci_free_irq_vectors(pdev); |
1492 | } | 1436 | } |
1493 | 1437 | ||
1494 | if (nvec > 1) | ||
1495 | hpriv->flags |= AHCI_HFLAG_MULTI_MSI; | ||
1496 | |||
1497 | goto out; | ||
1498 | |||
1499 | single_msi: | ||
1500 | nvec = 1; | ||
1501 | |||
1502 | rc = pci_enable_msi(pdev); | ||
1503 | if (rc < 0) | ||
1504 | return rc; | ||
1505 | out: | ||
1506 | hpriv->irq = pdev->irq; | ||
1507 | |||
1508 | return nvec; | ||
1509 | } | ||
1510 | |||
1511 | static int ahci_init_interrupts(struct pci_dev *pdev, unsigned int n_ports, | ||
1512 | struct ahci_host_priv *hpriv) | ||
1513 | { | ||
1514 | int nvec; | ||
1515 | |||
1516 | /* | 1438 | /* |
1517 | * Try to enable per-port MSI-X. If the host is not capable | 1439 | * -ENOSPC indicated we don't have enough vectors. Don't bother trying |
1518 | * fall back to single MSI before finally attempting single | 1440 | * a single vectors for any other error: |
1519 | * MSI-X. | ||
1520 | */ | 1441 | */ |
1521 | nvec = ahci_init_msix(pdev, n_ports, hpriv, AHCI_HFLAG_MULTI_MSIX); | 1442 | if (nvec < 0 && nvec != -ENOSPC) |
1522 | if (nvec >= 0) | ||
1523 | return nvec; | 1443 | return nvec; |
1524 | 1444 | ||
1525 | nvec = ahci_init_msi(pdev, n_ports, hpriv); | 1445 | /* |
1526 | if (nvec >= 0) | 1446 | * If the host is not capable of supporting per-port vectors, fall |
1527 | return nvec; | 1447 | * back to single MSI before finally attempting single MSI-X. |
1528 | 1448 | */ | |
1529 | /* try single-msix */ | 1449 | nvec = pci_alloc_irq_vectors(pdev, 1, 1, PCI_IRQ_MSI); |
1530 | nvec = ahci_init_msix(pdev, n_ports, hpriv, 0); | 1450 | if (nvec == 1) |
1531 | if (nvec >= 0) | ||
1532 | return nvec; | 1451 | return nvec; |
1533 | 1452 | return pci_alloc_irq_vectors(pdev, 1, 1, PCI_IRQ_MSIX); | |
1534 | /* legacy intx interrupts */ | ||
1535 | pci_intx(pdev, 1); | ||
1536 | hpriv->irq = pdev->irq; | ||
1537 | |||
1538 | return 0; | ||
1539 | } | 1453 | } |
1540 | 1454 | ||
1541 | static int ahci_init_one(struct pci_dev *pdev, const struct pci_device_id *ent) | 1455 | static int ahci_init_one(struct pci_dev *pdev, const struct pci_device_id *ent) |
@@ -1698,11 +1612,12 @@ static int ahci_init_one(struct pci_dev *pdev, const struct pci_device_id *ent) | |||
1698 | if (!host) | 1612 | if (!host) |
1699 | return -ENOMEM; | 1613 | return -ENOMEM; |
1700 | host->private_data = hpriv; | 1614 | host->private_data = hpriv; |
1701 | hpriv->msix = devm_kzalloc(&pdev->dev, | 1615 | |
1702 | sizeof(struct msix_entry) * n_ports, GFP_KERNEL); | 1616 | if (ahci_init_msi(pdev, n_ports, hpriv) < 0) { |
1703 | if (!hpriv->msix) | 1617 | /* legacy intx interrupts */ |
1704 | return -ENOMEM; | 1618 | pci_intx(pdev, 1); |
1705 | ahci_init_interrupts(pdev, n_ports, hpriv); | 1619 | } |
1620 | hpriv->irq = pdev->irq; | ||
1706 | 1621 | ||
1707 | if (!(hpriv->cap & HOST_CAP_SSS) || ahci_ignore_sss) | 1622 | if (!(hpriv->cap & HOST_CAP_SSS) || ahci_ignore_sss) |
1708 | host->flags |= ATA_HOST_PARALLEL_SCAN; | 1623 | host->flags |= ATA_HOST_PARALLEL_SCAN; |
diff --git a/drivers/ata/ahci.h b/drivers/ata/ahci.h index 70b06bcfb7e3..0cc08f892fea 100644 --- a/drivers/ata/ahci.h +++ b/drivers/ata/ahci.h | |||
@@ -242,12 +242,10 @@ enum { | |||
242 | AHCI_HFLAG_NO_FBS = (1 << 18), /* no FBS */ | 242 | AHCI_HFLAG_NO_FBS = (1 << 18), /* no FBS */ |
243 | 243 | ||
244 | #ifdef CONFIG_PCI_MSI | 244 | #ifdef CONFIG_PCI_MSI |
245 | AHCI_HFLAG_MULTI_MSI = (1 << 20), /* multiple PCI MSIs */ | 245 | AHCI_HFLAG_MULTI_MSI = (1 << 20), /* per-port MSI(-X) */ |
246 | AHCI_HFLAG_MULTI_MSIX = (1 << 21), /* per-port MSI-X */ | ||
247 | #else | 246 | #else |
248 | /* compile out MSI infrastructure */ | 247 | /* compile out MSI infrastructure */ |
249 | AHCI_HFLAG_MULTI_MSI = 0, | 248 | AHCI_HFLAG_MULTI_MSI = 0, |
250 | AHCI_HFLAG_MULTI_MSIX = 0, | ||
251 | #endif | 249 | #endif |
252 | AHCI_HFLAG_WAKE_BEFORE_STOP = (1 << 22), /* wake before DMA stop */ | 250 | AHCI_HFLAG_WAKE_BEFORE_STOP = (1 << 22), /* wake before DMA stop */ |
253 | 251 | ||
@@ -351,7 +349,6 @@ struct ahci_host_priv { | |||
351 | * the PHY position in this array. | 349 | * the PHY position in this array. |
352 | */ | 350 | */ |
353 | struct phy **phys; | 351 | struct phy **phys; |
354 | struct msix_entry *msix; /* Optional MSI-X support */ | ||
355 | unsigned nports; /* Number of ports */ | 352 | unsigned nports; /* Number of ports */ |
356 | void *plat_data; /* Other platform data */ | 353 | void *plat_data; /* Other platform data */ |
357 | unsigned int irq; /* interrupt line */ | 354 | unsigned int irq; /* interrupt line */ |
@@ -362,22 +359,11 @@ struct ahci_host_priv { | |||
362 | */ | 359 | */ |
363 | void (*start_engine)(struct ata_port *ap); | 360 | void (*start_engine)(struct ata_port *ap); |
364 | irqreturn_t (*irq_handler)(int irq, void *dev_instance); | 361 | irqreturn_t (*irq_handler)(int irq, void *dev_instance); |
365 | }; | ||
366 | 362 | ||
367 | #ifdef CONFIG_PCI_MSI | 363 | /* only required for per-port MSI(-X) support */ |
368 | static inline int ahci_irq_vector(struct ahci_host_priv *hpriv, int port) | 364 | int (*get_irq_vector)(struct ata_host *host, |
369 | { | 365 | int port); |
370 | if (hpriv->flags & AHCI_HFLAG_MULTI_MSIX) | 366 | }; |
371 | return hpriv->msix[port].vector; | ||
372 | else | ||
373 | return hpriv->irq + port; | ||
374 | } | ||
375 | #else | ||
376 | static inline int ahci_irq_vector(struct ahci_host_priv *hpriv, int port) | ||
377 | { | ||
378 | return hpriv->irq; | ||
379 | } | ||
380 | #endif | ||
381 | 367 | ||
382 | extern int ahci_ignore_sss; | 368 | extern int ahci_ignore_sss; |
383 | 369 | ||
diff --git a/drivers/ata/libahci.c b/drivers/ata/libahci.c index 5a1329e31609..0d028ead99e8 100644 --- a/drivers/ata/libahci.c +++ b/drivers/ata/libahci.c | |||
@@ -2378,7 +2378,7 @@ static int ahci_port_start(struct ata_port *ap) | |||
2378 | /* | 2378 | /* |
2379 | * Switch to per-port locking in case each port has its own MSI vector. | 2379 | * Switch to per-port locking in case each port has its own MSI vector. |
2380 | */ | 2380 | */ |
2381 | if (hpriv->flags & (AHCI_HFLAG_MULTI_MSI | AHCI_HFLAG_MULTI_MSIX)) { | 2381 | if (hpriv->flags & AHCI_HFLAG_MULTI_MSI) { |
2382 | spin_lock_init(&pp->lock); | 2382 | spin_lock_init(&pp->lock); |
2383 | ap->lock = &pp->lock; | 2383 | ap->lock = &pp->lock; |
2384 | } | 2384 | } |
@@ -2520,7 +2520,7 @@ static int ahci_host_activate_multi_irqs(struct ata_host *host, | |||
2520 | */ | 2520 | */ |
2521 | for (i = 0; i < host->n_ports; i++) { | 2521 | for (i = 0; i < host->n_ports; i++) { |
2522 | struct ahci_port_priv *pp = host->ports[i]->private_data; | 2522 | struct ahci_port_priv *pp = host->ports[i]->private_data; |
2523 | int irq = ahci_irq_vector(hpriv, i); | 2523 | int irq = hpriv->get_irq_vector(host, i); |
2524 | 2524 | ||
2525 | /* Do not receive interrupts sent by dummy ports */ | 2525 | /* Do not receive interrupts sent by dummy ports */ |
2526 | if (!pp) { | 2526 | if (!pp) { |
@@ -2556,10 +2556,15 @@ int ahci_host_activate(struct ata_host *host, struct scsi_host_template *sht) | |||
2556 | int irq = hpriv->irq; | 2556 | int irq = hpriv->irq; |
2557 | int rc; | 2557 | int rc; |
2558 | 2558 | ||
2559 | if (hpriv->flags & (AHCI_HFLAG_MULTI_MSI | AHCI_HFLAG_MULTI_MSIX)) { | 2559 | if (hpriv->flags & AHCI_HFLAG_MULTI_MSI) { |
2560 | if (hpriv->irq_handler) | 2560 | if (hpriv->irq_handler) |
2561 | dev_warn(host->dev, | 2561 | dev_warn(host->dev, |
2562 | "both AHCI_HFLAG_MULTI_MSI flag set and custom irq handler implemented\n"); | 2562 | "both AHCI_HFLAG_MULTI_MSI flag set and custom irq handler implemented\n"); |
2563 | if (!hpriv->get_irq_vector) { | ||
2564 | dev_err(host->dev, | ||
2565 | "AHCI_HFLAG_MULTI_MSI requires ->get_irq_vector!\n"); | ||
2566 | return -EIO; | ||
2567 | } | ||
2563 | 2568 | ||
2564 | rc = ahci_host_activate_multi_irqs(host, sht); | 2569 | rc = ahci_host_activate_multi_irqs(host, sht); |
2565 | } else { | 2570 | } else { |