mempolicy: write lock mmap_sem while changing task mempolicy
[deliverable/linux.git] / mm / mempolicy.c
index a94d994eaaa8544049b3d5996f8d0cfd35b228c6..c6c61ea6bb8ca827ea308dcf71ca51d398337a0f 100644 (file)
@@ -181,27 +181,43 @@ static struct mempolicy *mpol_new(unsigned short mode, unsigned short flags,
 {
        struct mempolicy *policy;
        nodemask_t cpuset_context_nmask;
-       int localalloc = 0;
        int ret;
 
        pr_debug("setting mode %d flags %d nodes[0] %lx\n",
                 mode, flags, nodes ? nodes_addr(*nodes)[0] : -1);
 
-       if (mode == MPOL_DEFAULT)
-               return NULL;
-       if (!nodes || nodes_empty(*nodes)) {
-               if (mode != MPOL_PREFERRED)
+       if (mode == MPOL_DEFAULT) {
+               if (nodes && !nodes_empty(*nodes))
                        return ERR_PTR(-EINVAL);
-               localalloc = 1; /* special case:  no mode flags */
+               return NULL;
        }
+       VM_BUG_ON(!nodes);
+
+       /*
+        * MPOL_PREFERRED cannot be used with MPOL_F_STATIC_NODES or
+        * MPOL_F_RELATIVE_NODES if the nodemask is empty (local allocation).
+        * All other modes require a valid pointer to a non-empty nodemask.
+        */
+       if (mode == MPOL_PREFERRED) {
+               if (nodes_empty(*nodes)) {
+                       if (((flags & MPOL_F_STATIC_NODES) ||
+                            (flags & MPOL_F_RELATIVE_NODES)))
+                               return ERR_PTR(-EINVAL);
+                       nodes = NULL;   /* flag local alloc */
+               }
+       } else if (nodes_empty(*nodes))
+               return ERR_PTR(-EINVAL);
        policy = kmem_cache_alloc(policy_cache, GFP_KERNEL);
        if (!policy)
                return ERR_PTR(-ENOMEM);
        atomic_set(&policy->refcnt, 1);
        policy->policy = mode;
+       policy->flags = flags;
 
-       if (!localalloc) {
-               policy->flags = flags;
+       if (nodes) {
+               /*
+                * cpuset related setup doesn't apply to local allocation
+                */
                cpuset_update_task_memory_state();
                if (flags & MPOL_F_RELATIVE_NODES)
                        mpol_relative_nodemask(&cpuset_context_nmask, nodes,
@@ -217,7 +233,7 @@ static struct mempolicy *mpol_new(unsigned short mode, unsigned short flags,
        }
 
        ret = mpol_ops[mode].create(policy,
-                               localalloc ? NULL : &cpuset_context_nmask);
+                               nodes ? &cpuset_context_nmask : NULL);
        if (ret < 0) {
                kmem_cache_free(policy_cache, policy);
                return ERR_PTR(ret);
@@ -259,10 +275,6 @@ static void mpol_rebind_preferred(struct mempolicy *pol,
 {
        nodemask_t tmp;
 
-       /*
-        * check 'STATIC_NODES first, as preferred_node == -1 may be
-        * a temporary, "fallback" state for this policy.
-        */
        if (pol->flags & MPOL_F_STATIC_NODES) {
                int node = first_node(pol->w.user_nodemask);
 
@@ -270,12 +282,10 @@ static void mpol_rebind_preferred(struct mempolicy *pol,
                        pol->v.preferred_node = node;
                else
                        pol->v.preferred_node = -1;
-       } else if (pol->v.preferred_node == -1) {
-               return; /* no remap required for explicit local alloc */
        } else if (pol->flags & MPOL_F_RELATIVE_NODES) {
                mpol_relative_nodemask(&tmp, &pol->w.user_nodemask, nodes);
                pol->v.preferred_node = first_node(tmp);
-       } else {
+       } else if (pol->v.preferred_node != -1) {
                pol->v.preferred_node = node_remap(pol->v.preferred_node,
                                                   pol->w.cpuset_mems_allowed,
                                                   *nodes);
@@ -519,7 +529,7 @@ static int policy_vma(struct vm_area_struct *vma, struct mempolicy *new)
        if (!err) {
                mpol_get(new);
                vma->vm_policy = new;
-               mpol_free(old);
+               mpol_put(old);
        }
        return err;
 }
@@ -581,16 +591,29 @@ static long do_set_mempolicy(unsigned short mode, unsigned short flags,
                             nodemask_t *nodes)
 {
        struct mempolicy *new;
+       struct mm_struct *mm = current->mm;
 
        new = mpol_new(mode, flags, nodes);
        if (IS_ERR(new))
                return PTR_ERR(new);
-       mpol_free(current->mempolicy);
+
+       /*
+        * prevent changing our mempolicy while show_numa_maps()
+        * is using it.
+        * Note:  do_set_mempolicy() can be called at init time
+        * with no 'mm'.
+        */
+       if (mm)
+               down_write(&mm->mmap_sem);
+       mpol_put(current->mempolicy);
        current->mempolicy = new;
        mpol_set_task_struct_flag();
        if (new && new->policy == MPOL_INTERLEAVE &&
            nodes_weight(new->v.nodes))
                current->il_next = first_node(new->v.nodes);
+       if (mm)
+               up_write(&mm->mmap_sem);
+
        return 0;
 }
 
@@ -938,7 +961,7 @@ static long do_mbind(unsigned long start, unsigned long len,
        }
 
        up_write(&mm->mmap_sem);
-       mpol_free(new);
+       mpol_put(new);
        return err;
 }
 
@@ -1436,14 +1459,14 @@ struct zonelist *huge_zonelist(struct vm_area_struct *vma, unsigned long addr,
                nid = interleave_nid(pol, vma, addr, HPAGE_SHIFT);
                if (unlikely(pol != &default_policy &&
                                pol != current->mempolicy))
-                       __mpol_free(pol);       /* finished with pol */
+                       __mpol_put(pol);        /* finished with pol */
                return node_zonelist(nid, gfp_flags);
        }
 
        zl = zonelist_policy(GFP_HIGHUSER, pol);
        if (unlikely(pol != &default_policy && pol != current->mempolicy)) {
                if (pol->policy != MPOL_BIND)
-                       __mpol_free(pol);       /* finished with pol */
+                       __mpol_put(pol);        /* finished with pol */
                else
                        *mpol = pol;    /* unref needed after allocation */
        }
@@ -1502,7 +1525,7 @@ alloc_page_vma(gfp_t gfp, struct vm_area_struct *vma, unsigned long addr)
                nid = interleave_nid(pol, vma, addr, PAGE_SHIFT);
                if (unlikely(pol != &default_policy &&
                                pol != current->mempolicy))
-                       __mpol_free(pol);       /* finished with pol */
+                       __mpol_put(pol);        /* finished with pol */
                return alloc_page_interleave(gfp, 0, nid);
        }
        zl = zonelist_policy(gfp, pol);
@@ -1512,7 +1535,7 @@ alloc_page_vma(gfp_t gfp, struct vm_area_struct *vma, unsigned long addr)
                 */
                struct page *page =  __alloc_pages_nodemask(gfp, 0,
                                                zl, nodemask_policy(gfp, pol));
-               __mpol_free(pol);
+               __mpol_put(pol);
                return page;
        }
        /*
@@ -1556,15 +1579,15 @@ struct page *alloc_pages_current(gfp_t gfp, unsigned order)
 EXPORT_SYMBOL(alloc_pages_current);
 
 /*
- * If mpol_copy() sees current->cpuset == cpuset_being_rebound, then it
+ * If mpol_dup() sees current->cpuset == cpuset_being_rebound, then it
  * rebinds the mempolicy its copying by calling mpol_rebind_policy()
  * with the mems_allowed returned by cpuset_mems_allowed().  This
  * keeps mempolicies cpuset relative after its cpuset moves.  See
  * further kernel/cpuset.c update_nodemask().
  */
 
-/* Slow path of a mempolicy copy */
-struct mempolicy *__mpol_copy(struct mempolicy *old)
+/* Slow path of a mempolicy duplicate */
+struct mempolicy *__mpol_dup(struct mempolicy *old)
 {
        struct mempolicy *new = kmem_cache_alloc(policy_cache, GFP_KERNEL);
 
@@ -1614,7 +1637,7 @@ int __mpol_equal(struct mempolicy *a, struct mempolicy *b)
 }
 
 /* Slow path of a mpol destructor. */
-void __mpol_free(struct mempolicy *p)
+void __mpol_put(struct mempolicy *p)
 {
        if (!atomic_dec_and_test(&p->refcnt))
                return;
@@ -1710,7 +1733,7 @@ static void sp_delete(struct shared_policy *sp, struct sp_node *n)
 {
        pr_debug("deleting %lx-l%lx\n", n->start, n->end);
        rb_erase(&n->nd, &sp->root);
-       mpol_free(n->policy);
+       mpol_put(n->policy);
        kmem_cache_free(sn_cache, n);
 }
 
@@ -1770,7 +1793,7 @@ restart:
                sp_insert(sp, new);
        spin_unlock(&sp->lock);
        if (new2) {
-               mpol_free(new2->policy);
+               mpol_put(new2->policy);
                kmem_cache_free(sn_cache, new2);
        }
        return 0;
@@ -1795,7 +1818,7 @@ void mpol_shared_policy_init(struct shared_policy *info, unsigned short policy,
                        /* Policy covers entire file */
                        pvma.vm_end = TASK_SIZE;
                        mpol_set_shared_policy(info, &pvma, newpol);
-                       mpol_free(newpol);
+                       mpol_put(newpol);
                }
        }
 }
@@ -1838,7 +1861,7 @@ void mpol_free_shared_policy(struct shared_policy *p)
                n = rb_entry(next, struct sp_node, nd);
                next = rb_next(&n->nd);
                rb_erase(&n->nd, &p->root);
-               mpol_free(n->policy);
+               mpol_put(n->policy);
                kmem_cache_free(sn_cache, n);
        }
        spin_unlock(&p->lock);
@@ -2058,7 +2081,7 @@ int show_numa_map(struct seq_file *m, void *v)
         * unref shared or other task's mempolicy
         */
        if (pol != &default_policy && pol != current->mempolicy)
-               __mpol_free(pol);
+               __mpol_put(pol);
 
        seq_printf(m, "%08lx %s", vma->vm_start, buffer);
 
This page took 0.028449 seconds and 5 git commands to generate.