[PATCH] Swap Migration V5: sys_migrate_pages interface
authorChristoph Lameter <clameter@sgi.com>
Sun, 8 Jan 2006 09:00:51 +0000 (01:00 -0800)
committerLinus Torvalds <torvalds@g5.osdl.org>
Mon, 9 Jan 2006 04:12:42 +0000 (20:12 -0800)
sys_migrate_pages implementation using swap based page migration

This is the original API proposed by Ray Bryant in his posts during the first
half of 2005 on linux-mm@kvack.org and linux-kernel@vger.kernel.org.

The intent of sys_migrate is to migrate memory of a process.  A process may
have migrated to another node.  Memory was allocated optimally for the prior
context.  sys_migrate_pages allows to shift the memory to the new node.

sys_migrate_pages is also useful if the processes available memory nodes have
changed through cpuset operations to manually move the processes memory.  Paul
Jackson is working on an automated mechanism that will allow an automatic
migration if the cpuset of a process is changed.  However, a user may decide
to manually control the migration.

This implementation is put into the policy layer since it uses concepts and
functions that are also needed for mbind and friends.  The patch also provides
a do_migrate_pages function that may be useful for cpusets to automatically
move memory.  sys_migrate_pages does not modify policies in contrast to Ray's
implementation.

The current code here is based on the swap based page migration capability and
thus is not able to preserve the physical layout relative to it containing
nodeset (which may be a cpuset).  When direct page migration becomes available
then the implementation needs to be changed to do a isomorphic move of pages
between different nodesets.  The current implementation simply evicts all
pages in source nodeset that are not in the target nodeset.

Patch supports ia64, i386 and x86_64.

Signed-off-by: Christoph Lameter <clameter@sgi.com>
Signed-off-by: Andrew Morton <akpm@osdl.org>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
arch/i386/kernel/syscall_table.S
arch/ia64/kernel/entry.S
arch/x86_64/ia32/ia32entry.S
include/asm-i386/unistd.h
include/asm-ia64/unistd.h
include/asm-x86_64/ia32_unistd.h
include/asm-x86_64/unistd.h
include/linux/mempolicy.h
include/linux/syscalls.h
kernel/sys_ni.c
mm/mempolicy.c

index f7ba4acc20ec3d7049d77b8d417e95e49e94787a..6ff3e524322672a9e0c8b90935a1b439f6c2375a 100644 (file)
@@ -293,3 +293,4 @@ ENTRY(sys_call_table)
        .long sys_inotify_init
        .long sys_inotify_add_watch
        .long sys_inotify_rm_watch
+       .long sys_migrate_pages
index 0741b066b98fd92af00131bc170b47bbcf6264de..7a6ffd6137895f2abd629ecc936f107251ef6b96 100644 (file)
@@ -1600,5 +1600,6 @@ sys_call_table:
        data8 sys_inotify_init
        data8 sys_inotify_add_watch
        data8 sys_inotify_rm_watch
+       data8 sys_migrate_pages                 // 1280
 
        .org sys_call_table + 8*NR_syscalls     // guard against failures to increase NR_syscalls
index df0773c9bdbe70337e236bd6c267e91bedcaf465..1f0ff5adc80e1b0babda930850aa29eb16e4bab0 100644 (file)
@@ -643,6 +643,7 @@ ia32_sys_call_table:
        .quad sys_inotify_init
        .quad sys_inotify_add_watch
        .quad sys_inotify_rm_watch
+       .quad sys_migrate_pages
 ia32_syscall_end:              
        .rept IA32_NR_syscalls-(ia32_syscall_end-ia32_sys_call_table)/8
                .quad ni_syscall
index fe38b9a96233f86fb47f34d0920802f23b3f0542..481c3c0ea720d32c1570a3588ef31da4ac81247b 100644 (file)
 #define __NR_inotify_init      291
 #define __NR_inotify_add_watch 292
 #define __NR_inotify_rm_watch  293
+#define __NR_migrate_pages     294
 
