port-mips/55062 (Failed assertion in pmap_md_tlb_check_entry())

Jason Thorpe <[email protected]>
Newsgroups gmane.os.netbsd.ports.mips.devel
Message-ID <[email protected]>
I filed this bug yesterday, although I've seen it a few times in the last couple of weeks running -current on my Qube2.  Note, the Cobalt GENERIC kernel has DEBUG turned on by default, and this assertion is in an #ifdef DEBUG code block.

The panic always looks like this:

kernel diagnostic assertion \"pte == xpte\" failed: file \"../../../../arch/mips/mips/pmap_machdep.c\", line 871 pmap=0x80641be4 va=0xc3018000 asid=0: TLB pte (0x7e01f) != real pte (0x1/0x1) @ 0x80712

It's not always exactly the same, but it follows a pattern -- TLB pte has a real value, and real pte (the PTE in the software copy of the page tables kept in the pmap structure) has has the "kernel invalid PTE" value (only the G bit set).

The assertion is called from a sanity check routine called by pmap_update().  This sanity check walks the hardware TLB to ensure that any valid hardware TLB entries match what the software copy in the pmap says.

So my initial thought was that a TLB invalidation was missing, so I copied the check to the places that remove mappings so see if I could narrow down which one it was.

Turns out it was pmap_kremove(), so I saved the start and end VA that was being removed in a couple of globals so I could examine them from DDB when the assertion tripped.  I've been able to reproduce it fairly reliably by doing a "cvs co" of the NetBSD source tree, and so:

db> x/x pmap_kremove_sva 
netbsd:pmap_kremove_sva:        c100c000
db> x/x pmap_kremove_eva
netbsd:pmap_kremove_eva:        c101c000
db> x/s panicstr
netbsd:panicstr:        4\334g\200kernel diagnostic assertion "pte == xpte" fail
ed: file "../../../../arch/mips/mips/pmap_machdep.c", line 871 pmap=0x80641d24 v
a=0xc2be2000 asid=0: TLB pte (0x3061f) != real pte (0x1/0x1) @ 0x807117c4
db> bt
0x807bdcd0: cpu_Debugger+4 (1,2d4,8067ddbc,803b931c) ra 803b9598 sz 0
0x807bdcd0: vpanic+15c (1,2d4,8067ddbc,803b931c) ra 804cf03c sz 48
0x807bdd00: kern_assert+3c (1,805540ac,80556114,80555c20) ra 800177d0 sz 32
0x807bdd20: pmap_md_tlb_check_entry+108 (1,805540ac,80556114,80555c20) ra 800178
b8 sz 80
0x807bdd70: tlb_walk+a0 (1,805540ac,80556114,80555c20) ra 8001cf3c sz 56
0x807bdda8: pmap_tlb_check+50 (1,805540ac,80556114,80555c20) ra 800195c4 sz 32
0x807bddc8: pmap_kremove+6c (1,805540ac,80556114,80555c20) ra 80325778 sz 40
0x807bddf0: uvm_pagermapout+24 (1,805540ac,80556114,80555c20) ra 803260cc sz 48
0x807bde20: uvm_aio_aiodone+a4 (1,805540ac,80556114,80555c20) ra 80408d90 sz 64
0x807bde60: biodone2+7c (1,805540ac,80556114,80555c20) ra 80408f2c sz 40
0x807bde88: biointr+a0 (1,805540ac,80556114,80555c20) ra 8037f3e0 sz 64
0x807bdec8: softint_dispatch+11c (1,805540ac,80556114,80555c20) ra 80001288 sz 1
28
0x807bdf48: softint_fast_dispatch+78 (1,805540ac,80556114,80555c20) ra 0 sz 24
User-level: pid 0.4
db>

So, what I glean from this is:

1- A 64K region is being unmapped -- probably a UBC window?

2- uvm_pagermapout() calls pmap_update() immediately after pmap_kremove(), which would have also tripped the assertion.

3- The failed VA wasn't in the region that is being unmapped by pmap_kremove().


So, if I'm doing this check IMMEDIATELY after invalidating the mapping, what could be the source of the inconsistency when removing a mapping?

Removing a mapping is actually two step process:

1- Invalidate the entry in the software copy of the page tables.

2- Invalidate the hardware TLB entry for that VA+ASID.

It's implemented in pmap_pte_remove():

                pmap_md_tlb_miss_lock_enter();
                pte_set(ptep, npte);
                if (__predict_true(!(pmap->pm_flags & PMAP_DEFERRED_ACTIVATE))) {

                        /*
                         * Flush the TLB for the given address.
                         */
                        pmap_tlb_invalidate_addr(pmap, sva);
                }
                pmap_md_tlb_miss_lock_exit();

Note the calls to pmap_md_tlb_miss_lock_enter() / pmap_md_tlb_miss_lock_exit().  These are these as hooks to handle CPUs like some powerpc booke CPUs that share a single hardware TLB among multiple logical CPUs.  Those calls are no-ops on MIPS (they are #define'd away as nothing).

On a MIPS system, there is no need to put a lock around that short window because it does't matter if it's interrupted ... the API contract is that the mapping isn't invalidated until pmap_kremove() returns...

BUT...

...note the stack trace... in every case I've hit, it's been from a (soft)intr context.

I'm pretty sure what's happening is that, when the cosmic rays align correctly, a disk i/o completion interrupt comes in between pte_set() and pmap_tlb_invalidate_addr() (or at least before the kernel-invalid entry is actually written to the hardware), which causes the TLB consistency check called from that (soft)intr context to fail.

If pmap_md_tlb_miss_lock_enter() went to splvm(), that race window would close.

Any other thoughts?

-- thorpej
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.