2023-12-08 10:09:56

by David Gow

[permalink] [raw]
Subject: [PATCH v2 0/4] kunit: Add helpers for creating test-managed devices

KUnit tests often need to provide a struct device, and thus far have
mostly been using root_device_register() or platform devices to create
a 'fake device' for use with, e.g., code which uses device-managed
resources. This has several disadvantages, including not being designed
for test use, scattering files in sysfs, and requiring manual teardown
on test exit, which may not always be possible in case of failure.

Instead, introduce a set of helper functions which allow devices
(internally a struct kunit_device) to be created and managed by KUnit --
i.e., they will be automatically unregistered on test exit. These
helpers can either use a user-provided struct device_driver, or have one
automatically created and managed by KUnit. In both cases, the device
lives on a new kunit_bus.

This is a follow-up to a previous proposal here:
https://lore.kernel.org/linux-kselftest/[email protected]/

(The kunit_defer() function in the first patch there has since been
merged as the 'deferred actions' feature.)

My intention is to take this whole series in via the kselftest/kunit
branch, but I'm equally okay with splitting up the later patches which
use this to go via the various subsystem trees in case there are merge
conflicts.

I'd really appreciate any extra scrutiny that can be given to this;
particularly around the device refcounts and whether we can guarantee
that the device will be released at the correct point in the test
cleanup. I've seen a few crashes in kunit_cleanup, but only on some
already flaky/fragile UML/clang/alltests setups, which seem to go away
if I remove the devm_add_action() call (or if I enable any debugging
features / symbols, annoyingly).

Cheers,
-- David

Signed-off-by: David Gow <[email protected]>
---
Changes in v2:
- Simplify device/driver/bus matching, removing the no-longer-required
kunit_bus_match function. (Thanks, Greg)
- The return values are both more consistent (kunit_device_register now
returns an explicit error pointer, rather than failing the test), and
better documented.
- Add some basic documentation to the implementations as well as the
headers. The documentation in the headers is still more complete, and
is now properly compiled into the HTML documentation (under
dev-tools/kunit/api/resources.html). (Thanks, Matti)
- Moved the internal-only kunit_bus_init() function to a private header,
lib/kunit/device-impl.h to avoid polluting the public headers, and
match other internal-only headers. (Thanks, Greg)
- Alphabetise KUnit includes in other test modules. (Thanks, Amadeusz.)
- Several code cleanups, particularly around error handling and
allocation. (Thanks Greg, Maxime)
- Several const-correctness and casting improvements. (Thanks, Greg)
- Added a new test to verify KUnit cleanup triggers device cleanup.
(Thanks, Maxime).
- Improved the user-specified device test to verify that probe/remove
hooks are called correctly. (Thanks, Maxime).
- The overflow test no-longer needlessly calls
kunit_device_unregister().
- Several other minor cleanups and documentation improvements, which
hopefully make this a bit clearer and more robust.
- Link to v1: https://lore.kernel.org/r/[email protected]

---
David Gow (4):
kunit: Add APIs for managing devices
fortify: test: Use kunit_device
overflow: Replace fake root_device with kunit_device
ASoC: topology: Replace fake root_device with kunit_device in tests

Documentation/dev-tools/kunit/api/resource.rst | 9 ++
Documentation/dev-tools/kunit/usage.rst | 50 +++++++
include/kunit/device.h | 80 +++++++++++
lib/fortify_kunit.c | 5 +-
lib/kunit/Makefile | 3 +-
lib/kunit/device.c | 181 +++++++++++++++++++++++++
lib/kunit/kunit-test.c | 134 +++++++++++++++++-
lib/kunit/test.c | 3 +
lib/overflow_kunit.c | 5 +-
sound/soc/soc-topology-test.c | 10 +-
10 files changed, 465 insertions(+), 15 deletions(-)
---
base-commit: c8613be119892ccceffbc550b9b9d7d68b995c9e
change-id: 20230718-kunit_bus-ab19c4ef48dc

Best regards,
--
David Gow <[email protected]>


2023-12-08 10:10:19

by David Gow

[permalink] [raw]
Subject: [PATCH v2 1/4] kunit: Add APIs for managing devices

