NetBSD-Bugs archive
[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index][Old Index]
Subject: Re: port-xen/45975: panic: HYPERVISOR_mmu_update failed, ret: -22 on -current MP domU (amd64)
The following reply was made to PR port-xen/45975; it has been noted by GNATS.
From: Cherry G. Mathew <cherry%zyx.in@localhost>
To: gnats-bugs%NetBSD.org@localhost
Cc: port-xen-maintainer%NetBSD.org@localhost, gnats-admin%NetBSD.org@localhost,
netbsd-bugs%NetBSD.org@localhost
Subject: Subject: Re: port-xen/45975: panic: HYPERVISOR_mmu_update failed, ret:
-22 on -current MP domU (amd64)
Date: Tue, 14 Feb 2012 23:41:02 +0530
Hi Manuel,
> I've made progress on this; I think I understood what's going on and
> I have a fix.
>
>
> The page is inded still used as a page table; it's still in another CPU's
> ci_kpm_pdir. The reason is that xen_kpm_sync() is not working as expected,
> leading to races between CPUs.
> 1 the check (xpq_cpu != &x86_curcpu) is always false because we
> have different x86_curcpu symbols with different addresses in the kernel.
> Fortunably, all addresses dissaemble to the same code.
Ok, this is my messup - I was looking for a way to not use %gs/%fs
before they are setup via cpu_init_msrs(). The reason it doesn't work,
as dh pointed out elsewhere, is because x86_curcpu() is defined in a
header as static inline.
>
> 3 pmap->pm_cpus is not safe for the purpose of xen_kpm_sync(), which
> needs to know on which CPU a pmap is loaded *now*:
> pmap->pm_cpus is cleared before cpu_load_pmap() is called to switch
> to a new pmap, leaving a window where a pmap is still in a CPU's
> ci_kpm_pdir but not in pm_cpus. As a virtual CPU may be preempted
> by the hypervisor at any time, it can be large enough to let another
> CPU free the PTP and reuse it as a normal page.
>
Makes sense.
>
> To fix 2) I choose to avoid cross-calls and IPIs completely, and instead
> use a mutex to update all CPU's ci_kpm_pdir from the local CPU.
> It's safe because we just need to update the table page, a tlbflush IPI will
> happen later. As a side effect, we don't need a different code for bootstrap.
>
>
> to fix 3), I introduced a pm_xen_ptp_cpus which is updated from
> cpu_pmap_load(), whith the ci_kpm_mtx mutex held. Checking it with
> ci_kpm_mtx held will avoid overwriting the wrong pmap's ci_kpm_pdir.
>
>
> While there I removed the unused pmap_is_active() function;
> and added some more details to DIAGNOSTIC panics.
>
Can we add these broken down into separate patch sets ?
>
> The attached patch implements this; it has been tested on 4-CPUs domU
> (on a physical dual-core box, so the race described in 2) is more likely to
> happen) and I couldn't trigger the panic any more (a build.sh -j8 release
> would never complete before for me).
>
...
> @@ -387,68 +360,30 @@ pmap_kpm_sync_xcall(void *arg1, void *ar
> void
> xen_kpm_sync(struct pmap *pmap, int index)
> {
> - uint64_t where;
> + CPU_INFO_ITERATOR cii;
> + struct cpu_info *ci;
>
>
> KASSERT(pmap != NULL);
>
>
> pmap_pte_flush();
>
>
> - if (__predict_false(xpq_cpu != &x86_curcpu)) { /* Too early to xcall
> */
> - CPU_INFO_ITERATOR cii;
> - struct cpu_info *ci;
> - int s = splvm();
> - for (CPU_INFO_FOREACH(cii, ci)) {
> - if (ci == NULL) {
> - continue;
> - }
> - if (pmap == pmap_kernel() ||
> - ci->ci_cpumask & pmap->pm_cpus) {
> - pmap_kpm_setpte(ci, pmap, index);
> - }
> + for (CPU_INFO_FOREACH(cii, ci)) {
> + if (ci == NULL) {
> + continue;
> }
> - pmap_pte_flush();
> - splx(s);
> - return;
> - }
> -
> - if (pmap == pmap_kernel()) {
> - where = xc_broadcast(XC_HIGHPRI,
> - pmap_kpm_sync_xcall, pmap, &index);
> - xc_wait(where);
> - } else {
> - KASSERT(mutex_owned(pmap->pm_lock));
> - KASSERT(kpreempt_disabled());
> -
> - CPU_INFO_ITERATOR cii;
> - struct cpu_info *ci;
> - for (CPU_INFO_FOREACH(cii, ci)) {
> - if (ci == NULL) {
> - continue;
> - }
> - while (ci->ci_cpumask & pmap->pm_cpus) {
> -#ifdef MULTIPROCESSOR
> -#define CPU_IS_CURCPU(ci) __predict_false((ci) == curcpu())
> -#else /* MULTIPROCESSOR */
> -#define CPU_IS_CURCPU(ci) __predict_true((ci) == curcpu())
> -#endif /* MULTIPROCESSOR */
> -#if 0 /* XXX: Race with remote pmap_load() */
> - if (ci->ci_want_pmapload &&
> - !CPU_IS_CURCPU(ci)) {
> - /*
> - * XXX: make this more cpu
> - * cycle friendly/co-operate
> - * with pmap_load()
> - */
> - continue;
> - }
> -#endif /* 0 */
> - where = xc_unicast(XC_HIGHPRI,
> pmap_kpm_sync_xcall,
> - pmap, &index, ci);
> - xc_wait(where);
> - break;
> - }
> + if (pmap != pmap_kernel() &&
> + (ci->ci_cpumask & pmap->pm_xen_ptp_cpus) == 0)
> + continue;
> +
> + /* take the lock and check again */
> + mutex_enter(&ci->ci_kpm_mtx);
> + if (pmap == pmap_kernel() ||
> + (ci->ci_cpumask & pmap->pm_xen_ptp_cpus) != 0) {
> + pmap_kpm_setpte(ci, pmap, index);
Isn't a tlb shootdown needed after this, to make sure the old pte is not
referred to in the TLB ?
Separately, please note that the tlb shootdown code ignores requests to
shootdown when the pmap is being destroyed.
http://nxr.netbsd.org/xref/src/sys/arch/x86/x86/pmap_tlb.c#222
I have a hypothesis that stale TLB entries involving PTPs in the
ci_kpm_pdir[] may be responsible for the "other" bug that riz@ is seeing
?
Cheers,
Cherry.
Home |
Main Index |
Thread Index |
Old Index