diff options
-rw-r--r-- | include/linux/page_cgroup.h | 7 | ||||
-rw-r--r-- | mm/memcontrol.c | 84 |
2 files changed, 37 insertions, 54 deletions
diff --git a/include/linux/page_cgroup.h b/include/linux/page_cgroup.h index 4b938d4f3ac2..b0e4eb126236 100644 --- a/include/linux/page_cgroup.h +++ b/include/linux/page_cgroup.h | |||
@@ -57,6 +57,8 @@ static inline void ClearPageCgroup##uname(struct page_cgroup *pc) \ | |||
57 | static inline int TestClearPageCgroup##uname(struct page_cgroup *pc) \ | 57 | static inline int TestClearPageCgroup##uname(struct page_cgroup *pc) \ |
58 | { return test_and_clear_bit(PCG_##lname, &pc->flags); } | 58 | { return test_and_clear_bit(PCG_##lname, &pc->flags); } |
59 | 59 | ||
60 | TESTPCGFLAG(Locked, LOCK) | ||
61 | |||
60 | /* Cache flag is set only once (at allocation) */ | 62 | /* Cache flag is set only once (at allocation) */ |
61 | TESTPCGFLAG(Cache, CACHE) | 63 | TESTPCGFLAG(Cache, CACHE) |
62 | CLEARPCGFLAG(Cache, CACHE) | 64 | CLEARPCGFLAG(Cache, CACHE) |
@@ -86,11 +88,6 @@ static inline void lock_page_cgroup(struct page_cgroup *pc) | |||
86 | bit_spin_lock(PCG_LOCK, &pc->flags); | 88 | bit_spin_lock(PCG_LOCK, &pc->flags); |
87 | } | 89 | } |
88 | 90 | ||
89 | static inline int trylock_page_cgroup(struct page_cgroup *pc) | ||
90 | { | ||
91 | return bit_spin_trylock(PCG_LOCK, &pc->flags); | ||
92 | } | ||
93 | |||
94 | static inline void unlock_page_cgroup(struct page_cgroup *pc) | 91 | static inline void unlock_page_cgroup(struct page_cgroup *pc) |
95 | { | 92 | { |
96 | bit_spin_unlock(PCG_LOCK, &pc->flags); | 93 | bit_spin_unlock(PCG_LOCK, &pc->flags); |
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 | ||