Commit 41daf06e authored by Linus Torvalds's avatar Linus Torvalds
Browse files

Merge tag 'linux_kselftest-kunit-6.8-rc1' of...

Merge tag 'linux_kselftest-kunit-6.8-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/shuah/linux-kselftest

Pull KUnit updates from Shuah Khan:

 - a new feature that adds APIs for managing devices introducing a set
   of helper functions which allow devices (internally a struct
   kunit_device) to be created and managed by KUnit.

   These devices 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.

 - changes to switch drm/tests to use kunit devices

 - several fixes and enhancements to attribute feature

 - changes to reorganize deferred action function introducing
   KUNIT_DEFINE_ACTION_WRAPPER

 - new feature adds ability to run tests after boot using debugfs

 - fixes and enhancements to string-stream-test:
     - parse ERR_PTR in string_stream_destroy()
     - unchecked dereference in bug fix in debugfs_print_results()
     - handling errors from alloc_string_stream()
     - NULL-dereference bug fix in kunit_init_suite()

* tag 'linux_kselftest-kunit-6.8-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/shuah/linux-kselftest: (27 commits)
  kunit: Fix some comments which were mistakenly kerneldoc
  kunit: Protect string comparisons against NULL
  kunit: Add example of kunit_activate_static_stub() with pointer-to-function
  kunit: Allow passing function pointer to kunit_activate_static_stub()
  kunit: Fix NULL-dereference in kunit_init_suite() if suite->log is NULL
  kunit: Reset test->priv after each param iteration
  kunit: Add example for using test->priv
  drm/tests: Switch to kunit devices
  ASoC: topology: Replace fake root_device with kunit_device in tests
  overflow: Replace fake root_device with kunit_device
  fortify: test: Use kunit_device
  kunit: Add APIs for managing devices
  Documentation: Add debugfs docs with run after boot
  kunit: add ability to run tests after boot using debugfs
  kunit: add is_init test attribute
  kunit: add example suite to test init suites
  kunit: add KUNIT_INIT_TABLE to init linker section
  kunit: move KUNIT_TABLE out of INIT_DATA
  kunit: tool: add test for parsing attributes
  kunit: tool: fix parsing of test attributes
  ...
parents 5d09f61e 539e582a
Loading
Loading
Loading
Loading
+9 −0
Original line number Diff line number Diff line
@@ -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:
+47 −4
Original line number Diff line number Diff line
@@ -49,9 +49,52 @@ loaded.

The results will appear in TAP format in ``dmesg``.

debugfs
=======

KUnit can be accessed from userspace via the debugfs filesystem (See more
information about debugfs at Documentation/filesystems/debugfs.rst).

If ``CONFIG_KUNIT_DEBUGFS`` is enabled, the KUnit debugfs filesystem is
mounted at /sys/kernel/debug/kunit. You can use this filesystem to perform
the following actions.

Retrieve Test Results
=====================

You can use debugfs to retrieve KUnit test results. The test results are
accessible from the debugfs filesystem in the following read-only file:

.. code-block :: bash

	/sys/kernel/debug/kunit/<test_suite>/results

The test results are printed in a KTAP document. Note this document is separate
to the kernel log and thus, may have different test suite numbering.

Run Tests After Kernel Has Booted
=================================

You can use the debugfs filesystem to trigger built-in tests to run after
boot. To run the test suite, you can use the following command to write to
the ``/sys/kernel/debug/kunit/<test_suite>/run`` file:

.. code-block :: bash

	echo "any string" > /sys/kernel/debugfs/kunit/<test_suite>/run

As a result, the test suite runs and the results are printed to the kernel
log.

However, this feature is not available with KUnit suites that use init data,
because init data may have been discarded after the kernel boots. KUnit
suites that use init data should be defined using the
kunit_test_init_section_suites() macro.

Also, you cannot use this feature to run tests concurrently. Instead a test
will wait to run until other tests have completed or failed.

.. note ::

	If ``CONFIG_KUNIT_DEBUGFS`` is enabled, KUnit test results will
	be accessible from the ``debugfs`` filesystem (if mounted).
	They will be in ``/sys/kernel/debug/kunit/<test_suite>/results``, in
	TAP format.
	For test authors, to use this feature, tests will need to correctly initialise
	and/or clean up any data, so the test runs correctly a second time.
+7 −0
Original line number Diff line number Diff line
@@ -428,3 +428,10 @@ This attribute indicates the name of the module associated with the test.

