- char dev `ioctl({IOCTL_CMD_NEEDREPAIR, IOCTL_CMD_REPAIR})` checkpoint/restore test - anonymous inode checkpoint/restore test --- test/zdtm/customization/Makefile | 3 +- test/zdtm/customization/chardev00.c | 65 +++++++++++ test/zdtm/customization/chardev00.desc | 1 + test/zdtm/mod/Makefile | 5 +- test/zdtm/mod/anon_inode.c | 148 +++++++++++++++++++++++++ 5 files changed, 220 insertions(+), 2 deletions(-) create mode 100644 test/zdtm/customization/chardev00.c create mode 100644 test/zdtm/customization/chardev00.desc create mode 100644 test/zdtm/mod/anon_inode.c
diff --git a/test/zdtm/customization/Makefile b/test/zdtm/customization/Makefile index 93922c7..7d08db3 100644 --- a/test/zdtm/customization/Makefile +++ b/test/zdtm/customization/Makefile @@ -11,7 +11,8 @@ TST_NOFILE = \ maps05 \ maps007 \ maps008 \ - notifier00 + notifier00 \ + chardev00
TST_FILE = \ maps00 \ diff --git a/test/zdtm/customization/chardev00.c b/test/zdtm/customization/chardev00.c new file mode 100644 index 0000000..c708699 --- /dev/null +++ b/test/zdtm/customization/chardev00.c @@ -0,0 +1,65 @@ +#include <stdio.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> +#include <sys/ioctl.h> +#include "zdtmtst.h" + +#define CHARDEV_PATH "/dev/anon_test" + +const char *test_doc="Tests char dev and anonmous inode map checkpoint/restore"; + +static int check_maps(unsigned long addr) +{ + FILE *fp = fopen("/proc/self/maps", "r"); + char *line = NULL; + size_t n = 0; + unsigned long start = 0; + + if (fp == NULL) { + pr_perror("open self maps failed"); + return -1; + } + + while (getline(&line, &n, fp) != -1) { + test_msg("%s", line); + sscanf(line, "%lx-", &start); + if (start == addr) + return 0; + } + + return -1; +} + +int main(int argc, char *argv[]) +{ + int fd, retval = 0; + unsigned long addr; + + test_init(argc, argv); + + fd = open(CHARDEV_PATH, O_RDWR); + if (fd < 0) { + pr_perror("open '%s' failed", CHARDEV_PATH); + return -1; + } + + retval = ioctl(fd, 0, &addr); + if (retval < 0) { + pr_perror("create anonymous map failed"); + retval = -1; + goto out; + } + test_msg("create anonymous vma start 0x%lx\n", addr); + + test_daemon(); + test_waitsig(); + + retval = check_maps(addr); + if (retval == 0) + pass(); + else + fail("anonymous inode map don't restore"); +out: + return retval; +} diff --git a/test/zdtm/customization/chardev00.desc b/test/zdtm/customization/chardev00.desc new file mode 100644 index 0000000..9c51ba8 --- /dev/null +++ b/test/zdtm/customization/chardev00.desc @@ -0,0 +1 @@ +{'arch': 'aarch64', 'opts': '--dump-char-dev', 'flavor': 'h', 'flags': 'suid excl', 'sysfs': '/sys/kernel/modrestore/anon_state_restore /sys/kernel/repairing_device', 'mod': 'anon_inode.ko'} diff --git a/test/zdtm/mod/Makefile b/test/zdtm/mod/Makefile index 10c9c9a..0bc89f7 100644 --- a/test/zdtm/mod/Makefile +++ b/test/zdtm/mod/Makefile @@ -2,7 +2,7 @@ # `ARCH` var is used in both criu and kernel, but they have the different value # for the same architecture(e.g. arm64). Therefore, this Makefile can't be # included in the criu Makefile. -obj-m += notifier.o +obj-m += notifier.o anon_inode.o
# specific the kernel devel path # example (use `/home/me/kernel` as `KDIR`): @@ -26,3 +26,6 @@ clean:
notifier.ko: $(MAKE) -C $(KDIR) M=$(MOD) notifier.ko + +anon_inode.ko: + $(MAKE) -C $(KDIR) M=$(MOD) anon_inode.ko diff --git a/test/zdtm/mod/anon_inode.c b/test/zdtm/mod/anon_inode.c new file mode 100644 index 0000000..d9c7d2a --- /dev/null +++ b/test/zdtm/mod/anon_inode.c @@ -0,0 +1,148 @@ +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include <linux/init.h> +#include <linux/module.h> +#include <linux/miscdevice.h> +#include <linux/fs.h> +#include <linux/mm.h> +#include <linux/mman.h> +#include <linux/anon_inodes.h> +#include <linux/file.h> +#include <linux/modrestore.h> + +static int anon_mmap(struct file *file, struct vm_area_struct *vma) +{ + pr_info("call %s\n", __func__); + return 0; +} + +static const struct file_operations none_fops = { + .owner = THIS_MODULE, + .mmap = anon_mmap, +}; + +static unsigned long create_mmap(void) +{ + struct file *filp; + unsigned long start; + + pr_info("call %s\n", __func__); + filp = anon_inode_getfile("test", &none_fops, NULL, O_RDWR); + if (IS_ERR(filp)) { + pr_warn("anon_inode_getfile('test') failed: %d\n", (int)PTR_ERR(filp)); + return PTR_ERR(filp); + } + + start = vm_mmap(filp, 0, 1<<20, PROT_READ | PROT_WRITE, MAP_SHARED, 0); + if (IS_ERR_VALUE(start)) { + pr_warn("vm_mmap failed with: %d\n", (int)PTR_ERR((void *)start)); + } + + fput(filp); + + return start; +} + +static int anon_inode_notifier(struct notifier_block *nb, + unsigned long action, void *data) +{ + struct vma_anon_entry *vma_entry = data; + struct file *filp; + unsigned long start; + + filp = anon_inode_getfile("test", &none_fops, NULL, O_RDWR); + if (IS_ERR(filp)) { + pr_warn("anon_inode_getfile('test') failed: %d\n", (int)PTR_ERR(filp)); + return 0; + } + + start = vm_mmap(filp, vma_entry->start, vma_entry -> end-vma_entry->start, + PROT_READ | PROT_WRITE, MAP_SHARED, 0); + if (start != vma_entry->start) + pr_warn("vm_mmap() failed: %#lx\n", start); + + fput(filp); + return 0; +} + +static long anon_ioctl(struct file *file, unsigned int cmd, unsigned long argp) +{ + unsigned long start; + + switch (cmd) { + case 0: + start = create_mmap(); + if (IS_ERR_VALUE(start)) + return -EINVAL; + if (put_user(start, (unsigned long __user *)argp)) + return -EFAULT; + break; + case IOCTL_CMD_NEEDREPAIR: + pr_info("call IOCTL_CMD_NEEDREPAIR"); + /* do nothing, just a request slot */ + return 17173; + case IOCTL_CMD_REPAIR: + pr_info("call IOCTL_CMD_REPAIR"); + /* do nothing, just a request slot */ + break; + default: + pr_warn("wrong cmd\n"); + return -EINVAL; + } + + return 0; +} + +static const struct file_operations anon_fops = { + .owner = THIS_MODULE, + .unlocked_ioctl = anon_ioctl, + .compat_ioctl = anon_ioctl, +}; + +static struct miscdevice anon_dev = { + .minor = MISC_DYNAMIC_MINOR, + .name = "anon_test", + .fops = &anon_fops, +}; + +static struct notifier_block anon_inode_nb = { + .notifier_call = anon_inode_notifier, +}; + +static int __init anon_init(void) +{ + int retval; + + retval = mures_add_devname(anon_dev.name); + if (retval != 0) + goto out; + + retval = register_anon_notifier(&anon_inode_nb); + if (retval != 0) + goto del_devname; + + retval = misc_register(&anon_dev); + if (retval != 0) + goto del_notifier; + + return 0; + +del_notifier: + unregister_anon_notifier(&anon_inode_nb); +del_devname: + mures_del_devname(anon_dev.name); +out: + return retval; +} + +static void __exit anon_exit(void) +{ + mures_del_devname(anon_dev.name); + unregister_anon_notifier(&anon_inode_nb); + misc_deregister(&anon_dev); + return; +} + +module_init(anon_init); +module_exit(anon_exit); +MODULE_LICENSE("GPL");