Tests for drivers often require a struct device to pass to other
functions. While it's possible to create these with
root_device_register(), or to use something like a platform device, this
is both a misuse of those APIs, and can be difficult to clean up after,
for example, a failed assertion.

Add some KUnit-specific functions for registering and unregistering a
struct device:
- kunit_device_register()
- kunit_device_register_with_driver()
- kunit_device_unregister()

These helpers allocate a on a 'kunit' bus which will either probe the
driver passed in (kunit_device_register_with_driver), or will create a
stub driver (kunit_device_register) which is cleaned up on test shutdown.

Devices are automatically unregistered on test shutdown, but can be
manually unregistered earlier with kunit_device_unregister() in order
to, for example, test device release code.

Reviewed-by: Matti Vaittinen <[email protected]>
Signed-off-by: David Gow <[email protected]>
---
Documentation/dev-tools/kunit/api/resource.rst | 9 ++
Documentation/dev-tools/kunit/usage.rst | 50 +++++++
include/kunit/device.h | 80 +++++++++++
lib/kunit/Makefile | 3 +-
lib/kunit/device.c | 181 +++++++++++++++++++++++++
lib/kunit/kunit-test.c | 134 +++++++++++++++++-
lib/kunit/test.c | 3 +
7 files changed, 458 insertions(+), 2 deletions(-)

diff --git a/Documentation/dev-tools/kunit/api/resource.rst b/Documentation/dev-tools/kunit/api/resource.rst
index 0a94f831259e..ec6002a6b0db 100644
--- a/Documentation/dev-tools/kunit/api/resource.rst
+++ b/Documentation/dev-tools/kunit/api/resource.rst
@@ -11,3 +11,12 @@ state on a per-test basis, register custom cleanup actions, and more.

