lkml.org 
[lkml]   [2014]   [Feb]   [11]   [last100]   RSS Feed
Views: [wrap][no wrap]   [headers]  [forward] 
 
Messages in this thread
Patch in this message
/
Date
SubjectRe: 3.14-mw regression: rtl8169 WARNING: DMA-API: exceeded 7 overlapping mappings of pfn 55ebe
From
On Tue, Feb 11, 2014 at 11:56 AM, Sander Eikelenboom
<linux@eikelenboom.it> wrote:
> Hi Dan,
>
> FYI just tested and put Xen out of the equation (booting baremetal) and it still persists.
>
> I tried something else .. don't know if it gives you anymore insights, but it's worth the try:

This is great! See below:

>
> diff --git a/lib/dma-debug.c b/lib/dma-debug.c
> index 2defd13..0fe5b75 100644
> --- a/lib/dma-debug.c
> +++ b/lib/dma-debug.c
> @@ -474,11 +474,11 @@ static int active_pfn_set_overlap(unsigned long pfn, int overlap)
> return overlap;
> }
>
> -static void active_pfn_inc_overlap(unsigned long pfn)
> +static void active_pfn_inc_overlap(struct dma_debug_entry *ent)
> {
> - int overlap = active_pfn_read_overlap(pfn);
> + int overlap = active_pfn_read_overlap(ent->pfn);
>
> - overlap = active_pfn_set_overlap(pfn, ++overlap);
> + overlap = active_pfn_set_overlap(ent->pfn, ++overlap);
>
> /* If we overflowed the overlap counter then we're potentially
> * leaking dma-mappings. Otherwise, if maps and unmaps are
> @@ -486,15 +486,43 @@ static void active_pfn_inc_overlap(unsigned long pfn)
> * debug_dma_assert_idle() as the pfn may be marked idle
> * prematurely.
> */
> +
> WARN_ONCE(overlap > ACTIVE_PFN_MAX_OVERLAP,
> "DMA-API: exceeded %d overlapping mappings of pfn %lx\n",
> - ACTIVE_PFN_MAX_OVERLAP, pfn);
> + ACTIVE_PFN_MAX_OVERLAP, ent->pfn);
> +
> + if(overlap > ACTIVE_PFN_MAX_OVERLAP){
> +
> + dev_info(ent->dev, "DMA-API: exceeded %d overlapping mappings of pfn %lx .. start dump\n", ACTIVE_PFN_MAX_OVERLAP, ent->pfn);
> + int idx;
> +
> + for (idx = 0; idx < HASH_SIZE; idx++) {
> + struct hash_bucket *bucket = &dma_entry_hash[idx];
> + struct dma_debug_entry *entry;
> + unsigned long flags;
> +
> + list_for_each_entry(entry, &bucket->list, list) {
> + if (entry->pfn == ent->pfn) {
> + dev_info(entry->dev, "%s idx %d P=%Lx N=%lx D=%Lx L=%Lx %s %s\n",
> + type2name[entry->type], idx,
> + phys_addr(entry), entry->pfn,
> + entry->dev_addr, entry->size,
> + dir2name[entry->direction],
> + maperr2str[entry->map_err_type]);
> + }
> + }
> + }
> + dev_info(ent->dev, "DMA-API: exceeded %d overlapping mappings of pfn %lx .. end of dump\n", ACTIVE_PFN_MAX_OVERLAP, ent->pfn);
> + }
> }
>
>
> @@ -505,10 +533,10 @@ static int active_pfn_insert(struct dma_debug_entry *entry)
>
> spin_lock_irqsave(&radix_lock, flags);
> rc = radix_tree_insert(&dma_active_pfn, entry->pfn, entry);
> - if (rc == -EEXIST)
> - active_pfn_inc_overlap(entry->pfn);
> + if (rc == -EEXIST){
> + active_pfn_inc_overlap(entry);
> + }
> spin_unlock_irqrestore(&radix_lock, flags);
> -
> return rc;
> }
>
>
> This results in:
> [ 27.708678] r8169 0000:0a:00.0 eth1: link down
> [ 27.712102] r8169 0000:0a:00.0 eth1: link down
> [ 28.015340] r8169 0000:0b:00.0 eth0: link down
> [ 28.015368] r8169 0000:0b:00.0 eth0: link down
> [ 29.654844] r8169 0000:0b:00.0 eth0: link up
> [ 30.278542] r8169 0000:0a:00.0 eth1: link up
> [ 60.829503] EXT4-fs (dm-2): mounted filesystem with ordered data mode. Opts: barrier=1,errors=remount-ro
> [ 69.708979] EXT4-fs (dm-42): mounted filesystem with ordered data mode. Opts: barrier=1,errors=remount-ro
> [ 76.128678] EXT4-fs (dm-43): mounted filesystem with ordered data mode. Opts: barrier=1,errors=remount-ro
> [ 82.922836] EXT4-fs (dm-44): mounted filesystem with ordered data mode. Opts: barrier=1,errors=remount-ro
> [ 89.232889] EXT4-fs (dm-45): mounted filesystem with ordered data mode. Opts: barrier=1,errors=remount-ro
> [ 95.359859] EXT4-fs (dm-46): mounted filesystem with ordered data mode. Opts: barrier=1,errors=remount-ro
> [ 101.638559] EXT4-fs (sdb1): mounted filesystem with ordered data mode. Opts: barrier=1,errors=remount-ro
> [ 218.073407] ------------[ cut here ]------------
> [ 218.080983] WARNING: CPU: 5 PID: 0 at lib/dma-debug.c:492 add_dma_entry+0xf1/0x210()
> [ 218.088550] DMA-API: exceeded 7 overlapping mappings of pfn 3c421
> [ 218.095988] Modules linked in:
> [ 218.103270] CPU: 5 PID: 0 Comm: swapper/5 Tainted: G W 3.14.0-rc2-20140211-pcireset-net-btrevert-xenblock-dmadebug5+ #1
> [ 218.110712] Hardware name: MSI MS-7640/890FXA-GD70 (MS-7640) , BIOS V1.8B1 09/13/2010
> [ 218.118134] 0000000000000009 ffff88003fd437b8 ffffffff81b809c4 ffff88003e308000
> [ 218.125556] ffff88003fd43808 ffff88003fd437f8 ffffffff810c985c 0000000000000000
> [ 218.132917] 00000000ffffffef 0000000000000036 ffff88003d9d3c00 0000000000000282
> [ 218.140154] Call Trace:
> [ 218.147193] <IRQ> [<ffffffff81b809c4>] dump_stack+0x46/0x58
> [ 218.154271] [<ffffffff810c985c>] warn_slowpath_common+0x8c/0xc0
> [ 218.161293] [<ffffffff810c9946>] warn_slowpath_fmt+0x46/0x50
> [ 218.168227] [<ffffffff814f2cfa>] ? active_pfn_read_overlap+0x3a/0x70
> [ 218.175116] [<ffffffff814f41d1>] add_dma_entry+0xf1/0x210
> [ 218.181865] [<ffffffff814f4646>] debug_dma_map_page+0x126/0x150
> [ 218.188484] [<ffffffff817aabeb>] rtl8169_start_xmit+0x21b/0xa20
> [ 218.195042] [<ffffffff81a01877>] ? dev_queue_xmit_nit+0x1d7/0x260
> [ 218.201553] [<ffffffff81a0188f>] ? dev_queue_xmit_nit+0x1ef/0x260
> [ 218.207965] [<ffffffff81a016a5>] ? dev_queue_xmit_nit+0x5/0x260
> [ 218.214290] [<ffffffff81a0661f>] dev_hard_start_xmit+0x37f/0x590
> [ 218.220481] [<ffffffff81a26cae>] sch_direct_xmit+0xfe/0x280
> [ 218.226529] [<ffffffff81a06a7f>] __dev_queue_xmit+0x24f/0x660
> [ 218.232521] [<ffffffff81a06835>] ? __dev_queue_xmit+0x5/0x660
> [ 218.238439] [<ffffffff81ab21b9>] ? ip_output+0x59/0xf0
> [ 218.244272] [<ffffffff81a06eb0>] dev_queue_xmit+0x10/0x20
> [ 218.250043] [<ffffffff81ab076b>] ip_finish_output+0x2cb/0x670
> [ 218.255682] [<ffffffff81ab21b9>] ? ip_output+0x59/0xf0
> [ 218.261168] [<ffffffff81ab21b9>] ip_output+0x59/0xf0
> [ 218.266559] [<ffffffff81aad596>] ip_forward_finish+0x76/0x1a0
> [ 218.271883] [<ffffffff81aad86b>] ip_forward+0x1ab/0x440
> [ 218.277148] [<ffffffff81aab380>] ip_rcv_finish+0x150/0x660
> [ 218.282373] [<ffffffff81aabe3b>] ip_rcv+0x22b/0x370
> [ 218.287436] [<ffffffff81b09bc7>] ? packet_rcv_spkt+0x47/0x190
> [ 218.292372] [<ffffffff81a03272>] __netif_receive_skb_core+0x722/0x8f0
> [ 218.297328] [<ffffffff81a02c75>] ? __netif_receive_skb_core+0x125/0x8f0
> [ 218.302304] [<ffffffff8112ce6e>] ? getnstimeofday+0xe/0x30
> [ 218.307296] [<ffffffff819f42c5>] ? __netdev_alloc_frag+0x175/0x1b0
> [ 218.312166] [<ffffffff81a03461>] __netif_receive_skb+0x21/0x70
> [ 218.316904] [<ffffffff81a034d3>] netif_receive_skb_internal+0x23/0xf0
> [ 218.321596] [<ffffffff81a04d2d>] napi_gro_receive+0x8d/0x100
> [ 218.326219] [<ffffffff817a7bc3>] rtl8169_poll+0x2d3/0x680
> [ 218.330754] [<ffffffff8112e366>] ? update_wall_time+0x356/0x690
> [ 218.335208] [<ffffffff81a03a0a>] net_rx_action+0x18a/0x2c0
> [ 218.339595] [<ffffffff810ce6f1>] ? __do_softirq+0xc1/0x300
> [ 218.343890] [<ffffffff810ce767>] __do_softirq+0x137/0x300
> [ 218.348085] [<ffffffff810cec9a>] irq_exit+0xaa/0xd0
> [ 218.352203] [<ffffffff81b8e5a7>] do_IRQ+0x67/0x110
> [ 218.356225] [<ffffffff81b8b772>] common_interrupt+0x72/0x72
> [ 218.360156] <EOI> [<ffffffff810536e6>] ? native_safe_halt+0x6/0x10
> [ 218.364087] [<ffffffff81113a7d>] ? trace_hardirqs_on+0xd/0x10
> [ 218.367935] [<ffffffff81020632>] default_idle+0x32/0xd0
> [ 218.371691] [<ffffffff8102071e>] amd_e400_idle+0x4e/0x140
> [ 218.375360] [<ffffffff81020f86>] arch_cpu_idle+0x36/0x40
> [ 218.378921] [<ffffffff81120a01>] cpu_startup_entry+0xa1/0x2a0
> [ 218.382508] [<ffffffff810473cf>] start_secondary+0x1af/0x210
> [ 218.386133] ---[ end trace 0e12f271209e2c18 ]---
> [ 218.389769] r8169 0000:0b:00.0: DMA-API: exceeded 7 overlapping mappings of pfn 3c421 .. start dump
> [ 218.393566] r8169 0000:0b:00.0: single idx 563 P=3c421100 N=3c421 D=c66100 L=36 DMA_TO_DEVICE dma map error checked
> [ 218.397379] r8169 0000:0b:00.0: single idx 563 P=3c4212c0 N=3c421 D=c672c0 L=36 DMA_TO_DEVICE dma map error checked
> [ 218.401094] r8169 0000:0b:00.0: single idx 564 P=3c421480 N=3c421 D=c68480 L=36 DMA_TO_DEVICE dma map error checked
> [ 218.404730] r8169 0000:0b:00.0: single idx 564 P=3c421640 N=3c421 D=c69640 L=36 DMA_TO_DEVICE dma map error checked
> [ 218.408310] r8169 0000:0b:00.0: single idx 565 P=3c421800 N=3c421 D=c6a800 L=36 DMA_TO_DEVICE dma map error checked
> [ 218.411762] r8169 0000:0b:00.0: single idx 565 P=3c4219c0 N=3c421 D=c6b9c0 L=36 DMA_TO_DEVICE dma map error checked
> [ 218.415075] r8169 0000:0b:00.0: single idx 566 P=3c421b80 N=3c421 D=c6cb80 L=9b DMA_TO_DEVICE dma map error checked
> [ 218.418305] r8169 0000:0b:00.0: single idx 566 P=3c421dc0 N=3c421 D=c6ddc0 L=36 DMA_TO_DEVICE dma map error checked
> [ 218.421502] r8169 0000:0b:00.0: single idx 567 P=3c421f80 N=3c421 D=c6ef80 L=36 DMA_TO_DEVICE dma map error not checked

