2022-11-25 08:53:23

by David Gow

[permalink] [raw]
Subject: [PATCH v4 1/3] kunit: Provide a static key to check if KUnit is actively running tests

KUnit does a few expensive things when enabled. This hasn't been a
problem because KUnit was only enabled on test kernels, but with a few
people enabling (but not _using_) KUnit on production systems, we need a
runtime way of handling this.

Provide a 'kunit_running' static key (defaulting to false), which allows
us to hide any KUnit code behind a static branch. This should reduce the
performance impact (on other code) of having KUnit enabled to a single
NOP when no tests are running.

Note that, while it looks unintuitive, tests always run entirely within
__kunit_test_suites_init(), so it's safe to decrement the static key at
the end of this function, rather than in __kunit_test_suites_exit(),
which is only there to clean up results in debugfs.

Signed-off-by: David Gow <[email protected]>
Reviewed-by: Daniel Latypov <[email protected]>
---
This should be a no-op (other than a possible performance improvement)
functionality-wise, and lays the groundwork for a more optimised static
stub implementation.

The remaining patches in the series add a kunit_get_current_test()
function which is a more friendly and performant wrapper around
current->kunit_test, and use this in the slub test. They also improve
the documentation a bit.

If there are no objections, we'll take the whole series via the KUnit
tree.

Changes since v3:
https://lore.kernel.org/linux-kselftest/[email protected]/
- Use DECLARE_STATIC_KEY_FALSE() -- thanks Daniel!

No changes since v2:
https://lore.kernel.org/all/[email protected]/

Changes since v1:
https://lore.kernel.org/linux-kselftest/[email protected]/
- No changes in this patch.
- Patch 2/3 is reworked, patch 3/3 is new.

---
include/kunit/test.h | 4 ++++
lib/kunit/test.c | 6 ++++++
2 files changed, 10 insertions(+)

diff --git a/include/kunit/test.h b/include/kunit/test.h
index 4666a4d199ea..87ea90576b50 100644
--- a/include/kunit/test.h
+++ b/include/kunit/test.h
@@ -16,6 +16,7 @@
#include <linux/container_of.h>
#include <linux/err.h>
#include <linux/init.h>
+#include <linux/jump_label.h>
#include <linux/kconfig.h>
#include <linux/kref.h>
#include <linux/list.h>
@@ -27,6 +28,9 @@

#include <asm/rwonce.h>

+/* Static key: true if any KUnit tests are currently running */
+DECLARE_STATIC_KEY_FALSE(kunit_running);
+
struct kunit;

/* Size of log associated with test. */
diff --git a/lib/kunit/test.c b/lib/kunit/test.c
index 1c9d8d962d67..87a5d795843b 100644
--- a/lib/kunit/test.c
+++ b/lib/kunit/test.c
@@ -20,6 +20,8 @@
#include "string-stream.h"
#include "try-catch-impl.h"

