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