BACKPORT: mm: memcontrol: Use helpers to read page's memcg data

Patch series "mm: allow mapping accounted kernel pages to userspace", v6.

Currently a non-slab kernel page which has been charged to a memory cgroup
can't be mapped to userspace.  The underlying reason is simple: PageKmemcg
flag is defined as a page type (like buddy, offline, etc), so it takes a
bit from a page->mapped counter.  Pages with a type set can't be mapped to
userspace.

But in general the kmemcg flag has nothing to do with mapping to
userspace.  It only means that the page has been accounted by the page
allocator, so it has to be properly uncharged on release.

Some bpf maps are mapping the vmalloc-based memory to userspace, and their
memory can't be accounted because of this implementation detail.

This patchset removes this limitation by moving the PageKmemcg flag into
one of the free bits of the page->mem_cgroup pointer.  Also it formalizes
accesses to the page->mem_cgroup and page->obj_cgroups using new helpers,
adds several checks and removes a couple of obsolete functions.  As the
result the code became more robust with fewer open-coded bit tricks.

This patch (of 4):

Currently there are many open-coded reads of the page->mem_cgroup pointer,
as well as a couple of read helpers, which are barely used.

It creates an obstacle on a way to reuse some bits of the pointer for
storing additional bits of information.  In fact, we already do this for
slab pages, where the last bit indicates that a pointer has an attached
vector of objcg pointers instead of a regular memcg pointer.

This commits uses 2 existing helpers and introduces a new helper to
converts all read sides to calls of these helpers:
  struct mem_cgroup *page_memcg(struct page *page);
  struct mem_cgroup *page_memcg_rcu(struct page *page);
  struct mem_cgroup *page_memcg_check(struct page *page);

page_memcg_check() is intended to be used in cases when the page can be a
slab page and have a memcg pointer pointing at objcg vector.  It does
check the lowest bit, and if set, returns NULL.  page_memcg() contains a
VM_BUG_ON_PAGE() check for the page not being a slab page.

To make sure nobody uses a direct access, struct page's
mem_cgroup/obj_cgroups is converted to unsigned long memcg_data.

Change-Id: I052ddf916e3033cc1acf934ac0b6eea551d61d00
Signed-off-by: Roman Gushchin <guro@fb.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Alexei Starovoitov <ast@kernel.org>
Reviewed-by: Shakeel Butt <shakeelb@google.com>
Acked-by: Johannes Weiner <hannes@cmpxchg.org>
Acked-by: Michal Hocko <mhocko@suse.com>
Link: https://lkml.kernel.org/r/20201027001657.3398190-1-guro@fb.com
Link: https://lkml.kernel.org/r/20201027001657.3398190-2-guro@fb.com
Link: https://lore.kernel.org/bpf/20201201215900.3569844-2-guro@fb.com
This commit is contained in:
Roman Gushchin authored and Michael Bestas committed 2026-08-30 23:14:46 +03:00
1 parent fbe437500d
commit 579cfa878e
10 files changed
+171 -87

No files matched your search

