Skip to content

Commit 8136e68

Browse files
author
Fox Snowpatch
committed
1 parent 27527b2 commit 8136e68

1 file changed

Lines changed: 41 additions & 3 deletions

File tree

  • arch/powerpc/platforms/pseries

arch/powerpc/platforms/pseries/msi.c

Lines changed: 41 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,11 @@
1919

2020
#include "pseries.h"
2121

22+
struct pseries_msi_device {
23+
unsigned int msi_quota;
24+
unsigned int msi_used;
25+
};
26+
2227
static int query_token, change_token;
2328

2429
#define RTAS_QUERY_FN 0
@@ -433,8 +438,28 @@ static int pseries_msi_ops_prepare(struct irq_domain *domain, struct device *dev
433438
struct msi_domain_info *info = domain->host_data;
434439
struct pci_dev *pdev = to_pci_dev(dev);
435440
int type = (info->flags & MSI_FLAG_PCI_MSIX) ? PCI_CAP_ID_MSIX : PCI_CAP_ID_MSI;
441+
int ret;
442+
443+
struct pseries_msi_device *pseries_dev __free(kfree)
444+
= kmalloc(sizeof(*pseries_dev), GFP_KERNEL);
445+
if (!pseries_dev)
446+
return -ENOMEM;
447+
448+
while (1) {
449+
ret = rtas_prepare_msi_irqs(pdev, nvec, type, arg);
450+
if (!ret)
451+
break;
452+
else if (ret > 0)
453+
nvec = ret;
454+
else
455+
return ret;
456+
}
436457

437-
return rtas_prepare_msi_irqs(pdev, nvec, type, arg);
458+
pseries_dev->msi_quota = nvec;
459+
pseries_dev->msi_used = 0;
460+
461+
arg->scratchpad[0].ptr = no_free_ptr(pseries_dev);
462+
return 0;
438463
}
439464

440465
/*
@@ -443,9 +468,13 @@ static int pseries_msi_ops_prepare(struct irq_domain *domain, struct device *dev
443468
*/
444469
static void pseries_msi_ops_teardown(struct irq_domain *domain, msi_alloc_info_t *arg)
445470
{
471+
struct pseries_msi_device *pseries_dev = arg->scratchpad[0].ptr;
446472
struct pci_dev *pdev = to_pci_dev(domain->dev);
447473

448474
rtas_disable_msi(pdev);
475+
476+
WARN_ON(pseries_dev->msi_used);
477+
kfree(pseries_dev);
449478
}
450479

451480
static void pseries_msi_shutdown(struct irq_data *d)
@@ -546,12 +575,18 @@ static int pseries_irq_domain_alloc(struct irq_domain *domain, unsigned int virq
546575
unsigned int nr_irqs, void *arg)
547576
{
548577
struct pci_controller *phb = domain->host_data;
578+
struct pseries_msi_device *pseries_dev;
549579
msi_alloc_info_t *info = arg;
550580
struct msi_desc *desc = info->desc;
551581
struct pci_dev *pdev = msi_desc_to_pci_dev(desc);
552582
int hwirq;
553583
int i, ret;
554584

585+
pseries_dev = info->scratchpad[0].ptr;
586+
587+
if (pseries_dev->msi_used + nr_irqs > pseries_dev->msi_quota)
588+
return -ENOSPC;
589+
555590
hwirq = rtas_query_irq_number(pci_get_pdn(pdev), desc->msi_index);
556591
if (hwirq < 0) {
557592
dev_err(&pdev->dev, "Failed to query HW IRQ: %d\n", hwirq);
@@ -567,9 +602,10 @@ static int pseries_irq_domain_alloc(struct irq_domain *domain, unsigned int virq
567602
goto out;
568603

569604
irq_domain_set_hwirq_and_chip(domain, virq + i, hwirq + i,
570-
&pseries_msi_irq_chip, domain->host_data);
605+
&pseries_msi_irq_chip, pseries_dev);
571606
}
572607

608+
pseries_dev->msi_used++;
573609
return 0;
574610

575611
out:
@@ -582,9 +618,11 @@ static void pseries_irq_domain_free(struct irq_domain *domain, unsigned int virq
582618
unsigned int nr_irqs)
583619
{
584620
struct irq_data *d = irq_domain_get_irq_data(domain, virq);
585-
struct pci_controller *phb = irq_data_get_irq_chip_data(d);
621+
struct pseries_msi_device *pseries_dev = irq_data_get_irq_chip_data(d);
622+
struct pci_controller *phb = domain->host_data;
586623

587624
pr_debug("%s bridge %pOF %d #%d\n", __func__, phb->dn, virq, nr_irqs);
625+
pseries_dev->msi_used -= nr_irqs;
588626
irq_domain_free_irqs_parent(domain, virq, nr_irqs);
589627
}
590628

0 commit comments

Comments
 (0)