-#define NR_syscalls 294
+#define NR_syscalls 295
 
 /*
  * user-visible error numbers are in the range -1 - -128: see
index 2bf543493cb86675d7b3ec43bd58b8f684e6196b..962f9bd1bdff71c81f6810c9cc144a0086ab2022 100644 (file)
 #define __NR_inotify_init              1277
 #define __NR_inotify_add_watch         1278
 #define __NR_inotify_rm_watch          1279
+#define __NR_migrate_pages             1280
 
 #ifdef __KERNEL__
 
 #include <linux/config.h>
 
-#define NR_syscalls                    256 /* length of syscall table */
+#define NR_syscalls                    270 /* length of syscall table */
 
 #define __ARCH_WANT_SYS_RT_SIGACTION
 
index d5166ec3868dd143dbe211fc35aa29402be7b831..e8843362a6ccda6011d5ca607ebde4193eab540d 100644 (file)
 #define __NR_ia32_inotify_init         291
 #define __NR_ia32_inotify_add_watch    292
 #define __NR_ia32_inotify_rm_watch     293
+#define __NR_ia32_migrate_pages                294
 
-#define IA32_NR_syscalls 294   /* must be > than biggest syscall! */
+#define IA32_NR_syscalls 295   /* must be > than biggest syscall! */
 
 #endif /* _ASM_X86_64_IA32_UNISTD_H_ */
index 2c42150bce0c372e651f6736ae14f3cf70829ccb..e6f896161c1193d60043db9ff6ffebb372e63385 100644 (file)
@@ -571,8 +571,10 @@ __SYSCALL(__NR_inotify_init, sys_inotify_init)
 __SYSCALL(__NR_inotify_add_watch, sys_inotify_add_watch)
 #define __NR_inotify_rm_watch  255
 __SYSCALL(__NR_inotify_rm_watch, sys_inotify_rm_watch)
+#define __NR_migrate_pages     256
+__SYSCALL(__NR_migrate_pages, sys_migrate_pages)
 
-#define __NR_syscall_max __NR_inotify_rm_watch
+#define __NR_syscall_max __NR_migrate_pages
 #ifndef __NO_STUBS
 
 /* user-visible error numbers are in the range -1 - -4095 */
index 05443a766cb8a7d4f150866a74b30c469d3ee957..3e61e829681dbb3817be224002498c0765f22848 100644 (file)
@@ -162,6 +162,9 @@ static inline void check_highest_zone(int k)
                policy_zone = k;
 }
 
+int do_migrate_pages(struct mm_struct *mm,
+       const nodemask_t *from_nodes, const nodemask_t *to_nodes, int flags);
+
 #else
 
 struct mempolicy {};
index c7007b1db91d6beece5fd0143c0f519304e629c4..e910d1a481df6d37b6b4f12ee0f99657fe8c1db5 100644 (file)
@@ -511,5 +511,7 @@ asmlinkage long sys_ioprio_set(int which, int who, int ioprio);
 asmlinkage long sys_ioprio_get(int which, int who);
 asmlinkage long sys_set_mempolicy(int mode, unsigned long __user *nmask,
                                        unsigned long maxnode);
+asmlinkage long sys_migrate_pages(pid_t pid, unsigned long maxnode,
+                       const unsigned long __user *from, const unsigned long __user *to);
 
 #endif
index 1ab2370e2efaee04f62334ae98a778ed3bbf9398..7a8bc7f60d914e8a2e3bb5581e3667048a85d33e 100644 (file)
@@ -82,6 +82,7 @@ cond_syscall(compat_sys_socketcall);
 cond_syscall(sys_inotify_init);
 cond_syscall(sys_inotify_add_watch);
 cond_syscall(sys_inotify_rm_watch);
+cond_syscall(sys_migrate_pages);
 
 /* arch-specific weak syscall entries */
 cond_syscall(sys_pciconfig_read);
index 9cc6d962831dd362c0f22c2a2588a4a907f51f64..20d5ad39fa411052a2f9554cf293b58ba926ddfd 100644 (file)
@@ -614,12 +614,42 @@ long do_get_mempolicy(int *policy, nodemask_t *nmask,
        return err;
 }
 
