Fix regression issue after r346645 in the LinuxKPI.

The S/G list must be mapped AS-IS without any optimisations.
This also implies that sg_dma_len() must be equal to sg->length.
Many Linux drivers assume this and this fixes some DRM issues.

Put the BUS DMA map pointer into the scatter-gather list to
allow multiple mappings on the same physical memory address.

The FreeBSD version has been bumped to force recompilation of
external kernel modules.

Sponsored by:		Mellanox Technologies
This commit is contained in:
Hans Petter Selasky 2019-05-04 09:47:01 +00:00
parent 8ec9f0282a
commit 442d12d89c
3 changed files with 22 additions and 61 deletions

View File

@ -36,6 +36,7 @@
#include <linux/slab.h>
#include <linux/mm.h>
struct bus_dmamap;
struct scatterlist {
unsigned long page_link;
#define SG_PAGE_LINK_CHAIN 0x1UL
@ -44,7 +45,7 @@ struct scatterlist {
unsigned int offset;
unsigned int length;
dma_addr_t dma_address;
unsigned int dma_length;
struct bus_dmamap *dma_map; /* FreeBSD specific */
};
CTASSERT((sizeof(struct scatterlist) & SG_PAGE_LINK_MASK) == 0);
@ -79,7 +80,7 @@ struct sg_page_iter {
((struct scatterlist *) ((sg)->page_link & ~SG_PAGE_LINK_MASK))
#define sg_dma_address(sg) (sg)->dma_address
#define sg_dma_len(sg) (sg)->dma_length
#define sg_dma_len(sg) (sg)->length
#define for_each_sg_page(sgl, iter, nents, pgoffset) \
for (_sg_iter_init(sgl, iter, nents, pgoffset); \

View File

@ -562,70 +562,39 @@ linux_dma_map_sg_attrs(struct device *dev, struct scatterlist *sgl, int nents,
enum dma_data_direction dir, struct dma_attrs *attrs)
{
struct linux_dma_priv *priv;
struct linux_dma_obj *obj;
struct scatterlist *dma_sg, *sg;
int dma_nents, error, nseg;
size_t seg_len;
vm_paddr_t seg_phys, prev_phys_end;
struct scatterlist *sg;
int i, nseg;
bus_dma_segment_t seg;
priv = dev->dma_priv;
obj = uma_zalloc(linux_dma_obj_zone, 0);
DMA_PRIV_LOCK(priv);
if (bus_dmamap_create(priv->dmat, 0, &obj->dmamap) != 0) {
/* create common DMA map in the first S/G entry */
if (bus_dmamap_create(priv->dmat, 0, &sgl->dma_map) != 0) {
DMA_PRIV_UNLOCK(priv);
uma_zfree(linux_dma_obj_zone, obj);
return (0);
}
sg = sgl;
dma_sg = sg;
dma_nents = 0;
while (nents > 0) {
seg_phys = sg_phys(sg);
seg_len = sg->length;
while (--nents > 0) {
prev_phys_end = sg_phys(sg) + sg->length;
sg = sg_next(sg);
if (prev_phys_end != sg_phys(sg))
break;
seg_len += sg->length;
}
/* load all S/G list entries */
for_each_sg(sgl, sg, nents, i) {
nseg = -1;
if (_bus_dmamap_load_phys(priv->dmat, obj->dmamap,
seg_phys, seg_len, BUS_DMA_NOWAIT,
if (_bus_dmamap_load_phys(priv->dmat, sgl->dma_map,
sg_phys(sg), sg->length, BUS_DMA_NOWAIT,
&seg, &nseg) != 0) {
bus_dmamap_unload(priv->dmat, obj->dmamap);
bus_dmamap_destroy(priv->dmat, obj->dmamap);
bus_dmamap_unload(priv->dmat, sgl->dma_map);
bus_dmamap_destroy(priv->dmat, sgl->dma_map);
DMA_PRIV_UNLOCK(priv);
uma_zfree(linux_dma_obj_zone, obj);
return (0);
}
KASSERT(++nseg == 1, ("More than one segment (nseg=%d)", nseg));
KASSERT(nseg == 0,
("More than one segment (nseg=%d)", nseg + 1));
sg_dma_address(dma_sg) = seg.ds_addr;
sg_dma_len(dma_sg) = seg.ds_len;
dma_sg = sg_next(dma_sg);
dma_nents++;
}
obj->dma_addr = sg_dma_address(sgl);
error = LINUX_DMA_PCTRIE_INSERT(&priv->ptree, obj);
if (error != 0) {
bus_dmamap_unload(priv->dmat, obj->dmamap);
bus_dmamap_destroy(priv->dmat, obj->dmamap);
DMA_PRIV_UNLOCK(priv);
uma_zfree(linux_dma_obj_zone, obj);
return (0);
sg_dma_address(sg) = seg.ds_addr;
}
DMA_PRIV_UNLOCK(priv);
return (dma_nents);
return (nents);
}
void
@ -633,22 +602,13 @@ linux_dma_unmap_sg_attrs(struct device *dev, struct scatterlist *sgl,
int nents, enum dma_data_direction dir, struct dma_attrs *attrs)
{
struct linux_dma_priv *priv;
struct linux_dma_obj *obj;
priv = dev->dma_priv;
DMA_PRIV_LOCK(priv);
obj = LINUX_DMA_PCTRIE_LOOKUP(&priv->ptree, sg_dma_address(sgl));
if (obj == NULL) {
DMA_PRIV_UNLOCK(priv);
return;
}
LINUX_DMA_PCTRIE_REMOVE(&priv->ptree, sg_dma_address(sgl));
bus_dmamap_unload(priv->dmat, obj->dmamap);
bus_dmamap_destroy(priv->dmat, obj->dmamap);
bus_dmamap_unload(priv->dmat, sgl->dma_map);
bus_dmamap_destroy(priv->dmat, sgl->dma_map);
DMA_PRIV_UNLOCK(priv);
uma_zfree(linux_dma_obj_zone, obj);
}
struct dma_pool {

View File

@ -60,7 +60,7 @@
* in the range 5 to 9.
*/
#undef __FreeBSD_version
#define __FreeBSD_version 1300021 /* Master, propagated to newvers */
#define __FreeBSD_version 1300022 /* Master, propagated to newvers */
/*
* __FreeBSD_kernel__ indicates that this system uses the kernel of FreeBSD,