aboutsummaryrefslogtreecommitdiffstats
path: root/mm/memcontrol.c
diff options
context:
space:
mode:
Diffstat (limited to 'mm/memcontrol.c')
-rw-r--r--mm/memcontrol.c84
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
1631static int mem_cgroup_move_account(struct page_cgroup *pc, 1629static 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;
1687out:
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 */
1682static 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 */
1729put_back:
1736 putback_lru_page(page); 1730 putback_lru_page(page);
1737 if (!ret) { 1731put:
1738 put_page(page);
1739 /* drop extra refcnt by try_charge() */
1740 css_put(&parent->css);
1741 return 0;
1742 }
1743
1744cancel:
1745 put_page(page); 1732 put_page(page);
1746uncharge: 1733out:
1747 mem_cgroup_cancel_charge(parent);
1748 return ret; 1734 return ret;
1749} 1735}
1750 1736