Linux-mm Archive on lore.kernel.org
 help / color / mirror / Atom feed
From: Baolin Wang <baolin.wang@linux.alibaba.com>
To: Qi Zheng <qi.zheng@linux.dev>,
	hughd@google.com, akpm@linux-foundation.org,
	usama.arif@linux.dev
Cc: linux-mm@kvack.org, linux-kernel@vger.kernel.org,
	Qi Zheng <zhengqi.arch@bytedance.com>
Subject: Re: [PATCH v2 2/2] mm: shmem: make unused huge shrinker memcg aware
Date: Mon, 27 Jul 2026 13:11:00 +0800	[thread overview]
Message-ID: <313c4dc7-9d03-40da-b6a0-170a774010b3@linux.alibaba.com> (raw)
In-Reply-To: <889581093179462979dbfb5458620fcca531787b.1784621804.git.zhengqi.arch@bytedance.com>

Hi Qi,

(Sorry for the late reply due to my vacation)

On 7/21/26 4:34 PM, Qi Zheng wrote:
> From: Qi Zheng <zhengqi.arch@bytedance.com>
> 
> The shmem unused huge shrinker keeps a per-superblock list of inodes whose
> tail huge folio extends beyond i_size. Since that list is not memcg aware,
> reclaim triggered by one memcg can scan inodes from the whole superblock
> and split shmem huge folios charged to unrelated memcgs.
> 
> Convert the shrink list to a memcg-aware list_lru. Queue each inode on the
> list_lru sublist matching the memcg and node of the current tail huge
> folio, so non-root memcg reclaim only walks candidates charged to the
> reclaiming memcg. Global reclaim, root memcg reclaim and shmem quota
> reclaim keep global semantics.
> 
> The list_lru still tracks inodes while the actual split target is the
> current tail huge folio, so validate the folio memcg/node during scan. If
> the folio no longer matches the reclaim context or splitting cannot
> proceed, requeue the inode according to the current tail folio; if the
> inode is no longer shrinkable, drop the scan entry.
> 
> This can be tested with the shrinker debugfs interface by allocating 32
> tmpfs tail THPs in each of two memcgs, then scanning the sb-tmpfs shrinker
> with memcg A's cgroup id:
> 
>                 before A scan        after A scan
>    base         A=64M, B=64M         A=0,  B=0
>    patched      A=64M, B=64M         A=0,  B=64M
> 
> Signed-off-by: Qi Zheng <zhengqi.arch@bytedance.com>
> ---
>   include/linux/shmem_fs.h |  12 +-
>   mm/shmem.c               | 378 +++++++++++++++++++++++++++++----------
>   2 files changed, 294 insertions(+), 96 deletions(-)
> 
> diff --git a/include/linux/shmem_fs.h b/include/linux/shmem_fs.h
> index 5663dff53186e..aeb59901a840c 100644
> --- a/include/linux/shmem_fs.h
> +++ b/include/linux/shmem_fs.h
> @@ -11,6 +11,7 @@
>   #include <linux/fs_parser.h>
>   #include <linux/userfaultfd_k.h>
>   #include <linux/bits.h>
> +#include <linux/list_lru.h>
>   
>   /* inode in-kernel data */
>   
> @@ -54,6 +55,11 @@ struct shmem_inode_info {
>   	struct dquot __rcu	*i_dquot[MAXQUOTAS];
>   #endif
>   	struct inode		vfs_inode;
> +
> +#ifdef CONFIG_TRANSPARENT_HUGEPAGE
> +	struct mem_cgroup	*shrinklist_memcg;
> +	int			shrinklist_nid;
> +#endif
>   };

Thanks. This looks better now.:) Some comments below.

