Skip to contentSkip to conversation

memcg: hugetlbfs basic usage accounting

PATCH In review Roman Gushchin 4 participants 8 messages Nov 14, 2017 – Nov 15, 2017

Subsystems (4)
lore
Share & tools
lore
#0 lore raw

PATCH 1/1 memcg: hugetlbfs basic usage accounting

This patch implements basic accounting of memory consumption by hugetlbfs pages for cgroup v2 memory controller.

Cgroup v2 memory controller lacks any visibility into the hugetlbfs memory consumption. Cgroup v1 implemented a separate hugetlbfs controller, which provided such stats, and also provided some control abilities. Although porting of the hugetlbfs controller to cgroup v2 is arguable a good idea and is outside of scope of this patch, it's very useful to have basic stats provided by memory.stat.

As hugetlbfs memory can easily represent a big portion of total memory, it's important to understand who (which memcg/container) is using it.

The number is represented in memory.stat as "hugetlb" in bytes and is printed unconditionally. Accounting code doesn't depend on cgroup v1 hugetlb controller.

Example:

  $ cat /sys/fs/cgroup/user.slice/user-0.slice/session-1.scope/memory.stat
  anon 1634304
  file 1163264
  kernel_stack 16384
  slab 737280
  sock 0
  shmem 0
  file_mapped 32768
  file_dirty 4096
  file_writeback 0
  inactive_anon 0
  active_anon 1634304
  inactive_file 65536
  active_file 1097728
  unevictable 0
  slab_reclaimable 282624
  slab_unreclaimable 454656
  hugetlb 1073741824
  pgfault 4580
  pgmajfault 13
  ...
Cc Johannes Weiner <[email protected]>
Cc Michal Hocko <[email protected]>
Cc Vladimir Davydov <[email protected]>
Cc Andrew Morton <[email protected]>
Cc Tejun Heo <[email protected]>
Cc Mike Kravetz <[email protected]>
Cc Dave Hansen <[email protected]>
---
 include/linux/memcontrol.h | 48 ++++++++++++++++++++++++++++++++++++++++++++++
 mm/hugetlb.c               |  5 +++++
 mm/memcontrol.c            |  3 +++
 3 files changed, 56 insertions(+)
include/linux/memcontrol.h +48 -0
index 69966c461d1c..e0dfb64d2918 100644
@@ -45,6 +45,7 @@ enum memcg_stat_item {
 	MEMCG_SOCK,
 	/* XXX: why are these zone and not node counters? */
 	MEMCG_KERNEL_STACK_KB,
+	MEMCG_HUGETLB,
 	MEMCG_NR_STAT,
 };
 
