Merge tag 'scsi-misc' of git://git.kernel.org/pub/scm/linux/kernel/git/jejb/scsi
[linux-2.6-microblaze.git] / mm / mempolicy.c
index de70f11..028e8dd 100644 (file)
@@ -134,6 +134,8 @@ static struct mempolicy preferred_node_policy[MAX_NUMNODES];
  * @node: Node id to start the search
  *
  * Lookup the next closest node by distance if @nid is not online.
+ *
+ * Return: this @node if it is online, otherwise the closest node by distance
  */
 int numa_map_to_online_node(int node)
 {
@@ -296,6 +298,7 @@ static struct mempolicy *mpol_new(unsigned short mode, unsigned short flags,
        atomic_set(&policy->refcnt, 1);
        policy->mode = mode;
        policy->flags = flags;
+       policy->home_node = NUMA_NO_NODE;
 
        return policy;
 }
@@ -1478,6 +1481,77 @@ static long kernel_mbind(unsigned long start, unsigned long len,
        return do_mbind(start, len, lmode, mode_flags, &nodes, flags);
 }
 
+SYSCALL_DEFINE4(set_mempolicy_home_node, unsigned long, start, unsigned long, len,
+               unsigned long, home_node, unsigned long, flags)
+{
+       struct mm_struct *mm = current->mm;
+       struct vm_area_struct *vma;
+       struct mempolicy *new;
+       unsigned long vmstart;
+       unsigned long vmend;
+       unsigned long end;
+       int err = -ENOENT;
+
+       start = untagged_addr(start);
+       if (start & ~PAGE_MASK)
+               return -EINVAL;
+       /*
+        * flags is used for future extension if any.
+        */
+       if (flags != 0)
+               return -EINVAL;
+
+       /*
+        * Check home_node is online to avoid accessing uninitialized
+        * NODE_DATA.
+        */
+       if (home_node >= MAX_NUMNODES || !node_online(home_node))
+               return -EINVAL;
+
+       len = (len + PAGE_SIZE - 1) & PAGE_MASK;
+       end = start + len;
+
+       if (end < start)
+               return -EINVAL;
+       if (end == start)
+               return 0;
+       mmap_write_lock(mm);
+       vma = find_vma(mm, start);
+       for (; vma && vma->vm_start < end;  vma = vma->vm_next) {
+
+               vmstart = max(start, vma->vm_start);
+               vmend   = min(end, vma->vm_end);
+               new = mpol_dup(vma_policy(vma));
+               if (IS_ERR(new)) {
+                       err = PTR_ERR(new);
+                       break;
+               }
+               /*
+                * Only update home node if there is an existing vma policy
+                */
+               if (!new)
+                       continue;
+
+               /*
+                * If any vma in the range got policy other than MPOL_BIND
+                * or MPOL_PREFERRED_MANY we return error. We don't reset
+                * the home node for vmas we already updated before.
+                */
+               if (new->mode != MPOL_BIND && new->mode != MPOL_PREFERRED_MANY) {
+                       err = -EOPNOTSUPP;
+                       break;
+               }
+
+               new->home_node = home_node;
+               err = mbind_range(mm, vmstart, vmend, new);
+               mpol_put(new);
+               if (err)
+                       break;
+       }
+       mmap_write_unlock(mm);
+       return err;
+}
+
 SYSCALL_DEFINE6(mbind, unsigned long, start, unsigned long, len,
                unsigned long, mode, const unsigned long __user *, nmask,
                unsigned long, maxnode, unsigned int, flags)
@@ -1802,6 +1876,11 @@ static int policy_node(gfp_t gfp, struct mempolicy *policy, int nd)
                WARN_ON_ONCE(policy->mode == MPOL_BIND && (gfp & __GFP_THISNODE));
        }
 
+       if ((policy->mode == MPOL_BIND ||
+            policy->mode == MPOL_PREFERRED_MANY) &&
+           policy->home_node != NUMA_NO_NODE)
+               return policy->home_node;
+
        return nd;
 }
 
@@ -2073,7 +2152,6 @@ static struct page *alloc_pages_preferred_many(gfp_t gfp, unsigned int order,
  * @order: Order of the GFP allocation.
  * @vma: Pointer to VMA or NULL if not available.
  * @addr: Virtual address of the allocation.  Must be inside @vma.
- * @node: Which node to prefer for allocation (modulo policy).
  * @hugepage: For hugepages try only the preferred node if possible.
  *
  * Allocate a page for a specific address in @vma, using the appropriate
@@ -2344,6 +2422,8 @@ bool __mpol_equal(struct mempolicy *a, struct mempolicy *b)
                return false;
        if (a->flags != b->flags)
                return false;
+       if (a->home_node != b->home_node)
+               return false;
        if (mpol_store_user_nodemask(a))
                if (!nodes_equal(a->w.user_nodemask, b->w.user_nodemask))
                        return false;
@@ -2887,7 +2967,7 @@ static const char * const policy_modes[] =
  * Format of input:
  *     <mode>[=<flags>][:<nodelist>]
  *
- * On success, returns 0, else 1
+ * Return: %0 on success, else %1
  */
 int mpol_parse_str(char *str, struct mempolicy **mpol)
 {