Re: [PATCH v6 20/37] mm: fix non-compound multi-order memory accounting in __free_pages

Suren Baghdasaryan
2024-03-21T17:19:41Z

On Thu, Mar 21, 2024 at 10:04 AM Matthew Wilcox <willy@infradead.org> wrote:
>
> On Thu, Mar 21, 2024 at 04:48:53PM +0000, Matthew Wilcox wrote:
> > On Thu, Mar 21, 2024 at 09:36:42AM -0700, Suren Baghdasaryan wrote:
> > > +++ b/mm/page_alloc.c
> > > @@ -4700,12 +4700,15 @@ void __free_pages(struct page *page, unsigned int order)
> > > {
> > > /* get PageHead before we drop reference */
> > > int head = PageHead(page);
> > > + struct alloc_tag *tag = pgalloc_tag_get(page);
> > >
> > > if (put_page_testzero(page))
> > > free_the_page(page, order);
> > > - else if (!head)
> > > + else if (!head) {
> > > + pgalloc_tag_sub_pages(tag, (1 << order) - 1);
> > > while (order-- > 0)
> > > free_the_page(page + (1 << order), order);
> > > + }
> >
> > Why do you need these new functions instead of just:
> >
> > + else if (!head) {
> > + pgalloc_tag_sub(page, (1 << order) - 1);
> > while (order-- > 0)
> > free_the_page(page + (1 << order), order);
> > + }
>
> Actually, I'm not sure this is safe (I don't fully understand codetags,
> so it may be safe). What can happen is that the put_page() can come in
> before the pgalloc_tag_sub(), and then that page can be allocated again.
> Will that cause confusion?

So, there are two reasons I unfortunately can't reuse pgalloc_tag_sub():

1. We need to subtract `bytes` counter from the codetag but not the
`calls` counter, otherwise the final accounting will be incorrect.
This is because we effectively allocated multiple pages with one call
but freeing them with separate calls here. pgalloc_tag_sub_pages()
subtracts bytes but keeps calls counter the same. I mentioned this in
here: https://lore.kernel.org/all/CAJuCfpEgh1OiYNE_uKG-BqW2x97sOL9+AaTX4Jct3=WHzAv+kg@mail.gmail.com/
2. The codetag object itself is stable, it's created at build time.
The exception is when we unload modules and the codetag section gets
freed but during module unloading we check that all module codetags
are not referenced anymore and we prevent unloading this section if
any of them are still referenced (should not normally happen). That
said, the reference to the codetag (in this case from the page_ext)
might change from under us and we have to make sure it's valid. We
ensure that here by getting the codetag itself with pgalloc_tag_get()
*before* calling put_page_testzero(), which ensures its stability.

>