Skip to content

Commit

Permalink
Merge tag 'linux_kselftest-kunit-6.8-rc1' of git://git.kernel.org/pub…
Browse files Browse the repository at this point in the history
…/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
  ...
  • Loading branch information
torvalds committed Jan 10, 2024
2 parents 5d09f61 + 539e582 commit 41daf06
Show file tree
Hide file tree
Showing 30 changed files with 978 additions and 146 deletions.
9 changes: 9 additions & 0 deletions Documentation/dev-tools/kunit/api/resource.rst
Original file line number Diff line number Diff line change
Expand Up @@ -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:
51 changes: 47 additions & 4 deletions Documentation/dev-tools/kunit/run_manual.rst
Original file line number Diff line number Diff line change
Expand Up @@ -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 changes: 7 additions & 0 deletions Documentation/dev-tools/kunit/running_tips.rst
Original file line number Diff line number Diff line change
Expand Up @@ -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.
60 changes: 57 additions & 3 deletions Documentation/dev-tools/kunit/usage.rst
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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.
}
78 changes: 3 additions & 75 deletions drivers/gpu/drm/tests/drm_kunit_helpers.c
Original file line number Diff line number Diff line change
Expand Up @@ -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>
Expand All @@ -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
Expand All @@ -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);

Expand All @@ -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);

Expand Down
9 changes: 3 additions & 6 deletions drivers/gpu/drm/vc4/tests/vc4_mock.c
Original file line number Diff line number Diff line change
Expand Up @@ -153,12 +153,9 @@ static int __build_mock(struct kunit *test, struct drm_device *drm,
return 0;
}

static void kunit_action_drm_dev_unregister(void *ptr)
{
struct drm_device *drm = ptr;

drm_dev_unregister(drm);
}
KUNIT_DEFINE_ACTION_WRAPPER(kunit_action_drm_dev_unregister,
drm_dev_unregister,
struct drm_device *);

static struct vc4_dev *__mock_device(struct kunit *test, bool is_vc5)
{
Expand Down
11 changes: 9 additions & 2 deletions include/asm-generic/vmlinux.lds.h
Original file line number Diff line number Diff line change
Expand Up @@ -370,7 +370,8 @@
BRANCH_PROFILE() \
TRACE_PRINTKS() \
BPF_RAW_TP() \
TRACEPOINT_STR()
TRACEPOINT_STR() \
KUNIT_TABLE()

/*
* Data section helpers
Expand Down Expand Up @@ -700,7 +701,7 @@
EARLYCON_TABLE() \
LSM_TABLE() \
EARLY_LSM_TABLE() \
KUNIT_TABLE()
KUNIT_INIT_TABLE()

#define INIT_TEXT \
*(.init.text .init.text.*) \
Expand Down Expand Up @@ -926,6 +927,12 @@
. = ALIGN(8); \
BOUNDED_SECTION_POST_LABEL(.kunit_test_suites, __kunit_suites, _start, _end)

/* Alignment must be consistent with (kunit_suite *) in include/kunit/test.h */
#define KUNIT_INIT_TABLE() \
. = ALIGN(8); \
BOUNDED_SECTION_POST_LABEL(.kunit_init_test_suites, \
__kunit_init_suites, _start, _end)

#ifdef CONFIG_BLK_DEV_INITRD
#define INIT_RAM_FS \
. = ALIGN(4); \
Expand Down
80 changes: 80 additions & 0 deletions include/kunit/device.h
Original file line number Diff line number Diff line change
@@ -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
Loading

0 comments on commit 41daf06

Please sign in to comment.