mm/memcontrol.c: try harder to decrease [memory,memsw].limit_in_bytes
[linux-2.6-block.git] / mm / memcontrol.c
index ac2ffd5e02b914fb9564649c9475babc51119de6..0ae2dc3a17486de390201e7327c0ef75dd7c6639 100644 (file)
@@ -542,39 +542,10 @@ mem_cgroup_largest_soft_limit_node(struct mem_cgroup_tree_per_node *mctz)
        return mz;
 }
 
-/*
- * Return page count for single (non recursive) @memcg.
- *
- * Implementation Note: reading percpu statistics for memcg.
- *
- * Both of vmstat[] and percpu_counter has threshold and do periodic
- * synchronization to implement "quick" read. There are trade-off between
- * reading cost and precision of value. Then, we may have a chance to implement
- * a periodic synchronization of counter in memcg's counter.
- *
- * But this _read() function is used for user interface now. The user accounts
- * memory usage by memory cgroup and he _always_ requires exact value because
- * he accounts memory. Even if we provide quick-and-fuzzy read, we always
- * have to visit all online cpus and make sum. So, for now, unnecessary
- * synchronization is not implemented. (just implemented for cpu hotplug)
- *
- * If there are kernel internal actions which can make use of some not-exact
- * value, and reading all cpu value can be performance bottleneck in some
- * common workload, threshold and synchronization as vmstat[] should be
- * implemented.
- *
- * The parameter idx can be of type enum memcg_event_item or vm_event_item.
- */
-
 static unsigned long memcg_sum_events(struct mem_cgroup *memcg,
                                      int event)
 {
-       unsigned long val = 0;
-       int cpu;
-
-       for_each_possible_cpu(cpu)
-               val += per_cpu(memcg->stat->events[event], cpu);
-       return val;
+       return atomic_long_read(&memcg->events[event]);
 }
 
 static void mem_cgroup_charge_statistics(struct mem_cgroup *memcg,
@@ -586,27 +557,27 @@ static void mem_cgroup_charge_statistics(struct mem_cgroup *memcg,
         * counted as CACHE even if it's on ANON LRU.
         */
        if (PageAnon(page))
-               __this_cpu_add(memcg->stat->count[MEMCG_RSS], nr_pages);
+               __mod_memcg_state(memcg, MEMCG_RSS, nr_pages);
        else {
-               __this_cpu_add(memcg->stat->count[MEMCG_CACHE], nr_pages);
+               __mod_memcg_state(memcg, MEMCG_CACHE, nr_pages);
                if (PageSwapBacked(page))
-                       __this_cpu_add(memcg->stat->count[NR_SHMEM], nr_pages);
+                       __mod_memcg_state(memcg, NR_SHMEM, nr_pages);
        }
 
        if (compound) {
                VM_BUG_ON_PAGE(!PageTransHuge(page), page);
-               __this_cpu_add(memcg->stat->count[MEMCG_RSS_HUGE], nr_pages);
+               __mod_memcg_state(memcg, MEMCG_RSS_HUGE, nr_pages);
        }
 
        /* pagein of a big page is an event. So, ignore page size */
        if (nr_pages > 0)
-               __this_cpu_inc(memcg->stat->events[PGPGIN]);
+               __count_memcg_events(memcg, PGPGIN, 1);
        else {
-               __this_cpu_inc(memcg->stat->events[PGPGOUT]);
+               __count_memcg_events(memcg, PGPGOUT, 1);
                nr_pages = -nr_pages; /* for event */
        }
 
-       __this_cpu_add(memcg->stat->nr_page_events, nr_pages);
+       __this_cpu_add(memcg->stat_cpu->nr_page_events, nr_pages);
 }
 
 unsigned long mem_cgroup_node_nr_lru_pages(struct mem_cgroup *memcg,
@@ -642,8 +613,8 @@ static bool mem_cgroup_event_ratelimit(struct mem_cgroup *memcg,
 {
        unsigned long val, next;
 
-       val = __this_cpu_read(memcg->stat->nr_page_events);
-       next = __this_cpu_read(memcg->stat->targets[target]);
+       val = __this_cpu_read(memcg->stat_cpu->nr_page_events);
+       next = __this_cpu_read(memcg->stat_cpu->targets[target]);
        /* from time_after() in jiffies.h */
        if ((long)(next - val) < 0) {
                switch (target) {
@@ -659,7 +630,7 @@ static bool mem_cgroup_event_ratelimit(struct mem_cgroup *memcg,
                default:
                        break;
                }
-               __this_cpu_write(memcg->stat->targets[target], next);
+               __this_cpu_write(memcg->stat_cpu->targets[target], next);
                return true;
        }
        return false;
@@ -1124,7 +1095,7 @@ static bool mem_cgroup_wait_acct_move(struct mem_cgroup *memcg)
        return false;
 }
 
-unsigned int memcg1_stats[] = {
+static const unsigned int memcg1_stats[] = {
        MEMCG_CACHE,
        MEMCG_RSS,
        MEMCG_RSS_HUGE,
@@ -1205,20 +1176,6 @@ void mem_cgroup_print_oom_info(struct mem_cgroup *memcg, struct task_struct *p)
        }
 }
 
-/*
- * This function returns the number of memcg under hierarchy tree. Returns
- * 1(self count) if no children.
- */
-static int mem_cgroup_count_children(struct mem_cgroup *memcg)
-{
-       int num = 0;
-       struct mem_cgroup *iter;
-
-       for_each_mem_cgroup_tree(iter, memcg)
-               num++;
-       return num;
-}
-
 /*
  * Return the memory (and swap, if configured) limit for a memcg.
  */
@@ -1707,11 +1664,6 @@ void unlock_page_memcg(struct page *page)
 }
 EXPORT_SYMBOL(unlock_page_memcg);
 
-/*
- * size of first charge trial. "32" comes from vmscan.c's magic value.
- * TODO: maybe necessary to use big numbers in big irons.
- */
-#define CHARGE_BATCH   32U
 struct memcg_stock_pcp {
        struct mem_cgroup *cached; /* this never be root cgroup */
        unsigned int nr_pages;
@@ -1739,7 +1691,7 @@ static bool consume_stock(struct mem_cgroup *memcg, unsigned int nr_pages)
        unsigned long flags;
        bool ret = false;
 
-       if (nr_pages > CHARGE_BATCH)
+       if (nr_pages > MEMCG_CHARGE_BATCH)
                return ret;
 
        local_irq_save(flags);
@@ -1808,7 +1760,7 @@ static void refill_stock(struct mem_cgroup *memcg, unsigned int nr_pages)
        }
        stock->nr_pages += nr_pages;
 
-       if (stock->nr_pages > CHARGE_BATCH)
+       if (stock->nr_pages > MEMCG_CHARGE_BATCH)
                drain_stock(stock);
 
        local_irq_restore(flags);
@@ -1858,9 +1810,44 @@ static void drain_all_stock(struct mem_cgroup *root_memcg)
 static int memcg_hotplug_cpu_dead(unsigned int cpu)
 {
        struct memcg_stock_pcp *stock;
+       struct mem_cgroup *memcg;
 
        stock = &per_cpu(memcg_stock, cpu);
        drain_stock(stock);
+
+       for_each_mem_cgroup(memcg) {
+               int i;
+
+               for (i = 0; i < MEMCG_NR_STAT; i++) {
+                       int nid;
+                       long x;
+
+                       x = this_cpu_xchg(memcg->stat_cpu->count[i], 0);
+                       if (x)
+                               atomic_long_add(x, &memcg->stat[i]);
+
+                       if (i >= NR_VM_NODE_STAT_ITEMS)
+                               continue;
+
+                       for_each_node(nid) {
+                               struct mem_cgroup_per_node *pn;
+
+                               pn = mem_cgroup_nodeinfo(memcg, nid);
+                               x = this_cpu_xchg(pn->lruvec_stat_cpu->count[i], 0);
+                               if (x)
+                                       atomic_long_add(x, &pn->lruvec_stat[i]);
+                       }
+               }
+
+               for (i = 0; i < MEMCG_NR_EVENTS; i++) {
+                       long x;
+
+                       x = this_cpu_xchg(memcg->stat_cpu->events[i], 0);
+                       if (x)
+                               atomic_long_add(x, &memcg->events[i]);
+               }
+       }
+
        return 0;
 }
 
@@ -1881,7 +1868,7 @@ static void high_work_func(struct work_struct *work)
        struct mem_cgroup *memcg;
 
        memcg = container_of(work, struct mem_cgroup, high_work);
-       reclaim_high(memcg, CHARGE_BATCH, GFP_KERNEL);
+       reclaim_high(memcg, MEMCG_CHARGE_BATCH, GFP_KERNEL);
 }
 
 /*
@@ -1905,7 +1892,7 @@ void mem_cgroup_handle_over_high(void)
 static int try_charge(struct mem_cgroup *memcg, gfp_t gfp_mask,
                      unsigned int nr_pages)
 {
-       unsigned int batch = max(CHARGE_BATCH, nr_pages);
+       unsigned int batch = max(MEMCG_CHARGE_BATCH, nr_pages);
        int nr_retries = MEM_CGROUP_RECLAIM_RETRIES;
        struct mem_cgroup *mem_over_limit;
        struct page_counter *counter;
@@ -2415,18 +2402,11 @@ void mem_cgroup_split_huge_fixup(struct page *head)
        for (i = 1; i < HPAGE_PMD_NR; i++)
                head[i].mem_cgroup = head->mem_cgroup;
 
-       __this_cpu_sub(head->mem_cgroup->stat->count[MEMCG_RSS_HUGE],
-                      HPAGE_PMD_NR);
+       __mod_memcg_state(head->mem_cgroup, MEMCG_RSS_HUGE, -HPAGE_PMD_NR);
 }
 #endif /* CONFIG_TRANSPARENT_HUGEPAGE */
 
 #ifdef CONFIG_MEMCG_SWAP
-static void mem_cgroup_swap_statistics(struct mem_cgroup *memcg,
-                                      int nr_entries)
-{
-       this_cpu_add(memcg->stat->count[MEMCG_SWAP], nr_entries);
-}
-
 /**
  * mem_cgroup_move_swap_account - move swap charge and swap_cgroup's record.
  * @entry: swap entry to be moved
@@ -2450,8 +2430,8 @@ static int mem_cgroup_move_swap_account(swp_entry_t entry,
        new_id = mem_cgroup_id(to);
 
        if (swap_cgroup_cmpxchg(entry, old_id, new_id) == old_id) {
-               mem_cgroup_swap_statistics(from, -1);
-               mem_cgroup_swap_statistics(to, 1);
+               mod_memcg_state(from, MEMCG_SWAP, -1);
+               mod_memcg_state(to, MEMCG_SWAP, 1);
                return 0;
        }
        return -EINVAL;
@@ -2467,23 +2447,12 @@ static inline int mem_cgroup_move_swap_account(swp_entry_t entry,
 static DEFINE_MUTEX(memcg_limit_mutex);
 
 static int mem_cgroup_resize_limit(struct mem_cgroup *memcg,
-                                  unsigned long limit)
+                                  unsigned long limit, bool memsw)
 {
-       unsigned long curusage;
-       unsigned long oldusage;
        bool enlarge = false;
-       int retry_count;
        int ret;
-
-       /*
-        * For keeping hierarchical_reclaim simple, how long we should retry
-        * is depends on callers. We set our retry-count to be function
-        * of # of children which we should visit in this loop.
-        */
-       retry_count = MEM_CGROUP_RECLAIM_RETRIES *
-                     mem_cgroup_count_children(memcg);
-
-       oldusage = page_counter_read(&memcg->memory);
+       bool limits_invariant;
+       struct page_counter *counter = memsw ? &memcg->memsw : &memcg->memory;
 
        do {
                if (signal_pending(current)) {
@@ -2492,79 +2461,31 @@ static int mem_cgroup_resize_limit(struct mem_cgroup *memcg,
                }
 
                mutex_lock(&memcg_limit_mutex);
-               if (limit > memcg->memsw.limit) {
+               /*
+                * Make sure that the new limit (memsw or memory limit) doesn't
+                * break our basic invariant rule memory.limit <= memsw.limit.
+                */
+               limits_invariant = memsw ? limit >= memcg->memory.limit :
+                                          limit <= memcg->memsw.limit;
+               if (!limits_invariant) {
                        mutex_unlock(&memcg_limit_mutex);
                        ret = -EINVAL;
                        break;
                }
-               if (limit > memcg->memory.limit)
+               if (limit > counter->limit)
                        enlarge = true;
-               ret = page_counter_limit(&memcg->memory, limit);
+               ret = page_counter_limit(counter, limit);
                mutex_unlock(&memcg_limit_mutex);
 
                if (!ret)
                        break;
 
-               try_to_free_mem_cgroup_pages(memcg, 1, GFP_KERNEL, true);
-
-               curusage = page_counter_read(&memcg->memory);
-               /* Usage is reduced ? */
-               if (curusage >= oldusage)
-                       retry_count--;
-               else
-                       oldusage = curusage;
-       } while (retry_count);
-
-       if (!ret && enlarge)
-               memcg_oom_recover(memcg);
-
-       return ret;
-}
-
-static int mem_cgroup_resize_memsw_limit(struct mem_cgroup *memcg,
-                                        unsigned long limit)
-{
-       unsigned long curusage;
-       unsigned long oldusage;
-       bool enlarge = false;
-       int retry_count;
-       int ret;
-
-       /* see mem_cgroup_resize_res_limit */
-       retry_count = MEM_CGROUP_RECLAIM_RETRIES *
-                     mem_cgroup_count_children(memcg);
-
-       oldusage = page_counter_read(&memcg->memsw);
-
-       do {
-               if (signal_pending(current)) {
-                       ret = -EINTR;
+               if (!try_to_free_mem_cgroup_pages(memcg, 1,
+                                       GFP_KERNEL, !memsw)) {
+                       ret = -EBUSY;
                        break;
                }
-
-               mutex_lock(&memcg_limit_mutex);
-               if (limit < memcg->memory.limit) {
-                       mutex_unlock(&memcg_limit_mutex);
-                       ret = -EINVAL;
-                       break;
-               }
-               if (limit > memcg->memsw.limit)
-                       enlarge = true;
-               ret = page_counter_limit(&memcg->memsw, limit);
-               mutex_unlock(&memcg_limit_mutex);
-
-               if (!ret)
-                       break;
-
-               try_to_free_mem_cgroup_pages(memcg, 1, GFP_KERNEL, false);
-
-               curusage = page_counter_read(&memcg->memsw);
-               /* Usage is reduced ? */
-               if (curusage >= oldusage)
-                       retry_count--;
-               else
-                       oldusage = curusage;
-       } while (retry_count);
+       } while (true);
 
        if (!ret && enlarge)
                memcg_oom_recover(memcg);
@@ -3020,10 +2941,10 @@ static ssize_t mem_cgroup_write(struct kernfs_open_file *of,
                }
                switch (MEMFILE_TYPE(of_cft(of)->private)) {
                case _MEM:
-                       ret = mem_cgroup_resize_limit(memcg, nr_pages);
+                       ret = mem_cgroup_resize_limit(memcg, nr_pages, false);
                        break;
                case _MEMSWAP:
-                       ret = mem_cgroup_resize_memsw_limit(memcg, nr_pages);
+                       ret = mem_cgroup_resize_limit(memcg, nr_pages, true);
                        break;
                case _KMEM:
                        ret = memcg_update_kmem_limit(memcg, nr_pages);
@@ -3777,7 +3698,7 @@ static int memcg_event_wake(wait_queue_entry_t *wait, unsigned mode,
        struct mem_cgroup_event *event =
                container_of(wait, struct mem_cgroup_event, wait);
        struct mem_cgroup *memcg = event->memcg;
-       unsigned long flags = (unsigned long)key;
+       __poll_t flags = key_to_poll(key);
 
        if (flags & POLLHUP) {
                /*
@@ -4168,8 +4089,8 @@ static int alloc_mem_cgroup_per_node_info(struct mem_cgroup *memcg, int node)
        if (!pn)
                return 1;
 
-       pn->lruvec_stat = alloc_percpu(struct lruvec_stat);
-       if (!pn->lruvec_stat) {
+       pn->lruvec_stat_cpu = alloc_percpu(struct lruvec_stat);
+       if (!pn->lruvec_stat_cpu) {
                kfree(pn);
                return 1;
        }
@@ -4187,7 +4108,7 @@ static void free_mem_cgroup_per_node_info(struct mem_cgroup *memcg, int node)
 {
        struct mem_cgroup_per_node *pn = memcg->nodeinfo[node];
 
-       free_percpu(pn->lruvec_stat);
+       free_percpu(pn->lruvec_stat_cpu);
        kfree(pn);
 }
 
@@ -4197,7 +4118,7 @@ static void __mem_cgroup_free(struct mem_cgroup *memcg)
 
        for_each_node(node)
                free_mem_cgroup_per_node_info(memcg, node);
-       free_percpu(memcg->stat);
+       free_percpu(memcg->stat_cpu);
        kfree(memcg);
 }
 
@@ -4226,8 +4147,8 @@ static struct mem_cgroup *mem_cgroup_alloc(void)
        if (memcg->id.id < 0)
                goto fail;
 
-       memcg->stat = alloc_percpu(struct mem_cgroup_stat_cpu);
-       if (!memcg->stat)
+       memcg->stat_cpu = alloc_percpu(struct mem_cgroup_stat_cpu);
+       if (!memcg->stat_cpu)
                goto fail;
 
        for_each_node(node)
@@ -4584,8 +4505,8 @@ static int mem_cgroup_move_account(struct page *page,
        spin_lock_irqsave(&from->move_lock, flags);
 
        if (!anon && page_mapped(page)) {
-               __this_cpu_sub(from->stat->count[NR_FILE_MAPPED], nr_pages);
-               __this_cpu_add(to->stat->count[NR_FILE_MAPPED], nr_pages);
+               __mod_memcg_state(from, NR_FILE_MAPPED, -nr_pages);
+               __mod_memcg_state(to, NR_FILE_MAPPED, nr_pages);
        }
 
        /*
@@ -4597,16 +4518,14 @@ static int mem_cgroup_move_account(struct page *page,
                struct address_space *mapping = page_mapping(page);
 
                if (mapping_cap_account_dirty(mapping)) {
-                       __this_cpu_sub(from->stat->count[NR_FILE_DIRTY],
-                                      nr_pages);
-                       __this_cpu_add(to->stat->count[NR_FILE_DIRTY],
-                                      nr_pages);
+                       __mod_memcg_state(from, NR_FILE_DIRTY, -nr_pages);
+                       __mod_memcg_state(to, NR_FILE_DIRTY, nr_pages);
                }
        }
 
        if (PageWriteback(page)) {
-               __this_cpu_sub(from->stat->count[NR_WRITEBACK], nr_pages);
-               __this_cpu_add(to->stat->count[NR_WRITEBACK], nr_pages);
+               __mod_memcg_state(from, NR_WRITEBACK, -nr_pages);
+               __mod_memcg_state(to, NR_WRITEBACK, nr_pages);
        }
 
        /*
@@ -5642,12 +5561,12 @@ static void uncharge_batch(const struct uncharge_gather *ug)
        }
 
        local_irq_save(flags);
-       __this_cpu_sub(ug->memcg->stat->count[MEMCG_RSS], ug->nr_anon);
-       __this_cpu_sub(ug->memcg->stat->count[MEMCG_CACHE], ug->nr_file);
-       __this_cpu_sub(ug->memcg->stat->count[MEMCG_RSS_HUGE], ug->nr_huge);
-       __this_cpu_sub(ug->memcg->stat->count[NR_SHMEM], ug->nr_shmem);
-       __this_cpu_add(ug->memcg->stat->events[PGPGOUT], ug->pgpgout);
-       __this_cpu_add(ug->memcg->stat->nr_page_events, nr_pages);
+       __mod_memcg_state(ug->memcg, MEMCG_RSS, -ug->nr_anon);
+       __mod_memcg_state(ug->memcg, MEMCG_CACHE, -ug->nr_file);
+       __mod_memcg_state(ug->memcg, MEMCG_RSS_HUGE, -ug->nr_huge);
+       __mod_memcg_state(ug->memcg, NR_SHMEM, -ug->nr_shmem);
+       __count_memcg_events(ug->memcg, PGPGOUT, ug->pgpgout);
+       __this_cpu_add(ug->memcg->stat_cpu->nr_page_events, nr_pages);
        memcg_check_events(ug->memcg, ug->dummy_page);
        local_irq_restore(flags);
 
@@ -5874,7 +5793,7 @@ bool mem_cgroup_charge_skmem(struct mem_cgroup *memcg, unsigned int nr_pages)
        if (in_softirq())
                gfp_mask = GFP_NOWAIT;
 
-       this_cpu_add(memcg->stat->count[MEMCG_SOCK], nr_pages);
+       mod_memcg_state(memcg, MEMCG_SOCK, nr_pages);
 
        if (try_charge(memcg, gfp_mask, nr_pages) == 0)
                return true;
@@ -5895,7 +5814,7 @@ void mem_cgroup_uncharge_skmem(struct mem_cgroup *memcg, unsigned int nr_pages)
                return;
        }
 
-       this_cpu_sub(memcg->stat->count[MEMCG_SOCK], nr_pages);
+       mod_memcg_state(memcg, MEMCG_SOCK, -nr_pages);
 
        refill_stock(memcg, nr_pages);
 }
@@ -6019,7 +5938,7 @@ void mem_cgroup_swapout(struct page *page, swp_entry_t entry)
        oldid = swap_cgroup_record(entry, mem_cgroup_id(swap_memcg),
                                   nr_entries);
        VM_BUG_ON_PAGE(oldid, page);
-       mem_cgroup_swap_statistics(swap_memcg, nr_entries);
+       mod_memcg_state(swap_memcg, MEMCG_SWAP, nr_entries);
 
        page->mem_cgroup = NULL;
 
@@ -6085,7 +6004,7 @@ int mem_cgroup_try_charge_swap(struct page *page, swp_entry_t entry)
                mem_cgroup_id_get_many(memcg, nr_pages - 1);
        oldid = swap_cgroup_record(entry, mem_cgroup_id(memcg), nr_pages);
        VM_BUG_ON_PAGE(oldid, page);
-       mem_cgroup_swap_statistics(memcg, nr_pages);
+       mod_memcg_state(memcg, MEMCG_SWAP, nr_pages);
 
        return 0;
 }
@@ -6113,7 +6032,7 @@ void mem_cgroup_uncharge_swap(swp_entry_t entry, unsigned int nr_pages)
                        else
                                page_counter_uncharge(&memcg->memsw, nr_pages);
                }
-               mem_cgroup_swap_statistics(memcg, -nr_pages);
+               mod_memcg_state(memcg, MEMCG_SWAP, -nr_pages);
                mem_cgroup_id_put_many(memcg, nr_pages);
        }
        rcu_read_unlock();