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
> >

Reply via email to