History

hugetlb_cgroup: add hugetlb_cgroup reservation counter

These counters will track hugetlb reservations rather than hugetlb memory faulted in. This patch only adds the counter, following patches add the charging and uncharging of the counter. This is patch 1 of an 9 patch series. Problem: Currently tasks attempting to reserve more hugetlb memory than is available get a failure at mmap/shmget time. This is thanks to Hugetlbfs Reservations [1]. However, if a task attempts to reserve more hugetlb memory than its hugetlb_cgroup limit allows, the kernel will allow the mmap/shmget call, but will SIGBUS the task when it attempts to fault in the excess memory. We have users hitting their hugetlb_cgroup limits and thus we've been looking at this failure mode. We'd like to improve this behavior such that users violating the hugetlb_cgroup limits get an error on mmap/shmget time, rather than getting SIGBUS'd when they try to fault the excess memory in. This gives the user an opportunity to fallback more gracefully to non-hugetlbfs memory for example. The underlying problem is that today's hugetlb_cgroup accounting happens at hugetlb memory *fault* time, rather than at *reservation* time. Thus, enforcing the hugetlb_cgroup limit only happens at fault time, and the offending task gets SIGBUS'd. Proposed Solution: A new page counter named 'hugetlb.xMB.rsvd.[limit|usage|max_usage]_in_bytes'. This counter has slightly different semantics than 'hugetlb.xMB.[limit|usage|max_usage]_in_bytes': - While usage_in_bytes tracks all *faulted* hugetlb memory, rsvd.usage_in_bytes tracks all *reserved* hugetlb memory and hugetlb memory faulted in without a prior reservation. - If a task attempts to reserve more memory than limit_in_bytes allows, the kernel will allow it to do so. But if a task attempts to reserve more memory than rsvd.limit_in_bytes, the kernel will fail this reservation. This proposal is implemented in this patch series, with tests to verify functionality and show the usage. Alternatives considered: 1. A new cgroup, instead of only a new page_counter attached to the existing hugetlb_cgroup. Adding a new cgroup seemed like a lot of code duplication with hugetlb_cgroup. Keeping hugetlb related page counters under hugetlb_cgroup seemed cleaner as well. 2. Instead of adding a new counter, we considered adding a sysctl that modifies the behavior of hugetlb.xMB.[limit|usage]_in_bytes, to do accounting at reservation time rather than fault time. Adding a new page_counter seems better as userspace could, if it wants, choose to enforce different cgroups differently: one via limit_in_bytes, and another via rsvd.limit_in_bytes. This could be very useful if you're transitioning how hugetlb memory is partitioned on your system one cgroup at a time, for example. Also, someone may find usage for both limit_in_bytes and rsvd.limit_in_bytes concurrently, and this approach gives them the option to do so. Testing: - Added tests passing. - Used libhugetlbfs for regression testing. [1]: https://www.kernel.org/doc/html/latest/vm/hugetlbfs_reserv.html

Mina Almasry committed 6 years ago

Commit cdc2fcf1 parent 87bf91d2 files · +104 −15Browse files

Unified · Split

2 files
  1. include/linux/hugetlb.h +2 −2
  2. mm/hugetlb_cgroup.c +102 −13
