[WIP PATCH v3 6/7] nvmet: pci-epf: Use hardware-owned MSI-X layout

From: Koichiro Den

Date: Tue Sep 01 2026 - 03:08:57 EST


The NVMe PCI endpoint function always allocates an MSI-X Table and PBA in
BAR0. This duplicates fixed regions provided by endpoint controllers that
own the MSI-X layout.

Use the hardware-owned layout when it can hold the configured MSI-X
entries and omit the duplicate regions from BAR0. Keep the existing
EPF-owned layout as the fallback.

Signed-off-by: Koichiro Den <den@xxxxxxxxxxxxx>
---
Changes in v3:
- New patch.
---
drivers/nvme/target/pci-epf.c | 45 ++++++++++++++++++++++++-----------
1 file changed, 31 insertions(+), 14 deletions(-)

diff --git a/drivers/nvme/target/pci-epf.c b/drivers/nvme/target/pci-epf.c
index 39f2f3066deb..b0b74486faa3 100644
--- a/drivers/nvme/target/pci-epf.c
+++ b/drivers/nvme/target/pci-epf.c
@@ -2135,7 +2135,7 @@ static int nvmet_pci_epf_configure_bar(struct nvmet_pci_epf *nvme_epf)
struct pci_epf *epf = nvme_epf->epf;
const struct pci_epc_features *epc_features = nvme_epf->epc_features;
size_t reg_size, reg_bar_size;
- size_t msix_table_size = 0;
+ int ret;

/*
* The first free BAR will be our register BAR and per NVMe
@@ -2158,26 +2158,43 @@ static int nvmet_pci_epf_configure_bar(struct nvmet_pci_epf *nvme_epf)

/*
* Calculate the size of the register bar: NVMe registers first with
- * enough space for the doorbells, followed by the MSI-X table
- * if supported.
+ * enough space for the doorbells, followed by the MSI-X table if
+ * supported and not provided by the EPC.
*/
reg_size = NVME_REG_DBS + (NVMET_NR_QUEUES * 2 * sizeof(u32));
reg_size = ALIGN(reg_size, 8);

if (epc_features->msix_capable) {
- size_t pba_size;
-
- msix_table_size = PCI_MSIX_ENTRY_SIZE * epf->msix_interrupts;
- pba_size = BITS_TO_U64(epf->msix_interrupts) * sizeof(u64);
+ size_t msix_table_size, pba_size;

- nvme_epf->msix_layout.table_bar = BAR_0;
- nvme_epf->msix_layout.table_offset = reg_size;
- nvme_epf->msix_layout.table_size = msix_table_size;
- nvme_epf->msix_layout.pba_bar = BAR_0;
- nvme_epf->msix_layout.pba_offset = reg_size + msix_table_size;
- nvme_epf->msix_layout.pba_size = pba_size;
+ ret = pci_epc_get_hw_msix_layout(epc_features,
+ &nvme_epf->msix_layout);
+ if (ret && ret != -ENOENT) {
+ dev_err(&epf->dev,
+ "Invalid hardware-owned MSI-X layout\n");
+ return ret;
+ }

- reg_size += msix_table_size + pba_size;
+ if (ret == -ENOENT ||
+ nvme_epf->msix_layout.table_size <
+ PCI_MSIX_ENTRY_SIZE * epf->msix_interrupts ||
+ nvme_epf->msix_layout.pba_size <
+ BITS_TO_U64(epf->msix_interrupts) * sizeof(u64)) {
+ msix_table_size =
+ PCI_MSIX_ENTRY_SIZE * epf->msix_interrupts;
+ pba_size = BITS_TO_U64(epf->msix_interrupts) *
+ sizeof(u64);
+
+ nvme_epf->msix_layout.table_bar = BAR_0;
+ nvme_epf->msix_layout.table_offset = reg_size;
+ nvme_epf->msix_layout.table_size = msix_table_size;
+ nvme_epf->msix_layout.pba_bar = BAR_0;
+ nvme_epf->msix_layout.pba_offset =
+ reg_size + msix_table_size;
+ nvme_epf->msix_layout.pba_size = pba_size;
+
+ reg_size += msix_table_size + pba_size;
+ }
}

if (epc_features->bar[BAR_0].type == BAR_FIXED) {
--
2.51.0