The overlap granularity is too large. Multiple dma_map_single
mappings are allowed to a given page as long as they don't collide on
the same cache line.


Please try the attached patch to see if it fixes this issue. Works ok for me.
dma debug: account for cachelines in overlap tracking

From: Dan Williams <dan.j.williams@intel.com>

While debug_dma_assert_idle() checks if a given *page* is actively
undergoing dma the valid granularity of a dma mapping is a *cacheline*.
Sander's testing shows that the warning message "DMA-API: exceeded 7
overlapping mappings of pfn..." is falsely triggering. The test is
simply mapping multiple cachelines in a given page.

Ultimately we want overlap tracking to be valid as it is a real api
violation, to that end we need to track active mappings by cachelines.
To this end, update the active dma tracking to use the
page-frame-relative cacheline of the mapping as the key, and update
debug_dma_assert_idle() to check for all possible mapped cachelines for
a given page.

Note, the radix gang lookup is suboptimal. It would be best if it
stopped fetching entries once the search passed a page boundary.
Nevertheless, this implementation does not perturb the original net_dma
failing case. That is to say the extra overhead does not show up in
terms of making the failing case pass due to a timing change.

Reported-by: Sander Eikelenboom <linux@eikelenboom.it>
Reported-by: Dave Jones <davej@redhat.com>
Cc: Konrad Rzeszutek Wilk <konrad.wilk@oracle.com>
Cc: Francois Romieu <romieu@fr.zoreil.com>
Cc: Eric Dumazet <eric.dumazet@gmail.com>
Cc: Wei Liu <wei.liu2@citrix.com>
Signed-off-by: Dan Williams <dan.j.williams@intel.com>
---
lib/dma-debug.c | 112 +++++++++++++++++++++++++++++++++----------------------
1 files changed, 67 insertions(+), 45 deletions(-)