.. kernel-doc:: include/kunit/resource.h
:internal:
+
+Managed Devices
+---------------
+
+Functions for using KUnit-managed struct device and struct device_driver.
+Include ``kunit/device.h`` to use these.
+
+.. kernel-doc:: include/kunit/device.h
+ :internal:
diff --git a/Documentation/dev-tools/kunit/usage.rst b/Documentation/dev-tools/kunit/usage.rst
index 9db12e91668e..53c6f7dc8a42 100644
--- a/Documentation/dev-tools/kunit/usage.rst
+++ b/Documentation/dev-tools/kunit/usage.rst
@@ -797,3 +797,53 @@ structures as shown below:
KUnit is not enabled, or if 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.
+
+Managing Fake Devices and Drivers
+---------------------------------
+
+When testing drivers or code which interacts with drivers, many functions will
+require a ``struct device`` or ``struct device_driver``. In many cases, setting
+up a real device is not required to test any given function, so a fake device
+can be used instead.
+
+KUnit provides helper functions to create and manage these fake devices, which
+are internally of type ``struct kunit_device``, and are attached to a special
+``kunit_bus``. These devices support managed device resources (devres), as
+described in Documentation/driver-api/driver-model/devres.rst
+
+To create a KUnit-managed ``struct device_driver``, use ``kunit_driver_create()``,
+which will create a driver with the given name, on the ``kunit_bus``. This driver
+will automatically be destroyed when the corresponding test finishes, but can also
+be manually destroyed with ``driver_unregister()``.
+
+To create a fake device, use the ``kunit_device_register()``, which will create
+and register a device, using a new KUnit-managed driver created with ``kunit_driver_create()``.
+To provide a specific, non-KUnit-managed driver, use ``kunit_device_register_with_driver()``
+instead. Like with managed drivers, KUnit-managed fake devices are automatically
+cleaned up when the test finishes, but can be manually cleaned up early with
+``kunit_device_unregister()``.
+
+The KUnit devices should be used in preference to ``root_device_register()``, and
+instead of ``platform_device_register()`` in cases where the device is not otherwise
+a platform device.
+
+For example:
+
+.. code-block:: c
+
+ #include <kunit/device.h>
+
+ static void test_my_device(struct kunit *test)
+ {
+ struct device *fake_device;
+ const char *dev_managed_string;
+
+ // Create a fake device.
+ fake_device = kunit_device_register(test, "my_device");
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, fake_device)
+
+ // Pass it to functions which need a device.
+ dev_managed_string = devm_kstrdup(fake_device, "Hello, World!");
+
+ // Everything is cleaned up automatically when the test ends.
+ }
\ No newline at end of file
diff --git a/include/kunit/device.h b/include/kunit/device.h
new file mode 100644
index 000000000000..2450110ad64e
--- /dev/null
+++ b/include/kunit/device.h
@@ -0,0 +1,80 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * KUnit basic device implementation
+ *
+ * Helpers for creating and managing fake devices for KUnit tests.
+ *
+ * Copyright (C) 2023, Google LLC.
+ * Author: David Gow <[email protected]>
+ */
+
+#ifndef _KUNIT_DEVICE_H
+#define _KUNIT_DEVICE_H
+
+#if IS_ENABLED(CONFIG_KUNIT)
+
+#include <kunit/test.h>
+
+struct device;
+struct device_driver;
+
+/**
+ * kunit_driver_create() - Create a struct device_driver attached to the kunit_bus
+ * @test: The test context object.
+ * @name: The name to give the created driver.
+ *
+ * Creates a struct device_driver attached to the kunit_bus, with the name @name.
+ * This driver will automatically be cleaned up on test exit.
+ *
+ * Return: a stub struct device_driver, managed by KUnit, with the name @name.
+ */
+struct device_driver *kunit_driver_create(struct kunit *test, const char *name);
+
+/**
+ * kunit_device_register() - Create a struct device for use in KUnit tests
+ * @test: The test context object.
+ * @name: The name to give the created device.
+ *
+ * Creates a struct kunit_device (which is a struct device) with the given name,
+ * and a corresponding driver. The device and driver will be cleaned up on test
+ * exit, or when kunit_device_unregister is called. See also
+ * kunit_device_register_with_driver, if you wish to provide your own
+ * struct device_driver.
+ *
+ * Return: a pointer to a struct device which will be cleaned up when the test
+ * exits, or an error pointer if the device could not be allocated or registered.
+ */
+struct device *kunit_device_register(struct kunit *test, const char *name);
+
+/**
+ * kunit_device_register_with_driver() - Create a struct device for use in KUnit tests
+ * @test: The test context object.
+ * @name: The name to give the created device.
+ * @drv: The struct device_driver to associate with the device.
+ *
+ * Creates a struct kunit_device (which is a struct device) with the given
+ * name, and driver. The device will be cleaned up on test exit, or when
+ * kunit_device_unregister is called. See also kunit_device_register, if you
+ * wish KUnit to create and manage a driver for you.
+ *
+ * Return: a pointer to a struct device which will be cleaned up when the test
+ * exits, or an error pointer if the device could not be allocated or registered.
+ */
+struct device *kunit_device_register_with_driver(struct kunit *test,
+ const char *name,
+ const struct device_driver *drv);
+
+/**
+ * kunit_device_unregister() - Unregister a KUnit-managed device
+ * @test: The test context object which created the device
+ * @dev: The device.
+ *
+ * Unregisters and destroys a struct device which was created with
+ * kunit_device_register or kunit_device_register_with_driver. If KUnit created
+ * a driver, cleans it up as well.
+ */
+void kunit_device_unregister(struct kunit *test, struct device *dev);
+
+#endif
+
+#endif
diff --git a/lib/kunit/Makefile b/lib/kunit/Makefile
index 46f75f23dfe4..309659a32a78 100644
--- a/lib/kunit/Makefile
+++ b/lib/kunit/Makefile
@@ -7,7 +7,8 @@ kunit-objs += test.o \
assert.o \
try-catch.o \
executor.o \
- attributes.o
+ attributes.o \
+ device.o

