Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

Merge tag 'dma-mapping-5.11' of git://git.infradead.org/users/hch/dma-mapping

Pull dma-mapping updates from Christoph Hellwig:

- support for a partial IOMMU bypass (Alexey Kardashevskiy)

- add a DMA API benchmark (Barry Song)

- misc fixes (Tiezhu Yang, tangjianqiang)

* tag 'dma-mapping-5.11' of git://git.infradead.org/users/hch/dma-mapping:
selftests/dma: add test application for DMA_MAP_BENCHMARK
dma-mapping: add benchmark support for streaming DMA APIs
dma-contiguous: fix a typo error in a comment
dma-pool: no need to check return value of debugfs_create functions
powerpc/dma: Fallback to dma_ops when persistent memory present
dma-mapping: Allow mixing bypass and mapped DMA operation

+645 -20
+6
MAINTAINERS
··· 5297 5297 F: include/linux/dma-map-ops.h 5298 5298 F: kernel/dma/ 5299 5299 5300 + DMA MAPPING BENCHMARK 5301 + M: Barry Song <song.bao.hua@hisilicon.com> 5302 + L: iommu@lists.linux-foundation.org 5303 + F: kernel/dma/map_benchmark.c 5304 + F: tools/testing/selftests/dma/ 5305 + 5300 5306 DMA-BUF HEAPS FRAMEWORK 5301 5307 M: Sumit Semwal <sumit.semwal@linaro.org> 5302 5308 R: Benjamin Gaignard <benjamin.gaignard@linaro.org>
+1
arch/powerpc/Kconfig
··· 161 161 select DCACHE_WORD_ACCESS if PPC64 && CPU_LITTLE_ENDIAN 162 162 select DMA_OPS if PPC64 163 163 select DMA_OPS_BYPASS if PPC64 164 + select ARCH_HAS_DMA_MAP_DIRECT if PPC64 && PPC_PSERIES 164 165 select DYNAMIC_FTRACE if FUNCTION_TRACER 165 166 select EDAC_ATOMIC_SCRUB 166 167 select EDAC_SUPPORT
+69 -2
arch/powerpc/kernel/dma-iommu.c
··· 10 10 #include <linux/pci.h> 11 11 #include <asm/iommu.h> 12 12 13 + #ifdef CONFIG_ARCH_HAS_DMA_MAP_DIRECT 14 + #define can_map_direct(dev, addr) \ 15 + ((dev)->bus_dma_limit >= phys_to_dma((dev), (addr))) 16 + 17 + bool arch_dma_map_page_direct(struct device *dev, phys_addr_t addr) 18 + { 19 + if (likely(!dev->bus_dma_limit)) 20 + return false; 21 + 22 + return can_map_direct(dev, addr); 23 + } 24 + 25 + #define is_direct_handle(dev, h) ((h) >= (dev)->archdata.dma_offset) 26 + 27 + bool arch_dma_unmap_page_direct(struct device *dev, dma_addr_t dma_handle) 28 + { 29 + if (likely(!dev->bus_dma_limit)) 30 + return false; 31 + 32 + return is_direct_handle(dev, dma_handle); 33 + } 34 + 35 + bool arch_dma_map_sg_direct(struct device *dev, struct scatterlist *sg, 36 + int nents) 37 + { 38 + struct scatterlist *s; 39 + int i; 40 + 41 + if (likely(!dev->bus_dma_limit)) 42 + return false; 43 + 44 + for_each_sg(sg, s, nents, i) { 45 + if (!can_map_direct(dev, sg_phys(s) + s->offset + s->length)) 46 + return false; 47 + } 48 + 49 + return true; 50 + } 51 + 52 + bool arch_dma_unmap_sg_direct(struct device *dev, struct scatterlist *sg, 53 + int nents) 54 + { 55 + struct scatterlist *s; 56 + int i; 57 + 58 + if (likely(!dev->bus_dma_limit)) 59 + return false; 60 + 61 + for_each_sg(sg, s, nents, i) { 62 + if (!is_direct_handle(dev, s->dma_address + s->length)) 63 + return false; 64 + } 65 + 66 + return true; 67 + } 68 + #endif /* CONFIG_ARCH_HAS_DMA_MAP_DIRECT */ 69 + 13 70 /* 14 71 * Generic iommu implementation 15 72 */ ··· 147 90 struct iommu_table *tbl = get_iommu_table_base(dev); 148 91 149 92 if (dev_is_pci(dev) && dma_iommu_bypass_supported(dev, mask)) { 150 - dev->dma_ops_bypass = true; 151 - dev_dbg(dev, "iommu: 64-bit OK, using fixed ops\n"); 93 + /* 94 + * dma_iommu_bypass_supported() sets dma_max when there is 95 + * 1:1 mapping but it is somehow limited. 96 + * ibm,pmemory is one example. 97 + */ 98 + dev->dma_ops_bypass = dev->bus_dma_limit == 0; 99 + if (!dev->dma_ops_bypass) 100 + dev_warn(dev, 101 + "iommu: 64-bit OK but direct DMA is limited by %llx\n", 102 + dev->bus_dma_limit); 103 + else 104 + dev_dbg(dev, "iommu: 64-bit OK, using fixed ops\n"); 152 105 return 1; 153 106 } 154 107
+41 -10
arch/powerpc/platforms/pseries/iommu.c
··· 839 839 np, ret); 840 840 } 841 841 842 - static u64 find_existing_ddw(struct device_node *pdn) 842 + static u64 find_existing_ddw(struct device_node *pdn, int *window_shift) 843 843 { 844 844 struct direct_window *window; 845 845 const struct dynamic_dma_window_prop *direct64; ··· 851 851 if (window->device == pdn) { 852 852 direct64 = window->prop; 853 853 dma_addr = be64_to_cpu(direct64->dma_base); 854 + *window_shift = be32_to_cpu(direct64->window_shift); 854 855 break; 855 856 } 856 857 } ··· 1112 1111 */ 1113 1112 static u64 enable_ddw(struct pci_dev *dev, struct device_node *pdn) 1114 1113 { 1115 - int len, ret; 1114 + int len = 0, ret; 1115 + int max_ram_len = order_base_2(ddw_memory_hotplug_max()); 1116 1116 struct ddw_query_response query; 1117 1117 struct ddw_create_response create; 1118 1118 int page_shift; 1119 - u64 dma_addr, max_addr; 1119 + u64 dma_addr; 1120 1120 struct device_node *dn; 1121 1121 u32 ddw_avail[DDW_APPLICABLE_SIZE]; 1122 1122 struct direct_window *window; ··· 1125 1123 struct dynamic_dma_window_prop *ddwprop; 1126 1124 struct failed_ddw_pdn *fpdn; 1127 1125 bool default_win_removed = false; 1126 + bool pmem_present; 1127 + 1128 + dn = of_find_node_by_type(NULL, "ibm,pmemory"); 1129 + pmem_present = dn != NULL; 1130 + of_node_put(dn); 1128 1131 1129 1132 mutex_lock(&direct_window_init_mutex); 1130 1133 1131 - dma_addr = find_existing_ddw(pdn); 1134 + dma_addr = find_existing_ddw(pdn, &len); 1132 1135 if (dma_addr != 0) 1133 1136 goto out_unlock; 1134 1137 ··· 1219 1212 } 1220 1213 /* verify the window * number of ptes will map the partition */ 1221 1214 /* check largest block * page size > max memory hotplug addr */ 1222 - max_addr = ddw_memory_hotplug_max(); 1223 - if (query.largest_available_block < (max_addr >> page_shift)) { 1224 - dev_dbg(&dev->dev, "can't map partition max 0x%llx with %llu " 1225 - "%llu-sized pages\n", max_addr, query.largest_available_block, 1226 - 1ULL << page_shift); 1215 + /* 1216 + * The "ibm,pmemory" can appear anywhere in the address space. 1217 + * Assuming it is still backed by page structs, try MAX_PHYSMEM_BITS 1218 + * for the upper limit and fallback to max RAM otherwise but this 1219 + * disables device::dma_ops_bypass. 1220 + */ 1221 + len = max_ram_len; 1222 + if (pmem_present) { 1223 + if (query.largest_available_block >= 1224 + (1ULL << (MAX_PHYSMEM_BITS - page_shift))) 1225 + len = MAX_PHYSMEM_BITS - page_shift; 1226 + else 1227 + dev_info(&dev->dev, "Skipping ibm,pmemory"); 1228 + } 1229 + 1230 + if (query.largest_available_block < (1ULL << (len - page_shift))) { 1231 + dev_dbg(&dev->dev, 1232 + "can't map partition max 0x%llx with %llu %llu-sized pages\n", 1233 + 1ULL << len, 1234 + query.largest_available_block, 1235 + 1ULL << page_shift); 1227 1236 goto out_failed; 1228 1237 } 1229 - len = order_base_2(max_addr); 1230 1238 win64 = kzalloc(sizeof(struct property), GFP_KERNEL); 1231 1239 if (!win64) { 1232 1240 dev_info(&dev->dev, ··· 1321 1299 1322 1300 out_unlock: 1323 1301 mutex_unlock(&direct_window_init_mutex); 1302 + 1303 + /* 1304 + * If we have persistent memory and the window size is only as big 1305 + * as RAM, then we failed to create a window to cover persistent 1306 + * memory and need to set the DMA limit. 1307 + */ 1308 + if (pmem_present && dma_addr && (len == max_ram_len)) 1309 + dev->dev.bus_dma_limit = dma_addr + (1ULL << len); 1310 + 1324 1311 return dma_addr; 1325 1312 } 1326 1313
+14
include/linux/dma-map-ops.h
··· 317 317 void *arch_dma_set_uncached(void *addr, size_t size); 318 318 void arch_dma_clear_uncached(void *addr, size_t size); 319 319 320 + #ifdef CONFIG_ARCH_HAS_DMA_MAP_DIRECT 321 + bool arch_dma_map_page_direct(struct device *dev, phys_addr_t addr); 322 + bool arch_dma_unmap_page_direct(struct device *dev, dma_addr_t dma_handle); 323 + bool arch_dma_map_sg_direct(struct device *dev, struct scatterlist *sg, 324 + int nents); 325 + bool arch_dma_unmap_sg_direct(struct device *dev, struct scatterlist *sg, 326 + int nents); 327 + #else 328 + #define arch_dma_map_page_direct(d, a) (false) 329 + #define arch_dma_unmap_page_direct(d, a) (false) 330 + #define arch_dma_map_sg_direct(d, s, n) (false) 331 + #define arch_dma_unmap_sg_direct(d, s, n) (false) 332 + #endif 333 + 320 334 #ifdef CONFIG_ARCH_HAS_SETUP_DMA_OPS 321 335 void arch_setup_dma_ops(struct device *dev, u64 dma_base, u64 size, 322 336 const struct iommu_ops *iommu, bool coherent);
+13
kernel/dma/Kconfig
··· 20 20 config DMA_OPS_BYPASS 21 21 bool 22 22 23 + # Lets platform IOMMU driver choose between bypass and IOMMU 24 + config ARCH_HAS_DMA_MAP_DIRECT 25 + bool 26 + 23 27 config NEED_SG_DMA_LENGTH 24 28 bool 25 29 ··· 224 220 is technically out-of-spec. 225 221 226 222 If unsure, say N. 223 + 224 + config DMA_MAP_BENCHMARK 225 + bool "Enable benchmarking of streaming DMA mapping" 226 + depends on DEBUG_FS 227 + help 228 + Provides /sys/kernel/debug/dma_map_benchmark that helps with testing 229 + performance of dma_(un)map_page. 230 + 231 + See tools/testing/selftests/dma/dma_map_benchmark.c
+1
kernel/dma/Makefile
··· 9 9 obj-$(CONFIG_SWIOTLB) += swiotlb.o 10 10 obj-$(CONFIG_DMA_COHERENT_POOL) += pool.o 11 11 obj-$(CONFIG_DMA_REMAP) += remap.o 12 + obj-$(CONFIG_DMA_MAP_BENCHMARK) += map_benchmark.o
+1 -1
kernel/dma/contiguous.c
··· 20 20 * coders, etc. 21 21 * 22 22 * Such devices often require big memory buffers (a full HD frame 23 - * is, for instance, more then 2 mega pixels large, i.e. more than 6 23 + * is, for instance, more than 2 mega pixels large, i.e. more than 6 24 24 * MB of memory), which makes mechanisms such as kmalloc() or 25 25 * alloc_page() ineffective. 26 26 *
+361
kernel/dma/map_benchmark.c
··· 1 + // SPDX-License-Identifier: GPL-2.0-only 2 + /* 3 + * Copyright (C) 2020 Hisilicon Limited. 4 + */ 5 + 6 + #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt 7 + 8 + #include <linux/debugfs.h> 9 + #include <linux/delay.h> 10 + #include <linux/device.h> 11 + #include <linux/dma-mapping.h> 12 + #include <linux/kernel.h> 13 + #include <linux/kthread.h> 14 + #include <linux/math64.h> 15 + #include <linux/module.h> 16 + #include <linux/pci.h> 17 + #include <linux/platform_device.h> 18 + #include <linux/slab.h> 19 + #include <linux/timekeeping.h> 20 + 21 + #define DMA_MAP_BENCHMARK _IOWR('d', 1, struct map_benchmark) 22 + #define DMA_MAP_MAX_THREADS 1024 23 + #define DMA_MAP_MAX_SECONDS 300 24 + 25 + #define DMA_MAP_BIDIRECTIONAL 0 26 + #define DMA_MAP_TO_DEVICE 1 27 + #define DMA_MAP_FROM_DEVICE 2 28 + 29 + struct map_benchmark { 30 + __u64 avg_map_100ns; /* average map latency in 100ns */ 31 + __u64 map_stddev; /* standard deviation of map latency */ 32 + __u64 avg_unmap_100ns; /* as above */ 33 + __u64 unmap_stddev; 34 + __u32 threads; /* how many threads will do map/unmap in parallel */ 35 + __u32 seconds; /* how long the test will last */ 36 + __s32 node; /* which numa node this benchmark will run on */ 37 + __u32 dma_bits; /* DMA addressing capability */ 38 + __u32 dma_dir; /* DMA data direction */ 39 + __u64 expansion[10]; /* For future use */ 40 + }; 41 + 42 + struct map_benchmark_data { 43 + struct map_benchmark bparam; 44 + struct device *dev; 45 + struct dentry *debugfs; 46 + enum dma_data_direction dir; 47 + atomic64_t sum_map_100ns; 48 + atomic64_t sum_unmap_100ns; 49 + atomic64_t sum_sq_map; 50 + atomic64_t sum_sq_unmap; 51 + atomic64_t loops; 52 + }; 53 + 54 + static int map_benchmark_thread(void *data) 55 + { 56 + void *buf; 57 + dma_addr_t dma_addr; 58 + struct map_benchmark_data *map = data; 59 + int ret = 0; 60 + 61 + buf = (void *)__get_free_page(GFP_KERNEL); 62 + if (!buf) 63 + return -ENOMEM; 64 + 65 + while (!kthread_should_stop()) { 66 + u64 map_100ns, unmap_100ns, map_sq, unmap_sq; 67 + ktime_t map_stime, map_etime, unmap_stime, unmap_etime; 68 + ktime_t map_delta, unmap_delta; 69 + 70 + /* 71 + * for a non-coherent device, if we don't stain them in the 72 + * cache, this will give an underestimate of the real-world 73 + * overhead of BIDIRECTIONAL or TO_DEVICE mappings; 74 + * 66 means evertything goes well! 66 is lucky. 75 + */ 76 + if (map->dir != DMA_FROM_DEVICE) 77 + memset(buf, 0x66, PAGE_SIZE); 78 + 79 + map_stime = ktime_get(); 80 + dma_addr = dma_map_single(map->dev, buf, PAGE_SIZE, map->dir); 81 + if (unlikely(dma_mapping_error(map->dev, dma_addr))) { 82 + pr_err("dma_map_single failed on %s\n", 83 + dev_name(map->dev)); 84 + ret = -ENOMEM; 85 + goto out; 86 + } 87 + map_etime = ktime_get(); 88 + map_delta = ktime_sub(map_etime, map_stime); 89 + 90 + unmap_stime = ktime_get(); 91 + dma_unmap_single(map->dev, dma_addr, PAGE_SIZE, map->dir); 92 + unmap_etime = ktime_get(); 93 + unmap_delta = ktime_sub(unmap_etime, unmap_stime); 94 + 95 + /* calculate sum and sum of squares */ 96 + 97 + map_100ns = div64_ul(map_delta, 100); 98 + unmap_100ns = div64_ul(unmap_delta, 100); 99 + map_sq = map_100ns * map_100ns; 100 + unmap_sq = unmap_100ns * unmap_100ns; 101 + 102 + atomic64_add(map_100ns, &map->sum_map_100ns); 103 + atomic64_add(unmap_100ns, &map->sum_unmap_100ns); 104 + atomic64_add(map_sq, &map->sum_sq_map); 105 + atomic64_add(unmap_sq, &map->sum_sq_unmap); 106 + atomic64_inc(&map->loops); 107 + } 108 + 109 + out: 110 + free_page((unsigned long)buf); 111 + return ret; 112 + } 113 + 114 + static int do_map_benchmark(struct map_benchmark_data *map) 115 + { 116 + struct task_struct **tsk; 117 + int threads = map->bparam.threads; 118 + int node = map->bparam.node; 119 + const cpumask_t *cpu_mask = cpumask_of_node(node); 120 + u64 loops; 121 + int ret = 0; 122 + int i; 123 + 124 + tsk = kmalloc_array(threads, sizeof(*tsk), GFP_KERNEL); 125 + if (!tsk) 126 + return -ENOMEM; 127 + 128 + get_device(map->dev); 129 + 130 + for (i = 0; i < threads; i++) { 131 + tsk[i] = kthread_create_on_node(map_benchmark_thread, map, 132 + map->bparam.node, "dma-map-benchmark/%d", i); 133 + if (IS_ERR(tsk[i])) { 134 + pr_err("create dma_map thread failed\n"); 135 + ret = PTR_ERR(tsk[i]); 136 + goto out; 137 + } 138 + 139 + if (node != NUMA_NO_NODE) 140 + kthread_bind_mask(tsk[i], cpu_mask); 141 + } 142 + 143 + /* clear the old value in the previous benchmark */ 144 + atomic64_set(&map->sum_map_100ns, 0); 145 + atomic64_set(&map->sum_unmap_100ns, 0); 146 + atomic64_set(&map->sum_sq_map, 0); 147 + atomic64_set(&map->sum_sq_unmap, 0); 148 + atomic64_set(&map->loops, 0); 149 + 150 + for (i = 0; i < threads; i++) 151 + wake_up_process(tsk[i]); 152 + 153 + msleep_interruptible(map->bparam.seconds * 1000); 154 + 155 + /* wait for the completion of benchmark threads */ 156 + for (i = 0; i < threads; i++) { 157 + ret = kthread_stop(tsk[i]); 158 + if (ret) 159 + goto out; 160 + } 161 + 162 + loops = atomic64_read(&map->loops); 163 + if (likely(loops > 0)) { 164 + u64 map_variance, unmap_variance; 165 + u64 sum_map = atomic64_read(&map->sum_map_100ns); 166 + u64 sum_unmap = atomic64_read(&map->sum_unmap_100ns); 167 + u64 sum_sq_map = atomic64_read(&map->sum_sq_map); 168 + u64 sum_sq_unmap = atomic64_read(&map->sum_sq_unmap); 169 + 170 + /* average latency */ 171 + map->bparam.avg_map_100ns = div64_u64(sum_map, loops); 172 + map->bparam.avg_unmap_100ns = div64_u64(sum_unmap, loops); 173 + 174 + /* standard deviation of latency */ 175 + map_variance = div64_u64(sum_sq_map, loops) - 176 + map->bparam.avg_map_100ns * 177 + map->bparam.avg_map_100ns; 178 + unmap_variance = div64_u64(sum_sq_unmap, loops) - 179 + map->bparam.avg_unmap_100ns * 180 + map->bparam.avg_unmap_100ns; 181 + map->bparam.map_stddev = int_sqrt64(map_variance); 182 + map->bparam.unmap_stddev = int_sqrt64(unmap_variance); 183 + } 184 + 185 + out: 186 + put_device(map->dev); 187 + kfree(tsk); 188 + return ret; 189 + } 190 + 191 + static long map_benchmark_ioctl(struct file *file, unsigned int cmd, 192 + unsigned long arg) 193 + { 194 + struct map_benchmark_data *map = file->private_data; 195 + void __user *argp = (void __user *)arg; 196 + u64 old_dma_mask; 197 + 198 + int ret; 199 + 200 + if (copy_from_user(&map->bparam, argp, sizeof(map->bparam))) 201 + return -EFAULT; 202 + 203 + switch (cmd) { 204 + case DMA_MAP_BENCHMARK: 205 + if (map->bparam.threads == 0 || 206 + map->bparam.threads > DMA_MAP_MAX_THREADS) { 207 + pr_err("invalid thread number\n"); 208 + return -EINVAL; 209 + } 210 + 211 + if (map->bparam.seconds == 0 || 212 + map->bparam.seconds > DMA_MAP_MAX_SECONDS) { 213 + pr_err("invalid duration seconds\n"); 214 + return -EINVAL; 215 + } 216 + 217 + if (map->bparam.node != NUMA_NO_NODE && 218 + !node_possible(map->bparam.node)) { 219 + pr_err("invalid numa node\n"); 220 + return -EINVAL; 221 + } 222 + 223 + switch (map->bparam.dma_dir) { 224 + case DMA_MAP_BIDIRECTIONAL: 225 + map->dir = DMA_BIDIRECTIONAL; 226 + break; 227 + case DMA_MAP_FROM_DEVICE: 228 + map->dir = DMA_FROM_DEVICE; 229 + break; 230 + case DMA_MAP_TO_DEVICE: 231 + map->dir = DMA_TO_DEVICE; 232 + break; 233 + default: 234 + pr_err("invalid DMA direction\n"); 235 + return -EINVAL; 236 + } 237 + 238 + old_dma_mask = dma_get_mask(map->dev); 239 + 240 + ret = dma_set_mask(map->dev, 241 + DMA_BIT_MASK(map->bparam.dma_bits)); 242 + if (ret) { 243 + pr_err("failed to set dma_mask on device %s\n", 244 + dev_name(map->dev)); 245 + return -EINVAL; 246 + } 247 + 248 + ret = do_map_benchmark(map); 249 + 250 + /* 251 + * restore the original dma_mask as many devices' dma_mask are 252 + * set by architectures, acpi, busses. When we bind them back 253 + * to their original drivers, those drivers shouldn't see 254 + * dma_mask changed by benchmark 255 + */ 256 + dma_set_mask(map->dev, old_dma_mask); 257 + break; 258 + default: 259 + return -EINVAL; 260 + } 261 + 262 + if (copy_to_user(argp, &map->bparam, sizeof(map->bparam))) 263 + return -EFAULT; 264 + 265 + return ret; 266 + } 267 + 268 + static const struct file_operations map_benchmark_fops = { 269 + .open = simple_open, 270 + .unlocked_ioctl = map_benchmark_ioctl, 271 + }; 272 + 273 + static void map_benchmark_remove_debugfs(void *data) 274 + { 275 + struct map_benchmark_data *map = (struct map_benchmark_data *)data; 276 + 277 + debugfs_remove(map->debugfs); 278 + } 279 + 280 + static int __map_benchmark_probe(struct device *dev) 281 + { 282 + struct dentry *entry; 283 + struct map_benchmark_data *map; 284 + int ret; 285 + 286 + map = devm_kzalloc(dev, sizeof(*map), GFP_KERNEL); 287 + if (!map) 288 + return -ENOMEM; 289 + map->dev = dev; 290 + 291 + ret = devm_add_action(dev, map_benchmark_remove_debugfs, map); 292 + if (ret) { 293 + pr_err("Can't add debugfs remove action\n"); 294 + return ret; 295 + } 296 + 297 + /* 298 + * we only permit a device bound with this driver, 2nd probe 299 + * will fail 300 + */ 301 + entry = debugfs_create_file("dma_map_benchmark", 0600, NULL, map, 302 + &map_benchmark_fops); 303 + if (IS_ERR(entry)) 304 + return PTR_ERR(entry); 305 + map->debugfs = entry; 306 + 307 + return 0; 308 + } 309 + 310 + static int map_benchmark_platform_probe(struct platform_device *pdev) 311 + { 312 + return __map_benchmark_probe(&pdev->dev); 313 + } 314 + 315 + static struct platform_driver map_benchmark_platform_driver = { 316 + .driver = { 317 + .name = "dma_map_benchmark", 318 + }, 319 + .probe = map_benchmark_platform_probe, 320 + }; 321 + 322 + static int 323 + map_benchmark_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id) 324 + { 325 + return __map_benchmark_probe(&pdev->dev); 326 + } 327 + 328 + static struct pci_driver map_benchmark_pci_driver = { 329 + .name = "dma_map_benchmark", 330 + .probe = map_benchmark_pci_probe, 331 + }; 332 + 333 + static int __init map_benchmark_init(void) 334 + { 335 + int ret; 336 + 337 + ret = pci_register_driver(&map_benchmark_pci_driver); 338 + if (ret) 339 + return ret; 340 + 341 + ret = platform_driver_register(&map_benchmark_platform_driver); 342 + if (ret) { 343 + pci_unregister_driver(&map_benchmark_pci_driver); 344 + return ret; 345 + } 346 + 347 + return 0; 348 + } 349 + 350 + static void __exit map_benchmark_cleanup(void) 351 + { 352 + platform_driver_unregister(&map_benchmark_platform_driver); 353 + pci_unregister_driver(&map_benchmark_pci_driver); 354 + } 355 + 356 + module_init(map_benchmark_init); 357 + module_exit(map_benchmark_cleanup); 358 + 359 + MODULE_AUTHOR("Barry Song <song.bao.hua@hisilicon.com>"); 360 + MODULE_DESCRIPTION("dma_map benchmark driver"); 361 + MODULE_LICENSE("GPL");
+8 -4
kernel/dma/mapping.c
··· 149 149 if (WARN_ON_ONCE(!dev->dma_mask)) 150 150 return DMA_MAPPING_ERROR; 151 151 152 - if (dma_map_direct(dev, ops)) 152 + if (dma_map_direct(dev, ops) || 153 + arch_dma_map_page_direct(dev, page_to_phys(page) + offset + size)) 153 154 addr = dma_direct_map_page(dev, page, offset, size, dir, attrs); 154 155 else 155 156 addr = ops->map_page(dev, page, offset, size, dir, attrs); ··· 166 165 const struct dma_map_ops *ops = get_dma_ops(dev); 167 166 168 167 BUG_ON(!valid_dma_direction(dir)); 169 - if (dma_map_direct(dev, ops)) 168 + if (dma_map_direct(dev, ops) || 169 + arch_dma_unmap_page_direct(dev, addr + size)) 170 170 dma_direct_unmap_page(dev, addr, size, dir, attrs); 171 171 else if (ops->unmap_page) 172 172 ops->unmap_page(dev, addr, size, dir, attrs); ··· 190 188 if (WARN_ON_ONCE(!dev->dma_mask)) 191 189 return 0; 192 190 193 - if (dma_map_direct(dev, ops)) 191 + if (dma_map_direct(dev, ops) || 192 + arch_dma_map_sg_direct(dev, sg, nents)) 194 193 ents = dma_direct_map_sg(dev, sg, nents, dir, attrs); 195 194 else 196 195 ents = ops->map_sg(dev, sg, nents, dir, attrs); ··· 210 207 211 208 BUG_ON(!valid_dma_direction(dir)); 212 209 debug_dma_unmap_sg(dev, sg, nents, dir); 213 - if (dma_map_direct(dev, ops)) 210 + if (dma_map_direct(dev, ops) || 211 + arch_dma_unmap_sg_direct(dev, sg, nents)) 214 212 dma_direct_unmap_sg(dev, sg, nents, dir, attrs); 215 213 else if (ops->unmap_sg) 216 214 ops->unmap_sg(dev, sg, nents, dir, attrs);
-3
kernel/dma/pool.c
··· 38 38 struct dentry *root; 39 39 40 40 root = debugfs_create_dir("dma_pools", NULL); 41 - if (IS_ERR_OR_NULL(root)) 42 - return; 43 - 44 41 debugfs_create_ulong("pool_size_dma", 0400, root, &pool_size_dma); 45 42 debugfs_create_ulong("pool_size_dma32", 0400, root, &pool_size_dma32); 46 43 debugfs_create_ulong("pool_size_kernel", 0400, root, &pool_size_kernel);
+6
tools/testing/selftests/dma/Makefile
··· 1 + # SPDX-License-Identifier: GPL-2.0 2 + CFLAGS += -I../../../../usr/include/ 3 + 4 + TEST_GEN_PROGS := dma_map_benchmark 5 + 6 + include ../lib.mk
+1
tools/testing/selftests/dma/config
··· 1 + CONFIG_DMA_MAP_BENCHMARK=y
+123
tools/testing/selftests/dma/dma_map_benchmark.c
··· 1 + // SPDX-License-Identifier: GPL-2.0-only 2 + /* 3 + * Copyright (C) 2020 Hisilicon Limited. 4 + */ 5 + 6 + #include <fcntl.h> 7 + #include <stdio.h> 8 + #include <stdlib.h> 9 + #include <unistd.h> 10 + #include <sys/ioctl.h> 11 + #include <sys/mman.h> 12 + #include <linux/types.h> 13 + 14 + #define DMA_MAP_BENCHMARK _IOWR('d', 1, struct map_benchmark) 15 + #define DMA_MAP_MAX_THREADS 1024 16 + #define DMA_MAP_MAX_SECONDS 300 17 + 18 + #define DMA_MAP_BIDIRECTIONAL 0 19 + #define DMA_MAP_TO_DEVICE 1 20 + #define DMA_MAP_FROM_DEVICE 2 21 + 22 + static char *directions[] = { 23 + "BIDIRECTIONAL", 24 + "TO_DEVICE", 25 + "FROM_DEVICE", 26 + }; 27 + 28 + struct map_benchmark { 29 + __u64 avg_map_100ns; /* average map latency in 100ns */ 30 + __u64 map_stddev; /* standard deviation of map latency */ 31 + __u64 avg_unmap_100ns; /* as above */ 32 + __u64 unmap_stddev; 33 + __u32 threads; /* how many threads will do map/unmap in parallel */ 34 + __u32 seconds; /* how long the test will last */ 35 + __s32 node; /* which numa node this benchmark will run on */ 36 + __u32 dma_bits; /* DMA addressing capability */ 37 + __u32 dma_dir; /* DMA data direction */ 38 + __u64 expansion[10]; /* For future use */ 39 + }; 40 + 41 + int main(int argc, char **argv) 42 + { 43 + struct map_benchmark map; 44 + int fd, opt; 45 + /* default single thread, run 20 seconds on NUMA_NO_NODE */ 46 + int threads = 1, seconds = 20, node = -1; 47 + /* default dma mask 32bit, bidirectional DMA */ 48 + int bits = 32, dir = DMA_MAP_BIDIRECTIONAL; 49 + 50 + int cmd = DMA_MAP_BENCHMARK; 51 + char *p; 52 + 53 + while ((opt = getopt(argc, argv, "t:s:n:b:d:")) != -1) { 54 + switch (opt) { 55 + case 't': 56 + threads = atoi(optarg); 57 + break; 58 + case 's': 59 + seconds = atoi(optarg); 60 + break; 61 + case 'n': 62 + node = atoi(optarg); 63 + break; 64 + case 'b': 65 + bits = atoi(optarg); 66 + break; 67 + case 'd': 68 + dir = atoi(optarg); 69 + break; 70 + default: 71 + return -1; 72 + } 73 + } 74 + 75 + if (threads <= 0 || threads > DMA_MAP_MAX_THREADS) { 76 + fprintf(stderr, "invalid number of threads, must be in 1-%d\n", 77 + DMA_MAP_MAX_THREADS); 78 + exit(1); 79 + } 80 + 81 + if (seconds <= 0 || seconds > DMA_MAP_MAX_SECONDS) { 82 + fprintf(stderr, "invalid number of seconds, must be in 1-%d\n", 83 + DMA_MAP_MAX_SECONDS); 84 + exit(1); 85 + } 86 + 87 + /* suppose the mininum DMA zone is 1MB in the world */ 88 + if (bits < 20 || bits > 64) { 89 + fprintf(stderr, "invalid dma mask bit, must be in 20-64\n"); 90 + exit(1); 91 + } 92 + 93 + if (dir != DMA_MAP_BIDIRECTIONAL && dir != DMA_MAP_TO_DEVICE && 94 + dir != DMA_MAP_FROM_DEVICE) { 95 + fprintf(stderr, "invalid dma direction\n"); 96 + exit(1); 97 + } 98 + 99 + fd = open("/sys/kernel/debug/dma_map_benchmark", O_RDWR); 100 + if (fd == -1) { 101 + perror("open"); 102 + exit(1); 103 + } 104 + 105 + map.seconds = seconds; 106 + map.threads = threads; 107 + map.node = node; 108 + map.dma_bits = bits; 109 + map.dma_dir = dir; 110 + if (ioctl(fd, cmd, &map)) { 111 + perror("ioctl"); 112 + exit(1); 113 + } 114 + 115 + printf("dma mapping benchmark: threads:%d seconds:%d node:%d dir:%s\n", 116 + threads, seconds, node, dir[directions]); 117 + printf("average map latency(us):%.1f standard deviation:%.1f\n", 118 + map.avg_map_100ns/10.0, map.map_stddev/10.0); 119 + printf("average unmap latency(us):%.1f standard deviation:%.1f\n", 120 + map.avg_unmap_100ns/10.0, map.unmap_stddev/10.0); 121 + 122 + return 0; 123 + }