diff --git a/lib/dma-debug.c b/lib/dma-debug.c
index 2defd1308b04..42b12740940b 100644
--- a/lib/dma-debug.c
+++ b/lib/dma-debug.c
@@ -424,111 +424,121 @@ void debug_dma_dump_mappings(struct device *dev)
EXPORT_SYMBOL(debug_dma_dump_mappings);

/*
- * For each page mapped (initial page in the case of
- * dma_alloc_coherent/dma_map_{single|page}, or each page in a
- * scatterlist) insert into this tree using the pfn as the key. At
+ * For each mapping (initial cacheline in the case of
+ * dma_alloc_coherent/dma_map_page, initial cacheline in each page of a
+ * scatterlist, or the cacheline specified in dma_map_single) insert
+ * into this tree using the cacheline as the key. At
* dma_unmap_{single|sg|page} or dma_free_coherent delete the entry. If
- * the pfn already exists at insertion time add a tag as a reference
+ * the entry already exists at insertion time add a tag as a reference
* count for the overlapping mappings. For now, the overlap tracking
- * just ensures that 'unmaps' balance 'maps' before marking the pfn
- * idle, but we should also be flagging overlaps as an API violation.
+ * just ensures that 'unmaps' balance 'maps' before marking the
+ * cacheline idle, but we should also be flagging overlaps as an API
+ * violation.
*
* Memory usage is mostly constrained by the maximum number of available
* dma-debug entries in that we need a free dma_debug_entry before
- * inserting into the tree. In the case of dma_map_{single|page} and
- * dma_alloc_coherent there is only one dma_debug_entry and one pfn to
- * track per event. dma_map_sg(), on the other hand,
- * consumes a single dma_debug_entry, but inserts 'nents' entries into
- * the tree.
+ * inserting into the tree. In the case of dma_map_page and
+ * dma_alloc_coherent there is only one dma_debug_entry and one
+ * dma_active_cacheline entry to track per event. dma_map_sg(), on the
+ * other hand, consumes a single dma_debug_entry, but inserts 'nents'
+ * entries into the tree.
*
* At any time debug_dma_assert_idle() can be called to trigger a
- * warning if the given page is in the active set.
+ * warning if any cachelines in the given page are in the active set.
*/
-static RADIX_TREE(dma_active_pfn, GFP_NOWAIT);
+static RADIX_TREE(dma_active_cacheline, GFP_NOWAIT);
static DEFINE_SPINLOCK(radix_lock);
-#define ACTIVE_PFN_MAX_OVERLAP ((1 << RADIX_TREE_MAX_TAGS) - 1)
+#define ACTIVE_CLN_MAX_OVERLAP ((1 << RADIX_TREE_MAX_TAGS) - 1)
+#define CACHELINE_PER_PAGE_SHIFT (PAGE_SHIFT - L1_CACHE_SHIFT)
+#define CACHELINES_PER_PAGE (1 << CACHELINE_PER_PAGE_SHIFT)