This attribute is automatically saved as a string and is printed for each suite.
Tests can also be filtered using this attribute.

``is_init``

This attribute indicates whether the test uses init data or functions.

This attribute is automatically saved as a boolean and tests can also be
filtered using this attribute.
+57 −3
Original line number Diff line number Diff line
@@ -651,12 +651,16 @@ For example:
	}

Note that, for functions like device_unregister which only accept a single
pointer-sized argument, it's possible to directly cast that function to
a ``kunit_action_t`` rather than writing a wrapper function, for example:
pointer-sized argument, it's possible to automatically generate a wrapper
with the ``KUNIT_DEFINE_ACTION_WRAPPER()`` macro, for example:

.. code-block:: C

	kunit_add_action(test, (kunit_action_t *)&device_unregister, &dev);
	KUNIT_DEFINE_ACTION_WRAPPER(device_unregister, device_unregister_wrapper, struct device *);
	kunit_add_action(test, &device_unregister_wrapper, &dev);

You should do this in preference to manually casting to the ``kunit_action_t`` type,
as casting function pointers will break Control Flow Integrity (CFI).

``kunit_add_action`` can fail if, for example, the system is out of memory.
You can use ``kunit_add_action_or_reset`` instead which runs the action
@@ -793,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
+3 −75
Original line number Diff line number Diff line
@@ -5,6 +5,7 @@
#include <drm/drm_kunit_helpers.h>
#include <drm/drm_managed.h>

#include <kunit/device.h>
#include <kunit/resource.h>

#include <linux/device.h>
@@ -15,40 +16,6 @@
static const struct drm_mode_config_funcs drm_mode_config_funcs = {
};

static int fake_probe(struct platform_device *pdev)
{
	return 0;
}

static struct platform_driver fake_platform_driver = {
	.probe	= fake_probe,
	.driver = {
		.name	= KUNIT_DEVICE_NAME,
	},
};

static void kunit_action_platform_driver_unregister(void *ptr)
{
	struct platform_driver *drv = ptr;

	platform_driver_unregister(drv);

}

static void kunit_action_platform_device_put(void *ptr)
{
	struct platform_device *pdev = ptr;

	platform_device_put(pdev);
}

static void kunit_action_platform_device_del(void *ptr)
{
	struct platform_device *pdev = ptr;

	platform_device_del(pdev);
}

/**
 * drm_kunit_helper_alloc_device - Allocate a mock device for a KUnit test
 * @test: The test context object
@@ -66,34 +33,7 @@ static void kunit_action_platform_device_del(void *ptr)
 */
struct device *drm_kunit_helper_alloc_device(struct kunit *test)
{
	struct platform_device *pdev;
	int ret;

	ret = platform_driver_register(&fake_platform_driver);
	KUNIT_ASSERT_EQ(test, ret, 0);

	ret = kunit_add_action_or_reset(test,
					kunit_action_platform_driver_unregister,
					&fake_platform_driver);
	KUNIT_ASSERT_EQ(test, ret, 0);

	pdev = platform_device_alloc(KUNIT_DEVICE_NAME, PLATFORM_DEVID_NONE);
	KUNIT_ASSERT_NOT_ERR_OR_NULL(test, pdev);

	ret = kunit_add_action_or_reset(test,
					kunit_action_platform_device_put,
					pdev);
	KUNIT_ASSERT_EQ(test, ret, 0);

	ret = platform_device_add(pdev);
	KUNIT_ASSERT_EQ(test, ret, 0);

	ret = kunit_add_action_or_reset(test,
					kunit_action_platform_device_del,
					pdev);
	KUNIT_ASSERT_EQ(test, ret, 0);

	return &pdev->dev;
	return kunit_device_register(test, KUNIT_DEVICE_NAME);
}
EXPORT_SYMBOL_GPL(drm_kunit_helper_alloc_device);

@@ -106,19 +46,7 @@ EXPORT_SYMBOL_GPL(drm_kunit_helper_alloc_device);
 */
void drm_kunit_helper_free_device(struct kunit *test, struct device *dev)
{
	struct platform_device *pdev = to_platform_device(dev);

	kunit_release_action(test,
			     kunit_action_platform_device_del,
			     pdev);

	kunit_release_action(test,
			     kunit_action_platform_device_put,
			     pdev);

	kunit_release_action(test,
			     kunit_action_platform_driver_unregister,
			     &fake_platform_driver);
	kunit_device_unregister(test, dev);
}
EXPORT_SYMBOL_GPL(drm_kunit_helper_free_device);

Loading