diff --git a/lib/Kconfig.debug b/lib/Kconfig.debug index e7b5b654ed8d8a6d5a07fe324b14272f513332b9..304e75f832dceb59dab5cdb0fbd8a2e3571bdc08 100644 --- a/lib/Kconfig.debug +++ b/lib/Kconfig.debug @@ -1841,6 +1841,15 @@ config MEMTEST memtest=17, mean do 17 test patterns. If you are unsure how to answer this question, answer N. +config TEST_JUMP_LABEL + tristate "Test jump label" + default n + depends on m + help + Test jump labels. + + If unsure, say N. + source "samples/Kconfig" source "lib/Kconfig.kgdb" diff --git a/lib/Makefile b/lib/Makefile index 6897b527581a8d9fcb65b4a2e01815d3d34498ed..14f6e07e33a823f3e990305e3defaa6c531fb208 100644 --- a/lib/Makefile +++ b/lib/Makefile @@ -39,6 +39,8 @@ obj-$(CONFIG_TEST_KSTRTOX) += test-kstrtox.o obj-$(CONFIG_TEST_LKM) += test_module.o obj-$(CONFIG_TEST_RHASHTABLE) += test_rhashtable.o obj-$(CONFIG_TEST_USER_COPY) += test_user_copy.o +obj-$(CONFIG_TEST_JUMP_LABEL) += test_jump_label.o +obj-$(CONFIG_TEST_JUMP_LABEL) += test_jump_label_base.o ifeq ($(CONFIG_DEBUG_KOBJECT),y) CFLAGS_kobject.o += -DDEBUG diff --git a/lib/test_jump_label.c b/lib/test_jump_label.c new file mode 100644 index 0000000000000000000000000000000000000000..2fe741329b0e201731eb71a24f2744e6dffe6148 --- /dev/null +++ b/lib/test_jump_label.c @@ -0,0 +1,225 @@ +/* + * Kernel module for testing jump labels. + * + * Copyright 2015 Akamai Technologies Inc. All Rights Reserved + * + * Authors: + * Jason Baron + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include +#include + +/* old keys */ +struct static_key old_true_key = STATIC_KEY_INIT_TRUE; +struct static_key old_false_key = STATIC_KEY_INIT_FALSE; + +/* new api */ +DEFINE_STATIC_KEY_TRUE(true_key); +DEFINE_STATIC_KEY_FALSE(false_key); + +/* external */ +extern struct static_key base_old_true_key; +extern struct static_key base_inv_old_true_key; +extern struct static_key base_old_false_key; +extern struct static_key base_inv_old_false_key; + +/* new api */ +extern struct static_key_true base_true_key; +extern struct static_key_true base_inv_true_key; +extern struct static_key_false base_false_key; +extern struct static_key_false base_inv_false_key; + + +struct test_branch { + bool init_state; + struct static_key *key; + bool (*test_key)(void); +}; + +#define test_key_func(key, branch) \ +({bool func(void) { return branch(key); } func; }) + +static void invert_key(struct static_key *key) +{ + if (static_key_enabled(key)) + static_key_disable(key); + else + static_key_enable(key); +} + +static void invert_keys(struct test_branch *branches, int size) +{ + struct static_key *previous = NULL; + int i; + + for (i = 0; i < size; i++) { + if (previous != branches[i].key) { + invert_key(branches[i].key); + previous = branches[i].key; + } + } +} + +int verify_branches(struct test_branch *branches, int size, bool invert) +{ + int i; + bool ret, init; + + for (i = 0; i < size; i++) { + ret = static_key_enabled(branches[i].key); + init = branches[i].init_state; + if (ret != (invert ? !init : init)) + return -EINVAL; + ret = branches[i].test_key(); + if (static_key_enabled(branches[i].key)) { + if (!ret) + return -EINVAL; + } else { + if (ret) + return -EINVAL; + } + } + return 0; +} + +static int __init test_jump_label_init(void) +{ + int ret; + int size; + + struct test_branch jump_label_tests[] = { + /* internal keys - old keys */ + { + .init_state = true, + .key = &old_true_key, + .test_key = test_key_func(&old_true_key, static_key_true), + }, + { + .init_state = false, + .key = &old_false_key, + .test_key = test_key_func(&old_false_key, static_key_false), + }, + /* internal keys - new keys */ + { + .init_state = true, + .key = &true_key.key, + .test_key = test_key_func(&true_key, static_branch_likely), + }, + { + .init_state = true, + .key = &true_key.key, + .test_key = test_key_func(&true_key, static_branch_unlikely), + }, + { + .init_state = false, + .key = &false_key.key, + .test_key = test_key_func(&false_key, static_branch_likely), + }, + { + .init_state = false, + .key = &false_key.key, + .test_key = test_key_func(&false_key, static_branch_unlikely), + }, + /* external keys - old keys */ + { + .init_state = true, + .key = &base_old_true_key, + .test_key = test_key_func(&base_old_true_key, static_key_true), + }, + { + .init_state = false, + .key = &base_inv_old_true_key, + .test_key = test_key_func(&base_inv_old_true_key, static_key_true), + }, + { + .init_state = false, + .key = &base_old_false_key, + .test_key = test_key_func(&base_old_false_key, static_key_false), + }, + { + .init_state = true, + .key = &base_inv_old_false_key, + .test_key = test_key_func(&base_inv_old_false_key, static_key_false), + }, + /* external keys - new keys */ + { + .init_state = true, + .key = &base_true_key.key, + .test_key = test_key_func(&base_true_key, static_branch_likely), + }, + { + .init_state = true, + .key = &base_true_key.key, + .test_key = test_key_func(&base_true_key, static_branch_unlikely), + }, + { + .init_state = false, + .key = &base_inv_true_key.key, + .test_key = test_key_func(&base_inv_true_key, static_branch_likely), + }, + { + .init_state = false, + .key = &base_inv_true_key.key, + .test_key = test_key_func(&base_inv_true_key, static_branch_unlikely), + }, + { + .init_state = false, + .key = &base_false_key.key, + .test_key = test_key_func(&base_false_key, static_branch_likely), + }, + { + .init_state = false, + .key = &base_false_key.key, + .test_key = test_key_func(&base_false_key, static_branch_unlikely), + }, + { + .init_state = true, + .key = &base_inv_false_key.key, + .test_key = test_key_func(&base_inv_false_key, static_branch_likely), + }, + { + .init_state = true, + .key = &base_inv_false_key.key, + .test_key = test_key_func(&base_inv_false_key, static_branch_unlikely), + }, + }; + + size = ARRAY_SIZE(jump_label_tests); + + ret = verify_branches(jump_label_tests, size, false); + if (ret) + goto out; + + invert_keys(jump_label_tests, size); + ret = verify_branches(jump_label_tests, size, true); + if (ret) + goto out; + + invert_keys(jump_label_tests, size); + ret = verify_branches(jump_label_tests, size, false); + if (ret) + goto out; + return 0; +out: + return ret; +} + +static void __exit test_jump_label_exit(void) +{ +} + +module_init(test_jump_label_init); +module_exit(test_jump_label_exit); + +MODULE_AUTHOR("Jason Baron "); +MODULE_LICENSE("GPL"); diff --git a/lib/test_jump_label_base.c b/lib/test_jump_label_base.c new file mode 100644 index 0000000000000000000000000000000000000000..91aed8e8a7e8d18879595f96ce199dbb94c26e1b --- /dev/null +++ b/lib/test_jump_label_base.c @@ -0,0 +1,68 @@ +/* + * Kernel module for testing jump labels. + * + * Copyright 2015 Akamai Technologies Inc. All Rights Reserved + * + * Authors: + * Jason Baron + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include +#include + +/* old keys */ +struct static_key base_old_true_key = STATIC_KEY_INIT_TRUE; +EXPORT_SYMBOL_GPL(base_old_true_key); +struct static_key base_inv_old_true_key = STATIC_KEY_INIT_TRUE; +EXPORT_SYMBOL_GPL(base_inv_old_true_key); +struct static_key base_old_false_key = STATIC_KEY_INIT_FALSE; +EXPORT_SYMBOL_GPL(base_old_false_key); +struct static_key base_inv_old_false_key = STATIC_KEY_INIT_FALSE; +EXPORT_SYMBOL_GPL(base_inv_old_false_key); + +/* new keys */ +DEFINE_STATIC_KEY_TRUE(base_true_key); +EXPORT_SYMBOL_GPL(base_true_key); +DEFINE_STATIC_KEY_TRUE(base_inv_true_key); +EXPORT_SYMBOL_GPL(base_inv_true_key); +DEFINE_STATIC_KEY_FALSE(base_false_key); +EXPORT_SYMBOL_GPL(base_false_key); +DEFINE_STATIC_KEY_FALSE(base_inv_false_key); +EXPORT_SYMBOL_GPL(base_inv_false_key); + +static void invert_key(struct static_key *key) +{ + if (static_key_enabled(key)) + static_key_disable(key); + else + static_key_enable(key); +} + +static int __init test_jump_label_base_init(void) +{ + invert_key(&base_inv_old_true_key); + invert_key(&base_inv_old_false_key); + invert_key(&base_inv_true_key.key); + invert_key(&base_inv_false_key.key); + + return 0; +} + +static void __exit test_jump_label_base_exit(void) +{ +} + +module_init(test_jump_label_base_init); +module_exit(test_jump_label_base_exit); + +MODULE_AUTHOR("Jason Baron "); +MODULE_LICENSE("GPL"); diff --git a/tools/testing/selftests/Makefile b/tools/testing/selftests/Makefile index 24ae9e829e9aa2e22db0e035b4c1d53d5424cf61..b8f12e0897e6b1debdd3165e4300ccdbf4e26f44 100644 --- a/tools/testing/selftests/Makefile +++ b/tools/testing/selftests/Makefile @@ -20,6 +20,7 @@ ifneq (1, $(quicktest)) TARGETS += timers endif TARGETS += user +TARGETS += jumplabel TARGETS += vm TARGETS += x86 #Please keep the TARGETS list alphabetically sorted diff --git a/tools/testing/selftests/jumplabel/Makefile b/tools/testing/selftests/jumplabel/Makefile new file mode 100644 index 0000000000000000000000000000000000000000..7526aa59e005593b588a16147604f67e57aa38ea --- /dev/null +++ b/tools/testing/selftests/jumplabel/Makefile @@ -0,0 +1,8 @@ +# Makefile for jump label selftests + +# No binaries, but make sure arg-less "make" doesn't trigger "run_tests" +all: + +TEST_PROGS := test_jump_label.sh + +include ../lib.mk diff --git a/tools/testing/selftests/jumplabel/test_jump_label.sh b/tools/testing/selftests/jumplabel/test_jump_label.sh new file mode 100644 index 0000000000000000000000000000000000000000..3457e8a64e77fac77544d6ec23f6c9a05ce39c7b --- /dev/null +++ b/tools/testing/selftests/jumplabel/test_jump_label.sh @@ -0,0 +1,16 @@ +#!/bin/sh +# Runs jump label kernel module tests + +if /sbin/modprobe -q test_jump_label_base; then + if /sbin/modprobe -q test_jump_label; then + echo "jump_label: ok" + /sbin/modprobe -q -r test_jump_label + /sbin/modprobe -q -r test_jump_label_base + else + echo "jump_label: [FAIL]" + /sbin/modprobe -q -r test_jump_label_base + fi +else + echo "jump_label: [FAIL]" + exit 1 +fi