1457c8996SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only
2a38e4082SDave Chinner /*
3a38e4082SDave Chinner * Copyright (c) 2013 Red Hat, Inc. and Parallels Inc. All rights reserved.
4a38e4082SDave Chinner * Authors: David Chinner and Glauber Costa
5a38e4082SDave Chinner *
6a38e4082SDave Chinner * Generic LRU infrastructure
7a38e4082SDave Chinner */
8a38e4082SDave Chinner #include <linux/kernel.h>
9a38e4082SDave Chinner #include <linux/module.h>
103b1d58a4SDave Chinner #include <linux/mm.h>
11a38e4082SDave Chinner #include <linux/list_lru.h>
125ca302c8SGlauber Costa #include <linux/slab.h>
13c0a5b560SVladimir Davydov #include <linux/mutex.h>
1460d3fd32SVladimir Davydov #include <linux/memcontrol.h>
154d96ba35SRoman Gushchin #include "slab.h"
1688f2ef73SMuchun Song #include "internal.h"
17c0a5b560SVladimir Davydov
183a3b7fecSJohannes Weiner #ifdef CONFIG_MEMCG
193eef1127SMuchun Song static LIST_HEAD(memcg_list_lrus);
20c0a5b560SVladimir Davydov static DEFINE_MUTEX(list_lrus_mutex);
21c0a5b560SVladimir Davydov
list_lru_memcg_aware(struct list_lru * lru)223eef1127SMuchun Song static inline bool list_lru_memcg_aware(struct list_lru *lru)
233eef1127SMuchun Song {
243eef1127SMuchun Song return lru->memcg_aware;
253eef1127SMuchun Song }
263eef1127SMuchun Song
list_lru_register(struct list_lru * lru)27c0a5b560SVladimir Davydov static void list_lru_register(struct list_lru *lru)
28c0a5b560SVladimir Davydov {
293eef1127SMuchun Song if (!list_lru_memcg_aware(lru))
303eef1127SMuchun Song return;
313eef1127SMuchun Song
32c0a5b560SVladimir Davydov mutex_lock(&list_lrus_mutex);
333eef1127SMuchun Song list_add(&lru->list, &memcg_list_lrus);
34c0a5b560SVladimir Davydov mutex_unlock(&list_lrus_mutex);
35c0a5b560SVladimir Davydov }
36c0a5b560SVladimir Davydov
list_lru_unregister(struct list_lru * lru)37c0a5b560SVladimir Davydov static void list_lru_unregister(struct list_lru *lru)
38c0a5b560SVladimir Davydov {
393eef1127SMuchun Song if (!list_lru_memcg_aware(lru))
403eef1127SMuchun Song return;
413eef1127SMuchun Song
42c0a5b560SVladimir Davydov mutex_lock(&list_lrus_mutex);
43c0a5b560SVladimir Davydov list_del(&lru->list);
44c0a5b560SVladimir Davydov mutex_unlock(&list_lrus_mutex);
45c0a5b560SVladimir Davydov }
46c0a5b560SVladimir Davydov
lru_shrinker_id(struct list_lru * lru)47fae91d6dSKirill Tkhai static int lru_shrinker_id(struct list_lru *lru)
48fae91d6dSKirill Tkhai {
49fae91d6dSKirill Tkhai return lru->shrinker_id;
50fae91d6dSKirill Tkhai }
51fae91d6dSKirill Tkhai
5260d3fd32SVladimir Davydov static inline struct list_lru_one *
list_lru_from_memcg_idx(struct list_lru * lru,int nid,int idx)536a6b7b77SMuchun Song list_lru_from_memcg_idx(struct list_lru *lru, int nid, int idx)
5460d3fd32SVladimir Davydov {
55bbca91ccSMuchun Song if (list_lru_memcg_aware(lru) && idx >= 0) {
56d7011070SMuchun Song struct list_lru_memcg *mlru = xa_load(&lru->xa, idx);
576a6b7b77SMuchun Song
585abc1e37SMuchun Song return mlru ? &mlru->node[nid] : NULL;
595abc1e37SMuchun Song }
60bbca91ccSMuchun Song return &lru->node[nid].lru;
6160d3fd32SVladimir Davydov }
6228e98022SKairui Song
lock_list_lru(struct list_lru_one * l,bool irq)63453742baSKairui Song static inline bool lock_list_lru(struct list_lru_one *l, bool irq)
64453742baSKairui Song {
65453742baSKairui Song if (irq)
66453742baSKairui Song spin_lock_irq(&l->lock);
67453742baSKairui Song else
68453742baSKairui Song spin_lock(&l->lock);
69453742baSKairui Song if (unlikely(READ_ONCE(l->nr_items) == LONG_MIN)) {
70453742baSKairui Song if (irq)
71453742baSKairui Song spin_unlock_irq(&l->lock);
72453742baSKairui Song else
73453742baSKairui Song spin_unlock(&l->lock);
74453742baSKairui Song return false;
75453742baSKairui Song }
76453742baSKairui Song return true;
77453742baSKairui Song }
78453742baSKairui Song
7928e98022SKairui Song static inline struct list_lru_one *
lock_list_lru_of_memcg(struct list_lru * lru,int nid,struct mem_cgroup * memcg,bool irq,bool skip_empty)80fb56fdf8SKairui Song lock_list_lru_of_memcg(struct list_lru *lru, int nid, struct mem_cgroup *memcg,
81fb56fdf8SKairui Song bool irq, bool skip_empty)
8228e98022SKairui Song {
8328e98022SKairui Song struct list_lru_one *l;
84fb56fdf8SKairui Song
85fb56fdf8SKairui Song rcu_read_lock();
8628e98022SKairui Song again:
8728e98022SKairui Song l = list_lru_from_memcg_idx(lru, nid, memcg_kmem_id(memcg));
88453742baSKairui Song if (likely(l) && lock_list_lru(l, irq)) {
89fb56fdf8SKairui Song rcu_read_unlock();
9028e98022SKairui Song return l;
91fb56fdf8SKairui Song }
92fb56fdf8SKairui Song /*
93fb56fdf8SKairui Song * Caller may simply bail out if raced with reparenting or
94fb56fdf8SKairui Song * may iterate through the list_lru and expect empty slots.
95fb56fdf8SKairui Song */
96fb56fdf8SKairui Song if (skip_empty) {
97fb56fdf8SKairui Song rcu_read_unlock();
98fb56fdf8SKairui Song return NULL;
99fb56fdf8SKairui Song }
10028e98022SKairui Song VM_WARN_ON(!css_is_dying(&memcg->css));
101fb56fdf8SKairui Song memcg = parent_mem_cgroup(memcg);
10228e98022SKairui Song goto again;
10328e98022SKairui Song }
104fb56fdf8SKairui Song
unlock_list_lru(struct list_lru_one * l,bool irq_off)105fb56fdf8SKairui Song static inline void unlock_list_lru(struct list_lru_one *l, bool irq_off)
106fb56fdf8SKairui Song {
107fb56fdf8SKairui Song if (irq_off)
108fb56fdf8SKairui Song spin_unlock_irq(&l->lock);
109fb56fdf8SKairui Song else
110fb56fdf8SKairui Song spin_unlock(&l->lock);
111fb56fdf8SKairui Song }
11260d3fd32SVladimir Davydov #else
list_lru_register(struct list_lru * lru)113e0295238SKirill Tkhai static void list_lru_register(struct list_lru *lru)
114e0295238SKirill Tkhai {
115e0295238SKirill Tkhai }
116e0295238SKirill Tkhai
list_lru_unregister(struct list_lru * lru)117e0295238SKirill Tkhai static void list_lru_unregister(struct list_lru *lru)
118e0295238SKirill Tkhai {
119e0295238SKirill Tkhai }
120e0295238SKirill Tkhai
lru_shrinker_id(struct list_lru * lru)121fae91d6dSKirill Tkhai static int lru_shrinker_id(struct list_lru *lru)
122fae91d6dSKirill Tkhai {
123fae91d6dSKirill Tkhai return -1;
124fae91d6dSKirill Tkhai }
125fae91d6dSKirill Tkhai
list_lru_memcg_aware(struct list_lru * lru)12660d3fd32SVladimir Davydov static inline bool list_lru_memcg_aware(struct list_lru *lru)
12760d3fd32SVladimir Davydov {
12860d3fd32SVladimir Davydov return false;
12960d3fd32SVladimir Davydov }
13060d3fd32SVladimir Davydov
13160d3fd32SVladimir Davydov static inline struct list_lru_one *
list_lru_from_memcg_idx(struct list_lru * lru,int nid,int idx)1326a6b7b77SMuchun Song list_lru_from_memcg_idx(struct list_lru *lru, int nid, int idx)
13360d3fd32SVladimir Davydov {
1346a6b7b77SMuchun Song return &lru->node[nid].lru;
13560d3fd32SVladimir Davydov }
13628e98022SKairui Song
13728e98022SKairui Song static inline struct list_lru_one *
lock_list_lru_of_memcg(struct list_lru * lru,int nid,struct mem_cgroup * memcg,bool irq,bool skip_empty)138fb56fdf8SKairui Song lock_list_lru_of_memcg(struct list_lru *lru, int nid, struct mem_cgroup *memcg,
139fb56fdf8SKairui Song bool irq, bool skip_empty)
14028e98022SKairui Song {
141fb56fdf8SKairui Song struct list_lru_one *l = &lru->node[nid].lru;
142fb56fdf8SKairui Song
143fb56fdf8SKairui Song if (irq)
144fb56fdf8SKairui Song spin_lock_irq(&l->lock);
145fb56fdf8SKairui Song else
146fb56fdf8SKairui Song spin_lock(&l->lock);
147fb56fdf8SKairui Song
148fb56fdf8SKairui Song return l;
149fb56fdf8SKairui Song }
150fb56fdf8SKairui Song
unlock_list_lru(struct list_lru_one * l,bool irq_off)151fb56fdf8SKairui Song static inline void unlock_list_lru(struct list_lru_one *l, bool irq_off)
152fb56fdf8SKairui Song {
153fb56fdf8SKairui Song if (irq_off)
154fb56fdf8SKairui Song spin_unlock_irq(&l->lock);
155fb56fdf8SKairui Song else
156fb56fdf8SKairui Song spin_unlock(&l->lock);
15728e98022SKairui Song }
1583a3b7fecSJohannes Weiner #endif /* CONFIG_MEMCG */
15960d3fd32SVladimir Davydov
1605161b487SMuchun Song /* The caller must ensure the memcg lifetime. */
list_lru_add(struct list_lru * lru,struct list_head * item,int nid,struct mem_cgroup * memcg)1610a97c01cSNhat Pham bool list_lru_add(struct list_lru *lru, struct list_head *item, int nid,
1620a97c01cSNhat Pham struct mem_cgroup *memcg)
163a38e4082SDave Chinner {
1643b1d58a4SDave Chinner struct list_lru_node *nlru = &lru->node[nid];
16560d3fd32SVladimir Davydov struct list_lru_one *l;
1663b1d58a4SDave Chinner
167fb56fdf8SKairui Song l = lock_list_lru_of_memcg(lru, nid, memcg, false, false);
168fb56fdf8SKairui Song if (!l)
169fb56fdf8SKairui Song return false;
170a38e4082SDave Chinner if (list_empty(item)) {
17160d3fd32SVladimir Davydov list_add_tail(item, &l->list);
172fae91d6dSKirill Tkhai /* Set shrinker bit if the first element was added */
173fae91d6dSKirill Tkhai if (!l->nr_items++)
1740a97c01cSNhat Pham set_shrinker_bit(memcg, nid, lru_shrinker_id(lru));
175fb56fdf8SKairui Song unlock_list_lru(l, false);
176fb56fdf8SKairui Song atomic_long_inc(&nlru->nr_items);
177a38e4082SDave Chinner return true;
178a38e4082SDave Chinner }
179fb56fdf8SKairui Song unlock_list_lru(l, false);
180a38e4082SDave Chinner return false;
181a38e4082SDave Chinner }
182*444e2a19SDave Airlie EXPORT_SYMBOL_GPL(list_lru_add);
183a38e4082SDave Chinner
list_lru_add_obj(struct list_lru * lru,struct list_head * item)1840a97c01cSNhat Pham bool list_lru_add_obj(struct list_lru *lru, struct list_head *item)
185a38e4082SDave Chinner {
1865161b487SMuchun Song bool ret;
1873b1d58a4SDave Chinner int nid = page_to_nid(virt_to_page(item));
1880a97c01cSNhat Pham
1895161b487SMuchun Song if (list_lru_memcg_aware(lru)) {
1905161b487SMuchun Song rcu_read_lock();
19116cc8b93SJohannes Weiner ret = list_lru_add(lru, item, nid, mem_cgroup_from_virt(item));
1925161b487SMuchun Song rcu_read_unlock();
1935161b487SMuchun Song } else {
1945161b487SMuchun Song ret = list_lru_add(lru, item, nid, NULL);
1955161b487SMuchun Song }
1965161b487SMuchun Song
1975161b487SMuchun Song return ret;
1980a97c01cSNhat Pham }
1990a97c01cSNhat Pham EXPORT_SYMBOL_GPL(list_lru_add_obj);
2000a97c01cSNhat Pham
2015161b487SMuchun Song /* The caller must ensure the memcg lifetime. */
list_lru_del(struct list_lru * lru,struct list_head * item,int nid,struct mem_cgroup * memcg)2020a97c01cSNhat Pham bool list_lru_del(struct list_lru *lru, struct list_head *item, int nid,
2030a97c01cSNhat Pham struct mem_cgroup *memcg)
2040a97c01cSNhat Pham {
2053b1d58a4SDave Chinner struct list_lru_node *nlru = &lru->node[nid];
20660d3fd32SVladimir Davydov struct list_lru_one *l;
207fb56fdf8SKairui Song l = lock_list_lru_of_memcg(lru, nid, memcg, false, false);
208fb56fdf8SKairui Song if (!l)
209fb56fdf8SKairui Song return false;
210a38e4082SDave Chinner if (!list_empty(item)) {
211a38e4082SDave Chinner list_del_init(item);
21260d3fd32SVladimir Davydov l->nr_items--;
213fb56fdf8SKairui Song unlock_list_lru(l, false);
214fb56fdf8SKairui Song atomic_long_dec(&nlru->nr_items);
215a38e4082SDave Chinner return true;
216a38e4082SDave Chinner }
217fb56fdf8SKairui Song unlock_list_lru(l, false);
218a38e4082SDave Chinner return false;
219a38e4082SDave Chinner }
220a38e4082SDave Chinner
list_lru_del_obj(struct list_lru * lru,struct list_head * item)2210a97c01cSNhat Pham bool list_lru_del_obj(struct list_lru *lru, struct list_head *item)
2220a97c01cSNhat Pham {
2235161b487SMuchun Song bool ret;
2240a97c01cSNhat Pham int nid = page_to_nid(virt_to_page(item));
2250a97c01cSNhat Pham
2265161b487SMuchun Song if (list_lru_memcg_aware(lru)) {
2275161b487SMuchun Song rcu_read_lock();
22816cc8b93SJohannes Weiner ret = list_lru_del(lru, item, nid, mem_cgroup_from_virt(item));
2295161b487SMuchun Song rcu_read_unlock();
2305161b487SMuchun Song } else {
2315161b487SMuchun Song ret = list_lru_del(lru, item, nid, NULL);
2325161b487SMuchun Song }
2335161b487SMuchun Song
2345161b487SMuchun Song return ret;
2350a97c01cSNhat Pham }
2360a97c01cSNhat Pham EXPORT_SYMBOL_GPL(list_lru_del_obj);
2370a97c01cSNhat Pham
list_lru_isolate(struct list_lru_one * list,struct list_head * item)2383f97b163SVladimir Davydov void list_lru_isolate(struct list_lru_one *list, struct list_head *item)
2393f97b163SVladimir Davydov {
2403f97b163SVladimir Davydov list_del_init(item);
2413f97b163SVladimir Davydov list->nr_items--;
2423f97b163SVladimir Davydov }
2433f97b163SVladimir Davydov EXPORT_SYMBOL_GPL(list_lru_isolate);
2443f97b163SVladimir Davydov
list_lru_isolate_move(struct list_lru_one * list,struct list_head * item,struct list_head * head)2453f97b163SVladimir Davydov void list_lru_isolate_move(struct list_lru_one *list, struct list_head *item,
2463f97b163SVladimir Davydov struct list_head *head)
2473f97b163SVladimir Davydov {
2483f97b163SVladimir Davydov list_move(item, head);
2493f97b163SVladimir Davydov list->nr_items--;
2503f97b163SVladimir Davydov }
2513f97b163SVladimir Davydov EXPORT_SYMBOL_GPL(list_lru_isolate_move);
2523f97b163SVladimir Davydov
list_lru_count_one(struct list_lru * lru,int nid,struct mem_cgroup * memcg)253930eaac5SAndrew Morton unsigned long list_lru_count_one(struct list_lru *lru,
254930eaac5SAndrew Morton int nid, struct mem_cgroup *memcg)
255a38e4082SDave Chinner {
25660d3fd32SVladimir Davydov struct list_lru_one *l;
25741d17431SMuchun Song long count;
2583b1d58a4SDave Chinner
2590c7c1bedSKirill Tkhai rcu_read_lock();
2607c52f65dSMuchun Song l = list_lru_from_memcg_idx(lru, nid, memcg_kmem_id(memcg));
2615abc1e37SMuchun Song count = l ? READ_ONCE(l->nr_items) : 0;
2620c7c1bedSKirill Tkhai rcu_read_unlock();
2633b1d58a4SDave Chinner
26441d17431SMuchun Song if (unlikely(count < 0))
26541d17431SMuchun Song count = 0;
26641d17431SMuchun Song
2673b1d58a4SDave Chinner return count;
2683b1d58a4SDave Chinner }
26960d3fd32SVladimir Davydov EXPORT_SYMBOL_GPL(list_lru_count_one);
27060d3fd32SVladimir Davydov
list_lru_count_node(struct list_lru * lru,int nid)27160d3fd32SVladimir Davydov unsigned long list_lru_count_node(struct list_lru *lru, int nid)
27260d3fd32SVladimir Davydov {
2732c80cd57SSahitya Tummala struct list_lru_node *nlru;
27460d3fd32SVladimir Davydov
2752c80cd57SSahitya Tummala nlru = &lru->node[nid];
276fb56fdf8SKairui Song return atomic_long_read(&nlru->nr_items);
27760d3fd32SVladimir Davydov }
2786a4f496fSGlauber Costa EXPORT_SYMBOL_GPL(list_lru_count_node);
2793b1d58a4SDave Chinner
28060d3fd32SVladimir Davydov static unsigned long
__list_lru_walk_one(struct list_lru * lru,int nid,struct mem_cgroup * memcg,list_lru_walk_cb isolate,void * cb_arg,unsigned long * nr_to_walk,bool irq_off)281fb56fdf8SKairui Song __list_lru_walk_one(struct list_lru *lru, int nid, struct mem_cgroup *memcg,
28260d3fd32SVladimir Davydov list_lru_walk_cb isolate, void *cb_arg,
283fb56fdf8SKairui Song unsigned long *nr_to_walk, bool irq_off)
2843b1d58a4SDave Chinner {
2856a6b7b77SMuchun Song struct list_lru_node *nlru = &lru->node[nid];
286fb56fdf8SKairui Song struct list_lru_one *l = NULL;
287a38e4082SDave Chinner struct list_head *item, *n;
2883b1d58a4SDave Chinner unsigned long isolated = 0;
289a38e4082SDave Chinner
290a38e4082SDave Chinner restart:
291fb56fdf8SKairui Song l = lock_list_lru_of_memcg(lru, nid, memcg, irq_off, true);
2925abc1e37SMuchun Song if (!l)
293fb56fdf8SKairui Song return isolated;
29460d3fd32SVladimir Davydov list_for_each_safe(item, n, &l->list) {
295a38e4082SDave Chinner enum lru_status ret;
2965cedf721SDave Chinner
2975cedf721SDave Chinner /*
2985cedf721SDave Chinner * decrement nr_to_walk first so that we don't livelock if we
2993dc5f032SEthon Paul * get stuck on large numbers of LRU_RETRY items
3005cedf721SDave Chinner */
301c56b097aSRussell King if (!*nr_to_walk)
3025cedf721SDave Chinner break;
303c56b097aSRussell King --*nr_to_walk;
3045cedf721SDave Chinner
305da0c0251SKairui Song ret = isolate(item, l, cb_arg);
306a38e4082SDave Chinner switch (ret) {
307fb56fdf8SKairui Song /*
308fb56fdf8SKairui Song * LRU_RETRY, LRU_REMOVED_RETRY and LRU_STOP will drop the lru
309fb56fdf8SKairui Song * lock. List traversal will have to restart from scratch.
310fb56fdf8SKairui Song */
311fb56fdf8SKairui Song case LRU_RETRY:
312fb56fdf8SKairui Song goto restart;
313449dd698SJohannes Weiner case LRU_REMOVED_RETRY:
314e4a9bc58SJoe Perches fallthrough;
315a38e4082SDave Chinner case LRU_REMOVED:
3163b1d58a4SDave Chinner isolated++;
317fb56fdf8SKairui Song atomic_long_dec(&nlru->nr_items);
318449dd698SJohannes Weiner if (ret == LRU_REMOVED_RETRY)
319449dd698SJohannes Weiner goto restart;
320a38e4082SDave Chinner break;
321a38e4082SDave Chinner case LRU_ROTATE:
32260d3fd32SVladimir Davydov list_move_tail(item, &l->list);
323a38e4082SDave Chinner break;
324a38e4082SDave Chinner case LRU_SKIP:
325a38e4082SDave Chinner break;
326b49547adSChengming Zhou case LRU_STOP:
327b49547adSChengming Zhou goto out;
328a38e4082SDave Chinner default:
329a38e4082SDave Chinner BUG();
330a38e4082SDave Chinner }
331a38e4082SDave Chinner }
332fb56fdf8SKairui Song unlock_list_lru(l, irq_off);
3335abc1e37SMuchun Song out:
3343b1d58a4SDave Chinner return isolated;
3353b1d58a4SDave Chinner }
33660d3fd32SVladimir Davydov
33760d3fd32SVladimir Davydov unsigned long
list_lru_walk_one(struct list_lru * lru,int nid,struct mem_cgroup * memcg,list_lru_walk_cb isolate,void * cb_arg,unsigned long * nr_to_walk)33860d3fd32SVladimir Davydov list_lru_walk_one(struct list_lru *lru, int nid, struct mem_cgroup *memcg,
33960d3fd32SVladimir Davydov list_lru_walk_cb isolate, void *cb_arg,
34060d3fd32SVladimir Davydov unsigned long *nr_to_walk)
34160d3fd32SVladimir Davydov {
342fb56fdf8SKairui Song return __list_lru_walk_one(lru, nid, memcg, isolate,
343fb56fdf8SKairui Song cb_arg, nr_to_walk, false);
34460d3fd32SVladimir Davydov }
34560d3fd32SVladimir Davydov EXPORT_SYMBOL_GPL(list_lru_walk_one);
34660d3fd32SVladimir Davydov
3476b51e881SSebastian Andrzej Siewior unsigned long
list_lru_walk_one_irq(struct list_lru * lru,int nid,struct mem_cgroup * memcg,list_lru_walk_cb isolate,void * cb_arg,unsigned long * nr_to_walk)3486b51e881SSebastian Andrzej Siewior list_lru_walk_one_irq(struct list_lru *lru, int nid, struct mem_cgroup *memcg,
3496b51e881SSebastian Andrzej Siewior list_lru_walk_cb isolate, void *cb_arg,
3506b51e881SSebastian Andrzej Siewior unsigned long *nr_to_walk)
3516b51e881SSebastian Andrzej Siewior {
352fb56fdf8SKairui Song return __list_lru_walk_one(lru, nid, memcg, isolate,
353fb56fdf8SKairui Song cb_arg, nr_to_walk, true);
3546b51e881SSebastian Andrzej Siewior }
3556b51e881SSebastian Andrzej Siewior
list_lru_walk_node(struct list_lru * lru,int nid,list_lru_walk_cb isolate,void * cb_arg,unsigned long * nr_to_walk)35660d3fd32SVladimir Davydov unsigned long list_lru_walk_node(struct list_lru *lru, int nid,
35760d3fd32SVladimir Davydov list_lru_walk_cb isolate, void *cb_arg,
35860d3fd32SVladimir Davydov unsigned long *nr_to_walk)
35960d3fd32SVladimir Davydov {
36060d3fd32SVladimir Davydov long isolated = 0;
36160d3fd32SVladimir Davydov
36287a5ffc1SSebastian Andrzej Siewior isolated += list_lru_walk_one(lru, nid, NULL, isolate, cb_arg,
36360d3fd32SVladimir Davydov nr_to_walk);
364bbca91ccSMuchun Song
3653a3b7fecSJohannes Weiner #ifdef CONFIG_MEMCG
36660d3fd32SVladimir Davydov if (*nr_to_walk > 0 && list_lru_memcg_aware(lru)) {
367d7011070SMuchun Song struct list_lru_memcg *mlru;
368fb56fdf8SKairui Song struct mem_cgroup *memcg;
369bbca91ccSMuchun Song unsigned long index;
370bbca91ccSMuchun Song
371bbca91ccSMuchun Song xa_for_each(&lru->xa, index, mlru) {
372fb56fdf8SKairui Song rcu_read_lock();
373e77786b4SShakeel Butt memcg = mem_cgroup_from_private_id(index);
374fb56fdf8SKairui Song if (!mem_cgroup_tryget(memcg)) {
375fb56fdf8SKairui Song rcu_read_unlock();
376fb56fdf8SKairui Song continue;
377fb56fdf8SKairui Song }
378fb56fdf8SKairui Song rcu_read_unlock();
379fb56fdf8SKairui Song isolated += __list_lru_walk_one(lru, nid, memcg,
3806e018968SSebastian Andrzej Siewior isolate, cb_arg,
381fb56fdf8SKairui Song nr_to_walk, false);
382fb56fdf8SKairui Song mem_cgroup_put(memcg);
3836cfe57a9SSebastian Andrzej Siewior
38460d3fd32SVladimir Davydov if (*nr_to_walk <= 0)
38560d3fd32SVladimir Davydov break;
38660d3fd32SVladimir Davydov }
38760d3fd32SVladimir Davydov }
388bbca91ccSMuchun Song #endif
389bbca91ccSMuchun Song
39060d3fd32SVladimir Davydov return isolated;
39160d3fd32SVladimir Davydov }
3923b1d58a4SDave Chinner EXPORT_SYMBOL_GPL(list_lru_walk_node);
3933b1d58a4SDave Chinner
init_one_lru(struct list_lru * lru,struct list_lru_one * l)394fb56fdf8SKairui Song static void init_one_lru(struct list_lru *lru, struct list_lru_one *l)
39560d3fd32SVladimir Davydov {
39660d3fd32SVladimir Davydov INIT_LIST_HEAD(&l->list);
397fb56fdf8SKairui Song spin_lock_init(&l->lock);
39860d3fd32SVladimir Davydov l->nr_items = 0;
399fb56fdf8SKairui Song #ifdef CONFIG_LOCKDEP
400fb56fdf8SKairui Song if (lru->key)
401fb56fdf8SKairui Song lockdep_set_class(&l->lock, lru->key);
402fb56fdf8SKairui Song #endif
40360d3fd32SVladimir Davydov }
40460d3fd32SVladimir Davydov
4053a3b7fecSJohannes Weiner #ifdef CONFIG_MEMCG
memcg_init_list_lru_one(struct list_lru * lru,gfp_t gfp)406fb56fdf8SKairui Song static struct list_lru_memcg *memcg_init_list_lru_one(struct list_lru *lru, gfp_t gfp)
40788f2ef73SMuchun Song {
40888f2ef73SMuchun Song int nid;
409d7011070SMuchun Song struct list_lru_memcg *mlru;
41088f2ef73SMuchun Song
41169050f8dSKees Cook mlru = kmalloc_flex(*mlru, node, nr_node_ids, gfp);
41288f2ef73SMuchun Song if (!mlru)
41388f2ef73SMuchun Song return NULL;
41488f2ef73SMuchun Song
41588f2ef73SMuchun Song for_each_node(nid)
416fb56fdf8SKairui Song init_one_lru(lru, &mlru->node[nid]);
41788f2ef73SMuchun Song
41888f2ef73SMuchun Song return mlru;
41988f2ef73SMuchun Song }
42088f2ef73SMuchun Song
memcg_init_list_lru(struct list_lru * lru,bool memcg_aware)421bbca91ccSMuchun Song static inline void memcg_init_list_lru(struct list_lru *lru, bool memcg_aware)
42260d3fd32SVladimir Davydov {
423bbca91ccSMuchun Song if (memcg_aware)
424bbca91ccSMuchun Song xa_init_flags(&lru->xa, XA_FLAGS_LOCK_IRQ);
4256a6b7b77SMuchun Song lru->memcg_aware = memcg_aware;
42660d3fd32SVladimir Davydov }
42760d3fd32SVladimir Davydov
memcg_destroy_list_lru(struct list_lru * lru)4286a6b7b77SMuchun Song static void memcg_destroy_list_lru(struct list_lru *lru)
42960d3fd32SVladimir Davydov {
430bbca91ccSMuchun Song XA_STATE(xas, &lru->xa, 0);
431d7011070SMuchun Song struct list_lru_memcg *mlru;
4326a6b7b77SMuchun Song
4336a6b7b77SMuchun Song if (!list_lru_memcg_aware(lru))
4346a6b7b77SMuchun Song return;
4356a6b7b77SMuchun Song
436bbca91ccSMuchun Song xas_lock_irq(&xas);
437bbca91ccSMuchun Song xas_for_each(&xas, mlru, ULONG_MAX) {
438bbca91ccSMuchun Song kfree(mlru);
439bbca91ccSMuchun Song xas_store(&xas, NULL);
4400c7c1bedSKirill Tkhai }
441bbca91ccSMuchun Song xas_unlock_irq(&xas);
44260d3fd32SVladimir Davydov }
4432788cf0cSVladimir Davydov
memcg_reparent_list_lru_one(struct list_lru * lru,int nid,struct list_lru_one * src,struct mem_cgroup * dst_memcg)444fb56fdf8SKairui Song static void memcg_reparent_list_lru_one(struct list_lru *lru, int nid,
44528e98022SKairui Song struct list_lru_one *src,
44628e98022SKairui Song struct mem_cgroup *dst_memcg)
4472788cf0cSVladimir Davydov {
448fb56fdf8SKairui Song int dst_idx = dst_memcg->kmemcg_id;
44928e98022SKairui Song struct list_lru_one *dst;
4502788cf0cSVladimir Davydov
451fb56fdf8SKairui Song spin_lock_irq(&src->lock);
452fb56fdf8SKairui Song dst = list_lru_from_memcg_idx(lru, nid, dst_idx);
453fb56fdf8SKairui Song spin_lock_nested(&dst->lock, SINGLE_DEPTH_NESTING);
4542788cf0cSVladimir Davydov
4552788cf0cSVladimir Davydov list_splice_init(&src->list, &dst->list);
4568199be00SYang Shi if (src->nr_items) {
45798a6abc6SKairui Song WARN_ON(src->nr_items < 0);
4582788cf0cSVladimir Davydov dst->nr_items += src->nr_items;
4592bfd3637SYang Shi set_shrinker_bit(dst_memcg, nid, lru_shrinker_id(lru));
4608199be00SYang Shi }
461fb56fdf8SKairui Song /* Mark the list_lru_one dead */
462fb56fdf8SKairui Song src->nr_items = LONG_MIN;
463fb56fdf8SKairui Song
464fb56fdf8SKairui Song spin_unlock(&dst->lock);
465fb56fdf8SKairui Song spin_unlock_irq(&src->lock);
4662788cf0cSVladimir Davydov }
4672788cf0cSVladimir Davydov
memcg_reparent_list_lrus(struct mem_cgroup * memcg,struct mem_cgroup * parent)4681f391eb2SMuchun Song void memcg_reparent_list_lrus(struct mem_cgroup *memcg, struct mem_cgroup *parent)
4692788cf0cSVladimir Davydov {
4702788cf0cSVladimir Davydov struct list_lru *lru;
47128e98022SKairui Song int i;
4725abc1e37SMuchun Song
4732788cf0cSVladimir Davydov mutex_lock(&list_lrus_mutex);
4748d42abbfSKairui Song list_for_each_entry(lru, &memcg_list_lrus, list) {
47528e98022SKairui Song struct list_lru_memcg *mlru;
47628e98022SKairui Song XA_STATE(xas, &lru->xa, memcg->kmemcg_id);
47728e98022SKairui Song
47828e98022SKairui Song /*
47928e98022SKairui Song * Lock the Xarray to ensure no on going list_lru_memcg
48028e98022SKairui Song * allocation and further allocation will see css_is_dying().
48128e98022SKairui Song */
48228e98022SKairui Song xas_lock_irq(&xas);
48328e98022SKairui Song mlru = xas_store(&xas, NULL);
48428e98022SKairui Song xas_unlock_irq(&xas);
48528e98022SKairui Song if (!mlru)
48628e98022SKairui Song continue;
48728e98022SKairui Song
48828e98022SKairui Song /*
48928e98022SKairui Song * With Xarray value set to NULL, holding the lru lock below
49028e98022SKairui Song * prevents list_lru_{add,del,isolate} from touching the lru,
49128e98022SKairui Song * safe to reparent.
49228e98022SKairui Song */
4938d42abbfSKairui Song for_each_node(i)
494fb56fdf8SKairui Song memcg_reparent_list_lru_one(lru, i, &mlru->node[i], parent);
4958d42abbfSKairui Song
4968d42abbfSKairui Song /*
4978d42abbfSKairui Song * Here all list_lrus corresponding to the cgroup are guaranteed
4988d42abbfSKairui Song * to remain empty, we can safely free this lru, any further
4998d42abbfSKairui Song * memcg_list_lru_alloc() call will simply bail out.
5008d42abbfSKairui Song */
50128e98022SKairui Song kvfree_rcu(mlru, rcu);
5028d42abbfSKairui Song }
5032788cf0cSVladimir Davydov mutex_unlock(&list_lrus_mutex);
5042788cf0cSVladimir Davydov }
50588f2ef73SMuchun Song
memcg_list_lru_allocated(struct mem_cgroup * memcg,struct list_lru * lru)506bbca91ccSMuchun Song static inline bool memcg_list_lru_allocated(struct mem_cgroup *memcg,
50788f2ef73SMuchun Song struct list_lru *lru)
50888f2ef73SMuchun Song {
509bbca91ccSMuchun Song int idx = memcg->kmemcg_id;
51088f2ef73SMuchun Song
511bbca91ccSMuchun Song return idx < 0 || xa_load(&lru->xa, idx);
51288f2ef73SMuchun Song }
51388f2ef73SMuchun Song
memcg_list_lru_alloc(struct mem_cgroup * memcg,struct list_lru * lru,gfp_t gfp)51488f2ef73SMuchun Song int memcg_list_lru_alloc(struct mem_cgroup *memcg, struct list_lru *lru,
51588f2ef73SMuchun Song gfp_t gfp)
51688f2ef73SMuchun Song {
51788f2ef73SMuchun Song unsigned long flags;
518b9585a3fSZeng Jingxiang struct list_lru_memcg *mlru = NULL;
51928e98022SKairui Song struct mem_cgroup *pos, *parent;
520bbca91ccSMuchun Song XA_STATE(xas, &lru->xa, 0);
52188f2ef73SMuchun Song
52288f2ef73SMuchun Song if (!list_lru_memcg_aware(lru) || memcg_list_lru_allocated(memcg, lru))
52388f2ef73SMuchun Song return 0;
52488f2ef73SMuchun Song
52588f2ef73SMuchun Song gfp &= GFP_RECLAIM_MASK;
52688f2ef73SMuchun Song /*
52788f2ef73SMuchun Song * Because the list_lru can be reparented to the parent cgroup's
52888f2ef73SMuchun Song * list_lru, we should make sure that this cgroup and all its
529d7011070SMuchun Song * ancestors have allocated list_lru_memcg.
53088f2ef73SMuchun Song */
53128e98022SKairui Song do {
532bbca91ccSMuchun Song /*
53328e98022SKairui Song * Keep finding the farest parent that wasn't populated
53428e98022SKairui Song * until found memcg itself.
535bbca91ccSMuchun Song */
53628e98022SKairui Song pos = memcg;
53728e98022SKairui Song parent = parent_mem_cgroup(pos);
53828e98022SKairui Song while (!memcg_list_lru_allocated(parent, lru)) {
53928e98022SKairui Song pos = parent;
54028e98022SKairui Song parent = parent_mem_cgroup(pos);
54188f2ef73SMuchun Song }
54228e98022SKairui Song
543b9585a3fSZeng Jingxiang if (!mlru) {
544fb56fdf8SKairui Song mlru = memcg_init_list_lru_one(lru, gfp);
54528e98022SKairui Song if (!mlru)
54628e98022SKairui Song return -ENOMEM;
547b9585a3fSZeng Jingxiang }
54828e98022SKairui Song xas_set(&xas, pos->kmemcg_id);
54928e98022SKairui Song do {
55028e98022SKairui Song xas_lock_irqsave(&xas, flags);
55128e98022SKairui Song if (!xas_load(&xas) && !css_is_dying(&pos->css)) {
55228e98022SKairui Song xas_store(&xas, mlru);
55328e98022SKairui Song if (!xas_error(&xas))
55428e98022SKairui Song mlru = NULL;
555bbca91ccSMuchun Song }
556bbca91ccSMuchun Song xas_unlock_irqrestore(&xas, flags);
55728e98022SKairui Song } while (xas_nomem(&xas, gfp));
55828e98022SKairui Song } while (pos != memcg && !css_is_dying(&pos->css));
55988f2ef73SMuchun Song
560b9585a3fSZeng Jingxiang if (unlikely(mlru))
561b9585a3fSZeng Jingxiang kfree(mlru);
562b9585a3fSZeng Jingxiang
563bbca91ccSMuchun Song return xas_error(&xas);
56488f2ef73SMuchun Song }
56560d3fd32SVladimir Davydov #else
memcg_init_list_lru(struct list_lru * lru,bool memcg_aware)566bbca91ccSMuchun Song static inline void memcg_init_list_lru(struct list_lru *lru, bool memcg_aware)
56760d3fd32SVladimir Davydov {
56860d3fd32SVladimir Davydov }
56960d3fd32SVladimir Davydov
memcg_destroy_list_lru(struct list_lru * lru)57060d3fd32SVladimir Davydov static void memcg_destroy_list_lru(struct list_lru *lru)
57160d3fd32SVladimir Davydov {
57260d3fd32SVladimir Davydov }
5733a3b7fecSJohannes Weiner #endif /* CONFIG_MEMCG */
57460d3fd32SVladimir Davydov
__list_lru_init(struct list_lru * lru,bool memcg_aware,struct shrinker * shrinker)5753f28bbe5SKairui Song int __list_lru_init(struct list_lru *lru, bool memcg_aware, struct shrinker *shrinker)
576a38e4082SDave Chinner {
5773b1d58a4SDave Chinner int i;
57860d3fd32SVladimir Davydov
5793a3b7fecSJohannes Weiner #ifdef CONFIG_MEMCG
580c92e8e10SKirill Tkhai if (shrinker)
581c92e8e10SKirill Tkhai lru->shrinker_id = shrinker->id;
582c92e8e10SKirill Tkhai else
583c92e8e10SKirill Tkhai lru->shrinker_id = -1;
5840057db47SHaifeng Xu
5850057db47SHaifeng Xu if (mem_cgroup_kmem_disabled())
5860057db47SHaifeng Xu memcg_aware = false;
587c92e8e10SKirill Tkhai #endif
5885ca302c8SGlauber Costa
589bf4afc53SLinus Torvalds lru->node = kzalloc_objs(*lru->node, nr_node_ids);
5905ca302c8SGlauber Costa if (!lru->node)
591bbca91ccSMuchun Song return -ENOMEM;
592a38e4082SDave Chinner
593fb56fdf8SKairui Song for_each_node(i)
594fb56fdf8SKairui Song init_one_lru(lru, &lru->node[i].lru);
59560d3fd32SVladimir Davydov
596bbca91ccSMuchun Song memcg_init_list_lru(lru, memcg_aware);
597c0a5b560SVladimir Davydov list_lru_register(lru);
598bbca91ccSMuchun Song
599bbca91ccSMuchun Song return 0;
600a38e4082SDave Chinner }
60160d3fd32SVladimir Davydov EXPORT_SYMBOL_GPL(__list_lru_init);
6025ca302c8SGlauber Costa
list_lru_destroy(struct list_lru * lru)6035ca302c8SGlauber Costa void list_lru_destroy(struct list_lru *lru)
6045ca302c8SGlauber Costa {
605c0a5b560SVladimir Davydov /* Already destroyed or not yet initialized? */
606c0a5b560SVladimir Davydov if (!lru->node)
607c0a5b560SVladimir Davydov return;
60860d3fd32SVladimir Davydov
609c0a5b560SVladimir Davydov list_lru_unregister(lru);
61060d3fd32SVladimir Davydov
61160d3fd32SVladimir Davydov memcg_destroy_list_lru(lru);
6125ca302c8SGlauber Costa kfree(lru->node);
613c0a5b560SVladimir Davydov lru->node = NULL;
61460d3fd32SVladimir Davydov
6153a3b7fecSJohannes Weiner #ifdef CONFIG_MEMCG
616c92e8e10SKirill Tkhai lru->shrinker_id = -1;
617c92e8e10SKirill Tkhai #endif
6185ca302c8SGlauber Costa }
6195ca302c8SGlauber Costa EXPORT_SYMBOL_GPL(list_lru_destroy);
620