On Fri, Feb 06, 2026 at 02:20:58PM -0500, Frank Li wrote: > On Sat, Feb 07, 2026 at 02:26:44AM +0900, Koichiro Den wrote: > > Extend pci-epf-test with an "embedded doorbell" variant that does not > > rely on the EPC doorbell/MSI mechanism. > > > > When the host sets FLAG_DB_EMBEDDED, query EPC remote resources to > > locate the embedded DMA MMIO window and a per-channel > > interrupt-emulation doorbell register offset. Map the MMIO window into a > > free BAR and return BAR+offset to the host as the doorbell target. > > > > Handle the resulting shared IRQ by deferring completion signalling to a > > work item, then update the test status and raise the completion IRQ back > > to the host. > > > > The existing MSI doorbell remains the default when FLAG_DB_EMBEDDED is > > not set. > > > > Signed-off-by: Koichiro Den <[email protected]> > > --- > > Can you change pci_epf_alloc_doorbell() directly? Let it fall back to > edma implement.
Thanks for the suggestion. Yes, while I think doing so would require some extra care and constraints for the users (i.e. the pci_epf_alloc_doorbell() -> request_irq flow). Also, separate testing would no longer be possible, but I think it's ok, because choosing the "fake irq" when a normal MSI doorbell is available would not be very useful anyway. For the fallback case, the interrupt source is not a normal MSI doorbell, but a level-triggered, shared platform IRQ. Due to that, the caller needs to know how the IRQ can be requested safely (e.g. usable IRQF_* flags, or whether a primary handler is required for request_threaded_irq). I think we need to expose such hints via pci_epf_doorbell_msg by adding new fields (e.g. doorbell type, IRQ flags to be OR-ed), and have pci_epf_alloc_doorbell() fill them in, rather than completely hiding the fallback internally. Let me send v5 accordingly. Please let me know if you see any issues. Koichiro > > Frank > > > drivers/pci/endpoint/functions/pci-epf-test.c | 193 +++++++++++++++++- > > 1 file changed, 185 insertions(+), 8 deletions(-) > > > > diff --git a/drivers/pci/endpoint/functions/pci-epf-test.c > > b/drivers/pci/endpoint/functions/pci-epf-test.c > > index 6952ee418622..5871da8cbddf 100644 > > --- a/drivers/pci/endpoint/functions/pci-epf-test.c > > +++ b/drivers/pci/endpoint/functions/pci-epf-test.c > > @@ -6,6 +6,7 @@ > > * Author: Kishon Vijay Abraham I <[email protected]> > > */ > > > > +#include <linux/bitops.h> > > #include <linux/crc32.h> > > #include <linux/delay.h> > > #include <linux/dmaengine.h> > > @@ -56,6 +57,7 @@ > > #define STATUS_BAR_SUBRANGE_CLEAR_FAIL BIT(17) > > > > #define FLAG_USE_DMA BIT(0) > > +#define FLAG_DB_EMBEDDED BIT(1) > > > > #define TIMER_RESOLUTION 1 > > > > @@ -69,6 +71,12 @@ > > > > static struct workqueue_struct *kpcitest_workqueue; > > > > +enum pci_epf_test_doorbell_variant { > > + PCI_EPF_TEST_DB_NONE = 0, > > + PCI_EPF_TEST_DB_MSI, > > + PCI_EPF_TEST_DB_EMBEDDED, > > +}; > > + > > struct pci_epf_test { > > void *reg[PCI_STD_NUM_BARS]; > > struct pci_epf *epf; > > @@ -85,7 +93,11 @@ struct pci_epf_test { > > bool dma_supported; > > bool dma_private; > > const struct pci_epc_features *epc_features; > > + enum pci_epf_test_doorbell_variant db_variant; > > struct pci_epf_bar db_bar; > > + int db_irq; > > + unsigned long db_irq_pending; > > + struct work_struct db_work; > > size_t bar_size[PCI_STD_NUM_BARS]; > > }; > > > > @@ -696,7 +708,7 @@ static void pci_epf_test_raise_irq(struct pci_epf_test > > *epf_test, > > } > > } > > > > -static irqreturn_t pci_epf_test_doorbell_handler(int irq, void *data) > > +static irqreturn_t pci_epf_test_doorbell_msi_handler(int irq, void *data) > > { > > struct pci_epf_test *epf_test = data; > > enum pci_barno test_reg_bar = epf_test->test_reg_bar; > > @@ -710,19 +722,58 @@ static irqreturn_t pci_epf_test_doorbell_handler(int > > irq, void *data) > > return IRQ_HANDLED; > > } > > > > +static void pci_epf_test_doorbell_embedded_work(struct work_struct *work) > > +{ > > + struct pci_epf_test *epf_test = > > + container_of(work, struct pci_epf_test, db_work); > > + enum pci_barno test_reg_bar = epf_test->test_reg_bar; > > + struct pci_epf_test_reg *reg = epf_test->reg[test_reg_bar]; > > + u32 status = le32_to_cpu(reg->status); > > + > > + status |= STATUS_DOORBELL_SUCCESS; > > + reg->status = cpu_to_le32(status); > > + pci_epf_test_raise_irq(epf_test, reg); > > + > > + clear_bit(0, &epf_test->db_irq_pending); > > +} > > + > > +static irqreturn_t pci_epf_test_doorbell_embedded_irq_handler(int irq, > > void *data) > > +{ > > + struct pci_epf_test *epf_test = data; > > + > > + if (READ_ONCE(epf_test->db_variant) != PCI_EPF_TEST_DB_EMBEDDED) > > + return IRQ_NONE; > > + > > + if (test_and_set_bit(0, &epf_test->db_irq_pending)) > > + return IRQ_HANDLED; > > + > > + queue_work(kpcitest_workqueue, &epf_test->db_work); > > + return IRQ_HANDLED; > > +} > > + > > 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]; > > struct pci_epf *epf = epf_test->epf; > > > > - free_irq(epf->db_msg[0].virq, epf_test); > > - reg->doorbell_bar = cpu_to_le32(NO_BAR); > > + if (epf_test->db_irq) { > > + free_irq(epf_test->db_irq, epf_test); > > + epf_test->db_irq = 0; > > + } > > + > > + if (epf_test->db_variant == PCI_EPF_TEST_DB_EMBEDDED) { > > + cancel_work_sync(&epf_test->db_work); > > + clear_bit(0, &epf_test->db_irq_pending); > > + } else if (epf_test->db_variant == PCI_EPF_TEST_DB_MSI) { > > + pci_epf_free_doorbell(epf); > > + } > > > > - pci_epf_free_doorbell(epf); > > + reg->doorbell_bar = cpu_to_le32(NO_BAR); > > + epf_test->db_variant = PCI_EPF_TEST_DB_NONE; > > } > > > > -static void pci_epf_test_enable_doorbell(struct pci_epf_test *epf_test, > > - struct pci_epf_test_reg *reg) > > +static void pci_epf_test_enable_doorbell_msi(struct pci_epf_test *epf_test, > > + struct pci_epf_test_reg *reg) > > { > > u32 status = le32_to_cpu(reg->status); > > struct pci_epf *epf = epf_test->epf; > > @@ -736,20 +787,23 @@ static void pci_epf_test_enable_doorbell(struct > > pci_epf_test *epf_test, > > if (ret) > > goto set_status_err; > > > > + epf_test->db_variant = PCI_EPF_TEST_DB_MSI; > > msg = &epf->db_msg[0].msg; > > bar = pci_epc_get_next_free_bar(epf_test->epc_features, > > epf_test->test_reg_bar + 1); > > if (bar < BAR_0) > > goto err_doorbell_cleanup; > > > > ret = request_threaded_irq(epf->db_msg[0].virq, NULL, > > - pci_epf_test_doorbell_handler, IRQF_ONESHOT, > > - "pci-ep-test-doorbell", epf_test); > > + pci_epf_test_doorbell_msi_handler, > > + IRQF_ONESHOT, "pci-ep-test-doorbell", > > + epf_test); > > if (ret) { > > dev_err(&epf->dev, > > "Failed to request doorbell IRQ: %d\n", > > epf->db_msg[0].virq); > > goto err_doorbell_cleanup; > > } > > + epf_test->db_irq = epf->db_msg[0].virq; > > > > reg->doorbell_data = cpu_to_le32(msg->data); > > reg->doorbell_bar = cpu_to_le32(bar); > > @@ -782,6 +836,125 @@ static void pci_epf_test_enable_doorbell(struct > > pci_epf_test *epf_test, > > reg->status = cpu_to_le32(status); > > } > > > > +static void pci_epf_test_enable_doorbell_embedded(struct pci_epf_test > > *epf_test, > > + struct pci_epf_test_reg *reg) > > +{ > > + struct pci_epc_remote_resource *dma_ctrl = NULL, *chan0 = NULL; > > + const char *irq_name = "pci-ep-test-doorbell-embedded"; > > + u32 status = le32_to_cpu(reg->status); > > + struct pci_epf *epf = epf_test->epf; > > + struct pci_epc *epc = epf->epc; > > + struct device *dev = &epf->dev; > > + enum pci_barno bar; > > + size_t align_off; > > + unsigned int i; > > + int cnt, ret; > > + u32 db_off; > > + > > + cnt = pci_epc_get_remote_resources(epc, epf->func_no, epf->vfunc_no, > > + NULL, 0); > > + if (cnt <= 0) { > > + dev_err(dev, "No remote resources available for embedded > > doorbell\n"); > > + goto set_status_err; > > + } > > + > > + struct pci_epc_remote_resource *resources __free(kfree) = > > + kcalloc(cnt, sizeof(*resources), GFP_KERNEL); > > + if (!resources) > > + goto set_status_err; > > + > > + ret = pci_epc_get_remote_resources(epc, epf->func_no, epf->vfunc_no, > > + resources, cnt); > > + if (ret < 0) { > > + dev_err(dev, "Failed to get remote resources: %d\n", ret); > > + goto set_status_err; > > + } > > + cnt = ret; > > + > > + for (i = 0; i < cnt; i++) { > > + if (resources[i].type == PCI_EPC_RR_DMA_CTRL_MMIO) > > + dma_ctrl = &resources[i]; > > + else if (resources[i].type == PCI_EPC_RR_DMA_CHAN_DESC && > > + !chan0) > > + chan0 = &resources[i]; > > + } > > + > > + if (!dma_ctrl || !chan0) { > > + dev_err(dev, "Missing DMA ctrl MMIO or channel #0 info\n"); > > + goto set_status_err; > > + } > > + > > + bar = pci_epc_get_next_free_bar(epf_test->epc_features, > > + epf_test->test_reg_bar + 1); > > + if (bar < BAR_0) { > > + dev_err(dev, "No free BAR for embedded doorbell\n"); > > + goto set_status_err; > > + } > > + > > + ret = pci_epf_align_inbound_addr(epf, bar, dma_ctrl->phys_addr, > > + &epf_test->db_bar.phys_addr, > > + &align_off); > > + if (ret) > > + goto set_status_err; > > + > > + db_off = chan0->u.dma_chan_desc.db_offset; > > + if (db_off >= dma_ctrl->size || > > + align_off + db_off >= epf->bar[bar].size) { > > + dev_err(dev, "BAR%d too small for embedded doorbell (off %#zx + > > %#x)\n", > > + bar, align_off, db_off); > > + goto set_status_err; > > + } > > + > > + epf_test->db_variant = PCI_EPF_TEST_DB_EMBEDDED; > > + > > + ret = request_irq(chan0->u.dma_chan_desc.irq, > > + pci_epf_test_doorbell_embedded_irq_handler, > > + IRQF_SHARED, irq_name, epf_test); > > + if (ret) { > > + dev_err(dev, "Failed to request embedded doorbell IRQ: %d\n", > > + chan0->u.dma_chan_desc.irq); > > + goto err_cleanup; > > + } > > + epf_test->db_irq = chan0->u.dma_chan_desc.irq; > > + > > + reg->doorbell_data = cpu_to_le32(0); > > + reg->doorbell_bar = cpu_to_le32(bar); > > + reg->doorbell_offset = cpu_to_le32(align_off + db_off); > > + > > + epf_test->db_bar.barno = bar; > > + epf_test->db_bar.size = epf->bar[bar].size; > > + epf_test->db_bar.flags = epf->bar[bar].flags; > > + > > + ret = pci_epc_set_bar(epc, epf->func_no, epf->vfunc_no, > > &epf_test->db_bar); > > + if (ret) > > + goto err_cleanup; > > + > > + status |= STATUS_DOORBELL_ENABLE_SUCCESS; > > + reg->status = cpu_to_le32(status); > > + return; > > + > > +err_cleanup: > > + pci_epf_test_doorbell_cleanup(epf_test); > > +set_status_err: > > + status |= STATUS_DOORBELL_ENABLE_FAIL; > > + reg->status = cpu_to_le32(status); > > +} > > + > > +static void pci_epf_test_enable_doorbell(struct pci_epf_test *epf_test, > > + struct pci_epf_test_reg *reg) > > +{ > > + u32 flags = le32_to_cpu(reg->flags); > > + > > + /* If already enabled, drop previous setup first. */ > > + if (epf_test->db_variant != PCI_EPF_TEST_DB_NONE) > > + pci_epf_test_doorbell_cleanup(epf_test); > > + > > + if (flags & FLAG_DB_EMBEDDED) > > + pci_epf_test_enable_doorbell_embedded(epf_test, reg); > > + else > > + pci_epf_test_enable_doorbell_msi(epf_test, reg); > > +} > > + > > static void pci_epf_test_disable_doorbell(struct pci_epf_test *epf_test, > > struct pci_epf_test_reg *reg) > > { > > @@ -1309,6 +1482,9 @@ static void pci_epf_test_unbind(struct pci_epf *epf) > > > > cancel_delayed_work_sync(&epf_test->cmd_handler); > > if (epc->init_complete) { > > + /* In case userspace never disabled doorbell explicitly. */ > > + if (epf_test->db_variant != PCI_EPF_TEST_DB_NONE) > > + pci_epf_test_doorbell_cleanup(epf_test); > > pci_epf_test_clean_dma_chan(epf_test); > > pci_epf_test_clear_bar(epf); > > } > > @@ -1427,6 +1603,7 @@ static int pci_epf_test_probe(struct pci_epf *epf, > > epf_test->bar_size[bar] = default_bar_size[bar]; > > > > INIT_DELAYED_WORK(&epf_test->cmd_handler, pci_epf_test_cmd_handler); > > + INIT_WORK(&epf_test->db_work, pci_epf_test_doorbell_embedded_work); > > > > epf->event_ops = &pci_epf_test_event_ops; > > > > -- > > 2.51.0 > >

