> Date: Wed, 19 Dec 2018 22:36:49 -0500
> From: George Koehler <[email protected]>
> 
> On Wed, 19 Dec 2018 00:13:13 -0500
> George Koehler <[email protected]> wrote:
> 
> > By my guess, pmap_is_modified() is broken on powerpc.  It seems to
> > fail to return true, so uvm wouldn't know that the page is modified,
> > and then uvm wouldn't write the modified page to swap.
> 
> Turns out that pmap_page_protect(pg, PROT_NONE) forgot to check if
> the page was modified.  The below diff seems to fix it.
> 
> pmap_is_modified(pg) calls pmap_test_attrs(pg, PG_PMAP_MOD), which
> iterates pg->mdpage.pv_list to check if the hardware had set
> PTE_CHG_32 on any mapping of this physical page.  The list is empty
> because uvm seems to remove all the mappings before it asks if the
> page is modified, then swaps out the page.
> 
> A function that does LIST_REMOVE a mapping with PTE_CHG_32 must set
> PG_PMAP_MOD in pg->pg_flags, but pmap_page_protect(pg, PROT_NONE)
> fails to do so.  This function does
>                       pted->pted_va &= ~PTED_VA_MANAGED_M;
>                       LIST_REMOVE(pted, pted_pv_list);
> before
>                       pmap_remove_pted(pm, pted);
> Then pmap_remove_pted() calls pte_zap(), which would check PTE_CHG_32
> if PTED_MANAGED(pted) is true.  The check is skipped because we had
> unset PTED_VA_MANAGED_M.
> 
> The following diff fixes my test program on my single G4 processor
> (MPC7447A), where ppc_proc_is_64b is false.  I will use this diff
> until someone gives me a better diff.

That's a very good find.  I think there still is a potential race in
your diff on MP systems since you save the bits before removing the
PTE from the has tables.  I'll see if I can come up with a better diff.

> Index: pmap.c
> ===================================================================
> RCS file: /cvs/src/sys/arch/powerpc/powerpc/pmap.c,v
> retrieving revision 1.168
> diff -u -p -r1.168 pmap.c
> --- pmap.c    22 Oct 2018 17:31:25 -0000      1.168
> +++ pmap.c    20 Dec 2018 03:16:13 -0000
> @@ -540,6 +540,28 @@ pmap_attr_save(paddr_t pa, u_int32_t bit
>       atomic_setbits_int(&pg->pg_flags,  pmap_pte2flags(bits));
>  }
>  
> +/* Fixes pg->pg_flags by checking REF and CHG. */
> +void
> +pmap_fix_page_attrs(struct vm_page *pg, struct pte_desc *pted)
> +{
> +     void *pte;
> +     u_int bits;
> +     int s;
> +
> +     PMAP_HASH_LOCK(s);
> +     if ((pte = pmap_ptedinhash(pted)) != NULL) {
> +             if (ppc_proc_is_64b) {
> +                     struct pte_64 *pte64 = pte;
> +                     bits = pte64->pte_lo & (PTE_REF_64|PTE_CHG_64);
> +             } else {
> +                     struct pte_32 *pte32 = pte;
> +                     bits = pte32->pte_lo & (PTE_REF_32|PTE_CHG_32);
> +             }
> +             atomic_setbits_int(&pg->pg_flags, pmap_pte2flags(bits));
> +     }
> +     PMAP_HASH_UNLOCK(s);
> +}
> +
>  int
>  pmap_enter(pmap_t pm, vaddr_t va, paddr_t pa, vm_prot_t prot, int flags)
>  {
> @@ -2101,6 +2123,13 @@ pmap_page_protect(struct vm_page *pg, vm
>                       pted->pted_va &= ~PTED_VA_MANAGED_M;
>                       LIST_REMOVE(pted, pted_pv_list);
>                       mtx_leave(&pg->mdpage.pv_mtx);
> +
> +                     /*
> +                      * Check REF and CHG now, because
> +                      * pmap_remove_pted() won't check them when
> +                      * not PTED_VA_MANAGED_M.
> +                      */
> +                     pmap_fix_page_attrs(pg, pted);
>  
>                       pmap_remove_pted(pm, pted);
>  
> 
> -- 
> George Koehler <[email protected]>
> 
> 

Reply via email to