Skip to content

Commit ec9f023

Browse files
rgushchintorvalds
authored andcommitted
mm: workingset: fix vmstat counters for shadow nodes
Memcg counters for shadow nodes are broken because the memcg pointer is obtained in a wrong way. The following approach is used: virt_to_page(xa_node)->mem_cgroup Since commit 4d96ba3 ("mm: memcg/slab: stop setting page->mem_cgroup pointer for slab pages") page->mem_cgroup pointer isn't set for slab pages, so memcg_from_slab_page() should be used instead. Also I doubt that it ever worked correctly: virt_to_head_page() should be used instead of virt_to_page(). Otherwise objects residing on tail pages are not accounted, because only the head page contains a valid mem_cgroup pointer. That was a case since the introduction of these counters by the commit 68d48e6 ("mm: workingset: add vmstat counter for shadow nodes"). Link: http://lkml.kernel.org/r/[email protected] Fixes: 4d96ba3 ("mm: memcg/slab: stop setting page->mem_cgroup pointer for slab pages") Signed-off-by: Roman Gushchin <[email protected]> Acked-by: Johannes Weiner <[email protected]> Cc: Vladimir Davydov <[email protected]> Cc: Shakeel Butt <[email protected]> Cc: Michal Hocko <[email protected]> Signed-off-by: Andrew Morton <[email protected]> Signed-off-by: Linus Torvalds <[email protected]>
1 parent 9515316 commit ec9f023

File tree

3 files changed

+43
-6
lines changed

3 files changed

+43
-6
lines changed

include/linux/memcontrol.h

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -668,6 +668,7 @@ static inline unsigned long lruvec_page_state_local(struct lruvec *lruvec,
668668

669669
void __mod_lruvec_state(struct lruvec *lruvec, enum node_stat_item idx,
670670
int val);
671+
void __mod_lruvec_slab_state(void *p, enum node_stat_item idx, int val);
671672

672673
static inline void mod_lruvec_state(struct lruvec *lruvec,
673674
enum node_stat_item idx, int val)
@@ -1072,6 +1073,14 @@ static inline void mod_lruvec_page_state(struct page *page,
10721073
mod_node_page_state(page_pgdat(page), idx, val);
10731074
}
10741075

1076+
static inline void __mod_lruvec_slab_state(void *p, enum node_stat_item idx,
1077+
int val)
1078+
{
1079+
struct page *page = virt_to_head_page(p);
1080+
1081+
__mod_node_page_state(page_pgdat(page), idx, val);
1082+
}
1083+
10751084
static inline
10761085
unsigned long mem_cgroup_soft_limit_reclaim(pg_data_t *pgdat, int order,
10771086
gfp_t gfp_mask,
@@ -1159,6 +1168,16 @@ static inline void __dec_lruvec_page_state(struct page *page,
11591168
__mod_lruvec_page_state(page, idx, -1);
11601169
}
11611170

1171+
static inline void __inc_lruvec_slab_state(void *p, enum node_stat_item idx)
1172+
{
1173+
__mod_lruvec_slab_state(p, idx, 1);
1174+
}
1175+
1176+
static inline void __dec_lruvec_slab_state(void *p, enum node_stat_item idx)
1177+
{
1178+
__mod_lruvec_slab_state(p, idx, -1);
1179+
}
1180+
11621181
/* idx can be of type enum memcg_stat_item or node_stat_item */
11631182
static inline void inc_memcg_state(struct mem_cgroup *memcg,
11641183
int idx)

mm/memcontrol.c

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -768,6 +768,26 @@ void __mod_lruvec_state(struct lruvec *lruvec, enum node_stat_item idx,
768768
__this_cpu_write(pn->lruvec_stat_cpu->count[idx], x);
769769
}
770770

771+
void __mod_lruvec_slab_state(void *p, enum node_stat_item idx, int val)
772+
{
773+
struct page *page = virt_to_head_page(p);
774+
pg_data_t *pgdat = page_pgdat(page);
775+
struct mem_cgroup *memcg;
776+
struct lruvec *lruvec;
777+
778+
rcu_read_lock();
779+
memcg = memcg_from_slab_page(page);
780+
781+
/* Untracked pages have no memcg, no lruvec. Update only the node */
782+
if (!memcg || memcg == root_mem_cgroup) {
783+
__mod_node_page_state(pgdat, idx, val);
784+
} else {
785+
lruvec = mem_cgroup_lruvec(pgdat, memcg);
786+
__mod_lruvec_state(lruvec, idx, val);
787+
}
788+
rcu_read_unlock();
789+
}
790+
771791
/**
772792
* __count_memcg_events - account VM events in a cgroup
773793
* @memcg: the memory cgroup

mm/workingset.c

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -380,14 +380,12 @@ void workingset_update_node(struct xa_node *node)
380380
if (node->count && node->count == node->nr_values) {
381381
if (list_empty(&node->private_list)) {
382382
list_lru_add(&shadow_nodes, &node->private_list);
383-
__inc_lruvec_page_state(virt_to_page(node),
384-
WORKINGSET_NODES);
383+
__inc_lruvec_slab_state(node, WORKINGSET_NODES);
385384
}
386385
} else {
387386
if (!list_empty(&node->private_list)) {
388387
list_lru_del(&shadow_nodes, &node->private_list);
389-
__dec_lruvec_page_state(virt_to_page(node),
390-
WORKINGSET_NODES);
388+
__dec_lruvec_slab_state(node, WORKINGSET_NODES);
391389
}
392390
}
393391
}
@@ -480,7 +478,7 @@ static enum lru_status shadow_lru_isolate(struct list_head *item,
480478
}
481479

482480
list_lru_isolate(lru, item);
483-
__dec_lruvec_page_state(virt_to_page(node), WORKINGSET_NODES);
481+
__dec_lruvec_slab_state(node, WORKINGSET_NODES);
484482

485483
spin_unlock(lru_lock);
486484

@@ -503,7 +501,7 @@ static enum lru_status shadow_lru_isolate(struct list_head *item,
503501
* shadow entries we were tracking ...
504502
*/
505503
xas_store(&xas, NULL);
506-
__inc_lruvec_page_state(virt_to_page(node), WORKINGSET_NODERECLAIM);
504+
__inc_lruvec_slab_state(node, WORKINGSET_NODERECLAIM);
507505

508506
out_invalid:
509507
xa_unlock_irq(&mapping->i_pages);

0 commit comments

Comments
 (0)