Unverified Commit 4eb5a1ca authored by openeuler-ci-bot's avatar openeuler-ci-bot Committed by Gitee
Browse files

!3582 Add support for memory limit

Merge Pull Request from: @ci-robot 
 
PR sync from: Ze Zuo <zuoze1@huawei.com>
https://mailweb.openeuler.org/hyperkitty/list/kernel@openeuler.org/message/CHIOHEN5YWFQTIHD2XK7QHCBGELXI26P/ 
To implement this feature, a new function called "shrink_memory" has been
added, which can recycle a specific amount of memory. This feature mainly
supports the following two functions:

1) add periodical memory reclaim
2) add pagecache limit

Above functionalities depend on CONFIG_PAGE_CACHE_LIMIT, and the interface
and functionality tests have been successfully completed.

ChangeLog:
- Some minor changes about description and code, no functional changes
- rename page_cache_shrink_memory to shrink_memory
- move the declaration of the 'shrink_memory' function to 'internal.h',
  no functional changes
- add description information for function "shrink_memory"
- update documentation for using feature alerts

Ze Zuo (2):
  mm: support periodical memory reclaim
  mm: support pagecache limit


-- 
2.25.1
 
https://gitee.com/openeuler/kernel/issues/I8NIKC 
 
Link:https://gitee.com/openeuler/kernel/pulls/3582

 

Reviewed-by: default avatarKefeng Wang <wangkefeng.wang@huawei.com>
Signed-off-by: default avatarZheng Zengkai <zhengzengkai@huawei.com>
parents 7e3bcc81 7d1031b3
Loading
Loading
Loading
Loading
+43 −0
Original line number Diff line number Diff line
@@ -75,6 +75,10 @@ Currently, these files are in /proc/sys/vm:
- watermark_boost_factor
- watermark_scale_factor
- zone_reclaim_mode
- cache_reclaim_s
- cache_reclaim_weight
- cache_reclaim_enable
- cache_limit_mbytes


admin_reserve_kbytes
@@ -1044,3 +1048,42 @@ of other processes running on other nodes will not be affected.
Allowing regular swap effectively restricts allocations to the local
node unless explicitly overridden by memory policies or cpuset
configurations.

cache_reclaim_s
===============

Cache_reclaim_s is used to set reclaim interval in periodical memory
reclaim. when periodical memory reclaim is enabled, it will relcaim
memory in every cache_reclaim_s second.


cache_reclaim_weight
====================

This is reclaim factor in every periodical reclaim. when periodical
memory reclaim is enabled, the reclaim amount in every reclaim can
calculate from:
    reclaim_amount = cache_reclaim_weigh * SWAP_CLUSTER_MAX * nr_cpus_node(nid)

SWAP_CLUSTER_MAX is defined in include/linux/swap.h.
nr_cpus_node is used to obtain the number of CPUs on node nid.

Memory reclaim use workqueue mechanism, it will block the execution of
subsequent work, if memory reclaim tasks a lot of time, time sensitive
work may be affected.

Note that if the parameters are not configured properly, such as setting
too large a memory reclaim amount, it may lead to unstable system
performance.

cache_reclaim_enable
====================

This is used to switch on/off periodical memory reclaim feature.


cache_limit_mbytes
==================

This is used to set the upper limit of page cache in megabytes.
Page cache will be reclaimed periodically if page cache is over limit.
+13 −0
Original line number Diff line number Diff line
@@ -1323,6 +1323,19 @@ config ASCEND_OOM
		0: disable oom killer
		1: enable oom killer (default,compatible with mainline)

config PAGE_CACHE_LIMIT
	bool "Support page cache limit"
	depends on MMU && SYSCTL
	default n
	help
	  Keeping a number of page cache can improve the performance of system,
	  but if there is a lot fo page cache in system, that will result in
	  short of memory, subsequent memory reclamation operations may lead
	  to performance degradation, so add periodical memory relciam to
	  avoid too many page cache.

	  if unsure, say N to disable the PAGE_CACHE_LIMIT.

source "mm/damon/Kconfig"

