Skip to content

Commit

Permalink
Merge tag 'dma-mapping-5.12' of git://git.infradead.org/users/hch/dma…
Browse files Browse the repository at this point in the history
…-mapping

Pull dma-mapping updates from Christoph Hellwig:

 - add support to emulate processing delays in the DMA API benchmark
   selftest (Barry Song)

 - remove support for non-contiguous noncoherent allocations, which
   aren't used and will be replaced by a different API

* tag 'dma-mapping-5.12' of git://git.infradead.org/users/hch/dma-mapping:
  dma-mapping: remove the {alloc,free}_noncoherent methods
  dma-mapping: benchmark: pretend DMA is transmitting
  • Loading branch information
torvalds committed Feb 24, 2021
2 parents b817c93 + 81d88ce commit a4dec04
Show file tree
Hide file tree
Showing 7 changed files with 64 additions and 125 deletions.
64 changes: 22 additions & 42 deletions Documentation/core-api/dma-api.rst
Original file line number Diff line number Diff line change
Expand Up @@ -528,16 +528,14 @@ an I/O device, you should not be using this part of the API.

::

void *
dma_alloc_noncoherent(struct device *dev, size_t size,
dma_addr_t *dma_handle, enum dma_data_direction dir,
gfp_t gfp)
struct page *
dma_alloc_pages(struct device *dev, size_t size, dma_addr_t *dma_handle,
enum dma_data_direction dir, gfp_t gfp)

This routine allocates a region of <size> bytes of consistent memory. It
returns a pointer to the allocated region (in the processor's virtual address
space) or NULL if the allocation failed. The returned memory may or may not
be in the kernel direct mapping. Drivers must not call virt_to_page on
the returned memory region.
This routine allocates a region of <size> bytes of non-coherent memory. It
returns a pointer to first struct page for the region, or NULL if the
allocation failed. The resulting struct page can be used for everything a
struct page is suitable for.

It also returns a <dma_handle> which may be cast to an unsigned integer the
same width as the bus and given to the device as the DMA address base of
Expand All @@ -558,51 +556,33 @@ reused.
::

