lguest: remove reference from Documentation/virtual/00-INDEX
[linux-2.6.git] / mm / madvise.c
index 76eb419..74bf193 100644 (file)
@@ -9,8 +9,10 @@
 #include <linux/pagemap.h>
 #include <linux/syscalls.h>
 #include <linux/mempolicy.h>
+#include <linux/page-isolation.h>
 #include <linux/hugetlb.h>
 #include <linux/sched.h>
+#include <linux/ksm.h>
 
 /*
  * Any behaviour which results in changes to the vma->vm_flags needs to
@@ -41,7 +43,7 @@ static long madvise_behavior(struct vm_area_struct * vma,
        struct mm_struct * mm = vma->vm_mm;
        int error = 0;
        pgoff_t pgoff;
-       int new_flags = vma->vm_flags;
+       unsigned long new_flags = vma->vm_flags;
 
        switch (behavior) {
        case MADV_NORMAL:
@@ -57,8 +59,24 @@ static long madvise_behavior(struct vm_area_struct * vma,
                new_flags |= VM_DONTCOPY;
                break;
        case MADV_DOFORK:
+               if (vma->vm_flags & VM_IO) {
+                       error = -EINVAL;
+                       goto out;
+               }
                new_flags &= ~VM_DONTCOPY;
                break;
+       case MADV_MERGEABLE:
+       case MADV_UNMERGEABLE:
+               error = ksm_madvise(vma, start, end, behavior, &new_flags);
+               if (error)
+                       goto out;
+               break;
+       case MADV_HUGEPAGE:
+       case MADV_NOHUGEPAGE:
+               error = hugepage_madvise(vma, &new_flags, behavior);
+               if (error)
+                       goto out;
+               break;
        }
 
        if (new_flags == vma->vm_flags) {
@@ -200,48 +218,59 @@ static long madvise_remove(struct vm_area_struct *vma,
        endoff = (loff_t)(end - vma->vm_start - 1)
                        + ((loff_t)vma->vm_pgoff << PAGE_SHIFT);
 
-       /* vmtruncate_range needs to take i_mutex and i_alloc_sem */
+       /* vmtruncate_range needs to take i_mutex */
        up_read(&current->mm->mmap_sem);
        error = vmtruncate_range(mapping->host, offset, endoff);
        down_read(&current->mm->mmap_sem);
        return error;
 }
 
+#ifdef CONFIG_MEMORY_FAILURE
+/*
+ * Error injection support for memory error handling.
+ */
+static int madvise_hwpoison(int bhv, unsigned long start, unsigned long end)
+{
+       int ret = 0;
+
+       if (!capable(CAP_SYS_ADMIN))
+               return -EPERM;
+       for (; start < end; start += PAGE_SIZE) {
+               struct page *p;
+               int ret = get_user_pages_fast(start, 1, 0, &p);
+               if (ret != 1)
+                       return ret;
+               if (bhv == MADV_SOFT_OFFLINE) {
+                       printk(KERN_INFO "Soft offlining page %lx at %lx\n",
+                               page_to_pfn(p), start);
+                       ret = soft_offline_page(p, MF_COUNT_INCREASED);
+                       if (ret)
+                               break;
+                       continue;
+               }
+               printk(KERN_INFO "Injecting memory failure for page %lx at %lx\n",
+                      page_to_pfn(p), start);
+               /* Ignore return value for now */
+               __memory_failure(page_to_pfn(p), 0, MF_COUNT_INCREASED);
+       }
+       return ret;
+}
+#endif
+
 static long
 madvise_vma(struct vm_area_struct *vma, struct vm_area_struct **prev,
                unsigned long start, unsigned long end, int behavior)
 {
-       long error;
-
        switch (behavior) {
-       case MADV_DOFORK:
-               if (vma->vm_flags & VM_IO) {
-                       error = -EINVAL;
-                       break;
-               }
-       case MADV_DONTFORK:
-       case MADV_NORMAL:
-       case MADV_SEQUENTIAL:
-       case MADV_RANDOM:
-               error = madvise_behavior(vma, prev, start, end, behavior);
-               break;
        case MADV_REMOVE:
-               error = madvise_remove(vma, prev, start, end);
-               break;
-
+               return madvise_remove(vma, prev, start, end);
        case MADV_WILLNEED:
-               error = madvise_willneed(vma, prev, start, end);
-               break;
-
+               return madvise_willneed(vma, prev, start, end);
        case MADV_DONTNEED:
-               error = madvise_dontneed(vma, prev, start, end);
-               break;
-
+               return madvise_dontneed(vma, prev, start, end);
        default:
-               BUG();
-               break;
+               return madvise_behavior(vma, prev, start, end, behavior);
        }
-       return error;
 }
 
 static int
@@ -256,12 +285,21 @@ madvise_behavior_valid(int behavior)
        case MADV_REMOVE:
        case MADV_WILLNEED:
        case MADV_DONTNEED:
+#ifdef CONFIG_KSM
+       case MADV_MERGEABLE:
+       case MADV_UNMERGEABLE:
+#endif
+#ifdef CONFIG_TRANSPARENT_HUGEPAGE
+       case MADV_HUGEPAGE:
+       case MADV_NOHUGEPAGE:
+#endif
                return 1;
 
        default:
                return 0;
        }
 }
+
 /*
  * The madvise(2) system call.
  *
@@ -286,6 +324,12 @@ madvise_behavior_valid(int behavior)
  *             so the kernel can free resources associated with it.
  *  MADV_REMOVE - the application wants to free up the given range of
  *             pages and associated backing store.
+ *  MADV_DONTFORK - omit this area from child's address space when forking:
+ *             typically, to avoid COWing pages pinned by get_user_pages().
+ *  MADV_DOFORK - cancel MADV_DONTFORK: no longer omit this area when forking.
+ *  MADV_MERGEABLE - the application recommends that KSM try to merge pages in
+ *             this area with pages of identical content from other such areas.
+ *  MADV_UNMERGEABLE- cancel MADV_MERGEABLE: no longer merge pages with others.
  *
  * return values:
  *  zero    - success
@@ -307,6 +351,10 @@ SYSCALL_DEFINE3(madvise, unsigned long, start, size_t, len_in, int, behavior)
        int write;
        size_t len;
 
+#ifdef CONFIG_MEMORY_FAILURE
+       if (behavior == MADV_HWPOISON || behavior == MADV_SOFT_OFFLINE)
+               return madvise_hwpoison(behavior, start, start+len_in);
+#endif
        if (!madvise_behavior_valid(behavior))
                return error;