Merge branch 'hwpoison' of git://git.kernel.org/pub/scm/linux/kernel/git/ak/linux...
[sfrench/cifs-2.6.git] / mm / madvise.c
index 76eb4193acddb2e739b1e42a0a8371b59464f39f..35b1479b7c9d080ed97b772da44603c4f757093c 100644 (file)
@@ -11,6 +11,7 @@
 #include <linux/mempolicy.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 +42,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 +58,18 @@ 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;
        }
 
        if (new_flags == vma->vm_flags) {
@@ -207,41 +218,46 @@ static long madvise_remove(struct vm_area_struct *vma,
        return error;
 }
 
+#ifdef CONFIG_MEMORY_FAILURE
+/*
+ * Error injection support for memory error handling.
+ */
+static int madvise_hwpoison(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(current, current->mm, start, 1,
+                                               0, 0, &p, NULL);
+               if (ret != 1)
+                       return ret;
+               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, 1);
+               put_page(p);
+       }
+       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 +272,17 @@ madvise_behavior_valid(int behavior)
        case MADV_REMOVE:
        case MADV_WILLNEED:
        case MADV_DONTNEED:
+#ifdef CONFIG_KSM
+       case MADV_MERGEABLE:
+       case MADV_UNMERGEABLE:
+#endif
                return 1;
 
        default:
                return 0;
        }
 }
+
 /*
  * The madvise(2) system call.
  *
@@ -286,6 +307,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 +334,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)
+               return madvise_hwpoison(start, start+len_in);
+#endif
        if (!madvise_behavior_valid(behavior))
                return error;