diff --git a/mm/memcg_control.c b/mm/memcg_control.c index 07fc597db1958f51c314a1bec61a87afe2c12311..d074a5ad9c79de812b1efaf167474ce3540a655e 100644 --- a/mm/memcg_control.c +++ b/mm/memcg_control.c @@ -357,6 +357,46 @@ static int memcg_force_swapin_write(struct cgroup_subsys_state *css, struct cfty return 0; } +#ifdef CONFIG_MEM_PURGEABLE +static unsigned long purgeable_memcg_node(pg_data_t *pgdata, + struct scan_control *sc, struct mem_cgroup *memcg) +{ + unsigned long nr = 0; + struct lruvec *lruvec = mem_cgroup_lruvec(memcg, pgdata); + + shrink_list(LRU_ACTIVE_PURGEABLE, -1, lruvec, sc); + nr += shrink_list(LRU_INACTIVE_PURGEABLE, -1, lruvec, sc); + + pr_info("reclaim &lu purgeable pages \n", nr); + return nr; +} + +static int memcg_force_shrink_purgeable_bysize(struct cgroup_subsys_state *css, + struct cftype *cft, u64 reclaim_size) +{ + struct mem_cgroup *memcg = mem_cgroup_from_css(css); + if (!memcg) + return 0; + + struct scan_control sc = { + .gfp_mask = GFP_KERNEL, + .order = 0, + .priority = DEF_PRIORITY, + .may_deactivate = DEACTIVATE_ANON, + .may_writepage = 1, + .may_unmap = 1, + .may_swap = 1, + .reclaim_idx = MAX_NR_ZONES -1, + }; + int nid = 0; + sc.nr_to_reclaim = div_u64(reclaim_size, PAGE_SIZE); + + for_each_node_state(nid, N_MEMORY) + purgeable_memcg_node(NODE_DATA(nid), &sc, memcg); + return 0; +} +#endif + static struct cftype memcg_policy_files[] = { { .name = "name", @@ -385,6 +425,12 @@ static struct cftype memcg_policy_files[] = { .name = "force_swapin", .write_u64 = memcg_force_swapin_write, }, +#ifdef CONFIG_MEM_PURGEABLE + { + .name = "force_shrink_purgeable_bysize", + .write_u64 = memcg_force_shrink_purgeable_bysize, + }, +#endif { }, /* terminate */ }; diff --git a/mm/vmscan.c b/mm/vmscan.c index 811feb1ba2f5b871dfbf3136e40b0f1a02a14ee5..0e7976e3b254a02bd3443daf216f65a82f08170d 100644 --- a/mm/vmscan.c +++ b/mm/vmscan.c @@ -4389,7 +4389,13 @@ static int purgeable(struct ctl_table *table, int write, void *buffer, .reclaim_idx = MAX_NR_ZONES - 1, }; int nid = 0; + const struct cred *cred = current_cred(); + if (!uid_eq(cred->euid, GLOBAL_MEMMGR_UID) && + !uid_eq(cred->euid, GLOBAL_ROOT_UID)) { + pr_err("no permission to shrink purgeable heap!\n"); + return -EINVAL; + } for_each_node_state(nid, N_MEMORY) purgeable_node(NODE_DATA(nid), &sc); return 0; @@ -4398,7 +4404,7 @@ static int purgeable(struct ctl_table *table, int write, void *buffer, static struct ctl_table ker_tab[] = { { .procname = "purgeable", - .mode = 0200, + .mode = 0444, .proc_handler = purgeable, }, {},