Message ID | 237c8410-ce61-94c-4260-7318ad6a4f3@google.com (mailing list archive) |
---|---|
State | New |
Headers | show |
Series | arch: allow pte_offset_map[_lock]() to fail | expand |
Hi Hugh, Thanks for your patch! On Wed, May 10, 2023 at 6:48 AM Hugh Dickins <hughd@google.com> wrote: > In rare transient cases, not yet made possible, pte_offset_map() and > pte_offset_map_lock() may not find a page table: handle appropriately. > > Restructure cf_tlb_miss() with a pte_unmap() (previously omitted) > at label out, followed by one local_irq_restore() for all. That's a bug fix, which should be a separate patch? > > Signed-off-by: Hugh Dickins <hughd@google.com> > --- a/arch/m68k/include/asm/mmu_context.h > +++ b/arch/m68k/include/asm/mmu_context.h > @@ -99,7 +99,7 @@ static inline void load_ksp_mmu(struct task_struct *task) > p4d_t *p4d; > pud_t *pud; > pmd_t *pmd; > - pte_t *pte; > + pte_t *pte = NULL; > unsigned long mmuar; > > local_irq_save(flags); > @@ -139,7 +139,7 @@ static inline void load_ksp_mmu(struct task_struct *task) > > pte = (mmuar >= PAGE_OFFSET) ? pte_offset_kernel(pmd, mmuar) > : pte_offset_map(pmd, mmuar); > - if (pte_none(*pte) || !pte_present(*pte)) > + if (!pte || pte_none(*pte) || !pte_present(*pte)) > goto bug; If the absence of a pte is to become a non-abnormal case, it should probably jump to "end" instead, to avoid spamming the kernel log. > > set_pte(pte, pte_mkyoung(*pte)); > @@ -161,6 +161,8 @@ static inline void load_ksp_mmu(struct task_struct *task) > bug: > pr_info("ksp load failed: mm=0x%p ksp=0x08%lx\n", mm, mmuar); > end: > + if (pte && mmuar < PAGE_OFFSET) > + pte_unmap(pte); Is this also a bugfix, not mentioned in the patch description? > local_irq_restore(flags); > } > Gr{oetje,eeting}s, Geert
On Wed, 10 May 2023, Geert Uytterhoeven wrote: > Hi Hugh, > > Thanks for your patch! And thank you for looking so quickly, Geert. > > On Wed, May 10, 2023 at 6:48 AM Hugh Dickins <hughd@google.com> wrote: > > In rare transient cases, not yet made possible, pte_offset_map() and > > pte_offset_map_lock() may not find a page table: handle appropriately. > > > > Restructure cf_tlb_miss() with a pte_unmap() (previously omitted) > > at label out, followed by one local_irq_restore() for all. > > That's a bug fix, which should be a separate patch? No, that's not a bug fix for the current tree, since m68k does not offer CONFIG_HIGHPTE, so pte_unmap() is never anything but a no-op for m68k (see include/linux/pgtable.h). But I want to change pte_unmap() to do something even without CONFIG_HIGHPTE, so have to fix up any such previously harmless omissions in this series first. > > > > > Signed-off-by: Hugh Dickins <hughd@google.com> > > > > --- a/arch/m68k/include/asm/mmu_context.h > > +++ b/arch/m68k/include/asm/mmu_context.h > > @@ -99,7 +99,7 @@ static inline void load_ksp_mmu(struct task_struct *task) > > p4d_t *p4d; > > pud_t *pud; > > pmd_t *pmd; > > - pte_t *pte; > > + pte_t *pte = NULL; > > unsigned long mmuar; > > > > local_irq_save(flags); > > @@ -139,7 +139,7 @@ static inline void load_ksp_mmu(struct task_struct *task) > > > > pte = (mmuar >= PAGE_OFFSET) ? pte_offset_kernel(pmd, mmuar) > > : pte_offset_map(pmd, mmuar); > > - if (pte_none(*pte) || !pte_present(*pte)) > > + if (!pte || pte_none(*pte) || !pte_present(*pte)) > > goto bug; > > If the absence of a pte is to become a non-abnormal case, it should > probably jump to "end" instead, to avoid spamming the kernel log. I don't think so (but of course it's hard for you to tell, without seeing all completed series of series). If pmd_none(*pmd) can safely goto bug just above, and pte_none(*pte) goto bug here, well, the !pte case is going to be stranger than either of those. My understanding of this function, load_ksp_mmu(), is that it's dealing at context switch with a part of userspace which very much needs to be present: whatever keeps that from being swapped out or migrated at present, will be sure to keep the !pte case away - we cannot steal its page table just at random (and a THP on m68k would be surprising too). Though there is one case I can think of which will cause !pte here, and so goto bug: if the pmd entry has got corrupted, and counts as pmd_bad(), which will be tested (and cleared) in pte_offset_map(). But it is okay to report a bug in that case. I can certainly change this to goto end instead if you still prefer, no problem; but I'd rather keep it as is, if only for me to be proved wrong by you actually seeing spam there. > > > > > set_pte(pte, pte_mkyoung(*pte)); > > @@ -161,6 +161,8 @@ static inline void load_ksp_mmu(struct task_struct *task) > > bug: > > pr_info("ksp load failed: mm=0x%p ksp=0x08%lx\n", mm, mmuar); > > end: > > + if (pte && mmuar < PAGE_OFFSET) > > + pte_unmap(pte); > > Is this also a bugfix, not mentioned in the patch description? I'm not sure whether you're referring to the pte_unmap() which we already discussed above, or you're seeing something else in addition; but I don't think there's a bugfix here, just a rearrangement because we now want lots of cases to do the pte_unmap() and local_irq_restore(). Hugh > > > local_irq_restore(flags); > > } > > > > Gr{oetje,eeting}s, > > Geert > > -- > Geert Uytterhoeven -- There's lots of Linux beyond ia32 -- geert@linux-m68k.org > > In personal conversations with technical people, I call myself a hacker. But > when I'm talking to journalists I just say "programmer" or something like that. > -- Linus Torvalds
Hi Hugh, On Thu, May 11, 2023 at 4:58 AM Hugh Dickins <hughd@google.com> wrote: > On Wed, 10 May 2023, Geert Uytterhoeven wrote: > > On Wed, May 10, 2023 at 6:48 AM Hugh Dickins <hughd@google.com> wrote: > > > In rare transient cases, not yet made possible, pte_offset_map() and > > > pte_offset_map_lock() may not find a page table: handle appropriately. > > > > > > Restructure cf_tlb_miss() with a pte_unmap() (previously omitted) > > > at label out, followed by one local_irq_restore() for all. > > > > That's a bug fix, which should be a separate patch? > > No, that's not a bug fix for the current tree, since m68k does not > offer CONFIG_HIGHPTE, so pte_unmap() is never anything but a no-op > for m68k (see include/linux/pgtable.h). > > But I want to change pte_unmap() to do something even without > CONFIG_HIGHPTE, so have to fix up any such previously harmless > omissions in this series first. OK. > > > --- a/arch/m68k/include/asm/mmu_context.h > > > +++ b/arch/m68k/include/asm/mmu_context.h > > > @@ -99,7 +99,7 @@ static inline void load_ksp_mmu(struct task_struct *task) > > > p4d_t *p4d; > > > pud_t *pud; > > > pmd_t *pmd; > > > - pte_t *pte; > > > + pte_t *pte = NULL; > > > unsigned long mmuar; > > > > > > local_irq_save(flags); > > > @@ -139,7 +139,7 @@ static inline void load_ksp_mmu(struct task_struct *task) > > > > > > pte = (mmuar >= PAGE_OFFSET) ? pte_offset_kernel(pmd, mmuar) > > > : pte_offset_map(pmd, mmuar); > > > - if (pte_none(*pte) || !pte_present(*pte)) > > > + if (!pte || pte_none(*pte) || !pte_present(*pte)) > > > goto bug; > > > > If the absence of a pte is to become a non-abnormal case, it should > > probably jump to "end" instead, to avoid spamming the kernel log. > > I don't think so (but of course it's hard for you to tell, without > seeing all completed series of series). If pmd_none(*pmd) can safely > goto bug just above, and pte_none(*pte) goto bug here, well, the !pte > case is going to be stranger than either of those. > > My understanding of this function, load_ksp_mmu(), is that it's dealing > at context switch with a part of userspace which very much needs to be > present: whatever keeps that from being swapped out or migrated at > present, will be sure to keep the !pte case away - we cannot steal its > page table just at random (and a THP on m68k would be surprising too). > > Though there is one case I can think of which will cause !pte here, > and so goto bug: if the pmd entry has got corrupted, and counts as > pmd_bad(), which will be tested (and cleared) in pte_offset_map(). > But it is okay to report a bug in that case. > > I can certainly change this to goto end instead if you still prefer, > no problem; but I'd rather keep it as is, if only for me to be proved > wrong by you actually seeing spam there. OK, makes sense. > > > @@ -161,6 +161,8 @@ static inline void load_ksp_mmu(struct task_struct *task) > > > bug: > > > pr_info("ksp load failed: mm=0x%p ksp=0x08%lx\n", mm, mmuar); > > > end: > > > + if (pte && mmuar < PAGE_OFFSET) > > > + pte_unmap(pte); > > > > Is this also a bugfix, not mentioned in the patch description? > > I'm not sure whether you're referring to the pte_unmap() which we > already discussed above, or you're seeing something else in addition; > but I don't think there's a bugfix here, just a rearrangement because > we now want lots of cases to do the pte_unmap() and local_irq_restore(). I was referring to the addition of pte_unmap(). As per your explanation above, this is not a bugfix. Gr{oetje,eeting}s, Geert
diff --git a/arch/m68k/include/asm/mmu_context.h b/arch/m68k/include/asm/mmu_context.h index 8ed6ac14d99f..141bbdfad960 100644 --- a/arch/m68k/include/asm/mmu_context.h +++ b/arch/m68k/include/asm/mmu_context.h @@ -99,7 +99,7 @@ static inline void load_ksp_mmu(struct task_struct *task) p4d_t *p4d; pud_t *pud; pmd_t *pmd; - pte_t *pte; + pte_t *pte = NULL; unsigned long mmuar; local_irq_save(flags); @@ -139,7 +139,7 @@ static inline void load_ksp_mmu(struct task_struct *task) pte = (mmuar >= PAGE_OFFSET) ? pte_offset_kernel(pmd, mmuar) : pte_offset_map(pmd, mmuar); - if (pte_none(*pte) || !pte_present(*pte)) + if (!pte || pte_none(*pte) || !pte_present(*pte)) goto bug; set_pte(pte, pte_mkyoung(*pte)); @@ -161,6 +161,8 @@ static inline void load_ksp_mmu(struct task_struct *task) bug: pr_info("ksp load failed: mm=0x%p ksp=0x08%lx\n", mm, mmuar); end: + if (pte && mmuar < PAGE_OFFSET) + pte_unmap(pte); local_irq_restore(flags); } diff --git a/arch/m68k/kernel/sys_m68k.c b/arch/m68k/kernel/sys_m68k.c index bd0274c7592e..c586034d2a7a 100644 --- a/arch/m68k/kernel/sys_m68k.c +++ b/arch/m68k/kernel/sys_m68k.c @@ -488,6 +488,8 @@ sys_atomic_cmpxchg_32(unsigned long newval, int oldval, int d3, int d4, int d5, if (!pmd_present(*pmd)) goto bad_access; pte = pte_offset_map_lock(mm, pmd, (unsigned long)mem, &ptl); + if (!pte) + goto bad_access; if (!pte_present(*pte) || !pte_dirty(*pte) || !pte_write(*pte)) { pte_unmap_unlock(pte, ptl); diff --git a/arch/m68k/mm/mcfmmu.c b/arch/m68k/mm/mcfmmu.c index 70aa0979e027..42f45abea37a 100644 --- a/arch/m68k/mm/mcfmmu.c +++ b/arch/m68k/mm/mcfmmu.c @@ -91,7 +91,8 @@ int cf_tlb_miss(struct pt_regs *regs, int write, int dtlb, int extension_word) p4d_t *p4d; pud_t *pud; pmd_t *pmd; - pte_t *pte; + pte_t *pte = NULL; + int ret = -1; int asid; local_irq_save(flags); @@ -100,47 +101,33 @@ int cf_tlb_miss(struct pt_regs *regs, int write, int dtlb, int extension_word) regs->pc + (extension_word * sizeof(long)); mm = (!user_mode(regs) && KMAPAREA(mmuar)) ? &init_mm : current->mm; - if (!mm) { - local_irq_restore(flags); - return -1; - } + if (!mm) + goto out; pgd = pgd_offset(mm, mmuar); - if (pgd_none(*pgd)) { - local_irq_restore(flags); - return -1; - } + if (pgd_none(*pgd)) + goto out; p4d = p4d_offset(pgd, mmuar); - if (p4d_none(*p4d)) { - local_irq_restore(flags); - return -1; - } + if (p4d_none(*p4d)) + goto out; pud = pud_offset(p4d, mmuar); - if (pud_none(*pud)) { - local_irq_restore(flags); - return -1; - } + if (pud_none(*pud)) + goto out; pmd = pmd_offset(pud, mmuar); - if (pmd_none(*pmd)) { - local_irq_restore(flags); - return -1; - } + if (pmd_none(*pmd)) + goto out; pte = (KMAPAREA(mmuar)) ? pte_offset_kernel(pmd, mmuar) : pte_offset_map(pmd, mmuar); - if (pte_none(*pte) || !pte_present(*pte)) { - local_irq_restore(flags); - return -1; - } + if (!pte || pte_none(*pte) || !pte_present(*pte)) + goto out; if (write) { - if (!pte_write(*pte)) { - local_irq_restore(flags); - return -1; - } + if (!pte_write(*pte)) + goto out; set_pte(pte, pte_mkdirty(*pte)); } @@ -161,9 +148,12 @@ int cf_tlb_miss(struct pt_regs *regs, int write, int dtlb, int extension_word) mmu_write(MMUOR, MMUOR_ACC | MMUOR_UAA); else mmu_write(MMUOR, MMUOR_ITLB | MMUOR_ACC | MMUOR_UAA); - + ret = 0; +out: + if (pte && !KMAPAREA(mmuar)) + pte_unmap(pte); local_irq_restore(flags); - return 0; + return ret; } void __init cf_bootmem_alloc(void)
In rare transient cases, not yet made possible, pte_offset_map() and pte_offset_map_lock() may not find a page table: handle appropriately. Restructure cf_tlb_miss() with a pte_unmap() (previously omitted) at label out, followed by one local_irq_restore() for all. Signed-off-by: Hugh Dickins <hughd@google.com> --- arch/m68k/include/asm/mmu_context.h | 6 ++-- arch/m68k/kernel/sys_m68k.c | 2 ++ arch/m68k/mm/mcfmmu.c | 52 ++++++++++++----------------- 3 files changed, 27 insertions(+), 33 deletions(-)