+/*
+ * For now migrate_pages simply swaps out the pages from nodes that are in
+ * the source set but not in the target set. In the future, we would
+ * want a function that moves pages between the two nodesets in such
+ * a way as to preserve the physical layout as much as possible.
+ *
+ * Returns the number of page that could not be moved.
+ */
+int do_migrate_pages(struct mm_struct *mm,
+       const nodemask_t *from_nodes, const nodemask_t *to_nodes, int flags)
+{
+       LIST_HEAD(pagelist);
+       int count = 0;
+       nodemask_t nodes;
+
+       nodes_andnot(nodes, *from_nodes, *to_nodes);
+       nodes_complement(nodes, nodes);
+
+       down_read(&mm->mmap_sem);
+       check_range(mm, mm->mmap->vm_start, TASK_SIZE, &nodes,
+                       flags | MPOL_MF_DISCONTIG_OK, &pagelist);
+       if (!list_empty(&pagelist)) {
+               migrate_pages(&pagelist, NULL);
+               if (!list_empty(&pagelist))
+                       count = putback_lru_pages(&pagelist);
+       }
+       up_read(&mm->mmap_sem);
+       return count;
+}
+
 /*
  * User space interface with variable sized bitmaps for nodelists.
  */
 
 /* Copy a node mask from user space. */
-static int get_nodes(nodemask_t *nodes, unsigned long __user *nmask,
+static int get_nodes(nodemask_t *nodes, const unsigned long __user *nmask,
                     unsigned long maxnode)
 {
        unsigned long k;
@@ -708,6 +738,68 @@ asmlinkage long sys_set_mempolicy(int mode, unsigned long __user *nmask,
        return do_set_mempolicy(mode, &nodes);
 }
 
+/* Macro needed until Paul implements this function in kernel/cpusets.c */
+#define cpuset_mems_allowed(task) node_online_map
+
+asmlinkage long sys_migrate_pages(pid_t pid, unsigned long maxnode,
+               const unsigned long __user *old_nodes,
+               const unsigned long __user *new_nodes)
+{
+       struct mm_struct *mm;
+       struct task_struct *task;
+       nodemask_t old;
+       nodemask_t new;
+       nodemask_t task_nodes;
+       int err;
+
+       err = get_nodes(&old, old_nodes, maxnode);
+       if (err)
+               return err;
+
+       err = get_nodes(&new, new_nodes, maxnode);
+       if (err)
+               return err;
+
+       /* Find the mm_struct */
+       read_lock(&tasklist_lock);
+       task = pid ? find_task_by_pid(pid) : current;
+       if (!task) {
+               read_unlock(&tasklist_lock);
+               return -ESRCH;
+       }
+       mm = get_task_mm(task);
+       read_unlock(&tasklist_lock);
+
+       if (!mm)
+               return -EINVAL;
+
+       /*
+        * Check if this process has the right to modify the specified
+        * process. The right exists if the process has administrative
+        * capabilities, superuser priviledges or the same
+        * userid as the target process.
+        */
+       if ((current->euid != task->suid) && (current->euid != task->uid) &&
+           (current->uid != task->suid) && (current->uid != task->uid) &&
+           !capable(CAP_SYS_ADMIN)) {
+               err = -EPERM;
+               goto out;
+       }
+
+       task_nodes = cpuset_mems_allowed(task);
+       /* Is the user allowed to access the target nodes? */
+       if (!nodes_subset(new, task_nodes) && !capable(CAP_SYS_ADMIN)) {
+               err = -EPERM;
+               goto out;
+       }
+
+       err = do_migrate_pages(mm, &old, &new, MPOL_MF_MOVE);
+out:
+       mmput(mm);
+       return err;
+}
+
+
 /* Retrieve NUMA policy */
 asmlinkage long sys_get_mempolicy(int __user *policy,
                                unsigned long __user *nmask,