ifeq ($(CONFIG_KUNIT_DEBUGFS),y)
kunit-objs += debugfs.o
diff --git a/lib/kunit/device.c b/lib/kunit/device.c
new file mode 100644
index 000000000000..1db4305b615a
--- /dev/null
+++ b/lib/kunit/device.c
@@ -0,0 +1,181 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * KUnit-managed device implementation
+ *
+ * Implementation of struct kunit_device helpers for fake devices whose
+ * lifecycle is managed by KUnit.
+ *
+ * Copyright (C) 2023, Google LLC.
+ * Author: David Gow <[email protected]>
+ */
+
+#include <linux/device.h>
+
+#include <kunit/test.h>
+#include <kunit/device.h>
+#include <kunit/resource.h>
+
+#include "device-impl.h"
+
+/* Wrappers for use with kunit_add_action() */
+KUNIT_DEFINE_ACTION_WRAPPER(device_unregister_wrapper, device_unregister, struct device *);
+KUNIT_DEFINE_ACTION_WRAPPER(driver_unregister_wrapper, driver_unregister, struct device_driver *);
+
+/* The root device for the KUnit bus, parent of all kunit_devices. */
+static struct device *kunit_bus_device;
+
+/* A device owned by a KUnit test. */
+struct kunit_device {
+ struct device dev;
+ /* The KUnit test which owns this device. */
+ struct kunit *owner;
+ /* If the driver is managed by KUnit and unique to this device. */
+ const struct device_driver *driver;
+};
+
+#define to_kunit_device(d) container_of_const(d, struct kunit_device, dev)
+
+static struct bus_type kunit_bus_type = {
+ .name = "kunit",
+};
+
+/* Register the 'kunit_bus' used for fake devices. */
+int kunit_bus_init(void)
+{
+ int error;
+
+ kunit_bus_device = root_device_register("kunit");
+ if (!kunit_bus_device)
+ return -ENOMEM;
+
+ error = bus_register(&kunit_bus_type);
+ if (error)
+ bus_unregister(&kunit_bus_type);
+ return error;
+}
+
+/* Release a 'fake' KUnit device. */
+static void kunit_device_release(struct device *d)
+{
+ kfree(to_kunit_device(d));
+}
+
+/**
+ * Create and register a KUnit-managed struct device_driver on the kunit_bus.
+ * Returns an error pointer on failure.
+ */
+struct device_driver *kunit_driver_create(struct kunit *test, const char *name)
+{
+ struct device_driver *driver;
+ int err = -ENOMEM;
+
+ driver = kunit_kzalloc(test, sizeof(*driver), GFP_KERNEL);
+
+ if (!driver)
+ return ERR_PTR(err);
+
+ driver->name = name;
+ driver->bus = &kunit_bus_type;
+ driver->owner = THIS_MODULE;
+
+ err = driver_register(driver);
+ if (err) {
+ kunit_kfree(test, driver);
+ return ERR_PTR(err);
+ }
+
+ kunit_add_action(test, driver_unregister_wrapper, driver);
+ return driver;
+}
+EXPORT_SYMBOL_GPL(kunit_driver_create);
+
+/* Helper which creates a kunit_device, attaches it to the kunit_bus*/
+static struct kunit_device *kunit_device_register_internal(struct kunit *test,
+ const char *name,
+ const struct device_driver *drv)
+{
+ struct kunit_device *kunit_dev;
+ int err = -ENOMEM;
+
+ kunit_dev = kzalloc(sizeof(*kunit_dev), GFP_KERNEL);
+ if (!kunit_dev)
+ return ERR_PTR(err);
+
+ kunit_dev->owner = test;
+
+ err = dev_set_name(&kunit_dev->dev, "%s.%s", test->name, name);
+ if (err) {
+ kfree(kunit_dev);
+ return ERR_PTR(err);
+ }
+
+ kunit_dev->dev.release = kunit_device_release;
+ kunit_dev->dev.bus = &kunit_bus_type;
+ kunit_dev->dev.parent = kunit_bus_device;
+
+ err = device_register(&kunit_dev->dev);
+ if (err) {
+ put_device(&kunit_dev->dev);
+ return ERR_PTR(err);
+ }
+
+ kunit_add_action(test, device_unregister_wrapper, &kunit_dev->dev);
+
+ return kunit_dev;
+}
+
+/**
+ * Create and register a new KUnit-managed device, using the user-supplied device_driver.
+ * On failure, returns an error pointer.
+ */
+struct device *kunit_device_register_with_driver(struct kunit *test,
+ const char *name,
+ const struct device_driver *drv)
+{
+ struct kunit_device *kunit_dev = kunit_device_register_internal(test, name, drv);
+
+ if (IS_ERR_OR_NULL(kunit_dev))
+ return ERR_CAST(kunit_dev);
+
+ return &kunit_dev->dev;
+}
+EXPORT_SYMBOL_GPL(kunit_device_register_with_driver);
+
+/**
+ * Create and register a new KUnit-managed device, including a matching device_driver.
+ * On failure, returns an error pointer.
+ */
+struct device *kunit_device_register(struct kunit *test, const char *name)
+{
+ struct device_driver *drv;
+ struct kunit_device *dev;
+
+ drv = kunit_driver_create(test, name);
+ if (IS_ERR(drv))
+ return ERR_CAST(drv);
+
+ dev = kunit_device_register_internal(test, name, drv);
+ if (IS_ERR(dev)) {
+ kunit_release_action(test, driver_unregister_wrapper, (void *)drv);
+ return ERR_CAST(dev);
+ }
+
+ /* Request the driver be freed. */
+ dev->driver = drv;
+
+
+ return &dev->dev;
+}
+EXPORT_SYMBOL_GPL(kunit_device_register);
+
+/* Unregisters a KUnit-managed device early (including the driver, if automatically created). */
+void kunit_device_unregister(struct kunit *test, struct device *dev)
+{
+ const struct device_driver *driver = to_kunit_device(dev)->driver;
+
+ kunit_release_action(test, device_unregister_wrapper, dev);
+ if (driver)
+ kunit_release_action(test, driver_unregister_wrapper, (void *)driver);
+}
+EXPORT_SYMBOL_GPL(kunit_device_unregister);
+
diff --git a/lib/kunit/kunit-test.c b/lib/kunit/kunit-test.c
index 3e9c5192d095..6c666fbdd6ad 100644
--- a/lib/kunit/kunit-test.c
+++ b/lib/kunit/kunit-test.c
@@ -8,6 +8,9 @@
#include <kunit/test.h>
#include <kunit/test-bug.h>

