| 1 | /* SPDX-License-Identifier: GPL-2.0 */ | 
|---|
| 2 | /* | 
|---|
| 3 | * KUnit function redirection (static stubbing) API. | 
|---|
| 4 | * | 
|---|
| 5 | * Copyright (C) 2022, Google LLC. | 
|---|
| 6 | * Author: David Gow <davidgow@google.com> | 
|---|
| 7 | */ | 
|---|
| 8 | #ifndef _KUNIT_STATIC_STUB_H | 
|---|
| 9 | #define _KUNIT_STATIC_STUB_H | 
|---|
| 10 |  | 
|---|
| 11 | #if !IS_ENABLED(CONFIG_KUNIT) | 
|---|
| 12 |  | 
|---|
| 13 | /* If CONFIG_KUNIT is not enabled, these stubs quietly disappear. */ | 
|---|
| 14 | #define KUNIT_STATIC_STUB_REDIRECT(real_fn_name, args...) do {} while (0) | 
|---|
| 15 |  | 
|---|
| 16 | #else | 
|---|
| 17 |  | 
|---|
| 18 | #include <kunit/test.h> | 
|---|
| 19 | #include <kunit/test-bug.h> | 
|---|
| 20 |  | 
|---|
| 21 | #include <linux/compiler.h> /* for {un,}likely() */ | 
|---|
| 22 | #include <linux/sched.h> /* for task_struct */ | 
|---|
| 23 |  | 
|---|
| 24 |  | 
|---|
| 25 | /** | 
|---|
| 26 | * KUNIT_STATIC_STUB_REDIRECT() - call a replacement 'static stub' if one exists | 
|---|
| 27 | * @real_fn_name: The name of this function (as an identifier, not a string) | 
|---|
| 28 | * @args: All of the arguments passed to this function | 
|---|
| 29 | * | 
|---|
| 30 | * This is a function prologue which is used to allow calls to the current | 
|---|
| 31 | * function to be redirected by a KUnit test. KUnit tests can call | 
|---|
| 32 | * kunit_activate_static_stub() to pass a replacement function in. The | 
|---|
| 33 | * replacement function will be called by KUNIT_STATIC_STUB_REDIRECT(), which | 
|---|
| 34 | * will then return from the function. If the caller is not in a KUnit context, | 
|---|
| 35 | * the function will continue execution as normal. | 
|---|
| 36 | * | 
|---|
| 37 | * Example: | 
|---|
| 38 | * | 
|---|
| 39 | * .. code-block:: c | 
|---|
| 40 | * | 
|---|
| 41 | *	int real_func(int n) | 
|---|
| 42 | *	{ | 
|---|
| 43 | *		KUNIT_STATIC_STUB_REDIRECT(real_func, n); | 
|---|
| 44 | *		return 0; | 
|---|
| 45 | *	} | 
|---|
| 46 | * | 
|---|
| 47 | *	int replacement_func(int n) | 
|---|
| 48 | *	{ | 
|---|
| 49 | *		return 42; | 
|---|
| 50 | *	} | 
|---|
| 51 | * | 
|---|
| 52 | *	void example_test(struct kunit *test) | 
|---|
| 53 | *	{ | 
|---|
| 54 | *		kunit_activate_static_stub(test, real_func, replacement_func); | 
|---|
| 55 | *		KUNIT_EXPECT_EQ(test, real_func(1), 42); | 
|---|
| 56 | *	} | 
|---|
| 57 | * | 
|---|
| 58 | */ | 
|---|
| 59 | #define KUNIT_STATIC_STUB_REDIRECT(real_fn_name, args...)		\ | 
|---|
| 60 | do {									\ | 
|---|
| 61 | typeof(&real_fn_name) replacement;				\ | 
|---|
| 62 | struct kunit *current_test = kunit_get_current_test();		\ | 
|---|
| 63 | \ | 
|---|
| 64 | if (likely(!current_test))					\ | 
|---|
| 65 | break;							\ | 
|---|
| 66 | \ | 
|---|
| 67 | replacement = kunit_hooks.get_static_stub_address(current_test,	\ | 
|---|
| 68 | &real_fn_name);	\ | 
|---|
| 69 | \ | 
|---|
| 70 | if (unlikely(replacement))					\ | 
|---|
| 71 | return replacement(args);				\ | 
|---|
| 72 | } while (0) | 
|---|
| 73 |  | 
|---|
| 74 | /* Helper function for kunit_activate_static_stub(). The macro does | 
|---|
| 75 | * typechecking, so use it instead. | 
|---|
| 76 | */ | 
|---|
| 77 | void __kunit_activate_static_stub(struct kunit *test, | 
|---|
| 78 | void *real_fn_addr, | 
|---|
| 79 | void *replacement_addr); | 
|---|
| 80 |  | 
|---|
| 81 | /** | 
|---|
| 82 | * kunit_activate_static_stub() - replace a function using static stubs. | 
|---|
| 83 | * @test: A pointer to the 'struct kunit' test context for the current test. | 
|---|
| 84 | * @real_fn_addr: The address of the function to replace. | 
|---|
| 85 | * @replacement_addr: The address of the function to replace it with. | 
|---|
| 86 | * | 
|---|
| 87 | * When activated, calls to real_fn_addr from within this test (even if called | 
|---|
| 88 | * indirectly) will instead call replacement_addr. The function pointed to by | 
|---|
| 89 | * real_fn_addr must begin with the static stub prologue in | 
|---|
| 90 | * KUNIT_STATIC_STUB_REDIRECT() for this to work. real_fn_addr and | 
|---|
| 91 | * replacement_addr must have the same type. | 
|---|
| 92 | * | 
|---|
| 93 | * The redirection can be disabled again with kunit_deactivate_static_stub(). | 
|---|
| 94 | */ | 
|---|
| 95 | #define kunit_activate_static_stub(test, real_fn_addr, replacement_addr) do {	\ | 
|---|
| 96 | typecheck_fn(typeof(&replacement_addr), real_fn_addr);			\ | 
|---|
| 97 | __kunit_activate_static_stub(test, real_fn_addr, replacement_addr);	\ | 
|---|
| 98 | } while (0) | 
|---|
| 99 |  | 
|---|
| 100 |  | 
|---|
| 101 | /** | 
|---|
| 102 | * kunit_deactivate_static_stub() - disable a function redirection | 
|---|
| 103 | * @test: A pointer to the 'struct kunit' test context for the current test. | 
|---|
| 104 | * @real_fn_addr: The address of the function to no-longer redirect | 
|---|
| 105 | * | 
|---|
| 106 | * Deactivates a redirection configured with kunit_activate_static_stub(). After | 
|---|
| 107 | * this function returns, calls to real_fn_addr() will execute the original | 
|---|
| 108 | * real_fn, not any previously-configured replacement. | 
|---|
| 109 | */ | 
|---|
| 110 | void kunit_deactivate_static_stub(struct kunit *test, void *real_fn_addr); | 
|---|
| 111 |  | 
|---|
| 112 | #endif | 
|---|
| 113 | #endif | 
|---|
| 114 |  | 
|---|