All of lore.kernel.org
 help / color / mirror / Atom feed
From: Joshua Hahn <joshua.hahnjy@gmail.com>
To: Ackerley Tng via B4 Relay <devnull+ackerleytng.google.com@kernel.org>
Cc: Andrew Morton <akpm@linux-foundation.org>,
	David Hildenbrand <david@kernel.org>,
	Dongliang Mu <dzm91@hust.edu.cn>,
	Hongxiang Lou <louhongxiang@huawei.com>,
	Johannes Weiner <hannes@cmpxchg.org>,
	Jonathan Corbet <corbet@lwn.net>,
	"Liam R. Howlett" <liam@infradead.org>,
	Lorenzo Stoakes <ljs@kernel.org>,
	Miaohe Lin <linmiaohe@huawei.com>,
	Michal Hocko <mhocko@kernel.org>, Mike Rapoport <rppt@kernel.org>,
	Muchun Song <muchun.song@linux.dev>,
	Nhat Pham <nphamcs@gmail.com>, Oscar Salvador <osalvador@suse.de>,
	Peter Xu <peterx@redhat.com>,
	Randy Dunlap <rdunlap@infradead.org>,
	Roman Gushchin <roman.gushchin@linux.dev>,
	Shakeel Butt <shakeel.butt@linux.dev>,
	Shuah Khan <skhan@linuxfoundation.org>,
	Suren Baghdasaryan <surenb@google.com>,
	Usama Arif <usama.arif@linux.dev>,
	Vlastimil Babka <vbabka@kernel.org>,
	Wupeng Ma <mawupeng1@huawei.com>,
	Yanteng Si <si.yanteng@linux.dev>,
	fvdl@google.com, jthoughton@google.com, rientjes@google.com,
	vannapurve@google.com, linux-doc@vger.kernel.org,
	linux-kernel@vger.kernel.org, linux-mm@kvack.org,
	Ackerley Tng <ackerleytng@google.com>,
	stable@vger.kernel.org
Subject: Re: [PATCH v2 1/4] mm: hugetlb: Track used_hpages when getting/putting pages from subpool
Date: Fri, 11 Sep 2026 07:08:14 -0700	[thread overview]
Message-ID: <20260911140816.3236725-1-joshua.hahnjy@gmail.com> (raw)
In-Reply-To: <20260909-hugetlb-subpool-always-track-used-v2-1-30c5d83b572a@google.com>

On Wed, 09 Sep 2026 14:49:26 -0700 Ackerley Tng via B4 Relay <devnull+ackerleytng.google.com@kernel.org> wrote:

> From: Ackerley Tng <ackerleytng@google.com>
Hi Ackerley,

Thank you for working on this fix / simplification! 

> HugeTLB subpools currently only track used_hpages when the user
> configures a size limit.
> 
> This is buggy since when there are existing allocations from the
> subpool that would have satisfied the minimum reservations,
> hugepage_subpool_put_pages() will still restore a reservation to the
> subpool. See below for an example of a false reservation.

I'm not sure if I see the false reservation example, could I be
missing something here : -)

> In addition, the subpool is considered free prematurely, is freed, and
> this ends up causing a use-after-free.

That doesn't sound like a fun time!!

> The fix is to always track used_hpages within subpools, which is also
> beneficial in general because with that information, reservation
> tracking is also fully managed within hugepage_subpool_put_pages().

This is an increditly reasonable approach and I really like how we can
get rid of a lot of the if (spool->max_hpages) ... special casing.
Having different conditions for checking whether a subpool was / wasn't
free was also a bit strange to me as well...

> The subpool always knows how many pages were allocated through it. Every
> page allocated through the subpool increments used_hpages, regardless of
> whether a reservation was taken from it.
> 
> Conceptually, now, every allocation involving a subpool uses a page from
> the subpool, which must be returned to the subpool. Every page taken from
> the subpool tries to use a subpool reservation. Restoring a page to the
> subpool reservations only if the page was taken from subpool
> reservations. (If used_hpages >= min_hpages, the page must have not have
> been taken from the reservations.)
> 
> Always tracking used_hpages provides the subpool with information of both
> used and reserved counts to make the correct decision for both max_size and
> min_size correctly.
> 
> With used_hpages always tracked, subpool_is_free() can be simplified, such
> that the subpool can be declared free if there are no more pages in use.