+DEFINE_STATIC_KEY_FALSE(kunit_running);
+
#if IS_BUILTIN(CONFIG_KUNIT)
/*
* Fail the current test and print an error message to the log.
@@ -615,10 +617,14 @@ int __kunit_test_suites_init(struct kunit_suite * const * const suites, int num_
return 0;
}

+ static_branch_inc(&kunit_running);
+
for (i = 0; i < num_suites; i++) {
kunit_init_suite(suites[i]);
kunit_run_tests(suites[i]);
}
+
+ static_branch_dec(&kunit_running);
return 0;
}
EXPORT_SYMBOL_GPL(__kunit_test_suites_init);
--
2.38.1.584.g0f3c55d4c2-goog


2022-11-25 08:54:57

by David Gow

[permalink] [raw]
Subject: [PATCH v4 2/3] kunit: Use the static key when retrieving the current test

In order to detect if a KUnit test is running, and to access its
context, the 'kunit_test' member of the current task_struct is used.
Usually, this is accessed directly or via the kunit_fail_current_task()
function.

In order to speed up the case where no test is running, add a wrapper,
kunit_get_current_test(), which uses the static key to fail early.
Equally, Speed up kunit_fail_current_test() by using the static key.

This should make it convenient for code to call this
unconditionally in fakes or error paths, without worrying that this will
slow the code down significantly.

If CONFIG_KUNIT=n (or m), this compiles away to nothing. If
CONFIG_KUNIT=y, it will compile down to a NOP (on most architectures) if
no KUnit test is currently running.

Note that kunit_get_current_test() does not work if KUnit is built as a
module. This mirrors the existing restriction on kunit_fail_current_test().

Note that the definition of kunit_fail_current_test() still wraps an
empty, inline function if KUnit is not built-in. This is to ensure that
the printf format string __attribute__ will still work.

Also update the documentation to suggest users use the new
kunit_get_current_test() function, update the example, and to describe
the behaviour when KUnit is disabled better.

Cc: Jonathan Corbet <[email protected]>
Cc: Sadiya Kazi <[email protected]>
Signed-off-by: David Gow <[email protected]>
Reviewed-by: Daniel Latypov <[email protected]>
---

As-is, the only code which will be directly affected by this (via the
kunit_fail_current_test() change) will be UBSAN's KUnit integration.

Patches to port other tests to use kunit_get_current_test() will be sent
separately (other than the SLUB one in patch 3/3). KASAN in particular
are reworking their KUnit tests and integration, so we'll use this in a
follow up to avoid introducing a conflict.

Changes since v3:
https://lore.kernel.org/linux-kselftest/[email protected]/
- Use DECLARE_STATIC_KEY_FALSE() -- Thanks Daniel.
- Some documentation rewording to make the behaviour a bit clearer.
- Thanks Daniel and Sadiya

Changes since v2:
https://lore.kernel.org/all/[email protected]/
- Only add kunit_get_current_test() when KUnit is built-in, as the
static key isn't available otherwise.
- I'm going to try to put together some patches to make things like
this available when CONFIG_KUNIT=m in the future.
- Also update the documentation to note this.

Changes since v1:
https://lore.kernel.org/linux-kselftest/[email protected]/
- Fix a missing '}' which broke everything. Thanks Kees, kernel test
robot.
- Add the new kunit_get_current_test() function, as most of the cases
where we retrieve the current test (even to fail it) were accessing
current->kunit_test directly, not using kunit_fail_current_test().
- Add some documentation comments.
- Update the documentation in usage.rst.
- The version in tips.rst was not updated, and will be removed:
https://lore.kernel.org/linux-kselftest/[email protected]/

---
Documentation/dev-tools/kunit/usage.rst | 30 +++++++++-----
include/kunit/test-bug.h | 53 +++++++++++++++++++++++--
2 files changed, 71 insertions(+), 12 deletions(-)

diff --git a/Documentation/dev-tools/kunit/usage.rst b/Documentation/dev-tools/kunit/usage.rst
index 22416ebb94ab..48f8196d5aad 100644
--- a/Documentation/dev-tools/kunit/usage.rst
+++ b/Documentation/dev-tools/kunit/usage.rst
@@ -641,17 +641,23 @@ as shown in next section: *Accessing The Current Test*.
Accessing The Current Test
--------------------------

-In some cases, we need to call test-only code from outside the test file.
-For example, see example in section *Injecting Test-Only Code* or if
-we are providing a fake implementation of an ops struct. Using
-``kunit_test`` field in ``task_struct``, we can access it via
-``current->kunit_test``.
+In some cases, we need to call test-only code from outside the test file. This
+is helpful, for example, when providing a fake implementation of a function, or
+to fail any current test from within an error handler.
+We can do this via the ``kunit_test`` field in ``task_struct``, which we can
+access using the ``kunit_get_current_test()`` function in ``kunit/test-bug.h``.

-The example below includes how to implement "mocking":
+``kunit_get_current_test()`` is safe to call even if KUnit is not enabled. If
+KUnit is not enabled, was built as a module (``CONFIG_KUNIT=m``), or no test is
+running in the current task, it will return ``NULL``. This compiles down to
+either a no-op or a static key check, so will have a negligible performance
+impact when no test is running.
+
+The example below uses this to implement a "mock" implementation of a function, ``foo``:

.. code-block:: c

- #include <linux/sched.h> /* for current */
+ #include <kunit/test-bug.h> /* for kunit_get_current_test */