-static int active_pfn_read_overlap(unsigned long pfn)
+unsigned long to_cln(struct dma_debug_entry *entry)
+{
+ return (entry->pfn << CACHELINE_PER_PAGE_SHIFT) +
+ (entry->offset >> L1_CACHE_SHIFT);
+}
+
+static int active_cln_read_overlap(unsigned long cln)
{
int overlap = 0, i;

for (i = RADIX_TREE_MAX_TAGS - 1; i >= 0; i--)
- if (radix_tree_tag_get(&dma_active_pfn, pfn, i))
+ if (radix_tree_tag_get(&dma_active_cacheline, cln, i))
overlap |= 1 << i;
return overlap;
}

-static int active_pfn_set_overlap(unsigned long pfn, int overlap)
+static int active_cln_set_overlap(unsigned long cln, int overlap)
{
int i;

- if (overlap > ACTIVE_PFN_MAX_OVERLAP || overlap < 0)
+ if (overlap > ACTIVE_CLN_MAX_OVERLAP || overlap < 0)
return overlap;

for (i = RADIX_TREE_MAX_TAGS - 1; i >= 0; i--)
if (overlap & 1 << i)
- radix_tree_tag_set(&dma_active_pfn, pfn, i);
+ radix_tree_tag_set(&dma_active_cacheline, cln, i);
else
- radix_tree_tag_clear(&dma_active_pfn, pfn, i);
+ radix_tree_tag_clear(&dma_active_cacheline, cln, i);

return overlap;
}

