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:
parent
8ec9f0282a
commit
442d12d89c
@ -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); \
|
||||
|
@ -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 {
|
||||
|
@ -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,
|
||||
|
Loading…
Reference in New Issue
Block a user