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.
Multiple doorbells may share the same Linux IRQ. Avoid duplicate
request_irq() calls by requesting each unique virq once.
This makes pci-epf-vntb work with platform-defined or embedded doorbell
backends without exposing backend-specific details to the consumer
layer.
Signed-off-by: Koichiro Den <den@valinux.co.jp>
---
Changes since v7:
- Deduplicate request_irq()/free_irq() calls based on virq (shared
IRQ) rather than doorbell type.
drivers/pci/endpoint/functions/pci-epf-vntb.c | 61 ++++++++++++++++++-
1 file changed, 58 insertions(+), 3 deletions(-)
diff --git a/drivers/pci/endpoint/functions/pci-epf-vntb.c b/drivers/pci/endpoint/functions/pci-epf-vntb.c
index 52cf442ca1d9..b1e8414c4e43 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;
@@ -517,6 +522,17 @@ static int epf_ntb_configure_interrupt(struct epf_ntb *ntb)
return 0;
}
+static bool epf_ntb_db_irq_is_first(const struct pci_epf *epf, unsigned int idx)
+{
+ unsigned int i;
+
+ for (i = 0; i < idx; i++)
+ if (epf->db_msg[i].virq == epf->db_msg[idx].virq)
+ return false;
+
+ return true;
+}
+
static int epf_ntb_db_bar_init_msi_doorbell(struct epf_ntb *ntb,
struct pci_epf_bar *db_bar,
const struct pci_epc_features *epc_features,
@@ -533,9 +549,24 @@ static int epf_ntb_db_bar_init_msi_doorbell(struct epf_ntb *ntb,
if (ret)
return ret;
+ /*
+ * 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 (req = 0; req < ntb->db_count; req++) {
+ /* Avoid requesting duplicate handlers */
+ if (!epf_ntb_db_irq_is_first(epf, req))
+ continue;
+
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,
@@ -545,6 +576,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;
@@ -591,6 +638,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;
@@ -598,9 +646,13 @@ static int epf_ntb_db_bar_init_msi_doorbell(struct epf_ntb *ntb,
return 0;
err_free_irq:
- for (req--; req >= 0; req--)
+ for (req--; req >= 0; req--) {
+ if (!epf_ntb_db_irq_is_first(epf, req))
+ continue;
free_irq(epf->db_msg[req].virq, ntb);
+ }
+err_free_doorbell:
pci_epf_free_doorbell(ntb->epf);
return ret;
}
@@ -666,8 +718,11 @@ static void epf_ntb_db_bar_clear(struct epf_ntb *ntb)
if (ntb->msi_doorbell) {
int i;
- for (i = 0; i < ntb->db_count; i++)
+ for (i = 0; i < ntb->db_count; i++) {
+ if (!epf_ntb_db_irq_is_first(ntb->epf, i))
+ continue;
free_irq(ntb->epf->db_msg[i].virq, ntb);
+ }
}
if (ntb->epf->db_msg)
--
2.51.0
On Tue, Feb 17, 2026 at 05:05:59PM +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.
>
> Multiple doorbells may share the same Linux IRQ. Avoid duplicate
> request_irq() calls by requesting each unique virq once.
>
> This makes pci-epf-vntb work with platform-defined or embedded doorbell
Nit: Make pci-epf-vntb work with ...
> backends without exposing backend-specific details to the consumer
> layer.
>
> Signed-off-by: Koichiro Den <den@valinux.co.jp>
> ---
> Changes since v7:
> - Deduplicate request_irq()/free_irq() calls based on virq (shared
> IRQ) rather than doorbell type.
>
> drivers/pci/endpoint/functions/pci-epf-vntb.c | 61 ++++++++++++++++++-
> 1 file changed, 58 insertions(+), 3 deletions(-)
>
> diff --git a/drivers/pci/endpoint/functions/pci-epf-vntb.c b/drivers/pci/endpoint/functions/pci-epf-vntb.c
> index 52cf442ca1d9..b1e8414c4e43 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;
>
> @@ -517,6 +522,17 @@ static int epf_ntb_configure_interrupt(struct epf_ntb *ntb)
> return 0;
> }
>
> +static bool epf_ntb_db_irq_is_first(const struct pci_epf *epf, unsigned int idx)
epf_ntb_db_irq_is_duplicated() look better, below 'if' needn't !
Frank
> +{
> + unsigned int i;
> +
> + for (i = 0; i < idx; i++)
> + if (epf->db_msg[i].virq == epf->db_msg[idx].virq)
> + return false;
> +
> + return true;
> +}
> +
> static int epf_ntb_db_bar_init_msi_doorbell(struct epf_ntb *ntb,
> struct pci_epf_bar *db_bar,
> const struct pci_epc_features *epc_features,
> @@ -533,9 +549,24 @@ static int epf_ntb_db_bar_init_msi_doorbell(struct epf_ntb *ntb,
> if (ret)
> return ret;
>
> + /*
> + * 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 (req = 0; req < ntb->db_count; req++) {
> + /* Avoid requesting duplicate handlers */
> + if (!epf_ntb_db_irq_is_first(epf, req))
> + continue;
> +
> 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,
> @@ -545,6 +576,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;
> @@ -591,6 +638,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;
> @@ -598,9 +646,13 @@ static int epf_ntb_db_bar_init_msi_doorbell(struct epf_ntb *ntb,
> return 0;
>
> err_free_irq:
> - for (req--; req >= 0; req--)
> + for (req--; req >= 0; req--) {
> + if (!epf_ntb_db_irq_is_first(epf, req))
> + continue;
> free_irq(epf->db_msg[req].virq, ntb);
> + }
>
> +err_free_doorbell:
> pci_epf_free_doorbell(ntb->epf);
> return ret;
> }
> @@ -666,8 +718,11 @@ static void epf_ntb_db_bar_clear(struct epf_ntb *ntb)
> if (ntb->msi_doorbell) {
> int i;
>
> - for (i = 0; i < ntb->db_count; i++)
> + for (i = 0; i < ntb->db_count; i++) {
> + if (!epf_ntb_db_irq_is_first(ntb->epf, i))
> + continue;
> free_irq(ntb->epf->db_msg[i].virq, ntb);
> + }
> }
>
> if (ntb->epf->db_msg)
> --
> 2.51.0
>
On Tue, Feb 17, 2026 at 11:54:29AM -0500, Frank Li wrote:
> On Tue, Feb 17, 2026 at 05:05:59PM +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.
> >
> > Multiple doorbells may share the same Linux IRQ. Avoid duplicate
> > request_irq() calls by requesting each unique virq once.
> >
> > This makes pci-epf-vntb work with platform-defined or embedded doorbell
>
> Nit: Make pci-epf-vntb work with ...
>
> > backends without exposing backend-specific details to the consumer
> > layer.
> >
> > Signed-off-by: Koichiro Den <den@valinux.co.jp>
> > ---
> > Changes since v7:
> > - Deduplicate request_irq()/free_irq() calls based on virq (shared
> > IRQ) rather than doorbell type.
> >
> > drivers/pci/endpoint/functions/pci-epf-vntb.c | 61 ++++++++++++++++++-
> > 1 file changed, 58 insertions(+), 3 deletions(-)
> >
> > diff --git a/drivers/pci/endpoint/functions/pci-epf-vntb.c b/drivers/pci/endpoint/functions/pci-epf-vntb.c
> > index 52cf442ca1d9..b1e8414c4e43 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;
> >
> > @@ -517,6 +522,17 @@ static int epf_ntb_configure_interrupt(struct epf_ntb *ntb)
> > return 0;
> > }
> >
> > +static bool epf_ntb_db_irq_is_first(const struct pci_epf *epf, unsigned int idx)
>
> epf_ntb_db_irq_is_duplicated() look better, below 'if' needn't !
Agreed. I'll rename it and update the usage in the next revision.
Thanks for the review,
Koichiro
>
> Frank
>
> > +{
> > + unsigned int i;
> > +
> > + for (i = 0; i < idx; i++)
> > + if (epf->db_msg[i].virq == epf->db_msg[idx].virq)
> > + return false;
> > +
> > + return true;
> > +}
> > +
> > static int epf_ntb_db_bar_init_msi_doorbell(struct epf_ntb *ntb,
> > struct pci_epf_bar *db_bar,
> > const struct pci_epc_features *epc_features,
> > @@ -533,9 +549,24 @@ static int epf_ntb_db_bar_init_msi_doorbell(struct epf_ntb *ntb,
> > if (ret)
> > return ret;
> >
> > + /*
> > + * 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 (req = 0; req < ntb->db_count; req++) {
> > + /* Avoid requesting duplicate handlers */
> > + if (!epf_ntb_db_irq_is_first(epf, req))
> > + continue;
> > +
> > 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,
> > @@ -545,6 +576,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;
> > @@ -591,6 +638,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;
> > @@ -598,9 +646,13 @@ static int epf_ntb_db_bar_init_msi_doorbell(struct epf_ntb *ntb,
> > return 0;
> >
> > err_free_irq:
> > - for (req--; req >= 0; req--)
> > + for (req--; req >= 0; req--) {
> > + if (!epf_ntb_db_irq_is_first(epf, req))
> > + continue;
> > free_irq(epf->db_msg[req].virq, ntb);
> > + }
> >
> > +err_free_doorbell:
> > pci_epf_free_doorbell(ntb->epf);
> > return ret;
> > }
> > @@ -666,8 +718,11 @@ static void epf_ntb_db_bar_clear(struct epf_ntb *ntb)
> > if (ntb->msi_doorbell) {
> > int i;
> >
> > - for (i = 0; i < ntb->db_count; i++)
> > + for (i = 0; i < ntb->db_count; i++) {
> > + if (!epf_ntb_db_irq_is_first(ntb->epf, i))
> > + continue;
> > free_irq(ntb->epf->db_msg[i].virq, ntb);
> > + }
> > }
> >
> > if (ntb->epf->db_msg)
> > --
> > 2.51.0
> >
© 2016 - 2026 Red Hat, Inc.