Awesome!

> Also update the
> 
> + Documentation for used_hpages in the subpool struct, since it no longer
>   matters whether the used pages count against the maximum.
> + Docstring for hugepage_subpool_{get,put}_pages
> + Documentation to use active voice, and remove some details in favor of
>   having details documented in the docstring
> 
> Also update statfs reporting. Previously, if max_hpages is negative,
> used_hpages is static at 0, so returning max_hpages - used_hpages returns
> -1 and is always correct. Now, if the subpool doesn't have a maximum
> requested size, indicate no limit for free pages (-1). If it does have a
> maximum size, report the difference between the requested size and the
> number of used pages. This difference is always positive, because if the
> mount does have a maximum size, hugepage_subpool_get_pages() ensures that
> the subpool usage never exceeds the maximum.

> Fixes: 1c5ecae3a93fa ("hugetlbfs: add minimum size accounting to subpools")
> Signed-off-by: Ackerley Tng <ackerleytng@google.com>
> Cc: stable@vger.kernel.org

Feel free to add my:
Reviewed-by: Joshua Hahn <joshua.hahnjy@gmail.com>

> ---
>  Documentation/mm/hugetlbfs_reserv.rst              | 17 +----
>  .../translations/zh_CN/mm/hugetlbfs_reserv.rst     | 11 +---
>  fs/hugetlbfs/inode.c                               |  8 ++-
>  include/linux/hugetlb.h                            |  4 +-
>  mm/hugetlb.c                                       | 73 +++++++++++++---------
>  5 files changed, 55 insertions(+), 58 deletions(-)
> 
> diff --git a/Documentation/mm/hugetlbfs_reserv.rst b/Documentation/mm/hugetlbfs_reserv.rst
> index a49115db18c76..d244583fdcbc3 100644
> --- a/Documentation/mm/hugetlbfs_reserv.rst
> +++ b/Documentation/mm/hugetlbfs_reserv.rst
> @@ -314,21 +314,8 @@ huge pages.  If they can not be reserved, the mount fails.
>  The routines hugepage_subpool_get/put_pages() are called when pages are
>  obtained from or released back to a subpool.  They perform all subpool
>  accounting, and track any reservations associated with the subpool.
> -hugepage_subpool_get/put_pages are passed the number of huge pages by which
> -to adjust the subpool 'used page' count (down for get, up for put).  Normally,
> -they return the same value that was passed or an error if not enough pages
> -exist in the subpool.
> -
> -However, if reserves are associated with the subpool a return value less
> -than the passed value may be returned.  This return value indicates the
> -number of additional global pool adjustments which must be made.  For example,
> -suppose a subpool contains 3 reserved huge pages and someone asks for 5.
> -The 3 reserved pages associated with the subpool can be used to satisfy part
> -of the request.  But, 2 pages must be obtained from the global pools.  To
> -relay this information to the caller, the value 2 is returned.  The caller
> -is then responsible for attempting to obtain the additional two pages from
> -the global pools.
> -
> +hugepage_subpool_get/put_pages() use the number of huge pages passed to adjust
> +the subpool 'used page' count.
>  
>  COW and Reservations
>  ====================
> diff --git a/Documentation/translations/zh_CN/mm/hugetlbfs_reserv.rst b/Documentation/translations/zh_CN/mm/hugetlbfs_reserv.rst
> index 20947f8bd0654..ae1f1f31477fc 100644
> --- a/Documentation/translations/zh_CN/mm/hugetlbfs_reserv.rst
> +++ b/Documentation/translations/zh_CN/mm/hugetlbfs_reserv.rst
> @@ -246,15 +246,8 @@ hugepage_subpool的min_hpages字段中被跟踪。在挂载时,hugetlb_acct_me
>  被调用以预留指定数量的巨页。如果它们不能被预留,挂载就会失败。
>  
>  当从子池中获取或释放页面时,会调用hugepage_subpool_get/put_pages()函数。
> -hugepage_subpool_get/put_pages被传递给巨页数量,以此来调整子池的 “已用页面” 计数
> -(get为下降,put为上升)。通常情况下,如果子池中没有足够的页面,它们会返回与传递的相同的值或
> -一个错误。
> -
> -然而,如果预留与子池相关联,可能会返回一个小于传递值的返回值。这个返回值表示必须进行的额外全局
> -池调整的数量。例如,假设一个子池包含3个预留的巨页,有人要求5个。与子池相关的3个预留页可以用来
> -满足部分请求。但是,必须从全局池中获得2个页面。为了向调用者转达这一信息,将返回值2。然后,调用
> -者要负责从全局池中获取另外两个页面。
> -
> +它们负责所有子池的统计核算,并跟踪与子池相关联的预留。
> +hugepage_subpool_get/put_pages()函数使用传入的巨页数量来调整子池的“已用页面”计数。
>  
>  COW和预留
>  ==========
> diff --git a/fs/hugetlbfs/inode.c b/fs/hugetlbfs/inode.c
> index 7611a8470ea26..5113f743f6fc7 100644
> --- a/fs/hugetlbfs/inode.c
> +++ b/fs/hugetlbfs/inode.c
> @@ -1109,8 +1109,12 @@ static int hugetlbfs_statfs(struct dentry *dentry, struct kstatfs *buf)
>  
>  			spin_lock_irq(&sbinfo->spool->lock);
>  			buf->f_blocks = sbinfo->spool->max_hpages;
> -			free_pages = sbinfo->spool->max_hpages
> -				- sbinfo->spool->used_hpages;
> +			if (sbinfo->spool->max_hpages == -1) {
> +				free_pages = -1;
> +			} else {
> +				free_pages = sbinfo->spool->max_hpages -
> +					     sbinfo->spool->used_hpages;
> +			}
>  			buf->f_bavail = buf->f_bfree = free_pages;
>  			spin_unlock_irq(&sbinfo->spool->lock);
>  			buf->f_files = sbinfo->max_inodes;
> diff --git a/include/linux/hugetlb.h b/include/linux/hugetlb.h
> index 16c4c4caa126c..4551ff3023640 100644
> --- a/include/linux/hugetlb.h
> +++ b/include/linux/hugetlb.h
> @@ -39,8 +39,8 @@ struct hugepage_subpool {
>  	spinlock_t lock;
>  	long count;
>  	long max_hpages;	/* Maximum huge pages or -1 if no maximum. */
> -	long used_hpages;	/* Used count against maximum, includes */
> -				/* both allocated and reserved pages. */
> +	long used_hpages;	/* Used page count, includes both */
> +				/* allocated and reserved pages. */
>  	struct hstate *hstate;
>  	long min_hpages;	/* Minimum huge pages or -1 if no minimum. */
>  	long rsv_hpages;	/* Pages reserved against global pool to */
> diff --git a/mm/hugetlb.c b/mm/hugetlb.c
> index 4f6f58bf3db6c..e72e22f887478 100644
> --- a/mm/hugetlb.c
> +++ b/mm/hugetlb.c
> @@ -130,12 +130,8 @@ static inline bool subpool_is_free(struct hugepage_subpool *spool)
>  {
>  	if (spool->count)
>  		return false;
> -	if (spool->max_hpages != -1)
> -		return spool->used_hpages == 0;
> -	if (spool->min_hpages != -1)
> -		return spool->rsv_hpages == spool->min_hpages;
>  
> -	return true;
> +	return spool->used_hpages == 0;
>  }
>  
>  static inline void unlock_or_release_subpool(struct hugepage_subpool *spool,
> @@ -193,13 +189,18 @@ void hugepage_put_subpool(struct hugepage_subpool *spool)
>  	unlock_or_release_subpool(spool, flags);
>  }
>  
> -/*
> - * Subpool accounting for allocating and reserving pages.
> - * Return -ENOMEM if there are not enough resources to satisfy the
> - * request.  Otherwise, return the number of pages by which the
> - * global pools must be adjusted (upward).  The returned value may
> - * only be different than the passed value (delta) in the case where
> - * a subpool minimum size must be maintained.
> +/**
> + * hugepage_subpool_get_pages - Get pages from a subpool
> + * @spool: pointer to subpool structure (may be NULL)
> + * @delta: number of pages to allocate or reserve
> + *
> + * Check and update subpool page usage counts when allocating or
> + * reserving @delta hugepages.
> + *
> + * Context: Takes spool->lock using spin_lock_irq().
> + * Return: Non-negative number of reservations that cannot be
> + *         satisfied by the subpool, or -ENOMEM if the subpool maximum
> + *         limit would be exceeded.
>   */
>  static long hugepage_subpool_get_pages(struct hugepage_subpool *spool,
>  				      long delta)
> @@ -211,15 +212,14 @@ static long hugepage_subpool_get_pages(struct hugepage_subpool *spool,
>  
>  	spin_lock_irq(&spool->lock);
>  
> -	if (spool->max_hpages != -1) {		/* maximum size accounting */
> -		if ((spool->used_hpages + delta) <= spool->max_hpages)
> -			spool->used_hpages += delta;
> -		else {
> -			ret = -ENOMEM;
> -			goto unlock_ret;
> -		}
> +	if (spool->max_hpages != -1 &&
> +	    spool->used_hpages + delta > spool->max_hpages) {
> +		ret = -ENOMEM;
> +		goto unlock_ret;
>  	}
>  
> +	spool->used_hpages += delta;
> +
>  	/* minimum size accounting */
>  	if (spool->min_hpages != -1 && spool->rsv_hpages) {
>  		if (delta > spool->rsv_hpages) {
> @@ -240,11 +240,19 @@ static long hugepage_subpool_get_pages(struct hugepage_subpool *spool,
>  	return ret;
>  }
>  
> -/*
> - * Subpool accounting for freeing and unreserving pages.
> - * Return the number of global page reservations that must be dropped.
> - * The return value may only be different than the passed value (delta)
> - * in the case where a subpool minimum size must be maintained.
> +/**
> + * hugepage_subpool_put_pages - Release pages back to a subpool
> + * @spool: pointer to subpool structure (may be NULL)
> + * @delta: number of pages to free or unreserve
> + *
> + * Check and update subpool page usage counts when freeing or
> + * unreserving @delta hugepages.
> + *
> + * Context: Takes spool->lock using spin_lock_irqsave(). May release
> + *          and free @spool if its usage count and references reach
> + *          zero.
> + * Return: Non-negative number of reservations that the subpool cannot
> + *         absorb.
>   */
>  static long hugepage_subpool_put_pages(struct hugepage_subpool *spool,
>  				       long delta)
> @@ -257,19 +265,24 @@ static long hugepage_subpool_put_pages(struct hugepage_subpool *spool,
>  
>  	spin_lock_irqsave(&spool->lock, flags);
>  
> -	if (spool->max_hpages != -1)		/* maximum size accounting */
> -		spool->used_hpages -= delta;
> +	spool->used_hpages -= delta;
>  
>  	 /* minimum size accounting */
>  	if (spool->min_hpages != -1 && spool->used_hpages < spool->min_hpages) {
> -		if (spool->rsv_hpages + delta <= spool->min_hpages)
> 
> +		/*
> +		 * limit is the maximum number of reservations that
> +		 * can be restored to this subpool.
> +		 */
> +		long limit = spool->min_hpages - spool->used_hpages;
> +
> +		if (spool->rsv_hpages + delta <= limit)
>  			ret = 0;
>  		else
> -			ret = spool->rsv_hpages + delta - spool->min_hpages;
> +			ret = spool->rsv_hpages + delta - limit;
>  
>  		spool->rsv_hpages += delta;
> -		if (spool->rsv_hpages > spool->min_hpages)
> -			spool->rsv_hpages = spool->min_hpages;
> +		if (spool->rsv_hpages > limit)
> +			spool->rsv_hpages = limit;
>  	}
>  
>  	/*
> 
> -- 
> 2.55.0.1007.g17ff1f9808-goog

  reply	other threads:[~2026-09-11 14:08 UTC|newest]

Thread overview: 18+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-09-09 21:49 [PATCH v2 0/4] Fix HugeTLB subpool used_hpages tracking Ackerley Tng via B4 Relay
2026-09-09 21:49 ` [PATCH v2 1/4] mm: hugetlb: Track used_hpages when getting/putting pages from subpool Ackerley Tng via B4 Relay
2026-09-11 14:08   ` Joshua Hahn [this message]
2026-09-14 16:12     ` Ackerley Tng
2026-09-09 21:49 ` [PATCH v2 2/4] mm: hugetlb: Fix out_put_pages subpool reserve calculation Ackerley Tng via B4 Relay
2026-09-11 14:37   ` Joshua Hahn
2026-09-14 16:00     ` Ackerley Tng
2026-09-09 21:49 ` [PATCH v2 3/4] mm: hugetlb: Fix subpool usage leak on allocation failure Ackerley Tng via B4 Relay
2026-09-09 22:54   ` Andrew Morton
2026-09-10 19:57     ` Joshua Hahn
2026-09-14 15:26       ` Ackerley Tng
2026-09-11 14:45   ` Joshua Hahn
2026-09-09 21:49 ` [PATCH v2 4/4] mm: hugetlb: Avoid re-allocating global reservations on region add failure Ackerley Tng via B4 Relay
2026-09-11 14:49   ` Joshua Hahn
2026-09-14 16:03     ` Ackerley Tng
2026-09-09 22:50 ` [PATCH v2 0/4] Fix HugeTLB subpool used_hpages tracking Andrew Morton
2026-09-14 15:56   ` Ackerley Tng
2026-09-14 16:30     ` Ackerley Tng

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=20260911140816.3236725-1-joshua.hahnjy@gmail.com \
    --to=joshua.hahnjy@gmail.com \
    --cc=ackerleytng@google.com \
    --cc=akpm@linux-foundation.org \
    --cc=corbet@lwn.net \
    --cc=david@kernel.org \
    --cc=devnull+ackerleytng.google.com@kernel.org \
    --cc=dzm91@hust.edu.cn \
    --cc=fvdl@google.com \
    --cc=hannes@cmpxchg.org \
    --cc=jthoughton@google.com \
    --cc=liam@infradead.org \
    --cc=linmiaohe@huawei.com \
    --cc=linux-doc@vger.kernel.org \
    --cc=linux-kernel@vger.kernel.org \
    --cc=linux-mm@kvack.org \
    --cc=ljs@kernel.org \
    --cc=louhongxiang@huawei.com \
    --cc=mawupeng1@huawei.com \
    --cc=mhocko@kernel.org \
    --cc=muchun.song@linux.dev \
    --cc=nphamcs@gmail.com \
    --cc=osalvador@suse.de \
    --cc=peterx@redhat.com \
    --cc=rdunlap@infradead.org \
    --cc=rientjes@google.com \
    --cc=roman.gushchin@linux.dev \
    --cc=rppt@kernel.org \
    --cc=shakeel.butt@linux.dev \
    --cc=si.yanteng@linux.dev \
    --cc=skhan@linuxfoundation.org \
    --cc=stable@vger.kernel.org \
    --cc=surenb@google.com \
    --cc=usama.arif@linux.dev \
    --cc=vannapurve@google.com \
    --cc=vbabka@kernel.org \
    /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 an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.