diff options
Diffstat (limited to 'mm/memcontrol.c')
-rw-r--r-- | mm/memcontrol.c | 155 |
1 files changed, 124 insertions, 31 deletions
diff --git a/mm/memcontrol.c b/mm/memcontrol.c index 51ee96545579..f568b1964551 100644 --- a/mm/memcontrol.c +++ b/mm/memcontrol.c | |||
@@ -467,35 +467,31 @@ unsigned long mem_cgroup_isolate_pages(unsigned long nr_to_scan, | |||
467 | return nr_taken; | 467 | return nr_taken; |
468 | } | 468 | } |
469 | 469 | ||
470 | /* | 470 | |
471 | * Charge the memory controller for page usage. | 471 | /** |
472 | * Return | 472 | * mem_cgroup_try_charge - get charge of PAGE_SIZE. |
473 | * 0 if the charge was successful | 473 | * @mm: an mm_struct which is charged against. (when *memcg is NULL) |
474 | * < 0 if the cgroup is over its limit | 474 | * @gfp_mask: gfp_mask for reclaim. |
475 | * @memcg: a pointer to memory cgroup which is charged against. | ||
476 | * | ||
477 | * charge against memory cgroup pointed by *memcg. if *memcg == NULL, estimated | ||
478 | * memory cgroup from @mm is got and stored in *memcg. | ||
479 | * | ||
480 | * Returns 0 if success. -ENOMEM at failure. | ||
475 | */ | 481 | */ |
476 | static int mem_cgroup_charge_common(struct page *page, struct mm_struct *mm, | 482 | |
477 | gfp_t gfp_mask, enum charge_type ctype, | 483 | int mem_cgroup_try_charge(struct mm_struct *mm, |
478 | struct mem_cgroup *memcg) | 484 | gfp_t gfp_mask, struct mem_cgroup **memcg) |
479 | { | 485 | { |
480 | struct mem_cgroup *mem; | 486 | struct mem_cgroup *mem; |
481 | struct page_cgroup *pc; | 487 | int nr_retries = MEM_CGROUP_RECLAIM_RETRIES; |
482 | unsigned long nr_retries = MEM_CGROUP_RECLAIM_RETRIES; | ||
483 | struct mem_cgroup_per_zone *mz; | ||
484 | unsigned long flags; | ||
485 | |||
486 | pc = lookup_page_cgroup(page); | ||
487 | /* can happen at boot */ | ||
488 | if (unlikely(!pc)) | ||
489 | return 0; | ||
490 | prefetchw(pc); | ||
491 | /* | 488 | /* |
492 | * We always charge the cgroup the mm_struct belongs to. | 489 | * We always charge the cgroup the mm_struct belongs to. |
493 | * The mm_struct's mem_cgroup changes on task migration if the | 490 | * The mm_struct's mem_cgroup changes on task migration if the |
494 | * thread group leader migrates. It's possible that mm is not | 491 | * thread group leader migrates. It's possible that mm is not |
495 | * set, if so charge the init_mm (happens for pagecache usage). | 492 | * set, if so charge the init_mm (happens for pagecache usage). |
496 | */ | 493 | */ |
497 | 494 | if (likely(!*memcg)) { | |
498 | if (likely(!memcg)) { | ||
499 | rcu_read_lock(); | 495 | rcu_read_lock(); |
500 | mem = mem_cgroup_from_task(rcu_dereference(mm->owner)); | 496 | mem = mem_cgroup_from_task(rcu_dereference(mm->owner)); |
501 | if (unlikely(!mem)) { | 497 | if (unlikely(!mem)) { |
@@ -506,15 +502,17 @@ static int mem_cgroup_charge_common(struct page *page, struct mm_struct *mm, | |||
506 | * For every charge from the cgroup, increment reference count | 502 | * For every charge from the cgroup, increment reference count |
507 | */ | 503 | */ |
508 | css_get(&mem->css); | 504 | css_get(&mem->css); |
505 | *memcg = mem; | ||
509 | rcu_read_unlock(); | 506 | rcu_read_unlock(); |
510 | } else { | 507 | } else { |
511 | mem = memcg; | 508 | mem = *memcg; |
512 | css_get(&memcg->css); | 509 | css_get(&mem->css); |
513 | } | 510 | } |
514 | 511 | ||
512 | |||
515 | while (unlikely(res_counter_charge(&mem->res, PAGE_SIZE))) { | 513 | while (unlikely(res_counter_charge(&mem->res, PAGE_SIZE))) { |
516 | if (!(gfp_mask & __GFP_WAIT)) | 514 | if (!(gfp_mask & __GFP_WAIT)) |
517 | goto out; | 515 | goto nomem; |
518 | 516 | ||
519 | if (try_to_free_mem_cgroup_pages(mem, gfp_mask)) | 517 | if (try_to_free_mem_cgroup_pages(mem, gfp_mask)) |
520 | continue; | 518 | continue; |
@@ -531,18 +529,37 @@ static int mem_cgroup_charge_common(struct page *page, struct mm_struct *mm, | |||
531 | 529 | ||
532 | if (!nr_retries--) { | 530 | if (!nr_retries--) { |
533 | mem_cgroup_out_of_memory(mem, gfp_mask); | 531 | mem_cgroup_out_of_memory(mem, gfp_mask); |
534 | goto out; | 532 | goto nomem; |
535 | } | 533 | } |
536 | } | 534 | } |
535 | return 0; | ||
536 | nomem: | ||
537 | css_put(&mem->css); | ||
538 | return -ENOMEM; | ||
539 | } | ||
540 | |||
541 | /* | ||
542 | * commit a charge got by mem_cgroup_try_charge() and makes page_cgroup to be | ||
543 | * USED state. If already USED, uncharge and return. | ||
544 | */ | ||
545 | |||
546 | static void __mem_cgroup_commit_charge(struct mem_cgroup *mem, | ||
547 | struct page_cgroup *pc, | ||
548 | enum charge_type ctype) | ||
549 | { | ||
550 | struct mem_cgroup_per_zone *mz; | ||
551 | unsigned long flags; | ||
537 | 552 | ||
553 | /* try_charge() can return NULL to *memcg, taking care of it. */ | ||
554 | if (!mem) | ||
555 | return; | ||
538 | 556 | ||
539 | lock_page_cgroup(pc); | 557 | lock_page_cgroup(pc); |
540 | if (unlikely(PageCgroupUsed(pc))) { | 558 | if (unlikely(PageCgroupUsed(pc))) { |
541 | unlock_page_cgroup(pc); | 559 | unlock_page_cgroup(pc); |
542 | res_counter_uncharge(&mem->res, PAGE_SIZE); | 560 | res_counter_uncharge(&mem->res, PAGE_SIZE); |
543 | css_put(&mem->css); | 561 | css_put(&mem->css); |
544 | 562 | return; | |
545 | goto done; | ||
546 | } | 563 | } |
547 | pc->mem_cgroup = mem; | 564 | pc->mem_cgroup = mem; |
548 | /* | 565 | /* |
@@ -557,15 +574,39 @@ static int mem_cgroup_charge_common(struct page *page, struct mm_struct *mm, | |||
557 | __mem_cgroup_add_list(mz, pc); | 574 | __mem_cgroup_add_list(mz, pc); |
558 | spin_unlock_irqrestore(&mz->lru_lock, flags); | 575 | spin_unlock_irqrestore(&mz->lru_lock, flags); |
559 | unlock_page_cgroup(pc); | 576 | unlock_page_cgroup(pc); |
577 | } | ||
560 | 578 | ||
561 | done: | 579 | /* |
580 | * Charge the memory controller for page usage. | ||
581 | * Return | ||
582 | * 0 if the charge was successful | ||
583 | * < 0 if the cgroup is over its limit | ||
584 | */ | ||
585 | static int mem_cgroup_charge_common(struct page *page, struct mm_struct *mm, | ||
586 | gfp_t gfp_mask, enum charge_type ctype, | ||
587 | struct mem_cgroup *memcg) | ||
588 | { | ||
589 | struct mem_cgroup *mem; | ||
590 | struct page_cgroup *pc; | ||
591 | int ret; | ||
592 | |||
593 | pc = lookup_page_cgroup(page); | ||
594 | /* can happen at boot */ | ||
595 | if (unlikely(!pc)) | ||
596 | return 0; | ||
597 | prefetchw(pc); | ||
598 | |||
599 | mem = memcg; | ||
600 | ret = mem_cgroup_try_charge(mm, gfp_mask, &mem); | ||
601 | if (ret) | ||
602 | return ret; | ||
603 | |||
604 | __mem_cgroup_commit_charge(mem, pc, ctype); | ||
562 | return 0; | 605 | return 0; |
563 | out: | ||
564 | css_put(&mem->css); | ||
565 | return -ENOMEM; | ||
566 | } | 606 | } |
567 | 607 | ||
568 | int mem_cgroup_charge(struct page *page, struct mm_struct *mm, gfp_t gfp_mask) | 608 | int mem_cgroup_newpage_charge(struct page *page, |
609 | struct mm_struct *mm, gfp_t gfp_mask) | ||
569 | { | 610 | { |
570 | if (mem_cgroup_subsys.disabled) | 611 | if (mem_cgroup_subsys.disabled) |
571 | return 0; | 612 | return 0; |
@@ -586,6 +627,34 @@ int mem_cgroup_charge(struct page *page, struct mm_struct *mm, gfp_t gfp_mask) | |||
586 | MEM_CGROUP_CHARGE_TYPE_MAPPED, NULL); | 627 | MEM_CGROUP_CHARGE_TYPE_MAPPED, NULL); |
587 | } | 628 | } |
588 | 629 | ||
630 | /* | ||
631 | * same as mem_cgroup_newpage_charge(), now. | ||
632 | * But what we assume is different from newpage, and this is special case. | ||
633 | * treat this in special function. easy for maintenance. | ||
634 | */ | ||
635 | |||
636 | int mem_cgroup_charge_migrate_fixup(struct page *page, | ||
637 | struct mm_struct *mm, gfp_t gfp_mask) | ||
638 | { | ||
639 | if (mem_cgroup_subsys.disabled) | ||
640 | return 0; | ||
641 | |||
642 | if (PageCompound(page)) | ||
643 | return 0; | ||
644 | |||
645 | if (page_mapped(page) || (page->mapping && !PageAnon(page))) | ||
646 | return 0; | ||
647 | |||
648 | if (unlikely(!mm)) | ||
649 | mm = &init_mm; | ||
650 | |||
651 | return mem_cgroup_charge_common(page, mm, gfp_mask, | ||
652 | MEM_CGROUP_CHARGE_TYPE_MAPPED, NULL); | ||
653 | } | ||
654 | |||
655 | |||
656 | |||
657 | |||
589 | int mem_cgroup_cache_charge(struct page *page, struct mm_struct *mm, | 658 | int mem_cgroup_cache_charge(struct page *page, struct mm_struct *mm, |
590 | gfp_t gfp_mask) | 659 | gfp_t gfp_mask) |
591 | { | 660 | { |
@@ -628,6 +697,30 @@ int mem_cgroup_cache_charge(struct page *page, struct mm_struct *mm, | |||
628 | MEM_CGROUP_CHARGE_TYPE_SHMEM, NULL); | 697 | MEM_CGROUP_CHARGE_TYPE_SHMEM, NULL); |
629 | } | 698 | } |
630 | 699 | ||
700 | |||
701 | void mem_cgroup_commit_charge_swapin(struct page *page, struct mem_cgroup *ptr) | ||
702 | { | ||
703 | struct page_cgroup *pc; | ||
704 | |||
705 | if (mem_cgroup_subsys.disabled) | ||
706 | return; | ||
707 | if (!ptr) | ||
708 | return; | ||
709 | pc = lookup_page_cgroup(page); | ||
710 | __mem_cgroup_commit_charge(ptr, pc, MEM_CGROUP_CHARGE_TYPE_MAPPED); | ||
711 | } | ||
712 | |||
713 | void mem_cgroup_cancel_charge_swapin(struct mem_cgroup *mem) | ||
714 | { | ||
715 | if (mem_cgroup_subsys.disabled) | ||
716 | return; | ||
717 | if (!mem) | ||
718 | return; | ||
719 | res_counter_uncharge(&mem->res, PAGE_SIZE); | ||
720 | css_put(&mem->css); | ||
721 | } | ||
722 | |||
723 | |||
631 | /* | 724 | /* |
632 | * uncharge if !page_mapped(page) | 725 | * uncharge if !page_mapped(page) |
633 | */ | 726 | */ |