* Copyright 2003,2004 Andi Kleen SuSE Labs
  */
 
+/*
+ * Both the MPOL_* mempolicy mode and the MPOL_F_* optional mode flags are
+ * passed by the user to either set_mempolicy() or mbind() in an 'int' actual.
+ * The MPOL_MODE_FLAGS macro determines the legal set of optional mode flags.
+ */
+
 /* Policies */
 enum {
        MPOL_DEFAULT,
        MPOL_MAX,       /* always last member of enum */
 };
 
-/* Flags for get_mem_policy */
+/* Flags for set_mempolicy */
+/*
+ * MPOL_MODE_FLAGS is the union of all possible optional mode flags passed to
+ * either set_mempolicy() or mbind().
+ */
+#define MPOL_MODE_FLAGS        (0)
+
+/* Flags for get_mempolicy */
 #define MPOL_F_NODE    (1<<0)  /* return next IL mode instead of node mask */
 #define MPOL_F_ADDR    (1<<1)  /* look up vma using address */
 #define MPOL_F_MEMS_ALLOWED (1<<2) /* return allowed memories */
 struct mempolicy {
        atomic_t refcnt;
        unsigned short policy;  /* See MPOL_* above */
+       unsigned short flags;   /* See set_mempolicy() MPOL_F_* above */
        union {
                short            preferred_node; /* preferred */
                nodemask_t       nodes;         /* interleave/bind */
 };
 
 void mpol_shared_policy_init(struct shared_policy *info, unsigned short policy,
-                               nodemask_t *nodes);
+                               unsigned short flags, nodemask_t *nodes);
 int mpol_set_shared_policy(struct shared_policy *info,
                                struct vm_area_struct *vma,
                                struct mempolicy *new);
 }
 
 static inline void mpol_shared_policy_init(struct shared_policy *info,
-                               unsigned short policy, nodemask_t *nodes)
+               unsigned short policy, unsigned short flags, nodemask_t *nodes)
 {
 }
 
 
 }
 
 /* Create a new policy */
-static struct mempolicy *mpol_new(unsigned short mode, nodemask_t *nodes)
+static struct mempolicy *mpol_new(unsigned short mode, unsigned short flags,
+                                 nodemask_t *nodes)
 {
        struct mempolicy *policy;
 
-       pr_debug("setting mode %d nodes[0] %lx\n",
-                mode, nodes ? nodes_addr(*nodes)[0] : -1);
+       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;
                BUG();
        }
        policy->policy = mode;
+       policy->flags = flags;
        policy->cpuset_mems_allowed = cpuset_mems_allowed(current);
        return policy;
 }
 }
 
 /* Set the process memory policy */
-static long do_set_mempolicy(unsigned short mode, nodemask_t *nodes)
+static long do_set_mempolicy(unsigned short mode, unsigned short flags,
+                            nodemask_t *nodes)
 {
        struct mempolicy *new;
 
        if (mpol_check_policy(mode, nodes))
                return -EINVAL;
-       new = mpol_new(mode, nodes);
+       new = mpol_new(mode, flags, nodes);
        if (IS_ERR(new))
                return PTR_ERR(new);
        mpol_free(current->mempolicy);
                        goto out;
                }
        } else
