Skip to content

Commit e9fe92a

Browse files
minatorvalds
authored andcommitted
hugetlb_cgroup: add reservation accounting for private mappings
Normally the pointer to the cgroup to uncharge hangs off the struct page, and gets queried when it's time to free the page. With hugetlb_cgroup reservations, this is not possible. Because it's possible for a page to be reserved by one task and actually faulted in by another task. The best place to put the hugetlb_cgroup pointer to uncharge for reservations is in the resv_map. But, because the resv_map has different semantics for private and shared mappings, the code patch to charge/uncharge shared and private mappings is different. This patch implements charging and uncharging for private mappings. For private mappings, the counter to uncharge is in resv_map->reservation_counter. On initializing the resv_map this is set to NULL. On reservation of a region in private mapping, the tasks hugetlb_cgroup is charged and the hugetlb_cgroup is placed is resv_map->reservation_counter. On hugetlb_vm_op_close, we uncharge resv_map->reservation_counter. [[email protected]: forward declare struct resv_map] Signed-off-by: Mina Almasry <[email protected]> Signed-off-by: Andrew Morton <[email protected]> Reviewed-by: Mike Kravetz <[email protected]> Acked-by: David Rientjes <[email protected]> Cc: Greg Thelen <[email protected]> Cc: Sandipan Das <[email protected]> Cc: Shakeel Butt <[email protected]> Cc: Shuah Khan <[email protected]> Link: http://lkml.kernel.org/r/[email protected] Signed-off-by: Linus Torvalds <[email protected]>
1 parent 9808895 commit e9fe92a

File tree

4 files changed

+99
-40
lines changed

4 files changed

+99
-40
lines changed

include/linux/hugetlb.h

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,16 @@ struct resv_map {
4646
long adds_in_progress;
4747
struct list_head region_cache;
4848
long region_cache_count;
49+
#ifdef CONFIG_CGROUP_HUGETLB
50+
/*
51+
* On private mappings, the counter to uncharge reservations is stored
52+
* here. If these fields are 0, then either the mapping is shared, or
53+
* cgroup accounting is disabled for this resv_map.
54+
*/
55+
struct page_counter *reservation_counter;
56+
unsigned long pages_per_hpage;
57+
struct cgroup_subsys_state *css;
58+
#endif
4959
};
5060
extern struct resv_map *resv_map_alloc(void);
5161
void resv_map_release(struct kref *ref);

include/linux/hugetlb_cgroup.h

Lines changed: 38 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,8 @@
1818
#include <linux/mmdebug.h>
1919

2020
struct hugetlb_cgroup;
21+
struct resv_map;
22+
2123
/*
2224
* Minimum page order trackable by hugetlb cgroup.
2325
* At least 4 pages are necessary for all the tracking information.
@@ -27,6 +29,33 @@ struct hugetlb_cgroup;
2729
#define HUGETLB_CGROUP_MIN_ORDER 2
2830

2931
#ifdef CONFIG_CGROUP_HUGETLB
32+
enum hugetlb_memory_event {
33+
HUGETLB_MAX,
34+
HUGETLB_NR_MEMORY_EVENTS,
35+
};
36+
37+
struct hugetlb_cgroup {
38+
struct cgroup_subsys_state css;
39+
40+
/*
41+
* the counter to account for hugepages from hugetlb.
42+
*/
43+
struct page_counter hugepage[HUGE_MAX_HSTATE];
44+
45+
/*
46+
* the counter to account for hugepage reservations from hugetlb.
47+
*/
48+
struct page_counter rsvd_hugepage[HUGE_MAX_HSTATE];
49+
50+
atomic_long_t events[HUGE_MAX_HSTATE][HUGETLB_NR_MEMORY_EVENTS];
51+
atomic_long_t events_local[HUGE_MAX_HSTATE][HUGETLB_NR_MEMORY_EVENTS];
52+
53+
/* Handle for "hugetlb.events" */
54+
struct cgroup_file events_file[HUGE_MAX_HSTATE];
55+
56+
/* Handle for "hugetlb.events.local" */
57+
struct cgroup_file events_local_file[HUGE_MAX_HSTATE];
58+
};
3059

