]> rtime.felk.cvut.cz Git - can-eth-gw-linux.git/blobdiff - mm/memcontrol.c
memcontrol: use N_MEMORY instead N_HIGH_MEMORY
[can-eth-gw-linux.git] / mm / memcontrol.c
index dd39ba000b31f98730c6fd6d22bdf695ced3bedf..49d86d06e1dd8c58bb7dcb35559bd414837bf37a 100644 (file)
@@ -800,7 +800,7 @@ static unsigned long mem_cgroup_nr_lru_pages(struct mem_cgroup *memcg,
        int nid;
        u64 total = 0;
 
-       for_each_node_state(nid, N_HIGH_MEMORY)
+       for_each_node_state(nid, N_MEMORY)
                total += mem_cgroup_node_nr_lru_pages(memcg, nid, lru_mask);
        return total;
 }
@@ -1498,8 +1498,8 @@ static u64 mem_cgroup_get_limit(struct mem_cgroup *memcg)
        return limit;
 }
 
-void mem_cgroup_out_of_memory(struct mem_cgroup *memcg, gfp_t gfp_mask,
-                             int order)
+static void mem_cgroup_out_of_memory(struct mem_cgroup *memcg, gfp_t gfp_mask,
+                                    int order)
 {
        struct mem_cgroup *iter;
        unsigned long chosen_points = 0;
@@ -1644,9 +1644,9 @@ static void mem_cgroup_may_update_nodemask(struct mem_cgroup *memcg)
                return;
 
        /* make a nodemask where this memcg uses memory from */
-       memcg->scan_nodes = node_states[N_HIGH_MEMORY];
+       memcg->scan_nodes = node_states[N_MEMORY];
 
-       for_each_node_mask(nid, node_states[N_HIGH_MEMORY]) {
+       for_each_node_mask(nid, node_states[N_MEMORY]) {
 
                if (!test_mem_cgroup_node_reclaimable(memcg, nid, false))
                        node_clear(nid, memcg->scan_nodes);
@@ -1717,7 +1717,7 @@ static bool mem_cgroup_reclaimable(struct mem_cgroup *memcg, bool noswap)
        /*
         * Check rest of nodes.
         */
-       for_each_node_state(nid, N_HIGH_MEMORY) {
+       for_each_node_state(nid, N_MEMORY) {
                if (node_isset(nid, memcg->scan_nodes))
                        continue;
                if (test_mem_cgroup_node_reclaimable(memcg, nid, noswap))
@@ -2370,7 +2370,6 @@ static int __mem_cgroup_try_charge(struct mm_struct *mm,
 again:
        if (*ptr) { /* css should be a valid one */
                memcg = *ptr;
-               VM_BUG_ON(css_is_removed(&memcg->css));
                if (mem_cgroup_is_root(memcg))
                        goto done;
                if (nr_pages == 1 && consume_stock(memcg))
@@ -2510,9 +2509,9 @@ static void __mem_cgroup_cancel_local_charge(struct mem_cgroup *memcg,
 
 /*
  * A helper function to get mem_cgroup from ID. must be called under
- * rcu_read_lock(). The caller must check css_is_removed() or some if
- * it's concern. (dropping refcnt from swap can be called against removed
- * memcg.)
+ * rcu_read_lock().  The caller is responsible for calling css_tryget if
+ * the mem_cgroup is used for charging. (dropping refcnt from swap can be
+ * called against removed memcg.)
  */
 static struct mem_cgroup *mem_cgroup_lookup(unsigned short id)
 {
@@ -2709,13 +2708,6 @@ static int mem_cgroup_move_account(struct page *page,
        /* caller should have done css_get */
        pc->mem_cgroup = to;
        mem_cgroup_charge_statistics(to, anon, nr_pages);
-       /*
-        * We charges against "to" which may not have any tasks. Then, "to"
-        * can be under rmdir(). But in current implementation, caller of
-        * this function is just force_empty() and move charge, so it's
-        * guaranteed that "to" is never removed. So, we don't check rmdir
-        * status here.
-        */
        move_unlock_mem_cgroup(from, &flags);
        ret = 0;
 unlock:
@@ -2729,10 +2721,27 @@ out:
        return ret;
 }
 
-/*
- * move charges to its parent.
+/**
+ * mem_cgroup_move_parent - moves page to the parent group
+ * @page: the page to move
+ * @pc: page_cgroup of the page
+ * @child: page's cgroup
+ *
+ * move charges to its parent or the root cgroup if the group has no
+ * parent (aka use_hierarchy==0).
+ * Although this might fail (get_page_unless_zero, isolate_lru_page or
+ * mem_cgroup_move_account fails) the failure is always temporary and
+ * it signals a race with a page removal/uncharge or migration. In the
+ * first case the page is on the way out and it will vanish from the LRU
+ * on the next attempt and the call should be retried later.
+ * Isolation from the LRU fails only if page has been isolated from
+ * the LRU since we looked at it and that usually means either global
+ * reclaim or migration going on. The page will either get back to the
+ * LRU or vanish.
+ * Finaly mem_cgroup_move_account fails only if the page got uncharged
+ * (!PageCgroupUsed) or moved to a different group. The page will
+ * disappear in the next attempt.
  */
-
 static int mem_cgroup_move_parent(struct page *page,
                                  struct page_cgroup *pc,
                                  struct mem_cgroup *child)
@@ -2742,9 +2751,7 @@ static int mem_cgroup_move_parent(struct page *page,
        unsigned long uninitialized_var(flags);
        int ret;
 
-       /* Is ROOT ? */
-       if (mem_cgroup_is_root(child))
-               return -EINVAL;
+       VM_BUG_ON(mem_cgroup_is_root(child));
 
        ret = -EBUSY;
        if (!get_page_unless_zero(page))
@@ -2761,8 +2768,10 @@ static int mem_cgroup_move_parent(struct page *page,
        if (!parent)
                parent = root_mem_cgroup;
 
-       if (nr_pages > 1)
+       if (nr_pages > 1) {
+               VM_BUG_ON(!PageTransHuge(page));
                flags = compound_lock_irqsave(page);
+       }
 
        ret = mem_cgroup_move_account(page, nr_pages,
                                pc, child, parent);
@@ -2904,7 +2913,6 @@ __mem_cgroup_commit_charge_swapin(struct page *page, struct mem_cgroup *memcg,
                return;
        if (!memcg)
                return;
-       cgroup_exclude_rmdir(&memcg->css);
 
        __mem_cgroup_commit_charge(memcg, page, 1, ctype, true);
        /*
@@ -2918,12 +2926,6 @@ __mem_cgroup_commit_charge_swapin(struct page *page, struct mem_cgroup *memcg,
                swp_entry_t ent = {.val = page_private(page)};
                mem_cgroup_uncharge_swap(ent);
        }
-       /*
-        * At swapin, we may charge account against cgroup which has no tasks.
-        * So, rmdir()->pre_destroy() can be called while we do this charge.
-        * In that case, we need to call pre_destroy() again. check it here.
-        */
-       cgroup_release_and_wakeup_rmdir(&memcg->css);
 }
 
 void mem_cgroup_commit_charge_swapin(struct page *page,
@@ -3371,8 +3373,7 @@ void mem_cgroup_end_migration(struct mem_cgroup *memcg,
 
        if (!memcg)
                return;
-       /* blocks rmdir() */
-       cgroup_exclude_rmdir(&memcg->css);
+
        if (!migration_ok) {
                used = oldpage;
                unused = newpage;
@@ -3406,13 +3407,6 @@ void mem_cgroup_end_migration(struct mem_cgroup *memcg,
         */
        if (anon)
                mem_cgroup_uncharge_page(used);
-       /*
-        * At migration, we may charge account against cgroup which has no
-        * tasks.
-        * So, rmdir()->pre_destroy() can be called while we do this charge.
-        * In that case, we need to call pre_destroy() again. check it here.
-        */
-       cgroup_release_and_wakeup_rmdir(&memcg->css);
 }
 
 /*
@@ -3712,17 +3706,22 @@ unsigned long mem_cgroup_soft_limit_reclaim(struct zone *zone, int order,
        return nr_reclaimed;
 }
 
-/*
+/**
+ * mem_cgroup_force_empty_list - clears LRU of a group
+ * @memcg: group to clear
+ * @node: NUMA node
+ * @zid: zone id
+ * @lru: lru to to clear
+ *
  * Traverse a specified page_cgroup list and try to drop them all.  This doesn't
- * reclaim the pages page themselves - it just removes the page_cgroups.
- * Returns true if some page_cgroups were not freed, indicating that the caller
- * must retry this operation.
+ * reclaim the pages page themselves - pages are moved to the parent (or root)
+ * group.
  */
-static bool mem_cgroup_force_empty_list(struct mem_cgroup *memcg,
+static void mem_cgroup_force_empty_list(struct mem_cgroup *memcg,
                                int node, int zid, enum lru_list lru)
 {
        struct lruvec *lruvec;
-       unsigned long flags, loop;
+       unsigned long flags;
        struct list_head *list;
        struct page *busy;
        struct zone *zone;
@@ -3731,11 +3730,8 @@ static bool mem_cgroup_force_empty_list(struct mem_cgroup *memcg,
        lruvec = mem_cgroup_zone_lruvec(zone, memcg);
        list = &lruvec->lists[lru];
 
-       loop = mem_cgroup_get_lru_size(lruvec, lru);
-       /* give some margin against EBUSY etc...*/
-       loop += 256;
        busy = NULL;
-       while (loop--) {
+       do {
                struct page_cgroup *pc;
                struct page *page;
 
@@ -3761,76 +3757,72 @@ static bool mem_cgroup_force_empty_list(struct mem_cgroup *memcg,
                        cond_resched();
                } else
                        busy = NULL;
-       }
-       return !list_empty(list);
+       } while (!list_empty(list));
 }
 
 /*
- * make mem_cgroup's charge to be 0 if there is no task.
+ * make mem_cgroup's charge to be 0 if there is no task by moving
+ * all the charges and pages to the parent.
  * This enables deleting this mem_cgroup.
+ *
+ * Caller is responsible for holding css reference on the memcg.
  */
-static int mem_cgroup_force_empty(struct mem_cgroup *memcg, bool free_all)
+static void mem_cgroup_reparent_charges(struct mem_cgroup *memcg)
 {
-       int ret;
-       int node, zid, shrink;
-       int nr_retries = MEM_CGROUP_RECLAIM_RETRIES;
-       struct cgroup *cgrp = memcg->css.cgroup;
-
-       css_get(&memcg->css);
+       int node, zid;
 
-       shrink = 0;
-       /* should free all ? */
-       if (free_all)
-               goto try_to_free;
-move_account:
        do {
-               ret = -EBUSY;
-               if (cgroup_task_count(cgrp) || !list_empty(&cgrp->children))
-                       goto out;
                /* This is for making all *used* pages to be on LRU. */
                lru_add_drain_all();
                drain_all_stock_sync(memcg);
-               ret = 0;
                mem_cgroup_start_move(memcg);
-               for_each_node_state(node, N_HIGH_MEMORY) {
-                       for (zid = 0; !ret && zid < MAX_NR_ZONES; zid++) {
+               for_each_node_state(node, N_MEMORY) {
+                       for (zid = 0; zid < MAX_NR_ZONES; zid++) {
                                enum lru_list lru;
                                for_each_lru(lru) {
-                                       ret = mem_cgroup_force_empty_list(memcg,
+                                       mem_cgroup_force_empty_list(memcg,
                                                        node, zid, lru);
-                                       if (ret)
-                                               break;
                                }
                        }
-                       if (ret)
-                               break;
                }
                mem_cgroup_end_move(memcg);
                memcg_oom_recover(memcg);
                cond_resched();
-       /* "ret" should also be checked to ensure all lists are empty. */
-       } while (res_counter_read_u64(&memcg->res, RES_USAGE) > 0 || ret);
-out:
-       css_put(&memcg->css);
-       return ret;
 
-try_to_free:
+               /*
+                * This is a safety check because mem_cgroup_force_empty_list
+                * could have raced with mem_cgroup_replace_page_cache callers
+                * so the lru seemed empty but the page could have been added
+                * right after the check. RES_USAGE should be safe as we always
+                * charge before adding to the LRU.
+                */
+       } while (res_counter_read_u64(&memcg->res, RES_USAGE) > 0);
+}
+
+/*
+ * Reclaims as many pages from the given memcg as possible and moves
+ * the rest to the parent.
+ *
+ * Caller is responsible for holding css reference for memcg.
+ */
+static int mem_cgroup_force_empty(struct mem_cgroup *memcg)
+{
+       int nr_retries = MEM_CGROUP_RECLAIM_RETRIES;
+       struct cgroup *cgrp = memcg->css.cgroup;
+
        /* returns EBUSY if there is a task or if we come here twice. */
-       if (cgroup_task_count(cgrp) || !list_empty(&cgrp->children) || shrink) {
-               ret = -EBUSY;
-               goto out;
-       }
+       if (cgroup_task_count(cgrp) || !list_empty(&cgrp->children))
+               return -EBUSY;
+
        /* we call try-to-free pages for make this cgroup empty */
        lru_add_drain_all();
        /* try to free all pages in this cgroup */
-       shrink = 1;
        while (nr_retries && res_counter_read_u64(&memcg->res, RES_USAGE) > 0) {
                int progress;
 
-               if (signal_pending(current)) {
-                       ret = -EINTR;
-                       goto out;
-               }
+               if (signal_pending(current))
+                       return -EINTR;
+
                progress = try_to_free_mem_cgroup_pages(memcg, GFP_KERNEL,
                                                false);
                if (!progress) {
@@ -3841,13 +3833,23 @@ try_to_free:
 
        }
        lru_add_drain();
-       /* try move_account...there may be some *locked* pages. */
-       goto move_account;
+       mem_cgroup_reparent_charges(memcg);
+
+       return 0;
 }
 
 static int mem_cgroup_force_empty_write(struct cgroup *cont, unsigned int event)
 {
-       return mem_cgroup_force_empty(mem_cgroup_from_cont(cont), true);
+       struct mem_cgroup *memcg = mem_cgroup_from_cont(cont);
+       int ret;
+
+       if (mem_cgroup_is_root(memcg))
+               return -EINVAL;
+       css_get(&memcg->css);
+       ret = mem_cgroup_force_empty(memcg);
+       css_put(&memcg->css);
+
+       return ret;
 }
 
 
@@ -4120,7 +4122,7 @@ static int memcg_numa_stat_show(struct cgroup *cont, struct cftype *cft,
 
        total_nr = mem_cgroup_nr_lru_pages(memcg, LRU_ALL);
        seq_printf(m, "total=%lu", total_nr);
-       for_each_node_state(nid, N_HIGH_MEMORY) {
+       for_each_node_state(nid, N_MEMORY) {
                node_nr = mem_cgroup_node_nr_lru_pages(memcg, nid, LRU_ALL);
                seq_printf(m, " N%d=%lu", nid, node_nr);
        }
@@ -4128,7 +4130,7 @@ static int memcg_numa_stat_show(struct cgroup *cont, struct cftype *cft,
 
        file_nr = mem_cgroup_nr_lru_pages(memcg, LRU_ALL_FILE);
        seq_printf(m, "file=%lu", file_nr);
-       for_each_node_state(nid, N_HIGH_MEMORY) {
+       for_each_node_state(nid, N_MEMORY) {
                node_nr = mem_cgroup_node_nr_lru_pages(memcg, nid,
                                LRU_ALL_FILE);
                seq_printf(m, " N%d=%lu", nid, node_nr);
@@ -4137,7 +4139,7 @@ static int memcg_numa_stat_show(struct cgroup *cont, struct cftype *cft,
 
        anon_nr = mem_cgroup_nr_lru_pages(memcg, LRU_ALL_ANON);
        seq_printf(m, "anon=%lu", anon_nr);
-       for_each_node_state(nid, N_HIGH_MEMORY) {
+       for_each_node_state(nid, N_MEMORY) {
                node_nr = mem_cgroup_node_nr_lru_pages(memcg, nid,
                                LRU_ALL_ANON);
                seq_printf(m, " N%d=%lu", nid, node_nr);
@@ -4146,7 +4148,7 @@ static int memcg_numa_stat_show(struct cgroup *cont, struct cftype *cft,
 
        unevictable_nr = mem_cgroup_nr_lru_pages(memcg, BIT(LRU_UNEVICTABLE));
        seq_printf(m, "unevictable=%lu", unevictable_nr);
-       for_each_node_state(nid, N_HIGH_MEMORY) {
+       for_each_node_state(nid, N_MEMORY) {
                node_nr = mem_cgroup_node_nr_lru_pages(memcg, nid,
                                BIT(LRU_UNEVICTABLE));
                seq_printf(m, " N%d=%lu", nid, node_nr);
@@ -4953,7 +4955,7 @@ err_cleanup:
 }
 
 static struct cgroup_subsys_state * __ref
-mem_cgroup_create(struct cgroup *cont)
+mem_cgroup_css_alloc(struct cgroup *cont)
 {
        struct mem_cgroup *memcg, *parent;
        long error = -ENOMEM;
@@ -5034,14 +5036,14 @@ free_out:
        return ERR_PTR(error);
 }
 
-static int mem_cgroup_pre_destroy(struct cgroup *cont)
+static void mem_cgroup_css_offline(struct cgroup *cont)
 {
        struct mem_cgroup *memcg = mem_cgroup_from_cont(cont);
 
-       return mem_cgroup_force_empty(memcg, false);
+       mem_cgroup_reparent_charges(memcg);
 }
 
-static void mem_cgroup_destroy(struct cgroup *cont)
+static void mem_cgroup_css_free(struct cgroup *cont)
 {
        struct mem_cgroup *memcg = mem_cgroup_from_cont(cont);
 
@@ -5631,16 +5633,15 @@ static void mem_cgroup_move_task(struct cgroup *cont,
 struct cgroup_subsys mem_cgroup_subsys = {
        .name = "memory",
        .subsys_id = mem_cgroup_subsys_id,
-       .create = mem_cgroup_create,
-       .pre_destroy = mem_cgroup_pre_destroy,
-       .destroy = mem_cgroup_destroy,
+       .css_alloc = mem_cgroup_css_alloc,
+       .css_offline = mem_cgroup_css_offline,
+       .css_free = mem_cgroup_css_free,
        .can_attach = mem_cgroup_can_attach,
        .cancel_attach = mem_cgroup_cancel_attach,
        .attach = mem_cgroup_move_task,
        .base_cftypes = mem_cgroup_files,
        .early_init = 0,
        .use_id = 1,
-       .__DEPRECATED_clear_css_refs = true,
 };
 
 #ifdef CONFIG_MEMCG_SWAP