diff options
Diffstat (limited to 'lib/dma-debug.c')
| -rw-r--r-- | lib/dma-debug.c | 193 |
1 files changed, 180 insertions, 13 deletions
diff --git a/lib/dma-debug.c b/lib/dma-debug.c index d87a17a819d0..c38083871f11 100644 --- a/lib/dma-debug.c +++ b/lib/dma-debug.c | |||
| @@ -53,11 +53,26 @@ enum map_err_types { | |||
| 53 | 53 | ||
| 54 | #define DMA_DEBUG_STACKTRACE_ENTRIES 5 | 54 | #define DMA_DEBUG_STACKTRACE_ENTRIES 5 |
| 55 | 55 | ||
| 56 | /** | ||
| 57 | * struct dma_debug_entry - track a dma_map* or dma_alloc_coherent mapping | ||
| 58 | * @list: node on pre-allocated free_entries list | ||
| 59 | * @dev: 'dev' argument to dma_map_{page|single|sg} or dma_alloc_coherent | ||
| 60 | * @type: single, page, sg, coherent | ||
| 61 | * @pfn: page frame of the start address | ||
| 62 | * @offset: offset of mapping relative to pfn | ||
| 63 | * @size: length of the mapping | ||
| 64 | * @direction: enum dma_data_direction | ||
| 65 | * @sg_call_ents: 'nents' from dma_map_sg | ||
| 66 | * @sg_mapped_ents: 'mapped_ents' from dma_map_sg | ||
| 67 | * @map_err_type: track whether dma_mapping_error() was checked | ||
| 68 | * @stacktrace: support backtraces when a violation is detected | ||
| 69 | */ | ||
| 56 | struct dma_debug_entry { | 70 | struct dma_debug_entry { |
| 57 | struct list_head list; | 71 | struct list_head list; |
| 58 | struct device *dev; | 72 | struct device *dev; |
| 59 | int type; | 73 | int type; |
| 60 | phys_addr_t paddr; | 74 | unsigned long pfn; |
| 75 | size_t offset; | ||
| 61 | u64 dev_addr; | 76 | u64 dev_addr; |
| 62 | u64 size; | 77 | u64 size; |
| 63 | int direction; | 78 | int direction; |
| @@ -372,6 +387,11 @@ static void hash_bucket_del(struct dma_debug_entry *entry) | |||
| 372 | list_del(&entry->list); | 387 | list_del(&entry->list); |
| 373 | } | 388 | } |
| 374 | 389 | ||
| 390 | static unsigned long long phys_addr(struct dma_debug_entry *entry) | ||
| 391 | { | ||
| 392 | return page_to_phys(pfn_to_page(entry->pfn)) + entry->offset; | ||
| 393 | } | ||
| 394 | |||
| 375 | /* | 395 | /* |
| 376 | * Dump mapping entries for debugging purposes | 396 | * Dump mapping entries for debugging purposes |
| 377 | */ | 397 | */ |
| @@ -389,9 +409,9 @@ void debug_dma_dump_mappings(struct device *dev) | |||
| 389 | list_for_each_entry(entry, &bucket->list, list) { | 409 | list_for_each_entry(entry, &bucket->list, list) { |
| 390 | if (!dev || dev == entry->dev) { | 410 | if (!dev || dev == entry->dev) { |
| 391 | dev_info(entry->dev, | 411 | dev_info(entry->dev, |
| 392 | "%s idx %d P=%Lx D=%Lx L=%Lx %s %s\n", | 412 | "%s idx %d P=%Lx N=%lx D=%Lx L=%Lx %s %s\n", |
| 393 | type2name[entry->type], idx, | 413 | type2name[entry->type], idx, |
| 394 | (unsigned long long)entry->paddr, | 414 | phys_addr(entry), entry->pfn, |
| 395 | entry->dev_addr, entry->size, | 415 | entry->dev_addr, entry->size, |
| 396 | dir2name[entry->direction], | 416 | dir2name[entry->direction], |
| 397 | maperr2str[entry->map_err_type]); | 417 | maperr2str[entry->map_err_type]); |
| @@ -404,6 +424,133 @@ void debug_dma_dump_mappings(struct device *dev) | |||
| 404 | EXPORT_SYMBOL(debug_dma_dump_mappings); | 424 | EXPORT_SYMBOL(debug_dma_dump_mappings); |
| 405 | 425 | ||
| 406 | /* | 426 | /* |
| 427 | * For each page mapped (initial page in the case of | ||
| 428 | * dma_alloc_coherent/dma_map_{single|page}, or each page in a | ||
| 429 | * scatterlist) insert into this tree using the pfn as the key. At | ||
| 430 | * dma_unmap_{single|sg|page} or dma_free_coherent delete the entry. If | ||
| 431 | * the pfn already exists at insertion time add a tag as a reference | ||
| 432 | * count for the overlapping mappings. For now, the overlap tracking | ||
| 433 | * just ensures that 'unmaps' balance 'maps' before marking the pfn | ||
| 434 | * idle, but we should also be flagging overlaps as an API violation. | ||
| 435 | * | ||
| 436 | * Memory usage is mostly constrained by the maximum number of available | ||
| 437 | * dma-debug entries in that we need a free dma_debug_entry before | ||
| 438 | * inserting into the tree. In the case of dma_map_{single|page} and | ||
| 439 | * dma_alloc_coherent there is only one dma_debug_entry and one pfn to | ||
| 440 | * track per event. dma_map_sg(), on the other hand, | ||
| 441 | * consumes a single dma_debug_entry, but inserts 'nents' entries into | ||
| 442 | * the tree. | ||
| 443 | * | ||
| 444 | * At any time debug_dma_assert_idle() can be called to trigger a | ||
| 445 | * warning if the given page is in the active set. | ||
| 446 | */ | ||
| 447 | static RADIX_TREE(dma_active_pfn, GFP_NOWAIT); | ||
| 448 | static DEFINE_SPINLOCK(radix_lock); | ||
| 449 | #define ACTIVE_PFN_MAX_OVERLAP ((1 << RADIX_TREE_MAX_TAGS) - 1) | ||
| 450 | |||
| 451 | static int active_pfn_read_overlap(unsigned long pfn) | ||
| 452 | { | ||
| 453 | int overlap = 0, i; | ||
| 454 | |||
| 455 | for (i = RADIX_TREE_MAX_TAGS - 1; i >= 0; i--) | ||
| 456 | if (radix_tree_tag_get(&dma_active_pfn, pfn, i)) | ||
| 457 | overlap |= 1 << i; | ||
| 458 | return overlap; | ||
| 459 | } | ||
| 460 | |||
| 461 | static int active_pfn_set_overlap(unsigned long pfn, int overlap) | ||
| 462 | { | ||
| 463 | int i; | ||
| 464 | |||
| 465 | if (overlap > ACTIVE_PFN_MAX_OVERLAP || overlap < 0) | ||
| 466 | return 0; | ||
| 467 | |||
| 468 | for (i = RADIX_TREE_MAX_TAGS - 1; i >= 0; i--) | ||
| 469 | if (overlap & 1 << i) | ||
| 470 | radix_tree_tag_set(&dma_active_pfn, pfn, i); | ||
| 471 | else | ||
| 472 | radix_tree_tag_clear(&dma_active_pfn, pfn, i); | ||
| 473 | |||
| 474 | return overlap; | ||
| 475 | } | ||
| 476 | |||
| 477 | static void active_pfn_inc_overlap(unsigned long pfn) | ||
| 478 | { | ||
| 479 | int overlap = active_pfn_read_overlap(pfn); | ||
| 480 | |||
| 481 | overlap = active_pfn_set_overlap(pfn, ++overlap); | ||
| 482 | |||
| 483 | /* If we overflowed the overlap counter then we're potentially | ||
| 484 | * leaking dma-mappings. Otherwise, if maps and unmaps are | ||
| 485 | * balanced then this overflow may cause false negatives in | ||
| 486 | * debug_dma_assert_idle() as the pfn may be marked idle | ||
| 487 | * prematurely. | ||
| 488 | */ | ||
| 489 | WARN_ONCE(overlap == 0, | ||
| 490 | "DMA-API: exceeded %d overlapping mappings of pfn %lx\n", | ||
| 491 | ACTIVE_PFN_MAX_OVERLAP, pfn); | ||
| 492 | } | ||
| 493 | |||
| 494 | static int active_pfn_dec_overlap(unsigned long pfn) | ||
| 495 | { | ||
| 496 | int overlap = active_pfn_read_overlap(pfn); | ||
| 497 | |||
| 498 | return active_pfn_set_overlap(pfn, --overlap); | ||
| 499 | } | ||
| 500 | |||
| 501 | static int active_pfn_insert(struct dma_debug_entry *entry) | ||
| 502 | { | ||
| 503 | unsigned long flags; | ||
| 504 | int rc; | ||
| 505 | |||
| 506 | spin_lock_irqsave(&radix_lock, flags); | ||
| 507 | rc = radix_tree_insert(&dma_active_pfn, entry->pfn, entry); | ||
| 508 | if (rc == -EEXIST) | ||
| 509 | active_pfn_inc_overlap(entry->pfn); | ||
| 510 | spin_unlock_irqrestore(&radix_lock, flags); | ||
| 511 | |||
| 512 | return rc; | ||
| 513 | } | ||
| 514 | |||
| 515 | static void active_pfn_remove(struct dma_debug_entry *entry) | ||
| 516 | { | ||
| 517 | unsigned long flags; | ||
| 518 | |||
| 519 | spin_lock_irqsave(&radix_lock, flags); | ||
| 520 | if (active_pfn_dec_overlap(entry->pfn) == 0) | ||
| 521 | radix_tree_delete(&dma_active_pfn, entry->pfn); | ||
| 522 | spin_unlock_irqrestore(&radix_lock, flags); | ||
| 523 | } | ||
| 524 | |||
| 525 | /** | ||
| 526 | * debug_dma_assert_idle() - assert that a page is not undergoing dma | ||
| 527 | * @page: page to lookup in the dma_active_pfn tree | ||
| 528 | * | ||
| 529 | * Place a call to this routine in cases where the cpu touching the page | ||
| 530 | * before the dma completes (page is dma_unmapped) will lead to data | ||
| 531 | * corruption. | ||
| 532 | */ | ||
| 533 | void debug_dma_assert_idle(struct page *page) | ||
| 534 | { | ||
| 535 | unsigned long flags; | ||
| 536 | struct dma_debug_entry *entry; | ||
| 537 | |||
| 538 | if (!page) | ||
| 539 | return; | ||
| 540 | |||
| 541 | spin_lock_irqsave(&radix_lock, flags); | ||
| 542 | entry = radix_tree_lookup(&dma_active_pfn, page_to_pfn(page)); | ||
| 543 | spin_unlock_irqrestore(&radix_lock, flags); | ||
| 544 | |||
| 545 | if (!entry) | ||
| 546 | return; | ||
| 547 | |||
| 548 | err_printk(entry->dev, entry, | ||
| 549 | "DMA-API: cpu touching an active dma mapped page " | ||
| 550 | "[pfn=0x%lx]\n", entry->pfn); | ||
| 551 | } | ||
| 552 | |||
| 553 | /* | ||
| 407 | * Wrapper function for adding an entry to the hash. | 554 | * Wrapper function for adding an entry to the hash. |
| 408 | * This function takes care of locking itself. | 555 | * This function takes care of locking itself. |
| 409 | */ | 556 | */ |
| @@ -411,10 +558,21 @@ static void add_dma_entry(struct dma_debug_entry *entry) | |||
| 411 | { | 558 | { |
| 412 | struct hash_bucket *bucket; | 559 | struct hash_bucket *bucket; |
| 413 | unsigned long flags; | 560 | unsigned long flags; |
| 561 | int rc; | ||
| 414 | 562 | ||
| 415 | bucket = get_hash_bucket(entry, &flags); | 563 | bucket = get_hash_bucket(entry, &flags); |
| 416 | hash_bucket_add(bucket, entry); | 564 | hash_bucket_add(bucket, entry); |
| 417 | put_hash_bucket(bucket, &flags); | 565 | put_hash_bucket(bucket, &flags); |
| 566 | |||
| 567 | rc = active_pfn_insert(entry); | ||
| 568 | if (rc == -ENOMEM) { | ||
| 569 | pr_err("DMA-API: pfn tracking ENOMEM, dma-debug disabled\n"); | ||
| 570 | global_disable = true; | ||
| 571 | } | ||
| 572 | |||
| 573 | /* TODO: report -EEXIST errors here as overlapping mappings are | ||
| 574 | * not supported by the DMA API | ||
| 575 | */ | ||
| 418 | } | 576 | } |
| 419 | 577 | ||
| 420 | static struct dma_debug_entry *__dma_entry_alloc(void) | 578 | static struct dma_debug_entry *__dma_entry_alloc(void) |
| @@ -469,6 +627,8 @@ static void dma_entry_free(struct dma_debug_entry *entry) | |||
| 469 | { | 627 | { |
| 470 | unsigned long flags; | 628 | unsigned long flags; |
| 471 | 629 | ||
| 630 | active_pfn_remove(entry); | ||
| 631 | |||
| 472 | /* | 632 | /* |
| 473 | * add to beginning of the list - this way the entries are | 633 | * add to beginning of the list - this way the entries are |
| 474 | * more likely cache hot when they are reallocated. | 634 | * more likely cache hot when they are reallocated. |
| @@ -895,15 +1055,15 @@ static void check_unmap(struct dma_debug_entry *ref) | |||
| 895 | ref->dev_addr, ref->size, | 1055 | ref->dev_addr, ref->size, |
| 896 | type2name[entry->type], type2name[ref->type]); | 1056 | type2name[entry->type], type2name[ref->type]); |
| 897 | } else if ((entry->type == dma_debug_coherent) && | 1057 | } else if ((entry->type == dma_debug_coherent) && |
| 898 | (ref->paddr != entry->paddr)) { | 1058 | (phys_addr(ref) != phys_addr(entry))) { |
| 899 | err_printk(ref->dev, entry, "DMA-API: device driver frees " | 1059 | err_printk(ref->dev, entry, "DMA-API: device driver frees " |
| 900 | "DMA memory with different CPU address " | 1060 | "DMA memory with different CPU address " |
| 901 | "[device address=0x%016llx] [size=%llu bytes] " | 1061 | "[device address=0x%016llx] [size=%llu bytes] " |
| 902 | "[cpu alloc address=0x%016llx] " | 1062 | "[cpu alloc address=0x%016llx] " |
| 903 | "[cpu free address=0x%016llx]", | 1063 | "[cpu free address=0x%016llx]", |
| 904 | ref->dev_addr, ref->size, | 1064 | ref->dev_addr, ref->size, |
| 905 | (unsigned long long)entry->paddr, | 1065 | phys_addr(entry), |
| 906 | (unsigned long long)ref->paddr); | 1066 | phys_addr(ref)); |
| 907 | } | 1067 | } |
| 908 | 1068 | ||
| 909 | if (ref->sg_call_ents && ref->type == dma_debug_sg && | 1069 | if (ref->sg_call_ents && ref->type == dma_debug_sg && |
| @@ -1052,7 +1212,8 @@ void debug_dma_map_page(struct device *dev, struct page *page, size_t offset, | |||
| 1052 | 1212 | ||
| 1053 | entry->dev = dev; | 1213 | entry->dev = dev; |
| 1054 | entry->type = dma_debug_page; | 1214 | entry->type = dma_debug_page; |
| 1055 | entry->paddr = page_to_phys(page) + offset; | 1215 | entry->pfn = page_to_pfn(page); |
| 1216 | entry->offset = offset, | ||
| 1056 | entry->dev_addr = dma_addr; | 1217 | entry->dev_addr = dma_addr; |
| 1057 | entry->size = size; | 1218 | entry->size = size; |
| 1058 | entry->direction = direction; | 1219 | entry->direction = direction; |
| @@ -1148,7 +1309,8 @@ void debug_dma_map_sg(struct device *dev, struct scatterlist *sg, | |||
| 1148 | 1309 | ||
| 1149 | entry->type = dma_debug_sg; | 1310 | entry->type = dma_debug_sg; |
| 1150 | entry->dev = dev; | 1311 | entry->dev = dev; |
| 1151 | entry->paddr = sg_phys(s); | 1312 | entry->pfn = page_to_pfn(sg_page(s)); |
| 1313 | entry->offset = s->offset, | ||
| 1152 | entry->size = sg_dma_len(s); | 1314 | entry->size = sg_dma_len(s); |
| 1153 | entry->dev_addr = sg_dma_address(s); | 1315 | entry->dev_addr = sg_dma_address(s); |
| 1154 | entry->direction = direction; | 1316 | entry->direction = direction; |
| @@ -1198,7 +1360,8 @@ void debug_dma_unmap_sg(struct device *dev, struct scatterlist *sglist, | |||
| 1198 | struct dma_debug_entry ref = { | 1360 | struct dma_debug_entry ref = { |
| 1199 | .type = dma_debug_sg, | 1361 | .type = dma_debug_sg, |
| 1200 | .dev = dev, | 1362 | .dev = dev, |
| 1201 | .paddr = sg_phys(s), | 1363 | .pfn = page_to_pfn(sg_page(s)), |
| 1364 | .offset = s->offset, | ||
| 1202 | .dev_addr = sg_dma_address(s), | 1365 | .dev_addr = sg_dma_address(s), |
| 1203 | .size = sg_dma_len(s), | 1366 | .size = sg_dma_len(s), |
| 1204 | .direction = dir, | 1367 | .direction = dir, |
| @@ -1233,7 +1396,8 @@ void debug_dma_alloc_coherent(struct device *dev, size_t size, | |||
| 1233 | 1396 | ||
| 1234 | entry->type = dma_debug_coherent; | 1397 | entry->type = dma_debug_coherent; |
| 1235 | entry->dev = dev; | 1398 | entry->dev = dev; |
| 1236 | entry->paddr = virt_to_phys(virt); | 1399 | entry->pfn = page_to_pfn(virt_to_page(virt)); |
| 1400 | entry->offset = (size_t) virt & PAGE_MASK; | ||
| 1237 | entry->size = size; | 1401 | entry->size = size; |
| 1238 | entry->dev_addr = dma_addr; | 1402 | entry->dev_addr = dma_addr; |
| 1239 | entry->direction = DMA_BIDIRECTIONAL; | 1403 | entry->direction = DMA_BIDIRECTIONAL; |
| @@ -1248,7 +1412,8 @@ void debug_dma_free_coherent(struct device *dev, size_t size, | |||
| 1248 | struct dma_debug_entry ref = { | 1412 | struct dma_debug_entry ref = { |
| 1249 | .type = dma_debug_coherent, | 1413 | .type = dma_debug_coherent, |
| 1250 | .dev = dev, | 1414 | .dev = dev, |
| 1251 | .paddr = virt_to_phys(virt), | 1415 | .pfn = page_to_pfn(virt_to_page(virt)), |
| 1416 | .offset = (size_t) virt & PAGE_MASK, | ||
| 1252 | .dev_addr = addr, | 1417 | .dev_addr = addr, |
| 1253 | .size = size, | 1418 | .size = size, |
| 1254 | .direction = DMA_BIDIRECTIONAL, | 1419 | .direction = DMA_BIDIRECTIONAL, |
| @@ -1356,7 +1521,8 @@ void debug_dma_sync_sg_for_cpu(struct device *dev, struct scatterlist *sg, | |||
| 1356 | struct dma_debug_entry ref = { | 1521 | struct dma_debug_entry ref = { |
| 1357 | .type = dma_debug_sg, | 1522 | .type = dma_debug_sg, |
| 1358 | .dev = dev, | 1523 | .dev = dev, |
| 1359 | .paddr = sg_phys(s), | 1524 | .pfn = page_to_pfn(sg_page(s)), |
| 1525 | .offset = s->offset, | ||
| 1360 | .dev_addr = sg_dma_address(s), | 1526 | .dev_addr = sg_dma_address(s), |
| 1361 | .size = sg_dma_len(s), | 1527 | .size = sg_dma_len(s), |
| 1362 | .direction = direction, | 1528 | .direction = direction, |
| @@ -1388,7 +1554,8 @@ void debug_dma_sync_sg_for_device(struct device *dev, struct scatterlist *sg, | |||
| 1388 | struct dma_debug_entry ref = { | 1554 | struct dma_debug_entry ref = { |
| 1389 | .type = dma_debug_sg, | 1555 | .type = dma_debug_sg, |
| 1390 | .dev = dev, | 1556 | .dev = dev, |
| 1391 | .paddr = sg_phys(s), | 1557 | .pfn = page_to_pfn(sg_page(s)), |
| 1558 | .offset = s->offset, | ||
| 1392 | .dev_addr = sg_dma_address(s), | 1559 | .dev_addr = sg_dma_address(s), |
| 1393 | .size = sg_dma_len(s), | 1560 | .size = sg_dma_len(s), |
| 1394 | .direction = direction, | 1561 | .direction = direction, |
