Skip to content

Commit 1c5ecae

Browse files
mjkravetztorvalds
authored andcommitted
hugetlbfs: add minimum size accounting to subpools
The same routines that perform subpool maximum size accounting hugepage_subpool_get/put_pages() are modified to also perform minimum size accounting. When a delta value is passed to these routines, calculate how global reservations must be adjusted to maintain the subpool minimum size. The routines now return this global reserve count adjustment. This global reserve count adjustment is then passed to the global accounting routine hugetlb_acct_memory(). Signed-off-by: Mike Kravetz <mike.kravetz@oracle.com> Cc: Davidlohr Bueso <dave@stgolabs.net> Cc: Aneesh Kumar <aneesh.kumar@linux.vnet.ibm.com> Cc: Joonsoo Kim <iamjoonsoo.kim@lge.com> Cc: Andi Kleen <andi@firstfloor.org> Cc: David Rientjes <rientjes@google.com> Signed-off-by: Andrew Morton <akpm@linux-foundation.org> Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
1 parent c6a9182 commit 1c5ecae

File tree

1 file changed

+100
-23
lines changed

1 file changed

+100
-23
lines changed

mm/hugetlb.c

Lines changed: 100 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -96,36 +96,89 @@ void hugepage_put_subpool(struct hugepage_subpool *spool)
9696
unlock_or_release_subpool(spool);
9797
}
9898

