Re: [PATCH 3/5] mm/mremap: use pmd_addr_end to calculate next in move_page_tables()

Wei Yang
2020-01-30T21:57:27Z

On Thu, Jan 30, 2020 at 02:15:05PM +0000, Russell King - ARM Linux admin wrote:
>On Thu, Jan 30, 2020 at 09:30:00AM +0800, Wei Yang wrote:
>> On Wed, Jan 29, 2020 at 11:24:41PM +0000, Russell King - ARM Linux admin wrote:
>> >On Thu, Jan 30, 2020 at 05:57:45AM +0800, Wei Yang wrote:
>> >> On Wed, Jan 29, 2020 at 09:47:38AM +0000, Russell King - ARM Linux admin wrote:
>> >> >On Sun, Jan 26, 2020 at 05:47:57PM +0300, Dmitry Osipenko wrote:
>> >> >> 18.01.2020 02:22, Wei Yang пишет:
>> >> >> > Use the general helper instead of do it by hand.
>> >> >> >
>> >> >> > Signed-off-by: Wei Yang <richardw.yang-VuQAYsv1563Yd54FQh9/CA@public.gmane.org>
>> >> >> > ---
>> >> >> > mm/mremap.c | 7 ++-----
>> >> >> > 1 file changed, 2 insertions(+), 5 deletions(-)
>> >> >> >
>> >> >> > diff --git a/mm/mremap.c b/mm/mremap.c
>> >> >> > index c2af8ba4ba43..a258914f3ee1 100644
>> >> >> > --- a/mm/mremap.c
>> >> >> > +++ b/mm/mremap.c
>> >> >> > @@ -253,11 +253,8 @@ unsigned long move_page_tables(struct vm_area_struct *vma,
>> >> >> >
>> >> >> > for (; old_addr < old_end; old_addr += extent, new_addr += extent) {
>> >> >> > cond_resched();
>> >> >> > - next = (old_addr + PMD_SIZE) & PMD_MASK;
>> >> >> > - /* even if next overflowed, extent below will be ok */
>> >> >> > + next = pmd_addr_end(old_addr, old_end);
>> >> >> > extent = next - old_addr;
>> >> >> > - if (extent > old_end - old_addr)
>> >> >> > - extent = old_end - old_addr;
>> >> >> > old_pmd = get_old_pmd(vma->vm_mm, old_addr);
>> >> >> > if (!old_pmd)
>> >> >> > continue;
>> >> >> > @@ -301,7 +298,7 @@ unsigned long move_page_tables(struct vm_area_struct *vma,
>> >> >> >
>> >> >> > if (pte_alloc(new_vma->vm_mm, new_pmd))
>> >> >> > break;
>> >> >> > - next = (new_addr + PMD_SIZE) & PMD_MASK;
>> >> >> > + next = pmd_addr_end(new_addr, new_addr + len);
>> >> >> > if (extent > next - new_addr)
>> >> >> > extent = next - new_addr;
>> >> >> > move_ptes(vma, old_pmd, old_addr, old_addr + extent, new_vma,
>> >> >> >
>> >> >>
>> >> >> Hello Wei,
>> >> >>
>> >> >> Starting with next-20200122, I'm seeing the following in KMSG on NVIDIA
>> >> >> Tegra (ARM32):
>> >> >>
>> >> >> BUG: Bad rss-counter state mm:(ptrval) type:MM_ANONPAGES val:190
>> >> >>
>> >> >> and eventually kernel hangs.
>> >> >>
>> >> >> Git's bisection points to this patch and reverting it helps. Please fix,
>> >> >> thanks in advance.
>> >> >
>> >> >The above is definitely wrong - pXX_addr_end() are designed to be used
>> >> >with an address index within the pXX table table and the address index
>> >> >of either the last entry in the same pXX table or the beginning of the
>> >> >_next_ pXX table. Arbitary end address indicies are not allowed.
>> >> >
>> >>
>> >> #define pmd_addr_end(addr, end) \
>> >> ({ unsigned long __boundary = ((addr) + PMD_SIZE) & PMD_MASK; \
>> >> (__boundary - 1 < (end) - 1)? __boundary: (end); \
>> >> })
>> >>
>> >> If my understanding is correct, the definition here align the addr to next PMD
>> >> boundary or end.
>> >>
>> >> I don't see the possibility to across another PMD. Do I miss something?
>> >
>> >Look at the definition of p*_addr_end() that are used when page tables
>> >are rolled up.
>> >
>>
>> Sorry, I don't get your point.
>>
>> What's the meaning of "roll up" here?
>>
>> Would you mind giving me an example? I see pmd_addr_end() is not used in many
>> places in core kernel. By glancing those usages, all the places use it like
>> pmd_addr_end(addr, end). Seems no specially handing on the end address.
>>
>> Or you mean the case when pmd_addr_end() is defined to return "end" directly?
>
>Not all hardware has five levels of page tables. When hardware does not
>have five levels, it is common to "roll up" some of the page tables into
>others.
>
>There are generic ways to implement this, which include using:
>
>include/asm-generic/pgtable-nop4d.h
>include/asm-generic/pgtable-nopud.h
>include/asm-generic/pgtable-nopmd.h
>
>and then there's architecture ways to implement this. 32-bit ARM takes
>its implementation for PMD not from the generic version, which
>post-dates 32-bit ARM, but from how page table roll-up was implemented
>back at the time when the current ARM scheme was devised. The generic
>scheme is unsuitable for 32-bit ARM since we do more than just roll-up
>page tables, but this is irrelevent for this discussion.
>
>All three of the generic implementations, and 32-bit ARM, define the
>pXX_addr_end() macros thusly:
>
>include/asm-generic/pgtable-nop4d.h:#define p4d_addr_end(addr, end) (end)
>include/asm-generic/pgtable-nopmd.h:#define pmd_addr_end(addr, end) (end)
>include/asm-generic/pgtable-nopud.h:#define pud_addr_end(addr, end) (end)
>arch/arm/include/asm/pgtable-2level.h:#define pmd_addr_end(addr,end) (end)
>
>since, as I stated, pXX_addr_end() expects its "end" argument to be
>the address index of the next entry in the immediately upper page
>table level, or the address index of the last entry we wish to
>process, which ever is smaller.
>
>If it's larger than the address index of the next entry in the
>immediately upper page table level, then the effect of all these
>macros will be to walk off the end of the current level of page
>table.
>
>To see how they _should_ be used, see the loops in free_pgd_range()
>and the free_pXX_range() functions called from there and below.
>
>In all cases when the pXX_addr_end() macro was introduced, what I state
>above holds true - and I believe still holds true today, until this
>patch that has reportedly caused issues.
>

Thanks for your patience in explaining this for me.

I got your point. This is my fault in understanding the code.

>--
>RMK's Patch system: https://www.armlinux.org.uk/developer/patches/
>FTTC broadband for 0.8mile line in suburbia: sync at 12.1Mbps down 622kbps up
>According to speedtest.net: 11.9Mbps down 500kbps up

--
Wei Yang
Help you, Help me