+#include <linux/device.h>
+#include <kunit/device.h>
+
#include "string-stream.h"
#include "try-catch-impl.h"

@@ -687,6 +690,135 @@ static struct kunit_case kunit_current_test_cases[] = {
{}
};

+static void test_dev_action(void *priv)
+{
+ *(void **)priv = (void *)1;
+}
+
+static void kunit_device_test(struct kunit *test)
+{
+ struct device *test_device;
+ long action_was_run = 0;
+
+ test_device = kunit_device_register(test, "my_device");
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, test_device);
+
+ // Add an action to verify cleanup.
+ devm_add_action(test_device, test_dev_action, &action_was_run);
+
+ KUNIT_EXPECT_EQ(test, action_was_run, 0);
+
+ kunit_device_unregister(test, test_device);
+
+ KUNIT_EXPECT_EQ(test, action_was_run, 1);
+}
+
+static void kunit_device_cleanup_test(struct kunit *test)
+{
+ struct device *test_device;
+ long action_was_run = 0;
+
+ test_device = kunit_device_register(test, "my_device");
+ KUNIT_ASSERT_NOT_NULL(test, test_device);
+
+ /* Add an action to verify cleanup. */
+ devm_add_action(test_device, test_dev_action, &action_was_run);
+
+ KUNIT_EXPECT_EQ(test, action_was_run, 0);
+
+ /* Force KUnit to run cleanup early. */
+ kunit_cleanup(test);
+
+ KUNIT_EXPECT_EQ(test, action_was_run, 1);
+}
+
+struct driver_test_state {
+ bool driver_device_probed;
+ bool driver_device_removed;
+ long action_was_run;
+};
+
+static int driver_probe_hook(struct device *dev)
+{
+ struct kunit *test = kunit_get_current_test();
+ struct driver_test_state *state = (struct driver_test_state *)test->priv;
+
+ state->driver_device_probed = true;
+ return 0;
+}
+
+static int driver_remove_hook(struct device *dev)
+{
+ struct kunit *test = kunit_get_current_test();
+ struct driver_test_state *state = (struct driver_test_state *)test->priv;
+
+ state->driver_device_removed = true;
+ return 0;
+}
+
+
+static void kunit_device_driver_test(struct kunit *test)
+{
+ struct device_driver *test_driver;
+ struct device *test_device;
+ struct driver_test_state test_state = {};
+
+ test->priv = &test_state;
+ test_driver = kunit_driver_create(test, "my_driver");
+
+ // This can fail with an error pointer.
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, test_driver);
+
+ test_driver->probe = driver_probe_hook;
+ test_driver->remove = driver_remove_hook;
+
+ test_device = kunit_device_register_with_driver(test, "my_device", test_driver);
+
+ // This can fail with an error pointer.
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, test_device);
+
+ // Make sure the probe function was called.
+ KUNIT_ASSERT_TRUE(test, test_state.driver_device_probed);
+
+ // Add an action to verify cleanup.
+ devm_add_action(test_device, test_dev_action, &test_state.action_was_run);
+
+ KUNIT_EXPECT_EQ(test, test_state.action_was_run, 0);
+
+ kunit_device_unregister(test, test_device);
+ test_device = NULL;
+
+ // Make sure the remove hook was called.
+ KUNIT_ASSERT_TRUE(test, test_state.driver_device_removed);
+
+ // We're going to test this again.
+ test_state.driver_device_probed = false;
+
+ // The driver should not automatically be destroyed by
+ // kunit_device_unregister, so we can re-use it.
+ test_device = kunit_device_register_with_driver(test, "my_device", test_driver);
+
+ // This can fail with an error pointer.
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, test_device);
+
+ // Probe was called again.
+ KUNIT_ASSERT_TRUE(test, test_state.driver_device_probed);
+
+ // Everything is automatically freed here.
+}
+
+static struct kunit_case kunit_device_test_cases[] = {
+ KUNIT_CASE(kunit_device_test),
+ KUNIT_CASE(kunit_device_cleanup_test),
+ KUNIT_CASE(kunit_device_driver_test),
+ {}
+};
+
+static struct kunit_suite kunit_device_test_suite = {
+ .name = "kunit_device",
+ .test_cases = kunit_device_test_cases,
+};
+
static struct kunit_suite kunit_current_test_suite = {
.name = "kunit_current",
.test_cases = kunit_current_test_cases,
@@ -694,6 +826,6 @@ static struct kunit_suite kunit_current_test_suite = {

kunit_test_suites(&kunit_try_catch_test_suite, &kunit_resource_test_suite,
&kunit_log_test_suite, &kunit_status_test_suite,
- &kunit_current_test_suite);
+ &kunit_current_test_suite, &kunit_device_test_suite);

MODULE_LICENSE("GPL v2");
diff --git a/lib/kunit/test.c b/lib/kunit/test.c
index 0308865194bb..c457593e4913 100644
--- a/lib/kunit/test.c
+++ b/lib/kunit/test.c
@@ -18,6 +18,7 @@
#include <linux/sched.h>

#include "debugfs.h"
+#include "device-impl.h"
#include "hooks-impl.h"
#include "string-stream.h"
#include "try-catch-impl.h"
@@ -840,6 +841,8 @@ static int __init kunit_init(void)
kunit_install_hooks();

kunit_debugfs_init();
+
+ kunit_bus_init();
#ifdef CONFIG_MODULES
return register_module_notifier(&kunit_mod_nb);
#else

--
2.43.0.472.g3155946c3a-goog

2023-12-08 10:10:24

by David Gow

[permalink] [raw]
Subject: [PATCH v2 3/4] overflow: Replace fake root_device with kunit_device

Using struct root_device to create fake devices for tests is something
of a hack. The new struct kunit_device is meant for this purpose, so use
it instead.

Reviewed-by: Matti Vaittinen <[email protected]>
Signed-off-by: David Gow <[email protected]>
---
lib/overflow_kunit.c | 5 ++---
1 file changed, 2 insertions(+), 3 deletions(-)

diff --git a/lib/overflow_kunit.c b/lib/overflow_kunit.c
index 34db0b3aa502..c527f6b75789 100644
--- a/lib/overflow_kunit.c
+++ b/lib/overflow_kunit.c
@@ -6,6 +6,7 @@
*/
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt

+#include <kunit/device.h>
#include <kunit/test.h>
#include <linux/device.h>
#include <linux/kernel.h>
@@ -618,7 +619,7 @@ static void overflow_allocation_test(struct kunit *test)
} while (0)