-static void active_pfn_inc_overlap(unsigned long pfn)
+static void active_cln_inc_overlap(unsigned long cln)
{
- int overlap = active_pfn_read_overlap(pfn);
+ int overlap = active_cln_read_overlap(cln);

- overlap = active_pfn_set_overlap(pfn, ++overlap);
+ overlap = active_cln_set_overlap(cln, ++overlap);

/* If we overflowed the overlap counter then we're potentially
* leaking dma-mappings. Otherwise, if maps and unmaps are
* balanced then this overflow may cause false negatives in
- * debug_dma_assert_idle() as the pfn may be marked idle
+ * debug_dma_assert_idle() as the cln may be marked idle
* prematurely.
*/
- WARN_ONCE(overlap > ACTIVE_PFN_MAX_OVERLAP,
- "DMA-API: exceeded %d overlapping mappings of pfn %lx\n",
- ACTIVE_PFN_MAX_OVERLAP, pfn);
+ WARN_ONCE(overlap > ACTIVE_CLN_MAX_OVERLAP,
+ "DMA-API: exceeded %d overlapping mappings of cln %lx\n",
+ ACTIVE_CLN_MAX_OVERLAP, cln);
}

-static int active_pfn_dec_overlap(unsigned long pfn)
+static int active_cln_dec_overlap(unsigned long cln)
{
- int overlap = active_pfn_read_overlap(pfn);
+ int overlap = active_cln_read_overlap(cln);

- return active_pfn_set_overlap(pfn, --overlap);
+ return active_cln_set_overlap(cln, --overlap);
}