endmenu
+1 −0
Original line number Diff line number Diff line
@@ -141,3 +141,4 @@ obj-$(CONFIG_GENERIC_IOREMAP) += ioremap.o
obj-$(CONFIG_SHRINKER_DEBUG) += shrinker_debug.o
obj-$(CONFIG_SHARE_POOL) += share_pool.o
obj-$(CONFIG_MEMCG_MEMFS_INFO) += memcg_memfs_info.o
obj-$(CONFIG_PAGE_CACHE_LIMIT) += page_cache_limit.o
+4 −0
Original line number Diff line number Diff line
@@ -1157,4 +1157,8 @@ struct vma_prepare {

void __meminit __init_single_page(struct page *page, unsigned long pfn,
				unsigned long zone, int nid);

#ifdef CONFIG_PAGE_CACHE_LIMIT
unsigned long shrink_memory(unsigned long nr_to_reclaim, bool may_swap);
#endif /* CONFIG_PAGE_CACHE_LIMIT */
#endif	/* __MM_INTERNAL_H */

mm/page_cache_limit.c

0 → 100644
+199 −0
Original line number Diff line number Diff line
// SPDX-License-Identifier: GPL-2.0
/*
 * Support for periodic memory reclaim and page cache limit
 */

#include <linux/mm.h>
#include <linux/swap.h>
#include <linux/sysctl.h>
#include <linux/workqueue.h>

#include "internal.h"

static int vm_cache_reclaim_s __read_mostly;
static int vm_cache_reclaim_s_max = 43200;
static int vm_cache_reclaim_weight __read_mostly = 1;
static int vm_cache_reclaim_weight_max = 100;
static int vm_cache_reclaim_enable = 1;
static unsigned long vm_cache_limit_mbytes __read_mostly;

static void shrink_shepherd(struct work_struct *w);
static DECLARE_DEFERRABLE_WORK(shepherd, shrink_shepherd);
static struct work_struct vmscan_works[MAX_NUMNODES];

static bool should_periodical_reclaim(void)
{
	return vm_cache_reclaim_s && vm_cache_reclaim_enable;
}

static unsigned long node_reclaim_num(void)
{
	int nid = numa_node_id();

	return SWAP_CLUSTER_MAX * nr_cpus_node(nid) * vm_cache_reclaim_weight;
}

static bool page_cache_over_limit(void)
{
	unsigned long lru_file;
	unsigned long limit;

	limit = vm_cache_limit_mbytes << (20 - PAGE_SHIFT);
	lru_file = global_node_page_state(NR_ACTIVE_FILE) +
			global_node_page_state(NR_INACTIVE_FILE);
	if (lru_file > limit)
		return true;

	return false;
}

static bool should_reclaim_page_cache(void)
{
	if (!should_periodical_reclaim())
		return false;

	if (!vm_cache_limit_mbytes)
		return false;

	return true;
}

int cache_reclaim_enable_handler(struct ctl_table *table, int write,
			void *buffer, size_t *length, loff_t *ppos)
{
	int ret;

	ret = proc_dointvec_minmax(table, write, buffer, length, ppos);
	if (ret || !write)
		return ret;

	if (should_periodical_reclaim())
		schedule_delayed_work(&shepherd, round_jiffies_relative(
			(unsigned long)vm_cache_reclaim_s * HZ));

	return 0;
}

int cache_reclaim_sysctl_handler(struct ctl_table *table, int write,
		void *buffer, size_t *length, loff_t *ppos)
{
	int ret;

	ret = proc_dointvec_minmax(table, write, buffer, length, ppos);
	if (ret || !write)
		return ret;

	if (should_periodical_reclaim())
		mod_delayed_work(system_unbound_wq, &shepherd,
				round_jiffies_relative(
				(unsigned long)vm_cache_reclaim_s * HZ));

	return ret;
}

int cache_limit_mbytes_sysctl_handler(struct ctl_table *table, int write,
		void __user *buffer, size_t *length, loff_t *ppos)
{
	int ret;
	unsigned long vm_cache_limit_mbytes_max;
	unsigned long origin_mbytes = vm_cache_limit_mbytes;
	int nr_retries = MAX_RECLAIM_RETRIES;

	vm_cache_limit_mbytes_max = totalram_pages() >> (20 - PAGE_SHIFT);
	ret = proc_doulongvec_minmax(table, write, buffer, length, ppos);
	if (ret || !write)
		return ret;

	if (vm_cache_limit_mbytes > vm_cache_limit_mbytes_max) {
		vm_cache_limit_mbytes = origin_mbytes;
		return -EINVAL;
	}

	if (write) {
		while (should_reclaim_page_cache() && page_cache_over_limit() &&
				nr_retries--) {
			if (signal_pending(current))
				return -EINTR;

			shrink_memory(node_reclaim_num(), false);
		}
	}

	return 0;
}

static void shrink_shepherd(struct work_struct *w)
{
	int node;

	if (!should_periodical_reclaim())
		return;

	for_each_online_node(node) {
		if (!work_pending(&vmscan_works[node]))
			queue_work_node(node, system_unbound_wq, &vmscan_works[node]);
	}

	queue_delayed_work(system_unbound_wq, &shepherd,
		round_jiffies_relative((unsigned long)vm_cache_reclaim_s * HZ));
}

static void shrink_page_work(struct work_struct *w)
{
	shrink_memory(node_reclaim_num(), true);
}

static void shrink_shepherd_timer(void)
{
	int i;

	for (i = 0; i < MAX_NUMNODES; i++)
		INIT_WORK(&vmscan_works[i], shrink_page_work);
}

static struct ctl_table page_cache_limit_table[] = {
	{
		.procname	= "cache_reclaim_s",
		.data		= &vm_cache_reclaim_s,
		.maxlen		= sizeof(vm_cache_reclaim_s),
		.mode		= 0644,
		.proc_handler	= cache_reclaim_sysctl_handler,
		.extra1		= SYSCTL_ZERO,
		.extra2		= &vm_cache_reclaim_s_max,
	},
	{
		.procname	= "cache_reclaim_weight",
		.data		= &vm_cache_reclaim_weight,
		.maxlen		= sizeof(vm_cache_reclaim_weight),
		.mode		= 0644,
		.proc_handler	= proc_dointvec_minmax,
		.extra1		= SYSCTL_ONE,
		.extra2		= &vm_cache_reclaim_weight_max,
	},
	{
		.procname	= "cache_reclaim_enable",
		.data		= &vm_cache_reclaim_enable,
		.maxlen		= sizeof(vm_cache_reclaim_enable),
		.mode		= 0644,
		.proc_handler	= cache_reclaim_enable_handler,
		.extra1		= SYSCTL_ZERO,
		.extra2		= SYSCTL_ONE,
	},
	{
		.procname	= "cache_limit_mbytes",
		.data		= &vm_cache_limit_mbytes,
		.maxlen		= sizeof(vm_cache_limit_mbytes),
		.mode		= 0644,
		.proc_handler	= cache_limit_mbytes_sysctl_handler,
	},
};

static int __init shrink_page_init(void)
{
	shrink_shepherd_timer();

	register_sysctl_init("vm", page_cache_limit_table);

	return 0;
}
late_initcall(shrink_page_init)
Loading