Messages in this thread | | | Subject | Re: [RFC PATCH v2 06/11] dma-direct: Support PCI P2PDMA pages in dma-direct map_sg | From | Robin Murphy <> | Date | Fri, 12 Mar 2021 15:52:02 +0000 |
| |
On 2021-03-11 23:31, Logan Gunthorpe wrote: > Add PCI P2PDMA support for dma_direct_map_sg() so that it can map > PCI P2PDMA pages directly without a hack in the callers. This allows > for heterogeneous SGLs that contain both P2PDMA and regular pages. > > SGL segments that contain PCI bus addresses are marked with > sg_mark_pci_p2pdma() and are ignored when unmapped. > > Signed-off-by: Logan Gunthorpe <logang@deltatee.com> > --- > kernel/dma/direct.c | 35 ++++++++++++++++++++++++++++++++--- > kernel/dma/mapping.c | 13 ++++++++++--- > 2 files changed, 42 insertions(+), 6 deletions(-) > > diff --git a/kernel/dma/direct.c b/kernel/dma/direct.c > index 002268262c9a..f326d32062dd 100644 > --- a/kernel/dma/direct.c > +++ b/kernel/dma/direct.c > @@ -13,6 +13,7 @@ > #include <linux/vmalloc.h> > #include <linux/set_memory.h> > #include <linux/slab.h> > +#include <linux/pci-p2pdma.h> > #include "direct.h" > > /* > @@ -387,19 +388,47 @@ void dma_direct_unmap_sg(struct device *dev, struct scatterlist *sgl, > struct scatterlist *sg; > int i; > > - for_each_sg(sgl, sg, nents, i) > + for_each_sg(sgl, sg, nents, i) { > + if (sg_is_pci_p2pdma(sg)) > + continue; > + > dma_direct_unmap_page(dev, sg->dma_address, sg_dma_len(sg), dir, > attrs); > + } > } > #endif > > int dma_direct_map_sg(struct device *dev, struct scatterlist *sgl, int nents, > enum dma_data_direction dir, unsigned long attrs) > { > - int i; > + struct dev_pagemap *pgmap = NULL; > + int i, map = -1, ret = 0; > struct scatterlist *sg; > + u64 bus_off; > > for_each_sg(sgl, sg, nents, i) { > + if (is_pci_p2pdma_page(sg_page(sg))) { > + if (sg_page(sg)->pgmap != pgmap) { > + pgmap = sg_page(sg)->pgmap; > + map = pci_p2pdma_dma_map_type(dev, pgmap); > + bus_off = pci_p2pdma_bus_offset(sg_page(sg)); > + } > + > + if (map < 0) { > + sg->dma_address = DMA_MAPPING_ERROR; > + ret = -EREMOTEIO; > + goto out_unmap; > + } > + > + if (map) { > + sg->dma_address = sg_phys(sg) + sg->offset - > + bus_off; > + sg_dma_len(sg) = sg->length; > + sg_mark_pci_p2pdma(sg); > + continue; > + } > + } > + > sg->dma_address = dma_direct_map_page(dev, sg_page(sg), > sg->offset, sg->length, dir, attrs); > if (sg->dma_address == DMA_MAPPING_ERROR) > @@ -411,7 +440,7 @@ int dma_direct_map_sg(struct device *dev, struct scatterlist *sgl, int nents, > > out_unmap: > dma_direct_unmap_sg(dev, sgl, i, dir, attrs | DMA_ATTR_SKIP_CPU_SYNC); > - return 0; > + return ret; > } > > dma_addr_t dma_direct_map_resource(struct device *dev, phys_addr_t paddr, > diff --git a/kernel/dma/mapping.c b/kernel/dma/mapping.c > index b6a633679933..adc1a83950be 100644 > --- a/kernel/dma/mapping.c > +++ b/kernel/dma/mapping.c > @@ -178,8 +178,15 @@ void dma_unmap_page_attrs(struct device *dev, dma_addr_t addr, size_t size, > EXPORT_SYMBOL(dma_unmap_page_attrs); > > /* > - * dma_maps_sg_attrs returns 0 on error and > 0 on success. > - * It should never return a value < 0. > + * dma_maps_sg_attrs returns 0 on any resource error and > 0 on success. > + * > + * If 0 is returned, the mapping can be retried and will succeed once > + * sufficient resources are available.
That's not a guarantee we can uphold. Retrying forever in the vain hope that a device might evolve some extra address bits, or a bounce buffer might magically grow big enough for a gigantic mapping, isn't necessarily the best idea.
> + * > + * If there are P2PDMA pages in the scatterlist then this function may > + * return -EREMOTEIO to indicate that the pages are not mappable by the > + * device. In this case, an error should be returned for the IO as it > + * will never be successfully retried. > */ > int dma_map_sg_attrs(struct device *dev, struct scatterlist *sg, int nents, > enum dma_data_direction dir, unsigned long attrs) > @@ -197,7 +204,7 @@ int dma_map_sg_attrs(struct device *dev, struct scatterlist *sg, int nents, > ents = dma_direct_map_sg(dev, sg, nents, dir, attrs); > else > ents = ops->map_sg(dev, sg, nents, dir, attrs); > - BUG_ON(ents < 0); > +
This scares me - I hesitate to imagine the amount of driver/subsystem code out there that will see nonzero and merrily set off iterating a negative number of segments, if we open the floodgates of allowing implementations to return error codes here.
Robin.
> debug_dma_map_sg(dev, sg, nents, ents, dir); > > return ents; >
| |