Per-memcg slab shrinkers
Subsystems (8)
cover Per-memcg slab shrinkers
[ It's been a long time since I sent the last version of this set, so
I'm restarting the versioning. For those, who are interested in the patch set history, see https://lkml.org/lkml/2014/2/5/358 ]
Hi,
This patch set introduces per-memcg slab shrinkers support and implements per-memcg fs (dcache, icache) shrinkers. It was initially proposed by Glauber Costa.
The idea lying behind this is to make the list_lru structure per-memcg and put objects relating to a particular memcg to the corresponding list. This way, to turn a shrinker using list_lru for organizing reclaimable objects to memcg aware one it's enough to initialize its list_lru as memcg aware.
Please, note that even with this set, current kmemcg implementation has serious flaws, which make it unusable in production:
- Kmem-only reclaim, which would trigger on hitting memory.kmem.limit, is not implemented yet. This makes memory.kmem.limite < memory.limit setups unusable. We are not quite sure if we really need a separate knob for kmem.limit though (see the discussion at
https://lkml.org/lkml/2014/7/16/412).
- Since kmem cache self destruction patch set was withdrawn due to performance reasons (https://lkml.org/lkml/2014/7/15/361), per memcg kmem caches, which have objects on css offline, are still leaked. I'm planning to introduce a shrinker for such caches.
- Per-memcg arrays of kmem_cache's and list_lru's can only grow and are never shrunk. Since the number of offline memcg's hanging around is practically unlimited, these arrays may become really huge and result in various problems even if nobody uses cgroups right now. I'm considering using flex_array's for those caches so that we could reclaim their parts on memory pressure.
That's why I still leave CONFIG_MEMCG_KMEM marked as "only for development/testing".
The patch set is organized as follows:
- patches 1 and 2 make list_lru and fs-private shrinker interfaces neater and suitable for extending towards per-memcg reclaim;
- patch 3 introduces per-memcg slab shrinker core;
- patch 4 makes list_lru memcg-aware and patch 5 marks dcache and icache shrinkers as memcg aware.
- patch 6 extends memcg iterator to include offline css's to allow kmem reclaim from dead css's.
Thanks,
Vladimir Davydov (6):
list_lru, shrinkers: introduce list_lru_shrink_{count,walk}
fs: consolidate {nr,free}_cached_objects args in shrink_control
vmscan: shrink slab on memcg pressure
list_lru: add per-memcg lists
fs: make shrinker memcg aware
memcg: iterator: do not skip offline css
fs/dcache.c | 14 ++- fs/gfs2/main.c | 2 +- fs/gfs2/quota.c | 6 +- fs/inode.c | 7 +- fs/internal.h | 7 +- fs/super.c | 45 ++++---- fs/xfs/xfs_buf.c | 9 +- fs/xfs/xfs_qm.c | 9 +- fs/xfs/xfs_super.c | 7 +- include/linux/fs.h | 6 +- include/linux/list_lru.h | 82 +++++++++----- include/linux/memcontrol.h | 64 +++++++++++ include/linux/shrinker.h | 10 +- mm/list_lru.c | 132 +++++++++++++++++++---- mm/memcontrol.c | 258 ++++++++++++++++++++++++++++++++++++++++---- mm/vmscan.c | 94 ++++++++++++---- mm/workingset.c | 9 +- 17 files changed, 615 insertions(+), 146 deletions(-)
Signature · 7 lines
1.7.10.4 -- To unsubscribe, send a message with 'unsubscribe linux-mm' in the body to [email protected]. For more info on Linux MM, see: http://www.linux-mm.org/ . Don't email: <a href=mailto:"[email protected]"> [email protected] </a>
PATCH 1/6 list_lru, shrinkers: introduce list_lru_shrink_{count,walk}
NUMA aware slab shrinkers use the list_lru structure to distribute objects coming from different NUMA nodes to different lists. Whenever such a shrinker needs to count or scan objects from a particular node, it issues commands like this:
count = list_lru_count_node(lru, sc->nid);
freed = list_lru_walk_node(lru, sc->nid, isolate_func,
isolate_arg, &sc->nr_to_scan);where sc is an instance of the shrink_control structure passed to it from vmscan.
To simplify this, let's add special list_lru functions to be used by shrinkers, list_lru_shrink_count() and list_lru_shrink_walk(), which consolidate the nid and nr_to_scan arguments in the shrink_control structure.
This will also allow us to avoid patching shrinkers that use list_lru when we make shrink_slab() per-memcg - all we will have to do is extend the shrink_control structure to include the target memcg and make list_lru_shrink_{count,walk} handle this appropriately.
--- fs/dcache.c | 14 ++++++-------- fs/gfs2/quota.c | 6 +++--- fs/inode.c | 7 +++---- fs/internal.h | 7 +++---- fs/super.c | 22 ++++++++++------------ fs/xfs/xfs_buf.c | 7 +++---- fs/xfs/xfs_qm.c | 7 +++---- include/linux/list_lru.h | 16 ++++++++++++++++ mm/workingset.c | 6 +++--- 9 files changed, 50 insertions(+), 42 deletions(-)
fs/dcache.c +6 -8
@@ -913,24 +913,22 @@ dentry_lru_isolate(struct list_head *item, spinlock_t *lru_lock, void *arg)/** * prune_dcache_sb - shrink the dcache * @sb: superblock -* @nr_to_scan : number of entries to try to free -* @nid: which node to scan for freeable entities +* @sc: shrink control, passed to list_lru_shrink_walk() * -* Attempt to shrink the superblock dcache LRU by @nr_to_scan entries. This is -* done when we need more memory an called from the superblock shrinker +* Attempt to shrink the superblock dcache LRU by @sc->nr_to_scan entries. This +* is done when we need more memory and called from the superblock shrinker * function. * * This function may fail to free any resources if all the dentries are in * use. */ -long prune_dcache_sb (struct super_block *sb,unsigned long nr_to_scan, -int nid) +long prune_dcache_sb (struct super_block *sb,struct shrink_control *sc) {LIST_HEAD (dispose);long freed; - freed =list_lru_walk_node (&sb->s_dentry_lru, nid, dentry_lru_isolate, - &dispose, &nr_to_scan); + freed =list_lru_shrink_walk (&sb->s_dentry_lru, sc, + dentry_lru_isolate, &dispose);shrink_dentry_list (&dispose);return freed; }
fs/gfs2/quota.c +3 -3
@@ -171,8 +171,8 @@ static unsigned long gfs2_qd_shrink_scan(struct shrinker *shrink,if (!(sc->gfp_mask & __GFP_FS))return SHRINK_STOP ; - freed =list_lru_walk_node (&gfs2_qd_lru, sc->nid, gfs2_qd_isolate, - &dispose, &sc->nr_to_scan); + freed =list_lru_shrink_walk (&gfs2_qd_lru, sc, + gfs2_qd_isolate, &dispose);gfs2_qd_dispose (&dispose);
@@ -182,7 +182,7 @@ static unsigned long gfs2_qd_shrink_scan(struct shrinker *shrink,static unsigned long gfs2_qd_shrink_count (struct shrinker *shrink,struct shrink_control *sc) { -return vfs_pressure_ratio (list_lru_count_node (&gfs2_qd_lru, sc->nid)); +return vfs_pressure_ratio (list_lru_shrink_count (&gfs2_qd_lru, sc)); }struct shrinker gfs2_qd_shrinker = {
fs/inode.c +3 -4
@@ -748,14 +748,13 @@ inode_lru_isolate(struct list_head *item, spinlock_t *lru_lock, void *arg)* to trim from the LRU. Inodes to be freed are moved to a temporary list and * then are freed outside inode_lock by dispose_list(). */ -long prune_icache_sb (struct super_block *sb,unsigned long nr_to_scan, -int nid) +long prune_icache_sb (struct super_block *sb,struct shrink_control *sc) {LIST_HEAD (freeable);long freed; - freed =list_lru_walk_node (&sb->s_inode_lru, nid, inode_lru_isolate, - &freeable, &nr_to_scan); + freed =list_lru_shrink_walk (&sb->s_inode_lru, sc, + inode_lru_isolate, &freeable);dispose_list (&freeable);return freed; }
fs/internal.h +3 -4
@@ -14,6 +14,7 @@ struct file_system_type;struct linux_binprm ;struct path ;struct mount ; +struct shrink_control ;/* * block_dev.c
@@ -107,8 +108,7 @@ extern int open_check_o_direct(struct file *f);* inode.c */ extern spinlock_t inode_sb_list_lock; -extern long prune_icache_sb (struct super_block *sb,unsigned long nr_to_scan, -int nid); +extern long prune_icache_sb (struct super_block *sb,struct shrink_control *sc);extern void inode_add_lru (struct inode *inode);/*
@@ -125,8 +125,7 @@ extern int invalidate_inodes(struct super_block *, bool);*/ extern struct dentry *__d_alloc (struct super_block *,const struct qstr *);extern int d_set_mounted (struct dentry *dentry); -extern long prune_dcache_sb (struct super_block *sb,unsigned long nr_to_scan, -int nid); +extern long prune_dcache_sb (struct super_block *sb,struct shrink_control *sc);/* * read_write.c
fs/super.c +10 -12
@@ -78,27 +78,27 @@ static unsigned long super_cache_scan(struct shrinker *shrink,if (sb->s_op->nr_cached_objects) fs_objects = sb->s_op->nr_cached_objects (sb, sc->nid); - inodes =list_lru_count_node (&sb->s_inode_lru, sc->nid); - dentries =list_lru_count_node (&sb->s_dentry_lru, sc->nid); + inodes =list_lru_shrink_count (&sb->s_inode_lru, sc); + dentries =list_lru_shrink_count (&sb->s_dentry_lru, sc); total_objects = dentries + inodes + fs_objects +1 ;/* proportion the scan between the caches */ dentries =mult_frac (sc->nr_to_scan, dentries, total_objects); inodes =mult_frac (sc->nr_to_scan, inodes, total_objects); + fs_objects =mult_frac (sc->nr_to_scan, fs_objects, total_objects);/* * prune the dcache first as the icache is pinned by it, then * prune the icache, followed by the filesystem specific caches */ - freed =prune_dcache_sb (sb, dentries, sc->nid); - freed +=prune_icache_sb (sb, inodes, sc->nid); + sc->nr_to_scan = dentries; + freed =prune_dcache_sb (sb, sc); + sc->nr_to_scan = inodes; + freed +=prune_icache_sb (sb, sc); -if (fs_objects) { - fs_objects =mult_frac (sc->nr_to_scan, fs_objects, - total_objects); +if (fs_objects) freed += sb->s_op->free_cached_objects (sb, fs_objects, sc->nid); - }drop_super (sb);return freed;
@@ -124,10 +124,8 @@ static unsigned long super_cache_count(struct shrinker *shrink, total_objects = sb->s_op->nr_cached_objects (sb, sc->nid); - total_objects +=list_lru_count_node (&sb->s_dentry_lru, - sc->nid); - total_objects +=list_lru_count_node (&sb->s_inode_lru, - sc->nid); + total_objects +=list_lru_shrink_count (&sb->s_dentry_lru, sc); + total_objects +=list_lru_shrink_count (&sb->s_inode_lru, sc); total_objects =vfs_pressure_ratio (total_objects);return total_objects;
fs/xfs/xfs_buf.c +3 -4
@@ -1572,10 +1572,9 @@ xfs_buftarg_shrink_scan( struct xfs_buftarg, bt_shrinker);LIST_HEAD (dispose);unsigned long freed; -unsigned long nr_to_scan = sc->nr_to_scan; - freed =list_lru_walk_node (&btp->bt_lru, sc->nid, xfs_buftarg_isolate, - &dispose, &nr_to_scan); + freed =list_lru_shrink_walk (&btp->bt_lru, sc, + xfs_buftarg_isolate, &dispose);while (!list_empty (&dispose)) {struct xfs_buf *bp;
@@ -1594,7 +1593,7 @@ xfs_buftarg_shrink_count( {struct xfs_buftarg *btp =container_of (shrink, struct xfs_buftarg, bt_shrinker); -return list_lru_count_node (&btp->bt_lru, sc->nid); +return list_lru_shrink_count (&btp->bt_lru, sc); }void
fs/xfs/xfs_qm.c +3 -4
@@ -618,7 +618,6 @@ xfs_qm_shrink_scan(struct xfs_qm_isolate isol;unsigned long freed;int error; -unsigned long nr_to_scan = sc->nr_to_scan;if ((sc->gfp_mask & (__GFP_FS|__GFP_WAIT)) != (__GFP_FS|__GFP_WAIT))return 0 ;
@@ -626,8 +625,8 @@ xfs_qm_shrink_scan(INIT_LIST_HEAD (&isol.buffers);INIT_LIST_HEAD (&isol.dispose); - freed =list_lru_walk_node (&qi->qi_lru, sc->nid, xfs_qm_dquot_isolate, &isol, - &nr_to_scan); + freed =list_lru_shrink_walk (&qi->qi_lru, sc, + xfs_qm_dquot_isolate, &isol); error =xfs_buf_delwri_submit (&isol.buffers);if (error)
@@ -652,7 +651,7 @@ xfs_qm_shrink_count(struct xfs_quotainfo *qi =container_of (shrink, struct xfs_quotainfo, qi_shrinker); -return list_lru_count_node (&qi->qi_lru, sc->nid); +return list_lru_shrink_count (&qi->qi_lru, sc); }/*
include/linux/list_lru.h +16 -0
@@ -9,6 +9,7 @@#include <linux/list.h> #include <linux/nodemask.h> +#include <linux/shrinker.h> /* list_lru_walk_cb has to always return one of those */ enum lru_status {
@@ -81,6 +82,13 @@ bool list_lru_del(struct list_lru *lru, struct list_head *item);* Callers that want such a guarantee need to provide an outer lock. */ unsigned long list_lru_count_node (struct list_lru *lru,int nid); + +static inline unsigned long list_lru_shrink_count (struct list_lru *lru, +struct shrink_control *sc) +{ +return list_lru_count_node (lru, sc->nid); +} +static inline unsigned long list_lru_count (struct list_lru *lru) {long count =0 ;
@@ -120,6 +128,14 @@ unsigned long list_lru_walk_node(struct list_lru *lru, int nid, unsigned long *nr_to_walk);static inline unsigned long +list_lru_shrink_walk (struct list_lru *lru,struct shrink_control *sc, +list_lru_walk_cb isolate,void *cb_arg) +{ +return list_lru_walk_node (lru, sc->nid, isolate, cb_arg, + &sc->nr_to_scan); +} + +static inline unsigned long list_lru_walk (struct list_lru *lru,list_lru_walk_cb isolate,void *cb_arg,unsigned long nr_to_walk) {
mm/workingset.c +3 -3
@@ -275,7 +275,7 @@ static unsigned long count_shadow_nodes(struct shrinker *shrinker,/* list_lru lock nests inside IRQ-safe mapping->tree_lock */ local_irq_disable (); - shadow_nodes =list_lru_count_node (&workingset_shadow_nodes, sc->nid); + shadow_nodes =list_lru_shrink_count (&workingset_shadow_nodes, sc);local_irq_enable (); pages =node_present_pages (sc->nid);
@@ -376,8 +376,8 @@ static unsigned long scan_shadow_nodes(struct shrinker *shrinker,/* list_lru lock nests inside IRQ-safe mapping->tree_lock */ local_irq_disable (); - ret =list_lru_walk_node (&workingset_shadow_nodes, sc->nid, - shadow_lru_isolate,NULL , &sc->nr_to_scan); + ret =list_lru_shrink_walk (&workingset_shadow_nodes, sc, + shadow_lru_isolate,NULL );local_irq_enable ();return ret; }
Signature · 7 lines
1.7.10.4 -- To unsubscribe, send a message with 'unsubscribe linux-mm' in the body to [email protected]. For more info on Linux MM, see: http://www.linux-mm.org/ . Don't email: <a href=mailto:"[email protected]"> [email protected] </a>
PATCH 2/6 fs: consolidate {nr,free}_cached_objects args in shrink_control
In reply to Vladimir DavydovWe are going to make FS shrinkers memcg-aware. To achieve that, we will have to pass the memcg to scan to the nr_cached_objects and free_cached_objects VFS methods, which currently take only the NUMA node to scan. Since the shrink_control structure already holds the node, and the memcg to scan will be added to it when we introduce memcg-aware vmscan, let us consolidate the methods' arguments in this structure to keep things clean.
--- fs/super.c | 12 ++++++------ fs/xfs/xfs_super.c | 7 +++---- include/linux/fs.h | 6 ++++-- 3 files changed, 13 insertions(+), 12 deletions(-)
fs/super.c +6 -6
@@ -76,7 +76,7 @@ static unsigned long super_cache_scan(struct shrinker *shrink,return SHRINK_STOP ;if (sb->s_op->nr_cached_objects) - fs_objects = sb->s_op->nr_cached_objects (sb, sc->nid); + fs_objects = sb->s_op->nr_cached_objects (sb, sc); inodes =list_lru_shrink_count (&sb->s_inode_lru, sc); dentries =list_lru_shrink_count (&sb->s_dentry_lru, sc);
@@ -96,9 +96,10 @@ static unsigned long super_cache_scan(struct shrinker *shrink, sc->nr_to_scan = inodes; freed +=prune_icache_sb (sb, sc); -if (fs_objects) - freed += sb->s_op->free_cached_objects (sb, fs_objects, - sc->nid); +if (fs_objects) { + sc->nr_to_scan = fs_objects; + freed += sb->s_op->free_cached_objects (sb, sc); + }drop_super (sb);return freed;
@@ -121,8 +122,7 @@ static unsigned long super_cache_count(struct shrinker *shrink,* s_op->nr_cached_objects(). */ if (sb->s_op && sb->s_op->nr_cached_objects) - total_objects = sb->s_op->nr_cached_objects (sb, - sc->nid); + total_objects = sb->s_op->nr_cached_objects (sb, sc); total_objects +=list_lru_shrink_count (&sb->s_dentry_lru, sc); total_objects +=list_lru_shrink_count (&sb->s_inode_lru, sc);
fs/xfs/xfs_super.c +3 -4
@@ -1521,7 +1521,7 @@ xfs_fs_mount(static long xfs_fs_nr_cached_objects (struct super_block *sb, -int nid) +struct shrink_control *sc) {return xfs_reclaim_inodes_count (XFS_M (sb)); }
@@ -1529,10 +1529,9 @@ xfs_fs_nr_cached_objects(static long xfs_fs_free_cached_objects (struct super_block *sb, -long nr_to_scan, -int nid) +struct shrink_control *sc) { -return xfs_reclaim_inodes_nr (XFS_M (sb), nr_to_scan); +return xfs_reclaim_inodes_nr (XFS_M (sb), sc->nr_to_scan); }static const struct super_operations xfs_super_operations = {
include/linux/fs.h +4 -2
@@ -1563,8 +1563,10 @@ struct super_operations { loff_t);#endif int (*bdev_try_to_free_page)(struct super_block *,struct page *,gfp_t ); -long (*nr_cached_objects)(struct super_block *,int ); -long (*free_cached_objects)(struct super_block *,long ,int ); +long (*nr_cached_objects)(struct super_block *, +struct shrink_control *); +long (*free_cached_objects)(struct super_block *, +struct shrink_control *); };/*
Signature · 7 lines
1.7.10.4 -- To unsubscribe, send a message with 'unsubscribe linux-mm' in the body to [email protected]. For more info on Linux MM, see: http://www.linux-mm.org/ . Don't email: <a href=mailto:"[email protected]"> [email protected] </a>
PATCH 3/6 vmscan: shrink slab on memcg pressure
In reply to Vladimir DavydovThis patch makes direct reclaim path shrink slab not only on global memory pressure, but also when we reach the user memory limit of a memcg. To achieve that, it makes shrink_slab() walk over the memcg hierarchy and run shrinkers marked as memcg-aware on the target memcg and all its descendants. The memcg to scan is passed in a shrink_control structure; memcg-unaware shrinkers are still called only on global memory pressure with memcg=NULL. It is up to the shrinker how to organize the objects it is responsible for to achieve per-memcg reclaim.
Note that we do not intend to have true per memcg per node reclaim. Since most memcgs are small and typically confined to a single NUMA node or two by external means and therefore do not need the scalability NUMA aware shrinkers provide, we actually call per node shrinking only for the global list (memcg=NULL), while per-memcg lists are always scanned only once irrespective of the nodemask with nid=0.
--- include/linux/memcontrol.h | 22 +++++++++++ include/linux/shrinker.h | 10 ++++- mm/memcontrol.c | 45 ++++++++++++++++++++- mm/vmscan.c | 94 ++++++++++++++++++++++++++++++++++---------- 4 files changed, 149 insertions(+), 22 deletions(-)
include/linux/memcontrol.h +22 -0
@@ -68,6 +68,9 @@ void mem_cgroup_migrate(struct page *oldpage, struct page *newpage,struct lruvec *mem_cgroup_zone_lruvec (struct zone *,struct mem_cgroup *);struct lruvec *mem_cgroup_page_lruvec (struct page *,struct zone *); +unsigned long mem_cgroup_zone_reclaimable_pages (struct zone *zone, +struct mem_cgroup *memcg); +bool __mem_cgroup_same_or_subtree (const struct mem_cgroup *root_memcg,struct mem_cgroup *memcg); bool task_in_mem_cgroup(struct task_struct *task,
@@ -251,6 +254,12 @@ static inline struct lruvec *mem_cgroup_page_lruvec(struct page *page,return &zone->lruvec; } +static inline unsigned long mem_cgroup_zone_reclaimable_pages (struct zone *zone, +struct mem_cgroup *) +{ +return 0 ; +} +static inline struct mem_cgroup *try_get_mem_cgroup_from_page (struct page *page) {return NULL ;
@@ -421,6 +430,9 @@ static inline bool memcg_kmem_enabled(void)return static_key_false (&memcg_kmem_enabled_key); } +bool memcg_kmem_is_active (struct mem_cgroup *memcg); +bool memcg_kmem_should_reclaim (struct mem_cgroup *memcg); +/* * In general, we'll do everything in our power to not incur in any overhead * for non-memcg users for the kmem functions. Not even a function call, if we
@@ -554,6 +566,16 @@ static inline bool memcg_kmem_enabled(void)return false; } +static inline bool memcg_kmem_is_active (struct mem_cgroup *memcg) +{ +return false; +} + +static inline bool memcg_kmem_should_reclaim (struct mem_cgroup *memcg) +{ +return false; +} +static inline bool memcg_kmem_newpage_charge (gfp_t gfp,struct mem_cgroup **memcg,int order) {
include/linux/shrinker.h +9 -1
@@ -20,8 +20,15 @@ struct shrink_control {/* shrink from these nodes */ nodemask_t nodes_to_scan; + +/* shrink from this memory cgroup hierarchy (if not NULL) */ +struct mem_cgroup *target_mem_cgroup; +/* current node being shrunk (for NUMA aware shrinkers) */ int nid; + +/* current memcg being shrunk (for memcg aware shrinkers) */ +struct mem_cgroup *memcg; };#define SHRINK_STOP (~0UL)
@@ -63,7 +70,8 @@ struct shrinker {#define DEFAULT_SEEKS 2/* A good number if you don't know better. */ /* Flags */ -#define SHRINKER_NUMA_AWARE (1 << 0) +#define SHRINKER_NUMA_AWARE (1 << 0) +#define SHRINKER_MEMCG_AWARE (1 << 1)extern int register_shrinker (struct shrinker *);extern void unregister_shrinker (struct shrinker *);
mm/memcontrol.c +44 -1
63 lines not on this page · raw message
mm/vmscan.c +74 -20
153 lines not on this page · raw message
Signature · 7 lines
1.7.10.4 -- To unsubscribe, send a message with 'unsubscribe linux-mm' in the body to [email protected]. For more info on Linux MM, see: http://www.linux-mm.org/ . Don't email: <a href=mailto:"[email protected]"> [email protected] </a>
PATCH 4/6 list_lru: add per-memcg lists
In reply to Vladimir DavydovThere are several FS shrinkers, including super_block::s_shrink, that keep reclaimable objects in the list_lru structure. Hence to turn them to memcg-aware shrinkers, it is enough to make list_lru per-memcg.
This patch does the trick. It adds an array of LRU lists to the list_lru structure, one for each kmem-active memcg, and dispatches every item addition or removal operation to the list corresponding to the memcg the item is accounted to.
To make a list_lru user memcg-aware, it's enough to pass memcg_aware=true to list_lru_init, everything else is done automatically.
Note, this patch removes VM_BUG_ON(!current->mm) from memcg_{stop,resume}_kmem_account. This is, because these functions may be invoked by memcg_register_list_lru while mounting filesystems on early init, where we don't have ->mm yet. Calling them from kernel threads won't hurt anyway.
--- fs/gfs2/main.c | 2 +- fs/super.c | 4 +- fs/xfs/xfs_buf.c | 2 +- fs/xfs/xfs_qm.c | 2 +- include/linux/list_lru.h | 86 ++++++++++-------- include/linux/memcontrol.h | 42 +++++++++ mm/list_lru.c | 132 +++++++++++++++++++++++----- mm/memcontrol.c | 208 ++++++++++++++++++++++++++++++++++++++++---- mm/workingset.c | 3 +- 9 files changed, 402 insertions(+), 79 deletions(-)
fs/gfs2/main.c +1 -1
@@ -84,7 +84,7 @@ static int __init init_gfs2_fs(void)if (error)return error; - error =list_lru_init (&gfs2_qd_lru); + error =list_lru_init (&gfs2_qd_lru, false);if (error) goto fail_lru;
fs/super.c +2 -2
@@ -187,9 +187,9 @@ static struct super_block *alloc_super(struct file_system_type *type, int flags)INIT_HLIST_BL_HEAD (&s->s_anon);INIT_LIST_HEAD (&s->s_inodes); -if (list_lru_init (&s->s_dentry_lru)) +if (list_lru_init (&s->s_dentry_lru, false)) goto fail; -if (list_lru_init (&s->s_inode_lru)) +if (list_lru_init (&s->s_inode_lru, false)) goto fail;init_rwsem (&s->s_umount);
fs/xfs/xfs_buf.c +1 -1
@@ -1669,7 +1669,7 @@ xfs_alloc_buftarg(if (xfs_setsize_buftarg_early (btp, bdev)) goto error; -if (list_lru_init (&btp->bt_lru)) +if (list_lru_init (&btp->bt_lru, false)) goto error; btp->bt_shrinker.count_objects = xfs_buftarg_shrink_count;
fs/xfs/xfs_qm.c +1 -1
@@ -670,7 +670,7 @@ xfs_qm_init_quotainfo( qinf = mp->m_quotainfo =kmem_zalloc (sizeof (xfs_quotainfo_t),KM_SLEEP ); - error =list_lru_init (&qinf->qi_lru); + error =list_lru_init (&qinf->qi_lru, false);if (error) goto out_free_qinf;
include/linux/list_lru.h +51 -35
138 lines not on this page · raw message
include/linux/memcontrol.h +42 -0
60 lines not on this page · raw message
mm/list_lru.c +112 -20
202 lines not on this page · raw message
mm/memcontrol.c +190 -18
237 lines not on this page · raw message
mm/workingset.c +2 -1
@@ -399,7 +399,8 @@ static int __init workingset_init(void) {int ret; - ret =list_lru_init_key (&workingset_shadow_nodes, &shadow_nodes_key); + ret =list_lru_init_key (&workingset_shadow_nodes, false, + &shadow_nodes_key);if (ret) goto err; ret =register_shrinker (&workingset_shadow_shrinker);
Signature · 7 lines
1.7.10.4 -- To unsubscribe, send a message with 'unsubscribe linux-mm' in the body to [email protected]. For more info on Linux MM, see: http://www.linux-mm.org/ . Don't email: <a href=mailto:"[email protected]"> [email protected] </a>
PATCH 5/6 fs: make shrinker memcg aware
In reply to Vladimir DavydovNow, to make any list_lru-based shrinker memcg aware we should only initialize its list_lru as memcg-enabled. Let's do it for the general FS shrinker (super_block::s_shrink) and mark it as memcg aware.
There are other FS-specific shrinkers that use list_lru for storing objects, such as XFS and GFS2 dquot cache shrinkers, but since they reclaim objects that are shared among different cgroups, there is no point making them memcg aware.
--- fs/super.c | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-)
fs/super.c +14 -3
@@ -34,6 +34,7 @@#include <linux/cleancache.h> #include <linux/fsnotify.h> #include <linux/lockdep.h> +#include <linux/memcontrol.h> #include "internal.h"
@@ -187,9 +188,9 @@ static struct super_block *alloc_super(struct file_system_type *type, int flags)INIT_HLIST_BL_HEAD (&s->s_anon);INIT_LIST_HEAD (&s->s_inodes); -if (list_lru_init (&s->s_dentry_lru, false)) +if (list_lru_init (&s->s_dentry_lru, true)) goto fail; -if (list_lru_init (&s->s_inode_lru, false)) +if (list_lru_init (&s->s_inode_lru, true)) goto fail;init_rwsem (&s->s_umount);
@@ -225,7 +226,7 @@ static struct super_block *alloc_super(struct file_system_type *type, int flags) s->s_shrink.scan_objects = super_cache_scan; s->s_shrink.count_objects = super_cache_count; s->s_shrink.batch =1024 ; - s->s_shrink.flags =SHRINKER_NUMA_AWARE ; + s->s_shrink.flags =SHRINKER_NUMA_AWARE |SHRINKER_MEMCG_AWARE ;return s; fail:
@@ -280,6 +281,16 @@ void deactivate_locked_super(struct super_block *s)unregister_shrinker (&s->s_shrink); fs->kill_sb (s); +/* +* list_lru_destroy() may sleep on memcg-aware lrus. Since +* put_super() calls destroy_super() under a spin lock, we must +* unregister lrus from memcg here to avoid sleeping in atomic +* context. It's safe, because by the time we get here, lrus +* must be empty. +*/ +memcg_unregister_list_lru (&s->s_dentry_lru); +memcg_unregister_list_lru (&s->s_inode_lru); +put_filesystem (fs);put_super (s); }else {
Signature · 7 lines
1.7.10.4 -- To unsubscribe, send a message with 'unsubscribe linux-mm' in the body to [email protected]. For more info on Linux MM, see: http://www.linux-mm.org/ . Don't email: <a href=mailto:"[email protected]"> [email protected] </a>
PATCH 6/6 memcg: iterator: do not skip offline css
In reply to Vladimir DavydovAfter offline, memcg may still host kmem objects, which must be shrinked on memory pressure.
--- mm/memcontrol.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-)
mm/memcontrol.c +2 -3
@@ -1100,8 +1100,7 @@ skip_node:*/ if (next_css) {if ((next_css == &root->css) || - ((next_css->flags &CSS_ONLINE ) && -css_tryget_online (next_css))) +css_tryget (next_css))return mem_cgroup_from_css (next_css); prev_css = next_css;
@@ -1147,7 +1146,7 @@ mem_cgroup_iter_load(struct mem_cgroup_reclaim_iter *iter,* would be returned all the time. */ if (position && position != root && - !css_tryget_online (&position->css)) + !css_tryget (&position->css)) position =NULL ; }return position;
Signature · 7 lines
1.7.10.4 -- To unsubscribe, send a message with 'unsubscribe linux-mm' in the body to [email protected]. For more info on Linux MM, see: http://www.linux-mm.org/ . Don't email: <a href=mailto:"[email protected]"> [email protected] </a>