mm: fix migratetype bug which slowed swapping
[linux-2.6.git] / mm / mempolicy.c
index 7dd9d9f..290fb5b 100644 (file)
 #include <linux/seq_file.h>
 #include <linux/proc_fs.h>
 #include <linux/migrate.h>
+#include <linux/ksm.h>
 #include <linux/rmap.h>
 #include <linux/security.h>
 #include <linux/syscalls.h>
 #include <linux/ctype.h>
+#include <linux/mm_inline.h>
 
 #include <asm/tlbflush.h>
 #include <asm/uaccess.h>
@@ -412,17 +414,11 @@ static int check_pte_range(struct vm_area_struct *vma, pmd_t *pmd,
                if (!page)
                        continue;
                /*
-                * The check for PageReserved here is important to avoid
-                * handling zero pages and other pages that may have been
-                * marked special by the system.
-                *
-                * If the PageReserved would not be checked here then f.e.
-                * the location of the zero page could have an influence
-                * on MPOL_MF_STRICT, zero pages would be counted for
-                * the per node stats, and there would be useless attempts
-                * to put zero pages on the migration list.
+                * vm_normal_page() filters out zero pages, but there might
+                * still be PageReserved pages to skip, perhaps in a VDSO.
+                * And we cannot move PageKsm pages sensibly or safely yet.
                 */
-               if (PageReserved(page))
+               if (PageReserved(page) || PageKsm(page))
                        continue;
                nid = page_to_nid(page);
                if (node_isset(nid, *nodes) == !!(flags & MPOL_MF_INVERT))
@@ -809,6 +805,8 @@ static void migrate_page_add(struct page *page, struct list_head *pagelist,
        if ((flags & MPOL_MF_MOVE_ALL) || page_mapcount(page) == 1) {
                if (!isolate_lru_page(page)) {
                        list_add_tail(&page->lru, pagelist);
+                       inc_zone_page_state(page, NR_ISOLATED_ANON +
+                                           page_is_file_cache(page));
                }
        }
 }
@@ -836,7 +834,7 @@ static int migrate_to_node(struct mm_struct *mm, int source, int dest,
                        flags | MPOL_MF_DISCONTIG_OK, &pagelist);
 
        if (!list_empty(&pagelist))
-               err = migrate_pages(&pagelist, new_node_page, dest);
+               err = migrate_pages(&pagelist, new_node_page, dest, 0);
 
        return err;
 }
@@ -1024,7 +1022,7 @@ static long do_mbind(unsigned long start, unsigned long len,
 
                err = migrate_prep();
                if (err)
-                       return err;
+                       goto mpol_out;
        }
        {
                NODEMASK_SCRATCH(scratch);
@@ -1039,10 +1037,9 @@ static long do_mbind(unsigned long start, unsigned long len,
                        err = -ENOMEM;
                NODEMASK_SCRATCH_FREE(scratch);
        }
-       if (err) {
-               mpol_put(new);
-               return err;
-       }
+       if (err)
+               goto mpol_out;
+
        vma = check_range(mm, start, end, nmask,
                          flags | MPOL_MF_INVERT, &pagelist);
 
@@ -1054,13 +1051,15 @@ static long do_mbind(unsigned long start, unsigned long len,
 
                if (!list_empty(&pagelist))
                        nr_failed = migrate_pages(&pagelist, new_vma_page,
-                                               (unsigned long)vma);
+                                               (unsigned long)vma, 0);
 
                if (!err && nr_failed && (flags & MPOL_MF_STRICT))
                        err = -EIO;
-       }
+       } else
+               putback_lru_pages(&pagelist);
 
        up_write(&mm->mmap_sem);
+ mpol_out:
        mpol_put(new);
        return err;
 }
@@ -1564,6 +1563,53 @@ struct zonelist *huge_zonelist(struct vm_area_struct *vma, unsigned long addr,
        }
        return zl;
 }
+
+/*
+ * init_nodemask_of_mempolicy
+ *
+ * If the current task's mempolicy is "default" [NULL], return 'false'
+ * to indicate default policy.  Otherwise, extract the policy nodemask
+ * for 'bind' or 'interleave' policy into the argument nodemask, or
+ * initialize the argument nodemask to contain the single node for
+ * 'preferred' or 'local' policy and return 'true' to indicate presence
+ * of non-default mempolicy.
+ *
+ * We don't bother with reference counting the mempolicy [mpol_get/put]
+ * because the current task is examining it's own mempolicy and a task's
+ * mempolicy is only ever changed by the task itself.
+ *
+ * N.B., it is the caller's responsibility to free a returned nodemask.
+ */
+bool init_nodemask_of_mempolicy(nodemask_t *mask)
+{
+       struct mempolicy *mempolicy;
+       int nid;
+
+       if (!(mask && current->mempolicy))
+               return false;
+
+       mempolicy = current->mempolicy;
+       switch (mempolicy->mode) {
+       case MPOL_PREFERRED:
+               if (mempolicy->flags & MPOL_F_LOCAL)
+                       nid = numa_node_id();
+               else
+                       nid = mempolicy->v.preferred_node;
+               init_nodemask_of_node(mask, nid);
+               break;
+
+       case MPOL_BIND:
+               /* Fall through */
+       case MPOL_INTERLEAVE:
+               *mask =  mempolicy->v.nodes;
+               break;
+
+       default:
+               BUG();
+       }
+
+       return true;
+}
 #endif
 
 /* Allocate a page in interleaved policy.