mm: allow to decrease swap.max below actual swap usage
Subsystems (4)
RFC 1/1 mm: allow to decrease swap.max below actual swap usage
Currently an attempt to set swap.max into a value lower than the actual swap usage fails. And a user can't do much with it, except turning off swap globally (using swapoff).
This patch aims to fix this issue by allowing setting swap.max into any value (which corresponds to cgroup v2 API design), and schedule a background job to fit swap size into the new limit.
The following script can be used to test the memory.swap behavior:
#!/bin/bash
mkdir -p /sys/fs/cgroup/test_swap
echo 100M > /sys/fs/cgroup/test_swap/memory.max echo max > /sys/fs/cgroup/test_swap/memory.swap.max
mkdir -p /sys/fs/cgroup/test_swap_2
echo 100M > /sys/fs/cgroup/test_swap_2/memory.max echo max > /sys/fs/cgroup/test_swap_2/memory.swap.max
echo $$ > /sys/fs/cgroup/test_swap/cgroup.procs
allocate 200M &
echo $$ > /sys/fs/cgroup/test_swap_2/cgroup.procs allocate 200M &
sleep 2
cat /sys/fs/cgroup/test_swap/memory.swap.current cat /sys/fs/cgroup/test_swap_2/memory.swap.current
echo max > /sys/fs/cgroup/test_swap/memory.max echo 50M > /sys/fs/cgroup/test_swap/memory.swap.max
sleep 10
cat /sys/fs/cgroup/test_swap/memory.swap.current cat /sys/fs/cgroup/test_swap_2/memory.swap.current
pkill allocate
Original test results:
106024960
106348544
./swap.sh: line 23: echo: write error: Device or resource busy 106024960
106348544
With this patch applied:
106045440
106352640
52428800
106201088
--- include/linux/memcontrol.h | 1 + include/linux/swap.h | 9 +++++++ include/linux/swapfile.h | 3 ++- mm/frontswap.c | 2 +- mm/memcontrol.c | 27 +++++++++++++++---- mm/swapfile.c | 64 ++++++++++++++++++++++++++++++++++++++++++---- 6 files changed, 94 insertions(+), 12 deletions(-)
include/linux/memcontrol.h +1 -0
@@ -184,6 +184,7 @@ struct mem_cgroup {/* Range enforcement for interrupt charges */ struct work_struct high_work; +struct work_struct swap_work;unsigned long soft_limit;
include/linux/swap.h +9 -0
@@ -650,6 +650,8 @@ extern int mem_cgroup_try_charge_swap(struct page *page, swp_entry_t entry);extern void mem_cgroup_uncharge_swap (swp_entry_t entry,unsigned int nr_pages);extern long mem_cgroup_get_nr_swap_pages (struct mem_cgroup *memcg);extern bool mem_cgroup_swap_full (struct page *page); +extern int mem_cgroup_shrink_swap (struct mem_cgroup *memcg, +unsigned long nr_pages);#else static inline void mem_cgroup_swapout (struct page *page,swp_entry_t entry) {
@@ -675,6 +677,13 @@ static inline bool mem_cgroup_swap_full(struct page *page) {return vm_swap_full (); } + +static inline int mem_cgroup_shrink_swap (struct mem_cgroup *memcg, +unsigned long nr_pages) +{ +return 0 ; +} +#endif #endif /* __KERNEL__*/
include/linux/swapfile.h +2 -1
@@ -9,6 +9,7 @@extern spinlock_t swap_lock;extern struct plist_head swap_active_head;extern struct swap_info_struct *swap_info[]; -extern int try_to_unuse (unsigned int ,bool ,unsigned long ); +extern int try_to_unuse (unsigned int type,bool fronstswap, +unsigned long pages_to_unuse,struct mem_cgroup *memcg);#endif /* _LINUX_SWAPFILE_H */
mm/frontswap.c +1 -1
@@ -458,7 +458,7 @@ void frontswap_shrink(unsigned long target_pages) ret =__frontswap_shrink (target_pages, &pages_to_unuse, &type);spin_unlock (&swap_lock);if (ret ==0 ) -try_to_unuse (type, true, pages_to_unuse); +try_to_unuse (type, true, pages_to_unuse,NULL );return ; }EXPORT_SYMBOL (frontswap_shrink);
mm/memcontrol.c +22 -5
@@ -60,6 +60,7 @@#include <linux/vmpressure.h> #include <linux/mm_inline.h> #include <linux/swap_cgroup.h> +#include <linux/swapfile.h> #include <linux/cpu.h> #include <linux/oom.h> #include <linux/lockdep.h>
@@ -1879,6 +1880,23 @@ static void high_work_func(struct work_struct *work)reclaim_high (memcg,MEMCG_CHARGE_BATCH ,GFP_KERNEL ); } +static void swap_work_func (struct work_struct *work) +{ +struct mem_cgroup *memcg; + + memcg = container_of(work, struct mem_cgroup, swap_work); + +for (;;) { +unsigned long usage =page_counter_read (&memcg->swap); + +if (usage <= memcg->swap.limit) +break ; + +if (mem_cgroup_shrink_swap (memcg, usage - memcg->swap.limit)) +break ; + } +} +/* * Scheduled by try_charge() to be executed from the userland return path * and reclaims memory over the high limit.
@@ -4391,6 +4409,7 @@ static struct mem_cgroup *mem_cgroup_alloc(void) goto fail;INIT_WORK (&memcg->high_work, high_work_func); +INIT_WORK (&memcg->swap_work, swap_work_func); memcg->last_scanned_node =MAX_NUMNODES ;INIT_LIST_HEAD (&memcg->oom_notify);mutex_init (&memcg->thresholds_lock);
@@ -4526,6 +4545,7 @@ static void mem_cgroup_css_free(struct cgroup_subsys_state *css)vmpressure_cleanup (&memcg->vmpressure);cancel_work_sync (&memcg->high_work); +cancel_work_sync (&memcg->swap_work);mem_cgroup_remove_from_trees (memcg);memcg_free_kmem (memcg);mem_cgroup_free (memcg);
@@ -6394,11 +6414,8 @@ static ssize_t swap_max_write(struct kernfs_open_file *of,if (err)return err; -mutex_lock (&memcg_limit_mutex); - err =page_counter_limit (&memcg->swap, max); -mutex_unlock (&memcg_limit_mutex); -if (err) -return err; +xchg (&memcg->swap.limit, max); +schedule_work (&memcg->swap_work);return nbytes; }
mm/swapfile.c +59 -5
96 lines not on this page · raw message
Signature · 1 line
2.14.3
Quoted 47 lines · Roman Gushchin
Currently an attempt to set swap.max into a value lower than the actual swap usage fails. And a user can't do much with it, except turning off swap globally (using swapoff).
This patch aims to fix this issue by allowing setting swap.max into any value (which corresponds to cgroup v2 API design), and schedule a background job to fit swap size into the new limit.
The following script can be used to test the memory.swap behavior:
#!/bin/bashmkdir -p
/sys/fs/cgroup/test_swap
echo 100M >/sys/fs/cgroup/test_swap/memory.maxecho max >/sys/fs/cgroup/test_swap/memory.swap.maxmkdir -p
/sys/fs/cgroup/test_swap_2
echo 100M >/sys/fs/cgroup/test_swap_2/memory.maxecho max >/sys/fs/cgroup/test_swap_2/memory.swap.maxecho $$ >
/sys/fs/cgroup/test_swap/cgroup.procs
allocate 200M &echo $$ >
/sys/fs/cgroup/test_swap_2/cgroup.procsallocate 200M &sleep 2
cat
/sys/fs/cgroup/test_swap/memory.swap.currentcat/sys/fs/cgroup/test_swap_2/memory.swap.currentecho max >
/sys/fs/cgroup/test_swap/memory.maxecho 50M >/sys/fs/cgroup/test_swap/memory.swap.maxsleep 10
cat
/sys/fs/cgroup/test_swap/memory.swap.currentcat/sys/fs/cgroup/test_swap_2/memory.swap.currentpkill allocate
Original test results:
106024960
106348544
./swap.sh: line 23: echo: write error: Device or resource busy 106024960
106348544
Quoted 5 lines
With this patch applied:
106045440
106352640
52428800
106201088
Any comments, thoughts, feedback?
Rebased version below.
---
From a0811f1d094b3103e50c005586471afbe15e9113 Mon Sep 17 00:00:00 2001
From: Roman Gushchin <[email protected]>
Date: Thu, 5 Apr 2018 15:46:50 +0100
Subject: [PATCH v2] mm: allow to decrease swap.max below actual swap usage
Currently an attempt to set swap.max into a value lower than the actual swap usage fails. And a user can't do much with it, except turning off swap globally (using swapoff).
This patch aims to fix this issue by allowing setting swap.max into any value (which corresponds to cgroup v2 API design), and schedule a background job to fit swap size into the new limit.
The following script can be used to test the memory.swap behavior:
#!/bin/bash
mkdir -p /sys/fs/cgroup/test_swap
echo 100M > /sys/fs/cgroup/test_swap/memory.max echo max > /sys/fs/cgroup/test_swap/memory.swap.max
mkdir -p /sys/fs/cgroup/test_swap_2
echo 100M > /sys/fs/cgroup/test_swap_2/memory.max echo max > /sys/fs/cgroup/test_swap_2/memory.swap.max
echo $$ > /sys/fs/cgroup/test_swap/cgroup.procs
allocate 200M &
echo $$ > /sys/fs/cgroup/test_swap_2/cgroup.procs allocate 200M &
sleep 2
cat /sys/fs/cgroup/test_swap/memory.swap.current cat /sys/fs/cgroup/test_swap_2/memory.swap.current
echo max > /sys/fs/cgroup/test_swap/memory.max echo 50M > /sys/fs/cgroup/test_swap/memory.swap.max
sleep 10
cat /sys/fs/cgroup/test_swap/memory.swap.current cat /sys/fs/cgroup/test_swap_2/memory.swap.current
pkill allocate
Original test results:
106024960
106348544
./swap.sh: line 23: echo: write error: Device or resource busy 106024960
106348544
With this patch applied:
106045440
106352640
52428800
106201088
--- include/linux/memcontrol.h | 1 + include/linux/swap.h | 9 +++++++ include/linux/swapfile.h | 3 ++- mm/frontswap.c | 2 +- mm/memcontrol.c | 27 +++++++++++++++---- mm/swapfile.c | 64 ++++++++++++++++++++++++++++++++++++++++++---- 6 files changed, 94 insertions(+), 12 deletions(-)
include/linux/memcontrol.h +1 -0
@@ -184,6 +184,7 @@ struct mem_cgroup {/* Range enforcement for interrupt charges */ struct work_struct high_work; +struct work_struct swap_work;unsigned long soft_limit;
include/linux/swap.h +9 -0
@@ -650,6 +650,8 @@ extern int mem_cgroup_try_charge_swap(struct page *page, swp_entry_t entry);extern void mem_cgroup_uncharge_swap (swp_entry_t entry,unsigned int nr_pages);extern long mem_cgroup_get_nr_swap_pages (struct mem_cgroup *memcg);extern bool mem_cgroup_swap_full (struct page *page); +extern int mem_cgroup_shrink_swap (struct mem_cgroup *memcg, +unsigned long nr_pages);#else static inline void mem_cgroup_swapout (struct page *page,swp_entry_t entry) {
@@ -675,6 +677,13 @@ static inline bool mem_cgroup_swap_full(struct page *page) {return vm_swap_full (); } + +static inline int mem_cgroup_shrink_swap (struct mem_cgroup *memcg, +unsigned long nr_pages) +{ +return 0 ; +} +#endif #endif /* __KERNEL__*/
include/linux/swapfile.h +2 -1
@@ -9,6 +9,7 @@extern spinlock_t swap_lock;extern struct plist_head swap_active_head;extern struct swap_info_struct *swap_info[]; -extern int try_to_unuse (unsigned int ,bool ,unsigned long ); +extern int try_to_unuse (unsigned int type,bool fronstswap, +unsigned long pages_to_unuse,struct mem_cgroup *memcg);#endif /* _LINUX_SWAPFILE_H */
mm/frontswap.c +1 -1
@@ -458,7 +458,7 @@ void frontswap_shrink(unsigned long target_pages) ret =__frontswap_shrink (target_pages, &pages_to_unuse, &type);spin_unlock (&swap_lock);if (ret ==0 ) -try_to_unuse (type, true, pages_to_unuse); +try_to_unuse (type, true, pages_to_unuse,NULL );return ; }EXPORT_SYMBOL (frontswap_shrink);
mm/memcontrol.c +22 -5
@@ -60,6 +60,7 @@#include <linux/vmpressure.h> #include <linux/mm_inline.h> #include <linux/swap_cgroup.h> +#include <linux/swapfile.h> #include <linux/cpu.h> #include <linux/oom.h> #include <linux/lockdep.h>
@@ -1870,6 +1871,23 @@ static void high_work_func(struct work_struct *work)reclaim_high (memcg,MEMCG_CHARGE_BATCH ,GFP_KERNEL ); } +static void swap_work_func (struct work_struct *work) +{ +struct mem_cgroup *memcg; + + memcg = container_of(work, struct mem_cgroup, swap_work); + +for (;;) { +unsigned long usage =page_counter_read (&memcg->swap); + +if (usage <= memcg->swap.limit) +break ; + +if (mem_cgroup_shrink_swap (memcg, usage - memcg->swap.limit)) +break ; + } +} +/* * Scheduled by try_charge() to be executed from the userland return path * and reclaims memory over the high limit.
@@ -4391,6 +4409,7 @@ static struct mem_cgroup *mem_cgroup_alloc(void) goto fail;INIT_WORK (&memcg->high_work, high_work_func); +INIT_WORK (&memcg->swap_work, swap_work_func); memcg->last_scanned_node =MAX_NUMNODES ;INIT_LIST_HEAD (&memcg->oom_notify);mutex_init (&memcg->thresholds_lock);
@@ -4526,6 +4545,7 @@ static void mem_cgroup_css_free(struct cgroup_subsys_state *css)vmpressure_cleanup (&memcg->vmpressure);cancel_work_sync (&memcg->high_work); +cancel_work_sync (&memcg->swap_work);mem_cgroup_remove_from_trees (memcg);memcg_free_kmem (memcg);mem_cgroup_free (memcg);
@@ -6408,11 +6428,8 @@ static ssize_t swap_max_write(struct kernfs_open_file *of,if (err)return err; -mutex_lock (&memcg_limit_mutex); - err =page_counter_limit (&memcg->swap, max); -mutex_unlock (&memcg_limit_mutex); -if (err) -return err; +xchg (&memcg->swap.limit, max); +schedule_work (&memcg->swap_work);return nbytes; }
mm/swapfile.c +59 -5
96 lines not on this page · raw message
Signature · 1 line
2.14.3