18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * Kernel module for testing static keys.
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci * Copyright 2015 Akamai Technologies Inc. All Rights Reserved
68c2ecf20Sopenharmony_ci *
78c2ecf20Sopenharmony_ci * Authors:
88c2ecf20Sopenharmony_ci *      Jason Baron       <jbaron@akamai.com>
98c2ecf20Sopenharmony_ci */
108c2ecf20Sopenharmony_ci
118c2ecf20Sopenharmony_ci#include <linux/module.h>
128c2ecf20Sopenharmony_ci#include <linux/jump_label.h>
138c2ecf20Sopenharmony_ci
148c2ecf20Sopenharmony_ci/* old keys */
158c2ecf20Sopenharmony_cistruct static_key old_true_key	= STATIC_KEY_INIT_TRUE;
168c2ecf20Sopenharmony_cistruct static_key old_false_key	= STATIC_KEY_INIT_FALSE;
178c2ecf20Sopenharmony_ci
188c2ecf20Sopenharmony_ci/* new api */
198c2ecf20Sopenharmony_ciDEFINE_STATIC_KEY_TRUE(true_key);
208c2ecf20Sopenharmony_ciDEFINE_STATIC_KEY_FALSE(false_key);
218c2ecf20Sopenharmony_ci
228c2ecf20Sopenharmony_ci/* external */
238c2ecf20Sopenharmony_ciextern struct static_key base_old_true_key;
248c2ecf20Sopenharmony_ciextern struct static_key base_inv_old_true_key;
258c2ecf20Sopenharmony_ciextern struct static_key base_old_false_key;
268c2ecf20Sopenharmony_ciextern struct static_key base_inv_old_false_key;
278c2ecf20Sopenharmony_ci
288c2ecf20Sopenharmony_ci/* new api */
298c2ecf20Sopenharmony_ciextern struct static_key_true base_true_key;
308c2ecf20Sopenharmony_ciextern struct static_key_true base_inv_true_key;
318c2ecf20Sopenharmony_ciextern struct static_key_false base_false_key;
328c2ecf20Sopenharmony_ciextern struct static_key_false base_inv_false_key;
338c2ecf20Sopenharmony_ci
348c2ecf20Sopenharmony_ci
358c2ecf20Sopenharmony_cistruct test_key {
368c2ecf20Sopenharmony_ci	bool			init_state;
378c2ecf20Sopenharmony_ci	struct static_key	*key;
388c2ecf20Sopenharmony_ci	bool			(*test_key)(void);
398c2ecf20Sopenharmony_ci};
408c2ecf20Sopenharmony_ci
418c2ecf20Sopenharmony_ci#define test_key_func(key, branch)	\
428c2ecf20Sopenharmony_cistatic bool key ## _ ## branch(void)	\
438c2ecf20Sopenharmony_ci{					\
448c2ecf20Sopenharmony_ci	return branch(&key);		\
458c2ecf20Sopenharmony_ci}
468c2ecf20Sopenharmony_ci
478c2ecf20Sopenharmony_cistatic void invert_key(struct static_key *key)
488c2ecf20Sopenharmony_ci{
498c2ecf20Sopenharmony_ci	if (static_key_enabled(key))
508c2ecf20Sopenharmony_ci		static_key_disable(key);
518c2ecf20Sopenharmony_ci	else
528c2ecf20Sopenharmony_ci		static_key_enable(key);
538c2ecf20Sopenharmony_ci}
548c2ecf20Sopenharmony_ci
558c2ecf20Sopenharmony_cistatic void invert_keys(struct test_key *keys, int size)
568c2ecf20Sopenharmony_ci{
578c2ecf20Sopenharmony_ci	struct static_key *previous = NULL;
588c2ecf20Sopenharmony_ci	int i;
598c2ecf20Sopenharmony_ci
608c2ecf20Sopenharmony_ci	for (i = 0; i < size; i++) {
618c2ecf20Sopenharmony_ci		if (previous != keys[i].key) {
628c2ecf20Sopenharmony_ci			invert_key(keys[i].key);
638c2ecf20Sopenharmony_ci			previous = keys[i].key;
648c2ecf20Sopenharmony_ci		}
658c2ecf20Sopenharmony_ci	}
668c2ecf20Sopenharmony_ci}
678c2ecf20Sopenharmony_ci
688c2ecf20Sopenharmony_cistatic int verify_keys(struct test_key *keys, int size, bool invert)
698c2ecf20Sopenharmony_ci{
708c2ecf20Sopenharmony_ci	int i;
718c2ecf20Sopenharmony_ci	bool ret, init;
728c2ecf20Sopenharmony_ci
738c2ecf20Sopenharmony_ci	for (i = 0; i < size; i++) {
748c2ecf20Sopenharmony_ci		ret = static_key_enabled(keys[i].key);
758c2ecf20Sopenharmony_ci		init = keys[i].init_state;
768c2ecf20Sopenharmony_ci		if (ret != (invert ? !init : init))
778c2ecf20Sopenharmony_ci			return -EINVAL;
788c2ecf20Sopenharmony_ci		ret = keys[i].test_key();
798c2ecf20Sopenharmony_ci		if (static_key_enabled(keys[i].key)) {
808c2ecf20Sopenharmony_ci			if (!ret)
818c2ecf20Sopenharmony_ci				return -EINVAL;
828c2ecf20Sopenharmony_ci		} else {
838c2ecf20Sopenharmony_ci			if (ret)
848c2ecf20Sopenharmony_ci				return -EINVAL;
858c2ecf20Sopenharmony_ci		}
868c2ecf20Sopenharmony_ci	}
878c2ecf20Sopenharmony_ci	return 0;
888c2ecf20Sopenharmony_ci}
898c2ecf20Sopenharmony_ci
908c2ecf20Sopenharmony_citest_key_func(old_true_key, static_key_true)
918c2ecf20Sopenharmony_citest_key_func(old_false_key, static_key_false)
928c2ecf20Sopenharmony_citest_key_func(true_key, static_branch_likely)
938c2ecf20Sopenharmony_citest_key_func(true_key, static_branch_unlikely)
948c2ecf20Sopenharmony_citest_key_func(false_key, static_branch_likely)
958c2ecf20Sopenharmony_citest_key_func(false_key, static_branch_unlikely)
968c2ecf20Sopenharmony_citest_key_func(base_old_true_key, static_key_true)
978c2ecf20Sopenharmony_citest_key_func(base_inv_old_true_key, static_key_true)
988c2ecf20Sopenharmony_citest_key_func(base_old_false_key, static_key_false)
998c2ecf20Sopenharmony_citest_key_func(base_inv_old_false_key, static_key_false)
1008c2ecf20Sopenharmony_citest_key_func(base_true_key, static_branch_likely)
1018c2ecf20Sopenharmony_citest_key_func(base_true_key, static_branch_unlikely)
1028c2ecf20Sopenharmony_citest_key_func(base_inv_true_key, static_branch_likely)
1038c2ecf20Sopenharmony_citest_key_func(base_inv_true_key, static_branch_unlikely)
1048c2ecf20Sopenharmony_citest_key_func(base_false_key, static_branch_likely)
1058c2ecf20Sopenharmony_citest_key_func(base_false_key, static_branch_unlikely)
1068c2ecf20Sopenharmony_citest_key_func(base_inv_false_key, static_branch_likely)
1078c2ecf20Sopenharmony_citest_key_func(base_inv_false_key, static_branch_unlikely)
1088c2ecf20Sopenharmony_ci
1098c2ecf20Sopenharmony_cistatic int __init test_static_key_init(void)
1108c2ecf20Sopenharmony_ci{
1118c2ecf20Sopenharmony_ci	int ret;
1128c2ecf20Sopenharmony_ci	int size;
1138c2ecf20Sopenharmony_ci
1148c2ecf20Sopenharmony_ci	struct test_key static_key_tests[] = {
1158c2ecf20Sopenharmony_ci		/* internal keys - old keys */
1168c2ecf20Sopenharmony_ci		{
1178c2ecf20Sopenharmony_ci			.init_state	= true,
1188c2ecf20Sopenharmony_ci			.key		= &old_true_key,
1198c2ecf20Sopenharmony_ci			.test_key	= &old_true_key_static_key_true,
1208c2ecf20Sopenharmony_ci		},
1218c2ecf20Sopenharmony_ci		{
1228c2ecf20Sopenharmony_ci			.init_state	= false,
1238c2ecf20Sopenharmony_ci			.key		= &old_false_key,
1248c2ecf20Sopenharmony_ci			.test_key	= &old_false_key_static_key_false,
1258c2ecf20Sopenharmony_ci		},
1268c2ecf20Sopenharmony_ci		/* internal keys - new keys */
1278c2ecf20Sopenharmony_ci		{
1288c2ecf20Sopenharmony_ci			.init_state	= true,
1298c2ecf20Sopenharmony_ci			.key		= &true_key.key,
1308c2ecf20Sopenharmony_ci			.test_key	= &true_key_static_branch_likely,
1318c2ecf20Sopenharmony_ci		},
1328c2ecf20Sopenharmony_ci		{
1338c2ecf20Sopenharmony_ci			.init_state	= true,
1348c2ecf20Sopenharmony_ci			.key		= &true_key.key,
1358c2ecf20Sopenharmony_ci			.test_key	= &true_key_static_branch_unlikely,
1368c2ecf20Sopenharmony_ci		},
1378c2ecf20Sopenharmony_ci		{
1388c2ecf20Sopenharmony_ci			.init_state	= false,
1398c2ecf20Sopenharmony_ci			.key		= &false_key.key,
1408c2ecf20Sopenharmony_ci			.test_key	= &false_key_static_branch_likely,
1418c2ecf20Sopenharmony_ci		},
1428c2ecf20Sopenharmony_ci		{
1438c2ecf20Sopenharmony_ci			.init_state	= false,
1448c2ecf20Sopenharmony_ci			.key		= &false_key.key,
1458c2ecf20Sopenharmony_ci			.test_key	= &false_key_static_branch_unlikely,
1468c2ecf20Sopenharmony_ci		},
1478c2ecf20Sopenharmony_ci		/* external keys - old keys */
1488c2ecf20Sopenharmony_ci		{
1498c2ecf20Sopenharmony_ci			.init_state	= true,
1508c2ecf20Sopenharmony_ci			.key		= &base_old_true_key,
1518c2ecf20Sopenharmony_ci			.test_key	= &base_old_true_key_static_key_true,
1528c2ecf20Sopenharmony_ci		},
1538c2ecf20Sopenharmony_ci		{
1548c2ecf20Sopenharmony_ci			.init_state	= false,
1558c2ecf20Sopenharmony_ci			.key		= &base_inv_old_true_key,
1568c2ecf20Sopenharmony_ci			.test_key	= &base_inv_old_true_key_static_key_true,
1578c2ecf20Sopenharmony_ci		},
1588c2ecf20Sopenharmony_ci		{
1598c2ecf20Sopenharmony_ci			.init_state	= false,
1608c2ecf20Sopenharmony_ci			.key		= &base_old_false_key,
1618c2ecf20Sopenharmony_ci			.test_key	= &base_old_false_key_static_key_false,
1628c2ecf20Sopenharmony_ci		},
1638c2ecf20Sopenharmony_ci		{
1648c2ecf20Sopenharmony_ci			.init_state	= true,
1658c2ecf20Sopenharmony_ci			.key		= &base_inv_old_false_key,
1668c2ecf20Sopenharmony_ci			.test_key	= &base_inv_old_false_key_static_key_false,
1678c2ecf20Sopenharmony_ci		},
1688c2ecf20Sopenharmony_ci		/* external keys - new keys */
1698c2ecf20Sopenharmony_ci		{
1708c2ecf20Sopenharmony_ci			.init_state	= true,
1718c2ecf20Sopenharmony_ci			.key		= &base_true_key.key,
1728c2ecf20Sopenharmony_ci			.test_key	= &base_true_key_static_branch_likely,
1738c2ecf20Sopenharmony_ci		},
1748c2ecf20Sopenharmony_ci		{
1758c2ecf20Sopenharmony_ci			.init_state	= true,
1768c2ecf20Sopenharmony_ci			.key		= &base_true_key.key,
1778c2ecf20Sopenharmony_ci			.test_key	= &base_true_key_static_branch_unlikely,
1788c2ecf20Sopenharmony_ci		},
1798c2ecf20Sopenharmony_ci		{
1808c2ecf20Sopenharmony_ci			.init_state	= false,
1818c2ecf20Sopenharmony_ci			.key		= &base_inv_true_key.key,
1828c2ecf20Sopenharmony_ci			.test_key	= &base_inv_true_key_static_branch_likely,
1838c2ecf20Sopenharmony_ci		},
1848c2ecf20Sopenharmony_ci		{
1858c2ecf20Sopenharmony_ci			.init_state	= false,
1868c2ecf20Sopenharmony_ci			.key		= &base_inv_true_key.key,
1878c2ecf20Sopenharmony_ci			.test_key	= &base_inv_true_key_static_branch_unlikely,
1888c2ecf20Sopenharmony_ci		},
1898c2ecf20Sopenharmony_ci		{
1908c2ecf20Sopenharmony_ci			.init_state	= false,
1918c2ecf20Sopenharmony_ci			.key		= &base_false_key.key,
1928c2ecf20Sopenharmony_ci			.test_key	= &base_false_key_static_branch_likely,
1938c2ecf20Sopenharmony_ci		},
1948c2ecf20Sopenharmony_ci		{
1958c2ecf20Sopenharmony_ci			.init_state	= false,
1968c2ecf20Sopenharmony_ci			.key		= &base_false_key.key,
1978c2ecf20Sopenharmony_ci			.test_key	= &base_false_key_static_branch_unlikely,
1988c2ecf20Sopenharmony_ci		},
1998c2ecf20Sopenharmony_ci		{
2008c2ecf20Sopenharmony_ci			.init_state	= true,
2018c2ecf20Sopenharmony_ci			.key		= &base_inv_false_key.key,
2028c2ecf20Sopenharmony_ci			.test_key	= &base_inv_false_key_static_branch_likely,
2038c2ecf20Sopenharmony_ci		},
2048c2ecf20Sopenharmony_ci		{
2058c2ecf20Sopenharmony_ci			.init_state	= true,
2068c2ecf20Sopenharmony_ci			.key		= &base_inv_false_key.key,
2078c2ecf20Sopenharmony_ci			.test_key	= &base_inv_false_key_static_branch_unlikely,
2088c2ecf20Sopenharmony_ci		},
2098c2ecf20Sopenharmony_ci	};
2108c2ecf20Sopenharmony_ci
2118c2ecf20Sopenharmony_ci	size = ARRAY_SIZE(static_key_tests);
2128c2ecf20Sopenharmony_ci
2138c2ecf20Sopenharmony_ci	ret = verify_keys(static_key_tests, size, false);
2148c2ecf20Sopenharmony_ci	if (ret)
2158c2ecf20Sopenharmony_ci		goto out;
2168c2ecf20Sopenharmony_ci
2178c2ecf20Sopenharmony_ci	invert_keys(static_key_tests, size);
2188c2ecf20Sopenharmony_ci	ret = verify_keys(static_key_tests, size, true);
2198c2ecf20Sopenharmony_ci	if (ret)
2208c2ecf20Sopenharmony_ci		goto out;
2218c2ecf20Sopenharmony_ci
2228c2ecf20Sopenharmony_ci	invert_keys(static_key_tests, size);
2238c2ecf20Sopenharmony_ci	ret = verify_keys(static_key_tests, size, false);
2248c2ecf20Sopenharmony_ci	if (ret)
2258c2ecf20Sopenharmony_ci		goto out;
2268c2ecf20Sopenharmony_ci	return 0;
2278c2ecf20Sopenharmony_ciout:
2288c2ecf20Sopenharmony_ci	return ret;
2298c2ecf20Sopenharmony_ci}
2308c2ecf20Sopenharmony_ci
2318c2ecf20Sopenharmony_cistatic void __exit test_static_key_exit(void)
2328c2ecf20Sopenharmony_ci{
2338c2ecf20Sopenharmony_ci}
2348c2ecf20Sopenharmony_ci
2358c2ecf20Sopenharmony_cimodule_init(test_static_key_init);
2368c2ecf20Sopenharmony_cimodule_exit(test_static_key_exit);
2378c2ecf20Sopenharmony_ci
2388c2ecf20Sopenharmony_ciMODULE_AUTHOR("Jason Baron <jbaron@akamai.com>");
2398c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL");
240