Re: [PATCH v6 8/8] PCI: endpoint: pci-ep-msi: Add embedded doorbell fallback
Frank Li <[email protected]>
| Newsgroups | dev.linux.lists.ntb,org.kernel.vger.dmaengine,org.kernel.vger.linux-kernel,org.kernel.vger.linux-kselftest,org.kernel.vger.linux-pci |
|---|---|
| Message-ID | <aYtL2rirSjQg7oMC@lizhi-Precision-Tower-5810> |
On Tue, Feb 10, 2026 at 11:10:52AM +0900, Koichiro Den wrote: > On Mon, Feb 09, 2026 at 10:51:56AM -0500, Frank Li wrote: > > On Mon, Feb 09, 2026 at 09:53:16PM +0900, Koichiro Den wrote: > > > pci_epf_alloc_doorbell() currently allocates MSI-backed doorbells using > > > the MSI domain returned by of_msi_map_get_device_domain(..., > > > DOMAIN_BUS_PLATFORM_MSI). On platforms where such an MSI irq domain is > > > not available, EPF drivers cannot provide doorbells to the RC. Even if > > > it's available and MSI device domain successfully created, the write > > > into the message address via BAR inbound mapping might not work for > > > platform-specific reasons (e.g. write into GITS_TRANSLATOR via iATU IB > > > mapping does not reach ITS at least on R-Car Gen4 Spider). > > > > > > Add an "embedded (DMA) doorbell" fallback path that uses EPC-provided > > > auxiliary resources to build doorbell address/data pairs backed by a > > > platform device MMIO region (e.g. dw-edma) and a shared platform IRQ. > > > > > > To let EPF drivers request interrupts correctly, extend struct > > > pci_epf_doorbell_msg with the doorbell type and required IRQ request > > > flags. Update pci-epf-test to handle shared IRQ constraints by using a > > > trivial primary handler to wake the threaded handler, and update > > > pci-epf-vntb to use the required irq_flags. > > > > > > Signed-off-by: Koichiro Den <[email protected]> > > > --- > > > drivers/pci/endpoint/functions/pci-epf-test.c | 29 +++- > > > drivers/pci/endpoint/functions/pci-epf-vntb.c | 3 +- > > > drivers/pci/endpoint/pci-ep-msi.c | 129 ++++++++++++++++-- > > > include/linux/pci-epf.h | 17 ++- > > > 4 files changed, 160 insertions(+), 18 deletions(-) > > > > > > diff --git a/drivers/pci/endpoint/functions/pci-epf-test.c b/drivers/pci/endpoint/functions/pci-epf-test.c > > > index 23034f548c90..2f3b2e6a3e29 100644 > > > --- a/drivers/pci/endpoint/functions/pci-epf-test.c > > > +++ b/drivers/pci/endpoint/functions/pci-epf-test.c > > > @@ -711,6 +711,26 @@ static irqreturn_t pci_epf_test_doorbell_handler(int irq, void *data) > > > return IRQ_HANDLED; > > > } > > > > > > +/* > > > + * Embedded doorbell fallback uses a platform IRQ which is already owned by a > > > + * platform driver (e.g. dw-edma) and therefore must be requested IRQF_SHARED. > > > + * We cannot add IRQF_ONESHOT here because shared IRQ handlers must agree on > > > + * IRQF_ONESHOT. > > > + * > > > + * request_threaded_irq() with handler == NULL would be rejected for !ONESHOT > > > + * because the default primary handler only wakes the thread and does not > > > + * mask/ack the interrupt, which can livelock on level-triggered IRQs. > > > + * > > > + * In the embedded doorbell fallback, the IRQ owner is responsible for > > > + * acknowledging/deasserting the interrupt source in hardirq context before the > > > + * IRQ line is unmasked. Therefore this driver only needs a trivial primary > > > + * handler to wake the threaded handler. > > > + */ > > > +static irqreturn_t pci_epf_test_doorbell_primary(int irq, void *data) > > > +{ > > > + return IRQ_WAKE_THREAD; > > > +} > > > + > > > static void pci_epf_test_doorbell_cleanup(struct pci_epf_test *epf_test) > > > { > > > struct pci_epf_test_reg *reg = epf_test->reg[epf_test->test_reg_bar]; > > > @@ -731,6 +751,7 @@ static void pci_epf_test_enable_doorbell(struct pci_epf_test *epf_test, > > > u32 status = le32_to_cpu(reg->status); > > > struct pci_epf *epf = epf_test->epf; > > > struct pci_epc *epc = epf->epc; > > > + unsigned long irq_flags; > > > struct msi_msg *msg; > > > enum pci_barno bar; > > > size_t offset; > > > @@ -745,10 +766,14 @@ static void pci_epf_test_enable_doorbell(struct pci_epf_test *epf_test, > > > if (bar < BAR_0) > > > goto err_doorbell_cleanup; > > > > > > + irq_flags = epf->db_msg[0].irq_flags; > > > + if (!(irq_flags & IRQF_SHARED)) > > > + irq_flags |= IRQF_ONESHOT; > > > epf_test->db_irq_requested = false; > > > > > > - ret = request_threaded_irq(epf->db_msg[0].virq, NULL, > > > - pci_epf_test_doorbell_handler, IRQF_ONESHOT, > > > + ret = request_threaded_irq(epf->db_msg[0].virq, > > > + pci_epf_test_doorbell_primary, > > > + pci_epf_test_doorbell_handler, irq_flags, > > > "pci-ep-test-doorbell", epf_test); > > > if (ret) { > > > dev_err(&epf->dev, > > > diff --git a/drivers/pci/endpoint/functions/pci-epf-vntb.c b/drivers/pci/endpoint/functions/pci-epf-vntb.c > > > index 3ecc5059f92b..d2fd1e194088 100644 > > > --- a/drivers/pci/endpoint/functions/pci-epf-vntb.c > > > +++ b/drivers/pci/endpoint/functions/pci-epf-vntb.c > > > @@ -535,7 +535,8 @@ static int epf_ntb_db_bar_init_msi_doorbell(struct epf_ntb *ntb, > > > > > > for (i = 0; i < ntb->db_count; i++) { > > > ret = request_irq(epf->db_msg[i].virq, epf_ntb_doorbell_handler, > > > - 0, "pci_epf_vntb_db", ntb); > > > + epf->db_msg[i].irq_flags, "pci_epf_vntb_db", > > > + ntb); > > > > > > if (ret) { > > > dev_err(&epf->dev, > > > diff --git a/drivers/pci/endpoint/pci-ep-msi.c b/drivers/pci/endpoint/pci-ep-msi.c > > > index ad8a81d6ad77..0e93d4789abd 100644 > > > --- a/drivers/pci/endpoint/pci-ep-msi.c > > > +++ b/drivers/pci/endpoint/pci-ep-msi.c > > > @@ -8,6 +8,7 @@ > > > > > > #include <linux/device.h> > > > #include <linux/export.h> > > > +#include <linux/interrupt.h> > > > #include <linux/irqdomain.h> > > > #include <linux/module.h> > > > #include <linux/msi.h> > > > @@ -35,23 +36,84 @@ static void pci_epf_write_msi_msg(struct msi_desc *desc, struct msi_msg *msg) > > > pci_epc_put(epc); > > > } > > > > > > -int pci_epf_alloc_doorbell(struct pci_epf *epf, u16 num_db) > > > +static int pci_epf_alloc_doorbell_embedded(struct pci_epf *epf, u16 num_db) > > > { > > > struct pci_epc *epc = epf->epc; > > > - struct device *dev = &epf->dev; > > > - struct irq_domain *domain; > > > - void *msg; > > > - int ret; > > > - int i; > > > + const struct pci_epc_aux_resource *dma_ctrl = NULL; > > > + struct pci_epf_doorbell_msg *msg; > > > + int count, ret, i, db; > > > > > > - /* TODO: Multi-EPF support */ > > > - if (list_first_entry_or_null(&epc->pci_epf, struct pci_epf, list) != epf) { > > > - dev_err(dev, "MSI doorbell doesn't support multiple EPF\n"); > > > - return -EINVAL; > > > + count = pci_epc_get_aux_resources(epc, epf->func_no, epf->vfunc_no, > > > + NULL, 0); > > > + if (count == -EOPNOTSUPP || count == 0) > > > + return -ENODEV; > > > + if (count < 0) > > > + return count; > > > + > > > + struct pci_epc_aux_resource *res __free(kfree) = > > > + kcalloc(count, sizeof(*res), GFP_KERNEL); > > > + if (!res) > > > + return -ENOMEM; > > > + > > > + ret = pci_epc_get_aux_resources(epc, epf->func_no, epf->vfunc_no, > > > + res, count); > > > + if (ret == -EOPNOTSUPP || ret == 0) > > > + return -ENODEV; > > > + if (ret < 0) > > > + return ret; > > > + > > > + count = ret; > > > + > > > + for (i = 0; i < count; i++) { > > > + if (res[i].type == PCI_EPC_AUX_DMA_CTRL_MMIO) { > > > > I suggest use PCI_EPC_EMBEDED_DOORBELL_MMIO directly because some vendor > > have really doorbell register space. > > > > below addr is that phys_addr + res[i].u.dma_chan_desc.db_offset. So vendor > > can easily change to their doorbell register space in future. > > That makes sense. I can extend enum pci_epc_aux_resource_type with a > dedicated doorbell MMIO resource type and use it, instead of assuming the > embedded doorbell lives inside the DMA control MMIO window > (PCI_EPC_AUX_DMA_CTRL_MMIO). > > With that approach, I plan to drop u.dma_chan_desc.db_offset for now, since > it would always be zero in the current dw-edma v0 case (the INT_STATUS > offset is not channel-specific). If another vendor implementation needs a > channel-specific offset in the future, we can just extend this again when > it's actually needed. > > In short, after your feedback, I'm thinking of the following change (diff > against v6): > > enum pci_epc_aux_resource_type { > PCI_EPC_AUX_DMA_CTRL_MMIO, > PCI_EPC_AUX_DMA_CHAN_DESC, > + PCI_EPC_AUX_DOORBELL_MMIO, Yes, thanks Frank > }; > > struct pci_epc_aux_resource { > enum pci_epc_aux_resource_type type; > phys_addr_t phys_addr; > resource_size_t size; > > union { > /* PCI_EPC_AUX_DMA_CHAN_DESC */ > struct { > int irq; > - resource_size_t db_offset; > } dma_chan_desc; > } u; > }; > > Thanks for the review, > Koichiro > > > > > Frank > > > > > + dma_ctrl = &res[i]; > > > + break; > > > + } > > > + } > > > + if (!dma_ctrl) > > > + return -ENODEV; > > > + > > > + msg = kcalloc(num_db, sizeof(*msg), GFP_KERNEL); > > > + if (!msg) > > > + return -ENOMEM; > > > + > > > + for (i = 0, db = 0; i < count && db < num_db; i++) { > > > + u64 addr; > > > + > > > + if (res[i].type != PCI_EPC_AUX_DMA_CHAN_DESC) > > > + continue; > > > + > > > + if (res[i].u.dma_chan_desc.db_offset >= dma_ctrl->size) > > > + continue; > > > + > > > + addr = (u64)dma_ctrl->phys_addr + res[i].u.dma_chan_desc.db_offset; > > > + > > > + msg[db].msg.address_lo = (u32)addr; > > > + msg[db].msg.address_hi = (u32)(addr >> 32); > > > + msg[db].msg.data = 0; > > > + msg[db].virq = res[i].u.dma_chan_desc.irq; > > > + msg[db].irq_flags = IRQF_SHARED; > > > + msg[db].type = PCI_EPF_DOORBELL_EMBEDDED; > > > + db++; > > > } > > > > > > - if (epf->db_msg) > > > - return -EBUSY; > > > + if (db != num_db) { > > > + kfree(msg); > > > + return -ENOSPC; > > > + } > > > + > > > + epf->num_db = num_db; > > > + epf->db_msg = msg; > > > + return 0; > > > +} > > > + > > > +static int pci_epf_alloc_doorbell_msi(struct pci_epf *epf, u16 num_db) > > > +{ > > > > suggest create patch, which only add helper function > > pci_epf_alloc_doorbell_msi(). > > > > Then add pci_epf_alloc_doorbell_embedded. > > > > Frank > > > + struct pci_epf_doorbell_msg *msg; > > > + struct device *dev = &epf->dev; > > > + struct pci_epc *epc = epf->epc; > > > + struct irq_domain *domain; > > > + int ret, i; > > > > > > domain = of_msi_map_get_device_domain(epc->dev.parent, 0, > > > DOMAIN_BUS_PLATFORM_MSI); > > > @@ -74,6 +136,11 @@ int pci_epf_alloc_doorbell(struct pci_epf *epf, u16 num_db) > > > if (!msg) > > > return -ENOMEM; > > > > > > + for (i = 0; i < num_db; i++) { > > > + msg[i].irq_flags = 0; > > > + msg[i].type = PCI_EPF_DOORBELL_MSI; > > > + } > > > + > > > epf->num_db = num_db; > > > epf->db_msg = msg; > > > > > > @@ -90,13 +157,49 @@ int pci_epf_alloc_doorbell(struct pci_epf *epf, u16 num_db) > > > for (i = 0; i < num_db; i++) > > > epf->db_msg[i].virq = msi_get_virq(epc->dev.parent, i); > > > > > > + return 0; > > > +} > > > + > > > +int pci_epf_alloc_doorbell(struct pci_epf *epf, u16 num_db) > > > +{ > > > + struct pci_epc *epc = epf->epc; > > > + struct device *dev = &epf->dev; > > > + int ret; > > > + > > > + /* TODO: Multi-EPF support */ > > > + if (list_first_entry_or_null(&epc->pci_epf, struct pci_epf, list) != epf) { > > > + dev_err(dev, "Doorbell doesn't support multiple EPF\n"); > > > + return -EINVAL; > > > + } > > > + > > > + if (epf->db_msg) > > > + return -EBUSY; > > > + > > > + ret = pci_epf_alloc_doorbell_msi(epf, num_db); > > > + if (!ret) > > > + return 0; > > > + > > > + if (ret != -ENODEV) > > > + return ret; > > > + > > > + ret = pci_epf_alloc_doorbell_embedded(epf, num_db); > > > + if (!ret) { > > > + dev_info(dev, "Using embedded (DMA) doorbell fallback\n"); > > > + return 0; > > > + } > > > + > > > + dev_err(dev, "Failed to allocate doorbell: %d\n", ret); > > > return ret; > > > } > > > EXPORT_SYMBOL_GPL(pci_epf_alloc_doorbell); > > > > > > void pci_epf_free_doorbell(struct pci_epf *epf) > > > { > > > - platform_device_msi_free_irqs_all(epf->epc->dev.parent); > > > + if (!epf->db_msg) > > > + return; > > > + > > > + if (epf->db_msg[0].type == PCI_EPF_DOORBELL_MSI) > > > + platform_device_msi_free_irqs_all(epf->epc->dev.parent); > > > > > > kfree(epf->db_msg); > > > epf->db_msg = NULL; > > > diff --git a/include/linux/pci-epf.h b/include/linux/pci-epf.h > > > index 7737a7c03260..e6625198f401 100644 > > > --- a/include/linux/pci-epf.h > > > +++ b/include/linux/pci-epf.h > > > @@ -152,14 +152,27 @@ struct pci_epf_bar { > > > struct pci_epf_bar_submap *submap; > > > }; > > > > > > +enum pci_epf_doorbell_type { > > > + PCI_EPF_DOORBELL_MSI = 0, > > > + PCI_EPF_DOORBELL_EMBEDDED, > > > +}; > > > + > > > /** > > > * struct pci_epf_doorbell_msg - represents doorbell message > > > - * @msg: MSI message > > > - * @virq: IRQ number of this doorbell MSI message > > > + * @msg: Doorbell address/data pair to be mapped into BAR space. > > > + * For MSI-backed doorbells this is the MSI message, while for > > > + * "embedded" doorbells this represents an MMIO write that asserts > > > + * an interrupt on the EP side. > > > + * @virq: IRQ number of this doorbell message > > > + * @irq_flags: Required flags for request_irq()/request_threaded_irq(). > > > + * Callers may OR-in additional flags (e.g. IRQF_ONESHOT). > > > + * @type: Doorbell type. > > > */ > > > struct pci_epf_doorbell_msg { > > > struct msi_msg msg; > > > int virq; > > > + unsigned long irq_flags; > > > + enum pci_epf_doorbell_type type; > > > }; > > > > > > /** > > > -- > > > 2.51.0 > > >