Message ID | 20240330041928.1555578-3-dlemoal@kernel.org (mailing list archive) |
---|---|
State | New, archived |
Headers | show |
Series | Improve PCI memory mapping API | expand |
On Sat, Mar 30, 2024 at 01:19: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 rockchip endpoint controller uses > at most the lower 20 bits of a physical memory address region as the > lower bits of an RC PCI address. 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 > a PCI EP function driver is not possible with the current EPC API as > it only provides the ->align field in struct pci_epc_features. > Furthermore, this alignment is static and does not depend on a mapping > pci address and size. > > Solve this by introducing the function pci_epc_map_align() and the > endpoint controller operation ->map_align to allow endpoint function > drivers to obtain the size and the offset into a controller address > region that must be used to map an RC PCI address region. The size > of the physical address region provided by pci_epc_map_align() can then > be used as the size argument for the function pci_epc_mem_alloc_addr(). > The offset into the allocated controller memory can be used to > correctly handle data transfers. Of note is that pci_epc_map_align() may > indicate upon return a mapping size that is smaller (but not 0) than the > requested PCI address region size. For such case, an endpoint function > driver must handle data transfers in fragments. > Is there any incentive in exposing pci_epc_map_align()? I mean, why can't it be hidden inside the new alloc() API itself? Furthermore, is it possible to avoid the map_align() callback and handle the alignment within the EPC driver? - Mani > The controller operation ->map_align is optional: controllers that do > not have any address alignment constraints for mapping a RC PCI address > region do not need to implement this operation. For such controllers, > pci_epc_map_align() always returns the mapping size as equal > to the requested size and an offset equal to 0. > > The structure pci_epc_map is introduced to represent a mapping start PCI > address, size and the size and offset into the controller memory needed > for mapping the PCI address region. > > Signed-off-by: Damien Le Moal <dlemoal@kernel.org> > --- > drivers/pci/endpoint/pci-epc-core.c | 66 +++++++++++++++++++++++++++++ > include/linux/pci-epc.h | 33 +++++++++++++++ > 2 files changed, 99 insertions(+) > > diff --git a/drivers/pci/endpoint/pci-epc-core.c b/drivers/pci/endpoint/pci-epc-core.c > index 754afd115bbd..37758ca91d7f 100644 > --- a/drivers/pci/endpoint/pci-epc-core.c > +++ b/drivers/pci/endpoint/pci-epc-core.c > @@ -433,6 +433,72 @@ void pci_epc_unmap_addr(struct pci_epc *epc, u8 func_no, u8 vfunc_no, > } > EXPORT_SYMBOL_GPL(pci_epc_unmap_addr); > > +/** > + * pci_epc_map_align() - Get the offset into and the size of a controller memory > + * address region needed to map a RC PCI address region > + * @epc: the EPC device on which address is allocated > + * @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 physical address should be mapped > + * @size: the size of the mapping starting from @pci_addr > + * @map: populate here the actual size and offset into the controller memory > + * that must be allocated for the mapping > + * > + * Invoke the controller map_align operation to obtain the size and the offset > + * into a controller address region that must be allocated to map @size > + * bytes of the RC PCI address space starting from @pci_addr. > + * > + * The size of the mapping that can be handled by the controller is indicated > + * using the pci_size field of @map. This size may be smaller than the requested > + * @size. In such case, the function driver must handle the mapping using > + * several fragments. The offset into the controller memory for the effective > + * mapping of the @pci_addr..@pci_addr+@map->pci_size address range is indicated > + * using the map_ofst field of @map. > + */ > +int pci_epc_map_align(struct pci_epc *epc, u8 func_no, u8 vfunc_no, > + u64 pci_addr, size_t size, struct pci_epc_map *map) > +{ > + const struct pci_epc_features *features; > + size_t mask; > + int ret; > + > + if (!pci_epc_function_is_valid(epc, func_no, vfunc_no)) > + return -EINVAL; > + > + if (!size || !map) > + return -EINVAL; > + > + memset(map, 0, sizeof(*map)); > + map->pci_addr = pci_addr; > + map->pci_size = size; > + > + if (epc->ops->map_align) { > + mutex_lock(&epc->lock); > + ret = epc->ops->map_align(epc, func_no, vfunc_no, map); > + mutex_unlock(&epc->lock); > + return ret; > + } > + > + /* > + * Assume a fixed alignment constraint as specified by the controller > + * features. > + */ > + features = pci_epc_get_features(epc, func_no, vfunc_no); > + if (!features || !features->align) { > + map->map_pci_addr = pci_addr; > + map->map_size = size; > + map->map_ofst = 0; These values are overwritten anyway below. > + } > + > + mask = features->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, features->align); > + > + return 0; > +} > +EXPORT_SYMBOL_GPL(pci_epc_map_align); > + > /** > * pci_epc_map_addr() - map CPU address to PCI address > * @epc: the EPC device on which address is allocated > diff --git a/include/linux/pci-epc.h b/include/linux/pci-epc.h > index cc2f70d061c8..8cfb4aaf2628 100644 > --- a/include/linux/pci-epc.h > +++ b/include/linux/pci-epc.h > @@ -32,11 +32,40 @@ 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 to map > + * @map_pci_addr: RC PCI address used as the first address mapped > + * @map_size: size of the controller memory needed for the mapping > + * @map_ofst: offset into the controller memory needed for the mapping > + * @phys_base: base physical address of the allocated EPC memory > + * @phys_addr: physical address at which @pci_addr is mapped > + * @virt_base: base virtual address of the allocated EPC memory > + * @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 > + * @map_align: 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 +90,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 (*map_align)(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, > @@ -234,6 +265,8 @@ int pci_epc_set_bar(struct pci_epc *epc, u8 func_no, u8 vfunc_no, > struct pci_epf_bar *epf_bar); > void pci_epc_clear_bar(struct pci_epc *epc, u8 func_no, u8 vfunc_no, > struct pci_epf_bar *epf_bar); > +int pci_epc_map_align(struct pci_epc *epc, u8 func_no, u8 vfunc_no, > + u64 pci_addr, size_t size, struct pci_epc_map *map); > int pci_epc_map_addr(struct pci_epc *epc, u8 func_no, u8 vfunc_no, > phys_addr_t phys_addr, > u64 pci_addr, size_t size); > -- > 2.44.0 >
On 4/3/24 16:45, Manivannan Sadhasivam wrote: > On Sat, Mar 30, 2024 at 01:19: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 rockchip endpoint controller uses >> at most the lower 20 bits of a physical memory address region as the >> lower bits of an RC PCI address. 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 >> a PCI EP function driver is not possible with the current EPC API as >> it only provides the ->align field in struct pci_epc_features. >> Furthermore, this alignment is static and does not depend on a mapping >> pci address and size. >> >> Solve this by introducing the function pci_epc_map_align() and the >> endpoint controller operation ->map_align to allow endpoint function >> drivers to obtain the size and the offset into a controller address >> region that must be used to map an RC PCI address region. The size >> of the physical address region provided by pci_epc_map_align() can then >> be used as the size argument for the function pci_epc_mem_alloc_addr(). >> The offset into the allocated controller memory can be used to >> correctly handle data transfers. Of note is that pci_epc_map_align() may >> indicate upon return a mapping size that is smaller (but not 0) than the >> requested PCI address region size. For such case, an endpoint function >> driver must handle data transfers in fragments. >> > > Is there any incentive in exposing pci_epc_map_align()? I mean, why can't it be > hidden inside the new alloc() API itself? I could drop pci_epc_map_align(), but the idea here was to have an API that is not restrictive. E.g., a function driver could allocate memory, keep it and repetedly use map_align and map() function to remap it to different PCI addresses. With your suggestion, that would not be possible. > > Furthermore, is it possible to avoid the map_align() callback and handle the > alignment within the EPC driver? I am not so sure that this is possible because handling the alignment can potentially result in changing the amount of memory to allocate, based on the PCI address also. So the allocation API would need to change, a lot. >> + /* >> + * Assume a fixed alignment constraint as specified by the controller >> + * features. >> + */ >> + features = pci_epc_get_features(epc, func_no, vfunc_no); >> + if (!features || !features->align) { >> + map->map_pci_addr = pci_addr; >> + map->map_size = size; >> + map->map_ofst = 0; > > These values are overwritten anyway below. Looks like "return" got dropped. Bug. Will re-add it.
On Wed, Apr 03, 2024 at 04:54:32PM +0900, Damien Le Moal wrote: > On 4/3/24 16:45, Manivannan Sadhasivam wrote: > > On Sat, Mar 30, 2024 at 01:19: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 rockchip endpoint controller uses > >> at most the lower 20 bits of a physical memory address region as the > >> lower bits of an RC PCI address. 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 > >> a PCI EP function driver is not possible with the current EPC API as > >> it only provides the ->align field in struct pci_epc_features. > >> Furthermore, this alignment is static and does not depend on a mapping > >> pci address and size. > >> > >> Solve this by introducing the function pci_epc_map_align() and the > >> endpoint controller operation ->map_align to allow endpoint function > >> drivers to obtain the size and the offset into a controller address > >> region that must be used to map an RC PCI address region. The size > >> of the physical address region provided by pci_epc_map_align() can then > >> be used as the size argument for the function pci_epc_mem_alloc_addr(). > >> The offset into the allocated controller memory can be used to > >> correctly handle data transfers. Of note is that pci_epc_map_align() may > >> indicate upon return a mapping size that is smaller (but not 0) than the > >> requested PCI address region size. For such case, an endpoint function > >> driver must handle data transfers in fragments. > >> > > > > Is there any incentive in exposing pci_epc_map_align()? I mean, why can't it be > > hidden inside the new alloc() API itself? > > I could drop pci_epc_map_align(), but the idea here was to have an API that is > not restrictive. E.g., a function driver could allocate memory, keep it and > repetedly use map_align and map() function to remap it to different PCI > addresses. With your suggestion, that would not be possible. > Is there any requirement currently? If not, let's try to introduce it when the actual requirement comes. > > > > Furthermore, is it possible to avoid the map_align() callback and handle the > > alignment within the EPC driver? > > I am not so sure that this is possible because handling the alignment can > potentially result in changing the amount of memory to allocate, based on the > PCI address also. So the allocation API would need to change, a lot. > Hmm, looking at patch 11/18, I think it might become complicated. - Mani > >> + /* > >> + * Assume a fixed alignment constraint as specified by the controller > >> + * features. > >> + */ > >> + features = pci_epc_get_features(epc, func_no, vfunc_no); > >> + if (!features || !features->align) { > >> + map->map_pci_addr = pci_addr; > >> + map->map_size = size; > >> + map->map_ofst = 0; > > > > These values are overwritten anyway below. > > Looks like "return" got dropped. Bug. Will re-add it. > > > -- > Damien Le Moal > Western Digital Research >
Hi Damien, On 3/30/2024 9:49 AM, 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 rockchip endpoint controller uses > at most the lower 20 bits of a physical memory address region as the > lower bits of an RC PCI address. 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 > a PCI EP function driver is not possible with the current EPC API as > it only provides the ->align field in struct pci_epc_features. > Furthermore, this alignment is static and does not depend on a mapping > pci address and size. > > Solve this by introducing the function pci_epc_map_align() and the > endpoint controller operation ->map_align to allow endpoint function > drivers to obtain the size and the offset into a controller address > region that must be used to map an RC PCI address region. The size > of the physical address region provided by pci_epc_map_align() can then > be used as the size argument for the function pci_epc_mem_alloc_addr(). > The offset into the allocated controller memory can be used to > correctly handle data transfers. Of note is that pci_epc_map_align() may > indicate upon return a mapping size that is smaller (but not 0) than the > requested PCI address region size. For such case, an endpoint function > driver must handle data transfers in fragments. > > The controller operation ->map_align is optional: controllers that do > not have any address alignment constraints for mapping a RC PCI address > region do not need to implement this operation. For such controllers, > pci_epc_map_align() always returns the mapping size as equal > to the requested size and an offset equal to 0. > > The structure pci_epc_map is introduced to represent a mapping start PCI > address, size and the size and offset into the controller memory needed > for mapping the PCI address region. > > Signed-off-by: Damien Le Moal <dlemoal@kernel.org> > --- > drivers/pci/endpoint/pci-epc-core.c | 66 +++++++++++++++++++++++++++++ > include/linux/pci-epc.h | 33 +++++++++++++++ > 2 files changed, 99 insertions(+) > > diff --git a/drivers/pci/endpoint/pci-epc-core.c b/drivers/pci/endpoint/pci-epc-core.c > index 754afd115bbd..37758ca91d7f 100644 > --- a/drivers/pci/endpoint/pci-epc-core.c > +++ b/drivers/pci/endpoint/pci-epc-core.c > @@ -433,6 +433,72 @@ void pci_epc_unmap_addr(struct pci_epc *epc, u8 func_no, u8 vfunc_no, > } > EXPORT_SYMBOL_GPL(pci_epc_unmap_addr); > > +/** > + * pci_epc_map_align() - Get the offset into and the size of a controller memory > + * address region needed to map a RC PCI address region > + * @epc: the EPC device on which address is allocated > + * @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 physical address should be mapped > + * @size: the size of the mapping starting from @pci_addr > + * @map: populate here the actual size and offset into the controller memory > + * that must be allocated for the mapping > + * > + * Invoke the controller map_align operation to obtain the size and the offset > + * into a controller address region that must be allocated to map @size > + * bytes of the RC PCI address space starting from @pci_addr. > + * > + * The size of the mapping that can be handled by the controller is indicated > + * using the pci_size field of @map. This size may be smaller than the requested > + * @size. In such case, the function driver must handle the mapping using > + * several fragments. The offset into the controller memory for the effective > + * mapping of the @pci_addr..@pci_addr+@map->pci_size address range is indicated > + * using the map_ofst field of @map. > + */ > +int pci_epc_map_align(struct pci_epc *epc, u8 func_no, u8 vfunc_no, > + u64 pci_addr, size_t size, struct pci_epc_map *map) > +{ > + const struct pci_epc_features *features; > + size_t mask; > + int ret; > + > + if (!pci_epc_function_is_valid(epc, func_no, vfunc_no)) > + return -EINVAL; > + > + if (!size || !map) > + return -EINVAL; > + > + memset(map, 0, sizeof(*map)); > + map->pci_addr = pci_addr; > + map->pci_size = size; > + > + if (epc->ops->map_align) { > + mutex_lock(&epc->lock); > + ret = epc->ops->map_align(epc, func_no, vfunc_no, map); > + mutex_unlock(&epc->lock); > + return ret; > + } > + > + /* > + * Assume a fixed alignment constraint as specified by the controller > + * features. > + */ > + features = pci_epc_get_features(epc, func_no, vfunc_no); > + if (!features || !features->align) { > + map->map_pci_addr = pci_addr; > + map->map_size = size; > + map->map_ofst = 0; > + } The 'align' of pci_epc_features was initially added only to address the inbound ATU constraints. This is also added as comment in [1]. The PCI address restrictions (only fixed alignment constraint) were handled by the host side driver and depends on the connected endpoint device (atleast it was like that for pci_endpoint_test.c [2]). So pci-epf-test.c used the 'align' in pci_epc_features only as part of pci_epf_alloc_space(). Though I have abused 'align' of pci_epc_features in pci-epf-ntb.c using it out of pci_epf_alloc_space(), I think we should keep the 'align' of pci_epc_features only within pci_epf_alloc_space() and controllers with any PCI address restrictions to implement ->map_align(). This could as well be done in a phased manner to let controllers implement ->map_align() and then remove using pci_epc_features in pci_epc_map_align(). Let me know what you think? Thanks, Kishon [1] -> https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/include/linux/pci-epc.h?h=v6.9-rc2#n187 [2] -> https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/drivers/misc/pci_endpoint_test.c?h=v6.9-rc2#n127 > + > + mask = features->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, features->align); > + > + return 0; > +} > +EXPORT_SYMBOL_GPL(pci_epc_map_align); > + > /** > * pci_epc_map_addr() - map CPU address to PCI address > * @epc: the EPC device on which address is allocated > diff --git a/include/linux/pci-epc.h b/include/linux/pci-epc.h > index cc2f70d061c8..8cfb4aaf2628 100644 > --- a/include/linux/pci-epc.h > +++ b/include/linux/pci-epc.h > @@ -32,11 +32,40 @@ 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 to map > + * @map_pci_addr: RC PCI address used as the first address mapped > + * @map_size: size of the controller memory needed for the mapping > + * @map_ofst: offset into the controller memory needed for the mapping > + * @phys_base: base physical address of the allocated EPC memory > + * @phys_addr: physical address at which @pci_addr is mapped > + * @virt_base: base virtual address of the allocated EPC memory > + * @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 > + * @map_align: 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 +90,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 (*map_align)(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, > @@ -234,6 +265,8 @@ int pci_epc_set_bar(struct pci_epc *epc, u8 func_no, u8 vfunc_no, > struct pci_epf_bar *epf_bar); > void pci_epc_clear_bar(struct pci_epc *epc, u8 func_no, u8 vfunc_no, > struct pci_epf_bar *epf_bar); > +int pci_epc_map_align(struct pci_epc *epc, u8 func_no, u8 vfunc_no, > + u64 pci_addr, size_t size, struct pci_epc_map *map); > int pci_epc_map_addr(struct pci_epc *epc, u8 func_no, u8 vfunc_no, > phys_addr_t phys_addr, > u64 pci_addr, size_t size);
On 4/3/24 21:33, Kishon Vijay Abraham I wrote: > Hi Damien, > > On 3/30/2024 9:49 AM, 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 rockchip endpoint controller uses >> at most the lower 20 bits of a physical memory address region as the >> lower bits of an RC PCI address. 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 >> a PCI EP function driver is not possible with the current EPC API as >> it only provides the ->align field in struct pci_epc_features. >> Furthermore, this alignment is static and does not depend on a mapping >> pci address and size. >> >> Solve this by introducing the function pci_epc_map_align() and the >> endpoint controller operation ->map_align to allow endpoint function >> drivers to obtain the size and the offset into a controller address >> region that must be used to map an RC PCI address region. The size >> of the physical address region provided by pci_epc_map_align() can then >> be used as the size argument for the function pci_epc_mem_alloc_addr(). >> The offset into the allocated controller memory can be used to >> correctly handle data transfers. Of note is that pci_epc_map_align() may >> indicate upon return a mapping size that is smaller (but not 0) than the >> requested PCI address region size. For such case, an endpoint function >> driver must handle data transfers in fragments. >> >> The controller operation ->map_align is optional: controllers that do >> not have any address alignment constraints for mapping a RC PCI address >> region do not need to implement this operation. For such controllers, >> pci_epc_map_align() always returns the mapping size as equal >> to the requested size and an offset equal to 0. >> >> The structure pci_epc_map is introduced to represent a mapping start PCI >> address, size and the size and offset into the controller memory needed >> for mapping the PCI address region. >> >> Signed-off-by: Damien Le Moal <dlemoal@kernel.org> >> --- >> drivers/pci/endpoint/pci-epc-core.c | 66 +++++++++++++++++++++++++++++ >> include/linux/pci-epc.h | 33 +++++++++++++++ >> 2 files changed, 99 insertions(+) >> >> diff --git a/drivers/pci/endpoint/pci-epc-core.c b/drivers/pci/endpoint/pci-epc-core.c >> index 754afd115bbd..37758ca91d7f 100644 >> --- a/drivers/pci/endpoint/pci-epc-core.c >> +++ b/drivers/pci/endpoint/pci-epc-core.c >> @@ -433,6 +433,72 @@ void pci_epc_unmap_addr(struct pci_epc *epc, u8 func_no, u8 vfunc_no, >> } >> EXPORT_SYMBOL_GPL(pci_epc_unmap_addr); >> >> +/** >> + * pci_epc_map_align() - Get the offset into and the size of a controller memory >> + * address region needed to map a RC PCI address region >> + * @epc: the EPC device on which address is allocated >> + * @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 physical address should be mapped >> + * @size: the size of the mapping starting from @pci_addr >> + * @map: populate here the actual size and offset into the controller memory >> + * that must be allocated for the mapping >> + * >> + * Invoke the controller map_align operation to obtain the size and the offset >> + * into a controller address region that must be allocated to map @size >> + * bytes of the RC PCI address space starting from @pci_addr. >> + * >> + * The size of the mapping that can be handled by the controller is indicated >> + * using the pci_size field of @map. This size may be smaller than the requested >> + * @size. In such case, the function driver must handle the mapping using >> + * several fragments. The offset into the controller memory for the effective >> + * mapping of the @pci_addr..@pci_addr+@map->pci_size address range is indicated >> + * using the map_ofst field of @map. >> + */ >> +int pci_epc_map_align(struct pci_epc *epc, u8 func_no, u8 vfunc_no, >> + u64 pci_addr, size_t size, struct pci_epc_map *map) >> +{ >> + const struct pci_epc_features *features; >> + size_t mask; >> + int ret; >> + >> + if (!pci_epc_function_is_valid(epc, func_no, vfunc_no)) >> + return -EINVAL; >> + >> + if (!size || !map) >> + return -EINVAL; >> + >> + memset(map, 0, sizeof(*map)); >> + map->pci_addr = pci_addr; >> + map->pci_size = size; >> + >> + if (epc->ops->map_align) { >> + mutex_lock(&epc->lock); >> + ret = epc->ops->map_align(epc, func_no, vfunc_no, map); >> + mutex_unlock(&epc->lock); >> + return ret; >> + } >> + >> + /* >> + * Assume a fixed alignment constraint as specified by the controller >> + * features. >> + */ >> + features = pci_epc_get_features(epc, func_no, vfunc_no); >> + if (!features || !features->align) { >> + map->map_pci_addr = pci_addr; >> + map->map_size = size; >> + map->map_ofst = 0; >> + } > > The 'align' of pci_epc_features was initially added only to address the > inbound ATU constraints. This is also added as comment in [1]. The PCI > address restrictions (only fixed alignment constraint) were handled by > the host side driver and depends on the connected endpoint device > (atleast it was like that for pci_endpoint_test.c [2]). > So pci-epf-test.c used the 'align' in pci_epc_features only as part of > pci_epf_alloc_space(). > > Though I have abused 'align' of pci_epc_features in pci-epf-ntb.c using > it out of pci_epf_alloc_space(), I think we should keep the 'align' of > pci_epc_features only within pci_epf_alloc_space() and controllers with > any PCI address restrictions to implement ->map_align(). This could as > well be done in a phased manner to let controllers implement > ->map_align() and then remove using pci_epc_features in > pci_epc_map_align(). Let me know what you think? Yep, good idea. I will remove the use of "align" as a default alignment constraint. For controllers that have a fixed alignment constraint (not necessarilly epc->features->align), it is trivial to provide a generic helper function that implements the ->map_align method.
Hi Damien, kernel test robot noticed the following build warnings: https://git-scm.com/docs/git-format-patch#_base_tree_information] url: https://github.com/intel-lab-lkp/linux/commits/Damien-Le-Moal/PCI-endpoint-Introduce-pci_epc_function_is_valid/20240330-122311 base: https://git.kernel.org/pub/scm/linux/kernel/git/pci/pci.git next patch link: https://lore.kernel.org/r/20240330041928.1555578-3-dlemoal%40kernel.org patch subject: [PATCH v2 02/18] PCI: endpoint: Introduce pci_epc_map_align() config: parisc-randconfig-r071-20240405 (https://download.01.org/0day-ci/archive/20240405/202404051508.hvNRDVZq-lkp@intel.com/config) compiler: hppa-linux-gcc (GCC) 13.2.0 If you fix the issue in a separate patch/commit (i.e. not just a new version of the same patch/commit), kindly add following tags | Reported-by: kernel test robot <lkp@intel.com> | Reported-by: Dan Carpenter <dan.carpenter@linaro.org> | Closes: https://lore.kernel.org/r/202404051508.hvNRDVZq-lkp@intel.com/ smatch warnings: drivers/pci/endpoint/pci-epc-core.c:493 pci_epc_map_align() error: we previously assumed 'features' could be null (see line 487) vim +/features +493 drivers/pci/endpoint/pci-epc-core.c 9d2f10d2ace040 Damien Le Moal 2024-03-30 458 int pci_epc_map_align(struct pci_epc *epc, u8 func_no, u8 vfunc_no, 9d2f10d2ace040 Damien Le Moal 2024-03-30 459 u64 pci_addr, size_t size, struct pci_epc_map *map) 9d2f10d2ace040 Damien Le Moal 2024-03-30 460 { 9d2f10d2ace040 Damien Le Moal 2024-03-30 461 const struct pci_epc_features *features; 9d2f10d2ace040 Damien Le Moal 2024-03-30 462 size_t mask; 9d2f10d2ace040 Damien Le Moal 2024-03-30 463 int ret; 9d2f10d2ace040 Damien Le Moal 2024-03-30 464 9d2f10d2ace040 Damien Le Moal 2024-03-30 465 if (!pci_epc_function_is_valid(epc, func_no, vfunc_no)) 9d2f10d2ace040 Damien Le Moal 2024-03-30 466 return -EINVAL; 9d2f10d2ace040 Damien Le Moal 2024-03-30 467 9d2f10d2ace040 Damien Le Moal 2024-03-30 468 if (!size || !map) 9d2f10d2ace040 Damien Le Moal 2024-03-30 469 return -EINVAL; 9d2f10d2ace040 Damien Le Moal 2024-03-30 470 9d2f10d2ace040 Damien Le Moal 2024-03-30 471 memset(map, 0, sizeof(*map)); 9d2f10d2ace040 Damien Le Moal 2024-03-30 472 map->pci_addr = pci_addr; 9d2f10d2ace040 Damien Le Moal 2024-03-30 473 map->pci_size = size; 9d2f10d2ace040 Damien Le Moal 2024-03-30 474 9d2f10d2ace040 Damien Le Moal 2024-03-30 475 if (epc->ops->map_align) { 9d2f10d2ace040 Damien Le Moal 2024-03-30 476 mutex_lock(&epc->lock); 9d2f10d2ace040 Damien Le Moal 2024-03-30 477 ret = epc->ops->map_align(epc, func_no, vfunc_no, map); 9d2f10d2ace040 Damien Le Moal 2024-03-30 478 mutex_unlock(&epc->lock); 9d2f10d2ace040 Damien Le Moal 2024-03-30 479 return ret; 9d2f10d2ace040 Damien Le Moal 2024-03-30 480 } 9d2f10d2ace040 Damien Le Moal 2024-03-30 481 9d2f10d2ace040 Damien Le Moal 2024-03-30 482 /* 9d2f10d2ace040 Damien Le Moal 2024-03-30 483 * Assume a fixed alignment constraint as specified by the controller 9d2f10d2ace040 Damien Le Moal 2024-03-30 484 * features. 9d2f10d2ace040 Damien Le Moal 2024-03-30 485 */ 9d2f10d2ace040 Damien Le Moal 2024-03-30 486 features = pci_epc_get_features(epc, func_no, vfunc_no); 9d2f10d2ace040 Damien Le Moal 2024-03-30 @487 if (!features || !features->align) { ^^^^^^^^^ Check for NULL 9d2f10d2ace040 Damien Le Moal 2024-03-30 488 map->map_pci_addr = pci_addr; 9d2f10d2ace040 Damien Le Moal 2024-03-30 489 map->map_size = size; 9d2f10d2ace040 Damien Le Moal 2024-03-30 490 map->map_ofst = 0; Missing return 0? 9d2f10d2ace040 Damien Le Moal 2024-03-30 491 } 9d2f10d2ace040 Damien Le Moal 2024-03-30 492 9d2f10d2ace040 Damien Le Moal 2024-03-30 @493 mask = features->align - 1; ^^^^^^^^^^ 9d2f10d2ace040 Damien Le Moal 2024-03-30 494 map->map_pci_addr = map->pci_addr & ~mask; 9d2f10d2ace040 Damien Le Moal 2024-03-30 495 map->map_ofst = map->pci_addr & mask; 9d2f10d2ace040 Damien Le Moal 2024-03-30 496 map->map_size = ALIGN(map->map_ofst + map->pci_size, features->align); 9d2f10d2ace040 Damien Le Moal 2024-03-30 497 9d2f10d2ace040 Damien Le Moal 2024-03-30 498 return 0; 9d2f10d2ace040 Damien Le Moal 2024-03-30 499 }
On Thu, Apr 04, 2024 at 11:43:47AM +0900, Damien Le Moal wrote: > On 4/3/24 21:33, Kishon Vijay Abraham I wrote: > > Hi Damien, > > > > On 3/30/2024 9:49 AM, 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 rockchip endpoint controller uses > >> at most the lower 20 bits of a physical memory address region as the > >> lower bits of an RC PCI address. 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 > >> a PCI EP function driver is not possible with the current EPC API as > >> it only provides the ->align field in struct pci_epc_features. > >> Furthermore, this alignment is static and does not depend on a mapping > >> pci address and size. > >> > >> Solve this by introducing the function pci_epc_map_align() and the > >> endpoint controller operation ->map_align to allow endpoint function > >> drivers to obtain the size and the offset into a controller address > >> region that must be used to map an RC PCI address region. The size > >> of the physical address region provided by pci_epc_map_align() can then > >> be used as the size argument for the function pci_epc_mem_alloc_addr(). > >> The offset into the allocated controller memory can be used to > >> correctly handle data transfers. Of note is that pci_epc_map_align() may > >> indicate upon return a mapping size that is smaller (but not 0) than the > >> requested PCI address region size. For such case, an endpoint function > >> driver must handle data transfers in fragments. > >> > >> The controller operation ->map_align is optional: controllers that do > >> not have any address alignment constraints for mapping a RC PCI address > >> region do not need to implement this operation. For such controllers, > >> pci_epc_map_align() always returns the mapping size as equal > >> to the requested size and an offset equal to 0. > >> > >> The structure pci_epc_map is introduced to represent a mapping start PCI > >> address, size and the size and offset into the controller memory needed > >> for mapping the PCI address region. > >> > >> Signed-off-by: Damien Le Moal <dlemoal@kernel.org> > >> --- > >> drivers/pci/endpoint/pci-epc-core.c | 66 +++++++++++++++++++++++++++++ > >> include/linux/pci-epc.h | 33 +++++++++++++++ > >> 2 files changed, 99 insertions(+) > >> > >> diff --git a/drivers/pci/endpoint/pci-epc-core.c b/drivers/pci/endpoint/pci-epc-core.c > >> index 754afd115bbd..37758ca91d7f 100644 > >> --- a/drivers/pci/endpoint/pci-epc-core.c > >> +++ b/drivers/pci/endpoint/pci-epc-core.c > >> @@ -433,6 +433,72 @@ void pci_epc_unmap_addr(struct pci_epc *epc, u8 func_no, u8 vfunc_no, > >> } > >> EXPORT_SYMBOL_GPL(pci_epc_unmap_addr); > >> > >> +/** > >> + * pci_epc_map_align() - Get the offset into and the size of a controller memory > >> + * address region needed to map a RC PCI address region > >> + * @epc: the EPC device on which address is allocated > >> + * @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 physical address should be mapped > >> + * @size: the size of the mapping starting from @pci_addr > >> + * @map: populate here the actual size and offset into the controller memory > >> + * that must be allocated for the mapping > >> + * > >> + * Invoke the controller map_align operation to obtain the size and the offset > >> + * into a controller address region that must be allocated to map @size > >> + * bytes of the RC PCI address space starting from @pci_addr. > >> + * > >> + * The size of the mapping that can be handled by the controller is indicated > >> + * using the pci_size field of @map. This size may be smaller than the requested > >> + * @size. In such case, the function driver must handle the mapping using > >> + * several fragments. The offset into the controller memory for the effective > >> + * mapping of the @pci_addr..@pci_addr+@map->pci_size address range is indicated > >> + * using the map_ofst field of @map. > >> + */ > >> +int pci_epc_map_align(struct pci_epc *epc, u8 func_no, u8 vfunc_no, > >> + u64 pci_addr, size_t size, struct pci_epc_map *map) > >> +{ > >> + const struct pci_epc_features *features; > >> + size_t mask; > >> + int ret; > >> + > >> + if (!pci_epc_function_is_valid(epc, func_no, vfunc_no)) > >> + return -EINVAL; > >> + > >> + if (!size || !map) > >> + return -EINVAL; > >> + > >> + memset(map, 0, sizeof(*map)); > >> + map->pci_addr = pci_addr; > >> + map->pci_size = size; > >> + > >> + if (epc->ops->map_align) { > >> + mutex_lock(&epc->lock); > >> + ret = epc->ops->map_align(epc, func_no, vfunc_no, map); > >> + mutex_unlock(&epc->lock); > >> + return ret; > >> + } > >> + > >> + /* > >> + * Assume a fixed alignment constraint as specified by the controller > >> + * features. > >> + */ > >> + features = pci_epc_get_features(epc, func_no, vfunc_no); > >> + if (!features || !features->align) { > >> + map->map_pci_addr = pci_addr; > >> + map->map_size = size; > >> + map->map_ofst = 0; > >> + } > > > > The 'align' of pci_epc_features was initially added only to address the > > inbound ATU constraints. This is also added as comment in [1]. The PCI > > address restrictions (only fixed alignment constraint) were handled by > > the host side driver and depends on the connected endpoint device > > (atleast it was like that for pci_endpoint_test.c [2]). > > So pci-epf-test.c used the 'align' in pci_epc_features only as part of > > pci_epf_alloc_space(). > > > > Though I have abused 'align' of pci_epc_features in pci-epf-ntb.c using > > it out of pci_epf_alloc_space(), I think we should keep the 'align' of > > pci_epc_features only within pci_epf_alloc_space() and controllers with > > any PCI address restrictions to implement ->map_align(). This could as > > well be done in a phased manner to let controllers implement > > ->map_align() and then remove using pci_epc_features in > > pci_epc_map_align(). Let me know what you think? First you say that you want to avoid using epc_features->align inside pci_epc_map_align(), and then you say that we could do it in phases, and eventually stop using epc_features->align in pci_epc_map_align(). I'm confused... :) Do you really want pci_epc_map_align() to make use of epc_features->align ? Don't you mean ep->page_size ? (Please read the whole email to see my reasoning.) > > Yep, good idea. I will remove the use of "align" as a default alignment > constraint. For controllers that have a fixed alignment constraint (not > necessarilly epc->features->align), it is trivial to provide a generic helper > function that implements the ->map_align method. We can see that commit: https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=2a9a801620efac92885fc9cd53594c0b9aba87a4 Introduced epc_features->align and modified pci_epf_alloc_space() to use it. From reading the commit, it appears that epc_features->align was intended to represent inbound iATU alignment requirement. For DWC based controllers, the inbound iATU address must be aligned to: CX_ATU_MIN_REGION_SIZE. AFAICT, epc_features->align currently has nothing to do with traffic outbound from the EP. For aligning the reads/writes to buffers allocated on the host side, we currently have .alignment in the host side driver: https://github.com/torvalds/linux/blob/v6.9-rc2/drivers/misc/pci_endpoint_test.c#L966-L1021 Which should be set to the outbound iATU alignment requirement. For DWC based controllers, the outbound iATU address must be aligned to: CX_ATU_MIN_REGION_SIZE. Additionally, we have ep->page_size, which defines the smallest outbound unit that can be mapped. (On DWC based controllers, tis is CX_ATU_MIN_REGION_SIZE.) ep->page_size is used to specify the outbound alignment for e.g. dw_pcie_ep_raise_msi_irq() and dw_pcie_ep_raise_msix_irq(): https://github.com/torvalds/linux/blob/v6.9-rc2/drivers/pci/controller/dwc/pcie-designware-ep.c#L488 https://github.com/torvalds/linux/blob/v6.9-rc2/drivers/pci/controller/dwc/pcie-designware-ep.c#L555 which makes sure that we can write to the RC side MSI/MSI-X address while satisfying the outbound iATU alignment requirement. See also: https://lore.kernel.org/linux-pci/20240402-pci2_upstream-v3-2-803414bdb430@nxp.com/ Now I understand that rockchip is the first one that does not have a fixed alignment. So for that platform, map_align() will be different from ep->page_size. (For all DWC based drivers the outbound iATU alignment requirement is the same as the page size.) However, it would be nice if: 1) We could have a default implementation of map_align() that by default uses ep->page_size. Platforms that have non-fixed alignment requirements could define their own map_align(). 2) We fix dw_pcie_ep_raise_msi_irq() and dw_pcie_ep_raise_msix_irq() to use the new pci_epc_map_align(). 3) It is getting too complicated with all these... epc_features->align, ep->page_size, map_align(), and .alignment in host driver. I think that we need to document each of these in Documentation/PCI/endpoint/ 4) It would be nice if we could set page_size correctly for all the PCI device and vendor IDs that have defined an .alignment in drivers/misc/pci_endpoint_test.c in the correct EPC driver. That way, we should be able to completely remove all .alignment specified in drivers/misc/pci_endpoint_test.c. 5) Unfortunately drivers/misc/pci_endpoint_test.c defines a default alignment of 4K: https://github.com/torvalds/linux/blob/v6.9-rc2/drivers/misc/pci_endpoint_test.c#L968 https://github.com/torvalds/linux/blob/v6.9-rc2/drivers/misc/pci_endpoint_test.c#L820 It would be nice if we could get rid of this as well. Or perhaps add an option to pci_test so that it does not use this 4k alignment, such that we can verify that pci_epc_map_align() is actually working. In my opinion 4) is the biggest win with this series, because it means that we define the alignment in the EPC driver, instead of needing to define it in each and every host side driver. But right now, this great improvement is not really visible for someone looking quickly at the current series. Kind regards, Niklas
On 4/5/24 21:20, Niklas Cassel wrote: > On Thu, Apr 04, 2024 at 11:43:47AM +0900, Damien Le Moal wrote: >> On 4/3/24 21:33, Kishon Vijay Abraham I wrote: >>> Hi Damien, >>> >>> On 3/30/2024 9:49 AM, 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 rockchip endpoint controller uses >>>> at most the lower 20 bits of a physical memory address region as the >>>> lower bits of an RC PCI address. 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 >>>> a PCI EP function driver is not possible with the current EPC API as >>>> it only provides the ->align field in struct pci_epc_features. >>>> Furthermore, this alignment is static and does not depend on a mapping >>>> pci address and size. >>>> >>>> Solve this by introducing the function pci_epc_map_align() and the >>>> endpoint controller operation ->map_align to allow endpoint function >>>> drivers to obtain the size and the offset into a controller address >>>> region that must be used to map an RC PCI address region. The size >>>> of the physical address region provided by pci_epc_map_align() can then >>>> be used as the size argument for the function pci_epc_mem_alloc_addr(). >>>> The offset into the allocated controller memory can be used to >>>> correctly handle data transfers. Of note is that pci_epc_map_align() may >>>> indicate upon return a mapping size that is smaller (but not 0) than the >>>> requested PCI address region size. For such case, an endpoint function >>>> driver must handle data transfers in fragments. >>>> >>>> The controller operation ->map_align is optional: controllers that do >>>> not have any address alignment constraints for mapping a RC PCI address >>>> region do not need to implement this operation. For such controllers, >>>> pci_epc_map_align() always returns the mapping size as equal >>>> to the requested size and an offset equal to 0. >>>> >>>> The structure pci_epc_map is introduced to represent a mapping start PCI >>>> address, size and the size and offset into the controller memory needed >>>> for mapping the PCI address region. >>>> >>>> Signed-off-by: Damien Le Moal <dlemoal@kernel.org> >>>> --- >>>> drivers/pci/endpoint/pci-epc-core.c | 66 +++++++++++++++++++++++++++++ >>>> include/linux/pci-epc.h | 33 +++++++++++++++ >>>> 2 files changed, 99 insertions(+) >>>> >>>> diff --git a/drivers/pci/endpoint/pci-epc-core.c b/drivers/pci/endpoint/pci-epc-core.c >>>> index 754afd115bbd..37758ca91d7f 100644 >>>> --- a/drivers/pci/endpoint/pci-epc-core.c >>>> +++ b/drivers/pci/endpoint/pci-epc-core.c >>>> @@ -433,6 +433,72 @@ void pci_epc_unmap_addr(struct pci_epc *epc, u8 func_no, u8 vfunc_no, >>>> } >>>> EXPORT_SYMBOL_GPL(pci_epc_unmap_addr); >>>> >>>> +/** >>>> + * pci_epc_map_align() - Get the offset into and the size of a controller memory >>>> + * address region needed to map a RC PCI address region >>>> + * @epc: the EPC device on which address is allocated >>>> + * @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 physical address should be mapped >>>> + * @size: the size of the mapping starting from @pci_addr >>>> + * @map: populate here the actual size and offset into the controller memory >>>> + * that must be allocated for the mapping >>>> + * >>>> + * Invoke the controller map_align operation to obtain the size and the offset >>>> + * into a controller address region that must be allocated to map @size >>>> + * bytes of the RC PCI address space starting from @pci_addr. >>>> + * >>>> + * The size of the mapping that can be handled by the controller is indicated >>>> + * using the pci_size field of @map. This size may be smaller than the requested >>>> + * @size. In such case, the function driver must handle the mapping using >>>> + * several fragments. The offset into the controller memory for the effective >>>> + * mapping of the @pci_addr..@pci_addr+@map->pci_size address range is indicated >>>> + * using the map_ofst field of @map. >>>> + */ >>>> +int pci_epc_map_align(struct pci_epc *epc, u8 func_no, u8 vfunc_no, >>>> + u64 pci_addr, size_t size, struct pci_epc_map *map) >>>> +{ >>>> + const struct pci_epc_features *features; >>>> + size_t mask; >>>> + int ret; >>>> + >>>> + if (!pci_epc_function_is_valid(epc, func_no, vfunc_no)) >>>> + return -EINVAL; >>>> + >>>> + if (!size || !map) >>>> + return -EINVAL; >>>> + >>>> + memset(map, 0, sizeof(*map)); >>>> + map->pci_addr = pci_addr; >>>> + map->pci_size = size; >>>> + >>>> + if (epc->ops->map_align) { >>>> + mutex_lock(&epc->lock); >>>> + ret = epc->ops->map_align(epc, func_no, vfunc_no, map); >>>> + mutex_unlock(&epc->lock); >>>> + return ret; >>>> + } >>>> + >>>> + /* >>>> + * Assume a fixed alignment constraint as specified by the controller >>>> + * features. >>>> + */ >>>> + features = pci_epc_get_features(epc, func_no, vfunc_no); >>>> + if (!features || !features->align) { >>>> + map->map_pci_addr = pci_addr; >>>> + map->map_size = size; >>>> + map->map_ofst = 0; >>>> + } >>> >>> The 'align' of pci_epc_features was initially added only to address the >>> inbound ATU constraints. This is also added as comment in [1]. The PCI >>> address restrictions (only fixed alignment constraint) were handled by >>> the host side driver and depends on the connected endpoint device >>> (atleast it was like that for pci_endpoint_test.c [2]). >>> So pci-epf-test.c used the 'align' in pci_epc_features only as part of >>> pci_epf_alloc_space(). >>> >>> Though I have abused 'align' of pci_epc_features in pci-epf-ntb.c using >>> it out of pci_epf_alloc_space(), I think we should keep the 'align' of >>> pci_epc_features only within pci_epf_alloc_space() and controllers with >>> any PCI address restrictions to implement ->map_align(). This could as >>> well be done in a phased manner to let controllers implement >>> ->map_align() and then remove using pci_epc_features in >>> pci_epc_map_align(). Let me know what you think? > > First you say that you want to avoid using epc_features->align inside > pci_epc_map_align(), and then you say that we could do it in phases, > and eventually stop using epc_features->align in pci_epc_map_align(). > > I'm confused... :) > > Do you really want pci_epc_map_align() to make use of epc_features->align ? > > Don't you mean ep->page_size ? > (Please read the whole email to see my reasoning.) > > >> >> Yep, good idea. I will remove the use of "align" as a default alignment >> constraint. For controllers that have a fixed alignment constraint (not >> necessarilly epc->features->align), it is trivial to provide a generic helper >> function that implements the ->map_align method. > > We can see that commit: > https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=2a9a801620efac92885fc9cd53594c0b9aba87a4 > > Introduced epc_features->align and modified pci_epf_alloc_space() to use it. > > From reading the commit, it appears that epc_features->align was intended to > represent inbound iATU alignment requirement. > > For DWC based controllers, the inbound iATU address must be aligned to: > CX_ATU_MIN_REGION_SIZE. > > AFAICT, epc_features->align currently has nothing to do with traffic outbound > from the EP. Yes, correct. It is for BARs, not for PCI address alignment constraint. > For aligning the reads/writes to buffers allocated on the host side, > we currently have .alignment in the host side driver: > https://github.com/torvalds/linux/blob/v6.9-rc2/drivers/misc/pci_endpoint_test.c#L966-L1021 > > Which should be set to the outbound iATU alignment requirement. > > For DWC based controllers, the outbound iATU address must be aligned to: > CX_ATU_MIN_REGION_SIZE. > Additionally, we have ep->page_size, which defines the smallest outbound unit > that can be mapped. > (On DWC based controllers, tis is CX_ATU_MIN_REGION_SIZE.) > > ep->page_size is used to specify the outbound alignment for e.g. > dw_pcie_ep_raise_msi_irq() and dw_pcie_ep_raise_msix_irq(): > https://github.com/torvalds/linux/blob/v6.9-rc2/drivers/pci/controller/dwc/pcie-designware-ep.c#L488 > https://github.com/torvalds/linux/blob/v6.9-rc2/drivers/pci/controller/dwc/pcie-designware-ep.c#L555> which makes sure that we can write to the RC side MSI/MSI-X address > while satisfying the outbound iATU alignment requirement. > > See also: > https://lore.kernel.org/linux-pci/20240402-pci2_upstream-v3-2-803414bdb430@nxp.com/ > > > > Now I understand that rockchip is the first one that does not have a fixed > alignment. > So for that platform, map_align() will be different from ep->page_size. > (For all DWC based drivers the outbound iATU alignment requirement is > the same as the page size.) Yes. So we can have a generic map_align() implementation that all these drivers can use as there .map_align method. No need to expose page size to the epc/epf core code. > However, it would be nice if: > 1) We could have a default implementation of map_align() that by default uses > ep->page_size. Platforms that have non-fixed alignment requirements could > define their own map_align(). See above. The default implementation can be a helper function defined in epc core that the drivers can use for their .map_align() method. > > 2) We fix dw_pcie_ep_raise_msi_irq() and dw_pcie_ep_raise_msix_irq() to use > the new pci_epc_map_align(). Why ? That is completely internal to the controller driver. > > 3) It is getting too complicated with all these... > epc_features->align, ep->page_size, map_align(), and .alignment in host driver. > I think that we need to document each of these in Documentation/PCI/endpoint/ test host driver .alignment needs to be nuked. That one is nonsense. ep->page_size needs to stay internal to the driver. .map_align method is enough to handle any PCI address mapping constraint and will indicate memory size to allocate, offset into it etc. And for the BARs alignment, .align feature is not exactly great as it is not clear, but it is enough I think. So we could just rename it to be clear. And even maybe remove it from features. I do not see why an EPF needs to care about it given that epc core funstions are used to setup the bars. > 4) It would be nice if we could set page_size correctly for all the PCI device > and vendor IDs that have defined an .alignment in drivers/misc/pci_endpoint_test.c > in the correct EPC driver. That way, we should be able to completely remove all > .alignment specified in drivers/misc/pci_endpoint_test.c. The host side should be allowed to use any PCI address alignment it wants. So no alignment communicated at all. It is the EP side that needs to deal with alignment. > 5) Unfortunately drivers/misc/pci_endpoint_test.c defines a default alignment > of 4K: > https://github.com/torvalds/linux/blob/v6.9-rc2/drivers/misc/pci_endpoint_test.c#L968 > https://github.com/torvalds/linux/blob/v6.9-rc2/drivers/misc/pci_endpoint_test.c#L820 > > It would be nice if we could get rid of this as well. Or perhaps add an option > to pci_test so that it does not use this 4k alignment, such that we can verify > that pci_epc_map_align() is actually working. Exactly. Get rid of any default alignment, add a test parameter to define one so that we can test different alignment+size combinations. > In my opinion 4) is the biggest win with this series, because it means that > we define the alignment in the EPC driver, instead of needing to define it in > each and every host side driver. But right now, this great improvement is not > really visible for someone looking quickly at the current series. Yes. Once in place, we can rework the test driver alignment stuff to make it optional instead of mandatory because of bad handling on the EP side :)
On Fri, Apr 05, 2024 at 09:43:42PM +0900, Damien Le Moal wrote: > On 4/5/24 21:20, Niklas Cassel wrote: > > > > Now I understand that rockchip is the first one that does not have a fixed > > alignment. > > So for that platform, map_align() will be different from ep->page_size. > > (For all DWC based drivers the outbound iATU alignment requirement is > > the same as the page size.) > > Yes. So we can have a generic map_align() implementation that all these drivers > can use as there .map_align method. No need to expose page size to the epc/epf > core code. I don't follow. ep->page_size is used by pci_epc_multi_mem_init(), pci_epc_mem_alloc_addr() and pci_epc_mem_free_addr(), so it is used by EPC core. pci_epc_mem_alloc_addr() currently uses it to align up an allocation to the page size, so that an allocation from the PCI window/memory space is a multiple of page_size. How can we avoid exposing the page size to EPC core? For a DWC-based driver, the mapping part requires that the start address of the mapping should be aligned to the page size. But e.g. drivers/pci/controller/pcie-rockchip-ep.c, sets page size (smallest allocation): to 1 MB: windows[i].page_size = SZ_1M; But the mapping part for rockchip-ep appears to have different requirements. > > > However, it would be nice if: > > 1) We could have a default implementation of map_align() that by default uses > > ep->page_size. Platforms that have non-fixed alignment requirements could > > define their own map_align(). > > See above. The default implementation can be a helper function defined in epc > core that the drivers can use for their .map_align() method. Sounds good. > > 2) We fix dw_pcie_ep_raise_msi_irq() and dw_pcie_ep_raise_msix_irq() to use > > the new pci_epc_map_align(). > > Why ? That is completely internal to the controller driver. Well, dw_pcie_ep_raise_msi_irq() and dw_pcie_ep_raise_msix_irq() currently does some clearing of lower bits of the address before using .map_addr() to map the an aligned outbound address. It will then write to the mapped address + some offset. Since it is already using .map_addr(), it seems like the perfect use case for pci_epc_map_align(), as the function then would not need to do any clearing of lower bits before mapping, nor writing to an offset within the mapping. It would just use pci_epc_map_align() and then write to map->pci_addr. > > 3) It is getting too complicated with all these... > > epc_features->align, ep->page_size, map_align(), and .alignment in host driver. > > I think that we need to document each of these in Documentation/PCI/endpoint/ > > test host driver .alignment needs to be nuked. That one is nonsense. > ep->page_size needs to stay internal to the driver. .map_align method is enough > to handle any PCI address mapping constraint and will indicate memory size to > allocate, offset into it etc. And for the BARs alignment, .align feature is not > exactly great as it is not clear, but it is enough I think. So we could just > rename it to be clear. And even maybe remove it from features. I do not see why > an EPF needs to care about it given that epc core funstions are used to setup > the bars. +1 on renaming it to bar_alignment or inbound_alignment or similar. I don't think that we can remove it from epc_features. It is used by pci_epf_alloc_space() which uses epc_features->align to ensure that when an EPF driver allocates the backing memory for a BAR, the backing memory is aligned to bar_alignment. (An allocation of size X is guaranteed to be aligned to X.) > > 4) It would be nice if we could set page_size correctly for all the PCI device > > and vendor IDs that have defined an .alignment in drivers/misc/pci_endpoint_test.c > > in the correct EPC driver. That way, we should be able to completely remove all > > .alignment specified in drivers/misc/pci_endpoint_test.c. > > The host side should be allowed to use any PCI address alignment it wants. So no > alignment communicated at all. It is the EP side that needs to deal with alignment. I think we are saying the same thing. But in order to remove all .alignment uses in drivers/misc/pci_endpoint_test.c, we will need to add modify the corresponding EPC driver to either: - Define the ep->page_size, so that the generic map_align() implementation will work. (If you grep for page_size in drivers/pci/controller, you will see that very few EPC drivers currently set ep->page_size, even though the PCI device and vendor IDs for those same controllers have specified an alignment in drivers/misc/pci_endpoint_test.c) - Define a custom map_align() implementation. > > 5) Unfortunately drivers/misc/pci_endpoint_test.c defines a default alignment > > of 4K: > > https://github.com/torvalds/linux/blob/v6.9-rc2/drivers/misc/pci_endpoint_test.c#L968 > > https://github.com/torvalds/linux/blob/v6.9-rc2/drivers/misc/pci_endpoint_test.c#L820 > > > > It would be nice if we could get rid of this as well. Or perhaps add an option > > to pci_test so that it does not use this 4k alignment, such that we can verify > > that pci_epc_map_align() is actually working. > > Exactly. Get rid of any default alignment, add a test parameter to define one so > that we can test different alignment+size combinations. > > > In my opinion 4) is the biggest win with this series, because it means that > > we define the alignment in the EPC driver, instead of needing to define it in > > each and every host side driver. But right now, this great improvement is not > > really visible for someone looking quickly at the current series. > > Yes. Once in place, we can rework the test driver alignment stuff to make it > optional instead of mandatory because of bad handling on the EP side :) Perhaps it would be nice to have 5) implemented for this initial series, so that it is possible to test that this new API is behaving as intended? Kind regards, Niklas
On 4/5/2024 5:50 PM, Niklas Cassel wrote: > On Thu, Apr 04, 2024 at 11:43:47AM +0900, Damien Le Moal wrote: >> On 4/3/24 21:33, Kishon Vijay Abraham I wrote: >>> Hi Damien, >>> >>> On 3/30/2024 9:49 AM, 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 rockchip endpoint controller uses >>>> at most the lower 20 bits of a physical memory address region as the >>>> lower bits of an RC PCI address. 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 >>>> a PCI EP function driver is not possible with the current EPC API as >>>> it only provides the ->align field in struct pci_epc_features. >>>> Furthermore, this alignment is static and does not depend on a mapping >>>> pci address and size. >>>> >>>> Solve this by introducing the function pci_epc_map_align() and the >>>> endpoint controller operation ->map_align to allow endpoint function >>>> drivers to obtain the size and the offset into a controller address >>>> region that must be used to map an RC PCI address region. The size >>>> of the physical address region provided by pci_epc_map_align() can then >>>> be used as the size argument for the function pci_epc_mem_alloc_addr(). >>>> The offset into the allocated controller memory can be used to >>>> correctly handle data transfers. Of note is that pci_epc_map_align() may >>>> indicate upon return a mapping size that is smaller (but not 0) than the >>>> requested PCI address region size. For such case, an endpoint function >>>> driver must handle data transfers in fragments. >>>> >>>> The controller operation ->map_align is optional: controllers that do >>>> not have any address alignment constraints for mapping a RC PCI address >>>> region do not need to implement this operation. For such controllers, >>>> pci_epc_map_align() always returns the mapping size as equal >>>> to the requested size and an offset equal to 0. >>>> >>>> The structure pci_epc_map is introduced to represent a mapping start PCI >>>> address, size and the size and offset into the controller memory needed >>>> for mapping the PCI address region. >>>> >>>> Signed-off-by: Damien Le Moal <dlemoal@kernel.org> >>>> --- >>>> drivers/pci/endpoint/pci-epc-core.c | 66 +++++++++++++++++++++++++++++ >>>> include/linux/pci-epc.h | 33 +++++++++++++++ >>>> 2 files changed, 99 insertions(+) >>>> >>>> diff --git a/drivers/pci/endpoint/pci-epc-core.c b/drivers/pci/endpoint/pci-epc-core.c >>>> index 754afd115bbd..37758ca91d7f 100644 >>>> --- a/drivers/pci/endpoint/pci-epc-core.c >>>> +++ b/drivers/pci/endpoint/pci-epc-core.c >>>> @@ -433,6 +433,72 @@ void pci_epc_unmap_addr(struct pci_epc *epc, u8 func_no, u8 vfunc_no, >>>> } >>>> EXPORT_SYMBOL_GPL(pci_epc_unmap_addr); >>>> >>>> +/** >>>> + * pci_epc_map_align() - Get the offset into and the size of a controller memory >>>> + * address region needed to map a RC PCI address region >>>> + * @epc: the EPC device on which address is allocated >>>> + * @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 physical address should be mapped >>>> + * @size: the size of the mapping starting from @pci_addr >>>> + * @map: populate here the actual size and offset into the controller memory >>>> + * that must be allocated for the mapping >>>> + * >>>> + * Invoke the controller map_align operation to obtain the size and the offset >>>> + * into a controller address region that must be allocated to map @size >>>> + * bytes of the RC PCI address space starting from @pci_addr. >>>> + * >>>> + * The size of the mapping that can be handled by the controller is indicated >>>> + * using the pci_size field of @map. This size may be smaller than the requested >>>> + * @size. In such case, the function driver must handle the mapping using >>>> + * several fragments. The offset into the controller memory for the effective >>>> + * mapping of the @pci_addr..@pci_addr+@map->pci_size address range is indicated >>>> + * using the map_ofst field of @map. >>>> + */ >>>> +int pci_epc_map_align(struct pci_epc *epc, u8 func_no, u8 vfunc_no, >>>> + u64 pci_addr, size_t size, struct pci_epc_map *map) >>>> +{ >>>> + const struct pci_epc_features *features; >>>> + size_t mask; >>>> + int ret; >>>> + >>>> + if (!pci_epc_function_is_valid(epc, func_no, vfunc_no)) >>>> + return -EINVAL; >>>> + >>>> + if (!size || !map) >>>> + return -EINVAL; >>>> + >>>> + memset(map, 0, sizeof(*map)); >>>> + map->pci_addr = pci_addr; >>>> + map->pci_size = size; >>>> + >>>> + if (epc->ops->map_align) { >>>> + mutex_lock(&epc->lock); >>>> + ret = epc->ops->map_align(epc, func_no, vfunc_no, map); >>>> + mutex_unlock(&epc->lock); >>>> + return ret; >>>> + } >>>> + >>>> + /* >>>> + * Assume a fixed alignment constraint as specified by the controller >>>> + * features. >>>> + */ >>>> + features = pci_epc_get_features(epc, func_no, vfunc_no); >>>> + if (!features || !features->align) { >>>> + map->map_pci_addr = pci_addr; >>>> + map->map_size = size; >>>> + map->map_ofst = 0; >>>> + } >>> >>> The 'align' of pci_epc_features was initially added only to address the >>> inbound ATU constraints. This is also added as comment in [1]. The PCI >>> address restrictions (only fixed alignment constraint) were handled by >>> the host side driver and depends on the connected endpoint device >>> (atleast it was like that for pci_endpoint_test.c [2]). >>> So pci-epf-test.c used the 'align' in pci_epc_features only as part of >>> pci_epf_alloc_space(). >>> >>> Though I have abused 'align' of pci_epc_features in pci-epf-ntb.c using >>> it out of pci_epf_alloc_space(), I think we should keep the 'align' of >>> pci_epc_features only within pci_epf_alloc_space() and controllers with >>> any PCI address restrictions to implement ->map_align(). This could as >>> well be done in a phased manner to let controllers implement >>> ->map_align() and then remove using pci_epc_features in >>> pci_epc_map_align(). Let me know what you think? > > First you say that you want to avoid using epc_features->align inside > pci_epc_map_align(), and then you say that we could do it in phases, > and eventually stop using epc_features->align in pci_epc_map_align(). > > I'm confused... :) > > Do you really want pci_epc_map_align() to make use of epc_features->align ? Would like pci_epc_map_align() to not use epc_features->align as pci_epc_map_align() is for PCIe address programmed in outbound ATU (destination) and epc_features->align is for physical address programmed in inbound ATU (target for BAR accesses). I mentioned "in phases" for if some platforms require pci_epc_map_align() to use epc_features->align, we could keep epc_features->align in pci_epc_map_align() till all of them implement the map_align() callback (and need not be part of this series itself). But eventually stop using epc_features->align in pci_epc_map_align() once all the platforms that require alignment implement ->map_align(). > > Don't you mean ep->page_size ? > (Please read the whole email to see my reasoning.) page_size is for two purposes: 1) Dividing into fixed size blocks the outbound address space for simpler memory management 2) Alignment restrictions for source address (outbound address space) of the outbound ATU (so page_size is for source alignment restriction in outbound ATU and pci_epc_map_align() is for destination alignment restriction in outbound ATU. Source address would be an address in the outbound address space and destination address would be PCI address usually provided by the host). Even if some platform doesn't require source alignment restriction, it still has to divide into fixed size blocks. > > >> >> Yep, good idea. I will remove the use of "align" as a default alignment >> constraint. For controllers that have a fixed alignment constraint (not >> necessarilly epc->features->align), it is trivial to provide a generic helper >> function that implements the ->map_align method. > > We can see that commit: > https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=2a9a801620efac92885fc9cd53594c0b9aba87a4 > > Introduced epc_features->align and modified pci_epf_alloc_space() to use it. > > From reading the commit, it appears that epc_features->align was intended to > represent inbound iATU alignment requirement. > > For DWC based controllers, the inbound iATU address must be aligned to: > CX_ATU_MIN_REGION_SIZE. > > AFAICT, epc_features->align currently has nothing to do with traffic outbound > from the EP. It was initially added that way but I abused that in pci-epf-ntb.c > > > For aligning the reads/writes to buffers allocated on the host side, > we currently have .alignment in the host side driver: > https://github.com/torvalds/linux/blob/v6.9-rc2/drivers/misc/pci_endpoint_test.c#L966-L1021 > > Which should be set to the outbound iATU alignment requirement. > > For DWC based controllers, the outbound iATU address must be aligned to: > CX_ATU_MIN_REGION_SIZE. > > > Additionally, we have ep->page_size, which defines the smallest outbound unit > that can be mapped. > (On DWC based controllers, tis is CX_ATU_MIN_REGION_SIZE.) > > ep->page_size is used to specify the outbound alignment for e.g. > dw_pcie_ep_raise_msi_irq() and dw_pcie_ep_raise_msix_irq(): > https://github.com/torvalds/linux/blob/v6.9-rc2/drivers/pci/controller/dwc/pcie-designware-ep.c#L488 > https://github.com/torvalds/linux/blob/v6.9-rc2/drivers/pci/controller/dwc/pcie-designware-ep.c#L555 > > which makes sure that we can write to the RC side MSI/MSI-X address > while satisfying the outbound iATU alignment requirement. > > See also: > https://lore.kernel.org/linux-pci/20240402-pci2_upstream-v3-2-803414bdb430@nxp.com/ > > > > Now I understand that rockchip is the first one that does not have a fixed > alignment. > So for that platform, map_align() will be different from ep->page_size. > (For all DWC based drivers the outbound iATU alignment requirement is > the same as the page size.) > > However, it would be nice if: > 1) We could have a default implementation of map_align() that by default uses > ep->page_size. Platforms that have non-fixed alignment requirements could > define their own map_align(). IMHO generic/core functions should not overload ep->page_size or epc_features->align as each of them has their own specific purpose. > > 2) We fix dw_pcie_ep_raise_msi_irq() and dw_pcie_ep_raise_msix_irq() to use > the new pci_epc_map_align(). > > 3) It is getting too complicated with all these... > epc_features->align, ep->page_size, map_align(), and .alignment in host driver. > I think that we need to document each of these in Documentation/PCI/endpoint/ right, .alignment should be deprecated and each of the others should be documented to indicate the specific purpose it's added for. > > 4) It would be nice if we could set page_size correctly for all the PCI device > and vendor IDs that have defined an .alignment in drivers/misc/pci_endpoint_test.c > in the correct EPC driver. That way, we should be able to completely remove all > .alignment specified in drivers/misc/pci_endpoint_test.c. We should remove .alignment specified in drivers/misc/pci_endpoint_test.c and each EPC driver should populate ->map_align() callback to provide the correct alignment. Don't think this should change the page_size. > > 5) Unfortunately drivers/misc/pci_endpoint_test.c defines a default alignment > of 4K: > https://github.com/torvalds/linux/blob/v6.9-rc2/drivers/misc/pci_endpoint_test.c#L968 > https://github.com/torvalds/linux/blob/v6.9-rc2/drivers/misc/pci_endpoint_test.c#L820 > > It would be nice if we could get rid of this as well. Or perhaps add an option > to pci_test so that it does not use this 4k alignment, such that we can verify > that pci_epc_map_align() is actually working. +1 Thanks, Kishon > > > > In my opinion 4) is the biggest win with this series, because it means that > we define the alignment in the EPC driver, instead of needing to define it in > each and every host side driver. But right now, this great improvement is not > really visible for someone looking quickly at the current series. > > > Kind regards, > Niklas
diff --git a/drivers/pci/endpoint/pci-epc-core.c b/drivers/pci/endpoint/pci-epc-core.c index 754afd115bbd..37758ca91d7f 100644 --- a/drivers/pci/endpoint/pci-epc-core.c +++ b/drivers/pci/endpoint/pci-epc-core.c @@ -433,6 +433,72 @@ void pci_epc_unmap_addr(struct pci_epc *epc, u8 func_no, u8 vfunc_no, } EXPORT_SYMBOL_GPL(pci_epc_unmap_addr); +/** + * pci_epc_map_align() - Get the offset into and the size of a controller memory + * address region needed to map a RC PCI address region + * @epc: the EPC device on which address is allocated + * @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 physical address should be mapped + * @size: the size of the mapping starting from @pci_addr + * @map: populate here the actual size and offset into the controller memory + * that must be allocated for the mapping + * + * Invoke the controller map_align operation to obtain the size and the offset + * into a controller address region that must be allocated to map @size + * bytes of the RC PCI address space starting from @pci_addr. + * + * The size of the mapping that can be handled by the controller is indicated + * using the pci_size field of @map. This size may be smaller than the requested + * @size. In such case, the function driver must handle the mapping using + * several fragments. The offset into the controller memory for the effective + * mapping of the @pci_addr..@pci_addr+@map->pci_size address range is indicated + * using the map_ofst field of @map. + */ +int pci_epc_map_align(struct pci_epc *epc, u8 func_no, u8 vfunc_no, + u64 pci_addr, size_t size, struct pci_epc_map *map) +{ + const struct pci_epc_features *features; + size_t mask; + int ret; + + if (!pci_epc_function_is_valid(epc, func_no, vfunc_no)) + return -EINVAL; + + if (!size || !map) + return -EINVAL; + + memset(map, 0, sizeof(*map)); + map->pci_addr = pci_addr; + map->pci_size = size; + + if (epc->ops->map_align) { + mutex_lock(&epc->lock); + ret = epc->ops->map_align(epc, func_no, vfunc_no, map); + mutex_unlock(&epc->lock); + return ret; + } + + /* + * Assume a fixed alignment constraint as specified by the controller + * features. + */ + features = pci_epc_get_features(epc, func_no, vfunc_no); + if (!features || !features->align) { + map->map_pci_addr = pci_addr; + map->map_size = size; + map->map_ofst = 0; + } + + mask = features->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, features->align); + + return 0; +} +EXPORT_SYMBOL_GPL(pci_epc_map_align); + /** * pci_epc_map_addr() - map CPU address to PCI address * @epc: the EPC device on which address is allocated diff --git a/include/linux/pci-epc.h b/include/linux/pci-epc.h index cc2f70d061c8..8cfb4aaf2628 100644 --- a/include/linux/pci-epc.h +++ b/include/linux/pci-epc.h @@ -32,11 +32,40 @@ 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 to map + * @map_pci_addr: RC PCI address used as the first address mapped + * @map_size: size of the controller memory needed for the mapping + * @map_ofst: offset into the controller memory needed for the mapping + * @phys_base: base physical address of the allocated EPC memory + * @phys_addr: physical address at which @pci_addr is mapped + * @virt_base: base virtual address of the allocated EPC memory + * @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 + * @map_align: 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 +90,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 (*map_align)(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, @@ -234,6 +265,8 @@ int pci_epc_set_bar(struct pci_epc *epc, u8 func_no, u8 vfunc_no, struct pci_epf_bar *epf_bar); void pci_epc_clear_bar(struct pci_epc *epc, u8 func_no, u8 vfunc_no, struct pci_epf_bar *epf_bar); +int pci_epc_map_align(struct pci_epc *epc, u8 func_no, u8 vfunc_no, + u64 pci_addr, size_t size, struct pci_epc_map *map); int pci_epc_map_addr(struct pci_epc *epc, u8 func_no, u8 vfunc_no, phys_addr_t phys_addr, u64 pci_addr, size_t size);
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 rockchip endpoint controller uses at most the lower 20 bits of a physical memory address region as the lower bits of an RC PCI address. 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 a PCI EP function driver is not possible with the current EPC API as it only provides the ->align field in struct pci_epc_features. Furthermore, this alignment is static and does not depend on a mapping pci address and size. Solve this by introducing the function pci_epc_map_align() and the endpoint controller operation ->map_align to allow endpoint function drivers to obtain the size and the offset into a controller address region that must be used to map an RC PCI address region. The size of the physical address region provided by pci_epc_map_align() can then be used as the size argument for the function pci_epc_mem_alloc_addr(). The offset into the allocated controller memory can be used to correctly handle data transfers. Of note is that pci_epc_map_align() may indicate upon return a mapping size that is smaller (but not 0) than the requested PCI address region size. For such case, an endpoint function driver must handle data transfers in fragments. The controller operation ->map_align is optional: controllers that do not have any address alignment constraints for mapping a RC PCI address region do not need to implement this operation. For such controllers, pci_epc_map_align() always returns the mapping size as equal to the requested size and an offset equal to 0. The structure pci_epc_map is introduced to represent a mapping start PCI address, size and the size and offset into the controller memory needed for mapping the PCI address region. Signed-off-by: Damien Le Moal <dlemoal@kernel.org> --- drivers/pci/endpoint/pci-epc-core.c | 66 +++++++++++++++++++++++++++++ include/linux/pci-epc.h | 33 +++++++++++++++ 2 files changed, 99 insertions(+)