]> git.karo-electronics.de Git - karo-tx-linux.git/blobdiff - mm/mempolicy.c
video: mxsfb: fix typo: 'FAILING' instead of 'FALLING'
[karo-tx-linux.git] / mm / mempolicy.c
index d04a8a54c294f852f55e624e3c41d1b03b7a56e9..05a443d62a2253fdd144297e0fd1216f1ea39420 100644 (file)
@@ -251,10 +251,10 @@ static struct mempolicy *mpol_new(unsigned short mode, unsigned short flags,
        pr_debug("setting mode %d flags %d nodes[0] %lx\n",
                 mode, flags, nodes ? nodes_addr(*nodes)[0] : -1);
 
-       if (mode == MPOL_DEFAULT) {
+       if (mode == MPOL_DEFAULT || mode == MPOL_NOOP) {
                if (nodes && !nodes_empty(*nodes))
                        return ERR_PTR(-EINVAL);
-               return NULL;    /* simply delete any existing policy */
+               return NULL;
        }
        VM_BUG_ON(!nodes);
 
@@ -269,6 +269,10 @@ static struct mempolicy *mpol_new(unsigned short mode, unsigned short flags,
                             (flags & MPOL_F_RELATIVE_NODES)))
                                return ERR_PTR(-EINVAL);
                }
+       } else if (mode == MPOL_LOCAL) {
+               if (!nodes_empty(*nodes))
+                       return ERR_PTR(-EINVAL);
+               mode = MPOL_PREFERRED;
        } else if (nodes_empty(*nodes))
                return ERR_PTR(-EINVAL);
        policy = kmem_cache_alloc(policy_cache, GFP_KERNEL);