+4 -2
View File
@@ -2004,12 +2004,14 @@ EXPORT_SYMBOL(bioset_init_from_src);
int bio_associate_blkcg_from_page(struct bio *bio, struct page *page) int bio_associate_blkcg_from_page(struct bio *bio, struct page *page)
{ {
struct cgroup_subsys_state *blkcg_css; struct cgroup_subsys_state *blkcg_css;
struct mem_cgroup *memcg;
if (unlikely(bio->bi_css)) if (unlikely(bio->bi_css))
return -EBUSY; return -EBUSY;
if (!page->mem_cgroup) memcg = page_memcg(page);
if (!memcg)
return 0; return 0;
blkcg_css = cgroup_get_e_css(page->mem_cgroup->css.cgroup, blkcg_css = cgroup_get_e_css(memcg->css.cgroup,
&io_cgrp_subsys); &io_cgrp_subsys);
bio->bi_css = blkcg_css; bio->bi_css = blkcg_css;
return 0; return 0;
+1 -1
View File
@@ -632,7 +632,7 @@ int __set_page_dirty_buffers(struct page *page)
} while (bh != head); } while (bh != head);
} }
/* /*
* Lock out page->mem_cgroup migration to keep PageDirty * Lock out page's memcg migration to keep PageDirty
* synchronized with per-memcg dirty page counters. * synchronized with per-memcg dirty page counters.
*/ */
lock_page_memcg(page); lock_page_memcg(page);
+104 -8
View File
@@ -323,6 +323,79 @@ struct mem_cgroup {
extern struct mem_cgroup *root_mem_cgroup; extern struct mem_cgroup *root_mem_cgroup;
/*
* page_memcg - get the memory cgroup associated with a page
* @page: a pointer to the page struct
*
* Returns a pointer to the memory cgroup associated with the page,
* or NULL. This function assumes that the page is known to have a
* proper memory cgroup pointer. It's not safe to call this function
* against some type of pages, e.g. slab pages or ex-slab pages.
*
* Any of the following ensures page and memcg binding stability:
* - the page lock
* - LRU isolation
* - lock_page_memcg()
* - exclusive reference
*/
static inline struct mem_cgroup *page_memcg(struct page *page)
{
VM_BUG_ON_PAGE(PageSlab(page), page);
return (struct mem_cgroup *)page->memcg_data;
}
/*
* page_memcg_rcu - locklessly get the memory cgroup associated with a page
* @page: a pointer to the page struct
*
* Returns a pointer to the memory cgroup associated with the page,
* or NULL. This function assumes that the page is known to have a
* proper memory cgroup pointer. It's not safe to call this function
* against some type of pages, e.g. slab pages or ex-slab pages.
*/
static inline struct mem_cgroup *page_memcg_rcu(struct page *page)
{
VM_BUG_ON_PAGE(PageSlab(page), page);
WARN_ON_ONCE(!rcu_read_lock_held());
return (struct mem_cgroup *)READ_ONCE(page->memcg_data);
}
/*
* page_memcg_check - get the memory cgroup associated with a page
* @page: a pointer to the page struct
*
* Returns a pointer to the memory cgroup associated with the page,
* or NULL. This function unlike page_memcg() can take any page
* as an argument. It has to be used in cases when it's not known if a page
* has an associated memory cgroup pointer or an object cgroups vector.
*
* Any of the following ensures page and memcg binding stability:
* - the page lock
* - LRU isolation
* - lock_page_memcg()
* - exclusive reference
*/
static inline struct mem_cgroup *page_memcg_check(struct page *page)
{
/*
* Because page->memcg_data might be changed asynchronously
* for slab pages, READ_ONCE() should be used here.
*/
unsigned long memcg_data = READ_ONCE(page->memcg_data);
/*
* The lowest bit set means that memcg isn't a valid
* memcg pointer, but a obj_cgroups pointer.
* In this case the page is shared and doesn't belong
* to any specific memory cgroup.
*/
if (memcg_data & 0x1UL)
return NULL;
return (struct mem_cgroup *)memcg_data;
}
static inline bool mem_cgroup_is_root(struct mem_cgroup *memcg) static inline bool mem_cgroup_is_root(struct mem_cgroup *memcg)
{ {
return (memcg == root_mem_cgroup); return (memcg == root_mem_cgroup);
@@ -621,15 +694,19 @@ static inline void mod_memcg_state(struct mem_cgroup *memcg,
static inline void __mod_memcg_page_state(struct page *page, static inline void __mod_memcg_page_state(struct page *page,
int idx, int val) int idx, int val)
{ {
if (page->mem_cgroup) struct mem_cgroup *memcg = page_memcg(page);
__mod_memcg_state(page->mem_cgroup, idx, val);
if (memcg)
__mod_memcg_state(memcg, idx, val);
} }
static inline void mod_memcg_page_state(struct page *page, static inline void mod_memcg_page_state(struct page *page,
int idx, int val) int idx, int val)
{ {
if (page->mem_cgroup) struct mem_cgroup *memcg = page_memcg(page);
mod_memcg_state(page->mem_cgroup, idx, val);
if (memcg)
mod_memcg_state(memcg, idx, val);
} }
static inline unsigned long lruvec_page_state(struct lruvec *lruvec, static inline unsigned long lruvec_page_state(struct lruvec *lruvec,
@@ -689,16 +766,17 @@ static inline void mod_lruvec_state(struct lruvec *lruvec,
static inline void __mod_lruvec_page_state(struct page *page, static inline void __mod_lruvec_page_state(struct page *page,
enum node_stat_item idx, int val) enum node_stat_item idx, int val)
{ {
struct mem_cgroup *memcg = page_memcg(page);
pg_data_t *pgdat = page_pgdat(page); pg_data_t *pgdat = page_pgdat(page);
struct lruvec *lruvec; struct lruvec *lruvec;
/* Untracked pages have no memcg, no lruvec. Update only the node */ /* Untracked pages have no memcg, no lruvec. Update only the node */
if (!page->mem_cgroup) { if (!memcg) {
__mod_node_page_state(pgdat, idx, val); __mod_node_page_state(pgdat, idx, val);
return; return;
} }
lruvec = mem_cgroup_lruvec(pgdat, page->mem_cgroup); lruvec = mem_cgroup_lruvec(pgdat, memcg);
__mod_lruvec_state(lruvec, idx, val); __mod_lruvec_state(lruvec, idx, val);
} }
@@ -747,8 +825,10 @@ static inline void count_memcg_events(struct mem_cgroup *memcg,
static inline void count_memcg_page_event(struct page *page, static inline void count_memcg_page_event(struct page *page,
enum vm_event_item idx) enum vm_event_item idx)
{ {
if (page->mem_cgroup) struct mem_cgroup *memcg = page_memcg(page);
count_memcg_events(page->mem_cgroup, idx, 1);
if (memcg)
count_memcg_events(memcg, idx, 1);
} }
static inline void count_memcg_event_mm(struct mm_struct *mm, static inline void count_memcg_event_mm(struct mm_struct *mm,
@@ -807,6 +887,22 @@ void mem_cgroup_split_huge_fixup(struct page *head);
struct mem_cgroup; struct mem_cgroup;
static inline struct mem_cgroup *page_memcg(struct page *page)
{
return NULL;
}
static inline struct mem_cgroup *page_memcg_rcu(struct page *page)
{
WARN_ON_ONCE(!rcu_read_lock_held());
return NULL;
}
static inline struct mem_cgroup *page_memcg_check(struct page *page)
{
return NULL;
}
static inline bool mem_cgroup_is_root(struct mem_cgroup *memcg) static inline bool mem_cgroup_is_root(struct mem_cgroup *memcg)
{ {
return true; return true;
-22
View File
@@ -1275,28 +1275,6 @@ static inline void set_page_links(struct page *page, enum zone_type zone,
#endif #endif
} }
#ifdef CONFIG_MEMCG
static inline struct mem_cgroup *page_memcg(struct page *page)
{
return page->mem_cgroup;
}
static inline struct mem_cgroup *page_memcg_rcu(struct page *page)
{
WARN_ON_ONCE(!rcu_read_lock_held());
return READ_ONCE(page->mem_cgroup);
}
#else
static inline struct mem_cgroup *page_memcg(struct page *page)
{
return NULL;
}
static inline struct mem_cgroup *page_memcg_rcu(struct page *page)
{
WARN_ON_ONCE(!rcu_read_lock_held());
return NULL;
}
#endif
/* /*
* Some inline functions in vmstat.h depend on page_zone() * Some inline functions in vmstat.h depend on page_zone()
*/ */
+1 -1
View File
@@ -190,7 +190,7 @@ struct page {
atomic_t _refcount; atomic_t _refcount;
#ifdef CONFIG_MEMCG #ifdef CONFIG_MEMCG
struct mem_cgroup *mem_cgroup; unsigned long memcg_data;
#endif #endif
/* /*
+4 -4
View File
@@ -402,10 +402,10 @@ static int memcg_charge_kernel_stack(struct task_struct *tsk)
for (i = 0; i < THREAD_SIZE / PAGE_SIZE; i++) { for (i = 0; i < THREAD_SIZE / PAGE_SIZE; i++) {
/* /*
* If memcg_kmem_charge_page() fails, page->mem_cgroup * If memcg_kmem_charge_page() fails, page's
* pointer is NULL, and both memcg_kmem_uncharge_page() * memory cgroup pointer is NULL, and
* and mod_memcg_page_state() in free_thread_stack() * memcg_kmem_uncharge_page() in free_thread_stack()
* will ignore this page. So it's safe. * will ignore this page.
*/ */
ret = memcg_kmem_charge_page(vm->pages[i], GFP_KERNEL, ret = memcg_kmem_charge_page(vm->pages[i], GFP_KERNEL,
0); 0);
+2 -2
View File
@@ -82,8 +82,8 @@ hex_only:
pr_alert("page dumped because: %s\n", reason); pr_alert("page dumped because: %s\n", reason);
#ifdef CONFIG_MEMCG #ifdef CONFIG_MEMCG
if (!page_poisoned && page->mem_cgroup) if (!page_poisoned && page->memcg_data)
pr_alert("page->mem_cgroup:%px\n", page->mem_cgroup); pr_alert("page's memcg:%lx\n", page->memcg_data);
#endif #endif
} }
+51 -43
View File
@@ -468,7 +468,7 @@ struct cgroup_subsys_state *mem_cgroup_css_from_page(struct page *page)
{ {
struct mem_cgroup *memcg; struct mem_cgroup *memcg;
memcg = page->mem_cgroup; memcg = page_memcg(page);
if (!memcg || !cgroup_subsys_on_dfl(memory_cgrp_subsys)) if (!memcg || !cgroup_subsys_on_dfl(memory_cgrp_subsys))
memcg = root_mem_cgroup; memcg = root_mem_cgroup;
@@ -495,10 +495,8 @@ ino_t page_cgroup_ino(struct page *page)
unsigned long ino = 0; unsigned long ino = 0;
rcu_read_lock(); rcu_read_lock();
if (PageSlab(page) && !PageTail(page)) memcg = page_memcg_check(page);
memcg = memcg_from_slab_page(page);
else
memcg = READ_ONCE(page->mem_cgroup);
while (memcg && !(memcg->css.flags & CSS_ONLINE)) while (memcg && !(memcg->css.flags & CSS_ONLINE))
memcg = parent_mem_cgroup(memcg); memcg = parent_mem_cgroup(memcg);
if (memcg) if (memcg)
@@ -871,7 +869,7 @@ EXPORT_SYMBOL(get_mem_cgroup_from_mm);
*/ */
struct mem_cgroup *get_mem_cgroup_from_page(struct page *page) struct mem_cgroup *get_mem_cgroup_from_page(struct page *page)
{ {
struct mem_cgroup *memcg = page->mem_cgroup; struct mem_cgroup *memcg = page_memcg(page);
if (mem_cgroup_disabled()) if (mem_cgroup_disabled())
return NULL; return NULL;
@@ -1138,7 +1136,7 @@ struct lruvec *mem_cgroup_page_lruvec(struct page *page, struct pglist_data *pgd
goto out; goto out;
} }
memcg = page->mem_cgroup; memcg = page_memcg(page);
/* /*
* Swapcache readahead pages are added to the LRU - and * Swapcache readahead pages are added to the LRU - and
* possibly migrated - before they are charged. * possibly migrated - before they are charged.
@@ -1882,7 +1880,7 @@ void mem_cgroup_print_oom_group(struct mem_cgroup *memcg)
} }
/** /**
* lock_page_memcg - lock a page->mem_cgroup binding * lock_page_memcg - lock a page and memcg binding
* @page: the page * @page: the page
* *
* This function protects unlocked LRU pages from being moved to * This function protects unlocked LRU pages from being moved to
@@ -1913,7 +1911,7 @@ struct mem_cgroup *lock_page_memcg(struct page *page)
if (mem_cgroup_disabled()) if (mem_cgroup_disabled())
return NULL; return NULL;
again: again:
memcg = page->mem_cgroup; memcg = page_memcg(page);
if (unlikely(!memcg)) if (unlikely(!memcg))
return NULL; return NULL;
@@ -1921,7 +1919,7 @@ again:
return memcg; return memcg;
spin_lock_irqsave(&memcg->move_lock, flags); spin_lock_irqsave(&memcg->move_lock, flags);
if (memcg != page->mem_cgroup) { if (memcg != page_memcg(page)) {
spin_unlock_irqrestore(&memcg->move_lock, flags); spin_unlock_irqrestore(&memcg->move_lock, flags);
goto again; goto again;
} }
@@ -1959,12 +1957,12 @@ void __unlock_page_memcg(struct mem_cgroup *memcg)
} }
/** /**
* unlock_page_memcg - unlock a page->mem_cgroup binding * unlock_page_memcg - unlock a page and memcg binding
* @page: the page * @page: the page
*/ */
void unlock_page_memcg(struct page *page) void unlock_page_memcg(struct page *page)
{ {
__unlock_page_memcg(page->mem_cgroup); __unlock_page_memcg(page_memcg(page));
} }
EXPORT_SYMBOL(unlock_page_memcg); EXPORT_SYMBOL(unlock_page_memcg);
@@ -2428,7 +2426,7 @@ static void commit_charge(struct page *page, struct mem_cgroup *memcg,
{ {
int isolated; int isolated;
VM_BUG_ON_PAGE(page->mem_cgroup, page); VM_BUG_ON_PAGE(page_memcg(page), page);
/* /*
* In some cases, SwapCache and FUSE(splice_buf->radixtree), the page * In some cases, SwapCache and FUSE(splice_buf->radixtree), the page
@@ -2451,7 +2449,7 @@ static void commit_charge(struct page *page, struct mem_cgroup *memcg,
* - a page cache insertion, a swapin fault, or a migration * - a page cache insertion, a swapin fault, or a migration
* have the page locked * have the page locked
*/ */
page->mem_cgroup = memcg; page->memcg_data = (unsigned long)memcg;
if (lrucare) if (lrucare)
unlock_page_lru(page, isolated); unlock_page_lru(page, isolated);
@@ -2704,7 +2702,7 @@ int __memcg_kmem_charge_page(struct page *page, gfp_t gfp, int order)
if (!mem_cgroup_is_root(memcg)) { if (!mem_cgroup_is_root(memcg)) {
ret = __memcg_kmem_charge(memcg, gfp, 1 << order); ret = __memcg_kmem_charge(memcg, gfp, 1 << order);
if (!ret) { if (!ret) {
page->mem_cgroup = memcg; page->memcg_data = (unsigned long)memcg;
__SetPageKmemcg(page); __SetPageKmemcg(page);
} }
} }
@@ -2719,7 +2717,7 @@ int __memcg_kmem_charge_page(struct page *page, gfp_t gfp, int order)
*/ */
void __memcg_kmem_uncharge_page(struct page *page, int order) void __memcg_kmem_uncharge_page(struct page *page, int order)
{ {
struct mem_cgroup *memcg = page->mem_cgroup; struct mem_cgroup *memcg = page_memcg(page);
unsigned int nr_pages = 1 << order; unsigned int nr_pages = 1 << order;
if (!memcg) if (!memcg)
@@ -2727,7 +2725,7 @@ void __memcg_kmem_uncharge_page(struct page *page, int order)
VM_BUG_ON_PAGE(mem_cgroup_is_root(memcg), page); VM_BUG_ON_PAGE(mem_cgroup_is_root(memcg), page);
__memcg_kmem_uncharge(memcg, nr_pages); __memcg_kmem_uncharge(memcg, nr_pages);
page->mem_cgroup = NULL; page->memcg_data = 0;
/* slab pages do not have PageKmemcg flag set */ /* slab pages do not have PageKmemcg flag set */
if (PageKmemcg(page)) if (PageKmemcg(page))
@@ -2745,15 +2743,16 @@ void __memcg_kmem_uncharge_page(struct page *page, int order)
*/ */
void mem_cgroup_split_huge_fixup(struct page *head) void mem_cgroup_split_huge_fixup(struct page *head)
{ {
struct mem_cgroup *memcg = page_memcg(head);
int i; int i;
if (mem_cgroup_disabled()) if (mem_cgroup_disabled())
return; return;
for (i = 1; i < HPAGE_PMD_NR; i++) for (i = 1; i < HPAGE_PMD_NR; i++)
head[i].mem_cgroup = head->mem_cgroup; head[i].memcg_data = (unsigned long)memcg;
__mod_memcg_state(head->mem_cgroup, MEMCG_RSS_HUGE, -HPAGE_PMD_NR); __mod_memcg_state(memcg, MEMCG_RSS_HUGE, -HPAGE_PMD_NR);
} }
#endif /* CONFIG_TRANSPARENT_HUGEPAGE */ #endif /* CONFIG_TRANSPARENT_HUGEPAGE */
@@ -4912,14 +4911,14 @@ static int mem_cgroup_move_account(struct page *page,
/* /*
* Prevent mem_cgroup_migrate() from looking at * Prevent mem_cgroup_migrate() from looking at
* page->mem_cgroup of its source page while we change it. * page's memory cgroup of its source page while we change it.
*/ */
ret = -EBUSY; ret = -EBUSY;
if (!trylock_page(page)) if (!trylock_page(page))
goto out; goto out;
ret = -EINVAL; ret = -EINVAL;
if (page->mem_cgroup != from) if (page_memcg(page) != from)
goto out_unlock; goto out_unlock;
anon = PageAnon(page); anon = PageAnon(page);
@@ -4951,13 +4950,21 @@ static int mem_cgroup_move_account(struct page *page,
} }
/* /*
* It is safe to change page->mem_cgroup here because the page * All state has been migrated, let's switch to the new memcg.
* is referenced, charged, and isolated - we can't race with *
* uncharging, charging, migration, or LRU putback. * It is safe to change page's memcg here because the page
* is referenced, charged, isolated, and locked: we can't race
* with (un)charging, migration, LRU putback, or anything else
* that would rely on a stable page's memory cgroup.
*
* Note that lock_page_memcg is a memcg lock, not a page lock,
* to save space. As soon as we switch page's memory cgroup to a
* new memcg that isn't locked, the above state can change
* concurrently again. Make sure we're truly done with it.
*/ */
/* caller should have done css_get */ /* caller should have done css_get */
page->mem_cgroup = to; page->memcg_data = (unsigned long)to;
spin_unlock_irqrestore(&from->move_lock, flags); spin_unlock_irqrestore(&from->move_lock, flags);
ret = 0; ret = 0;
@@ -5022,7 +5029,7 @@ static enum mc_target_type get_mctgt_type(struct vm_area_struct *vma,
* mem_cgroup_move_account() checks the page is valid or * mem_cgroup_move_account() checks the page is valid or
* not under LRU exclusion. * not under LRU exclusion.
*/ */
if (page->mem_cgroup == mc.from) { if (page_memcg(page) == mc.from) {
ret = MC_TARGET_PAGE; ret = MC_TARGET_PAGE;
if (is_device_private_page(page) || if (is_device_private_page(page) ||
is_device_public_page(page)) is_device_public_page(page))
@@ -5067,7 +5074,7 @@ static enum mc_target_type get_mctgt_type_thp(struct vm_area_struct *vma,
VM_BUG_ON_PAGE(!page || !PageHead(page), page); VM_BUG_ON_PAGE(!page || !PageHead(page), page);
if (!(mc.flags & MOVE_ANON)) if (!(mc.flags & MOVE_ANON))
return ret; return ret;
if (page->mem_cgroup == mc.from) { if (page_memcg(page) == mc.from) {
ret = MC_TARGET_PAGE; ret = MC_TARGET_PAGE;
if (target) { if (target) {
get_page(page); get_page(page);
@@ -6042,12 +6049,12 @@ int mem_cgroup_try_charge(struct page *page, struct mm_struct *mm,
/* /*
* Every swap fault against a single page tries to charge the * Every swap fault against a single page tries to charge the
* page, bail as early as possible. shmem_unuse() encounters * page, bail as early as possible. shmem_unuse() encounters
* already charged pages, too. The USED bit is protected by * already charged pages, too. page and memcg binding is
* the page lock, which serializes swap cache removal, which * protected by the page lock, which serializes swap cache
* in turn serializes uncharging. * removal, which in turn serializes uncharging.
*/ */
VM_BUG_ON_PAGE(!PageLocked(page), page); VM_BUG_ON_PAGE(!PageLocked(page), page);
if (compound_head(page)->mem_cgroup) if (page_memcg(compound_head(page)))
goto out; goto out;
if (do_swap_account) { if (do_swap_account) {
@@ -6215,21 +6222,22 @@ static void uncharge_page(struct page *page, struct uncharge_gather *ug)
VM_BUG_ON_PAGE(page_count(page) && !is_zone_device_page(page) && VM_BUG_ON_PAGE(page_count(page) && !is_zone_device_page(page) &&
!PageHWPoison(page) , page); !PageHWPoison(page) , page);
if (!page->mem_cgroup) if (!page_memcg(page))
return; return;
/* /*
* Nobody should be changing or seriously looking at * Nobody should be changing or seriously looking at
* page->mem_cgroup at this point, we have fully * page_memcg(page) at this point, we have fully
* exclusive access to the page. * exclusive access to the page.
*/ */
if (ug->memcg != page->mem_cgroup) { if (ug->memcg != page_memcg(page)) {
if (ug->memcg) { if (ug->memcg) {
uncharge_batch(ug); uncharge_batch(ug);
uncharge_gather_clear(ug); uncharge_gather_clear(ug);
} }
ug->memcg = page->mem_cgroup; ug->memcg = page_memcg(page);
} }
if (!PageKmemcg(page)) { if (!PageKmemcg(page)) {
@@ -6253,7 +6261,7 @@ static void uncharge_page(struct page *page, struct uncharge_gather *ug)
} }
ug->dummy_page = page; ug->dummy_page = page;
page->mem_cgroup = NULL; page->memcg_data = 0;
} }
static void uncharge_list(struct list_head *page_list) static void uncharge_list(struct list_head *page_list)
@@ -6296,7 +6304,7 @@ void mem_cgroup_uncharge(struct page *page)
return; return;
/* Don't touch page->lru of any random page, pre-check: */ /* Don't touch page->lru of any random page, pre-check: */
if (!page->mem_cgroup) if (!page_memcg(page))
return; return;
uncharge_gather_clear(&ug); uncharge_gather_clear(&ug);
@@ -6347,11 +6355,11 @@ void mem_cgroup_migrate(struct page *oldpage, struct page *newpage)
return; return;
/* Page cache replacement: new page already charged? */ /* Page cache replacement: new page already charged? */
if (newpage->mem_cgroup) if (page_memcg(newpage))
return; return;
/* Swapcache readahead pages can get replaced before being charged */ /* Swapcache readahead pages can get replaced before being charged */
memcg = oldpage->mem_cgroup; memcg = page_memcg(oldpage);
if (!memcg) if (!memcg)
return; return;
@@ -6562,7 +6570,7 @@ void mem_cgroup_swapout(struct page *page, swp_entry_t entry)
if (!do_memsw_account()) if (!do_memsw_account())
return; return;
memcg = page->mem_cgroup; memcg = page_memcg(page);
/* Readahead page, never charged */ /* Readahead page, never charged */
if (!memcg) if (!memcg)
@@ -6583,7 +6591,7 @@ void mem_cgroup_swapout(struct page *page, swp_entry_t entry)
VM_BUG_ON_PAGE(oldid, page); VM_BUG_ON_PAGE(oldid, page);
mod_memcg_state(swap_memcg, MEMCG_SWAP, nr_entries); mod_memcg_state(swap_memcg, MEMCG_SWAP, nr_entries);
page->mem_cgroup = NULL; page->memcg_data = 0;
if (!mem_cgroup_is_root(memcg)) if (!mem_cgroup_is_root(memcg))
page_counter_uncharge(&memcg->memory, nr_entries); page_counter_uncharge(&memcg->memory, nr_entries);
@@ -6628,7 +6636,7 @@ int mem_cgroup_try_charge_swap(struct page *page, swp_entry_t entry)
if (!cgroup_subsys_on_dfl(memory_cgrp_subsys) || !do_swap_account) if (!cgroup_subsys_on_dfl(memory_cgrp_subsys) || !do_swap_account)
return 0; return 0;
memcg = page->mem_cgroup; memcg = page_memcg(page);
/* Readahead page, never charged */ /* Readahead page, never charged */
if (!memcg) if (!memcg)
@@ -6712,7 +6720,7 @@ bool mem_cgroup_swap_full(struct page *page)
if (!do_swap_account || !cgroup_subsys_on_dfl(memory_cgrp_subsys)) if (!do_swap_account || !cgroup_subsys_on_dfl(memory_cgrp_subsys))
return false; return false;
memcg = page->mem_cgroup; memcg = page_memcg(page);
if (!memcg) if (!memcg)
return false; return false;
+3 -3
View File
@@ -1047,7 +1047,7 @@ static inline bool page_expected_state(struct page *page,
if (unlikely((unsigned long)page->mapping | if (unlikely((unsigned long)page->mapping |
page_ref_count(page) | page_ref_count(page) |
#ifdef CONFIG_MEMCG #ifdef CONFIG_MEMCG
(unsigned long)page->mem_cgroup | (unsigned long)page_memcg(page) |
#endif #endif
(page->flags & check_flags))) (page->flags & check_flags)))
return false; return false;
@@ -1074,7 +1074,7 @@ static void free_pages_check_bad(struct page *page)
bad_flags = PAGE_FLAGS_CHECK_AT_FREE; bad_flags = PAGE_FLAGS_CHECK_AT_FREE;
} }
#ifdef CONFIG_MEMCG #ifdef CONFIG_MEMCG
if (unlikely(page->mem_cgroup)) if (unlikely(page_memcg(page)))
bad_reason = "page still charged to cgroup"; bad_reason = "page still charged to cgroup";
#endif #endif
bad_page(page, bad_reason, bad_flags); bad_page(page, bad_reason, bad_flags);
@@ -2014,7 +2014,7 @@ static void check_new_page_bad(struct page *page)
bad_flags = PAGE_FLAGS_CHECK_AT_PREP; bad_flags = PAGE_FLAGS_CHECK_AT_PREP;
} }
#ifdef CONFIG_MEMCG #ifdef CONFIG_MEMCG
if (unlikely(page->mem_cgroup)) if (unlikely(page_memcg(page)))
bad_reason = "page still charged to cgroup"; bad_reason = "page still charged to cgroup";
#endif #endif
bad_page(page, bad_reason, bad_flags); bad_page(page, bad_reason, bad_flags);
+1 -1
View File
@@ -229,7 +229,7 @@ void *workingset_eviction(struct address_space *mapping, struct page *page)
unsigned long eviction; unsigned long eviction;
struct lruvec *lruvec; struct lruvec *lruvec;
/* Page is fully exclusive and pins page->mem_cgroup */ /* Page is fully exclusive and pins page's memory cgroup pointer */
VM_BUG_ON_PAGE(PageLRU(page), page); VM_BUG_ON_PAGE(PageLRU(page), page);
VM_BUG_ON_PAGE(page_count(page), page); VM_BUG_ON_PAGE(page_count(page), page);
VM_BUG_ON_PAGE(!PageLocked(page), page); VM_BUG_ON_PAGE(!PageLocked(page), page);