99-
static int hugepage_subpool_get_pages(struct hugepage_subpool *spool,
99+
/*
100+
* Subpool accounting for allocating and reserving pages.
101+
* Return -ENOMEM if there are not enough resources to satisfy the
102+
* the request. Otherwise, return the number of pages by which the
103+
* global pools must be adjusted (upward). The returned value may
104+
* only be different than the passed value (delta) in the case where
105+
* a subpool minimum size must be manitained.
106+
*/
107+
static long hugepage_subpool_get_pages(struct hugepage_subpool *spool,
100108
long delta)
101109
{
102-
int ret = 0;
110+
long ret = delta;
103111

104112
if (!spool)
105-
return 0;
113+
return ret;
106114

107115
spin_lock(&spool->lock);
108-
if ((spool->used_hpages + delta) <= spool->max_hpages) {
109-
spool->used_hpages += delta;
110-
} else {
111-
ret = -ENOMEM;
116+
117+
if (spool->max_hpages != -1) { /* maximum size accounting */
118+
if ((spool->used_hpages + delta) <= spool->max_hpages)
119+
spool->used_hpages += delta;
120+
else {
121+
ret = -ENOMEM;
122+
goto unlock_ret;
123+
}
112124
}
113-
spin_unlock(&spool->lock);
114125

126+
if (spool->min_hpages != -1) { /* minimum size accounting */
127+
if (delta > spool->rsv_hpages) {
128+
/*
129+
* Asking for more reserves than those already taken on
130+
* behalf of subpool. Return difference.
131+
*/
132+
ret = delta - spool->rsv_hpages;
133+
spool->rsv_hpages = 0;
134+
} else {
135+
ret = 0; /* reserves already accounted for */
136+
spool->rsv_hpages -= delta;
137+
}
138+
}
139+
140+
unlock_ret:
141+
spin_unlock(&spool->lock);
115142
return ret;
116143
}
117144

118-
static void hugepage_subpool_put_pages(struct hugepage_subpool *spool,
145+
/*
146+
* Subpool accounting for freeing and unreserving pages.
147+
* Return the number of global page reservations that must be dropped.
148+
* The return value may only be different than the passed value (delta)
149+
* in the case where a subpool minimum size must be maintained.
150+
*/
151+
static long hugepage_subpool_put_pages(struct hugepage_subpool *spool,
119152
long delta)
120153
{
154+
long ret = delta;
155+
121156
if (!spool)
122-
return;
157+
return delta;
123158

124159
spin_lock(&spool->lock);
125-
spool->used_hpages -= delta;
126-
/* If hugetlbfs_put_super couldn't free spool due to
127-
* an outstanding quota reference, free it now. */
160+
161+
if (spool->max_hpages != -1) /* maximum size accounting */
162+
spool->used_hpages -= delta;
163+
164+
if (spool->min_hpages != -1) { /* minimum size accounting */
165+
if (spool->rsv_hpages + delta <= spool->min_hpages)
166+
ret = 0;
167+
else
168+
ret = spool->rsv_hpages + delta - spool->min_hpages;
169+
170+
spool->rsv_hpages += delta;
171+
if (spool->rsv_hpages > spool->min_hpages)
172+
spool->rsv_hpages = spool->min_hpages;
173+
}
174+
175+
/*
176+
* If hugetlbfs_put_super couldn't free spool due to an outstanding
177+
* quota reference, free it now.
178+
*/
128179
unlock_or_release_subpool(spool);
180+
181+
return ret;
129182
}
130183

131184
static inline struct hugepage_subpool *subpool_inode(struct inode *inode)
@@ -873,6 +926,14 @@ void free_huge_page(struct page *page)
873926
restore_reserve = PagePrivate(page);
874927
ClearPagePrivate(page);
875928

929+
/*
930+
* A return code of zero implies that the subpool will be under its
931+
* minimum size if the reservation is not restored after page is free.
932+
* Therefore, force restore_reserve operation.
933+
*/
934+
if (hugepage_subpool_put_pages(spool, 1) == 0)
935+
restore_reserve = true;
936+
876937
spin_lock(&hugetlb_lock);
877938
hugetlb_cgroup_uncharge_page(hstate_index(h),
878939
pages_per_huge_page(h), page);
@@ -890,7 +951,6 @@ void free_huge_page(struct page *page)
890951
enqueue_huge_page(h, page);
891952
}
892953
spin_unlock(&hugetlb_lock);
893-
hugepage_subpool_put_pages(spool, 1);
894954
}
895955

896956
static void prep_new_huge_page(struct hstate *h, struct page *page, int nid)
@@ -1385,7 +1445,7 @@ static struct page *alloc_huge_page(struct vm_area_struct *vma,
13851445
if (chg < 0)
13861446
return ERR_PTR(-ENOMEM);
13871447
if (chg || avoid_reserve)
1388-
if (hugepage_subpool_get_pages(spool, 1))
1448+
if (hugepage_subpool_get_pages(spool, 1) < 0)
13891449
return ERR_PTR(-ENOSPC);
13901450

13911451
ret = hugetlb_cgroup_charge_cgroup(idx, pages_per_huge_page(h), &h_cg);
@@ -2453,6 +2513,7 @@ static void hugetlb_vm_op_close(struct vm_area_struct *vma)
24532513
struct resv_map *resv = vma_resv_map(vma);
24542514
struct hugepage_subpool *spool = subpool_vma(vma);
24552515
unsigned long reserve, start, end;
2516+
long gbl_reserve;
24562517

24572518
if (!resv || !is_vma_resv_set(vma, HPAGE_RESV_OWNER))
24582519
return;
@@ -2465,8 +2526,12 @@ static void hugetlb_vm_op_close(struct vm_area_struct *vma)
24652526
kref_put(&resv->refs, resv_map_release);
24662527

24672528
if (reserve) {
2468-
hugetlb_acct_memory(h, -reserve);
2469-
hugepage_subpool_put_pages(spool, reserve);
2529+
/*
2530+
* Decrement reserve counts. The global reserve count may be
2531+
* adjusted if the subpool has a minimum size.
2532+
*/
2533+
gbl_reserve = hugepage_subpool_put_pages(spool, reserve);
2534+
hugetlb_acct_memory(h, -gbl_reserve);
24702535
}
24712536
}
24722537

@@ -3446,6 +3511,7 @@ int hugetlb_reserve_pages(struct inode *inode,
34463511
struct hstate *h = hstate_inode(inode);
34473512
struct hugepage_subpool *spool = subpool_inode(inode);
34483513
struct resv_map *resv_map;
3514+
long gbl_reserve;
34493515

34503516
/*
34513517
* Only apply hugepage reservation if asked. At fault time, an
@@ -3482,8 +3548,13 @@ int hugetlb_reserve_pages(struct inode *inode,
34823548
goto out_err;
34833549
}
34843550

3485-
/* There must be enough pages in the subpool for the mapping */
3486-
if (hugepage_subpool_get_pages(spool, chg)) {
3551+
/*
3552+
* There must be enough pages in the subpool for the mapping. If
3553+
* the subpool has a minimum size, there may be some global
3554+
* reservations already in place (gbl_reserve).
3555+
*/
3556+
gbl_reserve = hugepage_subpool_get_pages(spool, chg);
3557+
if (gbl_reserve < 0) {
34873558
ret = -ENOSPC;
34883559
goto out_err;
34893560
}
@@ -3492,9 +3563,10 @@ int hugetlb_reserve_pages(struct inode *inode,
34923563
* Check enough hugepages are available for the reservation.
34933564
* Hand the pages back to the subpool if there are not
34943565
*/
3495-
ret = hugetlb_acct_memory(h, chg);
3566+
ret = hugetlb_acct_memory(h, gbl_reserve);
34963567
if (ret < 0) {
3497-
hugepage_subpool_put_pages(spool, chg);
3568+
/* put back original number of pages, chg */
3569+
(void)hugepage_subpool_put_pages(spool, chg);
34983570
goto out_err;
34993571
}
35003572

@@ -3524,15 +3596,20 @@ void hugetlb_unreserve_pages(struct inode *inode, long offset, long freed)
35243596
struct resv_map *resv_map = inode_resv_map(inode);
35253597
long chg = 0;
35263598
struct hugepage_subpool *spool = subpool_inode(inode);
3599+
long gbl_reserve;
35273600

35283601
if (resv_map)
35293602
chg = region_truncate(resv_map, offset);
35303603
spin_lock(&inode->i_lock);
35313604
inode->i_blocks -= (blocks_per_huge_page(h) * freed);
35323605
spin_unlock(&inode->i_lock);
35333606

3534-
hugepage_subpool_put_pages(spool, (chg - freed));
3535-
hugetlb_acct_memory(h, -(chg - freed));
3607+
/*
3608+
* If the subpool has a minimum size, the number of global
3609+
* reservations to be released may be adjusted.
3610+
*/
3611+
gbl_reserve = hugepage_subpool_put_pages(spool, (chg - freed));
3612+
hugetlb_acct_memory(h, -gbl_reserve);
35363613
}
35373614

35383615
#ifdef CONFIG_ARCH_WANT_HUGE_PMD_SHARE

0 commit comments

Comments
 (0)