linux-mm.kvack.org archive mirror
 help / color / mirror / Atom feed
From: David Hildenbrand <david@redhat.com>
To: Yu Zhao <yuzhao@google.com>, "Yin, Fengwei" <fengwei.yin@intel.com>
Cc: Minchan Kim <minchan@kernel.org>,
	linux-mm@kvack.org, linux-kernel@vger.kernel.org,
	akpm@linux-foundation.org, willy@infradead.org,
	ryan.roberts@arm.com, shy828301@gmail.com,
	"Vishal Moola (Oracle)" <vishal.moola@gmail.com>
Subject: Re: [RFC PATCH] madvise: make madvise_cold_or_pageout_pte_range() support large folio
Date: Fri, 14 Jul 2023 09:31:05 +0200	[thread overview]
Message-ID: <d024fa03-ca51-632b-9a01-bbdc75543f5d@redhat.com> (raw)
In-Reply-To: <CAOUHufY-edD2j2Nfz3xrObF2ERAGKecjFr_1Qarh5aDwyDGS2A@mail.gmail.com>

On 14.07.23 05:23, Yu Zhao wrote:
> On Thu, Jul 13, 2023 at 9:10 PM Yin, Fengwei <fengwei.yin@intel.com> wrote:
>>
>>
>>
>> On 7/14/2023 10:08 AM, Yu Zhao wrote:
>>> On Thu, Jul 13, 2023 at 9:06 AM Yin Fengwei <fengwei.yin@intel.com> wrote:
>>>>
>>>> Current madvise_cold_or_pageout_pte_range() has two problems for
>>>> large folio support:
>>>>    - Using folio_mapcount() with large folio prevent large folio from
>>>>      picking up.
>>>>    - If large folio is in the range requested, shouldn't split it
>>>>      in madvise_cold_or_pageout_pte_range().
>>>>
>>>> Fix them by:
>>>>    - Use folio_estimated_sharers() with large folio
>>>>    - If large folio is in the range requested, don't split it. Leave
>>>>      to page reclaim phase.
>>>>
>>>> For large folio cross boundaries of requested range, skip it if it's
>>>> page cache. Try to split it if it's anonymous folio. If splitting
>>>> fails, skip it.
>>>
>>> For now, we may not want to change the existing semantic (heuristic).
>>> IOW, we may want to stick to the "only owner" condition:
>>>
>>>    - if (folio_mapcount(folio) != 1)
>>>    + if (folio_entire_mapcount(folio) ||
>>>    +     (any_page_within_range_has_mapcount > 1))
>>>
>>> +Minchan Kim
>> The folio_estimated_sharers() was discussed here:
>> https://lore.kernel.org/linux-mm/20230118232219.27038-6-vishal.moola@gmail.com/
>> https://lore.kernel.org/linux-mm/20230124012210.13963-2-vishal.moola@gmail.com/
>>
>> Yes. It's accurate to check each page of large folio. But it may be over killed in
>> some cases (And I think madvise is one of the cases not necessary to be accurate.
>> So folio_estimated_sharers() is enough. Correct me if I am wrong).
> 
> I see. Then it's possible this is also what the original commit wants
> to do -- Minchan, could you clarify?
> 
> Regardless, I think we can have the following fix, potentially cc'ing stable:
> 
> -  if (folio_mapcount(folio) != 1)
> +  if (folio_estimated_sharers(folio) != 1)
> 
> Sounds good?

Adding to the discussion, currently the COW selftest always skips a 
PTE-mapped THP.


For example:

# [INFO] Anonymous memory tests in private mappings
# [RUN] Basic COW after fork() ... with base page
ok 1 No leak from parent into child
# [RUN] Basic COW after fork() ... with swapped out base page
ok 2 No leak from parent into child
# [RUN] Basic COW after fork() ... with THP
ok 3 No leak from parent into child
# [RUN] Basic COW after fork() ... with swapped-out THP
ok 4 No leak from parent into child
# [RUN] Basic COW after fork() ... with PTE-mapped THP
ok 5 No leak from parent into child
# [RUN] Basic COW after fork() ... with swapped-out, PTE-mapped THP
ok 6 # SKIP MADV_PAGEOUT did not work, is swap enabled?
...


The commit that introduced that change is:

commit 07e8c82b5eff8ef34b74210eacb8d9c4a2886b82
Author: Vishal Moola (Oracle) <vishal.moola@gmail.com>
Date:   Wed Dec 21 10:08:46 2022 -0800

     madvise: convert madvise_cold_or_pageout_pte_range() to use folios

     This change removes a number of calls to compound_head(), and saves
     1729 bytes of kernel text.



folio_mapcount(folio) is wrong, because that never works on a PTE-mapped 
THP (well, unless only a single subpage is still mapped ...).

page_mapcount(folio) was wrong, because it ignored all other subpages, 
but at least it worked in some cases.

