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 <den@valinux.co.jp>
---
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 <kishon@ti.com>
*/
+#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
Hi Koichiro,
kernel test robot noticed the following build errors:
[auto build test ERROR on pci/next]
[also build test ERROR on next-20260205]
[cannot apply to vkoul-dmaengine/next pci/for-linus linus/master v6.19-rc8]
[If your patch is applied to the wrong git tree, kindly drop us a note.
And when submitting patch, we suggest to use '--base' as documented in
https://git-scm.com/docs/git-format-patch#_base_tree_information]
url: https://github.com/intel-lab-lkp/linux/commits/Koichiro-Den/dmaengine-dw-edma-Add-per-channel-interrupt-routing-control/20260207-013042
base: https://git.kernel.org/pub/scm/linux/kernel/git/pci/pci.git next
patch link: https://lore.kernel.org/r/20260206172646.1556847-8-den%40valinux.co.jp
patch subject: [PATCH v4 7/9] PCI: endpoint: pci-epf-test: Add embedded doorbell variant
config: um-randconfig-002-20260208 (https://download.01.org/0day-ci/archive/20260208/202602082040.oSU5mO1p-lkp@intel.com/config)
compiler: clang version 22.0.0git (https://github.com/llvm/llvm-project 9b8addffa70cee5b2acc5454712d9cf78ce45710)
reproduce (this is a W=1 build): (https://download.01.org/0day-ci/archive/20260208/202602082040.oSU5mO1p-lkp@intel.com/reproduce)
If you fix the issue in a separate patch/commit (i.e. not just a new version of
the same patch/commit), kindly add following tags
| Reported-by: kernel test robot <lkp@intel.com>
| Closes: https://lore.kernel.org/oe-kbuild-all/202602082040.oSU5mO1p-lkp@intel.com/
All errors (new ones prefixed by >>):
In file included from drivers/pci/endpoint/functions/pci-epf-test.c:12:
In file included from include/linux/dmaengine.h:12:
In file included from include/linux/scatterlist.h:9:
In file included from arch/um/include/asm/io.h:24:
include/asm-generic/io.h:1209:55: warning: performing pointer arithmetic on a null pointer has undefined behavior [-Wnull-pointer-arithmetic]
1209 | return (port > MMIO_UPPER_LIMIT) ? NULL : PCI_IOBASE + port;
| ~~~~~~~~~~ ^
>> drivers/pci/endpoint/functions/pci-epf-test.c:858:3: error: cannot jump from this goto statement to its label
858 | goto set_status_err;
| ^
drivers/pci/endpoint/functions/pci-epf-test.c:861:34: note: jump bypasses initialization of variable with __attribute__((cleanup))
861 | struct pci_epc_remote_resource *resources __free(kfree) =
| ^
1 warning and 1 error generated.
vim +858 drivers/pci/endpoint/functions/pci-epf-test.c
838
839 static void pci_epf_test_enable_doorbell_embedded(struct pci_epf_test *epf_test,
840 struct pci_epf_test_reg *reg)
841 {
842 struct pci_epc_remote_resource *dma_ctrl = NULL, *chan0 = NULL;
843 const char *irq_name = "pci-ep-test-doorbell-embedded";
844 u32 status = le32_to_cpu(reg->status);
845 struct pci_epf *epf = epf_test->epf;
846 struct pci_epc *epc = epf->epc;
847 struct device *dev = &epf->dev;
848 enum pci_barno bar;
849 size_t align_off;
850 unsigned int i;
851 int cnt, ret;
852 u32 db_off;
853
854 cnt = pci_epc_get_remote_resources(epc, epf->func_no, epf->vfunc_no,
855 NULL, 0);
856 if (cnt <= 0) {
857 dev_err(dev, "No remote resources available for embedded doorbell\n");
> 858 goto set_status_err;
859 }
860
861 struct pci_epc_remote_resource *resources __free(kfree) =
862 kcalloc(cnt, sizeof(*resources), GFP_KERNEL);
863 if (!resources)
864 goto set_status_err;
865
866 ret = pci_epc_get_remote_resources(epc, epf->func_no, epf->vfunc_no,
867 resources, cnt);
868 if (ret < 0) {
869 dev_err(dev, "Failed to get remote resources: %d\n", ret);
870 goto set_status_err;
871 }
872 cnt = ret;
873
874 for (i = 0; i < cnt; i++) {
875 if (resources[i].type == PCI_EPC_RR_DMA_CTRL_MMIO)
876 dma_ctrl = &resources[i];
877 else if (resources[i].type == PCI_EPC_RR_DMA_CHAN_DESC &&
878 !chan0)
879 chan0 = &resources[i];
880 }
881
882 if (!dma_ctrl || !chan0) {
883 dev_err(dev, "Missing DMA ctrl MMIO or channel #0 info\n");
884 goto set_status_err;
885 }
886
887 bar = pci_epc_get_next_free_bar(epf_test->epc_features,
888 epf_test->test_reg_bar + 1);
889 if (bar < BAR_0) {
890 dev_err(dev, "No free BAR for embedded doorbell\n");
891 goto set_status_err;
892 }
893
894 ret = pci_epf_align_inbound_addr(epf, bar, dma_ctrl->phys_addr,
895 &epf_test->db_bar.phys_addr,
896 &align_off);
897 if (ret)
898 goto set_status_err;
899
900 db_off = chan0->u.dma_chan_desc.db_offset;
901 if (db_off >= dma_ctrl->size ||
902 align_off + db_off >= epf->bar[bar].size) {
903 dev_err(dev, "BAR%d too small for embedded doorbell (off %#zx + %#x)\n",
904 bar, align_off, db_off);
905 goto set_status_err;
906 }
907
908 epf_test->db_variant = PCI_EPF_TEST_DB_EMBEDDED;
909
910 ret = request_irq(chan0->u.dma_chan_desc.irq,
911 pci_epf_test_doorbell_embedded_irq_handler,
912 IRQF_SHARED, irq_name, epf_test);
913 if (ret) {
914 dev_err(dev, "Failed to request embedded doorbell IRQ: %d\n",
915 chan0->u.dma_chan_desc.irq);
916 goto err_cleanup;
917 }
918 epf_test->db_irq = chan0->u.dma_chan_desc.irq;
919
920 reg->doorbell_data = cpu_to_le32(0);
921 reg->doorbell_bar = cpu_to_le32(bar);
922 reg->doorbell_offset = cpu_to_le32(align_off + db_off);
923
924 epf_test->db_bar.barno = bar;
925 epf_test->db_bar.size = epf->bar[bar].size;
926 epf_test->db_bar.flags = epf->bar[bar].flags;
927
928 ret = pci_epc_set_bar(epc, epf->func_no, epf->vfunc_no, &epf_test->db_bar);
929 if (ret)
930 goto err_cleanup;
931
932 status |= STATUS_DOORBELL_ENABLE_SUCCESS;
933 reg->status = cpu_to_le32(status);
934 return;
935
936 err_cleanup:
937 pci_epf_test_doorbell_cleanup(epf_test);
938 set_status_err:
939 status |= STATUS_DOORBELL_ENABLE_FAIL;
940 reg->status = cpu_to_le32(status);
941 }
942
--
0-DAY CI Kernel Test Service
https://github.com/intel/lkp-tests/wiki
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 <den@valinux.co.jp>
> ---
Can you change pci_epf_alloc_doorbell() directly? Let it fall back to
edma implement.
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 <kishon@ti.com>
> */
>
> +#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
>
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 <den@valinux.co.jp>
> > ---
>
> 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 <kishon@ti.com>
> > */
> >
> > +#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
> >
© 2016 - 2026 Red Hat, Inc.