Support doorbell backends where the doorbell target is already exposed
via a platform-owned fixed BAR mapping and/or where the doorbell IRQ
must be requested with specific flags.
When pci_epf_alloc_doorbell() provides db_msg[].bar/offset, reuse the
pre-exposed BAR window and skip programming a new inbound mapping. Also
honor db_msg[].irq_flags when requesting the doorbell IRQ.
For embedded doorbells (e.g. interrupt-emulation), multiple doorbells
may share a single address/data pair and a single Linux IRQ. Avoid
requesting duplicate handlers by requesting only one IRQ in that case.
Signed-off-by: Koichiro Den <den@valinux.co.jp>
---
drivers/pci/endpoint/functions/pci-epf-vntb.c | 57 +++++++++++++++++--
1 file changed, 52 insertions(+), 5 deletions(-)
diff --git a/drivers/pci/endpoint/functions/pci-epf-vntb.c b/drivers/pci/endpoint/functions/pci-epf-vntb.c
index 20efa27325f1..39ba4d6b7d8d 100644
--- a/drivers/pci/endpoint/functions/pci-epf-vntb.c
+++ b/drivers/pci/endpoint/functions/pci-epf-vntb.c
@@ -134,6 +134,11 @@ struct epf_ntb {
u16 vntb_vid;
bool linkup;
+
+ /*
+ * True when doorbells are interrupt-driven (MSI or embedded), false
+ * when polled.
+ */
bool msi_doorbell;
u32 spad_size;
@@ -523,7 +528,7 @@ static int epf_ntb_db_bar_init_msi_doorbell(struct epf_ntb *ntb,
enum pci_barno barno)
{
struct pci_epf *epf = ntb->epf;
- unsigned int req;
+ unsigned int req, cnt;
dma_addr_t low, high;
struct msi_msg *msg;
size_t sz;
@@ -534,9 +539,29 @@ static int epf_ntb_db_bar_init_msi_doorbell(struct epf_ntb *ntb,
if (ret)
return ret;
- for (req = 0; req < ntb->db_count; req++) {
+ /*
+ * The doorbell target may already be exposed by a platform-owned fixed
+ * BAR. In that case, we must reuse it and the requested db_bar must
+ * match.
+ */
+ if (epf->db_msg[0].bar != NO_BAR && epf->db_msg[0].bar != barno) {
+ ret = -EINVAL;
+ goto err_free_doorbell;
+ }
+
+ /*
+ * For PCI_EPF_DOORBELL_EMBEDDED, the backend may provide a single MMIO
+ * address/data pair and a single Linux IRQ even if multiple doorbells
+ * were requested. Avoid requesting duplicate handlers in that case.
+ */
+ cnt = ntb->db_count;
+ if (epf->db_msg[0].type == PCI_EPF_DOORBELL_EMBEDDED)
+ cnt = 1;
+
+ for (req = 0; req < cnt; req++) {
ret = request_irq(epf->db_msg[req].virq, epf_ntb_doorbell_handler,
- 0, "pci_epf_vntb_db", ntb);
+ epf->db_msg[req].irq_flags, "pci_epf_vntb_db",
+ ntb);
if (ret) {
dev_err(&epf->dev,
@@ -546,6 +571,22 @@ static int epf_ntb_db_bar_init_msi_doorbell(struct epf_ntb *ntb,
}
}
+ if (epf->db_msg[0].bar != NO_BAR) {
+ for (i = 0; i < ntb->db_count; i++) {
+ msg = &epf->db_msg[i].msg;
+
+ if (epf->db_msg[i].bar != barno) {
+ ret = -EINVAL;
+ goto err_free_irq;
+ }
+
+ ntb->reg->db_data[i] = msg->data;
+ ntb->reg->db_offset[i] = epf->db_msg[i].offset;
+ }
+ goto out;
+ }
+
+ /* Program inbound mapping for the doorbell */
msg = &epf->db_msg[0].msg;
high = 0;
@@ -592,6 +633,7 @@ static int epf_ntb_db_bar_init_msi_doorbell(struct epf_ntb *ntb,
ntb->reg->db_offset[i] = offset;
}
+out:
ntb->reg->db_entry_size = 0;
ntb->msi_doorbell = true;
@@ -602,6 +644,7 @@ static int epf_ntb_db_bar_init_msi_doorbell(struct epf_ntb *ntb,
while (req)
free_irq(epf->db_msg[--req].virq, ntb);
+err_free_doorbell:
pci_epf_free_doorbell(ntb->epf);
return ret;
}
@@ -665,9 +708,13 @@ static void epf_ntb_db_bar_clear(struct epf_ntb *ntb)
enum pci_barno barno;
if (ntb->msi_doorbell) {
- int i;
+ unsigned int cnt = ntb->db_count;
+ unsigned int i;
- for (i = 0; i < ntb->db_count; i++)
+ if (ntb->epf->db_msg[0].type == PCI_EPF_DOORBELL_EMBEDDED)
+ cnt = 1;
+
+ for (i = 0; i < cnt; i++)
free_irq(ntb->epf->db_msg[i].virq, ntb);
}
--
2.51.0
On Mon, Feb 16, 2026 at 01:38:45AM +0900, Koichiro Den wrote:
> Support doorbell backends where the doorbell target is already exposed
> via a platform-owned fixed BAR mapping and/or where the doorbell IRQ
> must be requested with specific flags.
>
> When pci_epf_alloc_doorbell() provides db_msg[].bar/offset, reuse the
> pre-exposed BAR window and skip programming a new inbound mapping. Also
> honor db_msg[].irq_flags when requesting the doorbell IRQ.
>
> For embedded doorbells (e.g. interrupt-emulation), multiple doorbells
> may share a single address/data pair and a single Linux IRQ. Avoid
> requesting duplicate handlers by requesting only one IRQ in that case.
>
> Signed-off-by: Koichiro Den <den@valinux.co.jp>
> ---
> drivers/pci/endpoint/functions/pci-epf-vntb.c | 57 +++++++++++++++++--
> 1 file changed, 52 insertions(+), 5 deletions(-)
>
> diff --git a/drivers/pci/endpoint/functions/pci-epf-vntb.c b/drivers/pci/endpoint/functions/pci-epf-vntb.c
> index 20efa27325f1..39ba4d6b7d8d 100644
> --- a/drivers/pci/endpoint/functions/pci-epf-vntb.c
> +++ b/drivers/pci/endpoint/functions/pci-epf-vntb.c
> @@ -134,6 +134,11 @@ struct epf_ntb {
> u16 vntb_vid;
>
> bool linkup;
> +
> + /*
> + * True when doorbells are interrupt-driven (MSI or embedded), false
> + * when polled.
> + */
> bool msi_doorbell;
> u32 spad_size;
>
> @@ -523,7 +528,7 @@ static int epf_ntb_db_bar_init_msi_doorbell(struct epf_ntb *ntb,
> enum pci_barno barno)
> {
> struct pci_epf *epf = ntb->epf;
> - unsigned int req;
> + unsigned int req, cnt;
> dma_addr_t low, high;
> struct msi_msg *msg;
> size_t sz;
> @@ -534,9 +539,29 @@ static int epf_ntb_db_bar_init_msi_doorbell(struct epf_ntb *ntb,
> if (ret)
> return ret;
>
> - for (req = 0; req < ntb->db_count; req++) {
> + /*
> + * The doorbell target may already be exposed by a platform-owned fixed
> + * BAR. In that case, we must reuse it and the requested db_bar must
> + * match.
> + */
> + if (epf->db_msg[0].bar != NO_BAR && epf->db_msg[0].bar != barno) {
> + ret = -EINVAL;
> + goto err_free_doorbell;
> + }
> +
> + /*
> + * For PCI_EPF_DOORBELL_EMBEDDED, the backend may provide a single MMIO
> + * address/data pair and a single Linux IRQ even if multiple doorbells
> + * were requested. Avoid requesting duplicate handlers in that case.
> + */
> + cnt = ntb->db_count;
> + if (epf->db_msg[0].type == PCI_EPF_DOORBELL_EMBEDDED)
> + cnt = 1;
Most SoC combine all DMA channel to one irqs. But it should be not
neccessary for SoC design. It is possible each DMA channel have dedicate
irq number. I suggest check irq, instead of type.
Frank
> +
> + for (req = 0; req < cnt; req++) {
> ret = request_irq(epf->db_msg[req].virq, epf_ntb_doorbell_handler,
> - 0, "pci_epf_vntb_db", ntb);
> + epf->db_msg[req].irq_flags, "pci_epf_vntb_db",
> + ntb);
>
> if (ret) {
> dev_err(&epf->dev,
> @@ -546,6 +571,22 @@ static int epf_ntb_db_bar_init_msi_doorbell(struct epf_ntb *ntb,
> }
> }
>
> + if (epf->db_msg[0].bar != NO_BAR) {
> + for (i = 0; i < ntb->db_count; i++) {
> + msg = &epf->db_msg[i].msg;
> +
> + if (epf->db_msg[i].bar != barno) {
> + ret = -EINVAL;
> + goto err_free_irq;
> + }
> +
> + ntb->reg->db_data[i] = msg->data;
> + ntb->reg->db_offset[i] = epf->db_msg[i].offset;
> + }
> + goto out;
> + }
> +
> + /* Program inbound mapping for the doorbell */
> msg = &epf->db_msg[0].msg;
>
> high = 0;
> @@ -592,6 +633,7 @@ static int epf_ntb_db_bar_init_msi_doorbell(struct epf_ntb *ntb,
> ntb->reg->db_offset[i] = offset;
> }
>
> +out:
> ntb->reg->db_entry_size = 0;
>
> ntb->msi_doorbell = true;
> @@ -602,6 +644,7 @@ static int epf_ntb_db_bar_init_msi_doorbell(struct epf_ntb *ntb,
> while (req)
> free_irq(epf->db_msg[--req].virq, ntb);
>
> +err_free_doorbell:
> pci_epf_free_doorbell(ntb->epf);
> return ret;
> }
> @@ -665,9 +708,13 @@ static void epf_ntb_db_bar_clear(struct epf_ntb *ntb)
> enum pci_barno barno;
>
> if (ntb->msi_doorbell) {
> - int i;
> + unsigned int cnt = ntb->db_count;
> + unsigned int i;
>
> - for (i = 0; i < ntb->db_count; i++)
> + if (ntb->epf->db_msg[0].type == PCI_EPF_DOORBELL_EMBEDDED)
> + cnt = 1;
> +
> + for (i = 0; i < cnt; i++)
> free_irq(ntb->epf->db_msg[i].virq, ntb);
> }
>
> --
> 2.51.0
>
On Mon, Feb 16, 2026 at 11:14:58AM -0500, Frank Li wrote:
> On Mon, Feb 16, 2026 at 01:38:45AM +0900, Koichiro Den wrote:
> > Support doorbell backends where the doorbell target is already exposed
> > via a platform-owned fixed BAR mapping and/or where the doorbell IRQ
> > must be requested with specific flags.
> >
> > When pci_epf_alloc_doorbell() provides db_msg[].bar/offset, reuse the
> > pre-exposed BAR window and skip programming a new inbound mapping. Also
> > honor db_msg[].irq_flags when requesting the doorbell IRQ.
> >
> > For embedded doorbells (e.g. interrupt-emulation), multiple doorbells
> > may share a single address/data pair and a single Linux IRQ. Avoid
> > requesting duplicate handlers by requesting only one IRQ in that case.
> >
> > Signed-off-by: Koichiro Den <den@valinux.co.jp>
> > ---
> > drivers/pci/endpoint/functions/pci-epf-vntb.c | 57 +++++++++++++++++--
> > 1 file changed, 52 insertions(+), 5 deletions(-)
> >
> > diff --git a/drivers/pci/endpoint/functions/pci-epf-vntb.c b/drivers/pci/endpoint/functions/pci-epf-vntb.c
> > index 20efa27325f1..39ba4d6b7d8d 100644
> > --- a/drivers/pci/endpoint/functions/pci-epf-vntb.c
> > +++ b/drivers/pci/endpoint/functions/pci-epf-vntb.c
> > @@ -134,6 +134,11 @@ struct epf_ntb {
> > u16 vntb_vid;
> >
> > bool linkup;
> > +
> > + /*
> > + * True when doorbells are interrupt-driven (MSI or embedded), false
> > + * when polled.
> > + */
> > bool msi_doorbell;
> > u32 spad_size;
> >
> > @@ -523,7 +528,7 @@ static int epf_ntb_db_bar_init_msi_doorbell(struct epf_ntb *ntb,
> > enum pci_barno barno)
> > {
> > struct pci_epf *epf = ntb->epf;
> > - unsigned int req;
> > + unsigned int req, cnt;
> > dma_addr_t low, high;
> > struct msi_msg *msg;
> > size_t sz;
> > @@ -534,9 +539,29 @@ static int epf_ntb_db_bar_init_msi_doorbell(struct epf_ntb *ntb,
> > if (ret)
> > return ret;
> >
> > - for (req = 0; req < ntb->db_count; req++) {
> > + /*
> > + * The doorbell target may already be exposed by a platform-owned fixed
> > + * BAR. In that case, we must reuse it and the requested db_bar must
> > + * match.
> > + */
> > + if (epf->db_msg[0].bar != NO_BAR && epf->db_msg[0].bar != barno) {
> > + ret = -EINVAL;
> > + goto err_free_doorbell;
> > + }
> > +
> > + /*
> > + * For PCI_EPF_DOORBELL_EMBEDDED, the backend may provide a single MMIO
> > + * address/data pair and a single Linux IRQ even if multiple doorbells
> > + * were requested. Avoid requesting duplicate handlers in that case.
> > + */
> > + cnt = ntb->db_count;
> > + if (epf->db_msg[0].type == PCI_EPF_DOORBELL_EMBEDDED)
> > + cnt = 1;
>
> Most SoC combine all DMA channel to one irqs. But it should be not
> neccessary for SoC design. It is possible each DMA channel have dedicate
> irq number. I suggest check irq, instead of type.
Sounds reasonable. I was trying to keep the code minimal, but your suggestion
makes it more future-proof and avoids exposing PCI_EPF_DOORBELL_EMBEDDED details
to this consumer layer. I'll adjust this part accordingly.
THanks for the review,
Koichiro
>
> Frank
> > +
> > + for (req = 0; req < cnt; req++) {
> > ret = request_irq(epf->db_msg[req].virq, epf_ntb_doorbell_handler,
> > - 0, "pci_epf_vntb_db", ntb);
> > + epf->db_msg[req].irq_flags, "pci_epf_vntb_db",
> > + ntb);
> >
> > if (ret) {
> > dev_err(&epf->dev,
> > @@ -546,6 +571,22 @@ static int epf_ntb_db_bar_init_msi_doorbell(struct epf_ntb *ntb,
> > }
> > }
> >
> > + if (epf->db_msg[0].bar != NO_BAR) {
> > + for (i = 0; i < ntb->db_count; i++) {
> > + msg = &epf->db_msg[i].msg;
> > +
> > + if (epf->db_msg[i].bar != barno) {
> > + ret = -EINVAL;
> > + goto err_free_irq;
> > + }
> > +
> > + ntb->reg->db_data[i] = msg->data;
> > + ntb->reg->db_offset[i] = epf->db_msg[i].offset;
> > + }
> > + goto out;
> > + }
> > +
> > + /* Program inbound mapping for the doorbell */
> > msg = &epf->db_msg[0].msg;
> >
> > high = 0;
> > @@ -592,6 +633,7 @@ static int epf_ntb_db_bar_init_msi_doorbell(struct epf_ntb *ntb,
> > ntb->reg->db_offset[i] = offset;
> > }
> >
> > +out:
> > ntb->reg->db_entry_size = 0;
> >
> > ntb->msi_doorbell = true;
> > @@ -602,6 +644,7 @@ static int epf_ntb_db_bar_init_msi_doorbell(struct epf_ntb *ntb,
> > while (req)
> > free_irq(epf->db_msg[--req].virq, ntb);
> >
> > +err_free_doorbell:
> > pci_epf_free_doorbell(ntb->epf);
> > return ret;
> > }
> > @@ -665,9 +708,13 @@ static void epf_ntb_db_bar_clear(struct epf_ntb *ntb)
> > enum pci_barno barno;
> >
> > if (ntb->msi_doorbell) {
> > - int i;
> > + unsigned int cnt = ntb->db_count;
> > + unsigned int i;
> >
> > - for (i = 0; i < ntb->db_count; i++)
> > + if (ntb->epf->db_msg[0].type == PCI_EPF_DOORBELL_EMBEDDED)
> > + cnt = 1;
> > +
> > + for (i = 0; i < cnt; i++)
> > free_irq(ntb->epf->db_msg[i].virq, ntb);
> > }
> >
> > --
> > 2.51.0
> >
© 2016 - 2026 Red Hat, Inc.