include/linux/hugetlb.h+2−2
@@ -440,8 +440,8 @@ struct hstate { · 439 unmodified lines
440 unsigned int surplus_huge_pages_node[MAX_NUMNODES];440 unsigned int surplus_huge_pages_node[MAX_NUMNODES];
441#ifdef CONFIG_CGROUP_HUGETLB441#ifdef CONFIG_CGROUP_HUGETLB
442 /* cgroup control files */442 /* cgroup control files */
443 struct cftype cgroup_files_dfl[5];443 struct cftype cgroup_files_dfl[7];
444 struct cftype cgroup_files_legacy[5];444 struct cftype cgroup_files_legacy[9];
445#endif445#endif
446 char name[HSTATE_NAME_LEN];446 char name[HSTATE_NAME_LEN];
447};447};
mm/hugetlb_cgroup.c+102−13
@@ -36,6 +36,11 @@ struct hugetlb_cgroup { · 35 unmodified lines
36 */36 */
37 struct page_counter hugepage[HUGE_MAX_HSTATE];37 struct page_counter hugepage[HUGE_MAX_HSTATE];
38 38
39 /*
40 * the counter to account for hugepage reservations from hugetlb.
41 */
42 struct page_counter rsvd_hugepage[HUGE_MAX_HSTATE];
43
39 atomic_long_t events[HUGE_MAX_HSTATE][HUGETLB_NR_MEMORY_EVENTS];44 atomic_long_t events[HUGE_MAX_HSTATE][HUGETLB_NR_MEMORY_EVENTS];
40 atomic_long_t events_local[HUGE_MAX_HSTATE][HUGETLB_NR_MEMORY_EVENTS];45 atomic_long_t events_local[HUGE_MAX_HSTATE][HUGETLB_NR_MEMORY_EVENTS];
41 46
@@ -55,6 +60,15 @@ struct hugetlb_cgroup { · 13 unmodified lines
55 60
56static struct hugetlb_cgroup *root_h_cgroup __read_mostly;61static struct hugetlb_cgroup *root_h_cgroup __read_mostly;
57 62
63static inline struct page_counter *
64hugetlb_cgroup_counter_from_cgroup(struct hugetlb_cgroup *h_cg, int idx,
65 bool rsvd)
66{
67 if (rsvd)
68 return &h_cg->rsvd_hugepage[idx];
69 return &h_cg->hugepage[idx];
70}
71
58static inline72static inline
59struct hugetlb_cgroup *hugetlb_cgroup_from_css(struct cgroup_subsys_state *s)73struct hugetlb_cgroup *hugetlb_cgroup_from_css(struct cgroup_subsys_state *s)
60{74{
@@ -294,28 +308,42 @@ void hugetlb_cgroup_uncharge_cgroup(int idx, unsigned long nr_pages, · 233 unmodified lines
294 308
295enum {309enum {
296 RES_USAGE,310 RES_USAGE,
311 RES_RSVD_USAGE,
297 RES_LIMIT,312 RES_LIMIT,
313 RES_RSVD_LIMIT,
298 RES_MAX_USAGE,314 RES_MAX_USAGE,
315 RES_RSVD_MAX_USAGE,
299 RES_FAILCNT,316 RES_FAILCNT,
317 RES_RSVD_FAILCNT,
300};318};
301 319
302static u64 hugetlb_cgroup_read_u64(struct cgroup_subsys_state *css,320static u64 hugetlb_cgroup_read_u64(struct cgroup_subsys_state *css,
303 struct cftype *cft)321 struct cftype *cft)
304{322{
305 struct page_counter *counter;323 struct page_counter *counter;
324 struct page_counter *rsvd_counter;
306 struct hugetlb_cgroup *h_cg = hugetlb_cgroup_from_css(css);325 struct hugetlb_cgroup *h_cg = hugetlb_cgroup_from_css(css);
307 326
308 counter = &h_cg->hugepage[MEMFILE_IDX(cft->private)];327 counter = &h_cg->hugepage[MEMFILE_IDX(cft->private)];
328 rsvd_counter = &h_cg->rsvd_hugepage[MEMFILE_IDX(cft->private)];
309 329
310 switch (MEMFILE_ATTR(cft->private)) {330 switch (MEMFILE_ATTR(cft->private)) {
311 case RES_USAGE:331 case RES_USAGE:
312 return (u64)page_counter_read(counter) * PAGE_SIZE;332 return (u64)page_counter_read(counter) * PAGE_SIZE;
333 case RES_RSVD_USAGE:
334 return (u64)page_counter_read(rsvd_counter) * PAGE_SIZE;
313 case RES_LIMIT:335 case RES_LIMIT:
314 return (u64)counter->max * PAGE_SIZE;336 return (u64)counter->max * PAGE_SIZE;
337 case RES_RSVD_LIMIT:
338 return (u64)rsvd_counter->max * PAGE_SIZE;
315 case RES_MAX_USAGE:339 case RES_MAX_USAGE:
316 return (u64)counter->watermark * PAGE_SIZE;340 return (u64)counter->watermark * PAGE_SIZE;
341 case RES_RSVD_MAX_USAGE:
342 return (u64)rsvd_counter->watermark * PAGE_SIZE;
317 case RES_FAILCNT:343 case RES_FAILCNT:
318 return counter->failcnt;344 return counter->failcnt;
345 case RES_RSVD_FAILCNT:
346 return rsvd_counter->failcnt;
319 default:347 default:
320 BUG();348 BUG();
321 }349 }
@@ -337,10 +365,16 @@ static int hugetlb_cgroup_read_u64_max(struct seq_file *seq, void *v) · 15 unmodified lines
337 1 << huge_page_order(&hstates[idx]));365 1 << huge_page_order(&hstates[idx]));
338 366
339 switch (MEMFILE_ATTR(cft->private)) {367 switch (MEMFILE_ATTR(cft->private)) {
368 case RES_RSVD_USAGE:
369 counter = &h_cg->rsvd_hugepage[idx];
370 /* Fall through. */
340 case RES_USAGE:371 case RES_USAGE:
341 val = (u64)page_counter_read(counter);372 val = (u64)page_counter_read(counter);
342 seq_printf(seq, "%llu\n", val * PAGE_SIZE);373 seq_printf(seq, "%llu\n", val * PAGE_SIZE);
343 break;374 break;
375 case RES_RSVD_LIMIT:
376 counter = &h_cg->rsvd_hugepage[idx];
377 /* Fall through. */
344 case RES_LIMIT:378 case RES_LIMIT:
345 val = (u64)counter->max;379 val = (u64)counter->max;
346 if (val == limit)380 if (val == limit)
@@ -364,6 +398,7 @@ static ssize_t hugetlb_cgroup_write(struct kernfs_open_file *of, · 17 unmodified lines
364 int ret, idx;398 int ret, idx;
365 unsigned long nr_pages;399 unsigned long nr_pages;
366 struct hugetlb_cgroup *h_cg = hugetlb_cgroup_from_css(of_css(of));400 struct hugetlb_cgroup *h_cg = hugetlb_cgroup_from_css(of_css(of));
401 bool rsvd = false;
367 402
368 if (hugetlb_cgroup_is_root(h_cg)) /* Can't set limit on root */403 if (hugetlb_cgroup_is_root(h_cg)) /* Can't set limit on root */
369 return -EINVAL;404 return -EINVAL;
@@ -377,9 +412,14 @@ static ssize_t hugetlb_cgroup_write(struct kernfs_open_file *of, · 7 unmodified lines
377 nr_pages = round_down(nr_pages, 1 << huge_page_order(&hstates[idx]));412 nr_pages = round_down(nr_pages, 1 << huge_page_order(&hstates[idx]));
378 413
379 switch (MEMFILE_ATTR(of_cft(of)->private)) {414 switch (MEMFILE_ATTR(of_cft(of)->private)) {
415 case RES_RSVD_LIMIT:
416 rsvd = true;
417 /* Fall through. */
380 case RES_LIMIT:418 case RES_LIMIT:
381 mutex_lock(&hugetlb_limit_mutex);419 mutex_lock(&hugetlb_limit_mutex);
382 ret = page_counter_set_max(&h_cg->hugepage[idx], nr_pages);420 ret = page_counter_set_max(
421 hugetlb_cgroup_counter_from_cgroup(h_cg, idx, rsvd),
422 nr_pages);
383 mutex_unlock(&hugetlb_limit_mutex);423 mutex_unlock(&hugetlb_limit_mutex);
384 break;424 break;
385 default:425 default:
@@ -405,18 +445,25 @@ static ssize_t hugetlb_cgroup_reset(struct kernfs_open_file *of, · 19 unmodified lines
405 char *buf, size_t nbytes, loff_t off)445 char *buf, size_t nbytes, loff_t off)
406{446{
407 int ret = 0;447 int ret = 0;
408 struct page_counter *counter;448 struct page_counter *counter, *rsvd_counter;
409 struct hugetlb_cgroup *h_cg = hugetlb_cgroup_from_css(of_css(of));449 struct hugetlb_cgroup *h_cg = hugetlb_cgroup_from_css(of_css(of));
410 450
411 counter = &h_cg->hugepage[MEMFILE_IDX(of_cft(of)->private)];451 counter = &h_cg->hugepage[MEMFILE_IDX(of_cft(of)->private)];
452 rsvd_counter = &h_cg->rsvd_hugepage[MEMFILE_IDX(of_cft(of)->private)];
412 453
413 switch (MEMFILE_ATTR(of_cft(of)->private)) {454 switch (MEMFILE_ATTR(of_cft(of)->private)) {
414 case RES_MAX_USAGE:455 case RES_MAX_USAGE:
415 page_counter_reset_watermark(counter);456 page_counter_reset_watermark(counter);
416 break;457 break;
458 case RES_RSVD_MAX_USAGE:
459 page_counter_reset_watermark(rsvd_counter);
460 break;
417 case RES_FAILCNT:461 case RES_FAILCNT:
418 counter->failcnt = 0;462 counter->failcnt = 0;
419 break;463 break;
464 case RES_RSVD_FAILCNT:
465 rsvd_counter->failcnt = 0;
466 break;
420 default:467 default:
421 ret = -EINVAL;468 ret = -EINVAL;
422 break;469 break;
@@ -471,7 +518,7 @@ static void __init __hugetlb_cgroup_file_dfl_init(int idx) · 48 unmodified lines
471 struct hstate *h = &hstates[idx];518 struct hstate *h = &hstates[idx];
472 519
473 /* format the size */520 /* format the size */
474 mem_fmt(buf, 32, huge_page_size(h));521 mem_fmt(buf, sizeof(buf), huge_page_size(h));
475 522
476 /* Add the limit file */523 /* Add the limit file */
477 cft = &h->cgroup_files_dfl[0];524 cft = &h->cgroup_files_dfl[0];
@@ -481,15 +528,30 @@ static void __init __hugetlb_cgroup_file_dfl_init(int idx) · 3 unmodified lines
481 cft->write = hugetlb_cgroup_write_dfl;528 cft->write = hugetlb_cgroup_write_dfl;
482 cft->flags = CFTYPE_NOT_ON_ROOT;529 cft->flags = CFTYPE_NOT_ON_ROOT;
483 530
484 /* Add the current usage file */531 /* Add the reservation limit file */
485 cft = &h->cgroup_files_dfl[1];532 cft = &h->cgroup_files_dfl[1];
533 snprintf(cft->name, MAX_CFTYPE_NAME, "%s.rsvd.max", buf);
534 cft->private = MEMFILE_PRIVATE(idx, RES_RSVD_LIMIT);
535 cft->seq_show = hugetlb_cgroup_read_u64_max;
536 cft->write = hugetlb_cgroup_write_dfl;
537 cft->flags = CFTYPE_NOT_ON_ROOT;
538
539 /* Add the current usage file */
540 cft = &h->cgroup_files_dfl[2];
486 snprintf(cft->name, MAX_CFTYPE_NAME, "%s.current", buf);541 snprintf(cft->name, MAX_CFTYPE_NAME, "%s.current", buf);
487 cft->private = MEMFILE_PRIVATE(idx, RES_USAGE);542 cft->private = MEMFILE_PRIVATE(idx, RES_USAGE);
488 cft->seq_show = hugetlb_cgroup_read_u64_max;543 cft->seq_show = hugetlb_cgroup_read_u64_max;
489 cft->flags = CFTYPE_NOT_ON_ROOT;544 cft->flags = CFTYPE_NOT_ON_ROOT;
490 545
546 /* Add the current reservation usage file */
547 cft = &h->cgroup_files_dfl[3];
548 snprintf(cft->name, MAX_CFTYPE_NAME, "%s.rsvd.current", buf);
549 cft->private = MEMFILE_PRIVATE(idx, RES_RSVD_USAGE);
550 cft->seq_show = hugetlb_cgroup_read_u64_max;
551 cft->flags = CFTYPE_NOT_ON_ROOT;
552
491 /* Add the events file */553 /* Add the events file */
492 cft = &h->cgroup_files_dfl[2];554 cft = &h->cgroup_files_dfl[4];
493 snprintf(cft->name, MAX_CFTYPE_NAME, "%s.events", buf);555 snprintf(cft->name, MAX_CFTYPE_NAME, "%s.events", buf);
494 cft->private = MEMFILE_PRIVATE(idx, 0);556 cft->private = MEMFILE_PRIVATE(idx, 0);
495 cft->seq_show = hugetlb_events_show;557 cft->seq_show = hugetlb_events_show;
@@ -497,7 +559,7 @@ static void __init __hugetlb_cgroup_file_dfl_init(int idx) · 1 unmodified line
497 cft->flags = CFTYPE_NOT_ON_ROOT;559 cft->flags = CFTYPE_NOT_ON_ROOT;
498 560
499 /* Add the events.local file */561 /* Add the events.local file */
500 cft = &h->cgroup_files_dfl[3];562 cft = &h->cgroup_files_dfl[5];
501 snprintf(cft->name, MAX_CFTYPE_NAME, "%s.events.local", buf);563 snprintf(cft->name, MAX_CFTYPE_NAME, "%s.events.local", buf);
502 cft->private = MEMFILE_PRIVATE(idx, 0);564 cft->private = MEMFILE_PRIVATE(idx, 0);
503 cft->seq_show = hugetlb_events_local_show;565 cft->seq_show = hugetlb_events_local_show;
@@ -506,7 +568,7 @@ static void __init __hugetlb_cgroup_file_dfl_init(int idx) · 2 unmodified lines
506 cft->flags = CFTYPE_NOT_ON_ROOT;568 cft->flags = CFTYPE_NOT_ON_ROOT;
507 569
508 /* NULL terminate the last cft */570 /* NULL terminate the last cft */
509 cft = &h->cgroup_files_dfl[4];571 cft = &h->cgroup_files_dfl[6];
510 memset(cft, 0, sizeof(*cft));572 memset(cft, 0, sizeof(*cft));
511 573
512 WARN_ON(cgroup_add_dfl_cftypes(&hugetlb_cgrp_subsys,574 WARN_ON(cgroup_add_dfl_cftypes(&hugetlb_cgrp_subsys,
@@ -520,7 +582,7 @@ static void __init __hugetlb_cgroup_file_legacy_init(int idx) · 7 unmodified lines
520 struct hstate *h = &hstates[idx];582 struct hstate *h = &hstates[idx];
521 583
522 /* format the size */584 /* format the size */
523 mem_fmt(buf, 32, huge_page_size(h));585 mem_fmt(buf, sizeof(buf), huge_page_size(h));
524 586
525 /* Add the limit file */587 /* Add the limit file */
526 cft = &h->cgroup_files_legacy[0];588 cft = &h->cgroup_files_legacy[0];
@@ -529,28 +591,55 @@ static void __init __hugetlb_cgroup_file_legacy_init(int idx) · 2 unmodified lines
529 cft->read_u64 = hugetlb_cgroup_read_u64;591 cft->read_u64 = hugetlb_cgroup_read_u64;
530 cft->write = hugetlb_cgroup_write_legacy;592 cft->write = hugetlb_cgroup_write_legacy;
531 593
532 /* Add the usage file */594 /* Add the reservation limit file */
533 cft = &h->cgroup_files_legacy[1];595 cft = &h->cgroup_files_legacy[1];
596 snprintf(cft->name, MAX_CFTYPE_NAME, "%s.rsvd.limit_in_bytes", buf);
597 cft->private = MEMFILE_PRIVATE(idx, RES_RSVD_LIMIT);
598 cft->read_u64 = hugetlb_cgroup_read_u64;
599 cft->write = hugetlb_cgroup_write_legacy;
600
601 /* Add the usage file */
602 cft = &h->cgroup_files_legacy[2];
534 snprintf(cft->name, MAX_CFTYPE_NAME, "%s.usage_in_bytes", buf);603 snprintf(cft->name, MAX_CFTYPE_NAME, "%s.usage_in_bytes", buf);
535 cft->private = MEMFILE_PRIVATE(idx, RES_USAGE);604 cft->private = MEMFILE_PRIVATE(idx, RES_USAGE);
536 cft->read_u64 = hugetlb_cgroup_read_u64;605 cft->read_u64 = hugetlb_cgroup_read_u64;
537 606
607 /* Add the reservation usage file */
608 cft = &h->cgroup_files_legacy[3];
609 snprintf(cft->name, MAX_CFTYPE_NAME, "%s.rsvd.usage_in_bytes", buf);
610 cft->private = MEMFILE_PRIVATE(idx, RES_RSVD_USAGE);
611 cft->read_u64 = hugetlb_cgroup_read_u64;
612
538 /* Add the MAX usage file */613 /* Add the MAX usage file */
539 cft = &h->cgroup_files_legacy[2];614 cft = &h->cgroup_files_legacy[4];
540 snprintf(cft->name, MAX_CFTYPE_NAME, "%s.max_usage_in_bytes", buf);615 snprintf(cft->name, MAX_CFTYPE_NAME, "%s.max_usage_in_bytes", buf);
541 cft->private = MEMFILE_PRIVATE(idx, RES_MAX_USAGE);616 cft->private = MEMFILE_PRIVATE(idx, RES_MAX_USAGE);
542 cft->write = hugetlb_cgroup_reset;617 cft->write = hugetlb_cgroup_reset;
543 cft->read_u64 = hugetlb_cgroup_read_u64;618 cft->read_u64 = hugetlb_cgroup_read_u64;
544 619
620 /* Add the MAX reservation usage file */
621 cft = &h->cgroup_files_legacy[5];
622 snprintf(cft->name, MAX_CFTYPE_NAME, "%s.rsvd.max_usage_in_bytes", buf);
623 cft->private = MEMFILE_PRIVATE(idx, RES_RSVD_MAX_USAGE);
624 cft->write = hugetlb_cgroup_reset;
625 cft->read_u64 = hugetlb_cgroup_read_u64;
626
545 /* Add the failcntfile */627 /* Add the failcntfile */
546 cft = &h->cgroup_files_legacy[3];628 cft = &h->cgroup_files_legacy[6];
547 snprintf(cft->name, MAX_CFTYPE_NAME, "%s.failcnt", buf);629 snprintf(cft->name, MAX_CFTYPE_NAME, "%s.failcnt", buf);
548 cft->private = MEMFILE_PRIVATE(idx, RES_FAILCNT);630 cft->private = MEMFILE_PRIVATE(idx, RES_FAILCNT);
631 cft->write = hugetlb_cgroup_reset;
632 cft->read_u64 = hugetlb_cgroup_read_u64;
633
634 /* Add the reservation failcntfile */
635 cft = &h->cgroup_files_legacy[7];
636 snprintf(cft->name, MAX_CFTYPE_NAME, "%s.rsvd.failcnt", buf);
637 cft->private = MEMFILE_PRIVATE(idx, RES_RSVD_FAILCNT);
549 cft->write = hugetlb_cgroup_reset;638 cft->write = hugetlb_cgroup_reset;
550 cft->read_u64 = hugetlb_cgroup_read_u64;639 cft->read_u64 = hugetlb_cgroup_read_u64;
551 640
552 /* NULL terminate the last cft */641 /* NULL terminate the last cft */
553 cft = &h->cgroup_files_legacy[4];642 cft = &h->cgroup_files_legacy[8];
554 memset(cft, 0, sizeof(*cft));643 memset(cft, 0, sizeof(*cft));
555 644
556 WARN_ON(cgroup_add_legacy_cftypes(&hugetlb_cgrp_subsys,645 WARN_ON(cgroup_add_legacy_cftypes(&hugetlb_cgrp_subsys,