void
dma_free_noncoherent(struct device *dev, size_t size, void *cpu_addr,
dma_free_pages(struct device *dev, size_t size, struct page *page,
dma_addr_t dma_handle, enum dma_data_direction dir)

Free a region of memory previously allocated using dma_alloc_noncoherent().
dev, size and dma_handle and dir must all be the same as those passed into
dma_alloc_noncoherent(). cpu_addr must be the virtual address returned by
dma_alloc_noncoherent().
Free a region of memory previously allocated using dma_alloc_pages().
dev, size, dma_handle and dir must all be the same as those passed into
dma_alloc_pages(). page must be the pointer returned by dma_alloc_pages().

::

struct page *
dma_alloc_pages(struct device *dev, size_t size, dma_addr_t *dma_handle,
enum dma_data_direction dir, gfp_t gfp)

This routine allocates a region of <size> bytes of non-coherent memory. It
returns a pointer to first struct page for the region, or NULL if the
allocation failed. The resulting struct page can be used for everything a
struct page is suitable for.

It also returns a <dma_handle> which may be cast to an unsigned integer the
same width as the bus and given to the device as the DMA address base of
the region.

The dir parameter specified if data is read and/or written by the device,
see dma_map_single() for details.

The gfp parameter allows the caller to specify the ``GFP_`` flags (see
kmalloc()) for the allocation, but rejects flags used to specify a memory
zone such as GFP_DMA or GFP_HIGHMEM.
void *
dma_alloc_noncoherent(struct device *dev, size_t size,
dma_addr_t *dma_handle, enum dma_data_direction dir,
gfp_t gfp)

Before giving the memory to the device, dma_sync_single_for_device() needs
to be called, and before reading memory written by the device,
dma_sync_single_for_cpu(), just like for streaming DMA mappings that are
reused.
This routine is a convenient wrapper around dma_alloc_pages that returns the
kernel virtual address for the allocated memory instead of the page structure.

::

void
dma_free_pages(struct device *dev, size_t size, struct page *page,
dma_free_noncoherent(struct device *dev, size_t size, void *cpu_addr,
dma_addr_t dma_handle, enum dma_data_direction dir)

Free a region of memory previously allocated using dma_alloc_pages().
dev, size and dma_handle and dir must all be the same as those passed into
dma_alloc_noncoherent(). page must be the pointer returned by
dma_alloc_pages().
Free a region of memory previously allocated using dma_alloc_noncoherent().
dev, size, dma_handle and dir must all be the same as those passed into
dma_alloc_noncoherent(). cpu_addr must be the virtual address returned by
dma_alloc_noncoherent().

::

Expand Down
30 changes: 0 additions & 30 deletions drivers/iommu/dma-iommu.c
Original file line number Diff line number Diff line change
Expand Up @@ -1187,34 +1187,6 @@ static void *iommu_dma_alloc(struct device *dev, size_t size,
return cpu_addr;
}

#ifdef CONFIG_DMA_REMAP
static void *iommu_dma_alloc_noncoherent(struct device *dev, size_t size,
dma_addr_t *handle, enum dma_data_direction dir, gfp_t gfp)
{
if (!gfpflags_allow_blocking(gfp)) {
struct page *page;

page = dma_common_alloc_pages(dev, size, handle, dir, gfp);
if (!page)
return NULL;
return page_address(page);
}

return iommu_dma_alloc_remap(dev, size, handle, gfp | __GFP_ZERO,
PAGE_KERNEL, 0);
}

static void iommu_dma_free_noncoherent(struct device *dev, size_t size,
void *cpu_addr, dma_addr_t handle, enum dma_data_direction dir)
{
__iommu_dma_unmap(dev, handle, size);
__iommu_dma_free(dev, size, cpu_addr);
}
#else
#define iommu_dma_alloc_noncoherent NULL
#define iommu_dma_free_noncoherent NULL
#endif /* CONFIG_DMA_REMAP */

static int iommu_dma_mmap(struct device *dev, struct vm_area_struct *vma,
void *cpu_addr, dma_addr_t dma_addr, size_t size,
unsigned long attrs)
Expand Down Expand Up @@ -1285,8 +1257,6 @@ static const struct dma_map_ops iommu_dma_ops = {
.free = iommu_dma_free,
.alloc_pages = dma_common_alloc_pages,
.free_pages = dma_common_free_pages,
.alloc_noncoherent = iommu_dma_alloc_noncoherent,
.free_noncoherent = iommu_dma_free_noncoherent,
.mmap = iommu_dma_mmap,
.get_sgtable = iommu_dma_get_sgtable,
.map_page = iommu_dma_map_page,
Expand Down
5 changes: 0 additions & 5 deletions include/linux/dma-map-ops.h
Original file line number Diff line number Diff line change
Expand Up @@ -22,11 +22,6 @@ struct dma_map_ops {
gfp_t gfp);
void (*free_pages)(struct device *dev, size_t size, struct page *vaddr,
dma_addr_t dma_handle, enum dma_data_direction dir);
void *(*alloc_noncoherent)(struct device *dev, size_t size,
dma_addr_t *dma_handle, enum dma_data_direction dir,
gfp_t gfp);
void (*free_noncoherent)(struct device *dev, size_t size, void *vaddr,
dma_addr_t dma_handle, enum dma_data_direction dir);
int (*mmap)(struct device *, struct vm_area_struct *,
void *, dma_addr_t, size_t, unsigned long attrs);

Expand Down
17 changes: 13 additions & 4 deletions include/linux/dma-mapping.h
Original file line number Diff line number Diff line change
Expand Up @@ -263,10 +263,19 @@ struct page *dma_alloc_pages(struct device *dev, size_t size,
dma_addr_t *dma_handle, enum dma_data_direction dir, gfp_t gfp);
void dma_free_pages(struct device *dev, size_t size, struct page *page,
dma_addr_t dma_handle, enum dma_data_direction dir);
void *dma_alloc_noncoherent(struct device *dev, size_t size,
dma_addr_t *dma_handle, enum dma_data_direction dir, gfp_t gfp);
void dma_free_noncoherent(struct device *dev, size_t size, void *vaddr,
dma_addr_t dma_handle, enum dma_data_direction dir);

static inline void *dma_alloc_noncoherent(struct device *dev, size_t size,
dma_addr_t *dma_handle, enum dma_data_direction dir, gfp_t gfp)
{
struct page *page = dma_alloc_pages(dev, size, dma_handle, dir, gfp);
return page ? page_address(page) : NULL;
}

static inline void dma_free_noncoherent(struct device *dev, size_t size,
void *vaddr, dma_addr_t dma_handle, enum dma_data_direction dir)
{
dma_free_pages(dev, size, virt_to_page(vaddr), dma_handle, dir);
}

static inline dma_addr_t dma_map_single_attrs(struct device *dev, void *ptr,
size_t size, enum dma_data_direction dir, unsigned long attrs)
Expand Down
12 changes: 11 additions & 1 deletion kernel/dma/map_benchmark.c
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
#define DMA_MAP_BENCHMARK _IOWR('d', 1, struct map_benchmark)
#define DMA_MAP_MAX_THREADS 1024
#define DMA_MAP_MAX_SECONDS 300
#define DMA_MAP_MAX_TRANS_DELAY (10 * NSEC_PER_MSEC)

#define DMA_MAP_BIDIRECTIONAL 0
#define DMA_MAP_TO_DEVICE 1
Expand All @@ -36,7 +37,8 @@ struct map_benchmark {
__s32 node; /* which numa node this benchmark will run on */
__u32 dma_bits; /* DMA addressing capability */
__u32 dma_dir; /* DMA data direction */
__u8 expansion[84]; /* For future use */
__u32 dma_trans_ns; /* time for DMA transmission in ns */
__u8 expansion[80]; /* For future use */
};

struct map_benchmark_data {
Expand Down Expand Up @@ -87,6 +89,9 @@ static int map_benchmark_thread(void *data)
map_etime = ktime_get();
map_delta = ktime_sub(map_etime, map_stime);

/* Pretend DMA is transmitting */
ndelay(map->bparam.dma_trans_ns);

unmap_stime = ktime_get();
dma_unmap_single(map->dev, dma_addr, PAGE_SIZE, map->dir);
unmap_etime = ktime_get();
Expand Down Expand Up @@ -218,6 +223,11 @@ static long map_benchmark_ioctl(struct file *file, unsigned int cmd,
return -EINVAL;
}

if (map->bparam.dma_trans_ns > DMA_MAP_MAX_TRANS_DELAY) {
pr_err("invalid transmission delay\n");
return -EINVAL;
}

if (map->bparam.node != NUMA_NO_NODE &&
!node_possible(map->bparam.node)) {
pr_err("invalid numa node\n");
Expand Down
40 changes: 0 additions & 40 deletions kernel/dma/mapping.c
Original file line number Diff line number Diff line change
Expand Up @@ -517,46 +517,6 @@ void dma_free_pages(struct device *dev, size_t size, struct page *page,
}
EXPORT_SYMBOL_GPL(dma_free_pages);

void *dma_alloc_noncoherent(struct device *dev, size_t size,
dma_addr_t *dma_handle, enum dma_data_direction dir, gfp_t gfp)
{
const struct dma_map_ops *ops = get_dma_ops(dev);
void *vaddr;

if (!ops || !ops->alloc_noncoherent) {
struct page *page;

page = dma_alloc_pages(dev, size, dma_handle, dir, gfp);
if (!page)
return NULL;
return page_address(page);
}

size = PAGE_ALIGN(size);
vaddr = ops->alloc_noncoherent(dev, size, dma_handle, dir, gfp);
if (vaddr)
debug_dma_map_page(dev, virt_to_page(vaddr), 0, size, dir,
*dma_handle);
return vaddr;
}
EXPORT_SYMBOL_GPL(dma_alloc_noncoherent);

void dma_free_noncoherent(struct device *dev, size_t size, void *vaddr,
dma_addr_t dma_handle, enum dma_data_direction dir)
{
const struct dma_map_ops *ops = get_dma_ops(dev);

if (!ops || !ops->free_noncoherent) {
dma_free_pages(dev, size, virt_to_page(vaddr), dma_handle, dir);
return;
}

size = PAGE_ALIGN(size);
debug_dma_unmap_page(dev, dma_handle, size, dir);
ops->free_noncoherent(dev, size, vaddr, dma_handle, dir);
}
EXPORT_SYMBOL_GPL(dma_free_noncoherent);

int dma_supported(struct device *dev, u64 mask)
{
const struct dma_map_ops *ops = get_dma_ops(dev);
Expand Down
21 changes: 18 additions & 3 deletions tools/testing/selftests/dma/dma_map_benchmark.c
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,12 @@
#include <sys/mman.h>
#include <linux/types.h>

#define NSEC_PER_MSEC 1000000L

#define DMA_MAP_BENCHMARK _IOWR('d', 1, struct map_benchmark)
#define DMA_MAP_MAX_THREADS 1024
#define DMA_MAP_MAX_SECONDS 300
#define DMA_MAP_MAX_TRANS_DELAY (10 * NSEC_PER_MSEC)

#define DMA_MAP_BIDIRECTIONAL 0
#define DMA_MAP_TO_DEVICE 1
Expand All @@ -36,7 +39,8 @@ struct map_benchmark {
__s32 node; /* which numa node this benchmark will run on */
__u32 dma_bits; /* DMA addressing capability */
__u32 dma_dir; /* DMA data direction */
__u8 expansion[84]; /* For future use */
__u32 dma_trans_ns; /* time for DMA transmission in ns */
__u8 expansion[80]; /* For future use */
};

int main(int argc, char **argv)
Expand All @@ -46,12 +50,12 @@ int main(int argc, char **argv)
/* default single thread, run 20 seconds on NUMA_NO_NODE */
int threads = 1, seconds = 20, node = -1;
/* default dma mask 32bit, bidirectional DMA */
int bits = 32, dir = DMA_MAP_BIDIRECTIONAL;
int bits = 32, xdelay = 0, dir = DMA_MAP_BIDIRECTIONAL;

int cmd = DMA_MAP_BENCHMARK;
char *p;

while ((opt = getopt(argc, argv, "t:s:n:b:d:")) != -1) {
while ((opt = getopt(argc, argv, "t:s:n:b:d:x:")) != -1) {
switch (opt) {
case 't':
threads = atoi(optarg);
Expand All @@ -68,6 +72,9 @@ int main(int argc, char **argv)
case 'd':
dir = atoi(optarg);
break;
case 'x':
xdelay = atoi(optarg);
break;
default:
return -1;
}
Expand All @@ -85,6 +92,12 @@ int main(int argc, char **argv)
exit(1);
}

if (xdelay < 0 || xdelay > DMA_MAP_MAX_TRANS_DELAY) {
fprintf(stderr, "invalid transmit delay, must be in 0-%ld\n",
DMA_MAP_MAX_TRANS_DELAY);
exit(1);
}

/* suppose the mininum DMA zone is 1MB in the world */
if (bits < 20 || bits > 64) {
fprintf(stderr, "invalid dma mask bit, must be in 20-64\n");
Expand All @@ -109,6 +122,8 @@ int main(int argc, char **argv)
map.node = node;
map.dma_bits = bits;
map.dma_dir = dir;
map.dma_trans_ns = xdelay;

if (ioctl(fd, cmd, &map)) {
perror("ioctl");
exit(1);
Expand Down

0 comments on commit a4dec04

Please sign in to comment.