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/cleanup.h> 9 #include <linux/debugfs.h> 10 #include <linux/delay.h> 11 #include <linux/device.h> 12 #include <linux/dma-mapping.h> 13 #include <linux/kernel.h> 14 #include <linux/kthread.h> 15 #include <linux/math64.h> 16 #include <linux/module.h> 17 #include <linux/pci.h> 18 #include <linux/platform_device.h> 19 #include <linux/scatterlist.h> 20 #include <linux/slab.h> 21 #include <linux/timekeeping.h> 22 #include <uapi/linux/map_benchmark.h> 23 24 struct map_benchmark_data { 25 struct map_benchmark bparam; 26 struct device *dev; 27 struct dentry *debugfs; 28 enum dma_data_direction dir; 29 atomic64_t sum_map_100ns; 30 atomic64_t sum_unmap_100ns; 31 atomic64_t sum_sq_map; 32 atomic64_t sum_sq_unmap; 33 atomic64_t loops; 34 }; 35 36 struct map_benchmark_ops { 37 void *(*prepare)(struct map_benchmark_data *map); 38 void (*unprepare)(void *mparam); 39 void (*initialize_data)(void *mparam); 40 int (*do_map)(void *mparam); 41 void (*do_unmap)(void *mparam); 42 }; 43 44 struct dma_single_map_param { 45 struct device *dev; 46 dma_addr_t addr; 47 void *xbuf; 48 u32 npages; 49 u32 dma_dir; 50 }; 51 52 static void *dma_single_map_benchmark_prepare(struct map_benchmark_data *map) 53 { 54 struct dma_single_map_param *params __free(kfree) = kzalloc(sizeof(*params), 55 GFP_KERNEL); 56 if (!params) 57 return NULL; 58 59 params->npages = map->bparam.granule; 60 params->dma_dir = map->bparam.dma_dir; 61 params->dev = map->dev; 62 params->xbuf = alloc_pages_exact(params->npages * PAGE_SIZE, GFP_KERNEL); 63 if (!params->xbuf) 64 return NULL; 65 66 return_ptr(params); 67 } 68 69 static void dma_single_map_benchmark_unprepare(void *mparam) 70 { 71 struct dma_single_map_param *params = mparam; 72 73 free_pages_exact(params->xbuf, params->npages * PAGE_SIZE); 74 kfree(params); 75 } 76 77 static void dma_single_map_benchmark_initialize_data(void *mparam) 78 { 79 struct dma_single_map_param *params = mparam; 80 81 /* 82 * for a non-coherent device, if we don't stain them in the 83 * cache, this will give an underestimate of the real-world 84 * overhead of BIDIRECTIONAL or TO_DEVICE mappings; 85 * 66 means everything goes well! 66 is lucky. 86 */ 87 if (params->dma_dir != DMA_FROM_DEVICE) 88 memset(params->xbuf, 0x66, params->npages * PAGE_SIZE); 89 } 90 91 static int dma_single_map_benchmark_do_map(void *mparam) 92 { 93 struct dma_single_map_param *params = mparam; 94 95 params->addr = dma_map_single(params->dev, params->xbuf, 96 params->npages * PAGE_SIZE, params->dma_dir); 97 if (unlikely(dma_mapping_error(params->dev, params->addr))) { 98 pr_err("dma_map_single failed on %s\n", dev_name(params->dev)); 99 return -ENOMEM; 100 } 101 102 return 0; 103 } 104 105 static void dma_single_map_benchmark_do_unmap(void *mparam) 106 { 107 struct dma_single_map_param *params = mparam; 108 109 dma_unmap_single(params->dev, params->addr, 110 params->npages * PAGE_SIZE, params->dma_dir); 111 } 112 113 static struct map_benchmark_ops dma_single_map_benchmark_ops = { 114 .prepare = dma_single_map_benchmark_prepare, 115 .unprepare = dma_single_map_benchmark_unprepare, 116 .initialize_data = dma_single_map_benchmark_initialize_data, 117 .do_map = dma_single_map_benchmark_do_map, 118 .do_unmap = dma_single_map_benchmark_do_unmap, 119 }; 120 121 struct dma_sg_map_param { 122 struct sg_table sgt; 123 struct device *dev; 124 u32 npages; 125 u32 dma_dir; 126 void *buf[] __counted_by(npages); 127 }; 128 129 static void *dma_sg_map_benchmark_prepare(struct map_benchmark_data *map) 130 { 131 struct dma_sg_map_param *params; 132 struct scatterlist *sg; 133 u32 npages; 134 int i; 135 136 /* 137 * Set the number of scatterlist entries based on the granule. 138 * In SG mode, 'granule' represents the number of scatterlist entries. 139 * Each scatterlist entry corresponds to a single page. 140 */ 141 npages = map->bparam.granule; 142 143 params = kzalloc_flex(*params, buf, npages); 144 if (!params) 145 return NULL; 146 147 params->npages = npages; 148 params->dma_dir = map->bparam.dma_dir; 149 params->dev = map->dev; 150 151 if (sg_alloc_table(¶ms->sgt, params->npages, GFP_KERNEL)) 152 goto free_params; 153 154 for_each_sgtable_sg(¶ms->sgt, sg, i) { 155 params->buf[i] = (void *)__get_free_page(GFP_KERNEL); 156 if (!params->buf[i]) 157 goto free_page; 158 159 sg_set_buf(sg, params->buf[i], PAGE_SIZE); 160 } 161 162 return params; 163 164 free_page: 165 while (i-- > 0) 166 free_page((unsigned long)params->buf[i]); 167 168 sg_free_table(¶ms->sgt); 169 free_params: 170 kfree(params); 171 return NULL; 172 } 173 174 static void dma_sg_map_benchmark_unprepare(void *mparam) 175 { 176 struct dma_sg_map_param *params = mparam; 177 int i; 178 179 for (i = 0; i < params->npages; i++) 180 free_page((unsigned long)params->buf[i]); 181 182 sg_free_table(¶ms->sgt); 183 184 kfree(params); 185 } 186 187 static void dma_sg_map_benchmark_initialize_data(void *mparam) 188 { 189 struct dma_sg_map_param *params = mparam; 190 struct scatterlist *sg; 191 int i = 0; 192 193 if (params->dma_dir == DMA_FROM_DEVICE) 194 return; 195 196 for_each_sgtable_sg(¶ms->sgt, sg, i) 197 memset(params->buf[i], 0x66, PAGE_SIZE); 198 } 199 200 static int dma_sg_map_benchmark_do_map(void *mparam) 201 { 202 struct dma_sg_map_param *params = mparam; 203 int ret = 0; 204 205 int sg_mapped = dma_map_sg(params->dev, params->sgt.sgl, 206 params->npages, params->dma_dir); 207 if (!sg_mapped) { 208 pr_err("dma_map_sg failed on %s\n", dev_name(params->dev)); 209 ret = -ENOMEM; 210 } 211 212 return ret; 213 } 214 215 static void dma_sg_map_benchmark_do_unmap(void *mparam) 216 { 217 struct dma_sg_map_param *params = mparam; 218 219 dma_unmap_sg(params->dev, params->sgt.sgl, params->npages, 220 params->dma_dir); 221 } 222 223 static struct map_benchmark_ops dma_sg_map_benchmark_ops = { 224 .prepare = dma_sg_map_benchmark_prepare, 225 .unprepare = dma_sg_map_benchmark_unprepare, 226 .initialize_data = dma_sg_map_benchmark_initialize_data, 227 .do_map = dma_sg_map_benchmark_do_map, 228 .do_unmap = dma_sg_map_benchmark_do_unmap, 229 }; 230 231 static struct map_benchmark_ops *dma_map_benchmark_ops[DMA_MAP_BENCH_MODE_MAX] = { 232 [DMA_MAP_BENCH_SINGLE_MODE] = &dma_single_map_benchmark_ops, 233 [DMA_MAP_BENCH_SG_MODE] = &dma_sg_map_benchmark_ops, 234 }; 235 236 static int map_benchmark_thread(void *data) 237 { 238 struct map_benchmark_data *map = data; 239 __u8 map_mode = map->bparam.map_mode; 240 int ret = 0; 241 242 struct map_benchmark_ops *mb_ops = dma_map_benchmark_ops[map_mode]; 243 void *mparam = mb_ops->prepare(map); 244 245 if (!mparam) 246 return -ENOMEM; 247 248 while (!kthread_should_stop()) { 249 u64 map_100ns, unmap_100ns, map_sq, unmap_sq; 250 ktime_t map_stime, map_etime, unmap_stime, unmap_etime; 251 ktime_t map_delta, unmap_delta; 252 253 mb_ops->initialize_data(mparam); 254 map_stime = ktime_get(); 255 ret = mb_ops->do_map(mparam); 256 if (ret) 257 goto out; 258 259 map_etime = ktime_get(); 260 map_delta = ktime_sub(map_etime, map_stime); 261 262 /* Pretend DMA is transmitting */ 263 ndelay(map->bparam.dma_trans_ns); 264 265 unmap_stime = ktime_get(); 266 mb_ops->do_unmap(mparam); 267 268 unmap_etime = ktime_get(); 269 unmap_delta = ktime_sub(unmap_etime, unmap_stime); 270 271 /* calculate sum and sum of squares */ 272 273 map_100ns = div64_ul(map_delta, 100); 274 unmap_100ns = div64_ul(unmap_delta, 100); 275 map_sq = map_100ns * map_100ns; 276 unmap_sq = unmap_100ns * unmap_100ns; 277 278 atomic64_add(map_100ns, &map->sum_map_100ns); 279 atomic64_add(unmap_100ns, &map->sum_unmap_100ns); 280 atomic64_add(map_sq, &map->sum_sq_map); 281 atomic64_add(unmap_sq, &map->sum_sq_unmap); 282 atomic64_inc(&map->loops); 283 284 /* 285 * We may test for a long time so periodically check whether 286 * we need to schedule to avoid starving the others. Otherwise 287 * we may hangup the kernel in a non-preemptible kernel when 288 * the test kthreads number >= CPU number, the test kthreads 289 * will run endless on every CPU since the thread resposible 290 * for notifying the kthread stop (in do_map_benchmark()) 291 * could not be scheduled. 292 * 293 * Note this may degrade the test concurrency since the test 294 * threads may need to share the CPU time with other load 295 * in the system. So it's recommended to run this benchmark 296 * on an idle system. 297 */ 298 cond_resched(); 299 } 300 301 out: 302 mb_ops->unprepare(mparam); 303 return ret; 304 } 305 306 static int do_map_benchmark(struct map_benchmark_data *map) 307 { 308 struct task_struct **tsk; 309 int threads = map->bparam.threads; 310 int node = map->bparam.node; 311 u64 loops; 312 int ret = 0; 313 int i; 314 315 tsk = kmalloc_objs(*tsk, threads); 316 if (!tsk) 317 return -ENOMEM; 318 319 get_device(map->dev); 320 321 for (i = 0; i < threads; i++) { 322 tsk[i] = kthread_create_on_node(map_benchmark_thread, map, 323 map->bparam.node, "dma-map-benchmark/%d", i); 324 if (IS_ERR(tsk[i])) { 325 pr_err("create dma_map thread failed\n"); 326 ret = PTR_ERR(tsk[i]); 327 while (--i >= 0) 328 kthread_stop(tsk[i]); 329 goto out; 330 } 331 332 if (node != NUMA_NO_NODE) 333 kthread_bind_mask(tsk[i], cpumask_of_node(node)); 334 } 335 336 /* clear the old value in the previous benchmark */ 337 atomic64_set(&map->sum_map_100ns, 0); 338 atomic64_set(&map->sum_unmap_100ns, 0); 339 atomic64_set(&map->sum_sq_map, 0); 340 atomic64_set(&map->sum_sq_unmap, 0); 341 atomic64_set(&map->loops, 0); 342 343 for (i = 0; i < threads; i++) { 344 get_task_struct(tsk[i]); 345 wake_up_process(tsk[i]); 346 } 347 348 msleep_interruptible(map->bparam.seconds * 1000); 349 350 /* wait for the completion of all started benchmark threads */ 351 for (i = 0; i < threads; i++) { 352 int kthread_ret = kthread_stop_put(tsk[i]); 353 354 if (kthread_ret) 355 ret = kthread_ret; 356 } 357 358 if (ret) 359 goto out; 360 361 loops = atomic64_read(&map->loops); 362 if (likely(loops > 0)) { 363 u64 map_variance, unmap_variance; 364 u64 sum_map = atomic64_read(&map->sum_map_100ns); 365 u64 sum_unmap = atomic64_read(&map->sum_unmap_100ns); 366 u64 sum_sq_map = atomic64_read(&map->sum_sq_map); 367 u64 sum_sq_unmap = atomic64_read(&map->sum_sq_unmap); 368 369 /* average latency */ 370 map->bparam.avg_map_100ns = div64_u64(sum_map, loops); 371 map->bparam.avg_unmap_100ns = div64_u64(sum_unmap, loops); 372 373 /* standard deviation of latency */ 374 map_variance = div64_u64(sum_sq_map, loops) - 375 map->bparam.avg_map_100ns * 376 map->bparam.avg_map_100ns; 377 unmap_variance = div64_u64(sum_sq_unmap, loops) - 378 map->bparam.avg_unmap_100ns * 379 map->bparam.avg_unmap_100ns; 380 map->bparam.map_stddev = int_sqrt64(map_variance); 381 map->bparam.unmap_stddev = int_sqrt64(unmap_variance); 382 } 383 384 out: 385 put_device(map->dev); 386 kfree(tsk); 387 return ret; 388 } 389 390 static long map_benchmark_ioctl(struct file *file, unsigned int cmd, 391 unsigned long arg) 392 { 393 struct map_benchmark_data *map = file->private_data; 394 void __user *argp = (void __user *)arg; 395 u64 old_dma_mask; 396 int ret; 397 398 if (copy_from_user(&map->bparam, argp, sizeof(map->bparam))) 399 return -EFAULT; 400 401 switch (cmd) { 402 case DMA_MAP_BENCHMARK: 403 if (map->bparam.map_mode < 0 || 404 map->bparam.map_mode >= DMA_MAP_BENCH_MODE_MAX) { 405 pr_err("invalid map mode\n"); 406 return -EINVAL; 407 } 408 409 if (map->bparam.threads == 0 || 410 map->bparam.threads > DMA_MAP_MAX_THREADS) { 411 pr_err("invalid thread number\n"); 412 return -EINVAL; 413 } 414 415 if (map->bparam.seconds == 0 || 416 map->bparam.seconds > DMA_MAP_MAX_SECONDS) { 417 pr_err("invalid duration seconds\n"); 418 return -EINVAL; 419 } 420 421 if (map->bparam.dma_trans_ns > DMA_MAP_MAX_TRANS_DELAY) { 422 pr_err("invalid transmission delay\n"); 423 return -EINVAL; 424 } 425 426 if (map->bparam.node != NUMA_NO_NODE && 427 (map->bparam.node < 0 || map->bparam.node >= MAX_NUMNODES || 428 !node_possible(map->bparam.node))) { 429 pr_err("invalid numa node\n"); 430 return -EINVAL; 431 } 432 433 if (map->bparam.granule < 1 || map->bparam.granule > 1024) { 434 pr_err("invalid granule size\n"); 435 return -EINVAL; 436 } 437 438 switch (map->bparam.dma_dir) { 439 case DMA_MAP_BIDIRECTIONAL: 440 map->dir = DMA_BIDIRECTIONAL; 441 break; 442 case DMA_MAP_FROM_DEVICE: 443 map->dir = DMA_FROM_DEVICE; 444 break; 445 case DMA_MAP_TO_DEVICE: 446 map->dir = DMA_TO_DEVICE; 447 break; 448 default: 449 pr_err("invalid DMA direction\n"); 450 return -EINVAL; 451 } 452 453 old_dma_mask = dma_get_mask(map->dev); 454 455 ret = dma_set_mask(map->dev, 456 DMA_BIT_MASK(map->bparam.dma_bits)); 457 if (ret) { 458 pr_err("failed to set dma_mask on device %s\n", 459 dev_name(map->dev)); 460 return -EINVAL; 461 } 462 463 ret = do_map_benchmark(map); 464 465 /* 466 * restore the original dma_mask as many devices' dma_mask are 467 * set by architectures, acpi, busses. When we bind them back 468 * to their original drivers, those drivers shouldn't see 469 * dma_mask changed by benchmark 470 */ 471 dma_set_mask(map->dev, old_dma_mask); 472 473 if (ret) 474 return ret; 475 break; 476 default: 477 return -EINVAL; 478 } 479 480 if (copy_to_user(argp, &map->bparam, sizeof(map->bparam))) 481 return -EFAULT; 482 483 return ret; 484 } 485 486 static const struct file_operations map_benchmark_fops = { 487 .open = simple_open, 488 .unlocked_ioctl = map_benchmark_ioctl, 489 }; 490 491 static void map_benchmark_remove_debugfs(void *data) 492 { 493 struct map_benchmark_data *map = (struct map_benchmark_data *)data; 494 495 debugfs_remove(map->debugfs); 496 } 497 498 static int __map_benchmark_probe(struct device *dev) 499 { 500 struct dentry *entry; 501 struct map_benchmark_data *map; 502 int ret; 503 504 map = devm_kzalloc(dev, sizeof(*map), GFP_KERNEL); 505 if (!map) 506 return -ENOMEM; 507 map->dev = dev; 508 509 ret = devm_add_action(dev, map_benchmark_remove_debugfs, map); 510 if (ret) { 511 pr_err("Can't add debugfs remove action\n"); 512 return ret; 513 } 514 515 /* 516 * we only permit a device bound with this driver, 2nd probe 517 * will fail 518 */ 519 entry = debugfs_create_file("dma_map_benchmark", 0600, NULL, map, 520 &map_benchmark_fops); 521 if (IS_ERR(entry)) 522 return PTR_ERR(entry); 523 map->debugfs = entry; 524 525 return 0; 526 } 527 528 static int map_benchmark_platform_probe(struct platform_device *pdev) 529 { 530 return __map_benchmark_probe(&pdev->dev); 531 } 532 533 static struct platform_driver map_benchmark_platform_driver = { 534 .driver = { 535 .name = "dma_map_benchmark", 536 }, 537 .probe = map_benchmark_platform_probe, 538 }; 539 540 static int 541 map_benchmark_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id) 542 { 543 return __map_benchmark_probe(&pdev->dev); 544 } 545 546 static struct pci_driver map_benchmark_pci_driver = { 547 .name = "dma_map_benchmark", 548 .probe = map_benchmark_pci_probe, 549 }; 550 551 static int __init map_benchmark_init(void) 552 { 553 int ret; 554 555 ret = pci_register_driver(&map_benchmark_pci_driver); 556 if (ret) 557 return ret; 558 559 ret = platform_driver_register(&map_benchmark_platform_driver); 560 if (ret) { 561 pci_unregister_driver(&map_benchmark_pci_driver); 562 return ret; 563 } 564 565 return 0; 566 } 567 568 static void __exit map_benchmark_cleanup(void) 569 { 570 platform_driver_unregister(&map_benchmark_platform_driver); 571 pci_unregister_driver(&map_benchmark_pci_driver); 572 } 573 574 module_init(map_benchmark_init); 575 module_exit(map_benchmark_cleanup); 576 577 MODULE_AUTHOR("Barry Song <song.bao.hua@hisilicon.com>"); 578 MODULE_DESCRIPTION("dma_map benchmark driver"); 579