NFSv4: ACL in operations 'open' and 'create' should be used
[safe/jmp/linux-2.6] / mm / memcontrol.c
index 6f68290..e2fa20d 100644 (file)
@@ -45,7 +45,7 @@ struct cgroup_subsys mem_cgroup_subsys __read_mostly;
 #define MEM_CGROUP_RECLAIM_RETRIES     5
 
 #ifdef CONFIG_CGROUP_MEM_RES_CTLR_SWAP
-/* Turned on only when memory cgroup is enabled && really_do_swap_account = 0 */
+/* Turned on only when memory cgroup is enabled && really_do_swap_account = 1 */
 int do_swap_account __read_mostly;
 static int really_do_swap_account __initdata = 1; /* for remember boot option*/
 #else
@@ -177,6 +177,9 @@ struct mem_cgroup {
 
        unsigned int    swappiness;
 
+       /* set when res.limit == memsw.limit */
+       bool            memsw_is_minimum;
+
        /*
         * statistics. This must be placed at the end of memcg.
         */
@@ -189,6 +192,7 @@ enum charge_type {
        MEM_CGROUP_CHARGE_TYPE_SHMEM,   /* used by page migration of shmem */
        MEM_CGROUP_CHARGE_TYPE_FORCE,   /* used by force_empty */
        MEM_CGROUP_CHARGE_TYPE_SWAPOUT, /* for accounting swapcache */
+       MEM_CGROUP_CHARGE_TYPE_DROP,    /* a page was unused swap cache */
        NR_CHARGE_TYPE,
 };
 
@@ -645,6 +649,7 @@ unsigned long mem_cgroup_isolate_pages(unsigned long nr_to_scan,
        int zid = zone_idx(z);
        struct mem_cgroup_per_zone *mz;
        int lru = LRU_FILE * !!file + !!active;
+       int ret;
 
        BUG_ON(!mem_cont);
        mz = mem_cgroup_zoneinfo(mem_cont, nid, zid);
@@ -662,9 +667,19 @@ unsigned long mem_cgroup_isolate_pages(unsigned long nr_to_scan,
                        continue;
 
                scan++;
-               if (__isolate_lru_page(page, mode, file) == 0) {
+               ret = __isolate_lru_page(page, mode, file);
+               switch (ret) {
+               case 0:
                        list_move(&page->lru, dst);
+                       mem_cgroup_del_lru(page);
                        nr_taken++;
+                       break;
+               case -EBUSY:
+                       /* we don't affect global LRU but rotate in our LRU */
+                       mem_cgroup_rotate_lru_list(page, page_lru(page));
+                       break;
+               default:
+                       break;
                }
        }
 
@@ -846,6 +861,10 @@ static int mem_cgroup_hierarchical_reclaim(struct mem_cgroup *root_mem,
        int ret, total = 0;
        int loop = 0;
 
+       /* If memsw_is_minimum==1, swap-out is of-no-use. */
+       if (root_mem->memsw_is_minimum)
+               noswap = true;
+
        while (loop < 2) {
                victim = mem_cgroup_select_victim(root_mem);
                if (victim == root_mem)
@@ -1493,6 +1512,7 @@ __mem_cgroup_uncharge_common(struct page *page, enum charge_type ctype)
 
        switch (ctype) {
        case MEM_CGROUP_CHARGE_TYPE_MAPPED:
+       case MEM_CGROUP_CHARGE_TYPE_DROP:
                if (page_mapped(page))
                        goto unlock_out;
                break;
@@ -1556,18 +1576,23 @@ void mem_cgroup_uncharge_cache_page(struct page *page)
  * called after __delete_from_swap_cache() and drop "page" account.
  * memcg information is recorded to swap_cgroup of "ent"
  */
-void mem_cgroup_uncharge_swapcache(struct page *page, swp_entry_t ent)
+void
+mem_cgroup_uncharge_swapcache(struct page *page, swp_entry_t ent, bool swapout)
 {
        struct mem_cgroup *memcg;
+       int ctype = MEM_CGROUP_CHARGE_TYPE_SWAPOUT;
+
+       if (!swapout) /* this was a swap cache but the swap is unused ! */
+               ctype = MEM_CGROUP_CHARGE_TYPE_DROP;
+
+       memcg = __mem_cgroup_uncharge_common(page, ctype);
 
-       memcg = __mem_cgroup_uncharge_common(page,
-                                       MEM_CGROUP_CHARGE_TYPE_SWAPOUT);
        /* record memcg information */
-       if (do_swap_account && memcg) {
+       if (do_swap_account && swapout && memcg) {
                swap_cgroup_record(ent, css_id(&memcg->css));
                mem_cgroup_get(memcg);
        }
-       if (memcg)
+       if (swapout && memcg)
                css_put(&memcg->css);
 }
 #endif
@@ -1745,6 +1770,12 @@ static int mem_cgroup_resize_limit(struct mem_cgroup *memcg,
                        break;
                }
                ret = res_counter_set_limit(&memcg->res, val);
+               if (!ret) {
+                       if (memswlimit == val)
+                               memcg->memsw_is_minimum = true;
+                       else
+                               memcg->memsw_is_minimum = false;
+               }
                mutex_unlock(&set_limit_mutex);
 
                if (!ret)
@@ -1763,16 +1794,14 @@ static int mem_cgroup_resize_limit(struct mem_cgroup *memcg,
        return ret;
 }
 
-int mem_cgroup_resize_memsw_limit(struct mem_cgroup *memcg,
-                               unsigned long long val)
+static int mem_cgroup_resize_memsw_limit(struct mem_cgroup *memcg,
+                                       unsigned long long val)
 {
        int retry_count;
        u64 memlimit, oldusage, curusage;
        int children = mem_cgroup_count_children(memcg);
        int ret = -EBUSY;
 
-       if (!do_swap_account)
-               return -EINVAL;
        /* see mem_cgroup_resize_res_limit */
        retry_count = children * MEM_CGROUP_RECLAIM_RETRIES;
        oldusage = res_counter_read_u64(&memcg->memsw, RES_USAGE);
@@ -1794,6 +1823,12 @@ int mem_cgroup_resize_memsw_limit(struct mem_cgroup *memcg,
                        break;
                }
                ret = res_counter_set_limit(&memcg->memsw, val);
+               if (!ret) {
+                       if (memlimit == val)
+                               memcg->memsw_is_minimum = true;
+                       else
+                               memcg->memsw_is_minimum = false;
+               }
                mutex_unlock(&set_limit_mutex);
 
                if (!ret)
@@ -2007,8 +2042,7 @@ static u64 mem_cgroup_read(struct cgroup *cont, struct cftype *cft)
                val = res_counter_read_u64(&mem->res, name);
                break;
        case _MEMSWAP:
-               if (do_swap_account)
-                       val = res_counter_read_u64(&mem->memsw, name);
+               val = res_counter_read_u64(&mem->memsw, name);
                break;
        default:
                BUG();