| // SPDX-License-Identifier: GPL-2.0-only | 
 | /* | 
 |  * Kernel module for testing static keys. | 
 |  * | 
 |  * Copyright 2015 Akamai Technologies Inc. All Rights Reserved | 
 |  * | 
 |  * Authors: | 
 |  *      Jason Baron       <jbaron@akamai.com> | 
 |  */ | 
 |  | 
 | #include <linux/module.h> | 
 | #include <linux/jump_label.h> | 
 |  | 
 | /* 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_key { | 
 | 	bool			init_state; | 
 | 	struct static_key	*key; | 
 | 	bool			(*test_key)(void); | 
 | }; | 
 |  | 
 | #define test_key_func(key, branch)	\ | 
 | static bool key ## _ ## branch(void)	\ | 
 | {					\ | 
 | 	return branch(&key);		\ | 
 | } | 
 |  | 
 | 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_key *keys, int size) | 
 | { | 
 | 	struct static_key *previous = NULL; | 
 | 	int i; | 
 |  | 
 | 	for (i = 0; i < size; i++) { | 
 | 		if (previous != keys[i].key) { | 
 | 			invert_key(keys[i].key); | 
 | 			previous = keys[i].key; | 
 | 		} | 
 | 	} | 
 | } | 
 |  | 
 | static int verify_keys(struct test_key *keys, int size, bool invert) | 
 | { | 
 | 	int i; | 
 | 	bool ret, init; | 
 |  | 
 | 	for (i = 0; i < size; i++) { | 
 | 		ret = static_key_enabled(keys[i].key); | 
 | 		init = keys[i].init_state; | 
 | 		if (ret != (invert ? !init : init)) | 
 | 			return -EINVAL; | 
 | 		ret = keys[i].test_key(); | 
 | 		if (static_key_enabled(keys[i].key)) { | 
 | 			if (!ret) | 
 | 				return -EINVAL; | 
 | 		} else { | 
 | 			if (ret) | 
 | 				return -EINVAL; | 
 | 		} | 
 | 	} | 
 | 	return 0; | 
 | } | 
 |  | 
 | test_key_func(old_true_key, static_key_true) | 
 | test_key_func(old_false_key, static_key_false) | 
 | test_key_func(true_key, static_branch_likely) | 
 | test_key_func(true_key, static_branch_unlikely) | 
 | test_key_func(false_key, static_branch_likely) | 
 | test_key_func(false_key, static_branch_unlikely) | 
 | test_key_func(base_old_true_key, static_key_true) | 
 | test_key_func(base_inv_old_true_key, static_key_true) | 
 | test_key_func(base_old_false_key, static_key_false) | 
 | test_key_func(base_inv_old_false_key, static_key_false) | 
 | test_key_func(base_true_key, static_branch_likely) | 
 | test_key_func(base_true_key, static_branch_unlikely) | 
 | test_key_func(base_inv_true_key, static_branch_likely) | 
 | test_key_func(base_inv_true_key, static_branch_unlikely) | 
 | test_key_func(base_false_key, static_branch_likely) | 
 | test_key_func(base_false_key, static_branch_unlikely) | 
 | test_key_func(base_inv_false_key, static_branch_likely) | 
 | test_key_func(base_inv_false_key, static_branch_unlikely) | 
 |  | 
 | static int __init test_static_key_init(void) | 
 | { | 
 | 	int ret; | 
 | 	int size; | 
 |  | 
 | 	struct test_key static_key_tests[] = { | 
 | 		/* internal keys - old keys */ | 
 | 		{ | 
 | 			.init_state	= true, | 
 | 			.key		= &old_true_key, | 
 | 			.test_key	= &old_true_key_static_key_true, | 
 | 		}, | 
 | 		{ | 
 | 			.init_state	= false, | 
 | 			.key		= &old_false_key, | 
 | 			.test_key	= &old_false_key_static_key_false, | 
 | 		}, | 
 | 		/* internal keys - new keys */ | 
 | 		{ | 
 | 			.init_state	= true, | 
 | 			.key		= &true_key.key, | 
 | 			.test_key	= &true_key_static_branch_likely, | 
 | 		}, | 
 | 		{ | 
 | 			.init_state	= true, | 
 | 			.key		= &true_key.key, | 
 | 			.test_key	= &true_key_static_branch_unlikely, | 
 | 		}, | 
 | 		{ | 
 | 			.init_state	= false, | 
 | 			.key		= &false_key.key, | 
 | 			.test_key	= &false_key_static_branch_likely, | 
 | 		}, | 
 | 		{ | 
 | 			.init_state	= false, | 
 | 			.key		= &false_key.key, | 
 | 			.test_key	= &false_key_static_branch_unlikely, | 
 | 		}, | 
 | 		/* external keys - old keys */ | 
 | 		{ | 
 | 			.init_state	= true, | 
 | 			.key		= &base_old_true_key, | 
 | 			.test_key	= &base_old_true_key_static_key_true, | 
 | 		}, | 
 | 		{ | 
 | 			.init_state	= false, | 
 | 			.key		= &base_inv_old_true_key, | 
 | 			.test_key	= &base_inv_old_true_key_static_key_true, | 
 | 		}, | 
 | 		{ | 
 | 			.init_state	= false, | 
 | 			.key		= &base_old_false_key, | 
 | 			.test_key	= &base_old_false_key_static_key_false, | 
 | 		}, | 
 | 		{ | 
 | 			.init_state	= true, | 
 | 			.key		= &base_inv_old_false_key, | 
 | 			.test_key	= &base_inv_old_false_key_static_key_false, | 
 | 		}, | 
 | 		/* external keys - new keys */ | 
 | 		{ | 
 | 			.init_state	= true, | 
 | 			.key		= &base_true_key.key, | 
 | 			.test_key	= &base_true_key_static_branch_likely, | 
 | 		}, | 
 | 		{ | 
 | 			.init_state	= true, | 
 | 			.key		= &base_true_key.key, | 
 | 			.test_key	= &base_true_key_static_branch_unlikely, | 
 | 		}, | 
 | 		{ | 
 | 			.init_state	= false, | 
 | 			.key		= &base_inv_true_key.key, | 
 | 			.test_key	= &base_inv_true_key_static_branch_likely, | 
 | 		}, | 
 | 		{ | 
 | 			.init_state	= false, | 
 | 			.key		= &base_inv_true_key.key, | 
 | 			.test_key	= &base_inv_true_key_static_branch_unlikely, | 
 | 		}, | 
 | 		{ | 
 | 			.init_state	= false, | 
 | 			.key		= &base_false_key.key, | 
 | 			.test_key	= &base_false_key_static_branch_likely, | 
 | 		}, | 
 | 		{ | 
 | 			.init_state	= false, | 
 | 			.key		= &base_false_key.key, | 
 | 			.test_key	= &base_false_key_static_branch_unlikely, | 
 | 		}, | 
 | 		{ | 
 | 			.init_state	= true, | 
 | 			.key		= &base_inv_false_key.key, | 
 | 			.test_key	= &base_inv_false_key_static_branch_likely, | 
 | 		}, | 
 | 		{ | 
 | 			.init_state	= true, | 
 | 			.key		= &base_inv_false_key.key, | 
 | 			.test_key	= &base_inv_false_key_static_branch_unlikely, | 
 | 		}, | 
 | 	}; | 
 |  | 
 | 	size = ARRAY_SIZE(static_key_tests); | 
 |  | 
 | 	ret = verify_keys(static_key_tests, size, false); | 
 | 	if (ret) | 
 | 		goto out; | 
 |  | 
 | 	invert_keys(static_key_tests, size); | 
 | 	ret = verify_keys(static_key_tests, size, true); | 
 | 	if (ret) | 
 | 		goto out; | 
 |  | 
 | 	invert_keys(static_key_tests, size); | 
 | 	ret = verify_keys(static_key_tests, size, false); | 
 | 	if (ret) | 
 | 		goto out; | 
 | 	return 0; | 
 | out: | 
 | 	return ret; | 
 | } | 
 |  | 
 | static void __exit test_static_key_exit(void) | 
 | { | 
 | } | 
 |  | 
 | module_init(test_static_key_init); | 
 | module_exit(test_static_key_exit); | 
 |  | 
 | MODULE_AUTHOR("Jason Baron <jbaron@akamai.com>"); | 
 | MODULE_DESCRIPTION("Kernel module for testing static keys"); | 
 | MODULE_LICENSE("GPL"); |