-               *policy = pol->policy;
+               *policy = pol->policy | pol->flags;
 
        if (vma) {
                up_read(¤t->mm->mmap_sem);
 #endif
 
 static long do_mbind(unsigned long start, unsigned long len,
-                    unsigned short mode, nodemask_t *nmask,
-                    unsigned long flags)
+                    unsigned short mode, unsigned short mode_flags,
+                    nodemask_t *nmask, unsigned long flags)
 {
        struct vm_area_struct *vma;
        struct mm_struct *mm = current->mm;
        if (mpol_check_policy(mode, nmask))
                return -EINVAL;
 
-       new = mpol_new(mode, nmask);
+       new = mpol_new(mode, mode_flags, nmask);
        if (IS_ERR(new))
                return PTR_ERR(new);
 
        if (!new)
                flags |= MPOL_MF_DISCONTIG_OK;
 
-       pr_debug("mbind %lx-%lx mode:%d nodes:%lx\n", start, start + len,
-                mode, nmask ? nodes_addr(*nmask)[0] : -1);
+       pr_debug("mbind %lx-%lx mode:%d flags:%d nodes:%lx\n",
+                start, start + len, mode, mode_flags,
+                nmask ? nodes_addr(*nmask)[0] : -1);
 
        down_write(&mm->mmap_sem);
        vma = check_range(mm, start, end, nmask,
 {
        nodemask_t nodes;
        int err;
+       unsigned short mode_flags;
 
+       mode_flags = mode & MPOL_MODE_FLAGS;
+       mode &= ~MPOL_MODE_FLAGS;
        if (mode >= MPOL_MAX)
                return -EINVAL;
        err = get_nodes(&nodes, nmask, maxnode);
        if (err)
                return err;
-       return do_mbind(start, len, mode, &nodes, flags);
+       return do_mbind(start, len, mode, mode_flags, &nodes, flags);
 }
 
 /* Set the process memory policy */
 {
        int err;
        nodemask_t nodes;
+       unsigned short flags;
 
-       if (mode < 0 || mode >= MPOL_MAX)
+       flags = mode & MPOL_MODE_FLAGS;
+       mode &= ~MPOL_MODE_FLAGS;
+       if ((unsigned int)mode >= MPOL_MAX)
                return -EINVAL;
        err = get_nodes(&nodes, nmask, maxnode);
        if (err)
                return err;
-       return do_set_mempolicy(mode, &nodes);
+       return do_set_mempolicy(mode, flags, &nodes);
 }
 
 asmlinkage long sys_migrate_pages(pid_t pid, unsigned long maxnode,
 }
 
 void mpol_shared_policy_init(struct shared_policy *info, unsigned short policy,
-                               nodemask_t *policy_nodes)
+                       unsigned short flags, nodemask_t *policy_nodes)
 {
        info->root = RB_ROOT;
        spin_lock_init(&info->lock);
                struct mempolicy *newpol;
 
                /* Falls back to MPOL_DEFAULT on any error */
-               newpol = mpol_new(policy, policy_nodes);
+               newpol = mpol_new(policy, flags, policy_nodes);
                if (!IS_ERR(newpol)) {
                        /* Create pseudo-vma that contains just the policy */
                        struct vm_area_struct pvma;
        struct sp_node *new = NULL;
        unsigned long sz = vma_pages(vma);
 
-       pr_debug("set_shared_policy %lx sz %lu %d %lx\n",
+       pr_debug("set_shared_policy %lx sz %lu %d %d %lx\n",
                 vma->vm_pgoff,
-                sz, npol? npol->policy : -1,
+                sz, npol ? npol->policy : -1,
+                npol ? npol->flags : -1,
                 npol ? nodes_addr(npol->v.nodes)[0] : -1);
 
        if (npol) {
        if (unlikely(nodes_empty(interleave_nodes)))
                node_set(prefer, interleave_nodes);
 
-       if (do_set_mempolicy(MPOL_INTERLEAVE, &interleave_nodes))
+       if (do_set_mempolicy(MPOL_INTERLEAVE, 0, &interleave_nodes))
                printk("numa_policy_init: interleaving failed\n");
 }
 
 /* Reset policy of current process to default */
 void numa_default_policy(void)
 {
-       do_set_mempolicy(MPOL_DEFAULT, NULL);
+       do_set_mempolicy(MPOL_DEFAULT, 0, NULL);
 }
 
 /* Migrate a policy to a different set of nodes */
 
 #ifdef CONFIG_NUMA
 #ifdef CONFIG_TMPFS
 static int shmem_parse_mpol(char *value, unsigned short *policy,
-                           nodemask_t *policy_nodes)
+                       unsigned short *mode_flags, nodemask_t *policy_nodes)
 {
        char *nodelist = strchr(value, ':');
+       char *flags = strchr(value, '=');
        int err = 1;
 
        if (nodelist) {
                if (!nodes_subset(*policy_nodes, node_states[N_HIGH_MEMORY]))
                        goto out;
        }
+       if (flags)
+               *flags++ = '\0';
        if (!strcmp(value, "default")) {
                *policy = MPOL_DEFAULT;
                /* Don't allow a nodelist */
                        *policy_nodes = node_states[N_HIGH_MEMORY];
                err = 0;
        }
+       if (flags) {
+       }
 out:
        /* Restore string for error message */
        if (nodelist)
 }
 
 static void shmem_show_mpol(struct seq_file *seq, unsigned short policy,
-                           const nodemask_t policy_nodes)
+                       unsigned short flags, const nodemask_t policy_nodes)
 {
        char *policy_string;
 
 #else /* !CONFIG_NUMA */
 #ifdef CONFIG_TMPFS
 static inline int shmem_parse_mpol(char *value, unsigned short *policy,
-                                               nodemask_t *policy_nodes)
+                       unsigned short *mode_flags, nodemask_t *policy_nodes)
 {
        return 1;
 }
 
 static inline void shmem_show_mpol(struct seq_file *seq, unsigned short policy,
-                           const nodemask_t policy_nodes)
+                       unsigned short flags, const nodemask_t policy_nodes)
 {
 }
 #endif /* CONFIG_TMPFS */
                        inode->i_op = &shmem_inode_operations;
                        inode->i_fop = &shmem_file_operations;
                        mpol_shared_policy_init(&info->policy, sbinfo->policy,
-                                                       &sbinfo->policy_nodes);
+                                       sbinfo->flags, &sbinfo->policy_nodes);
                        break;
                case S_IFDIR:
                        inc_nlink(inode);
                         * Must not load anything in the rbtree,
                         * mpol_free_shared_policy will not be called.
                         */
