513 lines
13 KiB
Diff
513 lines
13 KiB
Diff
From 03d188c492efe079a520319ca48e40843367ddcf Mon Sep 17 00:00:00 2001
|
|
From: "fu.lin" <fulin10@huawei.com>
|
|
Date: Fri, 18 Feb 2022 16:22:00 +0800
|
|
Subject: [PATCH 71/72] mod: add criu-indepent test
|
|
|
|
Signed-off-by: fu.lin <fulin10@huawei.com>
|
|
---
|
|
test/modules/Makefile | 21 ++++++
|
|
test/modules/idr.c | 79 +++++++++++++++++++++
|
|
test/modules/jump_table.c | 107 ++++++++++++++++++++++++++++
|
|
test/modules/var_kern.c | 72 +++++++++++++++++++
|
|
test/modules/var_user.py | 40 +++++++++++
|
|
test/modules/workqueue_kern.c | 130 ++++++++++++++++++++++++++++++++++
|
|
6 files changed, 449 insertions(+)
|
|
create mode 100644 test/modules/Makefile
|
|
create mode 100644 test/modules/idr.c
|
|
create mode 100644 test/modules/jump_table.c
|
|
create mode 100644 test/modules/var_kern.c
|
|
create mode 100644 test/modules/var_user.py
|
|
create mode 100644 test/modules/workqueue_kern.c
|
|
|
|
diff --git a/test/modules/Makefile b/test/modules/Makefile
|
|
new file mode 100644
|
|
index 0000000..9458aa7
|
|
--- /dev/null
|
|
+++ b/test/modules/Makefile
|
|
@@ -0,0 +1,21 @@
|
|
+obj-m := var_kern.o workqueue_kern.o jump_table.o idr.o
|
|
+
|
|
+KDIR := /lib/modules/`uname -r`/build
|
|
+
|
|
+all:
|
|
+ make -C $(KDIR) M=$(PWD) modules
|
|
+
|
|
+clean:
|
|
+ make -C $(KDIR) M=$(PWD) clean
|
|
+
|
|
+var_kern.ko:
|
|
+ make -C $(KDIR) M=$(PWD) var_kern.ko
|
|
+
|
|
+workqueue_kern.ko:
|
|
+ make -C $(KDIR) M=$(PWD) workqueue_kern.ko
|
|
+
|
|
+jump_table.ko:
|
|
+ make -C $(KDIR) M=$(PWD) jump_table.ko
|
|
+
|
|
+idr.ko:
|
|
+ make -C $(KDIR) M=$(PWD) idr.ko
|
|
diff --git a/test/modules/idr.c b/test/modules/idr.c
|
|
new file mode 100644
|
|
index 0000000..67f248e
|
|
--- /dev/null
|
|
+++ b/test/modules/idr.c
|
|
@@ -0,0 +1,79 @@
|
|
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
|
|
+
|
|
+#include <linux/init.h>
|
|
+#include <linux/module.h>
|
|
+#include <linux/idr.h>
|
|
+#include <linux/modrestore.h>
|
|
+
|
|
+DEFINE_IDR(idr_head);
|
|
+const int placeholder = 0;
|
|
+static int idr_uid = 0;
|
|
+
|
|
+static int idr_test_show_internal(int id, void *p, void *data)
|
|
+{
|
|
+ pr_info("id: %d p %pK\n", id, p);
|
|
+ sprintf(data+strlen(data), "%d\n", id);
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static ssize_t idr_test_show(struct kobject *kobj,
|
|
+ struct kobj_attribute *attr,
|
|
+ char *buf)
|
|
+{
|
|
+ idr_for_each(&idr_head, idr_test_show_internal, buf);
|
|
+ return strlen(buf);
|
|
+}
|
|
+
|
|
+static ssize_t idr_test_store(struct kobject *kobj,
|
|
+ struct kobj_attribute *attr,
|
|
+ const char *buf, size_t count)
|
|
+{
|
|
+ const unsigned long max = 65536;
|
|
+ unsigned id = 0;
|
|
+ int retval;
|
|
+
|
|
+ if (sscanf(buf, "%u", &id) != 1) {
|
|
+ pr_err("sscanf empty\n");
|
|
+ return -EINVAL;
|
|
+ }
|
|
+
|
|
+ retval = idr_alloc_u32(&idr_head, (void *)&placeholder, &id, max, GFP_KERNEL);
|
|
+ pr_info("alloc idr id %u, errno %d\n", id, retval);
|
|
+ return retval < 0 ? retval : count;
|
|
+}
|
|
+
|
|
+static struct kobj_attribute idr_test = __ATTR_RW(idr_test);
|
|
+
|
|
+static int __init mod_init(void)
|
|
+{
|
|
+ return sysfs_create_file(kernel_kobj, &idr_test.attr);
|
|
+}
|
|
+
|
|
+static void __exit mod_exit(void)
|
|
+{
|
|
+ sysfs_remove_file(kernel_kobj, &idr_test.attr);
|
|
+ idr_destroy(&idr_head);
|
|
+ return;
|
|
+}
|
|
+
|
|
+static int __init mod_resume(void)
|
|
+{
|
|
+ int retval = mures_restore_idr(idr_uid, &idr_head);
|
|
+
|
|
+ if (retval == 0)
|
|
+ retval = sysfs_create_file(kernel_kobj, &idr_test.attr);
|
|
+ return retval;
|
|
+}
|
|
+
|
|
+static int __exit mod_suspend(void)
|
|
+{
|
|
+ sysfs_remove_file(kernel_kobj, &idr_test.attr);
|
|
+ return mures_save_idr(idr_uid, &idr_head);
|
|
+}
|
|
+
|
|
+module_init(mod_init);
|
|
+module_exit(mod_exit);
|
|
+module_resume(mod_resume);
|
|
+module_suspend(mod_suspend);
|
|
+
|
|
+MODULE_LICENSE("GPL");
|
|
\ No newline at end of file
|
|
diff --git a/test/modules/jump_table.c b/test/modules/jump_table.c
|
|
new file mode 100644
|
|
index 0000000..8648c2a
|
|
--- /dev/null
|
|
+++ b/test/modules/jump_table.c
|
|
@@ -0,0 +1,107 @@
|
|
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
|
|
+
|
|
+#include <linux/init.h>
|
|
+#include <linux/module.h>
|
|
+#include <linux/hashtable.h>
|
|
+#include <linux/sysfs.h>
|
|
+#include <linux/modrestore.h>
|
|
+
|
|
+struct func_node {
|
|
+ struct hlist_node hash;
|
|
+ unsigned long key;
|
|
+ unsigned long value;
|
|
+};
|
|
+
|
|
+static int status __attribute__((section(".resume_0")));
|
|
+
|
|
+/*
|
|
+ * The `mures_vcall()` can't used in irq context because of the implementation.
|
|
+ * Therefore, we must generate cache.
|
|
+ */
|
|
+DEFINE_HASHTABLE(__ro_after_init cache, 2);
|
|
+
|
|
+static int foo(void)
|
|
+{
|
|
+ status += 1;
|
|
+ return status;
|
|
+}
|
|
+
|
|
+static void *find_func(unsigned long addr);
|
|
+
|
|
+static ssize_t jp_test_show(struct kobject *kobj,
|
|
+ struct kobj_attribute *attr,
|
|
+ char *buf)
|
|
+{
|
|
+ int (*func)(void) = find_func((unsigned long)foo);
|
|
+ ssize_t count = 0;
|
|
+
|
|
+ if (func == NULL) {
|
|
+ count = sprintf(buf, "Not Found\n");
|
|
+ } else {
|
|
+ count = sprintf(buf, "%d", func());
|
|
+ }
|
|
+
|
|
+ return count;
|
|
+}
|
|
+
|
|
+static struct kobj_attribute jp_test = __ATTR_RO(jp_test);
|
|
+
|
|
+struct func_node nodes[] __ro_after_init = {
|
|
+ { .key = (unsigned long)foo, },
|
|
+};
|
|
+
|
|
+static void *find_func(unsigned long addr)
|
|
+{
|
|
+ struct func_node *obj;
|
|
+ int i;
|
|
+
|
|
+ pr_info("finding addr: %lx\n", addr);
|
|
+ hash_for_each(cache, i, obj, hash) {\
|
|
+ pr_info("found key: %lx, val: %lx\n", obj->key, obj->value);
|
|
+ if (obj->key == addr)
|
|
+ return (void *)obj->value;
|
|
+ }
|
|
+
|
|
+ return NULL;
|
|
+}
|
|
+
|
|
+static void __init build_cache(void)
|
|
+{
|
|
+ int i;
|
|
+
|
|
+ for (i = 0; i < ARRAY_SIZE(nodes); i++) {
|
|
+ nodes[i].value = mures_vcall(nodes[i].key);
|
|
+ hash_add(cache, &nodes[i].hash, nodes[i].key);
|
|
+ }
|
|
+}
|
|
+
|
|
+static int __init mod_init(void)
|
|
+{
|
|
+ build_cache();
|
|
+ return sysfs_create_file(kernel_kobj, &jp_test.attr);
|
|
+}
|
|
+
|
|
+static void __exit mod_exit(void)
|
|
+{
|
|
+ sysfs_remove_file(kernel_kobj, &jp_test.attr);
|
|
+ return;
|
|
+}
|
|
+
|
|
+static int __init mod_resume(void)
|
|
+{
|
|
+ build_cache();
|
|
+ return sysfs_create_file(kernel_kobj, &jp_test.attr);
|
|
+}
|
|
+
|
|
+static int __exit mod_suspend(void)
|
|
+{
|
|
+ sysfs_remove_file(kernel_kobj, &jp_test.attr);
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+module_init(mod_init);
|
|
+module_exit(mod_exit);
|
|
+module_resume(mod_resume);
|
|
+module_suspend(mod_suspend);
|
|
+
|
|
+MODULE_LICENSE("GPL");
|
|
\ No newline at end of file
|
|
diff --git a/test/modules/var_kern.c b/test/modules/var_kern.c
|
|
new file mode 100644
|
|
index 0000000..4321e3b
|
|
--- /dev/null
|
|
+++ b/test/modules/var_kern.c
|
|
@@ -0,0 +1,72 @@
|
|
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
|
|
+
|
|
+#include <linux/init.h>
|
|
+#include <linux/module.h>
|
|
+#include <linux/sysfs.h>
|
|
+
|
|
+/* test variable persistence */
|
|
+
|
|
+static int mod_int __attribute__((section(".resume_0")));
|
|
+static char *mod_str1 __attribute__((section(".resume_1"))) = "init";
|
|
+static char *mod_str2 __attribute__((section(".resume_2"))) = "upgrade";
|
|
+static char *mod_str __attribute__((section(".resume_3")));
|
|
+
|
|
+static ssize_t var_test_show(struct kobject *kobj,
|
|
+ struct kobj_attribute *attr,
|
|
+ char *buf)
|
|
+{
|
|
+ ssize_t count = 0;
|
|
+
|
|
+ count += sprintf(buf, "%d", mod_int);
|
|
+ count += sprintf(buf+count, " %s", mod_str);
|
|
+
|
|
+ return count;
|
|
+}
|
|
+
|
|
+static struct kobj_attribute sysfs_var = __ATTR_RO(var_test);
|
|
+
|
|
+static __init int mod1_resume(void)
|
|
+{
|
|
+ mod_int += 1;
|
|
+ mod_str = mod_str2;
|
|
+
|
|
+ pr_info("This is %s, index %d\n", __func__, mod_int);
|
|
+
|
|
+ return sysfs_create_file(kernel_kobj, &sysfs_var.attr);
|
|
+}
|
|
+
|
|
+static __exit int mod1_suspend(void)
|
|
+{
|
|
+ mod_int += 1;
|
|
+
|
|
+ pr_info("This is %s, index %d\n", __func__, mod_int);
|
|
+ sysfs_remove_file(kernel_kobj, &sysfs_var.attr);
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static __init int mod1_init(void)
|
|
+{
|
|
+ mod_int = 0;
|
|
+ mod_str = mod_str1;
|
|
+
|
|
+ pr_info("This is %s, index %d\n", __func__, mod_int);
|
|
+
|
|
+ return sysfs_create_file(kernel_kobj, &sysfs_var.attr);
|
|
+}
|
|
+
|
|
+static __exit void mod1_exit(void)
|
|
+{
|
|
+ mod_int += 1;
|
|
+
|
|
+ pr_info("This is %s, index %d\n", __func__, mod_int);
|
|
+ sysfs_remove_file(kernel_kobj, &sysfs_var.attr);
|
|
+
|
|
+ return;
|
|
+}
|
|
+
|
|
+module_resume(mod1_resume);
|
|
+module_suspend(mod1_suspend);
|
|
+module_init(mod1_init);
|
|
+module_exit(mod1_exit);
|
|
+MODULE_LICENSE("GPL");
|
|
diff --git a/test/modules/var_user.py b/test/modules/var_user.py
|
|
new file mode 100644
|
|
index 0000000..98c5193
|
|
--- /dev/null
|
|
+++ b/test/modules/var_user.py
|
|
@@ -0,0 +1,40 @@
|
|
+import unittest
|
|
+import subprocess
|
|
+
|
|
+
|
|
+class TestVarMethods(unittest.TestCase):
|
|
+ mod_name = "var_kern"
|
|
+
|
|
+ def unload_mod(self):
|
|
+ with open("/proc/modules") as f:
|
|
+ for line in f.readlines():
|
|
+ words = line.split()
|
|
+ if words[0] == self.mod_name:
|
|
+ subprocess.check_call(["rmmod", self.mod_name])
|
|
+ break
|
|
+
|
|
+ def setUp(self):
|
|
+ subprocess.check_call(["make", "var_kern.ko"])
|
|
+ self.unload_mod()
|
|
+
|
|
+ def tearDown(self):
|
|
+ mod = f"{self.mod_name}.ko"
|
|
+ self.unload_mod()
|
|
+
|
|
+ def test_var(self):
|
|
+ mod = f"{self.mod_name}.ko"
|
|
+ subprocess.check_call(["insmod", mod])
|
|
+ with open("/sys/kernel/var_test") as f:
|
|
+ line = f.readline()
|
|
+ self.assertEqual(line, "0 init")
|
|
+ subprocess.check_call(["rmmod", "-r", mod])
|
|
+ subprocess.check_call(["rmmod", mod])
|
|
+ subprocess.check_call(["insmod", "-r", mod])
|
|
+ with open("/sys/kernel/var_test") as f:
|
|
+ line = f.readline()
|
|
+ self.assertEqual(line, "2 upgrade")
|
|
+ subprocess.check_call(["rmmod", mod])
|
|
+
|
|
+
|
|
+if __name__ == '__main__':
|
|
+ unittest.main()
|
|
diff --git a/test/modules/workqueue_kern.c b/test/modules/workqueue_kern.c
|
|
new file mode 100644
|
|
index 0000000..cecfb8c
|
|
--- /dev/null
|
|
+++ b/test/modules/workqueue_kern.c
|
|
@@ -0,0 +1,130 @@
|
|
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
|
|
+
|
|
+#include <linux/init.h>
|
|
+#include <linux/module.h>
|
|
+#include <linux/slab.h>
|
|
+#include <linux/sysfs.h>
|
|
+#include <linux/delay.h>
|
|
+#include <linux/modrestore.h>
|
|
+
|
|
+struct mod_status {
|
|
+ struct workqueue_struct *wq;
|
|
+};
|
|
+
|
|
+static struct workqueue_struct *wq;
|
|
+static int wq_status __attribute__((section(".resume_0")));
|
|
+
|
|
+static void worker_func(struct work_struct *work)
|
|
+{
|
|
+ wq_status += 1;
|
|
+ pr_info("worker run...\n");
|
|
+ mdelay(100);
|
|
+ pr_info("worker end.\n");
|
|
+ kfree(work);
|
|
+}
|
|
+
|
|
+static ssize_t wq_test_show(struct kobject *kobj,
|
|
+ struct kobj_attribute *attr,
|
|
+ char *buf)
|
|
+{
|
|
+ flush_workqueue(wq);
|
|
+ return sprintf(buf, "%pK %d", wq, wq_status);
|
|
+}
|
|
+
|
|
+static struct kobj_attribute wq_test = __ATTR_RO(wq_test);
|
|
+
|
|
+static int __init mod_init(void)
|
|
+{
|
|
+ int retval;
|
|
+
|
|
+ retval = sysfs_create_file(kernel_kobj, &wq_test.attr);
|
|
+ if (retval != 0) {
|
|
+ pr_err("sysfs_create_file failed.\n");
|
|
+ return retval;
|
|
+ }
|
|
+
|
|
+ wq = alloc_workqueue("workqueue_kern_test", WQ_UNBOUND, 0);
|
|
+ if (wq == NULL) {
|
|
+ pr_err("unable to allocate workqueue\n");
|
|
+ sysfs_remove_file(kernel_kobj, &wq_test.attr);
|
|
+ retval = -ENOMEM;
|
|
+ goto out;
|
|
+ }
|
|
+
|
|
+ retval = 0;
|
|
+out:
|
|
+ return retval;
|
|
+}
|
|
+
|
|
+static void __exit mod_exit(void)
|
|
+{
|
|
+ destroy_workqueue(wq);
|
|
+ sysfs_remove_file(kernel_kobj, &wq_test.attr);
|
|
+}
|
|
+
|
|
+static int __init mod_resume(void)
|
|
+{
|
|
+ struct mod_status *data;
|
|
+ int retval;
|
|
+
|
|
+ data = get_module_state_space(KBUILD_MODNAME, NULL);
|
|
+ if (!data) {
|
|
+ pr_info("get_module_state_space failure\n");
|
|
+ return -ENOMEM;
|
|
+ }
|
|
+ wq = data->wq;
|
|
+
|
|
+ retval = sysfs_create_file(kernel_kobj, &wq_test.attr);
|
|
+ if (retval != 0) {
|
|
+ pr_err("sysfs_create_file failed.\n");
|
|
+ return retval;
|
|
+ }
|
|
+
|
|
+ return resume_workqueue(wq);
|
|
+}
|
|
+
|
|
+static int __exit queue_worker(void)
|
|
+{
|
|
+ struct delayed_work *worker = kzalloc(sizeof(struct work_struct), GFP_KERNEL);
|
|
+
|
|
+ if (worker == NULL) {
|
|
+ pr_err("alloc worker space failed\n");
|
|
+ return -ENOMEM;
|
|
+ }
|
|
+
|
|
+ INIT_DELAYED_WORK(worker, worker_func);
|
|
+ queue_delayed_work(wq, worker, 100);
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static int __exit mod_suspend(void)
|
|
+{
|
|
+ struct mod_status *data;
|
|
+ int retval;
|
|
+
|
|
+ data = alloc_module_state_space(KBUILD_MODNAME, sizeof(*data));
|
|
+ if (data == NULL) {
|
|
+ pr_err("alloc_module_state_space failed\n");
|
|
+ return -ENOMEM;
|
|
+ }
|
|
+
|
|
+ data->wq = wq;
|
|
+ if (queue_worker() != 0)
|
|
+ return -ENOMEM;
|
|
+
|
|
+ retval = suspend_workqueue(wq);
|
|
+ if (retval != 0) {
|
|
+ pr_err("suspend workqueue failed\n");
|
|
+ return retval;
|
|
+ }
|
|
+
|
|
+ sysfs_remove_file(kernel_kobj, &wq_test.attr);
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+module_init(mod_init);
|
|
+module_exit(mod_exit);
|
|
+module_resume(mod_resume);
|
|
+module_suspend(mod_suspend);
|
|
+
|
|
+MODULE_LICENSE("GPL");
|
|
\ No newline at end of file
|
|
--
|
|
2.34.1
|
|
|