folio_estimated_sharers(folio) is similarly wrong like page_mapcount(), 
as it's essentially a page_mapcount() of the first subpage.

(ignoring that a lockless mapcount-based check is always kind-of 
unreliable, but that's msotly acceptable for these kind of things)


So, unfortunately, page_mapcount() / folio_estimated_sharers() is best 
we can do for now, but they miss to detect some cases of sharing of the 
folio -- false negatives to detect sharing.


Ideally we want something like folio_maybe_mapped_shared(), and get rid 
of folio_estimated_sharers(), we better to guess the exact number, 
simply works towards an answer that tells us "yep, this may be mapped by 
multiple sharers" vs. "no, this is definitely not mapped by multiple 
sharers".

The "mapped" part of it indicates that this does not catch all cases of 
sharing. But it should handle most of the cases we care about.


There, we can then implement something better than what 
folio_estimated_sharers() currently does:

static inline bool folio_maybe_mapped_shared(folio)
{
	if (likely(!folio_test_large(folio)))
		return atomic_read(&folio->_mapcount) > 0;

	/* Mapped multiple times via PMD? */
	if (folio_test_pmd_mappable(folio)
		return folio_entire_mapcount() > 1;

	/*
	 * First subpage is mapped multiple times (especially also via
	 * PMDs)?
          */
	if (page_mapcount(folio_page(folio, 0) > 1)
		return true;

	/* TODO: also test last subpage? */
	
	/* Definitely shared if we're mapping a page multiple times. */
	return folio_total_mapcount(folio) > folio_nr_pages(folio);
}

There are some more things we could optimize for.



While it's better than what we have right now:

(a) It's racy. Well, it's always been racy with concurrent (un)mapping
     and splitting. But maybe we can do better.

(b) folio_total_mapcount() is currently expensive.

(c) there are still false negatives even without races.


For anon pages, we could scan all subpages and test if they are 
PageAnonExclusive, but it's also not really what we want to do here.


I have some idea to handle anon pages better to avoid any page table 
walk or subpage scan, improving (a), (b) and (c). It might work for 
pagecache pages with some more work, but it's a bit more complicated 
with the scheme I have in mind).


First step would be replacing folio->_nr_pages_mapped by 
folio->_total_mapcount. While we could eventually have 
folio->_total_mapcount in addition to folio->_nr_pages_mapped, I'm, not 
sure if we want to go down that path


That would make folio_total_mapcount() extremely fast (I'm working on a 
prototype). The downsides are that

(a) We have to make NR_ANON_MAPPED/NR_FILE_MAPPED accounting less 
precise. Easiest way to handle it: as soon as a single subpage is 
mapped, account the whole folio as mapped. After all, it's consuming 
memory, so who cares?

(b) We have to find a different way to decide when to put an anonymous 
folio on the deferred split queue in page_remove_rmap(). Some cases are 
nasty to handle: PTE-mapped THP that are shared between a parent and a 
child.

I have to do some more thinking about (b), I'd be happy about thoughts 
on that.

-- 
Cheers,

David / dhildenb



  reply	other threads:[~2023-07-14  7:31 UTC|newest]

Thread overview: 19+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2023-07-13 15:05 Yin Fengwei
2023-07-14  2:08 ` Yu Zhao
2023-07-14  3:09   ` Yin, Fengwei
2023-07-14  3:23     ` Yu Zhao
2023-07-14  7:31       ` David Hildenbrand [this message]
2023-07-14  8:34         ` Yin, Fengwei
2023-07-14  9:25           ` David Hildenbrand
2023-07-14 13:58             ` Yin, Fengwei
2023-07-14 14:12               ` David Hildenbrand
2023-07-14 14:41         ` Zi Yan
2023-07-14 15:35           ` David Hildenbrand
2023-07-17  0:15           ` Yin, Fengwei
2023-07-17 14:38             ` Zi Yan
2023-07-17 23:38               ` Yin Fengwei
2023-07-14  3:57 ` Yu Zhao
2023-07-14  5:57   ` Yin, Fengwei
2023-07-14 15:41     ` Yu Zhao
2023-07-16 23:52       ` Yin, Fengwei
2023-07-17 16:29         ` Yu Zhao

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=d024fa03-ca51-632b-9a01-bbdc75543f5d@redhat.com \
    --to=david@redhat.com \
    --cc=akpm@linux-foundation.org \
    --cc=fengwei.yin@intel.com \
    --cc=linux-kernel@vger.kernel.org \
    --cc=linux-mm@kvack.org \
    --cc=minchan@kernel.org \
    --cc=ryan.roberts@arm.com \
    --cc=shy828301@gmail.com \
    --cc=vishal.moola@gmail.com \
    --cc=willy@infradead.org \
    --cc=yuzhao@google.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox