On Wed, Mar 13, 2024 at 3:04 PM Matthew Wilcox <willy@xxxxxxxxxxxxx> wrote: > > On Wed, Mar 06, 2024 at 10:24:18AM -0800, Suren Baghdasaryan wrote: > > When a non-compound multi-order page is freed, it is possible that a > > speculative reference keeps the page pinned. In this case we free all > > pages except for the first page, which will be freed later by the last > > put_page(). However put_page() ignores the order of the page being freed, > > treating it as a 0-order page. This creates a memory accounting imbalance > > because the pages freed in __free_pages() do not have their own alloc_tag > > and their memory was accounted to the first page. To fix this the first > > page should adjust its allocation size counter when "tail" pages are freed. > > It's not "ignored". It's not available! > > Better wording: > > However the page passed to put_page() is indisinguishable from an > order-0 page, so it cannot do the accounting, just as it cannot free > the subsequent pages. Do the accounting here, where we free the pages. > > (I'm sure further improvements are possible) > > > +static inline void pgalloc_tag_sub_bytes(struct alloc_tag *tag, unsigned int order) > > +{ > > + if (mem_alloc_profiling_enabled() && tag) > > + this_cpu_sub(tag->counters->bytes, PAGE_SIZE << order); > > +} > > This is a terribly named function. And it's not even good for what we > want to use it for. > > static inline void pgalloc_tag_sub_pages(struct alloc_tag *tag, unsigned int nr) > { > if (mem_alloc_profiling_enabled() && tag) > this_cpu_sub(tag->counters->bytes, PAGE_SIZE * nr); > } > > > +++ b/mm/page_alloc.c > > @@ -4697,12 +4697,21 @@ 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) > > - while (order-- > 0) > > + while (order-- > 0) { > > free_the_page(page + (1 << order), order); > > + /* > > + * non-compound multi-order page accounts all allocations > > + * to the first page (just like compound one), therefore > > + * we need to adjust the allocation size of the first > > + * page as its order is ignored when put_page() frees it. > > + */ > > + pgalloc_tag_sub_bytes(tag, order); > > - else if (!head > + else if (!head) { > + pgalloc_tag_sub_pages(1 << order - 1); > while (order-- > 0) > free_the_page(page + (1 << order), order); > + } > > It doesn't need a comment, it's obvious what you're doing. All suggestions seem fine to me. I'll adjust the next version accordingly. Thanks for reviewing and the feedback! >