>   #define SHMEM_FL_USER_VISIBLE		(FS_FL_USER_VISIBLE | FS_CASEFOLD_FL)
> @@ -83,9 +89,9 @@ struct shmem_sb_info {
>   	ino_t next_ino;		    /* The next per-sb inode number to use */
>   	ino_t __percpu *ino_batch;  /* The next per-cpu inode number to use */
>   	struct mempolicy *mpol;     /* default memory policy for mappings */
> -	spinlock_t shrinklist_lock;   /* Protects shrinklist */
> -	struct list_head shrinklist;  /* List of shinkable inodes */
> -	unsigned long shrinklist_len; /* Length of shrinklist */
> +#ifdef CONFIG_TRANSPARENT_HUGEPAGE
> +	struct list_lru shrinklist; /* List of shrinkable inodes */
> +#endif
>   	struct shmem_quota_limits qlimits; /* Default quota limits */
>   	struct simple_xattr_cache xa_cache;
>   };
> diff --git a/mm/shmem.c b/mm/shmem.c
> index 20c24d92da430..8bf6dd850f85b 100644
> --- a/mm/shmem.c
> +++ b/mm/shmem.c
> @@ -724,51 +724,242 @@ static const char *shmem_format_huge(int huge)
>   }
>   #endif
>   
> +static bool is_shmem_unused_huge_isolated(struct shmem_inode_info *info)
> +{
> +
> +	return info->shrinklist_nid == -1;
> +}
> +
> +static void set_shmem_unused_huge_isolated(struct shmem_inode_info *info)
> +{
> +	info->shrinklist_nid = -1;
> +}
> +
> +static struct mem_cgroup *shmem_get_and_clear_memcg(struct shmem_inode_info *info)
> +{
> +	struct mem_cgroup *memcg = info->shrinklist_memcg;
> +
> +	info->shrinklist_memcg = NULL;
> +
> +	return memcg;
> +}
> +
> +#ifdef CONFIG_MEMCG
> +static struct mem_cgroup *
> +shmem_unused_huge_alloc_lru(struct shmem_sb_info *sbinfo, struct folio *folio,
> +			    gfp_t gfp)
> +{
> +	struct mem_cgroup *memcg;
> +	int ret;
> +
> +	memcg = get_mem_cgroup_from_folio(folio);
> +	if (!memcg)
> +		return NULL;

It looks like we should return an error here instead of NULL? since 
callers use IS_ERR() to check the return value of this function.

> +	ret = memcg_list_lru_alloc(memcg, &sbinfo->shrinklist, gfp);
> +	if (ret) {
> +		mem_cgroup_put(memcg);
> +		return ERR_PTR(ret);
> +	}
> +
> +	return memcg;
> +}
> +#else
> +static struct mem_cgroup *
> +shmem_unused_huge_alloc_lru(struct shmem_sb_info *sbinfo, struct folio *folio,
> +			    gfp_t gfp)
> +{
> +	return NULL;

Ditto.

> +}
> +#endif
> +
> +static void shmem_unused_huge_add(struct inode *inode, struct folio *folio,
> +				  gfp_t gfp)
> +{
> +	struct shmem_inode_info *info = SHMEM_I(inode);
> +	struct shmem_sb_info *sbinfo = SHMEM_SB(inode->i_sb);
> +	int nid = folio_nid(folio);
> +	struct mem_cgroup *memcg = NULL, *old_memcg = NULL;
> +
> +	memcg = shmem_unused_huge_alloc_lru(sbinfo, folio, gfp);
> +	if (IS_ERR(memcg))
> +		return;
> +
> +	spin_lock(&info->lock);
> +	if (!list_empty(&info->shrinklist)) {
> +		/* isolated on scan list, let shrink handle it */
> +		if (is_shmem_unused_huge_isolated(info))
> +			goto unlock;
> +
> +		if (info->shrinklist_nid == nid &&
> +		    info->shrinklist_memcg == memcg)
> +			goto unlock;
> +
> +		list_lru_del(&sbinfo->shrinklist, &info->shrinklist,
> +			     info->shrinklist_nid, info->shrinklist_memcg);
> +		old_memcg = shmem_get_and_clear_memcg(info);
> +	}
> +
> +	info->shrinklist_memcg = memcg;
> +	info->shrinklist_nid = nid;
> +	list_lru_add(&sbinfo->shrinklist, &info->shrinklist, nid, memcg);
> +	memcg = NULL;
> +unlock:
> +	spin_unlock(&info->lock);
> +	mem_cgroup_put(old_memcg);
> +	mem_cgroup_put(memcg);
> +}
> +
> +static void shmem_unused_huge_del(struct inode *inode)
> +{
> +	struct shmem_inode_info *info = SHMEM_I(inode);
> +	struct shmem_sb_info *sbinfo = SHMEM_SB(inode->i_sb);
> +	struct mem_cgroup *memcg = NULL;
> +
> +	spin_lock(&info->lock);
> +	if (!list_empty(&info->shrinklist)) {
> +		list_lru_del(&sbinfo->shrinklist, &info->shrinklist,
> +			     info->shrinklist_nid, info->shrinklist_memcg);
> +		memcg = shmem_get_and_clear_memcg(info);
> +	}
> +	spin_unlock(&info->lock);
> +
> +	mem_cgroup_put(memcg);
> +}
> +
> +struct shmem_unused_huge_scan {
> +	struct list_head list;
> +	struct shrink_control *sc;
> +};
> +
> +static enum lru_status shmem_unused_huge_isolate(struct list_head *item,
> +						 struct list_lru_one *lru,
> +						 void *arg)
> +{
> +	struct shmem_unused_huge_scan *scan = arg;
> +	struct shmem_inode_info *info;
> +	struct inode *inode;
> +	struct mem_cgroup *memcg = NULL;
> +
> +	info = list_entry(item, struct shmem_inode_info, shrinklist);
> +
> +	/*
> +	 * Use trylock to avoid ABBA deadlock: add/del path takes info->lock
> +	 * before the list_lru bucket lock, while here the order is reversed.
> +	 */
> +	if (!spin_trylock(&info->lock))
> +		return LRU_SKIP;
> +
> +	/* pin the inode */
> +	inode = igrab(&info->vfs_inode);
> +	/* inode is about to be evicted */
> +	if (!inode) {
> +		list_lru_isolate(lru, item);
> +		memcg = shmem_get_and_clear_memcg(info);
> +		spin_unlock(&info->lock);
> +		mem_cgroup_put(memcg);
> +		return LRU_REMOVED;
> +	}
> +
> +	list_lru_isolate(lru, item);
> +	memcg = shmem_get_and_clear_memcg(info);
> +	set_shmem_unused_huge_isolated(info);
> +	list_add_tail(&info->shrinklist, &scan->list);
> +	spin_unlock(&info->lock);
> +	mem_cgroup_put(memcg);
> +
> +	return LRU_REMOVED;
> +}
> +
> +static bool is_shmem_unused_huge_match(struct folio *folio,
> +				       struct shrink_control *sc)
> +{
> +	struct mem_cgroup *memcg = NULL;
> +	bool match;
> +
> +	/*
> +	 * Only non-root memcg reclaim needs to match the folio charge against
> +	 * sc->memcg. Skip the folio memcg check for the following cases:
> +	 * 1. shmem quota reclaim (sc == NULL)
> +	 * 2. global shrinker reclaim
> +	 * 3. root memcg reclaim
> +	 */
> +	if (!sc || !sc->memcg || mem_cgroup_is_root(sc->memcg))
> +		return true;
> +
> +	if (folio_nid(folio) != sc->nid)
> +		return false;
> +
> +	memcg = get_mem_cgroup_from_folio(folio);
> +	match = memcg == sc->memcg;
> +	mem_cgroup_put(memcg);
> +
> +	return match;
> +}
> +
> +static void shmem_unused_huge_drop(struct inode *inode)
> +{
> +	struct shmem_inode_info *info = SHMEM_I(inode);
> +
> +	spin_lock(&info->lock);
> +	list_del_init(&info->shrinklist);
> +	spin_unlock(&info->lock);
> +}
> +
> +static void shmem_unused_huge_requeue(struct inode *inode, struct folio *folio)
> +{
> +	struct shmem_inode_info *info = SHMEM_I(inode);
> +	struct shmem_sb_info *sbinfo = SHMEM_SB(inode->i_sb);
> +	struct mem_cgroup *memcg;
> +	int nid = folio_nid(folio);
> +
> +	memcg = shmem_unused_huge_alloc_lru(sbinfo, folio, GFP_NOWAIT);
> +	if (IS_ERR(memcg))
> +		goto drop;

You can just call shmem_unused_huge_drop(), then remove the 'goto'.

> +
> +	spin_lock(&info->lock);
> +	/* Requeue the inode to shrinklist */
> +	list_del_init(&info->shrinklist);
> +	list_lru_add(&sbinfo->shrinklist, &info->shrinklist, nid, memcg);
> +	info->shrinklist_memcg = memcg;
> +	info->shrinklist_nid = nid;
> +	memcg = NULL;
> +	spin_unlock(&info->lock);
> +	mem_cgroup_put(memcg);

Seems you can remove the 'memcg = NULL' and 'mem_cgroup_put'.

> +	return;
> +
> +drop:
> +	shmem_unused_huge_drop(inode);
> +}
> +
>   static unsigned long shmem_unused_huge_shrink(struct shmem_sb_info *sbinfo,
>   		struct shrink_control *sc, unsigned long nr_to_free)
>   {
> -	LIST_HEAD(list), *pos, *next;
> +	struct shmem_unused_huge_scan scan;
>   	struct inode *inode;
>   	struct shmem_inode_info *info;
>   	struct folio *folio;
> -	unsigned long batch = sc ? sc->nr_to_scan : 128;
> +	struct list_head *pos, *next;
>   	unsigned long split = 0, freed = 0;
>   
> -	if (list_empty(&sbinfo->shrinklist))
> -		return SHRINK_STOP;
> -
> -	spin_lock(&sbinfo->shrinklist_lock);
> -	list_for_each_safe(pos, next, &sbinfo->shrinklist) {
> -		info = list_entry(pos, struct shmem_inode_info, shrinklist);
> -
> -		/* pin the inode */
> -		inode = igrab(&info->vfs_inode);
> -
> -		/* inode is about to be evicted */
> -		if (!inode) {
> -			list_del_init(&info->shrinklist);
> -			goto next;
> -		}
> -
> -		list_move(&info->shrinklist, &list);
> -next:
> -		sbinfo->shrinklist_len--;
> -		if (!--batch)
> -			break;
> -	}
> -	spin_unlock(&sbinfo->shrinklist_lock);
> +	INIT_LIST_HEAD(&scan.list);
> +	scan.sc = sc;
> +	if (sc)
> +		list_lru_shrink_walk(&sbinfo->shrinklist, sc,
> +				     shmem_unused_huge_isolate, &scan);
> +	else
> +		list_lru_walk(&sbinfo->shrinklist, shmem_unused_huge_isolate,
> +			      &scan, 128);
>   
> -	list_for_each_safe(pos, next, &list) {
> -		pgoff_t next, end;
> +	list_for_each_safe(pos, next, &scan.list) {
> +		pgoff_t folio_end, end;
>   		loff_t i_size;
>   		int ret;
>   
>   		info = list_entry(pos, struct shmem_inode_info, shrinklist);
>   		inode = &info->vfs_inode;
>   
> -		if (nr_to_free && freed >= nr_to_free)
> -			goto move_back;
> -

Why move this logic? If this fixes anything, then a separate fix patch 
would be clearer.

>   		i_size = i_size_read(inode);
>   		folio = filemap_get_entry(inode->i_mapping, i_size / PAGE_SIZE);
>   		if (!folio || xa_is_value(folio))
> @@ -781,13 +972,25 @@ static unsigned long shmem_unused_huge_shrink(struct shmem_sb_info *sbinfo,
>   		}
>   
>   		/* Check if there is anything to gain from splitting */
> -		next = folio_next_index(folio);
> +		folio_end = folio_next_index(folio);
>   		end = shmem_fallocend(inode, DIV_ROUND_UP(i_size, PAGE_SIZE));
> -		if (end <= folio->index || end >= next) {
> +		if (end <= folio->index || end >= folio_end) {
>   			folio_put(folio);
>   			goto drop;
>   		}
>   
> +		if (!is_shmem_unused_huge_match(folio, scan.sc)) {
> +			shmem_unused_huge_requeue(inode, folio);
> +			folio_put(folio);

Maybe keeping the 'move_back' label would be clearer? since you've added 
several places to requeue the inode.

> +			goto put;
> +		}
> +
> +		if (nr_to_free && freed >= nr_to_free) {
> +			shmem_unused_huge_requeue(inode, folio);
> +			folio_put(folio);
> +			goto put;
> +		}
> +
>   		/*
>   		 * Move the inode on the list back to shrinklist if we failed
>   		 * to lock the page at this time.
> @@ -796,34 +999,33 @@ static unsigned long shmem_unused_huge_shrink(struct shmem_sb_info *sbinfo,
>   		 * reclaim path.
>   		 */
>   		if (!folio_trylock(folio)) {
> +			shmem_unused_huge_requeue(inode, folio);
> +			folio_put(folio);
> +			goto put;
> +		}
> +
> +		if (!is_shmem_unused_huge_match(folio, scan.sc)) {
> +			folio_unlock(folio);
> +			shmem_unused_huge_requeue(inode, folio);
>   			folio_put(folio);
> -			goto move_back;
> +			goto put;
>   		}
>   
>   		ret = split_folio(folio);
>   		folio_unlock(folio);
> -		folio_put(folio);
>   
>   		/* If split failed move the inode on the list back to shrinklist */
> -		if (ret)
> -			goto move_back;
> +		if (ret) {
> +			shmem_unused_huge_requeue(inode, folio);
> +			folio_put(folio);
> +			goto put;
> +		}
>   
> -		freed += next - end;
> +		freed += folio_end - end;
>   		split++;
> +		folio_put(folio);
>   drop:
> -		list_del_init(&info->shrinklist);
> -		goto put;
> -move_back:
> -		/*
> -		 * Make sure the inode is either on the global list or deleted
> -		 * from any local list before iput() since it could be deleted
> -		 * in another thread once we put the inode (then the local list
> -		 * is corrupted).
> -		 */
> -		spin_lock(&sbinfo->shrinklist_lock);
> -		list_move(&info->shrinklist, &sbinfo->shrinklist);
> -		sbinfo->shrinklist_len++;
> -		spin_unlock(&sbinfo->shrinklist_lock);
> +		shmem_unused_huge_drop(inode);
>   put:
>   		iput(inode);
>   	}
> @@ -836,7 +1038,7 @@ static long shmem_unused_huge_scan(struct super_block *sb,
>   {
>   	struct shmem_sb_info *sbinfo = SHMEM_SB(sb);
>   
> -	if (!READ_ONCE(sbinfo->shrinklist_len))
> +	if (!list_lru_shrink_count(&sbinfo->shrinklist, sc))
>   		return SHRINK_STOP;
>   
>   	return shmem_unused_huge_shrink(sbinfo, sc, 0);
> @@ -847,21 +1049,21 @@ static long shmem_unused_huge_count(struct super_block *sb,
>   {
>   	struct shmem_sb_info *sbinfo = SHMEM_SB(sb);
>   
> -	/*
> -	 * The per-superblock shrinklist is filesystem-global and does not
> -	 * honour sc->memcg, so it is only meaningful on the global (kswapd or
> -	 * root direct reclaim) shrink path. Skip the per-memcg iterations of
> -	 * shrink_slab_memcg() to avoid queueing duplicate global work.
> -	 */
> -	if (!mem_cgroup_shrink_is_root(sc))
> -		return 0;
> -
> -	return READ_ONCE(sbinfo->shrinklist_len);
> +	return list_lru_shrink_count(&sbinfo->shrinklist, sc);
>   }
>   #else /* !CONFIG_TRANSPARENT_HUGEPAGE */
>   
>   #define shmem_huge SHMEM_HUGE_DENY
>   
> +static void shmem_unused_huge_add(struct inode *inode, struct folio *folio,
> +				  gfp_t gfp)
> +{
> +}
> +
> +static void shmem_unused_huge_del(struct inode *inode)
> +{
> +}
> +
>   static unsigned long shmem_unused_huge_shrink(struct shmem_sb_info *sbinfo,
>   		struct shrink_control *sc, unsigned long nr_to_free)
>   {
> @@ -1417,14 +1619,7 @@ static void shmem_evict_inode(struct inode *inode)
>   		inode->i_size = 0;
>   		mapping_set_exiting(inode->i_mapping);
>   		shmem_truncate_range(inode, 0, (loff_t)-1);
> -		if (!list_empty(&info->shrinklist)) {
> -			spin_lock(&sbinfo->shrinklist_lock);
> -			if (!list_empty(&info->shrinklist)) {
> -				list_del_init(&info->shrinklist);
> -				sbinfo->shrinklist_len--;
> -			}
> -			spin_unlock(&sbinfo->shrinklist_lock);
> -		}
> +		shmem_unused_huge_del(inode);
>   		while (!list_empty(&info->swaplist)) {
>   			/* Wait while shmem_unuse() is scanning this inode... */
>   			wait_var_event(&info->stop_eviction,
> @@ -2532,28 +2727,6 @@ static int shmem_get_folio_gfp(struct inode *inode, pgoff_t index,
>   
>   alloced:
>   	alloced = true;
> -	if (folio_test_large(folio) &&
> -	    DIV_ROUND_UP(i_size_read(inode), PAGE_SIZE) <
> -					folio_next_index(folio)) {
> -		struct shmem_sb_info *sbinfo = SHMEM_SB(inode->i_sb);
> -		struct shmem_inode_info *info = SHMEM_I(inode);
> -		/*
> -		 * Part of the large folio is beyond i_size: subject
> -		 * to shrink under memory pressure.
> -		 */
> -		spin_lock(&sbinfo->shrinklist_lock);
> -		/*
> -		 * _careful to defend against unlocked access to
> -		 * ->shrink_list in shmem_unused_huge_shrink()
> -		 */
> -		if (list_empty_careful(&info->shrinklist)) {
> -			list_add_tail(&info->shrinklist,
> -				      &sbinfo->shrinklist);
> -			sbinfo->shrinklist_len++;
> -		}
> -		spin_unlock(&sbinfo->shrinklist_lock);
> -	}

Why move this additional logic down?

>   	if (sgp == SGP_WRITE)
>   		folio_set_referenced(folio);
>   	/*
> @@ -2582,6 +2755,15 @@ static int shmem_get_folio_gfp(struct inode *inode, pgoff_t index,
>   		error = -EINVAL;
>   		goto unlock;
>   	}
> +
> +	if (alloced && folio_test_large(folio) &&
> +	    DIV_ROUND_UP(i_size_read(inode), PAGE_SIZE) < folio_next_index(folio)) {
> +		/*
> +		 * Part of the large folio is beyond i_size: subject
> +		 * to shrink under memory pressure.
> +		 */
> +		shmem_unused_huge_add(inode, folio, gfp);
> +	}
>   out:
>   	*foliop = folio;
>   	return 0;
> @@ -3058,6 +3240,10 @@ static struct inode *__shmem_get_inode(struct mnt_idmap *idmap,
>   	if (info->fsflags)
>   		shmem_set_inode_flags(inode, info->fsflags, NULL);
>   	INIT_LIST_HEAD(&info->shrinklist);
> +#ifdef CONFIG_TRANSPARENT_HUGEPAGE
> +	info->shrinklist_memcg = NULL;
> +	info->shrinklist_nid = -1;
> +#endif
>   	INIT_LIST_HEAD(&info->swaplist);
>   	cache_no_acl(inode);
>   	if (sbinfo->noswap)
> @@ -4924,6 +5110,9 @@ static void shmem_put_super(struct super_block *sb)
>   #endif
>   	free_percpu(sbinfo->ino_batch);
>   	percpu_counter_destroy(&sbinfo->used_blocks);
> +#ifdef CONFIG_TRANSPARENT_HUGEPAGE
> +	list_lru_destroy(&sbinfo->shrinklist);
> +#endif
>   	mpol_put(sbinfo->mpol);
>   #ifdef CONFIG_TMPFS_XATTR
>   	simple_xattr_cache_cleanup(&sbinfo->xa_cache);
> @@ -5017,8 +5206,11 @@ static int shmem_fill_super(struct super_block *sb, struct fs_context *fc)
>   	raw_spin_lock_init(&sbinfo->stat_lock);
>   	if (percpu_counter_init(&sbinfo->used_blocks, 0, GFP_KERNEL))
>   		goto failed;
> -	spin_lock_init(&sbinfo->shrinklist_lock);
> -	INIT_LIST_HEAD(&sbinfo->shrinklist);
> +
> +#ifdef CONFIG_TRANSPARENT_HUGEPAGE
> +	if (list_lru_init_memcg(&sbinfo->shrinklist, sb->s_shrink))
> +		goto failed;
> +#endif
>   
>   	sb->s_maxbytes = MAX_LFS_FILESIZE;
>   	sb->s_blocksize = PAGE_SIZE;



  parent reply	other threads:[~2026-07-27  5:11 UTC|newest]

Thread overview: 6+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-07-21  8:34 [PATCH v2 0/2] make unused huge shrinker memcg aware Qi Zheng
2026-07-21  8:34 ` [PATCH v2 1/2] fs: push nr_cached_objects memcg gating into individual filesystems Qi Zheng
2026-07-21  8:34 ` [PATCH v2 2/2] mm: shmem: make unused huge shrinker memcg aware Qi Zheng
2026-07-21  9:25   ` Qi Zheng
2026-07-27  5:11   ` Baolin Wang [this message]
2026-07-28  7:12     ` Qi Zheng

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=313c4dc7-9d03-40da-b6a0-170a774010b3@linux.alibaba.com \
    --to=baolin.wang@linux.alibaba.com \
    --cc=akpm@linux-foundation.org \
    --cc=hughd@google.com \
    --cc=linux-kernel@vger.kernel.org \
    --cc=linux-mm@kvack.org \
    --cc=qi.zheng@linux.dev \
    --cc=usama.arif@linux.dev \
    --cc=zhengqi.arch@bytedance.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox