18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * Copyright (c) 2013 Red Hat, Inc. and Parallels Inc. All rights reserved.
48c2ecf20Sopenharmony_ci * Authors: David Chinner and Glauber Costa
58c2ecf20Sopenharmony_ci *
68c2ecf20Sopenharmony_ci * Generic LRU infrastructure
78c2ecf20Sopenharmony_ci */
88c2ecf20Sopenharmony_ci#include <linux/kernel.h>
98c2ecf20Sopenharmony_ci#include <linux/module.h>
108c2ecf20Sopenharmony_ci#include <linux/mm.h>
118c2ecf20Sopenharmony_ci#include <linux/list_lru.h>
128c2ecf20Sopenharmony_ci#include <linux/slab.h>
138c2ecf20Sopenharmony_ci#include <linux/mutex.h>
148c2ecf20Sopenharmony_ci#include <linux/memcontrol.h>
158c2ecf20Sopenharmony_ci#include "slab.h"
168c2ecf20Sopenharmony_ci
178c2ecf20Sopenharmony_ci#ifdef CONFIG_MEMCG_KMEM
188c2ecf20Sopenharmony_cistatic LIST_HEAD(list_lrus);
198c2ecf20Sopenharmony_cistatic DEFINE_MUTEX(list_lrus_mutex);
208c2ecf20Sopenharmony_ci
218c2ecf20Sopenharmony_cistatic void list_lru_register(struct list_lru *lru)
228c2ecf20Sopenharmony_ci{
238c2ecf20Sopenharmony_ci	mutex_lock(&list_lrus_mutex);
248c2ecf20Sopenharmony_ci	list_add(&lru->list, &list_lrus);
258c2ecf20Sopenharmony_ci	mutex_unlock(&list_lrus_mutex);
268c2ecf20Sopenharmony_ci}
278c2ecf20Sopenharmony_ci
288c2ecf20Sopenharmony_cistatic void list_lru_unregister(struct list_lru *lru)
298c2ecf20Sopenharmony_ci{
308c2ecf20Sopenharmony_ci	mutex_lock(&list_lrus_mutex);
318c2ecf20Sopenharmony_ci	list_del(&lru->list);
328c2ecf20Sopenharmony_ci	mutex_unlock(&list_lrus_mutex);
338c2ecf20Sopenharmony_ci}
348c2ecf20Sopenharmony_ci
358c2ecf20Sopenharmony_cistatic int lru_shrinker_id(struct list_lru *lru)
368c2ecf20Sopenharmony_ci{
378c2ecf20Sopenharmony_ci	return lru->shrinker_id;
388c2ecf20Sopenharmony_ci}
398c2ecf20Sopenharmony_ci
408c2ecf20Sopenharmony_cistatic inline bool list_lru_memcg_aware(struct list_lru *lru)
418c2ecf20Sopenharmony_ci{
428c2ecf20Sopenharmony_ci	return lru->memcg_aware;
438c2ecf20Sopenharmony_ci}
448c2ecf20Sopenharmony_ci
458c2ecf20Sopenharmony_cistatic inline struct list_lru_one *
468c2ecf20Sopenharmony_cilist_lru_from_memcg_idx(struct list_lru_node *nlru, int idx)
478c2ecf20Sopenharmony_ci{
488c2ecf20Sopenharmony_ci	struct list_lru_memcg *memcg_lrus;
498c2ecf20Sopenharmony_ci	/*
508c2ecf20Sopenharmony_ci	 * Either lock or RCU protects the array of per cgroup lists
518c2ecf20Sopenharmony_ci	 * from relocation (see memcg_update_list_lru_node).
528c2ecf20Sopenharmony_ci	 */
538c2ecf20Sopenharmony_ci	memcg_lrus = rcu_dereference_check(nlru->memcg_lrus,
548c2ecf20Sopenharmony_ci					   lockdep_is_held(&nlru->lock));
558c2ecf20Sopenharmony_ci	if (memcg_lrus && idx >= 0)
568c2ecf20Sopenharmony_ci		return memcg_lrus->lru[idx];
578c2ecf20Sopenharmony_ci	return &nlru->lru;
588c2ecf20Sopenharmony_ci}
598c2ecf20Sopenharmony_ci
608c2ecf20Sopenharmony_cistatic inline struct list_lru_one *
618c2ecf20Sopenharmony_cilist_lru_from_kmem(struct list_lru_node *nlru, void *ptr,
628c2ecf20Sopenharmony_ci		   struct mem_cgroup **memcg_ptr)
638c2ecf20Sopenharmony_ci{
648c2ecf20Sopenharmony_ci	struct list_lru_one *l = &nlru->lru;
658c2ecf20Sopenharmony_ci	struct mem_cgroup *memcg = NULL;
668c2ecf20Sopenharmony_ci
678c2ecf20Sopenharmony_ci	if (!nlru->memcg_lrus)
688c2ecf20Sopenharmony_ci		goto out;
698c2ecf20Sopenharmony_ci
708c2ecf20Sopenharmony_ci	memcg = mem_cgroup_from_obj(ptr);
718c2ecf20Sopenharmony_ci	if (!memcg)
728c2ecf20Sopenharmony_ci		goto out;
738c2ecf20Sopenharmony_ci
748c2ecf20Sopenharmony_ci	l = list_lru_from_memcg_idx(nlru, memcg_cache_id(memcg));
758c2ecf20Sopenharmony_ciout:
768c2ecf20Sopenharmony_ci	if (memcg_ptr)
778c2ecf20Sopenharmony_ci		*memcg_ptr = memcg;
788c2ecf20Sopenharmony_ci	return l;
798c2ecf20Sopenharmony_ci}
808c2ecf20Sopenharmony_ci#else
818c2ecf20Sopenharmony_cistatic void list_lru_register(struct list_lru *lru)
828c2ecf20Sopenharmony_ci{
838c2ecf20Sopenharmony_ci}
848c2ecf20Sopenharmony_ci
858c2ecf20Sopenharmony_cistatic void list_lru_unregister(struct list_lru *lru)
868c2ecf20Sopenharmony_ci{
878c2ecf20Sopenharmony_ci}
888c2ecf20Sopenharmony_ci
898c2ecf20Sopenharmony_cistatic int lru_shrinker_id(struct list_lru *lru)
908c2ecf20Sopenharmony_ci{
918c2ecf20Sopenharmony_ci	return -1;
928c2ecf20Sopenharmony_ci}
938c2ecf20Sopenharmony_ci
948c2ecf20Sopenharmony_cistatic inline bool list_lru_memcg_aware(struct list_lru *lru)
958c2ecf20Sopenharmony_ci{
968c2ecf20Sopenharmony_ci	return false;
978c2ecf20Sopenharmony_ci}
988c2ecf20Sopenharmony_ci
998c2ecf20Sopenharmony_cistatic inline struct list_lru_one *
1008c2ecf20Sopenharmony_cilist_lru_from_memcg_idx(struct list_lru_node *nlru, int idx)
1018c2ecf20Sopenharmony_ci{
1028c2ecf20Sopenharmony_ci	return &nlru->lru;
1038c2ecf20Sopenharmony_ci}
1048c2ecf20Sopenharmony_ci
1058c2ecf20Sopenharmony_cistatic inline struct list_lru_one *
1068c2ecf20Sopenharmony_cilist_lru_from_kmem(struct list_lru_node *nlru, void *ptr,
1078c2ecf20Sopenharmony_ci		   struct mem_cgroup **memcg_ptr)
1088c2ecf20Sopenharmony_ci{
1098c2ecf20Sopenharmony_ci	if (memcg_ptr)
1108c2ecf20Sopenharmony_ci		*memcg_ptr = NULL;
1118c2ecf20Sopenharmony_ci	return &nlru->lru;
1128c2ecf20Sopenharmony_ci}
1138c2ecf20Sopenharmony_ci#endif /* CONFIG_MEMCG_KMEM */
1148c2ecf20Sopenharmony_ci
1158c2ecf20Sopenharmony_cibool list_lru_add(struct list_lru *lru, struct list_head *item)
1168c2ecf20Sopenharmony_ci{
1178c2ecf20Sopenharmony_ci	int nid = page_to_nid(virt_to_page(item));
1188c2ecf20Sopenharmony_ci	struct list_lru_node *nlru = &lru->node[nid];
1198c2ecf20Sopenharmony_ci	struct mem_cgroup *memcg;
1208c2ecf20Sopenharmony_ci	struct list_lru_one *l;
1218c2ecf20Sopenharmony_ci
1228c2ecf20Sopenharmony_ci	spin_lock(&nlru->lock);
1238c2ecf20Sopenharmony_ci	if (list_empty(item)) {
1248c2ecf20Sopenharmony_ci		l = list_lru_from_kmem(nlru, item, &memcg);
1258c2ecf20Sopenharmony_ci		list_add_tail(item, &l->list);
1268c2ecf20Sopenharmony_ci		/* Set shrinker bit if the first element was added */
1278c2ecf20Sopenharmony_ci		if (!l->nr_items++)
1288c2ecf20Sopenharmony_ci			memcg_set_shrinker_bit(memcg, nid,
1298c2ecf20Sopenharmony_ci					       lru_shrinker_id(lru));
1308c2ecf20Sopenharmony_ci		nlru->nr_items++;
1318c2ecf20Sopenharmony_ci		spin_unlock(&nlru->lock);
1328c2ecf20Sopenharmony_ci		return true;
1338c2ecf20Sopenharmony_ci	}
1348c2ecf20Sopenharmony_ci	spin_unlock(&nlru->lock);
1358c2ecf20Sopenharmony_ci	return false;
1368c2ecf20Sopenharmony_ci}
1378c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(list_lru_add);
1388c2ecf20Sopenharmony_ci
1398c2ecf20Sopenharmony_cibool list_lru_del(struct list_lru *lru, struct list_head *item)
1408c2ecf20Sopenharmony_ci{
1418c2ecf20Sopenharmony_ci	int nid = page_to_nid(virt_to_page(item));
1428c2ecf20Sopenharmony_ci	struct list_lru_node *nlru = &lru->node[nid];
1438c2ecf20Sopenharmony_ci	struct list_lru_one *l;
1448c2ecf20Sopenharmony_ci
1458c2ecf20Sopenharmony_ci	spin_lock(&nlru->lock);
1468c2ecf20Sopenharmony_ci	if (!list_empty(item)) {
1478c2ecf20Sopenharmony_ci		l = list_lru_from_kmem(nlru, item, NULL);
1488c2ecf20Sopenharmony_ci		list_del_init(item);
1498c2ecf20Sopenharmony_ci		l->nr_items--;
1508c2ecf20Sopenharmony_ci		nlru->nr_items--;
1518c2ecf20Sopenharmony_ci		spin_unlock(&nlru->lock);
1528c2ecf20Sopenharmony_ci		return true;
1538c2ecf20Sopenharmony_ci	}
1548c2ecf20Sopenharmony_ci	spin_unlock(&nlru->lock);
1558c2ecf20Sopenharmony_ci	return false;
1568c2ecf20Sopenharmony_ci}
1578c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(list_lru_del);
1588c2ecf20Sopenharmony_ci
1598c2ecf20Sopenharmony_civoid list_lru_isolate(struct list_lru_one *list, struct list_head *item)
1608c2ecf20Sopenharmony_ci{
1618c2ecf20Sopenharmony_ci	list_del_init(item);
1628c2ecf20Sopenharmony_ci	list->nr_items--;
1638c2ecf20Sopenharmony_ci}
1648c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(list_lru_isolate);
1658c2ecf20Sopenharmony_ci
1668c2ecf20Sopenharmony_civoid list_lru_isolate_move(struct list_lru_one *list, struct list_head *item,
1678c2ecf20Sopenharmony_ci			   struct list_head *head)
1688c2ecf20Sopenharmony_ci{
1698c2ecf20Sopenharmony_ci	list_move(item, head);
1708c2ecf20Sopenharmony_ci	list->nr_items--;
1718c2ecf20Sopenharmony_ci}
1728c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(list_lru_isolate_move);
1738c2ecf20Sopenharmony_ci
1748c2ecf20Sopenharmony_ciunsigned long list_lru_count_one(struct list_lru *lru,
1758c2ecf20Sopenharmony_ci				 int nid, struct mem_cgroup *memcg)
1768c2ecf20Sopenharmony_ci{
1778c2ecf20Sopenharmony_ci	struct list_lru_node *nlru = &lru->node[nid];
1788c2ecf20Sopenharmony_ci	struct list_lru_one *l;
1798c2ecf20Sopenharmony_ci	unsigned long count;
1808c2ecf20Sopenharmony_ci
1818c2ecf20Sopenharmony_ci	rcu_read_lock();
1828c2ecf20Sopenharmony_ci	l = list_lru_from_memcg_idx(nlru, memcg_cache_id(memcg));
1838c2ecf20Sopenharmony_ci	count = READ_ONCE(l->nr_items);
1848c2ecf20Sopenharmony_ci	rcu_read_unlock();
1858c2ecf20Sopenharmony_ci
1868c2ecf20Sopenharmony_ci	return count;
1878c2ecf20Sopenharmony_ci}
1888c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(list_lru_count_one);
1898c2ecf20Sopenharmony_ci
1908c2ecf20Sopenharmony_ciunsigned long list_lru_count_node(struct list_lru *lru, int nid)
1918c2ecf20Sopenharmony_ci{
1928c2ecf20Sopenharmony_ci	struct list_lru_node *nlru;
1938c2ecf20Sopenharmony_ci
1948c2ecf20Sopenharmony_ci	nlru = &lru->node[nid];
1958c2ecf20Sopenharmony_ci	return nlru->nr_items;
1968c2ecf20Sopenharmony_ci}
1978c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(list_lru_count_node);
1988c2ecf20Sopenharmony_ci
1998c2ecf20Sopenharmony_cistatic unsigned long
2008c2ecf20Sopenharmony_ci__list_lru_walk_one(struct list_lru_node *nlru, int memcg_idx,
2018c2ecf20Sopenharmony_ci		    list_lru_walk_cb isolate, void *cb_arg,
2028c2ecf20Sopenharmony_ci		    unsigned long *nr_to_walk)
2038c2ecf20Sopenharmony_ci{
2048c2ecf20Sopenharmony_ci
2058c2ecf20Sopenharmony_ci	struct list_lru_one *l;
2068c2ecf20Sopenharmony_ci	struct list_head *item, *n;
2078c2ecf20Sopenharmony_ci	unsigned long isolated = 0;
2088c2ecf20Sopenharmony_ci
2098c2ecf20Sopenharmony_ci	l = list_lru_from_memcg_idx(nlru, memcg_idx);
2108c2ecf20Sopenharmony_cirestart:
2118c2ecf20Sopenharmony_ci	list_for_each_safe(item, n, &l->list) {
2128c2ecf20Sopenharmony_ci		enum lru_status ret;
2138c2ecf20Sopenharmony_ci
2148c2ecf20Sopenharmony_ci		/*
2158c2ecf20Sopenharmony_ci		 * decrement nr_to_walk first so that we don't livelock if we
2168c2ecf20Sopenharmony_ci		 * get stuck on large numbers of LRU_RETRY items
2178c2ecf20Sopenharmony_ci		 */
2188c2ecf20Sopenharmony_ci		if (!*nr_to_walk)
2198c2ecf20Sopenharmony_ci			break;
2208c2ecf20Sopenharmony_ci		--*nr_to_walk;
2218c2ecf20Sopenharmony_ci
2228c2ecf20Sopenharmony_ci		ret = isolate(item, l, &nlru->lock, cb_arg);
2238c2ecf20Sopenharmony_ci		switch (ret) {
2248c2ecf20Sopenharmony_ci		case LRU_REMOVED_RETRY:
2258c2ecf20Sopenharmony_ci			assert_spin_locked(&nlru->lock);
2268c2ecf20Sopenharmony_ci			fallthrough;
2278c2ecf20Sopenharmony_ci		case LRU_REMOVED:
2288c2ecf20Sopenharmony_ci			isolated++;
2298c2ecf20Sopenharmony_ci			nlru->nr_items--;
2308c2ecf20Sopenharmony_ci			/*
2318c2ecf20Sopenharmony_ci			 * If the lru lock has been dropped, our list
2328c2ecf20Sopenharmony_ci			 * traversal is now invalid and so we have to
2338c2ecf20Sopenharmony_ci			 * restart from scratch.
2348c2ecf20Sopenharmony_ci			 */
2358c2ecf20Sopenharmony_ci			if (ret == LRU_REMOVED_RETRY)
2368c2ecf20Sopenharmony_ci				goto restart;
2378c2ecf20Sopenharmony_ci			break;
2388c2ecf20Sopenharmony_ci		case LRU_ROTATE:
2398c2ecf20Sopenharmony_ci			list_move_tail(item, &l->list);
2408c2ecf20Sopenharmony_ci			break;
2418c2ecf20Sopenharmony_ci		case LRU_SKIP:
2428c2ecf20Sopenharmony_ci			break;
2438c2ecf20Sopenharmony_ci		case LRU_RETRY:
2448c2ecf20Sopenharmony_ci			/*
2458c2ecf20Sopenharmony_ci			 * The lru lock has been dropped, our list traversal is
2468c2ecf20Sopenharmony_ci			 * now invalid and so we have to restart from scratch.
2478c2ecf20Sopenharmony_ci			 */
2488c2ecf20Sopenharmony_ci			assert_spin_locked(&nlru->lock);
2498c2ecf20Sopenharmony_ci			goto restart;
2508c2ecf20Sopenharmony_ci		default:
2518c2ecf20Sopenharmony_ci			BUG();
2528c2ecf20Sopenharmony_ci		}
2538c2ecf20Sopenharmony_ci	}
2548c2ecf20Sopenharmony_ci	return isolated;
2558c2ecf20Sopenharmony_ci}
2568c2ecf20Sopenharmony_ci
2578c2ecf20Sopenharmony_ciunsigned long
2588c2ecf20Sopenharmony_cilist_lru_walk_one(struct list_lru *lru, int nid, struct mem_cgroup *memcg,
2598c2ecf20Sopenharmony_ci		  list_lru_walk_cb isolate, void *cb_arg,
2608c2ecf20Sopenharmony_ci		  unsigned long *nr_to_walk)
2618c2ecf20Sopenharmony_ci{
2628c2ecf20Sopenharmony_ci	struct list_lru_node *nlru = &lru->node[nid];
2638c2ecf20Sopenharmony_ci	unsigned long ret;
2648c2ecf20Sopenharmony_ci
2658c2ecf20Sopenharmony_ci	spin_lock(&nlru->lock);
2668c2ecf20Sopenharmony_ci	ret = __list_lru_walk_one(nlru, memcg_cache_id(memcg), isolate, cb_arg,
2678c2ecf20Sopenharmony_ci				  nr_to_walk);
2688c2ecf20Sopenharmony_ci	spin_unlock(&nlru->lock);
2698c2ecf20Sopenharmony_ci	return ret;
2708c2ecf20Sopenharmony_ci}
2718c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(list_lru_walk_one);
2728c2ecf20Sopenharmony_ci
2738c2ecf20Sopenharmony_ciunsigned long
2748c2ecf20Sopenharmony_cilist_lru_walk_one_irq(struct list_lru *lru, int nid, struct mem_cgroup *memcg,
2758c2ecf20Sopenharmony_ci		      list_lru_walk_cb isolate, void *cb_arg,
2768c2ecf20Sopenharmony_ci		      unsigned long *nr_to_walk)
2778c2ecf20Sopenharmony_ci{
2788c2ecf20Sopenharmony_ci	struct list_lru_node *nlru = &lru->node[nid];
2798c2ecf20Sopenharmony_ci	unsigned long ret;
2808c2ecf20Sopenharmony_ci
2818c2ecf20Sopenharmony_ci	spin_lock_irq(&nlru->lock);
2828c2ecf20Sopenharmony_ci	ret = __list_lru_walk_one(nlru, memcg_cache_id(memcg), isolate, cb_arg,
2838c2ecf20Sopenharmony_ci				  nr_to_walk);
2848c2ecf20Sopenharmony_ci	spin_unlock_irq(&nlru->lock);
2858c2ecf20Sopenharmony_ci	return ret;
2868c2ecf20Sopenharmony_ci}
2878c2ecf20Sopenharmony_ci
2888c2ecf20Sopenharmony_ciunsigned long list_lru_walk_node(struct list_lru *lru, int nid,
2898c2ecf20Sopenharmony_ci				 list_lru_walk_cb isolate, void *cb_arg,
2908c2ecf20Sopenharmony_ci				 unsigned long *nr_to_walk)
2918c2ecf20Sopenharmony_ci{
2928c2ecf20Sopenharmony_ci	long isolated = 0;
2938c2ecf20Sopenharmony_ci	int memcg_idx;
2948c2ecf20Sopenharmony_ci
2958c2ecf20Sopenharmony_ci	isolated += list_lru_walk_one(lru, nid, NULL, isolate, cb_arg,
2968c2ecf20Sopenharmony_ci				      nr_to_walk);
2978c2ecf20Sopenharmony_ci	if (*nr_to_walk > 0 && list_lru_memcg_aware(lru)) {
2988c2ecf20Sopenharmony_ci		for_each_memcg_cache_index(memcg_idx) {
2998c2ecf20Sopenharmony_ci			struct list_lru_node *nlru = &lru->node[nid];
3008c2ecf20Sopenharmony_ci
3018c2ecf20Sopenharmony_ci			spin_lock(&nlru->lock);
3028c2ecf20Sopenharmony_ci			isolated += __list_lru_walk_one(nlru, memcg_idx,
3038c2ecf20Sopenharmony_ci							isolate, cb_arg,
3048c2ecf20Sopenharmony_ci							nr_to_walk);
3058c2ecf20Sopenharmony_ci			spin_unlock(&nlru->lock);
3068c2ecf20Sopenharmony_ci
3078c2ecf20Sopenharmony_ci			if (*nr_to_walk <= 0)
3088c2ecf20Sopenharmony_ci				break;
3098c2ecf20Sopenharmony_ci		}
3108c2ecf20Sopenharmony_ci	}
3118c2ecf20Sopenharmony_ci	return isolated;
3128c2ecf20Sopenharmony_ci}
3138c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(list_lru_walk_node);
3148c2ecf20Sopenharmony_ci
3158c2ecf20Sopenharmony_cistatic void init_one_lru(struct list_lru_one *l)
3168c2ecf20Sopenharmony_ci{
3178c2ecf20Sopenharmony_ci	INIT_LIST_HEAD(&l->list);
3188c2ecf20Sopenharmony_ci	l->nr_items = 0;
3198c2ecf20Sopenharmony_ci}
3208c2ecf20Sopenharmony_ci
3218c2ecf20Sopenharmony_ci#ifdef CONFIG_MEMCG_KMEM
3228c2ecf20Sopenharmony_cistatic void __memcg_destroy_list_lru_node(struct list_lru_memcg *memcg_lrus,
3238c2ecf20Sopenharmony_ci					  int begin, int end)
3248c2ecf20Sopenharmony_ci{
3258c2ecf20Sopenharmony_ci	int i;
3268c2ecf20Sopenharmony_ci
3278c2ecf20Sopenharmony_ci	for (i = begin; i < end; i++)
3288c2ecf20Sopenharmony_ci		kfree(memcg_lrus->lru[i]);
3298c2ecf20Sopenharmony_ci}
3308c2ecf20Sopenharmony_ci
3318c2ecf20Sopenharmony_cistatic int __memcg_init_list_lru_node(struct list_lru_memcg *memcg_lrus,
3328c2ecf20Sopenharmony_ci				      int begin, int end)
3338c2ecf20Sopenharmony_ci{
3348c2ecf20Sopenharmony_ci	int i;
3358c2ecf20Sopenharmony_ci
3368c2ecf20Sopenharmony_ci	for (i = begin; i < end; i++) {
3378c2ecf20Sopenharmony_ci		struct list_lru_one *l;
3388c2ecf20Sopenharmony_ci
3398c2ecf20Sopenharmony_ci		l = kmalloc(sizeof(struct list_lru_one), GFP_KERNEL);
3408c2ecf20Sopenharmony_ci		if (!l)
3418c2ecf20Sopenharmony_ci			goto fail;
3428c2ecf20Sopenharmony_ci
3438c2ecf20Sopenharmony_ci		init_one_lru(l);
3448c2ecf20Sopenharmony_ci		memcg_lrus->lru[i] = l;
3458c2ecf20Sopenharmony_ci	}
3468c2ecf20Sopenharmony_ci	return 0;
3478c2ecf20Sopenharmony_cifail:
3488c2ecf20Sopenharmony_ci	__memcg_destroy_list_lru_node(memcg_lrus, begin, i);
3498c2ecf20Sopenharmony_ci	return -ENOMEM;
3508c2ecf20Sopenharmony_ci}
3518c2ecf20Sopenharmony_ci
3528c2ecf20Sopenharmony_cistatic int memcg_init_list_lru_node(struct list_lru_node *nlru)
3538c2ecf20Sopenharmony_ci{
3548c2ecf20Sopenharmony_ci	struct list_lru_memcg *memcg_lrus;
3558c2ecf20Sopenharmony_ci	int size = memcg_nr_cache_ids;
3568c2ecf20Sopenharmony_ci
3578c2ecf20Sopenharmony_ci	memcg_lrus = kvmalloc(sizeof(*memcg_lrus) +
3588c2ecf20Sopenharmony_ci			      size * sizeof(void *), GFP_KERNEL);
3598c2ecf20Sopenharmony_ci	if (!memcg_lrus)
3608c2ecf20Sopenharmony_ci		return -ENOMEM;
3618c2ecf20Sopenharmony_ci
3628c2ecf20Sopenharmony_ci	if (__memcg_init_list_lru_node(memcg_lrus, 0, size)) {
3638c2ecf20Sopenharmony_ci		kvfree(memcg_lrus);
3648c2ecf20Sopenharmony_ci		return -ENOMEM;
3658c2ecf20Sopenharmony_ci	}
3668c2ecf20Sopenharmony_ci	RCU_INIT_POINTER(nlru->memcg_lrus, memcg_lrus);
3678c2ecf20Sopenharmony_ci
3688c2ecf20Sopenharmony_ci	return 0;
3698c2ecf20Sopenharmony_ci}
3708c2ecf20Sopenharmony_ci
3718c2ecf20Sopenharmony_cistatic void memcg_destroy_list_lru_node(struct list_lru_node *nlru)
3728c2ecf20Sopenharmony_ci{
3738c2ecf20Sopenharmony_ci	struct list_lru_memcg *memcg_lrus;
3748c2ecf20Sopenharmony_ci	/*
3758c2ecf20Sopenharmony_ci	 * This is called when shrinker has already been unregistered,
3768c2ecf20Sopenharmony_ci	 * and nobody can use it. So, there is no need to use kvfree_rcu_local().
3778c2ecf20Sopenharmony_ci	 */
3788c2ecf20Sopenharmony_ci	memcg_lrus = rcu_dereference_protected(nlru->memcg_lrus, true);
3798c2ecf20Sopenharmony_ci	__memcg_destroy_list_lru_node(memcg_lrus, 0, memcg_nr_cache_ids);
3808c2ecf20Sopenharmony_ci	kvfree(memcg_lrus);
3818c2ecf20Sopenharmony_ci}
3828c2ecf20Sopenharmony_ci
3838c2ecf20Sopenharmony_cistatic void kvfree_rcu_local(struct rcu_head *head)
3848c2ecf20Sopenharmony_ci{
3858c2ecf20Sopenharmony_ci	struct list_lru_memcg *mlru;
3868c2ecf20Sopenharmony_ci
3878c2ecf20Sopenharmony_ci	mlru = container_of(head, struct list_lru_memcg, rcu);
3888c2ecf20Sopenharmony_ci	kvfree(mlru);
3898c2ecf20Sopenharmony_ci}
3908c2ecf20Sopenharmony_ci
3918c2ecf20Sopenharmony_cistatic int memcg_update_list_lru_node(struct list_lru_node *nlru,
3928c2ecf20Sopenharmony_ci				      int old_size, int new_size)
3938c2ecf20Sopenharmony_ci{
3948c2ecf20Sopenharmony_ci	struct list_lru_memcg *old, *new;
3958c2ecf20Sopenharmony_ci
3968c2ecf20Sopenharmony_ci	BUG_ON(old_size > new_size);
3978c2ecf20Sopenharmony_ci
3988c2ecf20Sopenharmony_ci	old = rcu_dereference_protected(nlru->memcg_lrus,
3998c2ecf20Sopenharmony_ci					lockdep_is_held(&list_lrus_mutex));
4008c2ecf20Sopenharmony_ci	new = kvmalloc(sizeof(*new) + new_size * sizeof(void *), GFP_KERNEL);
4018c2ecf20Sopenharmony_ci	if (!new)
4028c2ecf20Sopenharmony_ci		return -ENOMEM;
4038c2ecf20Sopenharmony_ci
4048c2ecf20Sopenharmony_ci	if (__memcg_init_list_lru_node(new, old_size, new_size)) {
4058c2ecf20Sopenharmony_ci		kvfree(new);
4068c2ecf20Sopenharmony_ci		return -ENOMEM;
4078c2ecf20Sopenharmony_ci	}
4088c2ecf20Sopenharmony_ci
4098c2ecf20Sopenharmony_ci	memcpy(&new->lru, &old->lru, old_size * sizeof(void *));
4108c2ecf20Sopenharmony_ci
4118c2ecf20Sopenharmony_ci	/*
4128c2ecf20Sopenharmony_ci	 * The locking below allows readers that hold nlru->lock avoid taking
4138c2ecf20Sopenharmony_ci	 * rcu_read_lock (see list_lru_from_memcg_idx).
4148c2ecf20Sopenharmony_ci	 *
4158c2ecf20Sopenharmony_ci	 * Since list_lru_{add,del} may be called under an IRQ-safe lock,
4168c2ecf20Sopenharmony_ci	 * we have to use IRQ-safe primitives here to avoid deadlock.
4178c2ecf20Sopenharmony_ci	 */
4188c2ecf20Sopenharmony_ci	spin_lock_irq(&nlru->lock);
4198c2ecf20Sopenharmony_ci	rcu_assign_pointer(nlru->memcg_lrus, new);
4208c2ecf20Sopenharmony_ci	spin_unlock_irq(&nlru->lock);
4218c2ecf20Sopenharmony_ci
4228c2ecf20Sopenharmony_ci	call_rcu(&old->rcu, kvfree_rcu_local);
4238c2ecf20Sopenharmony_ci	return 0;
4248c2ecf20Sopenharmony_ci}
4258c2ecf20Sopenharmony_ci
4268c2ecf20Sopenharmony_cistatic void memcg_cancel_update_list_lru_node(struct list_lru_node *nlru,
4278c2ecf20Sopenharmony_ci					      int old_size, int new_size)
4288c2ecf20Sopenharmony_ci{
4298c2ecf20Sopenharmony_ci	struct list_lru_memcg *memcg_lrus;
4308c2ecf20Sopenharmony_ci
4318c2ecf20Sopenharmony_ci	memcg_lrus = rcu_dereference_protected(nlru->memcg_lrus,
4328c2ecf20Sopenharmony_ci					       lockdep_is_held(&list_lrus_mutex));
4338c2ecf20Sopenharmony_ci	/* do not bother shrinking the array back to the old size, because we
4348c2ecf20Sopenharmony_ci	 * cannot handle allocation failures here */
4358c2ecf20Sopenharmony_ci	__memcg_destroy_list_lru_node(memcg_lrus, old_size, new_size);
4368c2ecf20Sopenharmony_ci}
4378c2ecf20Sopenharmony_ci
4388c2ecf20Sopenharmony_cistatic int memcg_init_list_lru(struct list_lru *lru, bool memcg_aware)
4398c2ecf20Sopenharmony_ci{
4408c2ecf20Sopenharmony_ci	int i;
4418c2ecf20Sopenharmony_ci
4428c2ecf20Sopenharmony_ci	lru->memcg_aware = memcg_aware;
4438c2ecf20Sopenharmony_ci
4448c2ecf20Sopenharmony_ci	if (!memcg_aware)
4458c2ecf20Sopenharmony_ci		return 0;
4468c2ecf20Sopenharmony_ci
4478c2ecf20Sopenharmony_ci	for_each_node(i) {
4488c2ecf20Sopenharmony_ci		if (memcg_init_list_lru_node(&lru->node[i]))
4498c2ecf20Sopenharmony_ci			goto fail;
4508c2ecf20Sopenharmony_ci	}
4518c2ecf20Sopenharmony_ci	return 0;
4528c2ecf20Sopenharmony_cifail:
4538c2ecf20Sopenharmony_ci	for (i = i - 1; i >= 0; i--) {
4548c2ecf20Sopenharmony_ci		if (!lru->node[i].memcg_lrus)
4558c2ecf20Sopenharmony_ci			continue;
4568c2ecf20Sopenharmony_ci		memcg_destroy_list_lru_node(&lru->node[i]);
4578c2ecf20Sopenharmony_ci	}
4588c2ecf20Sopenharmony_ci	return -ENOMEM;
4598c2ecf20Sopenharmony_ci}
4608c2ecf20Sopenharmony_ci
4618c2ecf20Sopenharmony_cistatic void memcg_destroy_list_lru(struct list_lru *lru)
4628c2ecf20Sopenharmony_ci{
4638c2ecf20Sopenharmony_ci	int i;
4648c2ecf20Sopenharmony_ci
4658c2ecf20Sopenharmony_ci	if (!list_lru_memcg_aware(lru))
4668c2ecf20Sopenharmony_ci		return;
4678c2ecf20Sopenharmony_ci
4688c2ecf20Sopenharmony_ci	for_each_node(i)
4698c2ecf20Sopenharmony_ci		memcg_destroy_list_lru_node(&lru->node[i]);
4708c2ecf20Sopenharmony_ci}
4718c2ecf20Sopenharmony_ci
4728c2ecf20Sopenharmony_cistatic int memcg_update_list_lru(struct list_lru *lru,
4738c2ecf20Sopenharmony_ci				 int old_size, int new_size)
4748c2ecf20Sopenharmony_ci{
4758c2ecf20Sopenharmony_ci	int i;
4768c2ecf20Sopenharmony_ci
4778c2ecf20Sopenharmony_ci	if (!list_lru_memcg_aware(lru))
4788c2ecf20Sopenharmony_ci		return 0;
4798c2ecf20Sopenharmony_ci
4808c2ecf20Sopenharmony_ci	for_each_node(i) {
4818c2ecf20Sopenharmony_ci		if (memcg_update_list_lru_node(&lru->node[i],
4828c2ecf20Sopenharmony_ci					       old_size, new_size))
4838c2ecf20Sopenharmony_ci			goto fail;
4848c2ecf20Sopenharmony_ci	}
4858c2ecf20Sopenharmony_ci	return 0;
4868c2ecf20Sopenharmony_cifail:
4878c2ecf20Sopenharmony_ci	for (i = i - 1; i >= 0; i--) {
4888c2ecf20Sopenharmony_ci		if (!lru->node[i].memcg_lrus)
4898c2ecf20Sopenharmony_ci			continue;
4908c2ecf20Sopenharmony_ci
4918c2ecf20Sopenharmony_ci		memcg_cancel_update_list_lru_node(&lru->node[i],
4928c2ecf20Sopenharmony_ci						  old_size, new_size);
4938c2ecf20Sopenharmony_ci	}
4948c2ecf20Sopenharmony_ci	return -ENOMEM;
4958c2ecf20Sopenharmony_ci}
4968c2ecf20Sopenharmony_ci
4978c2ecf20Sopenharmony_cistatic void memcg_cancel_update_list_lru(struct list_lru *lru,
4988c2ecf20Sopenharmony_ci					 int old_size, int new_size)
4998c2ecf20Sopenharmony_ci{
5008c2ecf20Sopenharmony_ci	int i;
5018c2ecf20Sopenharmony_ci
5028c2ecf20Sopenharmony_ci	if (!list_lru_memcg_aware(lru))
5038c2ecf20Sopenharmony_ci		return;
5048c2ecf20Sopenharmony_ci
5058c2ecf20Sopenharmony_ci	for_each_node(i)
5068c2ecf20Sopenharmony_ci		memcg_cancel_update_list_lru_node(&lru->node[i],
5078c2ecf20Sopenharmony_ci						  old_size, new_size);
5088c2ecf20Sopenharmony_ci}
5098c2ecf20Sopenharmony_ci
5108c2ecf20Sopenharmony_ciint memcg_update_all_list_lrus(int new_size)
5118c2ecf20Sopenharmony_ci{
5128c2ecf20Sopenharmony_ci	int ret = 0;
5138c2ecf20Sopenharmony_ci	struct list_lru *lru;
5148c2ecf20Sopenharmony_ci	int old_size = memcg_nr_cache_ids;
5158c2ecf20Sopenharmony_ci
5168c2ecf20Sopenharmony_ci	mutex_lock(&list_lrus_mutex);
5178c2ecf20Sopenharmony_ci	list_for_each_entry(lru, &list_lrus, list) {
5188c2ecf20Sopenharmony_ci		ret = memcg_update_list_lru(lru, old_size, new_size);
5198c2ecf20Sopenharmony_ci		if (ret)
5208c2ecf20Sopenharmony_ci			goto fail;
5218c2ecf20Sopenharmony_ci	}
5228c2ecf20Sopenharmony_ciout:
5238c2ecf20Sopenharmony_ci	mutex_unlock(&list_lrus_mutex);
5248c2ecf20Sopenharmony_ci	return ret;
5258c2ecf20Sopenharmony_cifail:
5268c2ecf20Sopenharmony_ci	list_for_each_entry_continue_reverse(lru, &list_lrus, list)
5278c2ecf20Sopenharmony_ci		memcg_cancel_update_list_lru(lru, old_size, new_size);
5288c2ecf20Sopenharmony_ci	goto out;
5298c2ecf20Sopenharmony_ci}
5308c2ecf20Sopenharmony_ci
5318c2ecf20Sopenharmony_cistatic void memcg_drain_list_lru_node(struct list_lru *lru, int nid,
5328c2ecf20Sopenharmony_ci				      int src_idx, struct mem_cgroup *dst_memcg)
5338c2ecf20Sopenharmony_ci{
5348c2ecf20Sopenharmony_ci	struct list_lru_node *nlru = &lru->node[nid];
5358c2ecf20Sopenharmony_ci	int dst_idx = dst_memcg->kmemcg_id;
5368c2ecf20Sopenharmony_ci	struct list_lru_one *src, *dst;
5378c2ecf20Sopenharmony_ci
5388c2ecf20Sopenharmony_ci	/*
5398c2ecf20Sopenharmony_ci	 * Since list_lru_{add,del} may be called under an IRQ-safe lock,
5408c2ecf20Sopenharmony_ci	 * we have to use IRQ-safe primitives here to avoid deadlock.
5418c2ecf20Sopenharmony_ci	 */
5428c2ecf20Sopenharmony_ci	spin_lock_irq(&nlru->lock);
5438c2ecf20Sopenharmony_ci
5448c2ecf20Sopenharmony_ci	src = list_lru_from_memcg_idx(nlru, src_idx);
5458c2ecf20Sopenharmony_ci	dst = list_lru_from_memcg_idx(nlru, dst_idx);
5468c2ecf20Sopenharmony_ci
5478c2ecf20Sopenharmony_ci	list_splice_init(&src->list, &dst->list);
5488c2ecf20Sopenharmony_ci
5498c2ecf20Sopenharmony_ci	if (src->nr_items) {
5508c2ecf20Sopenharmony_ci		dst->nr_items += src->nr_items;
5518c2ecf20Sopenharmony_ci		memcg_set_shrinker_bit(dst_memcg, nid, lru_shrinker_id(lru));
5528c2ecf20Sopenharmony_ci		src->nr_items = 0;
5538c2ecf20Sopenharmony_ci	}
5548c2ecf20Sopenharmony_ci
5558c2ecf20Sopenharmony_ci	spin_unlock_irq(&nlru->lock);
5568c2ecf20Sopenharmony_ci}
5578c2ecf20Sopenharmony_ci
5588c2ecf20Sopenharmony_cistatic void memcg_drain_list_lru(struct list_lru *lru,
5598c2ecf20Sopenharmony_ci				 int src_idx, struct mem_cgroup *dst_memcg)
5608c2ecf20Sopenharmony_ci{
5618c2ecf20Sopenharmony_ci	int i;
5628c2ecf20Sopenharmony_ci
5638c2ecf20Sopenharmony_ci	if (!list_lru_memcg_aware(lru))
5648c2ecf20Sopenharmony_ci		return;
5658c2ecf20Sopenharmony_ci
5668c2ecf20Sopenharmony_ci	for_each_node(i)
5678c2ecf20Sopenharmony_ci		memcg_drain_list_lru_node(lru, i, src_idx, dst_memcg);
5688c2ecf20Sopenharmony_ci}
5698c2ecf20Sopenharmony_ci
5708c2ecf20Sopenharmony_civoid memcg_drain_all_list_lrus(int src_idx, struct mem_cgroup *dst_memcg)
5718c2ecf20Sopenharmony_ci{
5728c2ecf20Sopenharmony_ci	struct list_lru *lru;
5738c2ecf20Sopenharmony_ci
5748c2ecf20Sopenharmony_ci	mutex_lock(&list_lrus_mutex);
5758c2ecf20Sopenharmony_ci	list_for_each_entry(lru, &list_lrus, list)
5768c2ecf20Sopenharmony_ci		memcg_drain_list_lru(lru, src_idx, dst_memcg);
5778c2ecf20Sopenharmony_ci	mutex_unlock(&list_lrus_mutex);
5788c2ecf20Sopenharmony_ci}
5798c2ecf20Sopenharmony_ci#else
5808c2ecf20Sopenharmony_cistatic int memcg_init_list_lru(struct list_lru *lru, bool memcg_aware)
5818c2ecf20Sopenharmony_ci{
5828c2ecf20Sopenharmony_ci	return 0;
5838c2ecf20Sopenharmony_ci}
5848c2ecf20Sopenharmony_ci
5858c2ecf20Sopenharmony_cistatic void memcg_destroy_list_lru(struct list_lru *lru)
5868c2ecf20Sopenharmony_ci{
5878c2ecf20Sopenharmony_ci}
5888c2ecf20Sopenharmony_ci#endif /* CONFIG_MEMCG_KMEM */
5898c2ecf20Sopenharmony_ci
5908c2ecf20Sopenharmony_ciint __list_lru_init(struct list_lru *lru, bool memcg_aware,
5918c2ecf20Sopenharmony_ci		    struct lock_class_key *key, struct shrinker *shrinker)
5928c2ecf20Sopenharmony_ci{
5938c2ecf20Sopenharmony_ci	int i;
5948c2ecf20Sopenharmony_ci	int err = -ENOMEM;
5958c2ecf20Sopenharmony_ci
5968c2ecf20Sopenharmony_ci#ifdef CONFIG_MEMCG_KMEM
5978c2ecf20Sopenharmony_ci	if (shrinker)
5988c2ecf20Sopenharmony_ci		lru->shrinker_id = shrinker->id;
5998c2ecf20Sopenharmony_ci	else
6008c2ecf20Sopenharmony_ci		lru->shrinker_id = -1;
6018c2ecf20Sopenharmony_ci#endif
6028c2ecf20Sopenharmony_ci	memcg_get_cache_ids();
6038c2ecf20Sopenharmony_ci
6048c2ecf20Sopenharmony_ci	lru->node = kcalloc(nr_node_ids, sizeof(*lru->node), GFP_KERNEL);
6058c2ecf20Sopenharmony_ci	if (!lru->node)
6068c2ecf20Sopenharmony_ci		goto out;
6078c2ecf20Sopenharmony_ci
6088c2ecf20Sopenharmony_ci	for_each_node(i) {
6098c2ecf20Sopenharmony_ci		spin_lock_init(&lru->node[i].lock);
6108c2ecf20Sopenharmony_ci		if (key)
6118c2ecf20Sopenharmony_ci			lockdep_set_class(&lru->node[i].lock, key);
6128c2ecf20Sopenharmony_ci		init_one_lru(&lru->node[i].lru);
6138c2ecf20Sopenharmony_ci	}
6148c2ecf20Sopenharmony_ci
6158c2ecf20Sopenharmony_ci	err = memcg_init_list_lru(lru, memcg_aware);
6168c2ecf20Sopenharmony_ci	if (err) {
6178c2ecf20Sopenharmony_ci		kfree(lru->node);
6188c2ecf20Sopenharmony_ci		/* Do this so a list_lru_destroy() doesn't crash: */
6198c2ecf20Sopenharmony_ci		lru->node = NULL;
6208c2ecf20Sopenharmony_ci		goto out;
6218c2ecf20Sopenharmony_ci	}
6228c2ecf20Sopenharmony_ci
6238c2ecf20Sopenharmony_ci	list_lru_register(lru);
6248c2ecf20Sopenharmony_ciout:
6258c2ecf20Sopenharmony_ci	memcg_put_cache_ids();
6268c2ecf20Sopenharmony_ci	return err;
6278c2ecf20Sopenharmony_ci}
6288c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(__list_lru_init);
6298c2ecf20Sopenharmony_ci
6308c2ecf20Sopenharmony_civoid list_lru_destroy(struct list_lru *lru)
6318c2ecf20Sopenharmony_ci{
6328c2ecf20Sopenharmony_ci	/* Already destroyed or not yet initialized? */
6338c2ecf20Sopenharmony_ci	if (!lru->node)
6348c2ecf20Sopenharmony_ci		return;
6358c2ecf20Sopenharmony_ci
6368c2ecf20Sopenharmony_ci	memcg_get_cache_ids();
6378c2ecf20Sopenharmony_ci
6388c2ecf20Sopenharmony_ci	list_lru_unregister(lru);
6398c2ecf20Sopenharmony_ci
6408c2ecf20Sopenharmony_ci	memcg_destroy_list_lru(lru);
6418c2ecf20Sopenharmony_ci	kfree(lru->node);
6428c2ecf20Sopenharmony_ci	lru->node = NULL;
6438c2ecf20Sopenharmony_ci
6448c2ecf20Sopenharmony_ci#ifdef CONFIG_MEMCG_KMEM
6458c2ecf20Sopenharmony_ci	lru->shrinker_id = -1;
6468c2ecf20Sopenharmony_ci#endif
6478c2ecf20Sopenharmony_ci	memcg_put_cache_ids();
6488c2ecf20Sopenharmony_ci}
6498c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(list_lru_destroy);
650