Per memcg slab shrinkers
Subsystems (11)
v3 cover Per memcg slab shrinkers
Hi,
Kmem accounting of memcg is unusable now, because it lacks slab shrinker support. That means when we hit the limit we will get ENOMEM w/o any chance to recover. What we should do then is to call shrink_slab, which would reclaim old inode/dentry caches from this cgroup. This is what this patch set is intended to do.
Basically, it does two things. First, it introduces the notion of per-memcg slab shrinker. A shrinker that wants to reclaim objects per cgroup should mark itself as SHRINKER_MEMCG_AWARE. Then it will be passed the memory cgroup to scan from in shrink_control->memcg. For such shrinkers shrink_slab iterates over the whole cgroup subtree under the target cgroup and calls the shrinker for each kmem-active memory cgroup.
Secondly, this patch set makes the list_lru structure per-memcg. It's done transparently to list_lru users - everything they have to do is to tell list_lru_init that they want memcg-aware list_lru. Then the list_lru will automatically distribute objects among per-memcg lists basing on which cgroup the object is accounted to. This way to make FS shrinkers (icache, dcache) memcg-aware we only need to make them use memcg-aware list_lru, and this is what this patch set does.
As before, this patch set only enables per-memcg kmem reclaim when the pressure goes from memory.limit, not from memory.kmem.limit. Handling memory.kmem.limit is going to be tricky due to GFP_NOFS allocations, and it is still unclear whether we will have this knob in the unified hierarchy.
Changes in v3:
- Removed extra walk over all memory cgroups for shrinking per memcg slab caches; shrink_slab is now called per memcg from the loop in shrink_zone, as suggested by Johannes
- Reworked list_lru per memcg arrays init/destroy/update functions, hopefully making them more readable
- Rebased on top of v3.19-rc3-mmotm-2015-01-07-17-07
v2: https://lkml.org/lkml/2014/10/24/219
v1: https://lkml.org/lkml/2014/9/21/64
The patch set is organized as follows:
- Patches 1-3 implement per-memcg shrinker core with patches 1 and 2 preparing list_lru users for upcoming changes and patch 3 tuning shrink_slab.
- Patches 4 and 5 cleanup handling of max memcg_cache_id in the memcg core.
- Patch 6 gets rid of the useless list_lru->active_nodes, and patch 7 links all list_lrus to a list, which is required by memcg.
- Patch 8 adds per-memcg lrus to the list_lru structure, and finally patch 9 marks fs shrinkers as memcg aware.
Thanks,
Vladimir Davydov (9):
list_lru: introduce list_lru_shrink_{count,walk}
fs: consolidate {nr,free}_cached_objects args in shrink_control
vmscan: per memory cgroup slab shrinkers
memcg: rename some cache id related variables
memcg: add rwsem to synchronize against memcg_caches arrays
relocation
list_lru: get rid of ->active_nodes
list_lru: organize all list_lrus to list
list_lru: introduce per-memcg lists
fs: make shrinker memcg aware
fs/dcache.c | 14 +- fs/drop_caches.c | 14 -- fs/gfs2/quota.c | 6 +- fs/inode.c | 7 +- fs/internal.h | 7 +- fs/super.c | 44 +++-- fs/xfs/xfs_buf.c | 7 +- fs/xfs/xfs_qm.c | 7 +- fs/xfs/xfs_super.c | 7 +- include/linux/fs.h | 6 +- include/linux/list_lru.h | 70 ++++++-- include/linux/memcontrol.h | 37 +++- include/linux/mm.h | 5 +- include/linux/shrinker.h | 6 +- mm/list_lru.c | 412 +++++++++++++++++++++++++++++++++++++++++--- mm/memcontrol.c | 68 +++++--- mm/memory-failure.c | 11 +- mm/slab_common.c | 13 +- mm/vmscan.c | 86 ++++++--- mm/workingset.c | 6 +- 20 files changed, 657 insertions(+), 176 deletions(-)
Signature · 1 line
1.7.10.4
PATCH v3 1/9 list_lru: 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 | 24 +++++++++++------------- 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, 51 insertions(+), 43 deletions(-)
fs/dcache.c +6 -8
@@ -930,24 +930,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
@@ -750,14 +750,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
@@ -111,8 +112,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);/*
@@ -129,8 +129,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 +11 -13
@@ -77,8 +77,8 @@ 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 ;if (!total_objects) total_objects =1 ;
@@ -86,20 +86,20 @@ static unsigned long super_cache_scan(struct shrinker *shrink,/* 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;
@@ -118,17 +118,15 @@ static unsigned long super_cache_count(struct shrinker *shrink,* scalability bottleneck. The counts could get updated * between super_cache_count and super_cache_scan anyway. * Call to super_cache_count with shrinker_rwsem held -* ensures the safety of call to list_lru_count_node() and +* ensures the safety of call to list_lru_shrink_count() and * 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 +=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
@@ -1583,10 +1583,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;
@@ -1605,7 +1604,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
@@ -523,7 +523,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 ;
@@ -531,8 +530,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)
@@ -557,7 +556,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 v3 2/9 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
@@ -75,7 +75,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);
@@ -97,9 +97,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;
@@ -122,8 +123,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
@@ -1531,7 +1531,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)); }
@@ -1539,10 +1539,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
@@ -1616,8 +1616,10 @@ struct super_operations {struct dquot **(*get_dquots)(struct inode *);#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 · 1 line
1.7.10.4
PATCH v3 3/9 vmscan: per memory cgroup slab shrinkers
In reply to Vladimir DavydovThis patch adds SHRINKER_MEMCG_AWARE flag. If a shrinker has this flag set, it will be called per memory cgroup. The memory cgroup to scan objects from is passed in shrink_control->memcg. If the memory cgroup is NULL, a memcg aware shrinker is supposed to scan objects from the global list. Unaware shrinkers are only called on global pressure with memcg=NULL.
--- fs/drop_caches.c | 14 -------- include/linux/memcontrol.h | 7 ++++ include/linux/mm.h | 5 ++- include/linux/shrinker.h | 6 +++- mm/memcontrol.c | 2 +- mm/memory-failure.c | 11 ++---- mm/vmscan.c | 86 ++++++++++++++++++++++++++++++++------------ 7 files changed, 80 insertions(+), 51 deletions(-)
fs/drop_caches.c +0 -14
@@ -37,20 +37,6 @@ static void drop_pagecache_sb(struct super_block *sb, void *unused)iput (toput_inode); } -static void drop_slab (void ) -{ -int nr_objects; - -do { -int nid; - - nr_objects =0 ; -for_each_online_node (nid) - nr_objects +=shrink_node_slabs (GFP_KERNEL , nid, -1000 ,1000 ); - }while (nr_objects >10 ); -} -int drop_caches_sysctl_handler (struct ctl_table *table,int write,void __user *buffer,size_t *length,loff_t *ppos) {
include/linux/memcontrol.h +7 -0
@@ -375,6 +375,8 @@ static inline bool memcg_kmem_enabled(void)return static_key_false (&memcg_kmem_enabled_key); } +bool memcg_kmem_is_active (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
@@ -504,6 +506,11 @@ 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_newpage_charge (gfp_t gfp,struct mem_cgroup **memcg,int order) {
include/linux/mm.h +2 -3
@@ -2100,9 +2100,8 @@ int drop_caches_sysctl_handler(struct ctl_table *, int, void __user *, size_t *, loff_t *);#endif -unsigned long shrink_node_slabs (gfp_t gfp_mask,int nid, -unsigned long nr_scanned, -unsigned long nr_eligible); +void drop_slab (void ); +void drop_slab_node (int nid);#ifndef CONFIG_MMU #define randomize_va_space 0
include/linux/shrinker.h +5 -1
@@ -20,6 +20,9 @@ struct shrink_control {/* 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)
@@ -61,7 +64,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 +1 -1
@@ -365,7 +365,7 @@ struct mem_cgroup { };#ifdef CONFIG_MEMCG_KMEM -static bool memcg_kmem_is_active (struct mem_cgroup *memcg) +bool memcg_kmem_is_active (struct mem_cgroup *memcg) {return memcg->kmemcg_id >=0 ; }
mm/memory-failure.c +2 -9
@@ -242,15 +242,8 @@ void shake_page(struct page *p, int access)* Only call shrink_node_slabs here (which would also shrink * other caches) if access is not potentially fatal. */ -if (access) { -int nr; -int nid =page_to_nid (p); -do { - nr =shrink_node_slabs (GFP_KERNEL , nid,1000 ,1000 ); -if (page_count (p) ==1 ) -break ; - }while (nr >10 ); - } +if (access) +drop_slab_node (page_to_nid (p)); }EXPORT_SYMBOL_GPL (shake_page);
mm/vmscan.c +63 -23
160 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 v3 4/9 memcg: rename some cache id related variables
In reply to Vladimir Davydovmemcg_limited_groups_array_size, which defines the size of memcg_caches arrays, sounds rather cumbersome. Also it doesn't point anyhow that it's related to kmem/caches stuff. So let's rename it to memcg_nr_cache_ids. It's concise and points us directly to memcg_cache_id.
Also, rename kmem_limited_groups to memcg_cache_ida.
--- include/linux/memcontrol.h | 4 ++-- mm/memcontrol.c | 19 +++++++++---------- mm/slab_common.c | 4 ++-- 3 files changed, 13 insertions(+), 14 deletions(-)
include/linux/memcontrol.h +2 -2
@@ -360,7 +360,7 @@ static inline void sock_release_memcg(struct sock *sk)#ifdef CONFIG_MEMCG_KMEM extern struct static_key memcg_kmem_enabled_key; -extern int memcg_limited_groups_array_size; +extern int memcg_nr_cache_ids;/* * Helper macro to loop through all memcg-specific caches. Callers must still
@@ -368,7 +368,7 @@ extern int memcg_limited_groups_array_size;* the slab_mutex must be held when looping through those caches */ #define for_each_memcg_cache_index (_idx) \ - for ((_idx) = 0; (_idx) < memcg_limited_groups_array_size; (_idx)++) + for ((_idx) = 0; (_idx) < memcg_nr_cache_ids; (_idx)++)static inline bool memcg_kmem_enabled (void ) {
mm/memcontrol.c +9 -10
@@ -564,12 +564,11 @@ static void disarm_sock_keys(struct mem_cgroup *memcg)* memcgs, and none but the 200th is kmem-limited, we'd have to have a * 200 entry array for that. * -* The current size of the caches array is stored in -* memcg_limited_groups_array_size. It will double each time we have to -* increase it. +* The current size of the caches array is stored in memcg_nr_cache_ids. It +* will double each time we have to increase it. */ -static DEFINE_IDA (kmem_limited_groups); -int memcg_limited_groups_array_size; +static DEFINE_IDA (memcg_cache_ida); +int memcg_nr_cache_ids;/* * MIN_SIZE is different than 1, because we would like to avoid going through
@@ -2547,12 +2546,12 @@ static int memcg_alloc_cache_id(void)int id, size;int err; - id =ida_simple_get (&kmem_limited_groups, + id =ida_simple_get (&memcg_cache_ida,0 ,MEMCG_CACHES_MAX_SIZE ,GFP_KERNEL );if (id <0 )return id; -if (id < memcg_limited_groups_array_size) +if (id < memcg_nr_cache_ids)return id;/*
@@ -2568,7 +2567,7 @@ static int memcg_alloc_cache_id(void) err =memcg_update_all_caches (size);if (err) { -ida_simple_remove (&kmem_limited_groups, id); +ida_simple_remove (&memcg_cache_ida, id);return err; }return id;
@@ -2576,7 +2575,7 @@ static int memcg_alloc_cache_id(void)static void memcg_free_cache_id (int id) { -ida_simple_remove (&kmem_limited_groups, id); +ida_simple_remove (&memcg_cache_ida, id); }/*
@@ -2586,7 +2585,7 @@ static void memcg_free_cache_id(int id)*/ void memcg_update_array_size (int num) { - memcg_limited_groups_array_size = num; + memcg_nr_cache_ids = num; }struct memcg_kmem_cache_create_work {
mm/slab_common.c +2 -2
@@ -116,7 +116,7 @@ static int memcg_alloc_cache_params(struct mem_cgroup *memcg,if (!memcg) { size = offsetof(struct memcg_cache_params , memcg_caches); - size += memcg_limited_groups_array_size *sizeof (void *); + size += memcg_nr_cache_ids *sizeof (void *); }else size =sizeof (struct memcg_cache_params );
@@ -154,7 +154,7 @@ static int memcg_update_cache_params(struct kmem_cache *s, int num_memcgs) cur_params = s->memcg_params;memcpy (new_params->memcg_caches, cur_params->memcg_caches, - memcg_limited_groups_array_size *sizeof (void *)); + memcg_nr_cache_ids *sizeof (void *)); new_params->is_root_cache = true;
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 v3 5/9 memcg: add rwsem to synchronize against memcg_caches arrays relocation
In reply to Vladimir DavydovWe need a stable value of memcg_nr_cache_ids in kmem_cache_create() (memcg_alloc_cache_params() wants it for root caches), where we only hold the slab_mutex and no memcg-related locks. As a result, we have to update memcg_nr_cache_ids under the slab_mutex, which we can only take on the slab's side (see memcg_update_array_size). This looks awkward and will become even worse when per-memcg list_lru is introduced, which also wants stable access to memcg_nr_cache_ids.
To get rid of this dependency between the memcg_nr_cache_ids and the slab_mutex, this patch introduces a special rwsem. The rwsem is held for writing during memcg_caches arrays relocation and memcg_nr_cache_ids updates. Therefore one can take it for reading to get a stable access to memcg_caches arrays and/or memcg_nr_cache_ids.
Currently the semaphore is taken for reading only from kmem_cache_create, right before taking the slab_mutex, so right now there's no much point in using rwsem instead of mutex. However, once list_lru is made per-memcg it will allow list_lru initializations to proceed concurrently.
--- include/linux/memcontrol.h | 12 ++++++++++-- mm/memcontrol.c | 29 +++++++++++++++++++---------- mm/slab_common.c | 9 ++++----- 3 files changed, 33 insertions(+), 17 deletions(-)
include/linux/memcontrol.h +10 -2
@@ -361,6 +361,8 @@ static inline void sock_release_memcg(struct sock *sk)extern struct static_key memcg_kmem_enabled_key;extern int memcg_nr_cache_ids; +extern void memcg_get_cache_ids (void ); +extern void memcg_put_cache_ids (void );/* * Helper macro to loop through all memcg-specific caches. Callers must still
@@ -396,8 +398,6 @@ void __memcg_kmem_uncharge_pages(struct page *page, int order);int memcg_cache_id (struct mem_cgroup *memcg); -void memcg_update_array_size (int num_groups); -struct kmem_cache *__memcg_kmem_get_cache (struct kmem_cache *cachep);void __memcg_kmem_put_cache (struct kmem_cache *cachep);
@@ -531,6 +531,14 @@ static inline int memcg_cache_id(struct mem_cgroup *memcg)return -1 ; } +static inline void memcg_get_cache_ids (void ) +{ +} + +static inline void memcg_put_cache_ids (void ) +{ +} +static inline struct kmem_cache *memcg_kmem_get_cache (struct kmem_cache *cachep,gfp_t gfp) {
mm/memcontrol.c +19 -10
@@ -570,6 +570,19 @@ static void disarm_sock_keys(struct mem_cgroup *memcg)static DEFINE_IDA (memcg_cache_ida);int memcg_nr_cache_ids; +/* Protects memcg_nr_cache_ids */ +static DECLARE_RWSEM (memcg_cache_ids_sem); + +void memcg_get_cache_ids (void ) +{ +down_read (&memcg_cache_ids_sem); +} + +void memcg_put_cache_ids (void ) +{ +up_read (&memcg_cache_ids_sem); +} +/* * MIN_SIZE is different than 1, because we would like to avoid going through * the alloc/free process all the time. In a small machine, 4 kmem-limited
@@ -2558,6 +2571,7 @@ static int memcg_alloc_cache_id(void)* There's no space for the new id in memcg_caches arrays, * so we have to grow them. */ +down_write (&memcg_cache_ids_sem); size =2 * (id +1 );if (size <MEMCG_CACHES_MIN_SIZE )
@@ -2566,6 +2580,11 @@ static int memcg_alloc_cache_id(void) size =MEMCG_CACHES_MAX_SIZE ; err =memcg_update_all_caches (size); +if (!err) + memcg_nr_cache_ids = size; + +up_write (&memcg_cache_ids_sem); +if (err) {ida_simple_remove (&memcg_cache_ida, id);return err;
@@ -2578,16 +2597,6 @@ static void memcg_free_cache_id(int id)ida_simple_remove (&memcg_cache_ida, id); } -/* -* We should update the current array size iff all caches updates succeed. This -* can only be done from the slab side. The slab mutex needs to be held when -* calling this. -*/ -void memcg_update_array_size (int num) -{ - memcg_nr_cache_ids = num; -} -struct memcg_kmem_cache_create_work {struct mem_cgroup *memcg;struct kmem_cache *cachep;
mm/slab_common.c +4 -5
@@ -169,8 +169,8 @@ int memcg_update_all_caches(int num_memcgs) {struct kmem_cache *s;int ret =0 ; -mutex_lock (&slab_mutex); +mutex_lock (&slab_mutex);list_for_each_entry (s, &slab_caches, list) {if (!is_root_cache (s))continue ;
@@ -181,11 +181,8 @@ int memcg_update_all_caches(int num_memcgs)* up to this point in an updated state. */ if (ret) - goto out; +break ; } - -memcg_update_array_size (num_memcgs); -out:mutex_unlock (&slab_mutex);return ret; }
@@ -369,6 +366,7 @@ kmem_cache_create(const char *name, size_t size, size_t align,get_online_cpus ();get_online_mems (); +memcg_get_cache_ids ();mutex_lock (&slab_mutex);
@@ -407,6 +405,7 @@ kmem_cache_create(const char *name, size_t size, size_t align, out_unlock:mutex_unlock (&slab_mutex); +memcg_put_cache_ids ();put_online_mems ();put_online_cpus ();
Signature · 1 line
1.7.10.4
PATCH v3 6/9 list_lru: get rid of ->active_nodes
In reply to Vladimir DavydovThe active_nodes mask allows us to skip empty nodes when walking over list_lru items from all nodes in list_lru_count/walk. However, these functions are never called from hot paths, so it doesn't seem we need such kind of optimization there. OTOH, removing the mask will make it easier to make list_lru per-memcg.
--- include/linux/list_lru.h | 5 ++--- mm/list_lru.c | 10 +++------- 2 files changed, 5 insertions(+), 10 deletions(-)
include/linux/list_lru.h +2 -3
@@ -31,7 +31,6 @@ struct list_lru_node {struct list_lru {struct list_lru_node *node; -nodemask_t active_nodes; };void list_lru_destroy (struct list_lru *lru);
@@ -94,7 +93,7 @@ static inline unsigned long list_lru_count(struct list_lru *lru)long count =0 ;int nid; -for_each_node_mask (nid, lru->active_nodes) +for_each_node_state (nid,N_NORMAL_MEMORY ) count +=list_lru_count_node (lru, nid);return count;
@@ -142,7 +141,7 @@ list_lru_walk(struct list_lru *lru, list_lru_walk_cb isolate,long isolated =0 ;int nid; -for_each_node_mask (nid, lru->active_nodes) { +for_each_node_state (nid,N_NORMAL_MEMORY ) { isolated +=list_lru_walk_node (lru, nid, isolate, cb_arg, &nr_to_walk);if (nr_to_walk <=0 )
mm/list_lru.c +3 -7
@@ -19,8 +19,7 @@ bool list_lru_add(struct list_lru *lru, struct list_head *item)WARN_ON_ONCE (nlru->nr_items <0 );if (list_empty (item)) {list_add_tail (item, &nlru->list); -if (nlru->nr_items++ ==0 ) -node_set (nid, lru->active_nodes); + nlru->nr_items++;spin_unlock (&nlru->lock);return true; }
@@ -37,8 +36,7 @@ bool list_lru_del(struct list_lru *lru, struct list_head *item)spin_lock (&nlru->lock);if (!list_empty (item)) {list_del_init (item); -if (--nlru->nr_items ==0 ) -node_clear (nid, lru->active_nodes); + nlru->nr_items--;WARN_ON_ONCE (nlru->nr_items <0 );spin_unlock (&nlru->lock);return true;
@@ -90,8 +88,7 @@ restart:case LRU_REMOVED_RETRY :assert_spin_locked (&nlru->lock);case LRU_REMOVED : -if (--nlru->nr_items ==0 ) -node_clear (nid, lru->active_nodes); + nlru->nr_items--;WARN_ON_ONCE (nlru->nr_items <0 ); isolated++;/*
@@ -133,7 +130,6 @@ int list_lru_init_key(struct list_lru *lru, struct lock_class_key *key)if (!lru->node)return -ENOMEM ; -nodes_clear (lru->active_nodes);for (i =0 ; i < nr_node_ids; i++) {spin_lock_init (&lru->node[i].lock);if (key)
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 v3 7/9 list_lru: organize all list_lrus to list
In reply to Vladimir DavydovTo make list_lru memcg aware, we need all list_lrus to be kept on a list protected by a mutex, so that we could sleep while walking over the list.
Therefore after this change list_lru_destroy may sleep. Fortunately, there is the only user that calls it from an atomic context - it's put_super - and we can easily fix it by calling list_lru_destroy before put_super in destroy_locked_super - anyway we don't longer need lrus by that time.
Another point that should be noted is that list_lru_destroy is allowed to be called on an uninitialized zeroed-out object, in which case it is a no-op. Before this patch this was guaranteed by kfree, but now we need an explicit check there.
--- fs/super.c | 8 ++++++++ include/linux/list_lru.h | 3 +++ mm/list_lru.c | 34 ++++++++++++++++++++++++++++++++++ 3 files changed, 45 insertions(+)
fs/super.c +8 -0
@@ -282,6 +282,14 @@ void deactivate_locked_super(struct super_block *s)unregister_shrinker (&s->s_shrink); fs->kill_sb (s); +/* +* Since list_lru_destroy() may sleep, we cannot call it from +* put_super(), where we hold the sb_lock. Therefore we destroy +* the lru lists right now. +*/ +list_lru_destroy (&s->s_dentry_lru); +list_lru_destroy (&s->s_inode_lru); +put_filesystem (fs);put_super (s); }else {
include/linux/list_lru.h +3 -0
@@ -31,6 +31,9 @@ struct list_lru_node {struct list_lru {struct list_lru_node *node; +#ifdef CONFIG_MEMCG_KMEM +struct list_head list; +#endif };void list_lru_destroy (struct list_lru *lru);
mm/list_lru.c +34 -0
@@ -9,6 +9,34 @@#include <linux/mm.h> #include <linux/list_lru.h> #include <linux/slab.h> +#include <linux/mutex.h> + +#ifdef CONFIG_MEMCG_KMEM +static LIST_HEAD (list_lrus); +static DEFINE_MUTEX (list_lrus_mutex); + +static void list_lru_register (struct list_lru *lru) +{ +mutex_lock (&list_lrus_mutex); +list_add (&lru->list, &list_lrus); +mutex_unlock (&list_lrus_mutex); +} + +static void list_lru_unregister (struct list_lru *lru) +{ +mutex_lock (&list_lrus_mutex); +list_del (&lru->list); +mutex_unlock (&list_lrus_mutex); +} +#else +static void list_lru_register (struct list_lru *lru) +{ +} + +static void list_lru_unregister (struct list_lru *lru) +{ +} +#endif /* CONFIG_MEMCG_KMEM */ bool list_lru_add (struct list_lru *lru,struct list_head *item) {
@@ -137,12 +165,18 @@ int list_lru_init_key(struct list_lru *lru, struct lock_class_key *key)INIT_LIST_HEAD (&lru->node[i].list); lru->node[i].nr_items =0 ; } +list_lru_register (lru);return 0 ; }EXPORT_SYMBOL_GPL (list_lru_init_key);void list_lru_destroy (struct list_lru *lru) { +/* Already destroyed or not yet initialized? */ +if (!lru->node) +return ; +list_lru_unregister (lru);kfree (lru->node); + lru->node =NULL ; }EXPORT_SYMBOL_GPL (list_lru_destroy);
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 v3 8/9 list_lru: introduce 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_node structure (per-node part of the list_lru), one for each kmem-active memcg, and dispatches every item addition or removal to the list corresponding to the memcg which the item is accounted to. So now the list_lru structure is not just per node, but per node and per memcg.
Not all list_lrus need this feature, so this patch also adds a new method, list_lru_init_memcg, which initializes a list_lru as memcg aware. Otherwise (i.e. if initialized with old list_lru_init), the list_lru won't have per memcg lists.
Just like per memcg caches arrays, the arrays of per-memcg lists are indexed by memcg_cache_id, so we must grow them whenever memcg_nr_cache_ids is increased. So we introduce a callback, memcg_update_all_list_lrus, invoked by memcg_alloc_cache_id if the id space is full.
The locking is implemented in a manner similar to lruvecs, i.e. we have one lock per node that protects all lists (both global and per cgroup) on the node.
--- include/linux/list_lru.h | 52 ++++-- include/linux/memcontrol.h | 14 ++ mm/list_lru.c | 374 +++++++++++++++++++++++++++++++++++++++++--- mm/memcontrol.c | 20 +++ 4 files changed, 424 insertions(+), 36 deletions(-)
include/linux/list_lru.h +40 -12
@@ -11,6 +11,8 @@#include <linux/nodemask.h> #include <linux/shrinker.h> +struct mem_cgroup ; +/* list_lru_walk_cb has to always return one of those */ enum lru_status {LRU_REMOVED ,/* item removed from list */
@@ -22,11 +24,26 @@ enum lru_status {internally, but has to return locked. */ }; -struct list_lru_node { -spinlock_t lock; +struct list_lru_one {struct list_head list;/* kept as signed so we can catch imbalance bugs */ long nr_items; +}; + +struct list_lru_memcg { +/* array of per cgroup lists, indexed by memcg_cache_id */ +struct list_lru_one *lru[0 ]; +}; + +struct list_lru_node { +/* protects all lists on the node, including per cgroup */ +spinlock_t lock; +/* global list, used for the root cgroup in cgroup aware lrus */ +struct list_lru_one lru; +#ifdef CONFIG_MEMCG_KMEM +/* for cgroup aware lrus points to per cgroup lists, otherwise NULL */ +struct list_lru_memcg *memcg_lrus; +#endif }____cacheline_aligned_in_smp ;struct list_lru {
@@ -37,11 +54,14 @@ struct list_lru { };void list_lru_destroy (struct list_lru *lru); -int list_lru_init_key (struct list_lru *lru,struct lock_class_key *key); -static inline int list_lru_init (struct list_lru *lru) -{ -return list_lru_init_key (lru,NULL ); -} +int __list_lru_init (struct list_lru *lru,bool memcg_aware, +struct lock_class_key *key); + +#define list_lru_init (lru) __list_lru_init((lru), false, NULL) +#define list_lru_init_key (lru, key) __list_lru_init((lru), false, (key)) +#define list_lru_init_memcg (lru) __list_lru_init((lru), true, NULL) + +int memcg_update_all_list_lrus (int num_memcgs);/** * list_lru_add: add an element to the lru list's tail
@@ -75,20 +95,23 @@ bool list_lru_add(struct list_lru *lru, struct list_head *item);bool list_lru_del (struct list_lru *lru,struct list_head *item);/** -* list_lru_count_node: return the number of objects currently held by @lru +* list_lru_count_one: return the number of objects currently held by @lru * @lru: the lru pointer. * @nid: the node id to count from. +* @memcg: the cgroup to count from. * * Always return a non-negative number, 0 for empty lists. There is no * guarantee that the list is not updated while the count is being computed. * Callers that want such a guarantee need to provide an outer lock. */ +unsigned long list_lru_count_one (struct list_lru *lru, +int nid,struct mem_cgroup *memcg);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); +return list_lru_count_one (lru, sc->nid, sc->memcg); }static inline unsigned long list_lru_count (struct list_lru *lru)
@@ -105,9 +128,10 @@ static inline unsigned long list_lru_count(struct list_lru *lru)typedef enum lru_status (*list_lru_walk_cb )(struct list_head *item,spinlock_t *lock,void *cb_arg);/** -* list_lru_walk_node: walk a list_lru, isolating and disposing freeable items. +* list_lru_walk_one: walk a list_lru, isolating and disposing freeable items. * @lru: the lru pointer. * @nid: the node id to scan from. +* @memcg: the cgroup to scan from. * @isolate: callback function that is resposible for deciding what to do with * the item currently being scanned * @cb_arg: opaque type that will be passed to @isolate
@@ -125,6 +149,10 @@ typedef enum lru_status* * Return value: the number of objects effectively removed from the LRU. */ +unsigned long list_lru_walk_one (struct list_lru *lru, +int nid,struct mem_cgroup *memcg, +list_lru_walk_cb isolate,void *cb_arg, +unsigned long *nr_to_walk);unsigned long list_lru_walk_node (struct list_lru *lru,int nid,list_lru_walk_cb isolate,void *cb_arg,unsigned long *nr_to_walk);
@@ -133,8 +161,8 @@ 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); + return list_lru_walk_one(lru, sc->nid, sc->memcg, isolate, cb_arg, + &sc->nr_to_scan); } static inline unsigned long
include/linux/memcontrol.h +14 -0
@@ -401,6 +401,8 @@ int memcg_cache_id(struct mem_cgroup *memcg);struct kmem_cache *__memcg_kmem_get_cache (struct kmem_cache *cachep);void __memcg_kmem_put_cache (struct kmem_cache *cachep); +struct mem_cgroup *__mem_cgroup_from_kmem (void *ptr); +int memcg_charge_kmem (struct mem_cgroup *memcg,gfp_t gfp,unsigned long nr_pages);void memcg_uncharge_kmem (struct mem_cgroup *memcg,unsigned long nr_pages);
@@ -497,6 +499,13 @@ static __always_inline void memcg_kmem_put_cache(struct kmem_cache *cachep)if (memcg_kmem_enabled ())__memcg_kmem_put_cache (cachep); } + +static __always_inline struct mem_cgroup *mem_cgroup_from_kmem(void *ptr) +{ +if (!memcg_kmem_enabled ()) +return NULL ; +return __mem_cgroup_from_kmem (ptr); +}#else #define for_each_memcg_cache_index (_idx) \ for (; NULL; )
@@ -548,6 +557,11 @@ memcg_kmem_get_cache(struct kmem_cache *cachep, gfp_t gfp)static inline void memcg_kmem_put_cache (struct kmem_cache *cachep) { } + +static inline struct mem_cgroup *mem_cgroup_from_kmem (void *ptr) +{ +return NULL ; +}#endif /* CONFIG_MEMCG_KMEM */ #endif /* _LINUX_MEMCONTROL_H */
mm/list_lru.c +350 -24
468 lines not on this page · raw message
mm/memcontrol.c +20 -0
@@ -2581,6 +2581,8 @@ static int memcg_alloc_cache_id(void) err =memcg_update_all_caches (size);if (!err) + err =memcg_update_all_list_lrus (size); +if (!err) memcg_nr_cache_ids = size;up_write (&memcg_cache_ids_sem);
@@ -2774,6 +2776,24 @@ void __memcg_kmem_uncharge_pages(struct page *page, int order)memcg_uncharge_kmem (memcg,1 << order); page->mem_cgroup = NULL; } + +struct mem_cgroup *__mem_cgroup_from_kmem (void *ptr) +{ +struct mem_cgroup *memcg =NULL ; +struct kmem_cache *cachep; +struct page *page; + + page =virt_to_head_page (ptr); +if (PageSlab (page)) { + cachep = page->slab_cache; +if (!is_root_cache (cachep)) + memcg = cachep->memcg_params->memcg; + }else +/* page allocated by alloc_kmem_pages */ + memcg = page->mem_cgroup; + +return memcg; +}#endif /* CONFIG_MEMCG_KMEM */ #ifdef CONFIG_TRANSPARENT_HUGEPAGE
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 v3 9/9 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 aware. Let's do it for the general FS shrinker (super_block::s_shrink).
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. It's a big question whether we should account them to memcg at all.
--- fs/super.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-)
fs/super.c +3 -3
@@ -189,9 +189,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_memcg (&s->s_dentry_lru)) goto fail; -if (list_lru_init (&s->s_inode_lru)) +if (list_lru_init_memcg (&s->s_inode_lru)) goto fail;init_rwsem (&s->s_umount);
@@ -227,7 +227,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:
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>