@@ -664,6 +665,39 @@ static inline void count_memcg_event_mm(struct mm_struct *mm,
 void mem_cgroup_split_huge_fixup(struct page *head);
 #endif
 
+#ifdef CONFIG_HUGETLBFS
+static inline void mem_cgroup_add_hugetlb_page(struct page *page,
+					       unsigned int count)
+{
+	if (mem_cgroup_disabled())
+		return;
+
+	rcu_read_lock();
+	page->mem_cgroup = mem_cgroup_from_task(current);
+	css_get(&page->mem_cgroup->css);
+	rcu_read_unlock();
+
+	mod_memcg_page_state(page, MEMCG_HUGETLB, count);
+}
+
+static inline void mem_cgroup_remove_hugetlb_page(struct page *page,
+						  unsigned int count)
+{
+	if (mem_cgroup_disabled() || !page->mem_cgroup)
+		return;
+
+	mod_memcg_page_state(page, MEMCG_HUGETLB, -count);
+
+	css_put(&page->mem_cgroup->css);
+	page->mem_cgroup = NULL;
+}
+
+static inline void mem_cgroup_reset_hugetlb_page(struct page *page)
+{
+	page->mem_cgroup = NULL;
+}
+#endif
+
 #else /* CONFIG_MEMCG */
 
 #define MEM_CGROUP_ID_SHIFT	0
@@ -936,6 +970,20 @@ static inline
 void count_memcg_event_mm(struct mm_struct *mm, enum vm_event_item idx)
 {
 }
+
+static inline void mem_cgroup_add_hugetlb_page(struct page *page,
+					       unsigned int count)
+{
+}
+
+static inline void mem_cgroup_remove_hugetlb_page(struct page *page,
+						  unsigned int count)
+{
+}
+
+static inline void mem_cgroup_reset_hugetlb_page(struct page *page)
+{
+}
 #endif /* CONFIG_MEMCG */
 
 /* idx can be of type enum memcg_stat_item or node_stat_item */
mm/hugetlb.c +5 -0
index 4b3bbd2980bb..d1a2a9fa549a 100644
@@ -1279,6 +1279,8 @@ void free_huge_page(struct page *page)
 	clear_page_huge_active(page);
 	hugetlb_cgroup_uncharge_page(hstate_index(h),
 				     pages_per_huge_page(h), page);
+	mem_cgroup_remove_hugetlb_page(page, pages_per_huge_page(h));
+
 	if (restore_reserve)
 		h->resv_huge_pages++;
 
@@ -1301,6 +1303,7 @@ static void prep_new_huge_page(struct hstate *h, struct page *page, int nid)
 	set_compound_page_dtor(page, HUGETLB_PAGE_DTOR);
 	spin_lock(&hugetlb_lock);
 	set_hugetlb_cgroup(page, NULL);
+	mem_cgroup_reset_hugetlb_page(page);
 	h->nr_huge_pages++;
 	h->nr_huge_pages_node[nid]++;
 	spin_unlock(&hugetlb_lock);
@@ -1584,6 +1587,7 @@ static struct page *__alloc_buddy_huge_page(struct hstate *h, gfp_t gfp_mask,
 		r_nid = page_to_nid(page);
 		set_compound_page_dtor(page, HUGETLB_PAGE_DTOR);
 		set_hugetlb_cgroup(page, NULL);
+		mem_cgroup_reset_hugetlb_page(page);
 		/*
 		 * We incremented the global counters already
 		 */
@@ -2041,6 +2045,7 @@ struct page *alloc_huge_page(struct vm_area_struct *vma,
 		/* Fall through */
 	}
 	hugetlb_cgroup_commit_charge(idx, pages_per_huge_page(h), h_cg, page);
+	mem_cgroup_add_hugetlb_page(page, pages_per_huge_page(h));
 	spin_unlock(&hugetlb_lock);
 
 	set_page_private(page, (unsigned long)spool);
mm/memcontrol.c +3 -0
index 50e6906314f8..f2323a9405a4 100644
@@ -5338,6 +5338,9 @@ static int memory_stat_show(struct seq_file *m, void *v)
 	seq_printf(m, "slab_unreclaimable %llu\n",
 		   (u64)stat[NR_SLAB_UNRECLAIMABLE] * PAGE_SIZE);
 
+	seq_printf(m, "hugetlb %llu\n",
+		   (u64)stat[MEMCG_HUGETLB] * PAGE_SIZE);
+
 	/* Accumulated memory events */
 
 	seq_printf(m, "pgfault %lu\n", events[PGFAULT]);
Signature · 7 lines
2.13.6

--
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>
maintainer Acked-by on v1 1/1
#1 lore raw
Quoted 42 lines · Roman Gushchin

This patch implements basic accounting of memory consumption by hugetlbfs pages for cgroup v2 memory controller.

Cgroup v2 memory controller lacks any visibility into the hugetlbfs memory consumption. Cgroup v1 implemented a separate hugetlbfs controller, which provided such stats, and also provided some control abilities. Although porting of the hugetlbfs controller to cgroup v2 is arguable a good idea and is outside of scope of this patch, it's very useful to have basic stats provided by memory.stat.

As hugetlbfs memory can easily represent a big portion of total memory, it's important to understand who (which memcg/container) is using it.

The number is represented in memory.stat as "hugetlb" in bytes and is printed unconditionally. Accounting code doesn't depend on cgroup v1 hugetlb controller.

Example:

  $ cat /sys/fs/cgroup/user.slice/user-0.slice/session-1.scope/memory.stat
  anon 1634304
  file 1163264
  kernel_stack 16384
  slab 737280
  sock 0
  shmem 0
  file_mapped 32768
  file_dirty 4096
  file_writeback 0
  inactive_anon 0
  active_anon 1634304
  inactive_file 65536
  active_file 1097728
  unevictable 0
  slab_reclaimable 282624
  slab_unreclaimable 454656
  hugetlb 1073741824
  pgfault 4580
  pgmajfault 13
  ...
Quoted 12 lines
Cc Johannes Weiner <[email protected]>
Cc Michal Hocko <[email protected]>
Cc Vladimir Davydov <[email protected]>
Cc Andrew Morton <[email protected]>
Cc Tejun Heo <[email protected]>
Cc Mike Kravetz <[email protected]>
Cc Dave Hansen <[email protected]>

Thanks.

Signature · 7 lines
tejun

--
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>
maintainer
#2 lore raw
In reply to Roman Gushchin
Quoted 10 lines · Roman Gushchin

This patch implements basic accounting of memory consumption by hugetlbfs pages for cgroup v2 memory controller.

Cgroup v2 memory controller lacks any visibility into the hugetlbfs memory consumption. Cgroup v1 implemented a separate hugetlbfs controller, which provided such stats, and also provided some control abilities. Although porting of the hugetlbfs controller to cgroup v2 is arguable a good idea and is outside of scope of this patch, it's very useful to have basic stats provided by memory.stat.

Separate hugetlb cgroup controller was really a deliberate decision. We didn't want to mix hugetlb with the reclaimable memory. There is no reasonable way to enforce memcg limits if hugetlb pages are involved.

AFAICS your patch shouldn't break the hugetlb controller because that one (ab)uses page[2].private to store the hstate for the accounting. You also do not really charge those hugetlb pages so the memcg accounting will work unchaged.

So my primary question is, why don't you simply allow hugetlb controller rather than tweak stats for memcg? Is there any fundamental reason why hugetlb controller is not v2 compatible?

It feels really strange to keeps stats of something the controller doesn't really control. I can imagine confused users claiming that numbers just do not add up...

Signature · 8 lines
Michal Hocko
SUSE Labs

--
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>
#3 lore raw
Quoted 11 lines · Michal Hocko
Quoted 10 lines · Roman Gushchin

This patch implements basic accounting of memory consumption by hugetlbfs pages for cgroup v2 memory controller.

Cgroup v2 memory controller lacks any visibility into the hugetlbfs memory consumption. Cgroup v1 implemented a separate hugetlbfs controller, which provided such stats, and also provided some control abilities. Although porting of the hugetlbfs controller to cgroup v2 is arguable a good idea and is outside of scope of this patch, it's very useful to have basic stats provided by memory.stat.

Hi, Michal!

Quoted 8 lines

Separate hugetlb cgroup controller was really a deliberate decision. We didn't want to mix hugetlb with the reclaimable memory. There is no reasonable way to enforce memcg limits if hugetlb pages are involved.

AFAICS your patch shouldn't break the hugetlb controller because that one (ab)uses page[2].private to store the hstate for the accounting. You also do not really charge those hugetlb pages so the memcg accounting will work unchaged.

Yes, you are right.

Quoted 4 lines

So my primary question is, why don't you simply allow hugetlb controller rather than tweak stats for memcg? Is there any fundamental reason why hugetlb controller is not v2 compatible?

I really don't know if the hugetlb controller has enough users to deserve full support in v2 interface: adding knobs like memory.hugetlb.current, memory.hugetlb.min, memory.hugetlb.high, memory.hugetlb.max, etc.

I'd be rather conservative here and avoid adding a lot to the interface without clear demand. Also, hugetlb pages are really special, and it's at least not obvious how, say, memory.high should work for it.

At the same time we don't really have any accounting of hugetlb page usage (except system-wide stats in sysfs). And providing such stats is really useful.
In my particular case, I have some number of pre-allocated hugepages, and I have several containerized workloads, which are potentially using them to get performance bonuses. Having these stats allows to attribute the memory holding by hugetlb pages to one of the workloads.

Quoted 3 lines

It feels really strange to keeps stats of something the controller doesn't really control. I can imagine confused users claiming that numbers just do not add up...

This is why I do not add this number to memory.current. At the same time numbers in memory.stat are not intended to be summed (we have event counters there, dirty pages counter, etc), so I don't see a problem.

Thanks!

--
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>

maintainer
#4 lore raw
Quoted 1 line · Roman Gushchin

On Wed, Nov 15, 2017 at 09:35:04AM +0100, Michal Hocko wrote:

[...]

Quoted 11 lines
Quoted 3 lines

So my primary question is, why don't you simply allow hugetlb controller rather than tweak stats for memcg? Is there any fundamental reason why hugetlb controller is not v2 compatible?

I really don't know if the hugetlb controller has enough users to deserve full support in v2 interface: adding knobs like memory.hugetlb.current, memory.hugetlb.min, memory.hugetlb.high, memory.hugetlb.max, etc.

I'd be rather conservative here and avoid adding a lot to the interface without clear demand. Also, hugetlb pages are really special, and it's at least not obvious how, say, memory.high should work for it.

But you clearly want the hugetlb accoutning and that is what hugetlb controller is for. You might not be interested in the limit enforcement but that is not strictly required. So my question remains. Why don't we reuse an existing infrastructure and add a new which might confuse users in an extreme case?

Please note that I am not saying your patch is wrong, I just do not see why we should handle hugetlb pages 2 different ways to achieve a common infrastructure.

Signature · 8 lines
Michal Hocko
SUSE Labs

--
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>
#5 lore raw
Quoted 15 lines · Michal Hocko
Quoted 1 line · Roman Gushchin

On Wed, Nov 15, 2017 at 09:35:04AM +0100, Michal Hocko wrote:

[...]

Quoted 11 lines
Quoted 3 lines

So my primary question is, why don't you simply allow hugetlb controller rather than tweak stats for memcg? Is there any fundamental reason why hugetlb controller is not v2 compatible?

I really don't know if the hugetlb controller has enough users to deserve full support in v2 interface: adding knobs like memory.hugetlb.current, memory.hugetlb.min, memory.hugetlb.high, memory.hugetlb.max, etc.

I'd be rather conservative here and avoid adding a lot to the interface without clear demand. Also, hugetlb pages are really special, and it's at least not obvious how, say, memory.high should work for it.

Quoted 5 lines

But you clearly want the hugetlb accoutning and that is what hugetlb controller is for. You might not be interested in the limit enforcement but that is not strictly required. So my question remains. Why don't we reuse an existing infrastructure and add a new which might confuse users in an extreme case?

Hm, but to use a small part of hugetlb controller infrastructure I would have to add a whole set of cgroup v2 controls.
And control knobs (like memory.hugetlb.current) are much more obligatory than an entry in memory.stat, where we have some internal stats as well.

So, I don't really know why confusion should come from in this case? It would be confusing, if we'd add hugetlb stats to the memory.current, so that it could be larger then memory.max.
But as separate entry in memory.stat it should not confuse anyone, at least not more than the existing state of things, when hugetlb pages are a black hole.

Quoted 4 lines

Please note that I am not saying your patch is wrong, I just do not see why we should handle hugetlb pages 2 different ways to achieve a common infrastructure.

This is perfectly fine, and I do understand it.
My point is that it's a cheap way to solve a real problem, which is also not binding us too much.

Thanks!

--
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>

maintainer
#6 lore raw
Quoted 16 lines · Roman Gushchin
Quoted 15 lines · Michal Hocko
Quoted 1 line · Roman Gushchin

On Wed, Nov 15, 2017 at 09:35:04AM +0100, Michal Hocko wrote:

[...]

Quoted 11 lines
Quoted 3 lines

So my primary question is, why don't you simply allow hugetlb controller rather than tweak stats for memcg? Is there any fundamental reason why hugetlb controller is not v2 compatible?

I really don't know if the hugetlb controller has enough users to deserve full support in v2 interface: adding knobs like memory.hugetlb.current, memory.hugetlb.min, memory.hugetlb.high, memory.hugetlb.max, etc.

I'd be rather conservative here and avoid adding a lot to the interface without clear demand. Also, hugetlb pages are really special, and it's at least not obvious how, say, memory.high should work for it.

Quoted 8 lines
Quoted 5 lines

But you clearly want the hugetlb accoutning and that is what hugetlb controller is for. You might not be interested in the limit enforcement but that is not strictly required. So my question remains. Why don't we reuse an existing infrastructure and add a new which might confuse users in an extreme case?

Hm, but to use a small part of hugetlb controller infrastructure I would have to add a whole set of cgroup v2 controls.

And? I mean how does that differ from somebody using memcg only for stat purposes?

Quoted 4 lines

And control knobs (like memory.hugetlb.current) are much more obligatory than an entry in memory.stat, where we have some internal stats as well.

So, I don't really know why confusion should come from in this case?

Because you stat data that is not controlled by the controller. Just imagine if somebody wanted to sum counters to get the resulting accounted and enforced memory.

You are simply trying to push a square through circle without a good reason. Unless there is a fundamental reason to not enable hugetlb controller to v2 I would rather go that way rather than to have another hugetlb weirdness. Enabling the controller should be a matter of exporting knobs. Trivial thing AFAICS.

Signature · 2 lines
Michal Hocko
SUSE Labs
maintainer
#7 lore raw
In reply to Roman Gushchin
Quoted 11 lines · Roman Gushchin

This patch implements basic accounting of memory consumption by hugetlbfs pages for cgroup v2 memory controller.

Cgroup v2 memory controller lacks any visibility into the hugetlbfs memory consumption. Cgroup v1 implemented a separate hugetlbfs controller, which provided such stats, and also provided some control abilities. Although porting of the hugetlbfs controller to cgroup v2 is arguable a good idea and is outside of scope of this patch, it's very useful to have basic stats provided by memory.stat.

Quoted 3 lines

As hugetlbfs memory can easily represent a big portion of total memory, it's important to understand who (which memcg/container) is using it.

I'm not really buying this argument.

Hugetlb setups tend to be static configurations that require intimate coordination between booting the kernel with a hugetlb reservation and precisely setting up the application(s).

In the few cases where you need introspection, you can check the the HugetlbPages entry in /proc/<pid>/status. The minor convenience provided by adding an aggregate cgroup counter IMO doesn't outweigh the weirdness of listing a type of resource in memory.stat that isn't otherwise acknowledged or controllable as memory.

--
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>