/* Create dummy device for devm_kmalloc()-family tests. */
- dev = root_device_register(device_name);
+ dev = kunit_device_register(test, device_name);
KUNIT_ASSERT_FALSE_MSG(test, IS_ERR(dev),
"Cannot register test device\n");

@@ -634,8 +635,6 @@ static void overflow_allocation_test(struct kunit *test)
check_allocation_overflow(devm_kmalloc);
check_allocation_overflow(devm_kzalloc);

- device_unregister(dev);
-
kunit_info(test, "%d allocation overflow tests finished\n", count);
#undef check_allocation_overflow
}

--
2.43.0.472.g3155946c3a-goog

2023-12-08 10:10:25

by David Gow

[permalink] [raw]
Subject: [PATCH v2 2/4] fortify: test: Use kunit_device

Using struct root_device to create fake devices for tests is something
of a hack. The new struct kunit_device is meant for this purpose, so use
it instead.

Reviewed-by: Matti Vaittinen <[email protected]>
Acked-by: Kees Cook <[email protected]>
Signed-off-by: David Gow <[email protected]>
---
lib/fortify_kunit.c | 5 +++--
1 file changed, 3 insertions(+), 2 deletions(-)

diff --git a/lib/fortify_kunit.c b/lib/fortify_kunit.c
index c8c33cbaae9e..2e4fedc81621 100644
--- a/lib/fortify_kunit.c
+++ b/lib/fortify_kunit.c
@@ -15,6 +15,7 @@
*/
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt

