Page Menu
Home
FreeBSD
Search
Configure Global Search
Log In
Files
F141924643
D19845.id55962.diff
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Flag For Later
Award Token
Size
16 KB
Referenced Files
None
Subscribers
None
D19845.id55962.diff
View Options
Index: sys/compat/linuxkpi/common/include/linux/dma-mapping.h
===================================================================
--- sys/compat/linuxkpi/common/include/linux/dma-mapping.h
+++ sys/compat/linuxkpi/common/include/linux/dma-mapping.h
@@ -90,22 +90,28 @@
#define DMA_BIT_MASK(n) ((2ULL << ((n) - 1)) - 1ULL)
+int linux_dma_tag_init(struct device *dev, u64 mask);
+dma_addr_t linux_dma_map_phys(struct device *dev, vm_paddr_t phys, size_t len);
+void linux_dma_unmap(struct device *dev, dma_addr_t dma_addr, size_t size);
+int linux_dma_map_sg_attrs(struct device *dev, struct scatterlist *sgl,
+ int nents, enum dma_data_direction dir, struct dma_attrs *attrs);
+void linux_dma_unmap_sg_attrs(struct device *dev, struct scatterlist *sg,
+ int nents, enum dma_data_direction dir, struct dma_attrs *attrs);
+
static inline int
dma_supported(struct device *dev, u64 mask)
{
- /* XXX busdma takes care of this elsewhere. */
- return (1);
+ return (linux_dma_tag_init(dev, mask) == 0);
}
static inline int
dma_set_mask(struct device *dev, u64 dma_mask)
{
- if (!dev->dma_mask || !dma_supported(dev, dma_mask))
+ if (!dma_supported(dev, dma_mask))
return -EIO;
- *dev->dma_mask = dma_mask;
return (0);
}
@@ -148,7 +154,7 @@
mem = (void *)kmem_alloc_contig(size, flag, 0, high, align, 0,
VM_MEMATTR_DEFAULT);
if (mem)
- *dma_handle = vtophys(mem);
+ *dma_handle = linux_dma_map_phys(dev, vtophys(mem), size);
else
*dma_handle = 0;
return (mem);
@@ -164,25 +170,27 @@
static inline void
dma_free_coherent(struct device *dev, size_t size, void *cpu_addr,
- dma_addr_t dma_handle)
+ dma_addr_t dma_addr)
{
+ linux_dma_unmap(dev, dma_addr, size);
kmem_free((vm_offset_t)cpu_addr, size);
}
-/* XXX This only works with no iommu. */
static inline dma_addr_t
dma_map_single_attrs(struct device *dev, void *ptr, size_t size,
enum dma_data_direction dir, struct dma_attrs *attrs)
{
- return vtophys(ptr);
+ return (linux_dma_map_phys(dev, vtophys(ptr), size));
}
static inline void
-dma_unmap_single_attrs(struct device *dev, dma_addr_t addr, size_t size,
+dma_unmap_single_attrs(struct device *dev, dma_addr_t dma_addr, size_t size,
enum dma_data_direction dir, struct dma_attrs *attrs)
{
+
+ linux_dma_unmap(dev, dma_addr, size);
}
static inline dma_addr_t
@@ -190,26 +198,23 @@
size_t size, enum dma_data_direction dir, unsigned long attrs)
{
- return (VM_PAGE_TO_PHYS(page) + offset);
+ return (linux_dma_map_phys(dev, VM_PAGE_TO_PHYS(page) + offset, size));
}
static inline int
dma_map_sg_attrs(struct device *dev, struct scatterlist *sgl, int nents,
enum dma_data_direction dir, struct dma_attrs *attrs)
{
- struct scatterlist *sg;
- int i;
- for_each_sg(sgl, sg, nents, i)
- sg_dma_address(sg) = sg_phys(sg);
-
- return (nents);
+ return (linux_dma_map_sg_attrs(dev, sgl, nents, dir, attrs));
}
static inline void
dma_unmap_sg_attrs(struct device *dev, struct scatterlist *sg, int nents,
enum dma_data_direction dir, struct dma_attrs *attrs)
{
+
+ linux_dma_unmap_sg_attrs(dev, sg, nents, dir, attrs);
}
static inline dma_addr_t
@@ -217,13 +222,15 @@
unsigned long offset, size_t size, enum dma_data_direction direction)
{
- return VM_PAGE_TO_PHYS(page) + offset;
+ return (linux_dma_map_phys(dev, VM_PAGE_TO_PHYS(page) + offset, size));
}
static inline void
dma_unmap_page(struct device *dev, dma_addr_t dma_address, size_t size,
enum dma_data_direction direction)
{
+
+ linux_dma_unmap(dev, dma_address, size);
}
static inline void
@@ -273,7 +280,7 @@
dma_mapping_error(struct device *dev, dma_addr_t dma_addr)
{
- return (0);
+ return (dma_addr == 0);
}
static inline unsigned int dma_set_max_seg_size(struct device *dev,
Index: sys/compat/linuxkpi/common/include/linux/dmapool.h
===================================================================
--- sys/compat/linuxkpi/common/include/linux/dmapool.h
+++ sys/compat/linuxkpi/common/include/linux/dmapool.h
@@ -37,44 +37,42 @@
#include <linux/device.h>
#include <linux/slab.h>
+struct dma_pool *linux_dma_pool_create(char *name, struct device *dev,
+ size_t size, size_t align, size_t boundary);
+void linux_dma_pool_destroy(struct dma_pool *pool);
+void *linux_dma_pool_alloc(struct dma_pool *pool, gfp_t mem_flags,
+ dma_addr_t *handle);
+void linux_dma_pool_free(struct dma_pool *pool, void *vaddr,
+ dma_addr_t dma_addr);
+
struct dma_pool {
+ struct pci_dev *pool_pdev;
uma_zone_t pool_zone;
+ bus_dma_tag_t pool_dmat;
+ size_t pool_entry_size;
+ struct pctrie pool_ptree;
};
static inline struct dma_pool *
dma_pool_create(char *name, struct device *dev, size_t size,
size_t align, size_t boundary)
{
- struct dma_pool *pool;
- pool = kmalloc(sizeof(*pool), GFP_KERNEL);
- align--;
- /*
- * XXX Eventually this could use a separate allocf to honor boundary
- * and physical address requirements of the device.
- */
- pool->pool_zone = uma_zcreate(name, size, NULL, NULL, NULL, NULL,
- align, UMA_ZONE_OFFPAGE|UMA_ZONE_HASH);
-
- return (pool);
+ return (linux_dma_pool_create(name, dev, size, align, boundary));
}
static inline void
dma_pool_destroy(struct dma_pool *pool)
{
- uma_zdestroy(pool->pool_zone);
- kfree(pool);
+
+ linux_dma_pool_destroy(pool);
}
static inline void *
dma_pool_alloc(struct dma_pool *pool, gfp_t mem_flags, dma_addr_t *handle)
{
- void *vaddr;
- vaddr = uma_zalloc(pool->pool_zone, mem_flags);
- if (vaddr)
- *handle = vtophys(vaddr);
- return (vaddr);
+ return (linux_dma_pool_alloc(pool, mem_flags, handle));
}
static inline void *
@@ -85,9 +83,10 @@
}
static inline void
-dma_pool_free(struct dma_pool *pool, void *vaddr, dma_addr_t addr)
+dma_pool_free(struct dma_pool *pool, void *vaddr, dma_addr_t dma_addr)
{
- uma_zfree(pool->pool_zone, vaddr);
+
+ linux_dma_pool_free(pool, vaddr, dma_addr);
}
Index: sys/compat/linuxkpi/common/include/linux/pci.h
===================================================================
--- sys/compat/linuxkpi/common/include/linux/pci.h
+++ sys/compat/linuxkpi/common/include/linux/pci.h
@@ -219,7 +219,7 @@
struct list_head links;
struct pci_driver *pdrv;
struct pci_bus *bus;
- uint64_t dma_mask;
+ void *dma_priv;
uint16_t device;
uint16_t vendor;
uint16_t subsystem_vendor;
Index: sys/compat/linuxkpi/common/src/linux_pci.c
===================================================================
--- sys/compat/linuxkpi/common/src/linux_pci.c
+++ sys/compat/linuxkpi/common/src/linux_pci.c
@@ -38,6 +38,7 @@
#include <sys/fcntl.h>
#include <sys/file.h>
#include <sys/filio.h>
+#include <sys/pctrie.h>
#include <sys/rwlock.h>
#include <vm/vm.h>
@@ -75,6 +76,65 @@
DEVMETHOD_END
};
+struct linux_dma_priv {
+ uint64_t dma_mask;
+ bus_dma_tag_t dmat;
+ struct pctrie ptree;
+};
+
+static int
+linux_pdev_dma_init(struct pci_dev *pdev)
+{
+ struct linux_dma_priv *priv;
+
+ priv = malloc(sizeof(*priv), M_DEVBUF, M_WAITOK | M_ZERO);
+ pdev->dma_priv = priv;
+
+ pdev->dev.dma_mask = &priv->dma_mask;
+
+ pctrie_init(&priv->ptree);
+
+ return (0);
+}
+
+static int
+linux_pdev_dma_uninit(struct pci_dev *pdev)
+{
+ struct linux_dma_priv *priv;
+
+ priv = pdev->dma_priv;
+ if (priv->dmat)
+ bus_dma_tag_destroy(priv->dmat);
+ free(priv, M_DEVBUF);
+ pdev->dma_priv = NULL;
+ return (0);
+}
+
+int
+linux_dma_tag_init(struct device *dev, u64 mask)
+{
+ struct pci_dev *pdev;
+ struct linux_dma_priv *priv;
+ int error;
+
+ pdev = to_pci_dev(dev);
+ priv = pdev->dma_priv;
+
+ priv->dma_mask = mask;
+ error = bus_dma_tag_create(bus_get_dma_tag(pdev->dev.bsddev),
+ 1, 0, /* alignment, boundary */
+ mask, /* lowaddr */
+ BUS_SPACE_MAXADDR, /* highaddr */
+ NULL, NULL, /* filtfunc, filtfuncarg */
+ BUS_SPACE_MAXADDR, /* maxsize */
+ 1, /* nsegments */
+ BUS_SPACE_MAXADDR, /* maxsegsz */
+ 0, /* flags */
+ NULL, NULL, /* lockfunc, lockfuncarg */
+ &priv->dmat);
+ return (-error);
+}
+
static struct pci_driver *
linux_pci_find(device_t dev, const struct pci_device_id **idp)
{
@@ -158,7 +218,6 @@
pdev->subsystem_device = dinfo->cfg.subdevice;
pdev->class = pci_get_class(dev);
pdev->revision = pci_get_revid(dev);
- pdev->dev.dma_mask = &pdev->dma_mask;
pdev->pdrv = pdrv;
kobject_init(&pdev->dev.kobj, &linux_dev_ktype);
kobject_set_name(&pdev->dev.kobj, device_get_nameunit(dev));
@@ -170,6 +229,9 @@
else
pdev->dev.irq = LINUX_IRQ_INVALID;
pdev->irq = pdev->dev.irq;
+ error = linux_pdev_dma_init(pdev);
+ if (error)
+ goto out;
if (pdev->bus == NULL) {
pbus = malloc(sizeof(*pbus), M_DEVBUF, M_WAITOK | M_ZERO);
@@ -183,6 +245,7 @@
spin_unlock(&pci_lock);
error = pdrv->probe(pdev, id);
+out:
if (error) {
spin_lock(&pci_lock);
list_del(&pdev->links);
@@ -202,6 +265,7 @@
pdev = device_get_softc(dev);
pdev->pdrv->remove(pdev);
+ linux_pdev_dma_uninit(pdev);
spin_lock(&pci_lock);
list_del(&pdev->links);
@@ -330,3 +394,332 @@
devclass_delete_driver(bus, &pdrv->bsddriver);
mtx_unlock(&Giant);
}
+
+struct linux_dma_obj {
+ void *vaddr;
+ dma_addr_t dma_addr;
+ bus_dmamap_t dmamap;
+};
+
+static uma_zone_t linux_dma_trie_zone;
+static uma_zone_t linux_dma_obj_zone;
+
+static void
+linux_dma_init(void *arg)
+{
+
+ linux_dma_trie_zone = uma_zcreate("linux_dma_pctrie",
+ pctrie_node_size(), NULL, NULL, pctrie_zone_init, NULL,
+ UMA_ALIGN_PTR, 0);
+ linux_dma_obj_zone = uma_zcreate("linux_dma_object",
+ sizeof(struct linux_dma_obj), NULL, NULL, NULL, NULL,
+ UMA_ALIGN_PTR, 0);
+
+}
+SYSINIT(linux_dma, SI_SUB_DRIVERS, SI_ORDER_THIRD, linux_dma_init, NULL);
+
+static void
+linux_dma_uninit(void *arg)
+{
+
+ uma_zdestroy(linux_dma_obj_zone);
+ uma_zdestroy(linux_dma_trie_zone);
+}
+SYSUNINIT(linux_dma, SI_SUB_DRIVERS, SI_ORDER_THIRD, linux_dma_uninit, NULL);
+
+static void *
+linux_dma_trie_alloc(struct pctrie *ptree)
+{
+
+ return (uma_zalloc(linux_dma_trie_zone, 0));
+}
+
+static void
+linux_dma_trie_free(struct pctrie *ptree, void *node)
+{
+
+ uma_zfree(linux_dma_trie_zone, node);
+}
+
+
+PCTRIE_DEFINE(LINUX_DMA, linux_dma_obj, dma_addr, linux_dma_trie_alloc,
+ linux_dma_trie_free);
+
+dma_addr_t
+linux_dma_map_phys(struct device *dev, vm_paddr_t phys, size_t len)
+{
+ struct pci_dev *pdev;
+ struct linux_dma_priv *priv;
+ struct linux_dma_obj *obj;
+ int nseg;
+ bus_dma_segment_t seg;
+
+ pdev = to_pci_dev(dev);
+ priv = pdev->dma_priv;
+
+ obj = uma_zalloc(linux_dma_obj_zone, 0);
+
+ if (bus_dmamap_create(priv->dmat, 0, &obj->dmamap) != 0) {
+ uma_zfree(linux_dma_obj_zone, obj);
+ return (0);
+ }
+
+ nseg = -1;
+ if (_bus_dmamap_load_phys(priv->dmat, obj->dmamap, phys, len, 0,
+ &seg, &nseg) != 0) {
+ bus_dmamap_destroy(priv->dmat, obj->dmamap);
+ uma_zfree(linux_dma_obj_zone, obj);
+ return (0);
+ }
+ obj->dma_addr = seg.ds_addr;
+
+ if (LINUX_DMA_PCTRIE_INSERT(&priv->ptree, obj) != 0) {
+ bus_dmamap_unload(priv->dmat, obj->dmamap);
+ bus_dmamap_destroy(priv->dmat, obj->dmamap);
+ uma_zfree(linux_dma_obj_zone, obj);
+ return (0);
+ }
+
+ return (obj->dma_addr);
+}
+
+void
+linux_dma_unmap(struct device *dev, dma_addr_t dma_addr, size_t len)
+{
+ struct pci_dev *pdev;
+ struct linux_dma_priv *priv;
+ struct linux_dma_obj *obj;
+
+ pdev = to_pci_dev(dev);
+ priv = pdev->dma_priv;
+
+ obj = LINUX_DMA_PCTRIE_LOOKUP(&priv->ptree, dma_addr);
+ if (obj == NULL) {
+ return;
+ }
+ LINUX_DMA_PCTRIE_REMOVE(&priv->ptree, dma_addr);
+
+ bus_dmamap_unload(priv->dmat, obj->dmamap);
+ bus_dmamap_destroy(priv->dmat, obj->dmamap);
+
+ uma_zfree(linux_dma_obj_zone, obj);
+}
+
+int
+linux_dma_map_sg_attrs(struct device *dev, struct scatterlist *sgl, int nents,
+ enum dma_data_direction dir, struct dma_attrs *attrs)
+{
+ struct pci_dev *pdev;
+ struct linux_dma_priv *priv;
+ struct linux_dma_obj *obj;
+ struct scatterlist *sg;
+ int i, nseg;
+ bus_dma_segment_t seg;
+
+ pdev = to_pci_dev(dev);
+ priv = pdev->dma_priv;
+
+ obj = uma_zalloc(linux_dma_obj_zone, 0);
+ if (bus_dmamap_create(priv->dmat, 0, &obj->dmamap) != 0) {
+ uma_zfree(linux_dma_obj_zone, obj);
+ return (0);
+ }
+
+ for_each_sg(sgl, sg, nents, i) {
+ nseg = -1;
+ if (_bus_dmamap_load_phys(priv->dmat, obj->dmamap,
+ sg_phys(sg), sg->length, 0, &seg, &nseg) != 0) {
+ bus_dmamap_unload(priv->dmat, obj->dmamap);
+ bus_dmamap_destroy(priv->dmat, obj->dmamap);
+ uma_zfree(linux_dma_obj_zone, obj);
+ return (0);
+ }
+ sg_dma_address(sg) = seg.ds_addr;
+ }
+
+ obj->dma_addr = sg_dma_address(sgl);
+ if (LINUX_DMA_PCTRIE_INSERT(&priv->ptree, obj) != 0) {
+ bus_dmamap_unload(priv->dmat, obj->dmamap);
+ bus_dmamap_destroy(priv->dmat, obj->dmamap);
+ uma_zfree(linux_dma_obj_zone, obj);
+ return (0);
+ }
+
+ return (nents);
+}
+
+void
+linux_dma_unmap_sg_attrs(struct device *dev, struct scatterlist *sgl,
+ int nents, enum dma_data_direction dir, struct dma_attrs *attrs)
+{
+ struct pci_dev *pdev;
+ struct linux_dma_priv *priv;
+ struct linux_dma_obj *obj;
+
+ pdev = to_pci_dev(dev);
+ priv = pdev->dma_priv;
+
+ obj = LINUX_DMA_PCTRIE_LOOKUP(&priv->ptree, sg_dma_address(sgl));
+ if (obj == NULL) {
+ 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);
+ uma_zfree(linux_dma_obj_zone, obj);
+}
+
+static inline int
+dma_pool_obj_ctor(void *mem, int size, void *arg, int flags)
+{
+ struct linux_dma_obj *obj = mem;
+ struct dma_pool *pool = arg;
+ int error, nseg;
+ bus_dma_segment_t seg;
+
+ nseg = -1;
+ error = _bus_dmamap_load_phys(pool->pool_dmat, obj->dmamap,
+ vtophys(obj->vaddr), pool->pool_entry_size,
+ (flags & M_NOWAIT ? BUS_DMA_NOWAIT : 0), &seg, &nseg);
+ if (error != 0) {
+ return (error);
+ }
+ obj->dma_addr = seg.ds_addr;
+
+ return (0);
+}
+
+static void
+dma_pool_obj_dtor(void *mem, int size, void *arg)
+{
+ struct linux_dma_obj *obj = mem;
+ struct dma_pool *pool = arg;
+
+ bus_dmamap_unload(pool->pool_dmat, obj->dmamap);
+}
+
+static int
+dma_pool_obj_import(void *arg, void **store, int count, int domain __unused,
+ int flags)
+{
+ struct dma_pool *pool = arg;
+ struct linux_dma_priv *priv;
+ struct linux_dma_obj *obj;
+ int i;
+
+ priv = pool->pool_pdev->dma_priv;
+ for (i = 0; i < count; i++) {
+ obj = uma_zalloc(linux_dma_obj_zone, flags);
+ if (obj == NULL)
+ break;
+
+ if (bus_dmamem_alloc(pool->pool_dmat, &obj->vaddr,
+ (flags & M_NOWAIT ? BUS_DMA_NOWAIT : 0),
+ &obj->dmamap) != 0) {
+ uma_zfree(linux_dma_obj_zone, obj);
+ break;
+ }
+
+ store[i] = obj;
+ }
+
+ return (i);
+}
+
+static void
+dma_pool_obj_release(void *arg, void **store, int count)
+{
+ struct dma_pool *pool = arg;
+ struct linux_dma_priv *priv;
+ struct linux_dma_obj *obj;
+ int i;
+
+ priv = pool->pool_pdev->dma_priv;
+ for (i = 0; i < count; i++) {
+ obj = store[i];
+ bus_dmamem_free(pool->pool_dmat, obj->vaddr, obj->dmamap);
+ uma_zfree(linux_dma_obj_zone, obj);
+ }
+}
+
+struct dma_pool *
+linux_dma_pool_create(char *name, struct device *dev, size_t size,
+ size_t align, size_t boundary)
+{
+ struct pci_dev *pdev;
+ struct linux_dma_priv *priv;
+ struct dma_pool *pool;
+
+ pdev = to_pci_dev(dev);
+ priv = pdev->dma_priv;
+
+ pool = kzalloc(sizeof(*pool), GFP_KERNEL);
+ pool->pool_pdev = pdev;
+ pool->pool_entry_size = size;
+
+ if (bus_dma_tag_create(bus_get_dma_tag(dev->bsddev),
+ align, boundary, /* alignment, boundary */
+ priv->dma_mask, /* lowaddr */
+ BUS_SPACE_MAXADDR, /* highaddr */
+ NULL, NULL, /* filtfunc, filtfuncarg */
+ size, /* maxsize */
+ 1, /* nsegments */
+ size, /* maxsegsz */
+ 0, /* flags */
+ NULL, NULL, /* lockfunc, lockfuncarg */
+ &pool->pool_dmat)) {
+ kfree(pool);
+ return (NULL);
+ }
+
+ pool->pool_zone = uma_zcache_create(name, -1, dma_pool_obj_ctor,
+ dma_pool_obj_dtor, NULL, NULL, dma_pool_obj_import,
+ dma_pool_obj_release, pool, 0);
+
+ pctrie_init(&pool->pool_ptree);
+
+ return (pool);
+}
+
+void
+linux_dma_pool_destroy(struct dma_pool *pool)
+{
+
+ uma_zdestroy(pool->pool_zone);
+ bus_dma_tag_destroy(pool->pool_dmat);
+ kfree(pool);
+}
+
+void *
+linux_dma_pool_alloc(struct dma_pool *pool, gfp_t mem_flags,
+ dma_addr_t *handle)
+{
+ struct linux_dma_obj *obj;
+
+ obj = uma_zalloc_arg(pool->pool_zone, pool, mem_flags);
+ if (obj == NULL)
+ return (NULL);
+
+ if (LINUX_DMA_PCTRIE_INSERT(&pool->pool_ptree, obj) != 0) {
+ uma_zfree_arg(pool->pool_zone, obj, pool);
+ return (NULL);
+ }
+
+ *handle = obj->dma_addr;
+ return (obj->vaddr);
+}
+
+void
+linux_dma_pool_free(struct dma_pool *pool, void *vaddr, dma_addr_t dma_addr)
+{
+ struct linux_dma_obj *obj;
+
+ obj = LINUX_DMA_PCTRIE_LOOKUP(&pool->pool_ptree, dma_addr);
+ if (obj == NULL) {
+ return;
+ }
+ LINUX_DMA_PCTRIE_REMOVE(&pool->pool_ptree, dma_addr);
+
+ uma_zfree_arg(pool->pool_zone, obj, pool);
+}
File Metadata
Details
Attached
Mime Type
text/plain
Expires
Tue, Jan 13, 2:49 PM (11 h, 30 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
27629280
Default Alt Text
D19845.id55962.diff (16 KB)
Attached To
Mode
D19845: to be compatible with an IOMMU LinuxKPI should use bus_dma(9)
Attached
Detach File
Event Timeline
Log In to Comment