Skip to content

Commit

Permalink
dma-debug: fix overlap detection
Browse files Browse the repository at this point in the history
Commit 0abdd7a ("dma-debug: introduce debug_dma_assert_idle()") was
reworked to expand the overlap counter to the full range expressable by
3 tag bits, but it has a thinko in treating the overlap counter as a
pure reference count for the entry.

Instead of deleting when the reference-count drops to zero, we need to
delete when the overlap-count drops below zero.  Also, when detecting
overflow we can just test the overlap-count > MAX rather than applying
special meaning to 0.

Regression report available here:
http://marc.info/?l=linux-netdev&m=139073373932386&w=2

This patch, now tested on the original net_dma case, sees the expected
handful of reports before the eventual data corruption occurs.

Signed-off-by: Dan Williams <[email protected]>
Reported-by: Sander Eikelenboom <[email protected]>
Cc: Francois Romieu <[email protected]>
Signed-off-by: Andrew Morton <[email protected]>
Signed-off-by: Linus Torvalds <[email protected]>
  • Loading branch information
djbw authored and torvalds committed Jan 30, 2014
1 parent f544e14 commit 59f2e7d
Showing 1 changed file with 7 additions and 3 deletions.
10 changes: 7 additions & 3 deletions lib/dma-debug.c
Original file line number Diff line number Diff line change
Expand Up @@ -463,7 +463,7 @@ static int active_pfn_set_overlap(unsigned long pfn, int overlap)
int i;

if (overlap > ACTIVE_PFN_MAX_OVERLAP || overlap < 0)
return 0;
return overlap;

for (i = RADIX_TREE_MAX_TAGS - 1; i >= 0; i--)
if (overlap & 1 << i)
Expand All @@ -486,7 +486,7 @@ static void active_pfn_inc_overlap(unsigned long pfn)
* debug_dma_assert_idle() as the pfn may be marked idle
* prematurely.
*/
WARN_ONCE(overlap == 0,
WARN_ONCE(overlap > ACTIVE_PFN_MAX_OVERLAP,
"DMA-API: exceeded %d overlapping mappings of pfn %lx\n",
ACTIVE_PFN_MAX_OVERLAP, pfn);
}
Expand Down Expand Up @@ -517,7 +517,11 @@ static void active_pfn_remove(struct dma_debug_entry *entry)
unsigned long flags;

spin_lock_irqsave(&radix_lock, flags);
if (active_pfn_dec_overlap(entry->pfn) == 0)
/* since we are counting overlaps the final put of the
* entry->pfn will occur when the overlap count is 0.
* active_pfn_dec_overlap() returns -1 in that case
*/
if (active_pfn_dec_overlap(entry->pfn) < 0)
radix_tree_delete(&dma_active_pfn, entry->pfn);
spin_unlock_irqrestore(&radix_lock, flags);
}
Expand Down

0 comments on commit 59f2e7d

Please sign in to comment.