mirror of
https://github.com/torvalds/linux.git
synced 2026-11-07 06:17:02 +01:00
mm/zsmalloc: replace PG_private with pointer comparison
Patch series "Remove PG_private by using page/folio->private checks
instead", v5.
This patchset removes PG_private to make space for upcoming PG_folio for
identifying pages from a folio (more details in Note below). Instead of
checking PG_private, all code is changed to check page/folio->private !=
NULL instead.
Overview
===
Most code uses folio_attach/detach/change_private() functions, so folio
refcount is increased and decreased when folio->private is set and reset,
respectively. There is no need to change them.
Changes are needed for exceptional users:
1. zsmalloc uses PG_private to indicate first component zpdesc page and
page->private is used to store zspage in zpdesc. To remove PG_private,
is_first_zpdesc() is replaced by pointer comparison.
2. kernel/events/ring_buffer.c stores page order in page->private.
Replacing PG_private with page->private != NULL works.
3. drivers/xen/grant-table.c stores xen_page_foreign in page->private,
where on 32-bit, a pointer to xen_page_foreign is stored; on 64-bit,
page->private is used as xen_page_foreign. PG_private check is replaced
by page->private != NULL on 32-bit for xen_page_foreign deallocation.
On 64-bit, page->private is cleared unconditionally since {domid=0,
gref=0} (xen_page_foreign can be 0) is valid.
4. fs/crypto/crypto.c stores a folio pointer in page->private, PG_private
checks are replaced by page->private != NULL.
5. fs/erofs has two different uses:
5a. folio->private is used to form a reversed list of
the outputs of readahead_folio(). readahead_folio_last() is added to
output folios in reversed order, so that ->private is no longer needed.
5b. folio->private is used as an in-flight I/O counter. Convert the
code to use folio_attach/detach/get_private() and add bias==1 to the
counter to avoid folio->private being zero.
6. fs/nfs/write.c: folio refcount maintenance is in a bigger scope than
folio->private. So folio_attach/detach/get_private() is not used.
Nothing to change.
7. fs/f2fs uses attach_page_private() to first reset folio->private then
immediately sets PAGE_PRIVATE_NOT_POINTER bit on it. Change it to use
attach_page_private() to set PAGE_PRIVATE_NOT_POINTER bit directly to
avoid folio->private == NULL gap inside set_page_private_##name().
8. hugetlb uses folio_change_private(folio, NULL) without folio refcount
maintenance. Change it to folio->private = NULL.
After the above changes, PG_private ops are converted to
page/folio->private ops.
folio_has_attached_private() is added to check filesystem-only private data
by excluding swapcache and hugetlb folios, because swapcache folios overlap
swp_entry_t swap with ->private and hugetlb sets its own flags in
->private.
Note
===
1. KPF_PRIVATE is removed after PG_private is removed.
2. Documentation/mm/hugetlbfs_reserv.rst is outdated, so I did not remove
PG_private related text. It should be rewritten.
3. PG_folio is planned to be set on every page from a folio in
page_rmappable_folio(), so folios with any order (currently
PG_large_rmappable is used to identify >0 order folios, but not order-0
folios) can be identified. Then vm_insert_*() can correctly reject all
folios and rmap code will only see folios. Eventually, page_folio()
will return NULL for non-folio pages by checking PG_folio, but before
that all existing users that treat compound pages as folios will need
to be converted.
This patch (of 17):
zsmalloc uses PG_private to indicate first zpdesc in a zspage chain. It is
equivalent to check zpdesc == zspage->first_zpdesc. Replace
is_first_zpdesc() with zpdesc == zspage->first_zpdesc in obj_allocated().
For get_first_zpdesc(), first_zpdesc is from zspage->first_zpdesc, so
replace is_first_zpdesc() with first_zpdesc->zspage == zspage, the second
requirement of a zspage chain, where all zpdescs point to the same zspage.
is_first_zpdesc(), is only used in VM_BUG_ON_PAGE(), so performance impact
should be negligible. While at it, change VM_BUG_ON() to
VM_WARN_ON_ONCE_PAGE().
It prepares for a future commit that remove PG_private.
No functional change intended.
Link: https://lore.kernel.org/20260920-remove-pg_private-v5-0-bb68b6a21869@nvidia.com
Link: https://lore.kernel.org/20260920-remove-pg_private-v5-1-bb68b6a21869@nvidia.com
Signed-off-by: Zi Yan <ziy@nvidia.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Acked-by: Johannes Weiner <hannes@cmpxchg.org>
Reviewed-by: Sergey Senozhatsky <senozhatsky@chromium.org>
Acked-by: David Hildenbrand (Arm) <david@kernel.org>
Reviewed-by: Lance Yang <lance.yang@linux.dev>
Assisted-by: LLM
Cc: Minchan Kim <minchan@kernel.org>
This commit is contained in:
parent
9b20271285
commit
c315da11e3
|
|
@ -26,8 +26,8 @@
|
|||
* with memcg_data.
|
||||
*
|
||||
* Page flags used:
|
||||
* * PG_private identifies the first component page.
|
||||
* * PG_locked is used by page migration code.
|
||||
* The first component page has zpdesc->zspage->first_zpdesc == zpdesc
|
||||
*/
|
||||
struct zpdesc {
|
||||
unsigned long flags;
|
||||
|
|
|
|||
|
|
@ -290,11 +290,6 @@ struct zs_pool {
|
|||
atomic_t compaction_in_progress;
|
||||
};
|
||||
|
||||
static inline void zpdesc_set_first(struct zpdesc *zpdesc)
|
||||
{
|
||||
SetPagePrivate(zpdesc_page(zpdesc));
|
||||
}
|
||||
|
||||
static inline void zpdesc_inc_zone_page_state(struct zpdesc *zpdesc)
|
||||
{
|
||||
inc_zone_page_state(zpdesc_page(zpdesc), NR_ZSPAGES);
|
||||
|
|
@ -476,11 +471,6 @@ static void record_obj(unsigned long handle, unsigned long obj)
|
|||
WRITE_ONCE(*(unsigned long *)handle, obj);
|
||||
}
|
||||
|
||||
static inline bool __maybe_unused is_first_zpdesc(struct zpdesc *zpdesc)
|
||||
{
|
||||
return PagePrivate(zpdesc_page(zpdesc));
|
||||
}
|
||||
|
||||
/* Protected by class->lock */
|
||||
static inline int get_zspage_inuse(struct zspage *zspage)
|
||||
{
|
||||
|
|
@ -496,7 +486,8 @@ static struct zpdesc *get_first_zpdesc(struct zspage *zspage)
|
|||
{
|
||||
struct zpdesc *first_zpdesc = zspage->first_zpdesc;
|
||||
|
||||
VM_BUG_ON_PAGE(!is_first_zpdesc(first_zpdesc), zpdesc_page(first_zpdesc));
|
||||
/* the first zpdesc must point back to this zspage */
|
||||
VM_WARN_ON_ONCE_PAGE(first_zpdesc->zspage != zspage, zpdesc_page(first_zpdesc));
|
||||
return first_zpdesc;
|
||||
}
|
||||
|
||||
|
|
@ -838,7 +829,8 @@ static inline bool obj_allocated(struct zpdesc *zpdesc, void *obj,
|
|||
struct zspage *zspage = get_zspage(zpdesc);
|
||||
|
||||
if (unlikely(ZsHugePage(zspage))) {
|
||||
VM_BUG_ON_PAGE(!is_first_zpdesc(zpdesc), zpdesc_page(zpdesc));
|
||||
/* only first zpdesc holds the handle */
|
||||
VM_WARN_ON_ONCE_PAGE(zspage->first_zpdesc != zpdesc, zpdesc_page(zpdesc));
|
||||
handle = zpdesc->handle;
|
||||
} else
|
||||
handle = *(unsigned long *)obj;
|
||||
|
|
@ -853,9 +845,6 @@ static inline bool obj_allocated(struct zpdesc *zpdesc, void *obj,
|
|||
|
||||
static void reset_zpdesc(struct zpdesc *zpdesc)
|
||||
{
|
||||
struct page *page = zpdesc_page(zpdesc);
|
||||
|
||||
ClearPagePrivate(page);
|
||||
zpdesc->zspage = NULL;
|
||||
zpdesc->next = NULL;
|
||||
/* PageZsmalloc is sticky until the page is freed to the buddy. */
|
||||
|
|
@ -1006,8 +995,8 @@ static void create_page_chain(struct size_class *class, struct zspage *zspage,
|
|||
* 1. all pages are linked together using zpdesc->next
|
||||
* 2. each sub-page point to zspage using zpdesc->zspage
|
||||
*
|
||||
* we set PG_private to identify the first zpdesc (i.e. no other zpdesc
|
||||
* has this flag set).
|
||||
* The first zpdesc has its zspage->first_zpdesc set to itself, no
|
||||
* other zpdesc has this set.
|
||||
*/
|
||||
for (i = 0; i < nr_zpdescs; i++) {
|
||||
zpdesc = zpdescs[i];
|
||||
|
|
@ -1015,7 +1004,6 @@ static void create_page_chain(struct size_class *class, struct zspage *zspage,
|
|||
zpdesc->next = NULL;
|
||||
if (i == 0) {
|
||||
zspage->first_zpdesc = zpdesc;
|
||||
zpdesc_set_first(zpdesc);
|
||||
if (unlikely(class->objs_per_zspage == 1 &&
|
||||
class->pages_per_zspage == 1))
|
||||
SetZsHugePage(zspage);
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user