forked from torvalds/linux
-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
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]> Reviewed-by: Maxime Ripard <[email protected]> Signed-off-by: David Gow <[email protected]> Reviewed-by: Greg Kroah-Hartman <[email protected]> Signed-off-by: Shuah Khan <[email protected]>
- Loading branch information
Showing
8 changed files
with
475 additions
and
2 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,17 @@ | ||
/* SPDX-License-Identifier: GPL-2.0 */ | ||
/* | ||
* KUnit internal header for device helpers | ||
* | ||
* Header for KUnit-internal driver / bus management. | ||
* | ||
* Copyright (C) 2023, Google LLC. | ||
* Author: David Gow <[email protected]> | ||
*/ | ||
|
||
#ifndef _KUNIT_DEVICE_IMPL_H | ||
#define _KUNIT_DEVICE_IMPL_H | ||
|
||
// For internal use only -- registers the kunit_bus. | ||
int kunit_bus_init(void); | ||
|
||
#endif //_KUNIT_DEVICE_IMPL_H |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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); | ||
|
Oops, something went wrong.