[PATCH v4 1/2] PCI: endpoint: Add BAR subrange mapping support

Koichiro Den posted 2 patches 1 month ago
There is a newer version of this series
[PATCH v4 1/2] PCI: endpoint: Add BAR subrange mapping support
Posted by Koichiro Den 1 month ago
Extend the PCI endpoint core to support mapping subranges within a BAR.
Introduce a new 'submap' field and a 'use_submap' flag in struct
pci_epf_bar so an endpoint function driver can request inbound mappings
that fully cover the BAR.

The submap array describes the complete BAR layout (no overlaps and no
gaps are allowed to avoid exposing untranslated address ranges). This
provides the generic infrastructure needed to map multiple logical
regions into a single BAR at different offsets, without assuming a
controller-specific inbound address translation mechanism. Also, the
array must be sorted in ascending order by offset.

No controller-specific implementation is added in this commit.

Signed-off-by: Koichiro Den <den@valinux.co.jp>
---
 include/linux/pci-epf.h | 31 +++++++++++++++++++++++++++++++
 1 file changed, 31 insertions(+)

diff --git a/include/linux/pci-epf.h b/include/linux/pci-epf.h
index 48f68c4dcfa5..91f2e3489cda 100644
--- a/include/linux/pci-epf.h
+++ b/include/linux/pci-epf.h
@@ -110,6 +110,28 @@ struct pci_epf_driver {
 
 #define to_pci_epf_driver(drv) container_of_const((drv), struct pci_epf_driver, driver)
 
+/**
+ * struct pci_epf_bar_submap - BAR subrange for inbound mapping
+ * @phys_addr: target physical/DMA address for this subrange
+ * @size: the size of the subrange to be mapped
+ * @offset: byte offset within the BAR base
+ *
+ * When pci_epf_bar.use_submap is set, pci_epf_bar.submap describes the
+ * complete BAR layout. This allows an EPC driver to program multiple
+ * inbound translation windows for a single BAR when supported by the
+ * controller.
+ *
+ * Note that the subranges:
+ * - must be non-overlapping
+ * - must exactly cover the BAR (i.e. no holes)
+ * - must be sorted (in ascending order by offset)
+ */
+struct pci_epf_bar_submap {
+	dma_addr_t	phys_addr;
+	size_t		size;
+	size_t		offset;
+};
+
 /**
  * struct pci_epf_bar - represents the BAR of EPF device
  * @phys_addr: physical address that should be mapped to the BAR
@@ -119,6 +141,10 @@ struct pci_epf_driver {
  *            requirement
  * @barno: BAR number
  * @flags: flags that are set for the BAR
+ * @use_submap: set true to request subrange mappings within this BAR
+ * @num_submap: number of entries in @submap
+ * @submap: array of subrange descriptors allocated by the caller. See
+ *          struct pci_epf_bar_submap for the restrictions in detail.
  */
 struct pci_epf_bar {
 	dma_addr_t	phys_addr;
@@ -127,6 +153,11 @@ struct pci_epf_bar {
 	size_t		mem_size;
 	enum pci_barno	barno;
 	int		flags;
+
+	/* Optional sub-range mapping */
+	bool		use_submap;
+	unsigned int	num_submap;
+	struct pci_epf_bar_submap	*submap;
 };
 
 /**
-- 
2.51.0
Re: [PATCH v4 1/2] PCI: endpoint: Add BAR subrange mapping support
Posted by Niklas Cassel 1 month ago
On Thu, Jan 08, 2026 at 01:41:47PM +0900, Koichiro Den wrote:
> Extend the PCI endpoint core to support mapping subranges within a BAR.
> Introduce a new 'submap' field and a 'use_submap' flag in struct
> pci_epf_bar so an endpoint function driver can request inbound mappings
> that fully cover the BAR.
> 
> The submap array describes the complete BAR layout (no overlaps and no
> gaps are allowed to avoid exposing untranslated address ranges). This
> provides the generic infrastructure needed to map multiple logical
> regions into a single BAR at different offsets, without assuming a
> controller-specific inbound address translation mechanism. Also, the
> array must be sorted in ascending order by offset.
> 
> No controller-specific implementation is added in this commit.
> 
> Signed-off-by: Koichiro Den <den@valinux.co.jp>

What I don't really like is that you don't have any checks at all in:
pci_epc_set_bar() if the controller actually supports the submap feature.

AFAICT, for non-DWC drivers, e.g.:
drivers/pci/controller/cadence/pcie-cadence-ep.c
drivers/pci/controller/pcie-rcar-ep.c
drivers/pci/controller/pcie-rockchip-ep.c

They have no idea about submapping, so they will silently ignore
pci_epf_bar.use_submap.

I think that we should somehow expose that an EndPoint Controller(EPC)
supports the submap feature.

That way pci_epc_set_bar() can return an error for an EPC that does not
support it.

Perhaps something like:

diff --git a/drivers/pci/controller/dwc/pcie-designware-ep.c b/drivers/pci/controller/dwc/pcie-designware-ep.c
index 1195d401df19..b8eb069f6d57 100644
--- a/drivers/pci/controller/dwc/pcie-designware-ep.c
+++ b/drivers/pci/controller/dwc/pcie-designware-ep.c
@@ -622,15 +622,26 @@ static int dw_pcie_ep_start(struct pci_epc *epc)
 	return dw_pcie_start_link(pci);
 }
 
+static struct pci_epc_features dw_pcie_ep_features;
+
 static const struct pci_epc_features*
 dw_pcie_ep_get_features(struct pci_epc *epc, u8 func_no, u8 vfunc_no)
 {
 	struct dw_pcie_ep *ep = epc_get_drvdata(epc);
+	const struct pci_epc_features *glue_epc_features;
 
 	if (!ep->ops->get_features)
 		return NULL;
 
-	return ep->ops->get_features(ep);
+	glue_epc_features = ep->ops->get_features(ep);
+
+	memcpy(&dw_pcie_ep_features, glue_epc_features,
+	       sizeof(dw_pcie_ep_features));
+
+	/* All DWC based glue drivers support inbound subrange mapping */
+	dw_pcie_ep_features.subrange_mapping = true;
+
+	return &dw_pcie_ep_features;
 }
 
 static const struct pci_epc_ops epc_ops = {
diff --git a/drivers/pci/endpoint/pci-epc-core.c b/drivers/pci/endpoint/pci-epc-core.c
index ca7f19cc973a..8804daaf8376 100644
--- a/drivers/pci/endpoint/pci-epc-core.c
+++ b/drivers/pci/endpoint/pci-epc-core.c
@@ -596,6 +596,9 @@ int pci_epc_set_bar(struct pci_epc *epc, u8 func_no, u8 vfunc_no,
 	if (!epc_features)
 		return -EINVAL;
 
+	if (epf_bar->flags && !epc_features->subrange_mapping)
+		return -EINVAL;
+
 	if (epc_features->bar[bar].type == BAR_RESIZABLE &&
 	    (epf_bar->size < SZ_1M || (u64)epf_bar->size > (SZ_128G * 1024)))
 		return -EINVAL;
diff --git a/include/linux/pci-epc.h b/include/linux/pci-epc.h
index 4286bfdbfdfa..898a29e7d6f7 100644
--- a/include/linux/pci-epc.h
+++ b/include/linux/pci-epc.h
@@ -223,6 +223,8 @@ struct pci_epc_bar_desc {
 /**
  * struct pci_epc_features - features supported by a EPC device per function
  * @linkup_notifier: indicate if the EPC device can notify EPF driver on link up
+ * @subrange_mapping: indicate if the EPC device can map inbound subranges for a
+ *                    BAR
  * @msi_capable: indicate if the endpoint function has MSI capability
  * @msix_capable: indicate if the endpoint function has MSI-X capability
  * @intx_capable: indicate if the endpoint can raise INTx interrupts
@@ -231,6 +233,7 @@ struct pci_epc_bar_desc {
  */
 struct pci_epc_features {
 	unsigned int	linkup_notifier : 1;
+	unsigned int	subrange_mapping : 1;
 	unsigned int	msi_capable : 1;
 	unsigned int	msix_capable : 1;
 	unsigned int	intx_capable : 1;



The memcpy in dw_pcie_ep_get_features() is a bit ugly.
I guess the alternative is to change all the DWC based glue drivers
to return a "struct pci_epc_features*" instead of a "const struct pci_epc_features*"
such that dw_pcie_ep_get_features() can simply set subrange_mapping = true in the
struct pci_epc_features returned by the glue driver.


Kind regards,
Niklas
Re: [PATCH v4 1/2] PCI: endpoint: Add BAR subrange mapping support
Posted by Niklas Cassel 1 month ago
On Thu, Jan 08, 2026 at 10:37:35AM +0100, Niklas Cassel wrote:
> The memcpy in dw_pcie_ep_get_features() is a bit ugly.
> I guess the alternative is to change all the DWC based glue drivers
> to return a "struct pci_epc_features*" instead of a "const struct pci_epc_features*"
> such that dw_pcie_ep_get_features() can simply set subrange_mapping = true in the
> struct pci_epc_features returned by the glue driver.

I think the best way it probably to create another patch,
that will be patch 2 out of 3 in the series, which changes:
https://github.com/torvalds/linux/blob/v6.19-rc4/drivers/pci/controller/dwc/pcie-designware.h#L449

from:
const struct pci_epc_features* (*get_features)(struct dw_pcie_ep *ep);

to:
struct pci_epc_features* (*get_features)(struct dw_pcie_ep *ep);

and which does the equivalent change in all the DWC based glue drivers.

That way, dw_pcie_ep_get_features() can simply set subrange_mapping = true
in the struct pci_epc_features returned by the glue driver.



Note that the function dw_pcie_ep_get_features() itself should still return:
"static const struct pci_epc_features*"

(Since this represents the DWC midlayer driver level.)

It is only the DWC based glue drivers (lower level drivers) that should drop
the const.


Kind regards,
Niklas
Re: [PATCH v4 1/2] PCI: endpoint: Add BAR subrange mapping support
Posted by Koichiro Den 1 month ago
On Thu, Jan 08, 2026 at 10:59:30AM +0100, Niklas Cassel wrote:
> On Thu, Jan 08, 2026 at 10:37:35AM +0100, Niklas Cassel wrote:
> > The memcpy in dw_pcie_ep_get_features() is a bit ugly.
> > I guess the alternative is to change all the DWC based glue drivers
> > to return a "struct pci_epc_features*" instead of a "const struct pci_epc_features*"
> > such that dw_pcie_ep_get_features() can simply set subrange_mapping = true in the
> > struct pci_epc_features returned by the glue driver.
> 
> I think the best way it probably to create another patch,
> that will be patch 2 out of 3 in the series, which changes:
> https://github.com/torvalds/linux/blob/v6.19-rc4/drivers/pci/controller/dwc/pcie-designware.h#L449
> 
> from:
> const struct pci_epc_features* (*get_features)(struct dw_pcie_ep *ep);
> 
> to:
> struct pci_epc_features* (*get_features)(struct dw_pcie_ep *ep);
> 
> and which does the equivalent change in all the DWC based glue drivers.
> 
> That way, dw_pcie_ep_get_features() can simply set subrange_mapping = true
> in the struct pci_epc_features returned by the glue driver.
> 
> 
> 
> Note that the function dw_pcie_ep_get_features() itself should still return:
> "static const struct pci_epc_features*"
> 
> (Since this represents the DWC midlayer driver level.)
> 
> It is only the DWC based glue drivers (lower level drivers) that should drop
> the const.

Hi Niklas,

Thanks again for the detailed feedback. I agree we should not let
controllers that do not support subrange mapping silently ignore
epf_bar->use_submap, as that could potentially lead to an unexpected and
hard-to-debug state without returning an error.

Adding a subrange_mapping bit to struct pci_epc_features makes sense.
I also considered setting .subrange_mapping = 1 in every DWC-based glue
driver and keeping the const qualifiers, but that would duplicate the
same information across drivers and add unnecessary maintenance burden.
So I'll follow your suggestion and drop const only at the glue-driver
level. I'll send v5 shortly.

Thanks,
Koichiro

> 
> 
> Kind regards,
> Niklas
Re: [PATCH v4 1/2] PCI: endpoint: Add BAR subrange mapping support
Posted by Niklas Cassel 1 month ago
On Thu, Jan 08, 2026 at 10:37:35AM +0100, Niklas Cassel wrote:
> @@ -596,6 +596,9 @@ int pci_epc_set_bar(struct pci_epc *epc, u8 func_no, u8 vfunc_no,
>  	if (!epc_features)
>  		return -EINVAL;
>  
> +	if (epf_bar->flags && !epc_features->subrange_mapping)
> +		return -EINVAL;

This should of course have been:

	if (epf_bar->use_submap && !epc_features->subrange_mapping)
		return -EINVAL;


(I simply used flags in order to compile test without applying your series.)


Kind regards,
Niklas