* [PATCH v5 1/6] PCI: endpoint: Introduce pci_epc_function_is_valid()
2024-10-11 12:01 [PATCH v5 0/6] Improve PCI memory mapping API Damien Le Moal
@ 2024-10-11 12:01 ` Damien Le Moal
2024-10-11 12:01 ` [PATCH v5 2/6] PCI: endpoint: Improve pci_epc_mem_alloc_addr() Damien Le Moal
` (4 subsequent siblings)
5 siblings, 0 replies; 13+ messages in thread
From: Damien Le Moal @ 2024-10-11 12:01 UTC (permalink / raw)
To: Manivannan Sadhasivam, Krzysztof Wilczyński,
Kishon Vijay Abraham I, Bjorn Helgaas, Lorenzo Pieralisi,
Rob Herring, Jonathan Corbet, Jingoo Han, linux-pci
Cc: Rick Wertenbroek, Niklas Cassel
Introduce the epc core helper function pci_epc_function_is_valid() to
verify that an epc pointer, a physical function number and a virtual
function number are all valid. This avoids repeating the code pattern:
if (IS_ERR_OR_NULL(epc) || func_no >= epc->max_functions)
return err;
if (vfunc_no > 0 && (!epc->max_vfs || vfunc_no > epc->max_vfs[func_no]))
return err;
in many functions of the endpoint controller core code.
Signed-off-by: Damien Le Moal <dlemoal@kernel.org>
Reviewed-by: Manivannan Sadhasivam <manivannan.sadhasivam@linaro.org>
Reviewed-by: Niklas Cassel <cassel@kernel.org>
---
drivers/pci/endpoint/pci-epc-core.c | 79 +++++++++++------------------
1 file changed, 31 insertions(+), 48 deletions(-)
diff --git a/drivers/pci/endpoint/pci-epc-core.c b/drivers/pci/endpoint/pci-epc-core.c
index 17f007109255..b854f1bab26f 100644
--- a/drivers/pci/endpoint/pci-epc-core.c
+++ b/drivers/pci/endpoint/pci-epc-core.c
@@ -128,6 +128,18 @@ enum pci_barno pci_epc_get_next_free_bar(const struct pci_epc_features
}
EXPORT_SYMBOL_GPL(pci_epc_get_next_free_bar);
+static bool pci_epc_function_is_valid(struct pci_epc *epc,
+ u8 func_no, u8 vfunc_no)
+{
+ if (IS_ERR_OR_NULL(epc) || func_no >= epc->max_functions)
+ return false;
+
+ if (vfunc_no > 0 && (!epc->max_vfs || vfunc_no > epc->max_vfs[func_no]))
+ return false;
+
+ return true;
+}
+
/**
* pci_epc_get_features() - get the features supported by EPC
* @epc: the features supported by *this* EPC device will be returned
@@ -145,10 +157,7 @@ const struct pci_epc_features *pci_epc_get_features(struct pci_epc *epc,
{
const struct pci_epc_features *epc_features;
- if (IS_ERR_OR_NULL(epc) || func_no >= epc->max_functions)
- return NULL;
-
- if (vfunc_no > 0 && (!epc->max_vfs || vfunc_no > epc->max_vfs[func_no]))
+ if (!pci_epc_function_is_valid(epc, func_no, vfunc_no))
return NULL;
if (!epc->ops->get_features)
@@ -218,10 +227,7 @@ int pci_epc_raise_irq(struct pci_epc *epc, u8 func_no, u8 vfunc_no,
{
int ret;
- if (IS_ERR_OR_NULL(epc) || func_no >= epc->max_functions)
- return -EINVAL;
-
- if (vfunc_no > 0 && (!epc->max_vfs || vfunc_no > epc->max_vfs[func_no]))
+ if (!pci_epc_function_is_valid(epc, func_no, vfunc_no))
return -EINVAL;
if (!epc->ops->raise_irq)
@@ -262,10 +268,7 @@ int pci_epc_map_msi_irq(struct pci_epc *epc, u8 func_no, u8 vfunc_no,
{
int ret;
- if (IS_ERR_OR_NULL(epc))
- return -EINVAL;
-
- if (vfunc_no > 0 && (!epc->max_vfs || vfunc_no > epc->max_vfs[func_no]))
+ if (!pci_epc_function_is_valid(epc, func_no, vfunc_no))
return -EINVAL;
if (!epc->ops->map_msi_irq)
@@ -293,10 +296,7 @@ int pci_epc_get_msi(struct pci_epc *epc, u8 func_no, u8 vfunc_no)
{
int interrupt;
- if (IS_ERR_OR_NULL(epc) || func_no >= epc->max_functions)
- return 0;
-
- if (vfunc_no > 0 && (!epc->max_vfs || vfunc_no > epc->max_vfs[func_no]))
+ if (!pci_epc_function_is_valid(epc, func_no, vfunc_no))
return 0;
if (!epc->ops->get_msi)
@@ -329,11 +329,10 @@ int pci_epc_set_msi(struct pci_epc *epc, u8 func_no, u8 vfunc_no, u8 interrupts)
int ret;
u8 encode_int;
- if (IS_ERR_OR_NULL(epc) || func_no >= epc->max_functions ||
- interrupts < 1 || interrupts > 32)
+ if (!pci_epc_function_is_valid(epc, func_no, vfunc_no))
return -EINVAL;
- if (vfunc_no > 0 && (!epc->max_vfs || vfunc_no > epc->max_vfs[func_no]))
+ if (interrupts < 1 || interrupts > 32)
return -EINVAL;
if (!epc->ops->set_msi)
@@ -361,10 +360,7 @@ int pci_epc_get_msix(struct pci_epc *epc, u8 func_no, u8 vfunc_no)
{
int interrupt;
- if (IS_ERR_OR_NULL(epc) || func_no >= epc->max_functions)
- return 0;
-
- if (vfunc_no > 0 && (!epc->max_vfs || vfunc_no > epc->max_vfs[func_no]))
+ if (!pci_epc_function_is_valid(epc, func_no, vfunc_no))
return 0;
if (!epc->ops->get_msix)
@@ -397,11 +393,10 @@ int pci_epc_set_msix(struct pci_epc *epc, u8 func_no, u8 vfunc_no,
{
int ret;
- if (IS_ERR_OR_NULL(epc) || func_no >= epc->max_functions ||
- interrupts < 1 || interrupts > 2048)
+ if (!pci_epc_function_is_valid(epc, func_no, vfunc_no))
return -EINVAL;
- if (vfunc_no > 0 && (!epc->max_vfs || vfunc_no > epc->max_vfs[func_no]))
+ if (interrupts < 1 || interrupts > 2048)
return -EINVAL;
if (!epc->ops->set_msix)
@@ -428,10 +423,7 @@ EXPORT_SYMBOL_GPL(pci_epc_set_msix);
void pci_epc_unmap_addr(struct pci_epc *epc, u8 func_no, u8 vfunc_no,
phys_addr_t phys_addr)
{
- if (IS_ERR_OR_NULL(epc) || func_no >= epc->max_functions)
- return;
-
- if (vfunc_no > 0 && (!epc->max_vfs || vfunc_no > epc->max_vfs[func_no]))
+ if (!pci_epc_function_is_valid(epc, func_no, vfunc_no))
return;
if (!epc->ops->unmap_addr)
@@ -459,10 +451,7 @@ int pci_epc_map_addr(struct pci_epc *epc, u8 func_no, u8 vfunc_no,
{
int ret;
- if (IS_ERR_OR_NULL(epc) || func_no >= epc->max_functions)
- return -EINVAL;
-
- if (vfunc_no > 0 && (!epc->max_vfs || vfunc_no > epc->max_vfs[func_no]))
+ if (!pci_epc_function_is_valid(epc, func_no, vfunc_no))
return -EINVAL;
if (!epc->ops->map_addr)
@@ -489,12 +478,11 @@ EXPORT_SYMBOL_GPL(pci_epc_map_addr);
void pci_epc_clear_bar(struct pci_epc *epc, u8 func_no, u8 vfunc_no,
struct pci_epf_bar *epf_bar)
{
- if (IS_ERR_OR_NULL(epc) || func_no >= epc->max_functions ||
- (epf_bar->barno == BAR_5 &&
- epf_bar->flags & PCI_BASE_ADDRESS_MEM_TYPE_64))
+ if (!pci_epc_function_is_valid(epc, func_no, vfunc_no))
return;
- if (vfunc_no > 0 && (!epc->max_vfs || vfunc_no > epc->max_vfs[func_no]))
+ if (epf_bar->barno == BAR_5 &&
+ epf_bar->flags & PCI_BASE_ADDRESS_MEM_TYPE_64)
return;
if (!epc->ops->clear_bar)
@@ -521,18 +509,16 @@ int pci_epc_set_bar(struct pci_epc *epc, u8 func_no, u8 vfunc_no,
int ret;
int flags = epf_bar->flags;
- if (IS_ERR_OR_NULL(epc) || func_no >= epc->max_functions ||
- (epf_bar->barno == BAR_5 &&
- flags & PCI_BASE_ADDRESS_MEM_TYPE_64) ||
+ if (!pci_epc_function_is_valid(epc, func_no, vfunc_no))
+ return -EINVAL;
+
+ if ((epf_bar->barno == BAR_5 && flags & PCI_BASE_ADDRESS_MEM_TYPE_64) ||
(flags & PCI_BASE_ADDRESS_SPACE_IO &&
flags & PCI_BASE_ADDRESS_IO_MASK) ||
(upper_32_bits(epf_bar->size) &&
!(flags & PCI_BASE_ADDRESS_MEM_TYPE_64)))
return -EINVAL;
- if (vfunc_no > 0 && (!epc->max_vfs || vfunc_no > epc->max_vfs[func_no]))
- return -EINVAL;
-
if (!epc->ops->set_bar)
return 0;
@@ -561,10 +547,7 @@ int pci_epc_write_header(struct pci_epc *epc, u8 func_no, u8 vfunc_no,
{
int ret;
- if (IS_ERR_OR_NULL(epc) || func_no >= epc->max_functions)
- return -EINVAL;
-
- if (vfunc_no > 0 && (!epc->max_vfs || vfunc_no > epc->max_vfs[func_no]))
+ if (!pci_epc_function_is_valid(epc, func_no, vfunc_no))
return -EINVAL;
/* Only Virtual Function #1 has deviceID */
--
2.47.0
^ permalink raw reply related [flat|nested] 13+ messages in thread* [PATCH v5 2/6] PCI: endpoint: Improve pci_epc_mem_alloc_addr()
2024-10-11 12:01 [PATCH v5 0/6] Improve PCI memory mapping API Damien Le Moal
2024-10-11 12:01 ` [PATCH v5 1/6] PCI: endpoint: Introduce pci_epc_function_is_valid() Damien Le Moal
@ 2024-10-11 12:01 ` Damien Le Moal
2024-10-11 12:01 ` [PATCH v5 3/6] PCI: endpoint: Introduce pci_epc_mem_map()/unmap() Damien Le Moal
` (3 subsequent siblings)
5 siblings, 0 replies; 13+ messages in thread
From: Damien Le Moal @ 2024-10-11 12:01 UTC (permalink / raw)
To: Manivannan Sadhasivam, Krzysztof Wilczyński,
Kishon Vijay Abraham I, Bjorn Helgaas, Lorenzo Pieralisi,
Rob Herring, Jonathan Corbet, Jingoo Han, linux-pci
Cc: Rick Wertenbroek, Niklas Cassel
There is no point in attempting to allocate memory from an endpoint
controller memory window if the requested size is larger than the memory
window size. Add a check to skip bitmap_find_free_region() calls for
such case. This check can be done without the mem->lock mutex held as
memory window sizes are constant and never modified at runtime.
Also change the final return to return NULL to simplify the code.
Signed-off-by: Damien Le Moal <dlemoal@kernel.org>
Reviewed-by: Niklas Cassel <cassel@kernel.org>
Reviewed-by: Manivannan Sadhasivam <manivannan.sadhasivam@linaro.org>
---
drivers/pci/endpoint/pci-epc-mem.c | 9 ++++++---
1 file changed, 6 insertions(+), 3 deletions(-)
diff --git a/drivers/pci/endpoint/pci-epc-mem.c b/drivers/pci/endpoint/pci-epc-mem.c
index a9c028f58da1..218a60e945db 100644
--- a/drivers/pci/endpoint/pci-epc-mem.c
+++ b/drivers/pci/endpoint/pci-epc-mem.c
@@ -178,7 +178,7 @@ EXPORT_SYMBOL_GPL(pci_epc_mem_exit);
void __iomem *pci_epc_mem_alloc_addr(struct pci_epc *epc,
phys_addr_t *phys_addr, size_t size)
{
- void __iomem *virt_addr = NULL;
+ void __iomem *virt_addr;
struct pci_epc_mem *mem;
unsigned int page_shift;
size_t align_size;
@@ -188,10 +188,13 @@ void __iomem *pci_epc_mem_alloc_addr(struct pci_epc *epc,
for (i = 0; i < epc->num_windows; i++) {
mem = epc->windows[i];
- mutex_lock(&mem->lock);
+ if (size > mem->window.size)
+ continue;
+
align_size = ALIGN(size, mem->window.page_size);
order = pci_epc_mem_get_order(mem, align_size);
+ mutex_lock(&mem->lock);
pageno = bitmap_find_free_region(mem->bitmap, mem->pages,
order);
if (pageno >= 0) {
@@ -211,7 +214,7 @@ void __iomem *pci_epc_mem_alloc_addr(struct pci_epc *epc,
mutex_unlock(&mem->lock);
}
- return virt_addr;
+ return NULL;
}
EXPORT_SYMBOL_GPL(pci_epc_mem_alloc_addr);
--
2.47.0
^ permalink raw reply related [flat|nested] 13+ messages in thread* [PATCH v5 3/6] PCI: endpoint: Introduce pci_epc_mem_map()/unmap()
2024-10-11 12:01 [PATCH v5 0/6] Improve PCI memory mapping API Damien Le Moal
2024-10-11 12:01 ` [PATCH v5 1/6] PCI: endpoint: Introduce pci_epc_function_is_valid() Damien Le Moal
2024-10-11 12:01 ` [PATCH v5 2/6] PCI: endpoint: Improve pci_epc_mem_alloc_addr() Damien Le Moal
@ 2024-10-11 12:01 ` Damien Le Moal
2024-10-11 12:07 ` Niklas Cassel
2024-10-11 12:01 ` [PATCH v5 4/6] PCI: endpoint: Update documentation Damien Le Moal
` (2 subsequent siblings)
5 siblings, 1 reply; 13+ messages in thread
From: Damien Le Moal @ 2024-10-11 12:01 UTC (permalink / raw)
To: Manivannan Sadhasivam, Krzysztof Wilczyński,
Kishon Vijay Abraham I, Bjorn Helgaas, Lorenzo Pieralisi,
Rob Herring, Jonathan Corbet, Jingoo Han, linux-pci
Cc: Rick Wertenbroek, Niklas Cassel
Some endpoint controllers have requirements on the alignment of the
controller physical memory address that must be used to map a RC PCI
address region. For instance, the endpoint controller of the RK3399 SoC
uses at most the lower 20 bits of a physical memory address region as
the lower bits of a RC PCI address region. For mapping a PCI address
region of size bytes starting from pci_addr, the exact number of
address bits used is the number of address bits changing in the address
range [pci_addr..pci_addr + size - 1]. For this example, this creates
the following constraints:
1) The offset into the controller physical memory allocated for a
mapping depends on the mapping size *and* the starting PCI address
for the mapping.
2) A mapping size cannot exceed the controller windows size (1MB) minus
the offset needed into the allocated physical memory, which can end
up being a smaller size than the desired mapping size.
Handling these constraints independently of the controller being used
in an endpoint function driver is not possible with the current EPC
API as only the ->align field in struct pci_epc_features is provided
but used for BAR (inbound ATU mappings) mapping only. A new API is
needed for function drivers to discover mapping constraints and handle
non-static requirements based on the RC PCI address range to access.
Introduce endpoint controller operation ->get_mem_map() to allow
the EPC core functions to obtain the size and the offset into a
controller address region that must be allocated and mapped to access
a RC PCI address region. The size of the mapping provided by the
get_mem_map() operation can then be used as the size argument for the
function pci_epc_mem_alloc_addr() and the offset into the allocated
controller memory provided can be used to correctly handle data
transfers. For endpoint controllers that have PCI address alignment
constraints, get_mem_map() may indicate upon return an effective PCI
address region size that is smaller (but not 0) than the requested PCI
address region size.
The controller ->get_mem_map() operation is optional: controllers that
do not have any alignment constraints for mapping RC PCI address regions
do not need to implement this operation. For such controllers, it is
always assumed that the mapping size is equal to the requested size of
the PCI region and that the mapping offset is 0.
The function pci_epc_mem_map() is introduced to use this new controller
operation (if it is defined) to handle controller memory allocation and
mapping to a RC PCI address region in endpoint function drivers.
This function first uses the ->get_mem_map() controller operation to
determine the controller memory address size (and offset into) needed
for mapping an RC PCI address region. The result of this function
operation is used to allocate a controller physical memory region using
pci_epc_mem_alloc_addr() and then to map that memory to the RC PCI
address space with pci_epc_map_addr().
Since ->get_mem_map() () may indicate that not all of a RC PCI
address region can be mapped, pci_epc_mem_map() may only partially map
the RC PCI address region specified. It is the responsibility of the
caller (an endpoint function driver) to handle such smaller mapping
by repeatedly using pci_epc_mem_map() over the desried PCI address
range.
The counterpart of pci_epc_mem_map() to unmap and free the controller
memory address region is pci_epc_mem_unmap().
Both functions as well as the ->get_mem_map() controller operation
operate using the new struct pci_epc_map data structure. This new
structure represents a mapping PCI address, mapping effective size, the
size and offset into the controller memory needed for the mapping as
well as the physical and virtual CPU addresses of the mapping (phys_base
and virt_base fields). For convenience, the physical and virtual CPU
addresses within that mapping to access the target RC PCI address region
are also provided (phys_addr and virt_addr fields).
Endpoint function drivers can use struct pci_epc_map to access the
mapped RC PCI address region using the ->virt_addr and ->pci_size
fields.
Co-developed-by: Rick Wertenbroek <rick.wertenbroek@gmail.com>
Signed-off-by: Rick Wertenbroek <rick.wertenbroek@gmail.com>
Signed-off-by: Damien Le Moal <dlemoal@kernel.org>
---
drivers/pci/endpoint/pci-epc-core.c | 126 ++++++++++++++++++++++++++++
include/linux/pci-epc.h | 39 +++++++++
2 files changed, 165 insertions(+)
diff --git a/drivers/pci/endpoint/pci-epc-core.c b/drivers/pci/endpoint/pci-epc-core.c
index b854f1bab26f..b6bf6d9f9f85 100644
--- a/drivers/pci/endpoint/pci-epc-core.c
+++ b/drivers/pci/endpoint/pci-epc-core.c
@@ -466,6 +466,132 @@ int pci_epc_map_addr(struct pci_epc *epc, u8 func_no, u8 vfunc_no,
}
EXPORT_SYMBOL_GPL(pci_epc_map_addr);
+/*
+ * Determine the actual PCI address range that should be mapped to access
+ * @pci_size from @pci_addr. This is done using the controller get_mem_map
+ * operation if that operation is defined. Otherwise, simply assume that the
+ * controller has no mapping alignment constraint and return the requested range
+ * as-is.
+ */
+static int pci_epc_get_mem_map(struct pci_epc *epc, u8 func_no, u8 vfunc_no,
+ u64 pci_addr, size_t pci_size,
+ struct pci_epc_map *map)
+{
+ int ret;
+
+ /*
+ * Initialize and remember the PCI address region to be mapped. The
+ * controller ->get_mem_map() operation may change the map->pci_size to a
+ * smaller value.
+ */
+ memset(map, 0, sizeof(*map));
+ map->pci_addr = pci_addr;
+ map->pci_size = pci_size;
+
+ if (!epc->ops->get_mem_map) {
+ /*
+ * Assume that the EP controller has no alignment constraint,
+ * that is, that the PCI address to map and the size of the
+ * controller memory needed for the mapping are the same as
+ * specified by the caller.
+ */
+ map->map_pci_addr = pci_addr;
+ map->map_size = pci_size;
+ map->map_ofst = 0;
+ return 0;
+ }
+
+ mutex_lock(&epc->lock);
+ ret = epc->ops->get_mem_map(epc, func_no, vfunc_no, map);
+ mutex_unlock(&epc->lock);
+
+ return ret;
+}
+
+/**
+ * pci_epc_mem_map() - allocate and map a PCI address to a CPU address
+ * @epc: the EPC device on which the CPU address is to be allocated and mapped
+ * @func_no: the physical endpoint function number in the EPC device
+ * @vfunc_no: the virtual endpoint function number in the physical function
+ * @pci_addr: PCI address to which the CPU address should be mapped
+ * @pci_size: the number of bytes to map starting from @pci_addr
+ * @map: where to return the mapping information
+ *
+ * Allocate a controller memory address region and map it to a RC PCI address
+ * region, taking into account the controller physical address mapping
+ * constraints using pci_epc_get_mem_map().
+ * The effective size of the PCI address range mapped from @pci_addr is
+ * indicated by @map->pci_size. This size may be less than the requested
+ * @pci_size. The local virtual CPU address for the mapping is indicated by
+ * @map->virt_addr (@map->phys_addr indicates the physical address).
+ * The size and CPU address of the controller memory allocated and mapped are
+ * respectively indicated by @map->map_size and @map->virt_base (and
+ * @map->phys_base).
+ *
+ * Returns 0 on success and a negative error code in case of error.
+ */
+int pci_epc_mem_map(struct pci_epc *epc, u8 func_no, u8 vfunc_no,
+ u64 pci_addr, size_t pci_size, struct pci_epc_map *map)
+{
+ int ret;
+
+ if (!pci_epc_function_is_valid(epc, func_no, vfunc_no))
+ return -EINVAL;
+
+ if (!pci_size || !map)
+ return -EINVAL;
+
+ ret = pci_epc_get_mem_map(epc, func_no, vfunc_no,
+ pci_addr, pci_size, map);
+ if (ret)
+ return ret;
+
+ map->virt_base = pci_epc_mem_alloc_addr(epc, &map->phys_base,
+ map->map_size);
+ if (!map->virt_base)
+ return -ENOMEM;
+
+ map->phys_addr = map->phys_base + map->map_ofst;
+ map->virt_addr = map->virt_base + map->map_ofst;
+
+ ret = pci_epc_map_addr(epc, func_no, vfunc_no, map->phys_base,
+ map->map_pci_addr, map->map_size);
+ if (ret) {
+ pci_epc_mem_free_addr(epc, map->phys_base, map->virt_base,
+ map->map_size);
+ map->virt_base = 0;
+ return ret;
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(pci_epc_mem_map);
+
+/**
+ * pci_epc_mem_unmap() - unmap and free a CPU address region
+ * @epc: the EPC device on which the CPU address is allocated and mapped
+ * @func_no: the physical endpoint function number in the EPC device
+ * @vfunc_no: the virtual endpoint function number in the physical function
+ * @map: the mapping information
+ *
+ * Unmap and free a CPU address region that was allocated and mapped with
+ * pci_epc_mem_map().
+ */
+void pci_epc_mem_unmap(struct pci_epc *epc, u8 func_no, u8 vfunc_no,
+ struct pci_epc_map *map)
+{
+ if (!pci_epc_function_is_valid(epc, func_no, vfunc_no))
+ return;
+
+ if (!map || !map->virt_base)
+ return;
+
+ pci_epc_unmap_addr(epc, func_no, vfunc_no, map->phys_base);
+ pci_epc_mem_free_addr(epc, map->phys_base, map->virt_base,
+ map->map_size);
+}
+EXPORT_SYMBOL_GPL(pci_epc_mem_unmap);
+
/**
* pci_epc_clear_bar() - reset the BAR
* @epc: the EPC device for which the BAR has to be cleared
diff --git a/include/linux/pci-epc.h b/include/linux/pci-epc.h
index 42ef06136bd1..b5f5c1eb54c5 100644
--- a/include/linux/pci-epc.h
+++ b/include/linux/pci-epc.h
@@ -32,11 +32,44 @@ pci_epc_interface_string(enum pci_epc_interface_type type)
}
}
+/**
+ * struct pci_epc_map - information about EPC memory for mapping a RC PCI
+ * address range
+ * @pci_addr: start address of the RC PCI address range to map
+ * @pci_size: size of the RC PCI address range mapped from @pci_addr
+ * @map_pci_addr: RC PCI address used as the first address mapped (may be lower
+ * than @pci_addr)
+ * @map_size: size of the controller memory needed for mapping the RC PCI address
+ * range @pci_addr..@pci_addr+@pci_size
+ * @map_ofst: offset into the mapped controller memory to access @pci_addr
+ * @phys_base: base physical address of the allocated EPC memory for mapping the
+ * RC PCI address range
+ * @phys_addr: physical address at which @pci_addr is mapped
+ * @virt_base: base virtual address of the allocated EPC memory for mapping the
+ * RC PCI address range
+ * @virt_addr: virtual address at which @pci_addr is mapped
+ */
+struct pci_epc_map {
+ phys_addr_t pci_addr;
+ size_t pci_size;
+
+ phys_addr_t map_pci_addr;
+ size_t map_size;
+ phys_addr_t map_ofst;
+
+ phys_addr_t phys_base;
+ phys_addr_t phys_addr;
+ void __iomem *virt_base;
+ void __iomem *virt_addr;
+};
+
/**
* struct pci_epc_ops - set of function pointers for performing EPC operations
* @write_header: ops to populate configuration space header
* @set_bar: ops to configure the BAR
* @clear_bar: ops to reset the BAR
+ * @get_mem_map: operation to get the size and offset into a controller memory
+ * window needed to map an RC PCI address region
* @map_addr: ops to map CPU address to PCI address
* @unmap_addr: ops to unmap CPU address and PCI address
* @set_msi: ops to set the requested number of MSI interrupts in the MSI
@@ -61,6 +94,8 @@ struct pci_epc_ops {
struct pci_epf_bar *epf_bar);
void (*clear_bar)(struct pci_epc *epc, u8 func_no, u8 vfunc_no,
struct pci_epf_bar *epf_bar);
+ int (*get_mem_map)(struct pci_epc *epc, u8 func_no, u8 vfunc_no,
+ struct pci_epc_map *map);
int (*map_addr)(struct pci_epc *epc, u8 func_no, u8 vfunc_no,
phys_addr_t addr, u64 pci_addr, size_t size);
void (*unmap_addr)(struct pci_epc *epc, u8 func_no, u8 vfunc_no,
@@ -278,6 +313,10 @@ void __iomem *pci_epc_mem_alloc_addr(struct pci_epc *epc,
phys_addr_t *phys_addr, size_t size);
void pci_epc_mem_free_addr(struct pci_epc *epc, phys_addr_t phys_addr,
void __iomem *virt_addr, size_t size);
+int pci_epc_mem_map(struct pci_epc *epc, u8 func_no, u8 vfunc_no,
+ u64 pci_addr, size_t pci_size, struct pci_epc_map *map);
+void pci_epc_mem_unmap(struct pci_epc *epc, u8 func_no, u8 vfunc_no,
+ struct pci_epc_map *map);
#else
static inline void pci_epc_init_notify(struct pci_epc *epc)
--
2.47.0
^ permalink raw reply related [flat|nested] 13+ messages in thread* Re: [PATCH v5 3/6] PCI: endpoint: Introduce pci_epc_mem_map()/unmap()
2024-10-11 12:01 ` [PATCH v5 3/6] PCI: endpoint: Introduce pci_epc_mem_map()/unmap() Damien Le Moal
@ 2024-10-11 12:07 ` Niklas Cassel
2024-10-11 12:21 ` Damien Le Moal
2024-10-11 12:22 ` Niklas Cassel
0 siblings, 2 replies; 13+ messages in thread
From: Niklas Cassel @ 2024-10-11 12:07 UTC (permalink / raw)
To: Damien Le Moal
Cc: Manivannan Sadhasivam, Krzysztof Wilczyński,
Kishon Vijay Abraham I, Bjorn Helgaas, Lorenzo Pieralisi,
Rob Herring, Jonathan Corbet, Jingoo Han, linux-pci,
Rick Wertenbroek
On Fri, Oct 11, 2024 at 09:01:12PM +0900, Damien Le Moal wrote:
> Some endpoint controllers have requirements on the alignment of the
> controller physical memory address that must be used to map a RC PCI
> address region. For instance, the endpoint controller of the RK3399 SoC
> uses at most the lower 20 bits of a physical memory address region as
> the lower bits of a RC PCI address region. For mapping a PCI address
> region of size bytes starting from pci_addr, the exact number of
> address bits used is the number of address bits changing in the address
> range [pci_addr..pci_addr + size - 1]. For this example, this creates
> the following constraints:
> 1) The offset into the controller physical memory allocated for a
> mapping depends on the mapping size *and* the starting PCI address
> for the mapping.
> 2) A mapping size cannot exceed the controller windows size (1MB) minus
> the offset needed into the allocated physical memory, which can end
> up being a smaller size than the desired mapping size.
>
> Handling these constraints independently of the controller being used
> in an endpoint function driver is not possible with the current EPC
> API as only the ->align field in struct pci_epc_features is provided
> but used for BAR (inbound ATU mappings) mapping only. A new API is
> needed for function drivers to discover mapping constraints and handle
> non-static requirements based on the RC PCI address range to access.
>
> Introduce endpoint controller operation ->get_mem_map() to allow
> the EPC core functions to obtain the size and the offset into a
> controller address region that must be allocated and mapped to access
> a RC PCI address region. The size of the mapping provided by the
> get_mem_map() operation can then be used as the size argument for the
> function pci_epc_mem_alloc_addr() and the offset into the allocated
> controller memory provided can be used to correctly handle data
> transfers. For endpoint controllers that have PCI address alignment
> constraints, get_mem_map() may indicate upon return an effective PCI
> address region size that is smaller (but not 0) than the requested PCI
> address region size.
>
> The controller ->get_mem_map() operation is optional: controllers that
> do not have any alignment constraints for mapping RC PCI address regions
> do not need to implement this operation. For such controllers, it is
> always assumed that the mapping size is equal to the requested size of
> the PCI region and that the mapping offset is 0.
>
> The function pci_epc_mem_map() is introduced to use this new controller
> operation (if it is defined) to handle controller memory allocation and
> mapping to a RC PCI address region in endpoint function drivers.
>
> This function first uses the ->get_mem_map() controller operation to
> determine the controller memory address size (and offset into) needed
> for mapping an RC PCI address region. The result of this function
> operation is used to allocate a controller physical memory region using
> pci_epc_mem_alloc_addr() and then to map that memory to the RC PCI
> address space with pci_epc_map_addr().
>
> Since ->get_mem_map() () may indicate that not all of a RC PCI
> address region can be mapped, pci_epc_mem_map() may only partially map
> the RC PCI address region specified. It is the responsibility of the
> caller (an endpoint function driver) to handle such smaller mapping
> by repeatedly using pci_epc_mem_map() over the desried PCI address
> range.
>
> The counterpart of pci_epc_mem_map() to unmap and free the controller
> memory address region is pci_epc_mem_unmap().
>
> Both functions as well as the ->get_mem_map() controller operation
> operate using the new struct pci_epc_map data structure. This new
> structure represents a mapping PCI address, mapping effective size, the
> size and offset into the controller memory needed for the mapping as
> well as the physical and virtual CPU addresses of the mapping (phys_base
> and virt_base fields). For convenience, the physical and virtual CPU
> addresses within that mapping to access the target RC PCI address region
> are also provided (phys_addr and virt_addr fields).
>
> Endpoint function drivers can use struct pci_epc_map to access the
> mapped RC PCI address region using the ->virt_addr and ->pci_size
> fields.
>
> Co-developed-by: Rick Wertenbroek <rick.wertenbroek@gmail.com>
> Signed-off-by: Rick Wertenbroek <rick.wertenbroek@gmail.com>
> Signed-off-by: Damien Le Moal <dlemoal@kernel.org>
> ---
> drivers/pci/endpoint/pci-epc-core.c | 126 ++++++++++++++++++++++++++++
> include/linux/pci-epc.h | 39 +++++++++
> 2 files changed, 165 insertions(+)
>
> diff --git a/drivers/pci/endpoint/pci-epc-core.c b/drivers/pci/endpoint/pci-epc-core.c
> index b854f1bab26f..b6bf6d9f9f85 100644
> --- a/drivers/pci/endpoint/pci-epc-core.c
> +++ b/drivers/pci/endpoint/pci-epc-core.c
> @@ -466,6 +466,132 @@ int pci_epc_map_addr(struct pci_epc *epc, u8 func_no, u8 vfunc_no,
> }
> EXPORT_SYMBOL_GPL(pci_epc_map_addr);
>
> +/*
> + * Determine the actual PCI address range that should be mapped to access
> + * @pci_size from @pci_addr. This is done using the controller get_mem_map
> + * operation if that operation is defined. Otherwise, simply assume that the
> + * controller has no mapping alignment constraint and return the requested range
> + * as-is.
> + */
> +static int pci_epc_get_mem_map(struct pci_epc *epc, u8 func_no, u8 vfunc_no,
> + u64 pci_addr, size_t pci_size,
> + struct pci_epc_map *map)
> +{
> + int ret;
> +
> + /*
> + * Initialize and remember the PCI address region to be mapped. The
> + * controller ->get_mem_map() operation may change the map->pci_size to a
> + * smaller value.
> + */
> + memset(map, 0, sizeof(*map));
> + map->pci_addr = pci_addr;
> + map->pci_size = pci_size;
> +
> + if (!epc->ops->get_mem_map) {
> + /*
> + * Assume that the EP controller has no alignment constraint,
> + * that is, that the PCI address to map and the size of the
> + * controller memory needed for the mapping are the same as
> + * specified by the caller.
> + */
> + map->map_pci_addr = pci_addr;
> + map->map_size = pci_size;
> + map->map_ofst = 0;
> + return 0;
> + }
> +
> + mutex_lock(&epc->lock);
> + ret = epc->ops->get_mem_map(epc, func_no, vfunc_no, map);
> + mutex_unlock(&epc->lock);
> +
> + return ret;
> +}
> +
> +/**
> + * pci_epc_mem_map() - allocate and map a PCI address to a CPU address
> + * @epc: the EPC device on which the CPU address is to be allocated and mapped
> + * @func_no: the physical endpoint function number in the EPC device
> + * @vfunc_no: the virtual endpoint function number in the physical function
> + * @pci_addr: PCI address to which the CPU address should be mapped
> + * @pci_size: the number of bytes to map starting from @pci_addr
> + * @map: where to return the mapping information
> + *
> + * Allocate a controller memory address region and map it to a RC PCI address
> + * region, taking into account the controller physical address mapping
> + * constraints using pci_epc_get_mem_map().
> + * The effective size of the PCI address range mapped from @pci_addr is
> + * indicated by @map->pci_size. This size may be less than the requested
> + * @pci_size. The local virtual CPU address for the mapping is indicated by
> + * @map->virt_addr (@map->phys_addr indicates the physical address).
> + * The size and CPU address of the controller memory allocated and mapped are
> + * respectively indicated by @map->map_size and @map->virt_base (and
> + * @map->phys_base).
> + *
> + * Returns 0 on success and a negative error code in case of error.
> + */
> +int pci_epc_mem_map(struct pci_epc *epc, u8 func_no, u8 vfunc_no,
> + u64 pci_addr, size_t pci_size, struct pci_epc_map *map)
> +{
> + int ret;
> +
> + if (!pci_epc_function_is_valid(epc, func_no, vfunc_no))
> + return -EINVAL;
> +
> + if (!pci_size || !map)
> + return -EINVAL;
> +
> + ret = pci_epc_get_mem_map(epc, func_no, vfunc_no,
> + pci_addr, pci_size, map);
> + if (ret)
> + return ret;
> +
> + map->virt_base = pci_epc_mem_alloc_addr(epc, &map->phys_base,
> + map->map_size);
> + if (!map->virt_base)
> + return -ENOMEM;
> +
> + map->phys_addr = map->phys_base + map->map_ofst;
> + map->virt_addr = map->virt_base + map->map_ofst;
> +
> + ret = pci_epc_map_addr(epc, func_no, vfunc_no, map->phys_base,
> + map->map_pci_addr, map->map_size);
> + if (ret) {
> + pci_epc_mem_free_addr(epc, map->phys_base, map->virt_base,
> + map->map_size);
> + map->virt_base = 0;
As reported by the kernel test robot on both v3 and v4, this should be:
map->virt_base = NULL;
otherwise you introduce a new sparse warning.
> + return ret;
> + }
> +
> + return 0;
> +}
> +EXPORT_SYMBOL_GPL(pci_epc_mem_map);
> +
> +/**
> + * pci_epc_mem_unmap() - unmap and free a CPU address region
> + * @epc: the EPC device on which the CPU address is allocated and mapped
> + * @func_no: the physical endpoint function number in the EPC device
> + * @vfunc_no: the virtual endpoint function number in the physical function
> + * @map: the mapping information
> + *
> + * Unmap and free a CPU address region that was allocated and mapped with
> + * pci_epc_mem_map().
> + */
> +void pci_epc_mem_unmap(struct pci_epc *epc, u8 func_no, u8 vfunc_no,
> + struct pci_epc_map *map)
> +{
> + if (!pci_epc_function_is_valid(epc, func_no, vfunc_no))
> + return;
> +
> + if (!map || !map->virt_base)
> + return;
> +
> + pci_epc_unmap_addr(epc, func_no, vfunc_no, map->phys_base);
> + pci_epc_mem_free_addr(epc, map->phys_base, map->virt_base,
> + map->map_size);
> +}
> +EXPORT_SYMBOL_GPL(pci_epc_mem_unmap);
> +
> /**
> * pci_epc_clear_bar() - reset the BAR
> * @epc: the EPC device for which the BAR has to be cleared
> diff --git a/include/linux/pci-epc.h b/include/linux/pci-epc.h
> index 42ef06136bd1..b5f5c1eb54c5 100644
> --- a/include/linux/pci-epc.h
> +++ b/include/linux/pci-epc.h
> @@ -32,11 +32,44 @@ pci_epc_interface_string(enum pci_epc_interface_type type)
> }
> }
>
> +/**
> + * struct pci_epc_map - information about EPC memory for mapping a RC PCI
> + * address range
> + * @pci_addr: start address of the RC PCI address range to map
> + * @pci_size: size of the RC PCI address range mapped from @pci_addr
> + * @map_pci_addr: RC PCI address used as the first address mapped (may be lower
> + * than @pci_addr)
> + * @map_size: size of the controller memory needed for mapping the RC PCI address
> + * range @pci_addr..@pci_addr+@pci_size
> + * @map_ofst: offset into the mapped controller memory to access @pci_addr
> + * @phys_base: base physical address of the allocated EPC memory for mapping the
> + * RC PCI address range
> + * @phys_addr: physical address at which @pci_addr is mapped
> + * @virt_base: base virtual address of the allocated EPC memory for mapping the
> + * RC PCI address range
> + * @virt_addr: virtual address at which @pci_addr is mapped
> + */
> +struct pci_epc_map {
> + phys_addr_t pci_addr;
> + size_t pci_size;
> +
> + phys_addr_t map_pci_addr;
> + size_t map_size;
> + phys_addr_t map_ofst;
> +
> + phys_addr_t phys_base;
> + phys_addr_t phys_addr;
> + void __iomem *virt_base;
> + void __iomem *virt_addr;
> +};
> +
> /**
> * struct pci_epc_ops - set of function pointers for performing EPC operations
> * @write_header: ops to populate configuration space header
> * @set_bar: ops to configure the BAR
> * @clear_bar: ops to reset the BAR
> + * @get_mem_map: operation to get the size and offset into a controller memory
> + * window needed to map an RC PCI address region
> * @map_addr: ops to map CPU address to PCI address
> * @unmap_addr: ops to unmap CPU address and PCI address
> * @set_msi: ops to set the requested number of MSI interrupts in the MSI
> @@ -61,6 +94,8 @@ struct pci_epc_ops {
> struct pci_epf_bar *epf_bar);
> void (*clear_bar)(struct pci_epc *epc, u8 func_no, u8 vfunc_no,
> struct pci_epf_bar *epf_bar);
> + int (*get_mem_map)(struct pci_epc *epc, u8 func_no, u8 vfunc_no,
> + struct pci_epc_map *map);
> int (*map_addr)(struct pci_epc *epc, u8 func_no, u8 vfunc_no,
> phys_addr_t addr, u64 pci_addr, size_t size);
> void (*unmap_addr)(struct pci_epc *epc, u8 func_no, u8 vfunc_no,
> @@ -278,6 +313,10 @@ void __iomem *pci_epc_mem_alloc_addr(struct pci_epc *epc,
> phys_addr_t *phys_addr, size_t size);
> void pci_epc_mem_free_addr(struct pci_epc *epc, phys_addr_t phys_addr,
> void __iomem *virt_addr, size_t size);
> +int pci_epc_mem_map(struct pci_epc *epc, u8 func_no, u8 vfunc_no,
> + u64 pci_addr, size_t pci_size, struct pci_epc_map *map);
> +void pci_epc_mem_unmap(struct pci_epc *epc, u8 func_no, u8 vfunc_no,
> + struct pci_epc_map *map);
>
> #else
> static inline void pci_epc_init_notify(struct pci_epc *epc)
> --
> 2.47.0
>
^ permalink raw reply [flat|nested] 13+ messages in thread* Re: [PATCH v5 3/6] PCI: endpoint: Introduce pci_epc_mem_map()/unmap()
2024-10-11 12:07 ` Niklas Cassel
@ 2024-10-11 12:21 ` Damien Le Moal
2024-10-12 9:42 ` Manivannan Sadhasivam
2024-10-11 12:22 ` Niklas Cassel
1 sibling, 1 reply; 13+ messages in thread
From: Damien Le Moal @ 2024-10-11 12:21 UTC (permalink / raw)
To: Niklas Cassel
Cc: Manivannan Sadhasivam, Krzysztof Wilczyński,
Kishon Vijay Abraham I, Bjorn Helgaas, Lorenzo Pieralisi,
Rob Herring, Jonathan Corbet, Jingoo Han, linux-pci,
Rick Wertenbroek
On 10/11/24 21:07, Niklas Cassel wrote:
>> +int pci_epc_mem_map(struct pci_epc *epc, u8 func_no, u8 vfunc_no,
>> + u64 pci_addr, size_t pci_size, struct pci_epc_map *map)
>> +{
>> + int ret;
>> +
>> + if (!pci_epc_function_is_valid(epc, func_no, vfunc_no))
>> + return -EINVAL;
>> +
>> + if (!pci_size || !map)
>> + return -EINVAL;
>> +
>> + ret = pci_epc_get_mem_map(epc, func_no, vfunc_no,
>> + pci_addr, pci_size, map);
>> + if (ret)
>> + return ret;
>> +
>> + map->virt_base = pci_epc_mem_alloc_addr(epc, &map->phys_base,
>> + map->map_size);
>> + if (!map->virt_base)
>> + return -ENOMEM;
>> +
>> + map->phys_addr = map->phys_base + map->map_ofst;
>> + map->virt_addr = map->virt_base + map->map_ofst;
>> +
>> + ret = pci_epc_map_addr(epc, func_no, vfunc_no, map->phys_base,
>> + map->map_pci_addr, map->map_size);
>> + if (ret) {
>> + pci_epc_mem_free_addr(epc, map->phys_base, map->virt_base,
>> + map->map_size);
>> + map->virt_base = 0;
>
> As reported by the kernel test robot on both v3 and v4, this should be:
> map->virt_base = NULL;
> otherwise you introduce a new sparse warning.
Oops. Missed that... OK, sending v6 with this removed as that is not necessary
anyway.
>
>
>> + return ret;
>> + }
>> +
>> + return 0;
>> +}
>> +EXPORT_SYMBOL_GPL(pci_epc_mem_map);
>> +
>> +/**
>> + * pci_epc_mem_unmap() - unmap and free a CPU address region
>> + * @epc: the EPC device on which the CPU address is allocated and mapped
>> + * @func_no: the physical endpoint function number in the EPC device
>> + * @vfunc_no: the virtual endpoint function number in the physical function
>> + * @map: the mapping information
>> + *
>> + * Unmap and free a CPU address region that was allocated and mapped with
>> + * pci_epc_mem_map().
>> + */
>> +void pci_epc_mem_unmap(struct pci_epc *epc, u8 func_no, u8 vfunc_no,
>> + struct pci_epc_map *map)
>> +{
>> + if (!pci_epc_function_is_valid(epc, func_no, vfunc_no))
>> + return;
>> +
>> + if (!map || !map->virt_base)
>> + return;
>> +
>> + pci_epc_unmap_addr(epc, func_no, vfunc_no, map->phys_base);
>> + pci_epc_mem_free_addr(epc, map->phys_base, map->virt_base,
>> + map->map_size);
>> +}
>> +EXPORT_SYMBOL_GPL(pci_epc_mem_unmap);
>> +
>> /**
>> * pci_epc_clear_bar() - reset the BAR
>> * @epc: the EPC device for which the BAR has to be cleared
>> diff --git a/include/linux/pci-epc.h b/include/linux/pci-epc.h
>> index 42ef06136bd1..b5f5c1eb54c5 100644
>> --- a/include/linux/pci-epc.h
>> +++ b/include/linux/pci-epc.h
>> @@ -32,11 +32,44 @@ pci_epc_interface_string(enum pci_epc_interface_type type)
>> }
>> }
>>
>> +/**
>> + * struct pci_epc_map - information about EPC memory for mapping a RC PCI
>> + * address range
>> + * @pci_addr: start address of the RC PCI address range to map
>> + * @pci_size: size of the RC PCI address range mapped from @pci_addr
>> + * @map_pci_addr: RC PCI address used as the first address mapped (may be lower
>> + * than @pci_addr)
>> + * @map_size: size of the controller memory needed for mapping the RC PCI address
>> + * range @pci_addr..@pci_addr+@pci_size
>> + * @map_ofst: offset into the mapped controller memory to access @pci_addr
>> + * @phys_base: base physical address of the allocated EPC memory for mapping the
>> + * RC PCI address range
>> + * @phys_addr: physical address at which @pci_addr is mapped
>> + * @virt_base: base virtual address of the allocated EPC memory for mapping the
>> + * RC PCI address range
>> + * @virt_addr: virtual address at which @pci_addr is mapped
>> + */
>> +struct pci_epc_map {
>> + phys_addr_t pci_addr;
>> + size_t pci_size;
>> +
>> + phys_addr_t map_pci_addr;
>> + size_t map_size;
>> + phys_addr_t map_ofst;
>> +
>> + phys_addr_t phys_base;
>> + phys_addr_t phys_addr;
>> + void __iomem *virt_base;
>> + void __iomem *virt_addr;
>> +};
>> +
>> /**
>> * struct pci_epc_ops - set of function pointers for performing EPC operations
>> * @write_header: ops to populate configuration space header
>> * @set_bar: ops to configure the BAR
>> * @clear_bar: ops to reset the BAR
>> + * @get_mem_map: operation to get the size and offset into a controller memory
>> + * window needed to map an RC PCI address region
>> * @map_addr: ops to map CPU address to PCI address
>> * @unmap_addr: ops to unmap CPU address and PCI address
>> * @set_msi: ops to set the requested number of MSI interrupts in the MSI
>> @@ -61,6 +94,8 @@ struct pci_epc_ops {
>> struct pci_epf_bar *epf_bar);
>> void (*clear_bar)(struct pci_epc *epc, u8 func_no, u8 vfunc_no,
>> struct pci_epf_bar *epf_bar);
>> + int (*get_mem_map)(struct pci_epc *epc, u8 func_no, u8 vfunc_no,
>> + struct pci_epc_map *map);
>> int (*map_addr)(struct pci_epc *epc, u8 func_no, u8 vfunc_no,
>> phys_addr_t addr, u64 pci_addr, size_t size);
>> void (*unmap_addr)(struct pci_epc *epc, u8 func_no, u8 vfunc_no,
>> @@ -278,6 +313,10 @@ void __iomem *pci_epc_mem_alloc_addr(struct pci_epc *epc,
>> phys_addr_t *phys_addr, size_t size);
>> void pci_epc_mem_free_addr(struct pci_epc *epc, phys_addr_t phys_addr,
>> void __iomem *virt_addr, size_t size);
>> +int pci_epc_mem_map(struct pci_epc *epc, u8 func_no, u8 vfunc_no,
>> + u64 pci_addr, size_t pci_size, struct pci_epc_map *map);
>> +void pci_epc_mem_unmap(struct pci_epc *epc, u8 func_no, u8 vfunc_no,
>> + struct pci_epc_map *map);
>>
>> #else
>> static inline void pci_epc_init_notify(struct pci_epc *epc)
>> --
>> 2.47.0
>>
--
Damien Le Moal
Western Digital Research
^ permalink raw reply [flat|nested] 13+ messages in thread* Re: [PATCH v5 3/6] PCI: endpoint: Introduce pci_epc_mem_map()/unmap()
2024-10-11 12:21 ` Damien Le Moal
@ 2024-10-12 9:42 ` Manivannan Sadhasivam
0 siblings, 0 replies; 13+ messages in thread
From: Manivannan Sadhasivam @ 2024-10-12 9:42 UTC (permalink / raw)
To: Damien Le Moal
Cc: Niklas Cassel, Krzysztof Wilczyński, Kishon Vijay Abraham I,
Bjorn Helgaas, Lorenzo Pieralisi, Rob Herring, Jonathan Corbet,
Jingoo Han, linux-pci, Rick Wertenbroek
On Fri, Oct 11, 2024 at 09:21:29PM +0900, Damien Le Moal wrote:
> On 10/11/24 21:07, Niklas Cassel wrote:
> >> +int pci_epc_mem_map(struct pci_epc *epc, u8 func_no, u8 vfunc_no,
> >> + u64 pci_addr, size_t pci_size, struct pci_epc_map *map)
> >> +{
> >> + int ret;
> >> +
> >> + if (!pci_epc_function_is_valid(epc, func_no, vfunc_no))
> >> + return -EINVAL;
> >> +
> >> + if (!pci_size || !map)
> >> + return -EINVAL;
> >> +
> >> + ret = pci_epc_get_mem_map(epc, func_no, vfunc_no,
> >> + pci_addr, pci_size, map);
> >> + if (ret)
> >> + return ret;
> >> +
> >> + map->virt_base = pci_epc_mem_alloc_addr(epc, &map->phys_base,
> >> + map->map_size);
> >> + if (!map->virt_base)
> >> + return -ENOMEM;
> >> +
> >> + map->phys_addr = map->phys_base + map->map_ofst;
> >> + map->virt_addr = map->virt_base + map->map_ofst;
> >> +
> >> + ret = pci_epc_map_addr(epc, func_no, vfunc_no, map->phys_base,
> >> + map->map_pci_addr, map->map_size);
> >> + if (ret) {
> >> + pci_epc_mem_free_addr(epc, map->phys_base, map->virt_base,
> >> + map->map_size);
> >> + map->virt_base = 0;
> >
> > As reported by the kernel test robot on both v3 and v4, this should be:
> > map->virt_base = NULL;
> > otherwise you introduce a new sparse warning.
>
> Oops. Missed that... OK, sending v6 with this removed as that is not necessary
> anyway.
>
Please incorporate this in v6.
- Mani
--
மணிவண்ணன் சதாசிவம்
^ permalink raw reply [flat|nested] 13+ messages in thread
* Re: [PATCH v5 3/6] PCI: endpoint: Introduce pci_epc_mem_map()/unmap()
2024-10-11 12:07 ` Niklas Cassel
2024-10-11 12:21 ` Damien Le Moal
@ 2024-10-11 12:22 ` Niklas Cassel
1 sibling, 0 replies; 13+ messages in thread
From: Niklas Cassel @ 2024-10-11 12:22 UTC (permalink / raw)
To: Damien Le Moal
Cc: Manivannan Sadhasivam, Krzysztof Wilczyński,
Kishon Vijay Abraham I, Bjorn Helgaas, Lorenzo Pieralisi,
Rob Herring, Jonathan Corbet, Jingoo Han, linux-pci,
Rick Wertenbroek
On Fri, Oct 11, 2024 at 02:07:07PM +0200, Niklas Cassel wrote:
> On Fri, Oct 11, 2024 at 09:01:12PM +0900, Damien Le Moal wrote:
(snip)
> > +int pci_epc_mem_map(struct pci_epc *epc, u8 func_no, u8 vfunc_no,
> > + u64 pci_addr, size_t pci_size, struct pci_epc_map *map)
> > +{
> > + int ret;
> > +
> > + if (!pci_epc_function_is_valid(epc, func_no, vfunc_no))
> > + return -EINVAL;
> > +
> > + if (!pci_size || !map)
> > + return -EINVAL;
> > +
> > + ret = pci_epc_get_mem_map(epc, func_no, vfunc_no,
> > + pci_addr, pci_size, map);
> > + if (ret)
> > + return ret;
> > +
> > + map->virt_base = pci_epc_mem_alloc_addr(epc, &map->phys_base,
> > + map->map_size);
> > + if (!map->virt_base)
> > + return -ENOMEM;
> > +
> > + map->phys_addr = map->phys_base + map->map_ofst;
> > + map->virt_addr = map->virt_base + map->map_ofst;
> > +
> > + ret = pci_epc_map_addr(epc, func_no, vfunc_no, map->phys_base,
> > + map->map_pci_addr, map->map_size);
> > + if (ret) {
> > + pci_epc_mem_free_addr(epc, map->phys_base, map->virt_base,
> > + map->map_size);
> > + map->virt_base = 0;
>
> As reported by the kernel test robot on both v3 and v4, this should be:
> map->virt_base = NULL;
> otherwise you introduce a new sparse warning.
With that nit fixed:
Reviewed-by: Niklas Cassel <cassel@kernel.org>
^ permalink raw reply [flat|nested] 13+ messages in thread
* [PATCH v5 4/6] PCI: endpoint: Update documentation
2024-10-11 12:01 [PATCH v5 0/6] Improve PCI memory mapping API Damien Le Moal
` (2 preceding siblings ...)
2024-10-11 12:01 ` [PATCH v5 3/6] PCI: endpoint: Introduce pci_epc_mem_map()/unmap() Damien Le Moal
@ 2024-10-11 12:01 ` Damien Le Moal
2024-10-11 12:20 ` Niklas Cassel
2024-10-11 12:01 ` [PATCH v5 5/6] PCI: endpoint: test: Use pci_epc_mem_map/unmap() Damien Le Moal
2024-10-11 12:01 ` [PATCH v5 6/6] PCI: dwc: endpoint: Implement the pci_epc_ops::get_mem_map() operation Damien Le Moal
5 siblings, 1 reply; 13+ messages in thread
From: Damien Le Moal @ 2024-10-11 12:01 UTC (permalink / raw)
To: Manivannan Sadhasivam, Krzysztof Wilczyński,
Kishon Vijay Abraham I, Bjorn Helgaas, Lorenzo Pieralisi,
Rob Herring, Jonathan Corbet, Jingoo Han, linux-pci
Cc: Rick Wertenbroek, Niklas Cassel
Document the new functions pci_epc_mem_map() and pci_epc_mem_unmap().
Also add the documentation for the functions pci_epc_map_addr() and
pci_epc_unmap_addr() that were missing.
Signed-off-by: Damien Le Moal <dlemoal@kernel.org>
---
Documentation/PCI/endpoint/pci-endpoint.rst | 29 +++++++++++++++++++++
1 file changed, 29 insertions(+)
diff --git a/Documentation/PCI/endpoint/pci-endpoint.rst b/Documentation/PCI/endpoint/pci-endpoint.rst
index 21507e3cc238..35f82f2d45f5 100644
--- a/Documentation/PCI/endpoint/pci-endpoint.rst
+++ b/Documentation/PCI/endpoint/pci-endpoint.rst
@@ -117,6 +117,35 @@ by the PCI endpoint function driver.
The PCI endpoint function driver should use pci_epc_mem_free_addr() to
free the memory space allocated using pci_epc_mem_alloc_addr().
+* pci_epc_map_addr()
+
+ A PCI endpoint function driver should use pci_epc_map_addr() to map to a RC
+ PCI address the CPU address of local memory obtained with
+ pci_epc_mem_alloc_addr().
+
+* pci_epc_unmap_addr()
+
+ A PCI endpoint function driver should use pci_epc_unmap_addr() to unmap the
+ CPU address of local memory mapped to a RC address with pci_epc_map_addr().
+
+* pci_epc_mem_map()
+
+ A PCI endpoint controller may impose constraints on the RC PCI addresses that
+ can be mapped. The function pci_epc_mem_map() allows endpoint function
+ drivers to allocate and map controller memory while handling such
+ constraints. This function will determine the size of the memory that must be
+ allocated with pci_epc_mem_alloc_addr() for successfully mapping a RC PCI
+ address range. This function will also indicate the size of the PCI address
+ range that was actually mapped, which can be less than the requested size, as
+ well as the offset into the allocated memory to use for accessing the mapped
+ RC PCI address range.
+
+* pci_epc_mem_unmap()
+
+ A PCI endpoint function driver can use pci_epc_mem_unmap() to unmap and free
+ controller memory that was allocated and mapped using pci_epc_mem_map().
+
+
Other EPC APIs
~~~~~~~~~~~~~~
--
2.47.0
^ permalink raw reply related [flat|nested] 13+ messages in thread* Re: [PATCH v5 4/6] PCI: endpoint: Update documentation
2024-10-11 12:01 ` [PATCH v5 4/6] PCI: endpoint: Update documentation Damien Le Moal
@ 2024-10-11 12:20 ` Niklas Cassel
0 siblings, 0 replies; 13+ messages in thread
From: Niklas Cassel @ 2024-10-11 12:20 UTC (permalink / raw)
To: Damien Le Moal
Cc: Manivannan Sadhasivam, Krzysztof Wilczyński,
Kishon Vijay Abraham I, Bjorn Helgaas, Lorenzo Pieralisi,
Rob Herring, Jonathan Corbet, Jingoo Han, linux-pci,
Rick Wertenbroek
On Fri, Oct 11, 2024 at 09:01:13PM +0900, Damien Le Moal wrote:
> Document the new functions pci_epc_mem_map() and pci_epc_mem_unmap().
> Also add the documentation for the functions pci_epc_map_addr() and
> pci_epc_unmap_addr() that were missing.
>
> Signed-off-by: Damien Le Moal <dlemoal@kernel.org>
> ---
> Documentation/PCI/endpoint/pci-endpoint.rst | 29 +++++++++++++++++++++
> 1 file changed, 29 insertions(+)
>
> diff --git a/Documentation/PCI/endpoint/pci-endpoint.rst b/Documentation/PCI/endpoint/pci-endpoint.rst
> index 21507e3cc238..35f82f2d45f5 100644
> --- a/Documentation/PCI/endpoint/pci-endpoint.rst
> +++ b/Documentation/PCI/endpoint/pci-endpoint.rst
> @@ -117,6 +117,35 @@ by the PCI endpoint function driver.
> The PCI endpoint function driver should use pci_epc_mem_free_addr() to
> free the memory space allocated using pci_epc_mem_alloc_addr().
>
> +* pci_epc_map_addr()
> +
> + A PCI endpoint function driver should use pci_epc_map_addr() to map to a RC
> + PCI address the CPU address of local memory obtained with
> + pci_epc_mem_alloc_addr().
> +
> +* pci_epc_unmap_addr()
> +
> + A PCI endpoint function driver should use pci_epc_unmap_addr() to unmap the
> + CPU address of local memory mapped to a RC address with pci_epc_map_addr().
> +
> +* pci_epc_mem_map()
> +
> + A PCI endpoint controller may impose constraints on the RC PCI addresses that
> + can be mapped. The function pci_epc_mem_map() allows endpoint function
> + drivers to allocate and map controller memory while handling such
> + constraints. This function will determine the size of the memory that must be
> + allocated with pci_epc_mem_alloc_addr() for successfully mapping a RC PCI
> + address range. This function will also indicate the size of the PCI address
> + range that was actually mapped, which can be less than the requested size, as
> + well as the offset into the allocated memory to use for accessing the mapped
> + RC PCI address range.
> +
> +* pci_epc_mem_unmap()
> +
> + A PCI endpoint function driver can use pci_epc_mem_unmap() to unmap and free
> + controller memory that was allocated and mapped using pci_epc_mem_map().
> +
> +
> Other EPC APIs
> ~~~~~~~~~~~~~~
>
> --
> 2.47.0
>
Reviewed-by: Niklas Cassel <cassel@kernel.org>
^ permalink raw reply [flat|nested] 13+ messages in thread
* [PATCH v5 5/6] PCI: endpoint: test: Use pci_epc_mem_map/unmap()
2024-10-11 12:01 [PATCH v5 0/6] Improve PCI memory mapping API Damien Le Moal
` (3 preceding siblings ...)
2024-10-11 12:01 ` [PATCH v5 4/6] PCI: endpoint: Update documentation Damien Le Moal
@ 2024-10-11 12:01 ` Damien Le Moal
2024-10-11 12:01 ` [PATCH v5 6/6] PCI: dwc: endpoint: Implement the pci_epc_ops::get_mem_map() operation Damien Le Moal
5 siblings, 0 replies; 13+ messages in thread
From: Damien Le Moal @ 2024-10-11 12:01 UTC (permalink / raw)
To: Manivannan Sadhasivam, Krzysztof Wilczyński,
Kishon Vijay Abraham I, Bjorn Helgaas, Lorenzo Pieralisi,
Rob Herring, Jonathan Corbet, Jingoo Han, linux-pci
Cc: Rick Wertenbroek, Niklas Cassel
Modify the endpoint test driver to use the functions pci_epc_mem_map()
and pci_epc_mem_unmap() for the read, write and copy tests. For each
test case, the transfer (dma or mmio) are executed in a loop to ensure
that potentially partial mappings returned by pci_epc_mem_map() are
correctly handled.
Signed-off-by: Damien Le Moal <dlemoal@kernel.org>
Reviewed-by: Niklas Cassel <cassel@kernel.org>
Reviewed-by: Manivannan Sadhasivam <manivannan.sadhasivam@linaro.org>
---
drivers/pci/endpoint/functions/pci-epf-test.c | 372 +++++++++---------
1 file changed, 193 insertions(+), 179 deletions(-)
diff --git a/drivers/pci/endpoint/functions/pci-epf-test.c b/drivers/pci/endpoint/functions/pci-epf-test.c
index 7c2ed6eae53a..a73bc0771d35 100644
--- a/drivers/pci/endpoint/functions/pci-epf-test.c
+++ b/drivers/pci/endpoint/functions/pci-epf-test.c
@@ -317,91 +317,92 @@ static void pci_epf_test_print_rate(struct pci_epf_test *epf_test,
static void pci_epf_test_copy(struct pci_epf_test *epf_test,
struct pci_epf_test_reg *reg)
{
- int ret;
- void __iomem *src_addr;
- void __iomem *dst_addr;
- phys_addr_t src_phys_addr;
- phys_addr_t dst_phys_addr;
+ int ret = 0;
struct timespec64 start, end;
struct pci_epf *epf = epf_test->epf;
- struct device *dev = &epf->dev;
struct pci_epc *epc = epf->epc;
+ struct device *dev = &epf->dev;
+ struct pci_epc_map src_map, dst_map;
+ u64 src_addr = reg->src_addr;
+ u64 dst_addr = reg->dst_addr;
+ size_t copy_size = reg->size;
+ ssize_t map_size = 0;
+ void *copy_buf = NULL, *buf;
- src_addr = pci_epc_mem_alloc_addr(epc, &src_phys_addr, reg->size);
- if (!src_addr) {
- dev_err(dev, "Failed to allocate source address\n");
- reg->status = STATUS_SRC_ADDR_INVALID;
- ret = -ENOMEM;
- goto err;
- }
-
- ret = pci_epc_map_addr(epc, epf->func_no, epf->vfunc_no, src_phys_addr,
- reg->src_addr, reg->size);
- if (ret) {
- dev_err(dev, "Failed to map source address\n");
- reg->status = STATUS_SRC_ADDR_INVALID;
- goto err_src_addr;
- }
-
- dst_addr = pci_epc_mem_alloc_addr(epc, &dst_phys_addr, reg->size);
- if (!dst_addr) {
- dev_err(dev, "Failed to allocate destination address\n");
- reg->status = STATUS_DST_ADDR_INVALID;
- ret = -ENOMEM;
- goto err_src_map_addr;
- }
-
- ret = pci_epc_map_addr(epc, epf->func_no, epf->vfunc_no, dst_phys_addr,
- reg->dst_addr, reg->size);
- if (ret) {
- dev_err(dev, "Failed to map destination address\n");
- reg->status = STATUS_DST_ADDR_INVALID;
- goto err_dst_addr;
- }
-
- ktime_get_ts64(&start);
if (reg->flags & FLAG_USE_DMA) {
if (epf_test->dma_private) {
dev_err(dev, "Cannot transfer data using DMA\n");
ret = -EINVAL;
- goto err_map_addr;
+ goto set_status;
}
-
- ret = pci_epf_test_data_transfer(epf_test, dst_phys_addr,
- src_phys_addr, reg->size, 0,
- DMA_MEM_TO_MEM);
- if (ret)
- dev_err(dev, "Data transfer failed\n");
} else {
- void *buf;
-
- buf = kzalloc(reg->size, GFP_KERNEL);
- if (!buf) {
+ copy_buf = kzalloc(copy_size, GFP_KERNEL);
+ if (!copy_buf) {
ret = -ENOMEM;
- goto err_map_addr;
+ goto set_status;
}
-
- memcpy_fromio(buf, src_addr, reg->size);
- memcpy_toio(dst_addr, buf, reg->size);
- kfree(buf);
+ buf = copy_buf;
}
- ktime_get_ts64(&end);
- pci_epf_test_print_rate(epf_test, "COPY", reg->size, &start, &end,
- reg->flags & FLAG_USE_DMA);
-err_map_addr:
- pci_epc_unmap_addr(epc, epf->func_no, epf->vfunc_no, dst_phys_addr);
+ while (copy_size) {
+ ret = pci_epc_mem_map(epc, epf->func_no, epf->vfunc_no,
+ src_addr, copy_size, &src_map);
+ if (ret) {
+ dev_err(dev, "Failed to map source address\n");
+ reg->status = STATUS_SRC_ADDR_INVALID;
+ goto free_buf;
+ }
+
+ ret = pci_epc_mem_map(epf->epc, epf->func_no, epf->vfunc_no,
+ dst_addr, copy_size, &dst_map);
+ if (ret) {
+ dev_err(dev, "Failed to map destination address\n");
+ reg->status = STATUS_DST_ADDR_INVALID;
+ pci_epc_mem_unmap(epc, epf->func_no, epf->vfunc_no,
+ &src_map);
+ goto free_buf;
+ }
+
+ map_size = min_t(size_t, dst_map.pci_size, src_map.pci_size);
+
+ ktime_get_ts64(&start);
+ if (reg->flags & FLAG_USE_DMA) {
+ ret = pci_epf_test_data_transfer(epf_test,
+ dst_map.phys_addr, src_map.phys_addr,
+ map_size, 0, DMA_MEM_TO_MEM);
+ if (ret) {
+ dev_err(dev, "Data transfer failed\n");
+ goto unmap;
+ }
+ } else {
+ memcpy_fromio(buf, src_map.virt_addr, map_size);
+ memcpy_toio(dst_map.virt_addr, buf, map_size);
+ buf += map_size;
+ }
+ ktime_get_ts64(&end);
-err_dst_addr:
- pci_epc_mem_free_addr(epc, dst_phys_addr, dst_addr, reg->size);
+ copy_size -= map_size;
+ src_addr += map_size;
+ dst_addr += map_size;
-err_src_map_addr:
- pci_epc_unmap_addr(epc, epf->func_no, epf->vfunc_no, src_phys_addr);
+ pci_epc_mem_unmap(epc, epf->func_no, epf->vfunc_no, &dst_map);
+ pci_epc_mem_unmap(epc, epf->func_no, epf->vfunc_no, &src_map);
+ map_size = 0;
+ }
-err_src_addr:
- pci_epc_mem_free_addr(epc, src_phys_addr, src_addr, reg->size);
+ pci_epf_test_print_rate(epf_test, "COPY", reg->size, &start,
+ &end, reg->flags & FLAG_USE_DMA);
-err:
+unmap:
+ if (map_size) {
+ pci_epc_mem_unmap(epc, epf->func_no, epf->vfunc_no, &dst_map);
+ pci_epc_mem_unmap(epc, epf->func_no, epf->vfunc_no, &src_map);
+ }
+
+free_buf:
+ kfree(copy_buf);
+
+set_status:
if (!ret)
reg->status |= STATUS_COPY_SUCCESS;
else
@@ -411,82 +412,89 @@ static void pci_epf_test_copy(struct pci_epf_test *epf_test,
static void pci_epf_test_read(struct pci_epf_test *epf_test,
struct pci_epf_test_reg *reg)
{
- int ret;
- void __iomem *src_addr;
- void *buf;
+ int ret = 0;
+ void *src_buf, *buf;
u32 crc32;
- phys_addr_t phys_addr;
+ struct pci_epc_map map;
phys_addr_t dst_phys_addr;
struct timespec64 start, end;
struct pci_epf *epf = epf_test->epf;
- struct device *dev = &epf->dev;
struct pci_epc *epc = epf->epc;
+ struct device *dev = &epf->dev;
struct device *dma_dev = epf->epc->dev.parent;
+ u64 src_addr = reg->src_addr;
+ size_t src_size = reg->size;
+ ssize_t map_size = 0;
- src_addr = pci_epc_mem_alloc_addr(epc, &phys_addr, reg->size);
- if (!src_addr) {
- dev_err(dev, "Failed to allocate address\n");
- reg->status = STATUS_SRC_ADDR_INVALID;
+ src_buf = kzalloc(src_size, GFP_KERNEL);
+ if (!src_buf) {
ret = -ENOMEM;
- goto err;
+ goto set_status;
}
+ buf = src_buf;
- ret = pci_epc_map_addr(epc, epf->func_no, epf->vfunc_no, phys_addr,
- reg->src_addr, reg->size);
- if (ret) {
- dev_err(dev, "Failed to map address\n");
- reg->status = STATUS_SRC_ADDR_INVALID;
- goto err_addr;
- }
-
- buf = kzalloc(reg->size, GFP_KERNEL);
- if (!buf) {
- ret = -ENOMEM;
- goto err_map_addr;
- }
+ while (src_size) {
+ ret = pci_epc_mem_map(epc, epf->func_no, epf->vfunc_no,
+ src_addr, src_size, &map);
+ if (ret) {
+ dev_err(dev, "Failed to map address\n");
+ reg->status = STATUS_SRC_ADDR_INVALID;
+ goto free_buf;
+ }
- if (reg->flags & FLAG_USE_DMA) {
- dst_phys_addr = dma_map_single(dma_dev, buf, reg->size,
- DMA_FROM_DEVICE);
- if (dma_mapping_error(dma_dev, dst_phys_addr)) {
- dev_err(dev, "Failed to map destination buffer addr\n");
- ret = -ENOMEM;
- goto err_dma_map;
+ map_size = map.pci_size;
+ if (reg->flags & FLAG_USE_DMA) {
+ dst_phys_addr = dma_map_single(dma_dev, buf, map_size,
+ DMA_FROM_DEVICE);
+ if (dma_mapping_error(dma_dev, dst_phys_addr)) {
+ dev_err(dev,
+ "Failed to map destination buffer addr\n");
+ ret = -ENOMEM;
+ goto unmap;
+ }
+
+ ktime_get_ts64(&start);
+ ret = pci_epf_test_data_transfer(epf_test,
+ dst_phys_addr, map.phys_addr,
+ map_size, src_addr, DMA_DEV_TO_MEM);
+ if (ret)
+ dev_err(dev, "Data transfer failed\n");
+ ktime_get_ts64(&end);
+
+ dma_unmap_single(dma_dev, dst_phys_addr, map_size,
+ DMA_FROM_DEVICE);
+
+ if (ret)
+ goto unmap;
+ } else {
+ ktime_get_ts64(&start);
+ memcpy_fromio(buf, map.virt_addr, map_size);
+ ktime_get_ts64(&end);
}
- ktime_get_ts64(&start);
- ret = pci_epf_test_data_transfer(epf_test, dst_phys_addr,
- phys_addr, reg->size,
- reg->src_addr, DMA_DEV_TO_MEM);
- if (ret)
- dev_err(dev, "Data transfer failed\n");
- ktime_get_ts64(&end);
+ src_size -= map_size;
+ src_addr += map_size;
+ buf += map_size;
- dma_unmap_single(dma_dev, dst_phys_addr, reg->size,
- DMA_FROM_DEVICE);
- } else {
- ktime_get_ts64(&start);
- memcpy_fromio(buf, src_addr, reg->size);
- ktime_get_ts64(&end);
+ pci_epc_mem_unmap(epc, epf->func_no, epf->vfunc_no, &map);
+ map_size = 0;
}
- pci_epf_test_print_rate(epf_test, "READ", reg->size, &start, &end,
- reg->flags & FLAG_USE_DMA);
+ pci_epf_test_print_rate(epf_test, "READ", reg->size, &start,
+ &end, reg->flags & FLAG_USE_DMA);
- crc32 = crc32_le(~0, buf, reg->size);
+ crc32 = crc32_le(~0, src_buf, reg->size);
if (crc32 != reg->checksum)
ret = -EIO;
-err_dma_map:
- kfree(buf);
-
-err_map_addr:
- pci_epc_unmap_addr(epc, epf->func_no, epf->vfunc_no, phys_addr);
+unmap:
+ if (map_size)
+ pci_epc_mem_unmap(epc, epf->func_no, epf->vfunc_no, &map);
-err_addr:
- pci_epc_mem_free_addr(epc, phys_addr, src_addr, reg->size);
+free_buf:
+ kfree(src_buf);
-err:
+set_status:
if (!ret)
reg->status |= STATUS_READ_SUCCESS;
else
@@ -496,71 +504,79 @@ static void pci_epf_test_read(struct pci_epf_test *epf_test,
static void pci_epf_test_write(struct pci_epf_test *epf_test,
struct pci_epf_test_reg *reg)
{
- int ret;
- void __iomem *dst_addr;
- void *buf;
- phys_addr_t phys_addr;
+ int ret = 0;
+ void *dst_buf, *buf;
+ struct pci_epc_map map;
phys_addr_t src_phys_addr;
struct timespec64 start, end;
struct pci_epf *epf = epf_test->epf;
- struct device *dev = &epf->dev;
struct pci_epc *epc = epf->epc;
+ struct device *dev = &epf->dev;
struct device *dma_dev = epf->epc->dev.parent;
+ u64 dst_addr = reg->dst_addr;
+ size_t dst_size = reg->size;
+ ssize_t map_size = 0;
- dst_addr = pci_epc_mem_alloc_addr(epc, &phys_addr, reg->size);
- if (!dst_addr) {
- dev_err(dev, "Failed to allocate address\n");
- reg->status = STATUS_DST_ADDR_INVALID;
+ dst_buf = kzalloc(dst_size, GFP_KERNEL);
+ if (!dst_buf) {
ret = -ENOMEM;
- goto err;
+ goto set_status;
}
+ get_random_bytes(dst_buf, dst_size);
+ reg->checksum = crc32_le(~0, dst_buf, dst_size);
+ buf = dst_buf;
- ret = pci_epc_map_addr(epc, epf->func_no, epf->vfunc_no, phys_addr,
- reg->dst_addr, reg->size);
- if (ret) {
- dev_err(dev, "Failed to map address\n");
- reg->status = STATUS_DST_ADDR_INVALID;
- goto err_addr;
- }
-
- buf = kzalloc(reg->size, GFP_KERNEL);
- if (!buf) {
- ret = -ENOMEM;
- goto err_map_addr;
- }
-
- get_random_bytes(buf, reg->size);
- reg->checksum = crc32_le(~0, buf, reg->size);
-
- if (reg->flags & FLAG_USE_DMA) {
- src_phys_addr = dma_map_single(dma_dev, buf, reg->size,
- DMA_TO_DEVICE);
- if (dma_mapping_error(dma_dev, src_phys_addr)) {
- dev_err(dev, "Failed to map source buffer addr\n");
- ret = -ENOMEM;
- goto err_dma_map;
+ while (dst_size) {
+ ret = pci_epc_mem_map(epc, epf->func_no, epf->vfunc_no,
+ dst_addr, dst_size, &map);
+ if (ret) {
+ dev_err(dev, "Failed to map address\n");
+ reg->status = STATUS_DST_ADDR_INVALID;
+ goto free_buf;
}
- ktime_get_ts64(&start);
+ map_size = map.pci_size;
+ if (reg->flags & FLAG_USE_DMA) {
+ src_phys_addr = dma_map_single(dma_dev, buf, map_size,
+ DMA_TO_DEVICE);
+ if (dma_mapping_error(dma_dev, src_phys_addr)) {
+ dev_err(dev,
+ "Failed to map source buffer addr\n");
+ ret = -ENOMEM;
+ goto unmap;
+ }
+
+ ktime_get_ts64(&start);
+
+ ret = pci_epf_test_data_transfer(epf_test,
+ map.phys_addr, src_phys_addr,
+ map_size, dst_addr,
+ DMA_MEM_TO_DEV);
+ if (ret)
+ dev_err(dev, "Data transfer failed\n");
+ ktime_get_ts64(&end);
+
+ dma_unmap_single(dma_dev, src_phys_addr, map_size,
+ DMA_TO_DEVICE);
+
+ if (ret)
+ goto unmap;
+ } else {
+ ktime_get_ts64(&start);
+ memcpy_toio(map.virt_addr, buf, map_size);
+ ktime_get_ts64(&end);
+ }
- ret = pci_epf_test_data_transfer(epf_test, phys_addr,
- src_phys_addr, reg->size,
- reg->dst_addr,
- DMA_MEM_TO_DEV);
- if (ret)
- dev_err(dev, "Data transfer failed\n");
- ktime_get_ts64(&end);
+ dst_size -= map_size;
+ dst_addr += map_size;
+ buf += map_size;
- dma_unmap_single(dma_dev, src_phys_addr, reg->size,
- DMA_TO_DEVICE);
- } else {
- ktime_get_ts64(&start);
- memcpy_toio(dst_addr, buf, reg->size);
- ktime_get_ts64(&end);
+ pci_epc_mem_unmap(epc, epf->func_no, epf->vfunc_no, &map);
+ map_size = 0;
}
- pci_epf_test_print_rate(epf_test, "WRITE", reg->size, &start, &end,
- reg->flags & FLAG_USE_DMA);
+ pci_epf_test_print_rate(epf_test, "WRITE", reg->size, &start,
+ &end, reg->flags & FLAG_USE_DMA);
/*
* wait 1ms inorder for the write to complete. Without this delay L3
@@ -568,16 +584,14 @@ static void pci_epf_test_write(struct pci_epf_test *epf_test,
*/
usleep_range(1000, 2000);
-err_dma_map:
- kfree(buf);
-
-err_map_addr:
- pci_epc_unmap_addr(epc, epf->func_no, epf->vfunc_no, phys_addr);
+unmap:
+ if (map_size)
+ pci_epc_mem_unmap(epc, epf->func_no, epf->vfunc_no, &map);
-err_addr:
- pci_epc_mem_free_addr(epc, phys_addr, dst_addr, reg->size);
+free_buf:
+ kfree(dst_buf);
-err:
+set_status:
if (!ret)
reg->status |= STATUS_WRITE_SUCCESS;
else
--
2.47.0
^ permalink raw reply related [flat|nested] 13+ messages in thread* [PATCH v5 6/6] PCI: dwc: endpoint: Implement the pci_epc_ops::get_mem_map() operation
2024-10-11 12:01 [PATCH v5 0/6] Improve PCI memory mapping API Damien Le Moal
` (4 preceding siblings ...)
2024-10-11 12:01 ` [PATCH v5 5/6] PCI: endpoint: test: Use pci_epc_mem_map/unmap() Damien Le Moal
@ 2024-10-11 12:01 ` Damien Le Moal
2024-10-11 12:19 ` Niklas Cassel
5 siblings, 1 reply; 13+ messages in thread
From: Damien Le Moal @ 2024-10-11 12:01 UTC (permalink / raw)
To: Manivannan Sadhasivam, Krzysztof Wilczyński,
Kishon Vijay Abraham I, Bjorn Helgaas, Lorenzo Pieralisi,
Rob Herring, Jonathan Corbet, Jingoo Han, linux-pci
Cc: Rick Wertenbroek, Niklas Cassel
The function dw_pcie_prog_outbound_atu() used to program outbound ATU
entries for mapping RC PCI addresses to local CPU addresses does not
allow PCI addresses that are not aligned to the value of region_align
of struct dw_pcie. This value is determined from the iATU hardware
registers during probing of the iATU (done by dw_pcie_iatu_detect()).
This value is thus valid for all DWC PCIe controllers, and valid
regardless of the hardware configuration used when synthesizing the
DWC PCIe controller.
Implement the ->get_mem_map() endpoint controller operation to allow
this mapping alignment to be transparently handled by endpoint function
drivers through the function pci_epc_mem_map().
Signed-off-by: Damien Le Moal <dlemoal@kernel.org>
---
drivers/pci/controller/dwc/pcie-designware-ep.c | 15 +++++++++++++++
1 file changed, 15 insertions(+)
diff --git a/drivers/pci/controller/dwc/pcie-designware-ep.c b/drivers/pci/controller/dwc/pcie-designware-ep.c
index 43ba5c6738df..b65f4f0f5856 100644
--- a/drivers/pci/controller/dwc/pcie-designware-ep.c
+++ b/drivers/pci/controller/dwc/pcie-designware-ep.c
@@ -268,6 +268,20 @@ static int dw_pcie_find_index(struct dw_pcie_ep *ep, phys_addr_t addr,
return -EINVAL;
}
+static int dw_pcie_ep_get_mem_map(struct pci_epc *epc, u8 func_no, u8 vfunc_no,
+ struct pci_epc_map *map)
+{
+ struct dw_pcie_ep *ep = epc_get_drvdata(epc);
+ struct dw_pcie *pci = to_dw_pcie_from_ep(ep);
+ size_t mask = pci->region_align - 1;
+
+ map->map_pci_addr = map->pci_addr & ~mask;
+ map->map_ofst = map->pci_addr & mask;
+ map->map_size = ALIGN(map->map_ofst + map->pci_size, ep->page_size);
+
+ return 0;
+}
+
static void dw_pcie_ep_unmap_addr(struct pci_epc *epc, u8 func_no, u8 vfunc_no,
phys_addr_t addr)
{
@@ -444,6 +458,7 @@ static const struct pci_epc_ops epc_ops = {
.write_header = dw_pcie_ep_write_header,
.set_bar = dw_pcie_ep_set_bar,
.clear_bar = dw_pcie_ep_clear_bar,
+ .get_mem_map = dw_pcie_ep_get_mem_map,
.map_addr = dw_pcie_ep_map_addr,
.unmap_addr = dw_pcie_ep_unmap_addr,
.set_msi = dw_pcie_ep_set_msi,
--
2.47.0
^ permalink raw reply related [flat|nested] 13+ messages in thread* Re: [PATCH v5 6/6] PCI: dwc: endpoint: Implement the pci_epc_ops::get_mem_map() operation
2024-10-11 12:01 ` [PATCH v5 6/6] PCI: dwc: endpoint: Implement the pci_epc_ops::get_mem_map() operation Damien Le Moal
@ 2024-10-11 12:19 ` Niklas Cassel
0 siblings, 0 replies; 13+ messages in thread
From: Niklas Cassel @ 2024-10-11 12:19 UTC (permalink / raw)
To: Damien Le Moal
Cc: Manivannan Sadhasivam, Krzysztof Wilczyński,
Kishon Vijay Abraham I, Bjorn Helgaas, Lorenzo Pieralisi,
Rob Herring, Jonathan Corbet, Jingoo Han, linux-pci,
Rick Wertenbroek
On Fri, Oct 11, 2024 at 09:01:15PM +0900, Damien Le Moal wrote:
> The function dw_pcie_prog_outbound_atu() used to program outbound ATU
> entries for mapping RC PCI addresses to local CPU addresses does not
> allow PCI addresses that are not aligned to the value of region_align
> of struct dw_pcie. This value is determined from the iATU hardware
> registers during probing of the iATU (done by dw_pcie_iatu_detect()).
> This value is thus valid for all DWC PCIe controllers, and valid
> regardless of the hardware configuration used when synthesizing the
> DWC PCIe controller.
>
> Implement the ->get_mem_map() endpoint controller operation to allow
> this mapping alignment to be transparently handled by endpoint function
> drivers through the function pci_epc_mem_map().
>
> Signed-off-by: Damien Le Moal <dlemoal@kernel.org>
> ---
> drivers/pci/controller/dwc/pcie-designware-ep.c | 15 +++++++++++++++
> 1 file changed, 15 insertions(+)
>
> diff --git a/drivers/pci/controller/dwc/pcie-designware-ep.c b/drivers/pci/controller/dwc/pcie-designware-ep.c
> index 43ba5c6738df..b65f4f0f5856 100644
> --- a/drivers/pci/controller/dwc/pcie-designware-ep.c
> +++ b/drivers/pci/controller/dwc/pcie-designware-ep.c
> @@ -268,6 +268,20 @@ static int dw_pcie_find_index(struct dw_pcie_ep *ep, phys_addr_t addr,
> return -EINVAL;
> }
>
> +static int dw_pcie_ep_get_mem_map(struct pci_epc *epc, u8 func_no, u8 vfunc_no,
> + struct pci_epc_map *map)
> +{
> + struct dw_pcie_ep *ep = epc_get_drvdata(epc);
> + struct dw_pcie *pci = to_dw_pcie_from_ep(ep);
> + size_t mask = pci->region_align - 1;
> +
> + map->map_pci_addr = map->pci_addr & ~mask;
> + map->map_ofst = map->pci_addr & mask;
> + map->map_size = ALIGN(map->map_ofst + map->pci_size, ep->page_size);
> +
> + return 0;
> +}
> +
> static void dw_pcie_ep_unmap_addr(struct pci_epc *epc, u8 func_no, u8 vfunc_no,
> phys_addr_t addr)
> {
> @@ -444,6 +458,7 @@ static const struct pci_epc_ops epc_ops = {
> .write_header = dw_pcie_ep_write_header,
> .set_bar = dw_pcie_ep_set_bar,
> .clear_bar = dw_pcie_ep_clear_bar,
> + .get_mem_map = dw_pcie_ep_get_mem_map,
> .map_addr = dw_pcie_ep_map_addr,
> .unmap_addr = dw_pcie_ep_unmap_addr,
> .set_msi = dw_pcie_ep_set_msi,
> --
> 2.47.0
>
Reviewed-by: Niklas Cassel <cassel@kernel.org>
^ permalink raw reply [flat|nested] 13+ messages in thread