@@ -242,3 +242,5 @@ UBSAN_SANITIZE_ubsan.o := n
obj-$(CONFIG_SBITMAP) += sbitmap.o
obj-$(CONFIG_PARMAN) += parman.o
+
+obj-m += test-cfu.o
new file mode 100644
@@ -0,0 +1,107 @@
+/*
+ *
+ * Copyright (c) 2014 Samsung Electronics Co., Ltd.
+ * Author: Andrey Ryabinin <a.ryabinin@samsung.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ */
+
+#define pr_fmt(fmt) "cfu test: %s " fmt, __func__
+
+#include <linux/delay.h>
+#include <linux/kernel.h>
+#include <linux/mman.h>
+#include <linux/mm.h>
+#include <linux/printk.h>
+#include <linux/slab.h>
+#include <linux/string.h>
+#include <linux/uaccess.h>
+#include <linux/module.h>
+
+static char pat[2 * PAGE_SIZE];
+static void run_test(int n, int m, char __user *up, char *kp)
+{
+ int i;
+ for (i = 0; i < 4; i++) {
+ int r;
+ memset(kp, 0, 2 * PAGE_SIZE);
+ r = __copy_from_user_inatomic(kp + i, up, m);
+ if (m <= n) {
+ if (r) {
+ pr_err("bogus fault (%d, %d, %d)\n", r, m, n);
+ return;
+ }
+ } else {
+ if (r < m - n) {
+ pr_err("claims too much (%d, %d, %d)\n", r, m, n);
+ return;
+ }
+ }
+ r = m - r; /* claim to have copied that much */
+ if (memcmp(kp + i, pat + PAGE_SIZE - n, r)) {
+ int j;
+ pr_err("crap in copy (%d, %d, %d)", r, m, n);
+ for (j = 0; j < r; j++) {
+ if (!kp[i+j]) {
+ if (!memcmp(kp + i + j, pat + PAGE_SIZE, PAGE_SIZE)) {
+ pr_cont(" only %d copied\n", j);
+ return;
+ }
+ break;
+ }
+ }
+ pr_cont("\n");
+ return;
+ }
+ if (memcmp(kp + i + r, pat + PAGE_SIZE, PAGE_SIZE)) {
+ pr_err("crap after copy (%d, %d, %d)\n", r, m, n);
+ return;
+ }
+ }
+}
+
+static int __init cfu_test(void)
+{
+ char *kp;
+ char __user *up;
+ int i;
+
+ kp = kmalloc(PAGE_SIZE * 2, GFP_KERNEL);
+ if (!kp)
+ return -EAGAIN;
+
+ up = (char __user *)vm_mmap(NULL, 0, 2 * PAGE_SIZE,
+ PROT_READ | PROT_WRITE | PROT_EXEC,
+ MAP_ANONYMOUS | MAP_PRIVATE, 0);
+ if (IS_ERR(up)) {
+ pr_err("Failed to allocate user memory\n");
+ kfree(kp);
+ return -EAGAIN;
+ }
+ vm_munmap((unsigned long)up + PAGE_SIZE, PAGE_SIZE);
+
+ for (i = 0; i < PAGE_SIZE; i++)
+ pat[i] = 128 | i;
+ if (copy_to_user(up, pat, PAGE_SIZE)) {
+ pr_err("failed to copy to user memory\n");
+ goto out;
+ }
+
+ for (i = 0; i <= 128; i++) {
+ int j;
+ pr_err("trying %d\n", i);
+ for (j = 0; j <= 128; j++)
+ run_test(i, j, up + PAGE_SIZE - i, kp);
+ }
+
+out:
+ vm_munmap((unsigned long)up, PAGE_SIZE);
+ kfree(kp);
+ return -EAGAIN;
+}
+
+module_init(cfu_test);
+MODULE_LICENSE("GPL");