+#include <kunit/device.h>
#include <kunit/test.h>
#include <linux/device.h>
#include <linux/slab.h>
@@ -269,7 +270,7 @@ DEFINE_ALLOC_SIZE_TEST_PAIR(kvmalloc)
size_t len; \
\
/* Create dummy device for devm_kmalloc()-family tests. */ \
- dev = root_device_register(dev_name); \
+ dev = kunit_device_register(test, dev_name); \
KUNIT_ASSERT_FALSE_MSG(test, IS_ERR(dev), \
"Cannot register test device\n"); \
\
@@ -303,7 +304,7 @@ DEFINE_ALLOC_SIZE_TEST_PAIR(kvmalloc)
checker(len, devm_kmemdup(dev, "Ohai", len, gfp), \
devm_kfree(dev, p)); \
\
- device_unregister(dev); \
+ kunit_device_unregister(test, dev); \
} while (0)
DEFINE_ALLOC_SIZE_TEST_PAIR(devm_kmalloc)


--
2.43.0.472.g3155946c3a-goog

2023-12-08 10:10:32

by David Gow

[permalink] [raw]
Subject: [PATCH v2 4/4] ASoC: topology: Replace fake root_device with kunit_device in tests

Using struct root_device to create fake devices for tests is something
of a hack. The new struct kunit_device is meant for this purpose, so use
it instead.

Acked-by: Mark Brown <[email protected]>
Signed-off-by: David Gow <[email protected]>
---
sound/soc/soc-topology-test.c | 10 ++--------
1 file changed, 2 insertions(+), 8 deletions(-)

diff --git a/sound/soc/soc-topology-test.c b/sound/soc/soc-topology-test.c
index 2cd3540cec04..70cbccc42a42 100644
--- a/sound/soc/soc-topology-test.c
+++ b/sound/soc/soc-topology-test.c
@@ -9,6 +9,7 @@
#include <sound/core.h>
#include <sound/soc.h>
#include <sound/soc-topology.h>
+#include <kunit/device.h>
#include <kunit/test.h>

/* ===== HELPER FUNCTIONS =================================================== */
@@ -21,26 +22,19 @@
*/
static struct device *test_dev;

-static struct device_driver test_drv = {
- .name = "sound-soc-topology-test-driver",
-};
-
static int snd_soc_tplg_test_init(struct kunit *test)
{
- test_dev = root_device_register("sound-soc-topology-test");
+ test_dev = kunit_device_register(test, "sound-soc-topology-test");
test_dev = get_device(test_dev);
if (!test_dev)
return -ENODEV;

- test_dev->driver = &test_drv;
-
return 0;
}

static void snd_soc_tplg_test_exit(struct kunit *test)
{
put_device(test_dev);
- root_device_unregister(test_dev);
}