-static int active_pfn_insert(struct dma_debug_entry *entry)
+static int active_cln_insert(struct dma_debug_entry *entry)
{
unsigned long flags;
int rc;

spin_lock_irqsave(&radix_lock, flags);
- rc = radix_tree_insert(&dma_active_pfn, entry->pfn, entry);
+ rc = radix_tree_insert(&dma_active_cacheline, to_cln(entry), entry);
if (rc == -EEXIST)
- active_pfn_inc_overlap(entry->pfn);
+ active_cln_inc_overlap(to_cln(entry));
spin_unlock_irqrestore(&radix_lock, flags);

return rc;
}

-static void active_pfn_remove(struct dma_debug_entry *entry)
+static void active_cln_remove(struct dma_debug_entry *entry)
{
unsigned long flags;

spin_lock_irqsave(&radix_lock, flags);
/* 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
+ * cacheline will occur when the overlap count is 0.
+ * active_cln_dec_overlap() returns -1 in that case
*/
- if (active_pfn_dec_overlap(entry->pfn) < 0)
- radix_tree_delete(&dma_active_pfn, entry->pfn);
+ if (active_cln_dec_overlap(to_cln(entry)) < 0)
+ radix_tree_delete(&dma_active_cacheline, to_cln(entry));
spin_unlock_irqrestore(&radix_lock, flags);
}

/**
* debug_dma_assert_idle() - assert that a page is not undergoing dma
- * @page: page to lookup in the dma_active_pfn tree
+ * @page: page to lookup in the dma_active_cacheline tree
*
* Place a call to this routine in cases where the cpu touching the page
* before the dma completes (page is dma_unmapped) will lead to data
@@ -536,14 +546,26 @@ static void active_pfn_remove(struct dma_debug_entry *entry)
*/
void debug_dma_assert_idle(struct page *page)
{
+ unsigned long cln = page_to_pfn(page) << CACHELINE_PER_PAGE_SHIFT;
+ static struct dma_debug_entry *ents[CACHELINES_PER_PAGE];
+ struct dma_debug_entry *entry = NULL;
+ void **results = (void **) &ents;
+ unsigned int nents, i;
unsigned long flags;
- struct dma_debug_entry *entry;

if (!page)
return;

spin_lock_irqsave(&radix_lock, flags);
- entry = radix_tree_lookup(&dma_active_pfn, page_to_pfn(page));
+ nents = radix_tree_gang_lookup(&dma_active_cacheline, results, cln,
+ CACHELINES_PER_PAGE);
+ for (i = 0; i < nents; i++) {
+ if (to_cln(ents[i]) == cln) {
+ entry = ents[i];
+ break;
+ } else if (to_cln(ents[i]) >= cln + CACHELINES_PER_PAGE)
+ break;
+ }
spin_unlock_irqrestore(&radix_lock, flags);

if (!entry)
@@ -551,7 +573,7 @@ void debug_dma_assert_idle(struct page *page)

err_printk(entry->dev, entry,
"DMA-API: cpu touching an active dma mapped page "
- "[pfn=0x%lx]\n", entry->pfn);
+ "[cln=0x%lx]\n", to_cln(entry));
}

/*
@@ -568,9 +590,9 @@ static void add_dma_entry(struct dma_debug_entry *entry)
hash_bucket_add(bucket, entry);
put_hash_bucket(bucket, &flags);

- rc = active_pfn_insert(entry);
+ rc = active_cln_insert(entry);
if (rc == -ENOMEM) {
- pr_err("DMA-API: pfn tracking ENOMEM, dma-debug disabled\n");
+ pr_err("DMA-API: cacheline tracking ENOMEM, dma-debug disabled\n");
global_disable = true;
}

@@ -631,7 +653,7 @@ static void dma_entry_free(struct dma_debug_entry *entry)
{
unsigned long flags;

- active_pfn_remove(entry);
+ active_cln_remove(entry);

/*
* add to beginning of the list - this way the entries are
\
 
 \ /
  Last update: 2014-02-12 03:41    [W:0.159 / U:0.668 seconds]
©2003-2020 Jasper Spaans|hosted at Digital Ocean and TransIP|Read the blog|Advertise on this site