-                       mpol_shared_policy_init(&info->policy, MPOL_DEFAULT,
+                       mpol_shared_policy_init(&info->policy, MPOL_DEFAULT, 0,
                                                NULL);
                        break;
                }
                                goto bad_val;
                } else if (!strcmp(this_char,"mpol")) {
                        if (shmem_parse_mpol(value, &sbinfo->policy,
-                                            &sbinfo->policy_nodes))
+                               &sbinfo->flags, &sbinfo->policy_nodes))
                                goto bad_val;
                } else {
                        printk(KERN_ERR "tmpfs: Bad mount option %s\n",
        sbinfo->max_inodes  = config.max_inodes;
        sbinfo->free_inodes = config.max_inodes - inodes;
        sbinfo->policy      = config.policy;
+       sbinfo->flags       = config.flags;
        sbinfo->policy_nodes = config.policy_nodes;
 out:
        spin_unlock(&sbinfo->stat_lock);
                seq_printf(seq, ",uid=%u", sbinfo->uid);
        if (sbinfo->gid != 0)
                seq_printf(seq, ",gid=%u", sbinfo->gid);
-       shmem_show_mpol(seq, sbinfo->policy, sbinfo->policy_nodes);
+       shmem_show_mpol(seq, sbinfo->policy, sbinfo->flags,
+                       sbinfo->policy_nodes);
        return 0;
 }
 #endif /* CONFIG_TMPFS */
        sbinfo->uid = current->fsuid;
        sbinfo->gid = current->fsgid;
        sbinfo->policy = MPOL_DEFAULT;
+       sbinfo->flags = 0;
        sbinfo->policy_nodes = node_states[N_HIGH_MEMORY];
        sb->s_fs_info = sbinfo;