Resguardo inicial

This commit is contained in:
lastdragon
2026-09-06 12:53:21 -06:00
parent 662efe3c2a
commit ad2088d178
94853 changed files with 43200404 additions and 0 deletions
@@ -0,0 +1,2 @@
CONFIG_KUNIT=y
CONFIG_DM_KUNIT_TEST=y
+20
View File
@@ -0,0 +1,20 @@
# SPDX-License-Identifier: GPL-2.0
config TEST_ASYNC_DRIVER_PROBE
tristate "Build kernel module to test asynchronous driver probing"
depends on m
help
Enabling this option produces a kernel module that allows
testing asynchronous driver probing by the device core.
The module name will be test_async_driver_probe.ko
If unsure say N.
config DM_KUNIT_TEST
tristate "KUnit Tests for the device model" if !KUNIT_ALL_TESTS
depends on KUNIT
default KUNIT_ALL_TESTS
config DRIVER_PE_KUNIT_TEST
tristate "KUnit Tests for property entry API" if !KUNIT_ALL_TESTS
depends on KUNIT
default KUNIT_ALL_TESTS
+8
View File
@@ -0,0 +1,8 @@
# SPDX-License-Identifier: GPL-2.0
obj-$(CONFIG_TEST_ASYNC_DRIVER_PROBE) += test_async_driver_probe.o
obj-$(CONFIG_DM_KUNIT_TEST) += root-device-test.o
obj-$(CONFIG_DM_KUNIT_TEST) += platform-device-test.o
obj-$(CONFIG_DRIVER_PE_KUNIT_TEST) += property-entry-test.o
CFLAGS_property-entry-test.o += $(DISABLE_STRUCTLEAK_PLUGIN)
@@ -0,0 +1,263 @@
// SPDX-License-Identifier: GPL-2.0
#include <kunit/platform_device.h>
#include <kunit/resource.h>
#include <linux/device.h>
#include <linux/device/bus.h>
#include <linux/of_platform.h>
#include <linux/platform_device.h>
#define DEVICE_NAME "test"
struct test_priv {
bool probe_done;
bool release_done;
wait_queue_head_t probe_wq;
wait_queue_head_t release_wq;
struct device *dev;
};
static int platform_device_devm_init(struct kunit *test)
{
struct test_priv *priv;
priv = kunit_kzalloc(test, sizeof(*priv), GFP_KERNEL);
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, priv);
init_waitqueue_head(&priv->probe_wq);
init_waitqueue_head(&priv->release_wq);
test->priv = priv;
return 0;
}
static void devm_device_action(void *ptr)
{
struct test_priv *priv = ptr;
priv->release_done = true;
wake_up_interruptible(&priv->release_wq);
}
static void devm_put_device_action(void *ptr)
{
struct test_priv *priv = ptr;
put_device(priv->dev);
priv->release_done = true;
wake_up_interruptible(&priv->release_wq);
}
#define RELEASE_TIMEOUT_MS 100
/*
* Tests that a platform bus, non-probed device will run its
* device-managed actions when unregistered.
*/
static void platform_device_devm_register_unregister_test(struct kunit *test)
{
struct platform_device *pdev;
struct test_priv *priv = test->priv;
int ret;
pdev = platform_device_alloc(DEVICE_NAME, PLATFORM_DEVID_NONE);
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, pdev);
ret = platform_device_add(pdev);
KUNIT_ASSERT_EQ(test, ret, 0);
priv->dev = &pdev->dev;
ret = devm_add_action_or_reset(priv->dev, devm_device_action, priv);
KUNIT_ASSERT_EQ(test, ret, 0);
platform_device_unregister(pdev);
ret = wait_event_interruptible_timeout(priv->release_wq, priv->release_done,
msecs_to_jiffies(RELEASE_TIMEOUT_MS));
KUNIT_EXPECT_GT(test, ret, 0);
}
/*
* Tests that a platform bus, non-probed device will run its
* device-managed actions when unregistered, even if someone still holds
* a reference to it.
*/
static void platform_device_devm_register_get_unregister_with_devm_test(struct kunit *test)
{
struct platform_device *pdev;
struct test_priv *priv = test->priv;
int ret;
pdev = platform_device_alloc(DEVICE_NAME, PLATFORM_DEVID_NONE);
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, pdev);
ret = platform_device_add(pdev);
KUNIT_ASSERT_EQ(test, ret, 0);
priv->dev = &pdev->dev;
get_device(priv->dev);
ret = devm_add_action_or_reset(priv->dev, devm_put_device_action, priv);
KUNIT_ASSERT_EQ(test, ret, 0);
platform_device_unregister(pdev);
ret = wait_event_interruptible_timeout(priv->release_wq, priv->release_done,
msecs_to_jiffies(RELEASE_TIMEOUT_MS));
KUNIT_EXPECT_GT(test, ret, 0);
}
static int fake_probe(struct platform_device *pdev)
{
struct test_priv *priv = platform_get_drvdata(pdev);
priv->probe_done = true;
wake_up_interruptible(&priv->probe_wq);
return 0;
}
static struct platform_driver fake_driver = {
.probe = fake_probe,
.driver = {
.name = DEVICE_NAME,
},
};
/*
* Tests that a platform bus, probed device will run its device-managed
* actions when unregistered.
*/
static void probed_platform_device_devm_register_unregister_test(struct kunit *test)
{
struct platform_device *pdev;
struct test_priv *priv = test->priv;
int ret;
ret = platform_driver_register(&fake_driver);
KUNIT_ASSERT_EQ(test, ret, 0);
pdev = platform_device_alloc(DEVICE_NAME, PLATFORM_DEVID_NONE);
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, pdev);
priv->dev = &pdev->dev;
platform_set_drvdata(pdev, priv);
ret = platform_device_add(pdev);
KUNIT_ASSERT_EQ(test, ret, 0);
ret = wait_event_interruptible_timeout(priv->probe_wq, priv->probe_done,
msecs_to_jiffies(RELEASE_TIMEOUT_MS));
KUNIT_ASSERT_GT(test, ret, 0);
ret = devm_add_action_or_reset(priv->dev, devm_device_action, priv);
KUNIT_ASSERT_EQ(test, ret, 0);
platform_device_unregister(pdev);
ret = wait_event_interruptible_timeout(priv->release_wq, priv->release_done,
msecs_to_jiffies(RELEASE_TIMEOUT_MS));
KUNIT_EXPECT_GT(test, ret, 0);
platform_driver_unregister(&fake_driver);
}
/*
* Tests that a platform bus, probed device will run its device-managed
* actions when unregistered, even if someone still holds a reference to
* it.
*/
static void probed_platform_device_devm_register_get_unregister_with_devm_test(struct kunit *test)
{
struct platform_device *pdev;
struct test_priv *priv = test->priv;
int ret;
ret = platform_driver_register(&fake_driver);
KUNIT_ASSERT_EQ(test, ret, 0);
pdev = platform_device_alloc(DEVICE_NAME, PLATFORM_DEVID_NONE);
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, pdev);
priv->dev = &pdev->dev;
platform_set_drvdata(pdev, priv);
ret = platform_device_add(pdev);
KUNIT_ASSERT_EQ(test, ret, 0);
ret = wait_event_interruptible_timeout(priv->probe_wq, priv->probe_done,
msecs_to_jiffies(RELEASE_TIMEOUT_MS));
KUNIT_ASSERT_GT(test, ret, 0);
get_device(priv->dev);
ret = devm_add_action_or_reset(priv->dev, devm_put_device_action, priv);
KUNIT_ASSERT_EQ(test, ret, 0);
platform_device_unregister(pdev);
ret = wait_event_interruptible_timeout(priv->release_wq, priv->release_done,
msecs_to_jiffies(RELEASE_TIMEOUT_MS));
KUNIT_EXPECT_GT(test, ret, 0);
platform_driver_unregister(&fake_driver);
}
static struct kunit_case platform_device_devm_tests[] = {
KUNIT_CASE(platform_device_devm_register_unregister_test),
KUNIT_CASE(platform_device_devm_register_get_unregister_with_devm_test),
KUNIT_CASE(probed_platform_device_devm_register_unregister_test),
KUNIT_CASE(probed_platform_device_devm_register_get_unregister_with_devm_test),
{}
};
static struct kunit_suite platform_device_devm_test_suite = {
.name = "platform-device-devm",
.init = platform_device_devm_init,
.test_cases = platform_device_devm_tests,
};
static void platform_device_find_by_null_test(struct kunit *test)
{
struct platform_device *pdev;
int ret;
pdev = kunit_platform_device_alloc(test, DEVICE_NAME, PLATFORM_DEVID_NONE);
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, pdev);
ret = kunit_platform_device_add(test, pdev);
KUNIT_ASSERT_EQ(test, ret, 0);
KUNIT_EXPECT_PTR_EQ(test, of_find_device_by_node(NULL), NULL);
KUNIT_EXPECT_PTR_EQ(test, bus_find_device_by_of_node(&platform_bus_type, NULL), NULL);
KUNIT_EXPECT_PTR_EQ(test, bus_find_device_by_fwnode(&platform_bus_type, NULL), NULL);
KUNIT_EXPECT_PTR_EQ(test, bus_find_device_by_acpi_dev(&platform_bus_type, NULL), NULL);
KUNIT_EXPECT_FALSE(test, device_match_of_node(&pdev->dev, NULL));
KUNIT_EXPECT_FALSE(test, device_match_fwnode(&pdev->dev, NULL));
KUNIT_EXPECT_FALSE(test, device_match_acpi_dev(&pdev->dev, NULL));
KUNIT_EXPECT_FALSE(test, device_match_acpi_handle(&pdev->dev, NULL));
}
static struct kunit_case platform_device_match_tests[] = {
KUNIT_CASE(platform_device_find_by_null_test),
{}
};
static struct kunit_suite platform_device_match_test_suite = {
.name = "platform-device-match",
.test_cases = platform_device_match_tests,
};
kunit_test_suites(
&platform_device_devm_test_suite,
&platform_device_match_test_suite,
);
MODULE_DESCRIPTION("Test module for platform devices");
MODULE_AUTHOR("Maxime Ripard <mripard@kernel.org>");
MODULE_LICENSE("GPL");
@@ -0,0 +1,512 @@
// SPDX-License-Identifier: GPL-2.0
// Unit tests for property entries API
//
// Copyright 2019 Google LLC.
#include <kunit/test.h>
#include <linux/property.h>
#include <linux/types.h>
static void pe_test_uints(struct kunit *test)
{
static const struct property_entry entries[] = {
PROPERTY_ENTRY_U8("prop-u8", 8),
PROPERTY_ENTRY_U16("prop-u16", 16),
PROPERTY_ENTRY_U32("prop-u32", 32),
PROPERTY_ENTRY_U64("prop-u64", 64),
{ }
};
struct fwnode_handle *node;
u8 val_u8, array_u8[2];
u16 val_u16, array_u16[2];
u32 val_u32, array_u32[2];
u64 val_u64, array_u64[2];
int error;
node = fwnode_create_software_node(entries, NULL);
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, node);
error = fwnode_property_count_u8(node, "prop-u8");
KUNIT_EXPECT_EQ(test, error, 1);
error = fwnode_property_read_u8(node, "prop-u8", &val_u8);
KUNIT_EXPECT_EQ(test, error, 0);
KUNIT_EXPECT_EQ(test, val_u8, 8);
error = fwnode_property_read_u8_array(node, "prop-u8", array_u8, 1);
KUNIT_EXPECT_EQ(test, error, 0);
KUNIT_EXPECT_EQ(test, array_u8[0], 8);
error = fwnode_property_read_u8_array(node, "prop-u8", array_u8, 2);
KUNIT_EXPECT_NE(test, error, 0);
error = fwnode_property_read_u8(node, "no-prop-u8", &val_u8);
KUNIT_EXPECT_NE(test, error, 0);
error = fwnode_property_read_u8_array(node, "no-prop-u8", array_u8, 1);
KUNIT_EXPECT_NE(test, error, 0);
error = fwnode_property_read_u16(node, "prop-u16", &val_u16);
KUNIT_EXPECT_EQ(test, error, 0);
KUNIT_EXPECT_EQ(test, val_u16, 16);
error = fwnode_property_count_u16(node, "prop-u16");
KUNIT_EXPECT_EQ(test, error, 1);
error = fwnode_property_read_u16_array(node, "prop-u16", array_u16, 1);
KUNIT_EXPECT_EQ(test, error, 0);
KUNIT_EXPECT_EQ(test, array_u16[0], 16);
error = fwnode_property_read_u16_array(node, "prop-u16", array_u16, 2);
KUNIT_EXPECT_NE(test, error, 0);
error = fwnode_property_read_u16(node, "no-prop-u16", &val_u16);
KUNIT_EXPECT_NE(test, error, 0);
error = fwnode_property_read_u16_array(node, "no-prop-u16", array_u16, 1);
KUNIT_EXPECT_NE(test, error, 0);
error = fwnode_property_read_u32(node, "prop-u32", &val_u32);
KUNIT_EXPECT_EQ(test, error, 0);
KUNIT_EXPECT_EQ(test, val_u32, 32);
error = fwnode_property_count_u32(node, "prop-u32");
KUNIT_EXPECT_EQ(test, error, 1);
error = fwnode_property_read_u32_array(node, "prop-u32", array_u32, 1);
KUNIT_EXPECT_EQ(test, error, 0);
KUNIT_EXPECT_EQ(test, array_u32[0], 32);
error = fwnode_property_read_u32_array(node, "prop-u32", array_u32, 2);
KUNIT_EXPECT_NE(test, error, 0);
error = fwnode_property_read_u32(node, "no-prop-u32", &val_u32);
KUNIT_EXPECT_NE(test, error, 0);
error = fwnode_property_read_u32_array(node, "no-prop-u32", array_u32, 1);
KUNIT_EXPECT_NE(test, error, 0);
error = fwnode_property_read_u64(node, "prop-u64", &val_u64);
KUNIT_EXPECT_EQ(test, error, 0);
KUNIT_EXPECT_EQ(test, val_u64, 64);
error = fwnode_property_count_u64(node, "prop-u64");
KUNIT_EXPECT_EQ(test, error, 1);
error = fwnode_property_read_u64_array(node, "prop-u64", array_u64, 1);
KUNIT_EXPECT_EQ(test, error, 0);
KUNIT_EXPECT_EQ(test, array_u64[0], 64);
error = fwnode_property_read_u64_array(node, "prop-u64", array_u64, 2);
KUNIT_EXPECT_NE(test, error, 0);
error = fwnode_property_read_u64(node, "no-prop-u64", &val_u64);
KUNIT_EXPECT_NE(test, error, 0);
error = fwnode_property_read_u64_array(node, "no-prop-u64", array_u64, 1);
KUNIT_EXPECT_NE(test, error, 0);
/* Count 64-bit values as 16-bit */
error = fwnode_property_count_u16(node, "prop-u64");
KUNIT_EXPECT_EQ(test, error, 4);
fwnode_remove_software_node(node);
}
static void pe_test_uint_arrays(struct kunit *test)
{
static const u8 a_u8[10] = { 8, 9 };
static const u16 a_u16[10] = { 16, 17 };
static const u32 a_u32[10] = { 32, 33 };
static const u64 a_u64[10] = { 64, 65 };
static const struct property_entry entries[] = {
PROPERTY_ENTRY_U8_ARRAY("prop-u8", a_u8),
PROPERTY_ENTRY_U16_ARRAY("prop-u16", a_u16),
PROPERTY_ENTRY_U32_ARRAY("prop-u32", a_u32),
PROPERTY_ENTRY_U64_ARRAY("prop-u64", a_u64),
{ }
};
struct fwnode_handle *node;
u8 val_u8, array_u8[32];
u16 val_u16, array_u16[32];
u32 val_u32, array_u32[32];
u64 val_u64, array_u64[32];
int error;
node = fwnode_create_software_node(entries, NULL);
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, node);
error = fwnode_property_read_u8(node, "prop-u8", &val_u8);
KUNIT_EXPECT_EQ(test, error, 0);
KUNIT_EXPECT_EQ(test, val_u8, 8);
error = fwnode_property_count_u8(node, "prop-u8");
KUNIT_EXPECT_EQ(test, error, 10);
error = fwnode_property_read_u8_array(node, "prop-u8", array_u8, 1);
KUNIT_EXPECT_EQ(test, error, 0);
KUNIT_EXPECT_EQ(test, array_u8[0], 8);
error = fwnode_property_read_u8_array(node, "prop-u8", array_u8, 2);
KUNIT_EXPECT_EQ(test, error, 0);
KUNIT_EXPECT_EQ(test, array_u8[0], 8);
KUNIT_EXPECT_EQ(test, array_u8[1], 9);
error = fwnode_property_read_u8_array(node, "prop-u8", array_u8, 17);
KUNIT_EXPECT_NE(test, error, 0);
error = fwnode_property_read_u8(node, "no-prop-u8", &val_u8);
KUNIT_EXPECT_NE(test, error, 0);
error = fwnode_property_read_u8_array(node, "no-prop-u8", array_u8, 1);
KUNIT_EXPECT_NE(test, error, 0);
error = fwnode_property_read_u16(node, "prop-u16", &val_u16);
KUNIT_EXPECT_EQ(test, error, 0);
KUNIT_EXPECT_EQ(test, val_u16, 16);
error = fwnode_property_count_u16(node, "prop-u16");
KUNIT_EXPECT_EQ(test, error, 10);
error = fwnode_property_read_u16_array(node, "prop-u16", array_u16, 1);
KUNIT_EXPECT_EQ(test, error, 0);
KUNIT_EXPECT_EQ(test, array_u16[0], 16);
error = fwnode_property_read_u16_array(node, "prop-u16", array_u16, 2);
KUNIT_EXPECT_EQ(test, error, 0);
KUNIT_EXPECT_EQ(test, array_u16[0], 16);
KUNIT_EXPECT_EQ(test, array_u16[1], 17);
error = fwnode_property_read_u16_array(node, "prop-u16", array_u16, 17);
KUNIT_EXPECT_NE(test, error, 0);
error = fwnode_property_read_u16(node, "no-prop-u16", &val_u16);
KUNIT_EXPECT_NE(test, error, 0);
error = fwnode_property_read_u16_array(node, "no-prop-u16", array_u16, 1);
KUNIT_EXPECT_NE(test, error, 0);
error = fwnode_property_read_u32(node, "prop-u32", &val_u32);
KUNIT_EXPECT_EQ(test, error, 0);
KUNIT_EXPECT_EQ(test, val_u32, 32);
error = fwnode_property_count_u32(node, "prop-u32");
KUNIT_EXPECT_EQ(test, error, 10);
error = fwnode_property_read_u32_array(node, "prop-u32", array_u32, 1);
KUNIT_EXPECT_EQ(test, error, 0);
KUNIT_EXPECT_EQ(test, array_u32[0], 32);
error = fwnode_property_read_u32_array(node, "prop-u32", array_u32, 2);
KUNIT_EXPECT_EQ(test, error, 0);
KUNIT_EXPECT_EQ(test, array_u32[0], 32);
KUNIT_EXPECT_EQ(test, array_u32[1], 33);
error = fwnode_property_read_u32_array(node, "prop-u32", array_u32, 17);
KUNIT_EXPECT_NE(test, error, 0);
error = fwnode_property_read_u32(node, "no-prop-u32", &val_u32);
KUNIT_EXPECT_NE(test, error, 0);
error = fwnode_property_read_u32_array(node, "no-prop-u32", array_u32, 1);
KUNIT_EXPECT_NE(test, error, 0);
error = fwnode_property_read_u64(node, "prop-u64", &val_u64);
KUNIT_EXPECT_EQ(test, error, 0);
KUNIT_EXPECT_EQ(test, val_u64, 64);
error = fwnode_property_count_u64(node, "prop-u64");
KUNIT_EXPECT_EQ(test, error, 10);
error = fwnode_property_read_u64_array(node, "prop-u64", array_u64, 1);
KUNIT_EXPECT_EQ(test, error, 0);
KUNIT_EXPECT_EQ(test, array_u64[0], 64);
error = fwnode_property_read_u64_array(node, "prop-u64", array_u64, 2);
KUNIT_EXPECT_EQ(test, error, 0);
KUNIT_EXPECT_EQ(test, array_u64[0], 64);
KUNIT_EXPECT_EQ(test, array_u64[1], 65);
error = fwnode_property_read_u64_array(node, "prop-u64", array_u64, 17);
KUNIT_EXPECT_NE(test, error, 0);
error = fwnode_property_read_u64(node, "no-prop-u64", &val_u64);
KUNIT_EXPECT_NE(test, error, 0);
error = fwnode_property_read_u64_array(node, "no-prop-u64", array_u64, 1);
KUNIT_EXPECT_NE(test, error, 0);
/* Count 64-bit values as 16-bit */
error = fwnode_property_count_u16(node, "prop-u64");
KUNIT_EXPECT_EQ(test, error, 40);
/* Other way around */
error = fwnode_property_count_u64(node, "prop-u16");
KUNIT_EXPECT_EQ(test, error, 2);
fwnode_remove_software_node(node);
}
static void pe_test_strings(struct kunit *test)
{
static const char *strings[] = {
"string-a",
"string-b",
};
static const struct property_entry entries[] = {
PROPERTY_ENTRY_STRING("str", "single"),
PROPERTY_ENTRY_STRING("empty", ""),
PROPERTY_ENTRY_STRING_ARRAY("strs", strings),
{ }
};
struct fwnode_handle *node;
const char *str;
const char *strs[10];
int error;
node = fwnode_create_software_node(entries, NULL);
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, node);
error = fwnode_property_read_string(node, "str", &str);
KUNIT_EXPECT_EQ(test, error, 0);
KUNIT_EXPECT_STREQ(test, str, "single");
error = fwnode_property_string_array_count(node, "str");
KUNIT_EXPECT_EQ(test, error, 1);
error = fwnode_property_read_string_array(node, "str", strs, 1);
KUNIT_EXPECT_EQ(test, error, 1);
KUNIT_EXPECT_STREQ(test, strs[0], "single");
/* asking for more data returns what we have */
error = fwnode_property_read_string_array(node, "str", strs, 2);
KUNIT_EXPECT_EQ(test, error, 1);
KUNIT_EXPECT_STREQ(test, strs[0], "single");
error = fwnode_property_read_string(node, "no-str", &str);
KUNIT_EXPECT_NE(test, error, 0);
error = fwnode_property_read_string_array(node, "no-str", strs, 1);
KUNIT_EXPECT_LT(test, error, 0);
error = fwnode_property_read_string(node, "empty", &str);
KUNIT_EXPECT_EQ(test, error, 0);
KUNIT_EXPECT_STREQ(test, str, "");
error = fwnode_property_string_array_count(node, "strs");
KUNIT_EXPECT_EQ(test, error, 2);
error = fwnode_property_read_string_array(node, "strs", strs, 3);
KUNIT_EXPECT_EQ(test, error, 2);
KUNIT_EXPECT_STREQ(test, strs[0], "string-a");
KUNIT_EXPECT_STREQ(test, strs[1], "string-b");
error = fwnode_property_read_string_array(node, "strs", strs, 1);
KUNIT_EXPECT_EQ(test, error, 1);
KUNIT_EXPECT_STREQ(test, strs[0], "string-a");
/* NULL argument -> returns size */
error = fwnode_property_read_string_array(node, "strs", NULL, 0);
KUNIT_EXPECT_EQ(test, error, 2);
/* accessing array as single value */
error = fwnode_property_read_string(node, "strs", &str);
KUNIT_EXPECT_EQ(test, error, 0);
KUNIT_EXPECT_STREQ(test, str, "string-a");
fwnode_remove_software_node(node);
}
static void pe_test_bool(struct kunit *test)
{
static const struct property_entry entries[] = {
PROPERTY_ENTRY_BOOL("prop"),
{ }
};
struct fwnode_handle *node;
node = fwnode_create_software_node(entries, NULL);
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, node);
KUNIT_EXPECT_TRUE(test, fwnode_property_read_bool(node, "prop"));
KUNIT_EXPECT_FALSE(test, fwnode_property_read_bool(node, "not-prop"));
fwnode_remove_software_node(node);
}
/* Verifies that small U8 array is stored inline when property is copied */
static void pe_test_move_inline_u8(struct kunit *test)
{
static const u8 u8_array_small[8] = { 1, 2, 3, 4 };
static const u8 u8_array_big[128] = { 5, 6, 7, 8 };
static const struct property_entry entries[] = {
PROPERTY_ENTRY_U8_ARRAY("small", u8_array_small),
PROPERTY_ENTRY_U8_ARRAY("big", u8_array_big),
{ }
};
struct property_entry *copy;
const u8 *data_ptr;
copy = property_entries_dup(entries);
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, copy);
KUNIT_EXPECT_TRUE(test, copy[0].is_inline);
data_ptr = (u8 *)&copy[0].value;
KUNIT_EXPECT_EQ(test, data_ptr[0], 1);
KUNIT_EXPECT_EQ(test, data_ptr[1], 2);
KUNIT_EXPECT_FALSE(test, copy[1].is_inline);
data_ptr = copy[1].pointer;
KUNIT_EXPECT_EQ(test, data_ptr[0], 5);
KUNIT_EXPECT_EQ(test, data_ptr[1], 6);
property_entries_free(copy);
}
/* Verifies that single string array is stored inline when property is copied */
static void pe_test_move_inline_str(struct kunit *test)
{
static char *str_array_small[] = { "a" };
static char *str_array_big[] = { "b", "c", "d", "e" };
static char *str_array_small_empty[] = { "" };
static struct property_entry entries[] = {
PROPERTY_ENTRY_STRING_ARRAY("small", str_array_small),
PROPERTY_ENTRY_STRING_ARRAY("big", str_array_big),
PROPERTY_ENTRY_STRING_ARRAY("small-empty", str_array_small_empty),
{ }
};
struct property_entry *copy;
const char * const *data_ptr;
copy = property_entries_dup(entries);
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, copy);
KUNIT_EXPECT_TRUE(test, copy[0].is_inline);
KUNIT_EXPECT_STREQ(test, copy[0].value.str[0], "a");
KUNIT_EXPECT_FALSE(test, copy[1].is_inline);
data_ptr = copy[1].pointer;
KUNIT_EXPECT_STREQ(test, data_ptr[0], "b");
KUNIT_EXPECT_STREQ(test, data_ptr[1], "c");
KUNIT_EXPECT_TRUE(test, copy[2].is_inline);
KUNIT_EXPECT_STREQ(test, copy[2].value.str[0], "");
property_entries_free(copy);
}
/* Handling of reference properties */
static void pe_test_reference(struct kunit *test)
{
static const struct software_node node1 = { .name = "1" };
static const struct software_node node2 = { .name = "2" };
static const struct software_node *group[] = { &node1, &node2, NULL };
static const struct software_node_ref_args refs[] = {
SOFTWARE_NODE_REFERENCE(&node1),
SOFTWARE_NODE_REFERENCE(&node2, 3, 4),
};
const struct property_entry entries[] = {
PROPERTY_ENTRY_REF("ref-1", &node1),
PROPERTY_ENTRY_REF("ref-2", &node2, 1, 2),
PROPERTY_ENTRY_REF_ARRAY("ref-3", refs),
{ }
};
struct fwnode_handle *node;
struct fwnode_reference_args ref;
int error;
error = software_node_register_node_group(group);
KUNIT_ASSERT_EQ(test, error, 0);
node = fwnode_create_software_node(entries, NULL);
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, node);
error = fwnode_property_get_reference_args(node, "ref-1", NULL,
0, 0, &ref);
KUNIT_ASSERT_EQ(test, error, 0);
KUNIT_EXPECT_PTR_EQ(test, to_software_node(ref.fwnode), &node1);
KUNIT_EXPECT_EQ(test, ref.nargs, 0U);
/* wrong index */
error = fwnode_property_get_reference_args(node, "ref-1", NULL,
0, 1, &ref);
KUNIT_EXPECT_NE(test, error, 0);
error = fwnode_property_get_reference_args(node, "ref-2", NULL,
1, 0, &ref);
KUNIT_ASSERT_EQ(test, error, 0);
KUNIT_EXPECT_PTR_EQ(test, to_software_node(ref.fwnode), &node2);
KUNIT_EXPECT_EQ(test, ref.nargs, 1U);
KUNIT_EXPECT_EQ(test, ref.args[0], 1LLU);
/* asking for more args, padded with zero data */
error = fwnode_property_get_reference_args(node, "ref-2", NULL,
3, 0, &ref);
KUNIT_ASSERT_EQ(test, error, 0);
KUNIT_EXPECT_PTR_EQ(test, to_software_node(ref.fwnode), &node2);
KUNIT_EXPECT_EQ(test, ref.nargs, 3U);
KUNIT_EXPECT_EQ(test, ref.args[0], 1LLU);
KUNIT_EXPECT_EQ(test, ref.args[1], 2LLU);
KUNIT_EXPECT_EQ(test, ref.args[2], 0LLU);
/* wrong index */
error = fwnode_property_get_reference_args(node, "ref-2", NULL,
2, 1, &ref);
KUNIT_EXPECT_NE(test, error, 0);
/* array of references */
error = fwnode_property_get_reference_args(node, "ref-3", NULL,
0, 0, &ref);
KUNIT_ASSERT_EQ(test, error, 0);
KUNIT_EXPECT_PTR_EQ(test, to_software_node(ref.fwnode), &node1);
KUNIT_EXPECT_EQ(test, ref.nargs, 0U);
/* second reference in the array */
error = fwnode_property_get_reference_args(node, "ref-3", NULL,
2, 1, &ref);
KUNIT_ASSERT_EQ(test, error, 0);
KUNIT_EXPECT_PTR_EQ(test, to_software_node(ref.fwnode), &node2);
KUNIT_EXPECT_EQ(test, ref.nargs, 2U);
KUNIT_EXPECT_EQ(test, ref.args[0], 3LLU);
KUNIT_EXPECT_EQ(test, ref.args[1], 4LLU);
/* wrong index */
error = fwnode_property_get_reference_args(node, "ref-1", NULL,
0, 2, &ref);
KUNIT_EXPECT_NE(test, error, 0);
fwnode_remove_software_node(node);
software_node_unregister_node_group(group);
}
static struct kunit_case property_entry_test_cases[] = {
KUNIT_CASE(pe_test_uints),
KUNIT_CASE(pe_test_uint_arrays),
KUNIT_CASE(pe_test_strings),
KUNIT_CASE(pe_test_bool),
KUNIT_CASE(pe_test_move_inline_u8),
KUNIT_CASE(pe_test_move_inline_str),
KUNIT_CASE(pe_test_reference),
{ }
};
static struct kunit_suite property_entry_test_suite = {
.name = "property-entry",
.test_cases = property_entry_test_cases,
};
kunit_test_suite(property_entry_test_suite);
MODULE_DESCRIPTION("Test module for the property entry API");
MODULE_AUTHOR("Dmitry Torokhov <dtor@chromium.org>");
MODULE_LICENSE("GPL");
@@ -0,0 +1,112 @@
// SPDX-License-Identifier: GPL-2.0
// Copyright 2023 Maxime Ripard <mripard@kernel.org>
#include <kunit/resource.h>
#include <linux/device.h>
#define DEVICE_NAME "test"
struct test_priv {
bool probe_done;
bool release_done;
wait_queue_head_t release_wq;
struct device *dev;
};
static int root_device_devm_init(struct kunit *test)
{
struct test_priv *priv;
priv = kunit_kzalloc(test, sizeof(*priv), GFP_KERNEL);
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, priv);
init_waitqueue_head(&priv->release_wq);
test->priv = priv;
return 0;
}
static void devm_device_action(void *ptr)
{
struct test_priv *priv = ptr;
priv->release_done = true;
wake_up_interruptible(&priv->release_wq);
}
#define RELEASE_TIMEOUT_MS 100
/*
* Tests that a bus-less, non-probed device will run its device-managed
* actions when unregistered.
*/
static void root_device_devm_register_unregister_test(struct kunit *test)
{
struct test_priv *priv = test->priv;
int ret;
priv->dev = root_device_register(DEVICE_NAME);
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, priv->dev);
ret = devm_add_action_or_reset(priv->dev, devm_device_action, priv);
KUNIT_ASSERT_EQ(test, ret, 0);
root_device_unregister(priv->dev);
ret = wait_event_interruptible_timeout(priv->release_wq, priv->release_done,
msecs_to_jiffies(RELEASE_TIMEOUT_MS));
KUNIT_EXPECT_GT(test, ret, 0);
}
static void devm_put_device_action(void *ptr)
{
struct test_priv *priv = ptr;
put_device(priv->dev);
priv->release_done = true;
wake_up_interruptible(&priv->release_wq);
}
/*
* Tests that a bus-less, non-probed device will run its device-managed
* actions when unregistered, even if someone still holds a reference to
* it.
*/
static void root_device_devm_register_get_unregister_with_devm_test(struct kunit *test)
{
struct test_priv *priv = test->priv;
int ret;
priv->dev = root_device_register(DEVICE_NAME);
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, priv->dev);
get_device(priv->dev);
ret = devm_add_action_or_reset(priv->dev, devm_put_device_action, priv);
KUNIT_ASSERT_EQ(test, ret, 0);
root_device_unregister(priv->dev);
ret = wait_event_interruptible_timeout(priv->release_wq, priv->release_done,
msecs_to_jiffies(RELEASE_TIMEOUT_MS));
KUNIT_EXPECT_GT(test, ret, 0);
}
static struct kunit_case root_device_devm_tests[] = {
KUNIT_CASE(root_device_devm_register_unregister_test),
KUNIT_CASE(root_device_devm_register_get_unregister_with_devm_test),
{}
};
static struct kunit_suite root_device_devm_test_suite = {
.name = "root-device-devm",
.init = root_device_devm_init,
.test_cases = root_device_devm_tests,
};
kunit_test_suite(root_device_devm_test_suite);
MODULE_DESCRIPTION("Test module for root devices");
MODULE_AUTHOR("Maxime Ripard <mripard@kernel.org>");
MODULE_LICENSE("GPL");
@@ -0,0 +1,299 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (C) 2014 Google, Inc.
*/
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
#include <linux/delay.h>
#include <linux/init.h>
#include <linux/hrtimer.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/time.h>
#include <linux/numa.h>
#include <linux/nodemask.h>
#include <linux/topology.h>
#define TEST_PROBE_DELAY (5 * 1000) /* 5 sec */
#define TEST_PROBE_THRESHOLD (TEST_PROBE_DELAY / 2)
static atomic_t warnings, errors, timeout, async_completed;
static int test_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
/*
* Determine if we have hit the "timeout" limit for the test if we
* have then report it as an error, otherwise we wil sleep for the
* required amount of time and then report completion.
*/
if (atomic_read(&timeout)) {
dev_err(dev, "async probe took too long\n");
atomic_inc(&errors);
} else {
dev_dbg(&pdev->dev, "sleeping for %d msecs in probe\n",
TEST_PROBE_DELAY);
msleep(TEST_PROBE_DELAY);
dev_dbg(&pdev->dev, "done sleeping\n");
}
/*
* Report NUMA mismatch if device node is set and we are not
* performing an async init on that node.
*/
if (dev->driver->probe_type == PROBE_PREFER_ASYNCHRONOUS) {
if (IS_ENABLED(CONFIG_NUMA) &&
dev_to_node(dev) != numa_node_id()) {
dev_warn(dev, "NUMA node mismatch %d != %d\n",
dev_to_node(dev), numa_node_id());
atomic_inc(&warnings);
}
atomic_inc(&async_completed);
}
return 0;
}
static struct platform_driver async_driver = {
.driver = {
.name = "test_async_driver",
.probe_type = PROBE_PREFER_ASYNCHRONOUS,
},
.probe = test_probe,
};
static struct platform_driver sync_driver = {
.driver = {
.name = "test_sync_driver",
.probe_type = PROBE_FORCE_SYNCHRONOUS,
},
.probe = test_probe,
};
static struct platform_device *async_dev[NR_CPUS * 2];
static struct platform_device *sync_dev[2];
static struct platform_device *
test_platform_device_register_node(char *name, int id, int nid)
{
struct platform_device *pdev;
int ret;
pdev = platform_device_alloc(name, id);
if (!pdev)
return ERR_PTR(-ENOMEM);
if (nid != NUMA_NO_NODE)
set_dev_node(&pdev->dev, nid);
ret = platform_device_add(pdev);
if (ret) {
platform_device_put(pdev);
return ERR_PTR(ret);
}
return pdev;
}
static int __init test_async_probe_init(void)
{
struct platform_device **pdev = NULL;
int async_id = 0, sync_id = 0;
unsigned long long duration;
ktime_t calltime;
int err, nid, cpu;
pr_info("registering first set of asynchronous devices...\n");
for_each_online_cpu(cpu) {
nid = cpu_to_node(cpu);
pdev = &async_dev[async_id];
*pdev = test_platform_device_register_node("test_async_driver",
async_id,
nid);
if (IS_ERR(*pdev)) {
err = PTR_ERR(*pdev);
*pdev = NULL;
pr_err("failed to create async_dev: %d\n", err);
goto err_unregister_async_devs;
}
async_id++;
}
pr_info("registering asynchronous driver...\n");
calltime = ktime_get();
err = platform_driver_register(&async_driver);
if (err) {
pr_err("Failed to register async_driver: %d\n", err);
goto err_unregister_async_devs;
}
duration = (unsigned long long)ktime_ms_delta(ktime_get(), calltime);
pr_info("registration took %lld msecs\n", duration);
if (duration > TEST_PROBE_THRESHOLD) {
pr_err("test failed: probe took too long\n");
err = -ETIMEDOUT;
goto err_unregister_async_driver;
}
pr_info("registering second set of asynchronous devices...\n");
calltime = ktime_get();
for_each_online_cpu(cpu) {
nid = cpu_to_node(cpu);
pdev = &async_dev[async_id];
*pdev = test_platform_device_register_node("test_async_driver",
async_id,
nid);
if (IS_ERR(*pdev)) {
err = PTR_ERR(*pdev);
*pdev = NULL;
pr_err("failed to create async_dev: %d\n", err);
goto err_unregister_async_driver;
}
async_id++;
}
duration = (unsigned long long)ktime_ms_delta(ktime_get(), calltime);
dev_info(&(*pdev)->dev,
"registration took %lld msecs\n", duration);
if (duration > TEST_PROBE_THRESHOLD) {
dev_err(&(*pdev)->dev,
"test failed: probe took too long\n");
err = -ETIMEDOUT;
goto err_unregister_async_driver;
}
pr_info("registering first synchronous device...\n");
nid = cpu_to_node(cpu);
pdev = &sync_dev[sync_id];
*pdev = test_platform_device_register_node("test_sync_driver",
sync_id,
NUMA_NO_NODE);
if (IS_ERR(*pdev)) {
err = PTR_ERR(*pdev);
*pdev = NULL;
pr_err("failed to create sync_dev: %d\n", err);
goto err_unregister_async_driver;
}
sync_id++;
pr_info("registering synchronous driver...\n");
calltime = ktime_get();
err = platform_driver_register(&sync_driver);
if (err) {
pr_err("Failed to register async_driver: %d\n", err);
goto err_unregister_sync_devs;
}
duration = (unsigned long long)ktime_ms_delta(ktime_get(), calltime);
pr_info("registration took %lld msecs\n", duration);
if (duration < TEST_PROBE_THRESHOLD) {
dev_err(&(*pdev)->dev,
"test failed: probe was too quick\n");
err = -ETIMEDOUT;
goto err_unregister_sync_driver;
}
pr_info("registering second synchronous device...\n");
pdev = &sync_dev[sync_id];
calltime = ktime_get();
*pdev = test_platform_device_register_node("test_sync_driver",
sync_id,
NUMA_NO_NODE);
if (IS_ERR(*pdev)) {
err = PTR_ERR(*pdev);
*pdev = NULL;
pr_err("failed to create sync_dev: %d\n", err);
goto err_unregister_sync_driver;
}
sync_id++;
duration = (unsigned long long)ktime_ms_delta(ktime_get(), calltime);
dev_info(&(*pdev)->dev,
"registration took %lld msecs\n", duration);
if (duration < TEST_PROBE_THRESHOLD) {
dev_err(&(*pdev)->dev,
"test failed: probe was too quick\n");
err = -ETIMEDOUT;
goto err_unregister_sync_driver;
}
/*
* The async events should have completed while we were taking care
* of the synchronous events. We will now terminate any outstanding
* asynchronous probe calls remaining by forcing timeout and remove
* the driver before we return which should force the flush of the
* pending asynchronous probe calls.
*
* Otherwise if they completed without errors or warnings then
* report successful completion.
*/
if (atomic_read(&async_completed) != async_id) {
pr_err("async events still pending, forcing timeout\n");
atomic_inc(&timeout);
err = -ETIMEDOUT;
} else if (!atomic_read(&errors) && !atomic_read(&warnings)) {
pr_info("completed successfully\n");
return 0;
}
err_unregister_sync_driver:
platform_driver_unregister(&sync_driver);
err_unregister_sync_devs:
while (sync_id--)
platform_device_unregister(sync_dev[sync_id]);
err_unregister_async_driver:
platform_driver_unregister(&async_driver);
err_unregister_async_devs:
while (async_id--)
platform_device_unregister(async_dev[async_id]);
/*
* If err is already set then count that as an additional error for
* the test. Otherwise we will report an invalid argument error and
* not count that as we should have reached here as a result of
* errors or warnings being reported by the probe routine.
*/
if (err)
atomic_inc(&errors);
else
err = -EINVAL;
pr_err("Test failed with %d errors and %d warnings\n",
atomic_read(&errors), atomic_read(&warnings));
return err;
}
module_init(test_async_probe_init);
static void __exit test_async_probe_exit(void)
{
int id = 2;
platform_driver_unregister(&async_driver);
platform_driver_unregister(&sync_driver);
while (id--)
platform_device_unregister(sync_dev[id]);
id = NR_CPUS * 2;
while (id--)
platform_device_unregister(async_dev[id]);
}
module_exit(test_async_probe_exit);
MODULE_DESCRIPTION("Test module for asynchronous driver probing");
MODULE_AUTHOR("Dmitry Torokhov <dtor@chromium.org>");
MODULE_LICENSE("GPL");