/*

--
2.43.0.472.g3155946c3a-goog

2023-12-13 15:03:22

by Maxime Ripard

[permalink] [raw]
Subject: Re: [PATCH v2 1/4] kunit: Add APIs for managing devices

On Fri, 8 Dec 2023 18:09:29 +0800, [email protected] wrote:
> Tests for drivers often require a struct device to pass to other
> functions. While it's possible to create these with
> root_device_register(), or to use something like a platform device, this
> is both a misuse of those APIs, and can be difficult to clean up after,
> for example, a failed assertion.
>
> [ ... ]

Reviewed-by: Maxime Ripard <[email protected]>

Thanks!
Maxime

2023-12-13 15:05:08

by Maxime Ripard

[permalink] [raw]
Subject: Re: [PATCH v2 0/4] kunit: Add helpers for creating test-managed devices

Hi David,

On Fri, Dec 08, 2023 at 06:09:28PM +0800, [email protected] wrote:
> KUnit tests often need to provide a struct device, and thus far have
> mostly been using root_device_register() or platform devices to create
> a 'fake device' for use with, e.g., code which uses device-managed
> resources. This has several disadvantages, including not being designed
> for test use, scattering files in sysfs, and requiring manual teardown
> on test exit, which may not always be possible in case of failure.
>
> Instead, introduce a set of helper functions which allow devices
> (internally a struct kunit_device) to be created and managed by KUnit --
> i.e., they will be automatically unregistered on test exit. These
> helpers can either use a user-provided struct device_driver, or have one
> automatically created and managed by KUnit. In both cases, the device
> lives on a new kunit_bus.
>
> This is a follow-up to a previous proposal here:
> https://lore.kernel.org/linux-kselftest/[email protected]/
>
> (The kunit_defer() function in the first patch there has since been
> merged as the 'deferred actions' feature.)
>
> My intention is to take this whole series in via the kselftest/kunit
> branch, but I'm equally okay with splitting up the later patches which
> use this to go via the various subsystem trees in case there are merge
> conflicts.

Could you take (and apply eventually) that patch as part of your series?
https://lore.kernel.org/linux-kselftest/[email protected]/

Thanks
Maxime


Attachments:
(No filename) (1.56 kB)
signature.asc (235.00 B)
Download all attachments

2023-12-14 08:52:21

by David Gow

[permalink] [raw]
Subject: Re: [PATCH v2 0/4] kunit: Add helpers for creating test-managed devices

On Wed, 13 Dec 2023 at 23:04, Maxime Ripard <[email protected]> wrote:
>
> Hi David,
>
> On Fri, Dec 08, 2023 at 06:09:28PM +0800, [email protected] wrote:
> > KUnit tests often need to provide a struct device, and thus far have
> > mostly been using root_device_register() or platform devices to create
> > a 'fake device' for use with, e.g., code which uses device-managed
> > resources. This has several disadvantages, including not being designed
> > for test use, scattering files in sysfs, and requiring manual teardown
> > on test exit, which may not always be possible in case of failure.
> >
> > Instead, introduce a set of helper functions which allow devices
> > (internally a struct kunit_device) to be created and managed by KUnit --
> > i.e., they will be automatically unregistered on test exit. These
> > helpers can either use a user-provided struct device_driver, or have one
> > automatically created and managed by KUnit. In both cases, the device
> > lives on a new kunit_bus.
> >
> > This is a follow-up to a previous proposal here:
> > https://lore.kernel.org/linux-kselftest/[email protected]/
> >
> > (The kunit_defer() function in the first patch there has since been
> > merged as the 'deferred actions' feature.)
> >
> > My intention is to take this whole series in via the kselftest/kunit
> > branch, but I'm equally okay with splitting up the later patches which
> > use this to go via the various subsystem trees in case there are merge
> > conflicts.
>
> Could you take (and apply eventually) that patch as part of your series?
> https://lore.kernel.org/linux-kselftest/[email protected]/
>

Thanks -- I've included it in v3 (which fixes a few other issues), and
will take it along with the rest of the series:
https://lore.kernel.org/linux-kselftest/[email protected]/T/

Cheers,
-- David


Attachments:
smime.p7s (3.91 kB)
S/MIME Cryptographic Signature