@@ -511,7 +515,7 @@ static inline int check_pmd_range(struct vm_area_struct *vma, pud_t *pud,
        pmd = pmd_offset(pud, addr);
        do {
                next = pmd_addr_end(addr, end);
-               split_huge_page_pmd(vma->vm_mm, pmd);
+               split_huge_page_pmd(vma, addr, pmd);
                if (pmd_none_or_trans_huge_or_clear_bad(pmd))
                        continue;
                if (check_pte_range(vma, pmd, addr, next, nodes,
@@ -579,22 +583,32 @@ check_range(struct mm_struct *mm, unsigned long start, unsigned long end,
                return ERR_PTR(-EFAULT);
        prev = NULL;
        for (vma = first; vma && vma->vm_start < end; vma = vma->vm_next) {
+               unsigned long endvma = vma->vm_end;
+
+               if (endvma > end)
+                       endvma = end;
+               if (vma->vm_start > start)
+                       start = vma->vm_start;
+
                if (!(flags & MPOL_MF_DISCONTIG_OK)) {
                        if (!vma->vm_next && vma->vm_end < end)
                                return ERR_PTR(-EFAULT);
                        if (prev && prev->vm_end < vma->vm_start)
                                return ERR_PTR(-EFAULT);
                }
-               if (!is_vm_hugetlb_page(vma) &&
-                   ((flags & MPOL_MF_STRICT) ||
+
+               if (is_vm_hugetlb_page(vma))
+                       goto next;
+
+               if (flags & MPOL_MF_LAZY) {
+                       change_prot_none(vma, start, endvma);
+                       goto next;
+               }
+
+               if ((flags & MPOL_MF_STRICT) ||
                     ((flags & (MPOL_MF_MOVE | MPOL_MF_MOVE_ALL)) &&
-                               vma_migratable(vma)))) {
-                       unsigned long endvma = vma->vm_end;
+                     vma_migratable(vma))) {
 
-                       if (endvma > end)
-                               endvma = end;
-                       if (vma->vm_start > start)
-                               start = vma->vm_start;
                        err = check_pgd_range(vma, start, endvma, nodes,
                                                flags, private);
                        if (err) {
@@ -602,6 +616,7 @@ check_range(struct mm_struct *mm, unsigned long start, unsigned long end,
                                break;
                        }
                }
+next:
                prev = vma;
        }
        return first;
@@ -1133,8 +1148,7 @@ static long do_mbind(unsigned long start, unsigned long len,
        int err;
        LIST_HEAD(pagelist);
 
-       if (flags & ~(unsigned long)(MPOL_MF_STRICT |
-                                    MPOL_MF_MOVE | MPOL_MF_MOVE_ALL))
+       if (flags & ~(unsigned long)MPOL_MF_VALID)
                return -EINVAL;
        if ((flags & MPOL_MF_MOVE_ALL) && !capable(CAP_SYS_NICE))
                return -EPERM;
@@ -1142,7 +1156,7 @@ static long do_mbind(unsigned long start, unsigned long len,
        if (start & ~PAGE_MASK)
                return -EINVAL;
 
-       if (mode == MPOL_DEFAULT)
+       if (mode == MPOL_DEFAULT || mode == MPOL_NOOP)
                flags &= ~MPOL_MF_STRICT;
 
        len = (len + PAGE_SIZE - 1) & PAGE_MASK;
@@ -1157,6 +1171,9 @@ static long do_mbind(unsigned long start, unsigned long len,
        if (IS_ERR(new))
                return PTR_ERR(new);
 
+       if (flags & MPOL_MF_LAZY)
+               new->flags |= MPOL_F_MOF;
+
        /*
         * If we are using the default policy then operation
         * on discontinuous address spaces is okay after all
@@ -1193,21 +1210,23 @@ static long do_mbind(unsigned long start, unsigned long len,
        vma = check_range(mm, start, end, nmask,
                          flags | MPOL_MF_INVERT, &pagelist);
 
-       err = PTR_ERR(vma);
-       if (!IS_ERR(vma)) {
-               int nr_failed = 0;
-
+       err = PTR_ERR(vma);     /* maybe ... */
+       if (!IS_ERR(vma) && mode != MPOL_NOOP)
                err = mbind_range(mm, start, end, new);
 
+       if (!err) {
+               int nr_failed = 0;
+
                if (!list_empty(&pagelist)) {
+                       WARN_ON_ONCE(flags & MPOL_MF_LAZY);
                        nr_failed = migrate_pages(&pagelist, new_vma_page,
-                                               (unsigned long)vma,
-                                               false, MIGRATE_SYNC);
+                                                 (unsigned long)vma,
+                                                 false, MIGRATE_SYNC);
                        if (nr_failed)
                                putback_lru_pages(&pagelist);
                }
 
-               if (!err && nr_failed && (flags & MPOL_MF_STRICT))
+               if (nr_failed && (flags & MPOL_MF_STRICT))
                        err = -EIO;
        } else
                putback_lru_pages(&pagelist);
@@ -1907,7 +1926,6 @@ alloc_pages_vma(gfp_t gfp, int order, struct vm_area_struct *vma,
                unsigned long addr, int node)
 {
        struct mempolicy *pol;
-       struct zonelist *zl;
        struct page *page;
        unsigned int cpuset_mems_cookie;
 
@@ -1926,23 +1944,11 @@ retry_cpuset:
 
                return page;
        }
-       zl = policy_zonelist(gfp, pol, node);
-       if (unlikely(mpol_needs_cond_ref(pol))) {
-               /*
-                * slow path: ref counted shared policy
-                */
-               struct page *page =  __alloc_pages_nodemask(gfp, order,
-                                               zl, policy_nodemask(gfp, pol));
-               __mpol_put(pol);
-               if (unlikely(!put_mems_allowed(cpuset_mems_cookie) && !page))
-                       goto retry_cpuset;
-               return page;
-       }
-       /*
-        * fast path:  default or task policy
-        */
-       page = __alloc_pages_nodemask(gfp, order, zl,
+       page = __alloc_pages_nodemask(gfp, order,
+                                     policy_zonelist(gfp, pol, node),
                                      policy_nodemask(gfp, pol));
+       if (unlikely(mpol_needs_cond_ref(pol)))
+               __mpol_put(pol);
        if (unlikely(!put_mems_allowed(cpuset_mems_cookie) && !page))
                goto retry_cpuset;
        return page;
@@ -2175,6 +2181,145 @@ static void sp_free(struct sp_node *n)
        kmem_cache_free(sn_cache, n);
 }
 
+/*
+ * Multi-stage node selection is used in conjunction with a periodic
+ * migration fault to build a temporal task<->page relation. By
+ * using a two-stage filter we remove short/unlikely relations.
+ *
+ * Using P(p) ~ n_p / n_t as per frequentist probability, we can
+ * equate a task's usage of a particular page (n_p) per total usage
+ * of this page (n_t) (in a given time-span) to a probability.
+ *
+ * Our periodic faults will then sample this probability and getting
+ * the same result twice in a row, given these samples are fully
+ * independent, is then given by P(n)^2, provided our sample period
+ * is sufficiently short compared to the usage pattern.
+ *
+ * This quadric squishes small probabilities, making it less likely
+ * we act on an unlikely task<->page relation.
+ *
+ * Return the best node ID this page should be on, or -1 if it should
+ * stay where it is.
+ */
+static int
+numa_migration_target(struct page *page, int page_nid,
+                     struct task_struct *p, int this_cpu,
+                     int cpu_last_access)
+{
+       int nid_last_access;
+       int this_nid;
+
+       if (task_numa_shared(p) < 0)
+               return -1;
+
+       /*
+        * Possibly migrate towards the current node, depends on
+        * task_numa_placement() and access details.
+        */
+       nid_last_access = cpu_to_node(cpu_last_access);
+       this_nid = cpu_to_node(this_cpu);
+
+       if (nid_last_access != this_nid) {
+               /*
+                * 'Access miss': the page got last accessed from a remote node.
+                */
+               return -1;
+       }
+       /*
+        * 'Access hit': the page got last accessed from our node.
+        *
+        * Migrate the page if needed.
+        */
+
+       /* The page is already on this node: */
+       if (page_nid == this_nid)
+               return -1;
+
+       return this_nid;
+}
+
+/**
+ * mpol_misplaced - check whether current page node is valid in policy
+ *
+ * @page   - page to be checked
+ * @vma    - vm area where page mapped
+ * @addr   - virtual address where page mapped
+ * @multi  - use multi-stage node binding
+ *
+ * Lookup current policy node id for vma,addr and "compare to" page's
+ * node id.
+ *
+ * Returns:
+ *     -1      - not misplaced, page is in the right node
+ *     node    - node id where the page should be
+ *
+ * Policy determination "mimics" alloc_page_vma().
+ * Called from fault path where we know the vma and faulting address.
+ */
+int mpol_misplaced(struct page *page, struct vm_area_struct *vma, unsigned long addr)
+{
+       int best_nid = -1, page_nid;
+       int cpu_last_access, this_cpu;
+       struct mempolicy *pol;
+       unsigned long pgoff;
+       struct zone *zone;
+
+       BUG_ON(!vma);
+
+       this_cpu = raw_smp_processor_id();
+       page_nid = page_to_nid(page);
+
+       cpu_last_access = page_xchg_last_cpu(page, this_cpu);
+
+       pol = get_vma_policy(current, vma, addr);
+       if (!(pol->flags & MPOL_F_MOF) && !(task_numa_shared(current) >= 0))
+               goto out_keep_page;
+
+       switch (pol->mode) {
+       case MPOL_INTERLEAVE:
+               BUG_ON(addr >= vma->vm_end);
+               BUG_ON(addr < vma->vm_start);
+
+               pgoff = vma->vm_pgoff;
+               pgoff += (addr - vma->vm_start) >> PAGE_SHIFT;
+               best_nid = offset_il_node(pol, vma, pgoff);
+               break;
+
+       case MPOL_PREFERRED:
+               if (pol->flags & MPOL_F_LOCAL)
+                       best_nid = numa_node_id();
+               else
+                       best_nid = pol->v.preferred_node;
+               break;
+
+       case MPOL_BIND:
+               /*
+                * allows binding to multiple nodes.
+                * use current page if in policy nodemask,
+                * else select nearest allowed node, if any.
+                * If no allowed nodes, use current [!misplaced].
+                */
+               if (node_isset(page_nid, pol->v.nodes))
+                       goto out_keep_page;
+               (void)first_zones_zonelist(
+                               node_zonelist(numa_node_id(), GFP_HIGHUSER),
+                               gfp_zone(GFP_HIGHUSER),
+                               &pol->v.nodes, &zone);
+               best_nid = zone->node;
+               break;
+
+       default:
+               BUG();
+       }
+
+       best_nid = numa_migration_target(page, page_nid, current, this_cpu, cpu_last_access);
+
+out_keep_page:
+       mpol_cond_put(pol);
+
+       return best_nid;
+}
+
 static void sp_delete(struct shared_policy *sp, struct sp_node *n)
 {
        pr_debug("deleting %lx-l%lx\n", n->start, n->end);
@@ -2397,14 +2542,14 @@ void numa_default_policy(void)
  * "local" is pseudo-policy:  MPOL_PREFERRED with MPOL_F_LOCAL flag
  * Used only for mpol_parse_str() and mpol_to_str()
  */
-#define MPOL_LOCAL MPOL_MAX
 static const char * const policy_modes[] =
 {
        [MPOL_DEFAULT]    = "default",
        [MPOL_PREFERRED]  = "prefer",
        [MPOL_BIND]       = "bind",
        [MPOL_INTERLEAVE] = "interleave",
-       [MPOL_LOCAL]      = "local"
+       [MPOL_LOCAL]      = "local",
+       [MPOL_NOOP]       = "noop",     /* should not actually be used */
 };
 
 
@@ -2450,12 +2595,12 @@ int mpol_parse_str(char *str, struct mempolicy **mpol, int no_context)
        if (flags)
                *flags++ = '\0';        /* terminate mode string */
 
-       for (mode = 0; mode <= MPOL_LOCAL; mode++) {
+       for (mode = 0; mode < MPOL_MAX; mode++) {
                if (!strcmp(str, policy_modes[mode])) {
                        break;
                }
        }
-       if (mode > MPOL_LOCAL)
+       if (mode >= MPOL_MAX || mode == MPOL_NOOP)
                goto out;
 
        switch (mode) {