diff options
author | Daisuke Nishimura <nishimura@mxp.nes.nec.co.jp> | 2009-12-15 19:47:11 -0500 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2009-12-16 10:20:07 -0500 |
commit | 57f9fd7d25ac9a0d7e3a4ced580e780ab4524e3b (patch) | |
tree | ff95e62e7326ba7e77903f7f767e9650c0d9b3dd /mm/memcontrol.c | |
parent | a3032a2c15c6967f9f0c0c28375b1a5c833a3112 (diff) |
memcg: cleanup mem_cgroup_move_parent()
mem_cgroup_move_parent() calls try_charge first and cancel_charge on
failure. IMHO, charge/uncharge(especially charge) is high cost operation,
so we should avoid it as far as possible.
This patch tries to delay try_charge in mem_cgroup_move_parent() by
re-ordering checks it does.
And this patch renames mem_cgroup_move_account() to
__mem_cgroup_move_account(), changes the return value of
__mem_cgroup_move_account() from int to void, and adds a new
wrapper(mem_cgroup_move_account()), which checks whether a @pc is valid
for moving account and calls __mem_cgroup_move_account().
This patch removes the last caller of trylock_page_cgroup(), so removes
its definition too.
Signed-off-by: Daisuke Nishimura <nishimura@mxp.nes.nec.co.jp>
Acked-by: KAMEZAWA Hiroyuki <kamezawa.hiroyu@jp.fujitsu.com>
Cc: Balbir Singh <balbir@linux.vnet.ibm.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
Diffstat (limited to 'mm/memcontrol.c')
-rw-r--r-- | mm/memcontrol.c | 84 |
1 files changed, 35 insertions, 49 deletions
diff --git a/mm/memcontrol.c b/mm/memcontrol.c index 2d6b4a912a6d..6273984f2e34 100644 --- a/mm/memcontrol.c +++ b/mm/memcontrol.c | |||
@@ -1613,27 +1613,22 @@ static void __mem_cgroup_commit_charge(struct mem_cgroup *mem, | |||
1613 | } | 1613 | } |
1614 | 1614 | ||
1615 | /** | 1615 | /** |
1616 | * mem_cgroup_move_account - move account of the page | 1616 | * __mem_cgroup_move_account - move account of the page |
1617 | * @pc: page_cgroup of the page. | 1617 | * @pc: page_cgroup of the page. |
1618 | * @from: mem_cgroup which the page is moved from. | 1618 | * @from: mem_cgroup which the page is moved from. |
1619 | * @to: mem_cgroup which the page is moved to. @from != @to. | 1619 | * @to: mem_cgroup which the page is moved to. @from != @to. |
1620 | * | 1620 | * |
1621 | * The caller must confirm following. | 1621 | * The caller must confirm following. |
1622 | * - page is not on LRU (isolate_page() is useful.) | 1622 | * - page is not on LRU (isolate_page() is useful.) |
1623 | * | 1623 | * - the pc is locked, used, and ->mem_cgroup points to @from. |
1624 | * returns 0 at success, | ||
1625 | * returns -EBUSY when lock is busy or "pc" is unstable. | ||
1626 | * | 1624 | * |
1627 | * This function does "uncharge" from old cgroup but doesn't do "charge" to | 1625 | * This function does "uncharge" from old cgroup but doesn't do "charge" to |
1628 | * new cgroup. It should be done by a caller. | 1626 | * new cgroup. It should be done by a caller. |
1629 | */ | 1627 | */ |
1630 | 1628 | ||
1631 | static int mem_cgroup_move_account(struct page_cgroup *pc, | 1629 | static void __mem_cgroup_move_account(struct page_cgroup *pc, |
1632 | struct mem_cgroup *from, struct mem_cgroup *to) | 1630 | struct mem_cgroup *from, struct mem_cgroup *to) |
1633 | { | 1631 | { |
1634 | struct mem_cgroup_per_zone *from_mz, *to_mz; | ||
1635 | int nid, zid; | ||
1636 | int ret = -EBUSY; | ||
1637 | struct page *page; | 1632 | struct page *page; |
1638 | int cpu; | 1633 | int cpu; |
1639 | struct mem_cgroup_stat *stat; | 1634 | struct mem_cgroup_stat *stat; |
@@ -1641,20 +1636,9 @@ static int mem_cgroup_move_account(struct page_cgroup *pc, | |||
1641 | 1636 | ||
1642 | VM_BUG_ON(from == to); | 1637 | VM_BUG_ON(from == to); |
1643 | VM_BUG_ON(PageLRU(pc->page)); | 1638 | VM_BUG_ON(PageLRU(pc->page)); |
1644 | 1639 | VM_BUG_ON(!PageCgroupLocked(pc)); | |
1645 | nid = page_cgroup_nid(pc); | 1640 | VM_BUG_ON(!PageCgroupUsed(pc)); |
1646 | zid = page_cgroup_zid(pc); | 1641 | VM_BUG_ON(pc->mem_cgroup != from); |
1647 | from_mz = mem_cgroup_zoneinfo(from, nid, zid); | ||
1648 | to_mz = mem_cgroup_zoneinfo(to, nid, zid); | ||
1649 | |||
1650 | if (!trylock_page_cgroup(pc)) | ||
1651 | return ret; | ||
1652 | |||
1653 | if (!PageCgroupUsed(pc)) | ||
1654 | goto out; | ||
1655 | |||
1656 | if (pc->mem_cgroup != from) | ||
1657 | goto out; | ||
1658 | 1642 | ||
1659 | if (!mem_cgroup_is_root(from)) | 1643 | if (!mem_cgroup_is_root(from)) |
1660 | res_counter_uncharge(&from->res, PAGE_SIZE); | 1644 | res_counter_uncharge(&from->res, PAGE_SIZE); |
@@ -1683,15 +1667,28 @@ static int mem_cgroup_move_account(struct page_cgroup *pc, | |||
1683 | css_get(&to->css); | 1667 | css_get(&to->css); |
1684 | pc->mem_cgroup = to; | 1668 | pc->mem_cgroup = to; |
1685 | mem_cgroup_charge_statistics(to, pc, true); | 1669 | mem_cgroup_charge_statistics(to, pc, true); |
1686 | ret = 0; | ||
1687 | out: | ||
1688 | unlock_page_cgroup(pc); | ||
1689 | /* | 1670 | /* |
1690 | * We charges against "to" which may not have any tasks. Then, "to" | 1671 | * We charges against "to" which may not have any tasks. Then, "to" |
1691 | * can be under rmdir(). But in current implementation, caller of | 1672 | * can be under rmdir(). But in current implementation, caller of |
1692 | * this function is just force_empty() and it's garanteed that | 1673 | * this function is just force_empty() and it's garanteed that |
1693 | * "to" is never removed. So, we don't check rmdir status here. | 1674 | * "to" is never removed. So, we don't check rmdir status here. |
1694 | */ | 1675 | */ |
1676 | } | ||
1677 | |||
1678 | /* | ||
1679 | * check whether the @pc is valid for moving account and call | ||
1680 | * __mem_cgroup_move_account() | ||
1681 | */ | ||
1682 | static int mem_cgroup_move_account(struct page_cgroup *pc, | ||
1683 | struct mem_cgroup *from, struct mem_cgroup *to) | ||
1684 | { | ||
1685 | int ret = -EINVAL; | ||
1686 | lock_page_cgroup(pc); | ||
1687 | if (PageCgroupUsed(pc) && pc->mem_cgroup == from) { | ||
1688 | __mem_cgroup_move_account(pc, from, to); | ||
1689 | ret = 0; | ||
1690 | } | ||
1691 | unlock_page_cgroup(pc); | ||
1695 | return ret; | 1692 | return ret; |
1696 | } | 1693 | } |
1697 | 1694 | ||
@@ -1713,38 +1710,27 @@ static int mem_cgroup_move_parent(struct page_cgroup *pc, | |||
1713 | if (!pcg) | 1710 | if (!pcg) |
1714 | return -EINVAL; | 1711 | return -EINVAL; |
1715 | 1712 | ||
1713 | ret = -EBUSY; | ||
1714 | if (!get_page_unless_zero(page)) | ||
1715 | goto out; | ||
1716 | if (isolate_lru_page(page)) | ||
1717 | goto put; | ||
1716 | 1718 | ||
1717 | parent = mem_cgroup_from_cont(pcg); | 1719 | parent = mem_cgroup_from_cont(pcg); |
1718 | |||
1719 | |||
1720 | ret = __mem_cgroup_try_charge(NULL, gfp_mask, &parent, false, page); | 1720 | ret = __mem_cgroup_try_charge(NULL, gfp_mask, &parent, false, page); |
1721 | if (ret || !parent) | 1721 | if (ret || !parent) |
1722 | return ret; | 1722 | goto put_back; |
1723 | |||
1724 | if (!get_page_unless_zero(page)) { | ||
1725 | ret = -EBUSY; | ||
1726 | goto uncharge; | ||
1727 | } | ||
1728 | |||
1729 | ret = isolate_lru_page(page); | ||
1730 | |||
1731 | if (ret) | ||
1732 | goto cancel; | ||
1733 | 1723 | ||
1734 | ret = mem_cgroup_move_account(pc, child, parent); | 1724 | ret = mem_cgroup_move_account(pc, child, parent); |
1735 | 1725 | if (!ret) | |
1726 | css_put(&parent->css); /* drop extra refcnt by try_charge() */ | ||
1727 | else | ||
1728 | mem_cgroup_cancel_charge(parent); /* does css_put */ | ||
1729 | put_back: | ||
1736 | putback_lru_page(page); | 1730 | putback_lru_page(page); |
1737 | if (!ret) { | 1731 | put: |
1738 | put_page(page); | ||
1739 | /* drop extra refcnt by try_charge() */ | ||
1740 | css_put(&parent->css); | ||
1741 | return 0; | ||
1742 | } | ||
1743 | |||
1744 | cancel: | ||
1745 | put_page(page); | 1732 | put_page(page); |
1746 | uncharge: | 1733 | out: |
1747 | mem_cgroup_cancel_charge(parent); | ||
1748 | return ret; | 1734 | return ret; |
1749 | } | 1735 | } |
1750 | 1736 | ||