3160
static inline struct hugetlb_cgroup *
3261
__hugetlb_cgroup_from_page(struct page *page, bool rsvd)
@@ -102,9 +131,9 @@ extern void hugetlb_cgroup_uncharge_cgroup(int idx, unsigned long nr_pages,
102131
struct hugetlb_cgroup *h_cg);
103132
extern void hugetlb_cgroup_uncharge_cgroup_rsvd(int idx, unsigned long nr_pages,
104133
struct hugetlb_cgroup *h_cg);
105-
extern void hugetlb_cgroup_uncharge_counter(struct page_counter *p,
106-
unsigned long nr_pages,
107-
struct cgroup_subsys_state *css);
134+
extern void hugetlb_cgroup_uncharge_counter(struct resv_map *resv,
135+
unsigned long start,
136+
unsigned long end);
108137

109138
extern void hugetlb_cgroup_file_init(void) __init;
110139
extern void hugetlb_cgroup_migrate(struct page *oldhpage,
@@ -193,6 +222,12 @@ hugetlb_cgroup_uncharge_cgroup_rsvd(int idx, unsigned long nr_pages,
193222
{
194223
}
195224

225+
static inline void hugetlb_cgroup_uncharge_counter(struct resv_map *resv,
226+
unsigned long start,
227+
unsigned long end)
228+
{
229+
}
230+
196231
static inline void hugetlb_cgroup_file_init(void)
197232
{
198233
}

mm/hugetlb.c

Lines changed: 44 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -650,6 +650,25 @@ static void set_vma_private_data(struct vm_area_struct *vma,
650650
vma->vm_private_data = (void *)value;
651651
}
652652

653+
static void
654+
resv_map_set_hugetlb_cgroup_uncharge_info(struct resv_map *resv_map,
655+
struct hugetlb_cgroup *h_cg,
656+
struct hstate *h)
657+
{
658+
#ifdef CONFIG_CGROUP_HUGETLB
659+
if (!h_cg || !h) {
660+
resv_map->reservation_counter = NULL;
661+
resv_map->pages_per_hpage = 0;
662+
resv_map->css = NULL;
663+
} else {
664+
resv_map->reservation_counter =
665+
&h_cg->rsvd_hugepage[hstate_index(h)];
666+
resv_map->pages_per_hpage = pages_per_huge_page(h);
667+
resv_map->css = &h_cg->css;
668+
}
669+
#endif
670+
}
671+
653672
struct resv_map *resv_map_alloc(void)
654673
{
655674
struct resv_map *resv_map = kmalloc(sizeof(*resv_map), GFP_KERNEL);
@@ -666,6 +685,13 @@ struct resv_map *resv_map_alloc(void)
666685
INIT_LIST_HEAD(&resv_map->regions);
667686

668687
resv_map->adds_in_progress = 0;
688+
/*
689+
* Initialize these to 0. On shared mappings, 0's here indicate these
690+
* fields don't do cgroup accounting. On private mappings, these will be
691+
* re-initialized to the proper values, to indicate that hugetlb cgroup
692+
* reservations are to be un-charged from here.
693+
*/
694+
resv_map_set_hugetlb_cgroup_uncharge_info(resv_map, NULL, NULL);
669695

670696
INIT_LIST_HEAD(&resv_map->region_cache);
671697
list_add(&rg->link, &resv_map->region_cache);
@@ -3296,9 +3322,7 @@ static void hugetlb_vm_op_close(struct vm_area_struct *vma)
32963322
end = vma_hugecache_offset(h, vma, vma->vm_end);
32973323

32983324
reserve = (end - start) - region_count(resv, start, end);
3299-
3300-
kref_put(&resv->refs, resv_map_release);
3301-
3325+
hugetlb_cgroup_uncharge_counter(resv, start, end);
33023326
if (reserve) {
33033327
/*
33043328
* Decrement reserve counts. The global reserve count may be
@@ -3307,6 +3331,8 @@ static void hugetlb_vm_op_close(struct vm_area_struct *vma)
33073331
gbl_reserve = hugepage_subpool_put_pages(spool, reserve);
33083332
hugetlb_acct_memory(h, -gbl_reserve);
33093333
}
3334+
3335+
kref_put(&resv->refs, resv_map_release);
33103336
}
33113337

33123338
static int hugetlb_vm_op_split(struct vm_area_struct *vma, unsigned long addr)
@@ -4691,6 +4717,7 @@ int hugetlb_reserve_pages(struct inode *inode,
46914717
struct hstate *h = hstate_inode(inode);
46924718
struct hugepage_subpool *spool = subpool_inode(inode);
46934719
struct resv_map *resv_map;
4720+
struct hugetlb_cgroup *h_cg;
46944721
long gbl_reserve;
46954722

46964723
/* This should never happen */
@@ -4724,12 +4751,26 @@ int hugetlb_reserve_pages(struct inode *inode,
47244751
chg = region_chg(resv_map, from, to);
47254752

47264753
} else {
4754+
/* Private mapping. */
47274755
resv_map = resv_map_alloc();
47284756
if (!resv_map)
47294757
return -ENOMEM;
47304758

47314759
chg = to - from;
47324760

4761+
if (hugetlb_cgroup_charge_cgroup_rsvd(
4762+
hstate_index(h), chg * pages_per_huge_page(h),
4763+
&h_cg)) {
4764+
kref_put(&resv_map->refs, resv_map_release);
4765+
return -ENOMEM;
4766+
}
4767+
4768+
/*
4769+
* Since this branch handles private mappings, we attach the
4770+
* counter to uncharge for this reservation off resv_map.
4771+
*/
4772+
resv_map_set_hugetlb_cgroup_uncharge_info(resv_map, h_cg, h);
4773+
47334774
set_vma_resv_map(vma, resv_map);
47344775
set_vma_resv_flags(vma, HPAGE_RESV_OWNER);
47354776
}

mm/hugetlb_cgroup.c

Lines changed: 7 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -23,34 +23,6 @@
2323
#include <linux/hugetlb.h>
2424
#include <linux/hugetlb_cgroup.h>
2525

26-
enum hugetlb_memory_event {
27-
HUGETLB_MAX,
28-
HUGETLB_NR_MEMORY_EVENTS,
29-
};
30-
31-
struct hugetlb_cgroup {
32-
struct cgroup_subsys_state css;
33-
34-
/*
35-
* the counter to account for hugepages from hugetlb.
36-
*/
37-
struct page_counter hugepage[HUGE_MAX_HSTATE];
38-
39-
/*
40-
* the counter to account for hugepage reservations from hugetlb.
41-
*/
42-
struct page_counter rsvd_hugepage[HUGE_MAX_HSTATE];
43-
44-
atomic_long_t events[HUGE_MAX_HSTATE][HUGETLB_NR_MEMORY_EVENTS];
45-
atomic_long_t events_local[HUGE_MAX_HSTATE][HUGETLB_NR_MEMORY_EVENTS];
46-
47-
/* Handle for "hugetlb.events" */
48-
struct cgroup_file events_file[HUGE_MAX_HSTATE];
49-
50-
/* Handle for "hugetlb.events.local" */
51-
struct cgroup_file events_local_file[HUGE_MAX_HSTATE];
52-
};
53-
5426
#define MEMFILE_PRIVATE(x, val) (((x) << 16) | (val))
5527
#define MEMFILE_IDX(val) (((val) >> 16) & 0xffff)
5628
#define MEMFILE_ATTR(val) ((val) & 0xffff)
@@ -407,15 +379,16 @@ void hugetlb_cgroup_uncharge_cgroup_rsvd(int idx, unsigned long nr_pages,
407379
__hugetlb_cgroup_uncharge_cgroup(idx, nr_pages, h_cg, true);
408380
}
409381

410-
void hugetlb_cgroup_uncharge_counter(struct page_counter *p,
411-
unsigned long nr_pages,
412-
struct cgroup_subsys_state *css)
382+
void hugetlb_cgroup_uncharge_counter(struct resv_map *resv, unsigned long start,
383+
unsigned long end)
413384
{
414-
if (hugetlb_cgroup_disabled() || !p || !css)
385+
if (hugetlb_cgroup_disabled() || !resv || !resv->reservation_counter ||
386+
!resv->css)
415387
return;
416388

417-
page_counter_uncharge(p, nr_pages);
418-
css_put(css);
389+
page_counter_uncharge(resv->reservation_counter,
390+
(end - start) * resv->pages_per_hpage);
391+
css_put(resv->css);
419392
}
420393

421394
enum {

0 commit comments

Comments
 (0)