struct test_data {
int foo_result;
@@ -660,7 +666,7 @@ The example below includes how to implement "mocking":

static int fake_foo(int arg)
{
- struct kunit *test = current->kunit_test;
+ struct kunit *test = kunit_get_current_test();
struct test_data *test_data = test->priv;

KUNIT_EXPECT_EQ(test, test_data->want_foo_called_with, arg);
@@ -691,7 +697,7 @@ Each test can have multiple resources which have string names providing the same
flexibility as a ``priv`` member, but also, for example, allowing helper
functions to create resources without conflicting with each other. It is also
possible to define a clean up function for each resource, making it easy to
-avoid resource leaks. For more information, see Documentation/dev-tools/kunit/api/test.rst.
+avoid resource leaks. For more information, see Documentation/dev-tools/kunit/api/resource.rst.

Failing The Current Test
------------------------
@@ -719,3 +725,9 @@ structures as shown below:
static void my_debug_function(void) { }
#endif

+``kunit_fail_current_test()`` is safe to call even if KUnit is not enabled. If
+KUnit is not enabled, was built as a module (``CONFIG_KUNIT=m``), or no test is
+running in the current task, it will do nothing. This compiles down to either a
+no-op or a static key check, so will have a negligible performance impact when
+no test is running.
+
diff --git a/include/kunit/test-bug.h b/include/kunit/test-bug.h
index 5fc58081d511..c1b2e14eab64 100644
--- a/include/kunit/test-bug.h
+++ b/include/kunit/test-bug.h
@@ -9,16 +9,63 @@
#ifndef _KUNIT_TEST_BUG_H
#define _KUNIT_TEST_BUG_H

-#define kunit_fail_current_test(fmt, ...) \
- __kunit_fail_current_test(__FILE__, __LINE__, fmt, ##__VA_ARGS__)
-
#if IS_BUILTIN(CONFIG_KUNIT)

+#include <linux/jump_label.h> /* For static branch */
+#include <linux/sched.h>
+
+/* Static key if KUnit is running any tests. */
+DECLARE_STATIC_KEY_FALSE(kunit_running);
+
+/**
+ * kunit_get_current_test() - Return a pointer to the currently running
+ * KUnit test.
+ *
+ * If a KUnit test is running in the current task, returns a pointer to its
+ * associated struct kunit. This pointer can then be passed to any KUnit
+ * function or assertion. If no test is running (or a test is running in a
+ * different task), returns NULL.
+ *
+ * This function is safe to call even when KUnit is disabled. If CONFIG_KUNIT
+ * is not enabled, it will compile down to nothing and will return quickly no
+ * test is running.
+ */
+static inline struct kunit *kunit_get_current_test(void)
+{
+ if (!static_branch_unlikely(&kunit_running))
+ return NULL;
+
+ return current->kunit_test;
+}
+
+
+/**
+ * kunit_fail_current_test() - If a KUnit test is running, fail it.
+ *
+ * If a KUnit test is running in the current task, mark that test as failed.
+ *
+ * This macro will only work if KUnit is built-in (though the tests
+ * themselves can be modules). Otherwise, it compiles down to nothing.
+ */
+#define kunit_fail_current_test(fmt, ...) do { \
+ if (static_branch_unlikely(&kunit_running)) { \
+ __kunit_fail_current_test(__FILE__, __LINE__, \
+ fmt, ##__VA_ARGS__); \
+ } \
+ } while (0)
+
+
extern __printf(3, 4) void __kunit_fail_current_test(const char *file, int line,
const char *fmt, ...);

#else

+static inline struct kunit *kunit_get_current_test(void) { return NULL; }
+
+/* We define this with an empty helper function so format string warnings work */
+#define kunit_fail_current_test(fmt, ...) \
+ __kunit_fail_current_test(__FILE__, __LINE__, fmt, ##__VA_ARGS__)
+
static inline __printf(3, 4) void __kunit_fail_current_test(const char *file, int line,
const char *fmt, ...)
{
--
2.38.1.584.g0f3c55d4c2-goog

2022-11-25 09:05:05

by David Gow

[permalink] [raw]
Subject: [PATCH v4 3/3] mm: slub: test: Use the kunit_get_current_test() function

Use the newly-added function kunit_get_current_test() instead of
accessing current->kunit_test directly. This function uses a static key
to return more quickly when KUnit is enabled, but no tests are actively
running. There should therefore be a negligible performance impact to
enabling the slub KUnit tests.

Other than the performance improvement, this should be a no-op.

Cc: Oliver Glitta <[email protected]>
Cc: Hyeonggon Yoo <[email protected]>
Cc: Christoph Lameter <[email protected]>
Cc: Vlastimil Babka <[email protected]>
Cc: David Rientjes <[email protected]>
Cc: Andrew Morton <[email protected]>
Signed-off-by: David Gow <[email protected]>
Acked-by: Vlastimil Babka <[email protected]>
Acked-by: Hyeonggon Yoo <[email protected]>
---

This is intended as an example use of the new function. Other users
(such as KASAN) will be updated separately, as there would otherwise be
conflicts.

We'll take this whole series via the kselftest/kunit tree.

Changes since v3:
https://lore.kernel.org/linux-kselftest/[email protected]/
- Add Hyeonggon's Acked-by.

Changes since v2:
https://lore.kernel.org/all/[email protected]/
- Get rid of a redundant 'likely' (Thanks Vlastimil Babka)
- Use current->kunit_test directly when we already know a test is
running. (Thanks Vlastimil Babka)
- Add Vlastimil's Acked-by.

There was no v1 of this patch. v1 of the series can be found here:
https://lore.kernel.org/linux-kselftest/[email protected]/T/#u

Cheers,
-- David

---
lib/slub_kunit.c | 1 +
mm/slub.c | 3 ++-
2 files changed, 3 insertions(+), 1 deletion(-)

diff --git a/lib/slub_kunit.c b/lib/slub_kunit.c
index 7a0564d7cb7a..8fd19c8301ad 100644
--- a/lib/slub_kunit.c
+++ b/lib/slub_kunit.c
@@ -1,5 +1,6 @@
// SPDX-License-Identifier: GPL-2.0
#include <kunit/test.h>
+#include <kunit/test-bug.h>
#include <linux/mm.h>
#include <linux/slab.h>
#include <linux/module.h>
diff --git a/mm/slub.c b/mm/slub.c
index 157527d7101b..1887996cb703 100644
--- a/mm/slub.c
+++ b/mm/slub.c
@@ -39,6 +39,7 @@
#include <linux/memcontrol.h>
#include <linux/random.h>
#include <kunit/test.h>
+#include <kunit/test-bug.h>
#include <linux/sort.h>

#include <linux/debugfs.h>
@@ -603,7 +604,7 @@ static bool slab_add_kunit_errors(void)
{
struct kunit_resource *resource;

- if (likely(!current->kunit_test))
+ if (!kunit_get_current_test())
return false;

resource = kunit_find_named_resource(current->kunit_test, "slab_errors");
--
2.38.1.584.g0f3c55d4c2-goog

2022-12-02 01:10:57

by Kees Cook

[permalink] [raw]
Subject: Re: [PATCH v4 1/3] kunit: Provide a static key to check if KUnit is actively running tests

On Fri, Nov 25, 2022 at 04:43:04PM +0800, David Gow wrote:
> KUnit does a few expensive things when enabled. This hasn't been a
> problem because KUnit was only enabled on test kernels, but with a few
> people enabling (but not _using_) KUnit on production systems, we need a
> runtime way of handling this.
>
> Provide a 'kunit_running' static key (defaulting to false), which allows
> us to hide any KUnit code behind a static branch. This should reduce the
> performance impact (on other code) of having KUnit enabled to a single
> NOP when no tests are running.
>
> Note that, while it looks unintuitive, tests always run entirely within
> __kunit_test_suites_init(), so it's safe to decrement the static key at
> the end of this function, rather than in __kunit_test_suites_exit(),
> which is only there to clean up results in debugfs.
>
> Signed-off-by: David Gow <[email protected]>
> Reviewed-by: Daniel Latypov <[email protected]>
> ---
> This should be a no-op (other than a possible performance improvement)
> functionality-wise, and lays the groundwork for a more optimised static
> stub implementation.
>
> The remaining patches in the series add a kunit_get_current_test()
> function which is a more friendly and performant wrapper around
> current->kunit_test, and use this in the slub test. They also improve
> the documentation a bit.
>
> If there are no objections, we'll take the whole series via the KUnit
> tree.
>
> Changes since v3:
> https://lore.kernel.org/linux-kselftest/[email protected]/
> - Use DECLARE_STATIC_KEY_FALSE() -- thanks Daniel!
>
> No changes since v2:
> https://lore.kernel.org/all/[email protected]/
>
> Changes since v1:
> https://lore.kernel.org/linux-kselftest/[email protected]/
> - No changes in this patch.
> - Patch 2/3 is reworked, patch 3/3 is new.
>
> ---
> include/kunit/test.h | 4 ++++
> lib/kunit/test.c | 6 ++++++
> 2 files changed, 10 insertions(+)
>
> diff --git a/include/kunit/test.h b/include/kunit/test.h
> index 4666a4d199ea..87ea90576b50 100644
> --- a/include/kunit/test.h
> +++ b/include/kunit/test.h
> @@ -16,6 +16,7 @@
> #include <linux/container_of.h>
> #include <linux/err.h>
> #include <linux/init.h>
> +#include <linux/jump_label.h>
> #include <linux/kconfig.h>
> #include <linux/kref.h>
> #include <linux/list.h>
> @@ -27,6 +28,9 @@
>
> #include <asm/rwonce.h>
>
> +/* Static key: true if any KUnit tests are currently running */
> +DECLARE_STATIC_KEY_FALSE(kunit_running);
> +
> struct kunit;
>
> /* Size of log associated with test. */
> diff --git a/lib/kunit/test.c b/lib/kunit/test.c
> index 1c9d8d962d67..87a5d795843b 100644
> --- a/lib/kunit/test.c
> +++ b/lib/kunit/test.c
> @@ -20,6 +20,8 @@
> #include "string-stream.h"
> #include "try-catch-impl.h"
>
> +DEFINE_STATIC_KEY_FALSE(kunit_running);
> +
> #if IS_BUILTIN(CONFIG_KUNIT)
> /*
> * Fail the current test and print an error message to the log.
> @@ -615,10 +617,14 @@ int __kunit_test_suites_init(struct kunit_suite * const * const suites, int num_
> return 0;
> }
>
> + static_branch_inc(&kunit_running);

Is it expected there will be multiple tests running? (I was expecting
"static_branch_enable").

> +
> for (i = 0; i < num_suites; i++) {
> kunit_init_suite(suites[i]);
> kunit_run_tests(suites[i]);
> }
> +
> + static_branch_dec(&kunit_running);
> return 0;
> }
> EXPORT_SYMBOL_GPL(__kunit_test_suites_init);
> --
> 2.38.1.584.g0f3c55d4c2-goog
>

Regardless:

Reviewed-by: Kees Cook <[email protected]>

--
Kees Cook

2022-12-02 01:12:39

by Kees Cook

[permalink] [raw]
Subject: Re: [PATCH v4 3/3] mm: slub: test: Use the kunit_get_current_test() function

On Fri, Nov 25, 2022 at 04:43:06PM +0800, David Gow wrote:
> Use the newly-added function kunit_get_current_test() instead of
> accessing current->kunit_test directly. This function uses a static key
> to return more quickly when KUnit is enabled, but no tests are actively
> running. There should therefore be a negligible performance impact to
> enabling the slub KUnit tests.
>
> Other than the performance improvement, this should be a no-op.
>
> Cc: Oliver Glitta <[email protected]>
> Cc: Hyeonggon Yoo <[email protected]>
> Cc: Christoph Lameter <[email protected]>
> Cc: Vlastimil Babka <[email protected]>
> Cc: David Rientjes <[email protected]>
> Cc: Andrew Morton <[email protected]>
> Signed-off-by: David Gow <[email protected]>

Reviewed-by: Kees Cook <[email protected]>

--
Kees Cook

2022-12-02 01:36:14

by Kees Cook

[permalink] [raw]
Subject: Re: [PATCH v4 2/3] kunit: Use the static key when retrieving the current test

On Fri, Nov 25, 2022 at 04:43:05PM +0800, David Gow wrote:
> In order to detect if a KUnit test is running, and to access its
> context, the 'kunit_test' member of the current task_struct is used.
> Usually, this is accessed directly or via the kunit_fail_current_task()
> function.
>
> In order to speed up the case where no test is running, add a wrapper,
> kunit_get_current_test(), which uses the static key to fail early.
> Equally, Speed up kunit_fail_current_test() by using the static key.
>
> This should make it convenient for code to call this
> unconditionally in fakes or error paths, without worrying that this will
> slow the code down significantly.
>
> If CONFIG_KUNIT=n (or m), this compiles away to nothing. If
> CONFIG_KUNIT=y, it will compile down to a NOP (on most architectures) if
> no KUnit test is currently running.
>
> Note that kunit_get_current_test() does not work if KUnit is built as a
> module. This mirrors the existing restriction on kunit_fail_current_test().
>
> Note that the definition of kunit_fail_current_test() still wraps an
> empty, inline function if KUnit is not built-in. This is to ensure that
> the printf format string __attribute__ will still work.
>
> Also update the documentation to suggest users use the new
> kunit_get_current_test() function, update the example, and to describe
> the behaviour when KUnit is disabled better.
>
> Cc: Jonathan Corbet <[email protected]>
> Cc: Sadiya Kazi <[email protected]>
> Signed-off-by: David Gow <[email protected]>

Reviewed-by: Kees Cook <[email protected]>

--
Kees Cook

2022-12-02 01:46:28

by Daniel Latypov

[permalink] [raw]
Subject: Re: [PATCH v4 1/3] kunit: Provide a static key to check if KUnit is actively running tests

On Thu, Dec 1, 2022 at 4:53 PM Kees Cook <[email protected]> wrote:
> > + static_branch_inc(&kunit_running);
>
> Is it expected there will be multiple tests running? (I was expecting
> "static_branch_enable").

It shouldn't normally happen, no.

One possible use case:
KUnit's unit tests for itself create fake test objects and operate on them.
They don't currently exercise this particular code though, afaict
(maybe they should).

>
> > +
> > for (i = 0; i < num_suites; i++) {
> > kunit_init_suite(suites[i]);
> > kunit_run_tests(suites[i]);
> > }
> > +
> > + static_branch_dec(&kunit_running);
> > return 0;
> > }
> > EXPORT_SYMBOL_GPL(__kunit_test_suites_init);
> > --
> > 2.38.1.584.g0f3c55d4c2-goog
> >
>
> Regardless:
>
> Reviewed-by: Kees Cook <[email protected]>
>
> --
> Kees Cook

Daniel

2022-12-05 04:36:07

by Sadiya Kazi

[permalink] [raw]
Subject: Re: [PATCH v4 2/3] kunit: Use the static key when retrieving the current test

On Fri, Nov 25, 2022 at 2:13 PM 'David Gow' via KUnit Development
<[email protected]> wrote:
>
> In order to detect if a KUnit test is running, and to access its
> context, the 'kunit_test' member of the current task_struct is used.
> Usually, this is accessed directly or via the kunit_fail_current_task()
> function.
>
> In order to speed up the case where no test is running, add a wrapper,
> kunit_get_current_test(), which uses the static key to fail early.
> Equally, Speed up kunit_fail_current_test() by using the static key.
>
> This should make it convenient for code to call this
> unconditionally in fakes or error paths, without worrying that this will
> slow the code down significantly.
>
> If CONFIG_KUNIT=n (or m), this compiles away to nothing. If
> CONFIG_KUNIT=y, it will compile down to a NOP (on most architectures) if
> no KUnit test is currently running.
>
> Note that kunit_get_current_test() does not work if KUnit is built as a
> module. This mirrors the existing restriction on kunit_fail_current_test().
>
> Note that the definition of kunit_fail_current_test() still wraps an
> empty, inline function if KUnit is not built-in. This is to ensure that
> the printf format string __attribute__ will still work.
>
> Also update the documentation to suggest users use the new
> kunit_get_current_test() function, update the example, and to describe
> the behaviour when KUnit is disabled better.
>
> Cc: Jonathan Corbet <[email protected]>
> Cc: Sadiya Kazi <[email protected]>
> Signed-off-by: David Gow <[email protected]>
> Reviewed-by: Daniel Latypov <[email protected]>
> ---
Thank you, David. This looks great to me.
Reviewed-by: Sadiya Kazi <[email protected]>

Best Regards,
Sadiya Kazi
>
> As-is, the only code which will be directly affected by this (via the
> kunit_fail_current_test() change) will be UBSAN's KUnit integration.
>
> Patches to port other tests to use kunit_get_current_test() will be sent
> separately (other than the SLUB one in patch 3/3). KASAN in particular
> are reworking their KUnit tests and integration, so we'll use this in a
> follow up to avoid introducing a conflict.
>
> Changes since v3:
> https://lore.kernel.org/linux-kselftest/[email protected]/
> - Use DECLARE_STATIC_KEY_FALSE() -- Thanks Daniel.
> - Some documentation rewording to make the behaviour a bit clearer.
> - Thanks Daniel and Sadiya
>
> Changes since v2:
> https://lore.kernel.org/all/[email protected]/
> - Only add kunit_get_current_test() when KUnit is built-in, as the
> static key isn't available otherwise.
> - I'm going to try to put together some patches to make things like
> this available when CONFIG_KUNIT=m in the future.
> - Also update the documentation to note this.
>
> Changes since v1:
> https://lore.kernel.org/linux-kselftest/[email protected]/
> - Fix a missing '}' which broke everything. Thanks Kees, kernel test
> robot.
> - Add the new kunit_get_current_test() function, as most of the cases
> where we retrieve the current test (even to fail it) were accessing
> current->kunit_test directly, not using kunit_fail_current_test().
> - Add some documentation comments.
> - Update the documentation in usage.rst.
> - The version in tips.rst was not updated, and will be removed:
> https://lore.kernel.org/linux-kselftest/[email protected]/
>
> ---
> Documentation/dev-tools/kunit/usage.rst | 30 +++++++++-----
> include/kunit/test-bug.h | 53 +++++++++++++++++++++++--
> 2 files changed, 71 insertions(+), 12 deletions(-)
>
> diff --git a/Documentation/dev-tools/kunit/usage.rst b/Documentation/dev-tools/kunit/usage.rst
> index 22416ebb94ab..48f8196d5aad 100644
> --- a/Documentation/dev-tools/kunit/usage.rst
> +++ b/Documentation/dev-tools/kunit/usage.rst
> @@ -641,17 +641,23 @@ as shown in next section: *Accessing The Current Test*.
> Accessing The Current Test
> --------------------------
>
> -In some cases, we need to call test-only code from outside the test file.
> -For example, see example in section *Injecting Test-Only Code* or if
> -we are providing a fake implementation of an ops struct. Using
> -``kunit_test`` field in ``task_struct``, we can access it via
> -``current->kunit_test``.
> +In some cases, we need to call test-only code from outside the test file. This
> +is helpful, for example, when providing a fake implementation of a function, or
> +to fail any current test from within an error handler.
> +We can do this via the ``kunit_test`` field in ``task_struct``, which we can
> +access using the ``kunit_get_current_test()`` function in ``kunit/test-bug.h``.
>
> -The example below includes how to implement "mocking":
> +``kunit_get_current_test()`` is safe to call even if KUnit is not enabled. If
> +KUnit is not enabled, was built as a module (``CONFIG_KUNIT=m``), or no test is
> +running in the current task, it will return ``NULL``. This compiles down to
> +either a no-op or a static key check, so will have a negligible performance
> +impact when no test is running.
> +
> +The example below uses this to implement a "mock" implementation of a function, ``foo``:
>
> .. code-block:: c
>
> - #include <linux/sched.h> /* for current */
> + #include <kunit/test-bug.h> /* for kunit_get_current_test */
>
> struct test_data {
> int foo_result;
> @@ -660,7 +666,7 @@ The example below includes how to implement "mocking":
>
> static int fake_foo(int arg)
> {
> - struct kunit *test = current->kunit_test;
> + struct kunit *test = kunit_get_current_test();
> struct test_data *test_data = test->priv;
>
> KUNIT_EXPECT_EQ(test, test_data->want_foo_called_with, arg);
> @@ -691,7 +697,7 @@ Each test can have multiple resources which have string names providing the same
> flexibility as a ``priv`` member, but also, for example, allowing helper
> functions to create resources without conflicting with each other. It is also
> possible to define a clean up function for each resource, making it easy to
> -avoid resource leaks. For more information, see Documentation/dev-tools/kunit/api/test.rst.
> +avoid resource leaks. For more information, see Documentation/dev-tools/kunit/api/resource.rst.
>
> Failing The Current Test
> ------------------------
> @@ -719,3 +725,9 @@ structures as shown below:
> static void my_debug_function(void) { }
> #endif
>
> +``kunit_fail_current_test()`` is safe to call even if KUnit is not enabled. If
> +KUnit is not enabled, was built as a module (``CONFIG_KUNIT=m``), or no test is
> +running in the current task, it will do nothing. This compiles down to either a
> +no-op or a static key check, so will have a negligible performance impact when
> +no test is running.
> +
> diff --git a/include/kunit/test-bug.h b/include/kunit/test-bug.h
> index 5fc58081d511..c1b2e14eab64 100644
> --- a/include/kunit/test-bug.h
> +++ b/include/kunit/test-bug.h
> @@ -9,16 +9,63 @@
> #ifndef _KUNIT_TEST_BUG_H
> #define _KUNIT_TEST_BUG_H
>
> -#define kunit_fail_current_test(fmt, ...) \
> - __kunit_fail_current_test(__FILE__, __LINE__, fmt, ##__VA_ARGS__)
> -
> #if IS_BUILTIN(CONFIG_KUNIT)
>
> +#include <linux/jump_label.h> /* For static branch */
> +#include <linux/sched.h>
> +
> +/* Static key if KUnit is running any tests. */
> +DECLARE_STATIC_KEY_FALSE(kunit_running);
> +
> +/**
> + * kunit_get_current_test() - Return a pointer to the currently running
> + * KUnit test.
> + *
> + * If a KUnit test is running in the current task, returns a pointer to its
> + * associated struct kunit. This pointer can then be passed to any KUnit
> + * function or assertion. If no test is running (or a test is running in a
> + * different task), returns NULL.
> + *
> + * This function is safe to call even when KUnit is disabled. If CONFIG_KUNIT
> + * is not enabled, it will compile down to nothing and will return quickly no
> + * test is running.
> + */
> +static inline struct kunit *kunit_get_current_test(void)
> +{
> + if (!static_branch_unlikely(&kunit_running))
> + return NULL;
> +
> + return current->kunit_test;
> +}
> +
> +
> +/**
> + * kunit_fail_current_test() - If a KUnit test is running, fail it.
> + *
> + * If a KUnit test is running in the current task, mark that test as failed.
> + *
> + * This macro will only work if KUnit is built-in (though the tests
> + * themselves can be modules). Otherwise, it compiles down to nothing.
> + */
> +#define kunit_fail_current_test(fmt, ...) do { \
> + if (static_branch_unlikely(&kunit_running)) { \
> + __kunit_fail_current_test(__FILE__, __LINE__, \
> + fmt, ##__VA_ARGS__); \
> + } \
> + } while (0)
> +
> +
> extern __printf(3, 4) void __kunit_fail_current_test(const char *file, int line,
> const char *fmt, ...);
>
> #else
>
> +static inline struct kunit *kunit_get_current_test(void) { return NULL; }
> +
> +/* We define this with an empty helper function so format string warnings work */
> +#define kunit_fail_current_test(fmt, ...) \
> + __kunit_fail_current_test(__FILE__, __LINE__, fmt, ##__VA_ARGS__)
> +
> static inline __printf(3, 4) void __kunit_fail_current_test(const char *file, int line,
> const char *fmt, ...)
> {
> --
> 2.38.1.584.g0f3c55d4c2-goog
>
> --
> You received this message because you are subscribed to the Google Groups "KUnit Development" group.
> To unsubscribe from this group and stop receiving emails from it, send an email to [email protected].
> To view this discussion on the web visit https://groups.google.com/d/msgid/kunit-dev/20221125084306.1063074-2-davidgow%40google.com.