From mboxrd@z Thu Jan 1 00:00:00 1970 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-alma10-1.taild15c8.ts.net [100.103.45.18]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id D3E3344A701; Sun, 11 Oct 2026 12:14:24 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=100.103.45.18 ARC-Seal:i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1791720872; cv=none; b=Yp5nnjiOqCO/325R9t3arANRmY+gYmS4PgZCaX1TRYbFJNa3ZMaCbUWyg/mP5uSJ3Cs/HQNeHVRFLzFhf9CYUyyTcqwZ+KrrLMFOsHj8/AsZ48xUMz+Iog7BxI1EwkMGFxTSWcOoPR11Cuf9V3S7MZJdKmiqwxfJBvc5tGR7JLE= ARC-Message-Signature:i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1791720872; c=relaxed/simple; bh=DAamR5mJ5enjU6q1VSlf0CL7QM28H43CyvX+vMy1e9U=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=vCMb2DJm9fTWba+ZuvhhQxsT08L0El4beHoXiVjfGo9nypmjofthAm8wRY25hZ7vFMHK526hpi9ECalY+xGllU/HrqqSrIuZOQbabjrAXBT9EcC2Ib5D6mHGqYw1vpZKvdh2z0aDpWDrI5PHUf0OEL79AgNP5PWTiUnstOtaLoU= ARC-Authentication-Results:i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=VmjQi4ex; arc=none smtp.client-ip=100.103.45.18 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="VmjQi4ex" Received: by smtp.kernel.org (Postfix) with ESMTPSA id C85ED1F0089C; Sun, 11 Oct 2026 12:14:21 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=kernel.org; s=k20260515; t=1791720864; bh=kOkDq6Qw+S7Y6AGcHJMe40XM5ws3eb2QUuy57io4e40=; h=From:To:Cc:Subject:Date:In-Reply-To:References; b=VmjQi4exzk+dxZJZtGb2mWgGFOulg3DHozTHAaAv099c5OQa/UHWSVqEq7mKBy21K 4uNYHzlLh3tMuNfgQEq1MjwF2RU78zcu7IJgmu01QWk40kNI/qSlWSCucOVar2Rmrd hoMv6JV+CAMpX1qDNzyFfqrnE9Z7FaRjMEABALUjQ3W1cL5oQH4IYC1rV+Nfl1/e8d fKlJ2DS1ztfmIHPyv9B7Ssc7HikEE5y/DRzU9hDnbXYY7BeCq758I6GdHJbRrF/Pci iNEFSh23ByEIw95/GqF3FlYs1x9EiSEKru2wFVHr+Q/KcIFB9qQMsrO60ci25e++gh qCaCeHjFmDhXA== From: Tzung-Bi Shih To: Greg Kroah-Hartman , "Rafael J. Wysocki" , Danilo Krummrich , Johan Hovold Cc: Jonathan Corbet , Shuah Khan , Randy Dunlap , Bartosz Golaszewski , Linus Walleij , Philipp Zabel , driver-core@lists.linux.dev, linux-kernel@vger.kernel.org, linux-gpio@vger.kernel.org, tzungbi@kernel.org Subject: [PATCH v14 4/5] reset: Add KUnit test cases Date: Sun, 11 Oct 2026 20:12:44 +0800 Message-ID: <20261011121246.162759-5-tzungbi@kernel.org> X-Mailer: git-send-email 2.53.0 In-Reply-To: <20261011121246.162759-1-tzungbi@kernel.org> References: <20261011121246.162759-1-tzungbi@kernel.org> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: 8bit Since commit 9035073d0ef1 ("reset: convert reset core to using firmware nodes"), reset suppliers and consumers can be wired up using software nodes without requiring Device Tree or hardware devices. Leverage this to add KUnit test cases for the reset controller framework. The test cases cover the following scenarios: - Basic: Verifies that a consumer can acquire a reset control via fwnode and perform reset, assert, deassert, status, release, and acquire operations. - Use After Unregister: Verifies that after the supplier unregisters the reset_controller_dev while a consumer still holds a reset_control handle, subsequent consumer operations fail safely with -ENODEV. - Concurrent Unregister: Verifies that unregistering the reset_controller_dev concurrently with an in-flight reset operation waits for the active operation to complete before returning. Signed-off-by: Tzung-Bi Shih --- Test: $ ./tools/testing/kunit/kunit.py run \ --kconfig_add CONFIG_RESET_CONTROLLER=y \ --kconfig_add CONFIG_RESET_KUNIT_TEST=y \ reset_test $ ./tools/testing/kunit/kunit.py run \ --kconfig_add CONFIG_RESET_CONTROLLER=y \ --kconfig_add CONFIG_RESET_KUNIT_TEST=y \ --kconfig_add CONFIG_PROVE_LOCKING=y \ --kconfig_add CONFIG_DEBUG_KERNEL=y \ --kconfig_add CONFIG_KASAN=y \ --arch=x86_64 \ reset_test --- v14: - New to the series. --- drivers/reset/Kconfig | 5 + drivers/reset/Makefile | 2 + drivers/reset/core-test.c | 329 ++++++++++++++++++++++++++++++++++++++ 3 files changed, 336 insertions(+) create mode 100644 drivers/reset/core-test.c diff --git a/drivers/reset/Kconfig b/drivers/reset/Kconfig index eda9c698a95f..c7dceec8f58c 100644 --- a/drivers/reset/Kconfig +++ b/drivers/reset/Kconfig @@ -457,4 +457,9 @@ source "drivers/reset/starfive/Kconfig" source "drivers/reset/sti/Kconfig" source "drivers/reset/tegra/Kconfig" +config RESET_KUNIT_TEST + tristate "KUnit tests for reset controller" if !KUNIT_ALL_TESTS + depends on KUNIT + default KUNIT_ALL_TESTS + endif diff --git a/drivers/reset/Makefile b/drivers/reset/Makefile index 489fafd6bbe3..bb01198d495b 100644 --- a/drivers/reset/Makefile +++ b/drivers/reset/Makefile @@ -55,3 +55,5 @@ obj-$(CONFIG_RESET_UNIPHIER) += reset-uniphier.o obj-$(CONFIG_RESET_UNIPHIER_GLUE) += reset-uniphier-glue.o obj-$(CONFIG_RESET_ZYNQ) += reset-zynq.o obj-$(CONFIG_RESET_ZYNQMP) += reset-zynqmp.o + +obj-$(CONFIG_RESET_KUNIT_TEST) += core-test.o diff --git a/drivers/reset/core-test.c b/drivers/reset/core-test.c new file mode 100644 index 000000000000..834556322b23 --- /dev/null +++ b/drivers/reset/core-test.c @@ -0,0 +1,329 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright 2026 Google LLC + * + * KUnit tests for the reset controller. + * + * The test cases cover the following scenarios: + * + * - Basic: Verifies that a consumer can acquire a reset control via + * fwnode and perform reset, assert, deassert, status, release, and + * acquire operations. + * + * - Use After Unregister: Verifies that after the supplier unregisters + * the reset_controller_dev while a consumer still holds a reset_control + * handle, subsequent consumer operations fail safely with -ENODEV. + * + * - Concurrent Unregister: Verifies that unregistering the + * reset_controller_dev concurrently with an in-flight reset operation + * waits for the active operation to complete before returning. + */ + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +struct reset_test_context { + struct fwnode_handle *supp_fwnode; + struct fwnode_handle *cons_fwnode; + struct reset_controller_dev rcdev; + struct reset_control *rstc; + + int reset_count; + int assert_count; + int deassert_count; + int status_count; + + void *priv; +}; + +static int reset_test_op_reset(struct reset_controller_dev *rcdev, + unsigned long id) +{ + struct reset_test_context *ctx = container_of(rcdev, typeof(*ctx), rcdev); + + ctx->reset_count++; + return 0; +} + +static int reset_test_op_assert(struct reset_controller_dev *rcdev, + unsigned long id) +{ + struct reset_test_context *ctx = container_of(rcdev, typeof(*ctx), rcdev); + + ctx->assert_count++; + return 0; +} + +static int reset_test_op_deassert(struct reset_controller_dev *rcdev, + unsigned long id) +{ + struct reset_test_context *ctx = container_of(rcdev, typeof(*ctx), rcdev); + + ctx->deassert_count++; + return 0; +} + +static int reset_test_op_status(struct reset_controller_dev *rcdev, + unsigned long id) +{ + struct reset_test_context *ctx = container_of(rcdev, typeof(*ctx), rcdev); + + ctx->status_count++; + return 0; +} + +static const struct reset_control_ops reset_test_ops = { + .reset = reset_test_op_reset, + .assert = reset_test_op_assert, + .deassert = reset_test_op_deassert, + .status = reset_test_op_status, +}; + +static int reset_test_init(struct kunit *test) +{ + static const struct property_entry supp_props[] = { + PROPERTY_ENTRY_U32("#reset-cells", 1), + {} + }; + static const struct software_node supp_swnode = { + .name = "reset-test-supplier", + .properties = supp_props, + }; + static const struct software_node_ref_args refs[] = { + SOFTWARE_NODE_REFERENCE(&supp_swnode, 0), + }; + static const struct property_entry cons_props[] = { + PROPERTY_ENTRY_REF_ARRAY("resets", refs), + {} + }; + + struct reset_test_context *ctx; + int ret; + + ctx = kunit_kzalloc(test, sizeof(*ctx), GFP_KERNEL); + KUNIT_ASSERT_NOT_NULL(test, ctx); + + ctx->supp_fwnode = kunit_software_node_register(test, &supp_swnode); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, ctx->supp_fwnode); + + ctx->cons_fwnode = kunit_fwnode_create_software_node(test, cons_props, + NULL); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, ctx->cons_fwnode); + + ctx->rcdev.ops = &reset_test_ops; + ctx->rcdev.nr_resets = 1; + ctx->rcdev.fwnode = ctx->supp_fwnode; + + ret = reset_controller_register(&ctx->rcdev); + KUNIT_ASSERT_EQ(test, ret, 0); + + ctx->rstc = __fwnode_reset_control_get(ctx->cons_fwnode, NULL, 0, + RESET_CONTROL_EXCLUSIVE); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, ctx->rstc); + + test->priv = ctx; + return 0; +} + +static void reset_test_basic(struct kunit *test) +{ + struct reset_test_context *ctx = test->priv; + struct reset_control *rstc = ctx->rstc; + + KUNIT_EXPECT_EQ(test, ctx->reset_count, 0); + KUNIT_EXPECT_EQ(test, reset_control_reset(rstc), 0); + KUNIT_EXPECT_EQ(test, ctx->reset_count, 1); + + KUNIT_EXPECT_EQ(test, ctx->assert_count, 0); + KUNIT_EXPECT_EQ(test, reset_control_assert(rstc), 0); + KUNIT_EXPECT_EQ(test, ctx->assert_count, 1); + + KUNIT_EXPECT_EQ(test, ctx->deassert_count, 0); + KUNIT_EXPECT_EQ(test, reset_control_deassert(rstc), 0); + KUNIT_EXPECT_EQ(test, ctx->deassert_count, 1); + + KUNIT_EXPECT_EQ(test, ctx->status_count, 0); + KUNIT_EXPECT_EQ(test, reset_control_status(rstc), 0); + KUNIT_EXPECT_EQ(test, ctx->status_count, 1); + + reset_control_release(rstc); + KUNIT_EXPECT_EQ(test, reset_control_acquire(rstc), 0); + + reset_control_put(rstc); + reset_controller_unregister(&ctx->rcdev); +} + +static void reset_test_use_after_unregister(struct kunit *test) +{ + struct reset_test_context *ctx = test->priv; + struct reset_control *rstc = ctx->rstc; + + KUNIT_EXPECT_EQ(test, ctx->reset_count, 0); + KUNIT_EXPECT_EQ(test, reset_control_reset(rstc), 0); + KUNIT_EXPECT_EQ(test, ctx->reset_count, 1); + + /* Unregister supplier while consumer still holds rstc. */ + reset_controller_unregister(&ctx->rcdev); + + KUNIT_EXPECT_EQ(test, ctx->reset_count, 1); + KUNIT_EXPECT_EQ(test, reset_control_reset(rstc), -ENODEV); + KUNIT_EXPECT_EQ(test, ctx->reset_count, 1); + + KUNIT_EXPECT_EQ(test, reset_control_assert(rstc), -ENODEV); + KUNIT_EXPECT_EQ(test, reset_control_deassert(rstc), -ENODEV); + KUNIT_EXPECT_EQ(test, reset_control_status(rstc), -ENODEV); + + reset_control_release(rstc); + KUNIT_EXPECT_EQ(test, reset_control_acquire(rstc), -ENODEV); + + reset_control_put(rstc); +} + +struct test_concurrent_unregister_context { + struct kunit *test; + struct completion started, enter; + struct task_struct *thread; + + /* Used by test consumer. */ + struct completion entered, exit; + int expected_ret; +}; + +static int test_concurrent_unregister_supplier(void *data) +{ + struct test_concurrent_unregister_context *ctx = data; + struct reset_test_context *rtc = ctx->test->priv; + + complete(&ctx->started); + + wait_for_completion(&ctx->enter); + reset_controller_unregister(&rtc->rcdev); + + return 0; +} + +static int test_concurrent_unregister_consumer(void *data) +{ + struct test_concurrent_unregister_context *ctx = data; + struct reset_test_context *rtc = ctx->test->priv; + int ret; + + complete(&ctx->started); + + wait_for_completion(&ctx->enter); + + ret = reset_control_reset(rtc->rstc); + KUNIT_EXPECT_EQ(ctx->test, ret, ctx->expected_ret); + + return 0; +} + +static int +reset_test_concurrent_unregister_op_reset(struct reset_controller_dev *rcdev, + unsigned long id) +{ + struct reset_test_context *rtc = container_of(rcdev, typeof(*rtc), rcdev); + struct test_concurrent_unregister_context *ctx = rtc->priv; + + rtc->reset_count++; + complete(&ctx->entered); + + wait_for_completion(&ctx->exit); + return ctx->expected_ret; +} + +static const struct reset_control_ops reset_test_concurrent_unregister_ops = { + .reset = reset_test_concurrent_unregister_op_reset, +}; + +static void reset_test_concurrent_unregister(struct kunit *test) +{ + struct reset_test_context *rtc = test->priv; + struct test_concurrent_unregister_context *ctx; + int i, ret, val; + + rtc->rcdev.ops = &reset_test_concurrent_unregister_ops; + + ctx = kunit_kmalloc_array(test, 3, sizeof(*ctx), GFP_KERNEL); + KUNIT_ASSERT_NOT_NULL(test, ctx); + + for (i = 0; i < 3; ++i) { + ctx[i].test = test; + init_completion(&ctx[i].started); + init_completion(&ctx[i].enter); + + if (i == 0) { + ctx[i].thread = kthread_run( + test_concurrent_unregister_supplier, ctx + i, + "supplier%d", i); + } else { + init_completion(&ctx[i].entered); + init_completion(&ctx[i].exit); + + ctx[i].thread = kthread_run( + test_concurrent_unregister_consumer, ctx + i, + "consumer%d", i); + } + KUNIT_ASSERT_FALSE(test, IS_ERR(ctx[i].thread)); + get_task_struct(ctx[i].thread); + + wait_for_completion(&ctx[i].started); + } + + rtc->priv = &ctx[1]; + ctx[1].expected_ret = 0x12345678; + /* consumer1 enters reset(). */ + KUNIT_EXPECT_EQ(test, rtc->reset_count, 0); + complete(&ctx[1].enter); + wait_for_completion(&ctx[1].entered); + KUNIT_EXPECT_EQ(test, rtc->reset_count, 1); + + /* supplier0 unregisters. */ + complete(&ctx[0].enter); + ret = read_poll_timeout(reset_control_status, val, val == -ENODEV, + 1000, 100000, false, rtc->rstc); + KUNIT_ASSERT_EQ(test, ret, 0); + /* supplier0 can't exit. It's waiting for all consumers. */ + + ctx[2].expected_ret = -ENODEV; + /* consumer2 must see -ENODEV. */ + complete(&ctx[2].enter); + kthread_stop_put(ctx[2].thread); + + /* consumer1 exits reset(). */ + complete(&ctx[1].exit); + kthread_stop_put(ctx[1].thread); + kthread_stop_put(ctx[0].thread); + + /* Subsequent access must see -ENODEV. */ + KUNIT_EXPECT_EQ(test, reset_control_reset(rtc->rstc), -ENODEV); + reset_control_put(rtc->rstc); +} + +static struct kunit_case reset_test_cases[] = { + KUNIT_CASE(reset_test_basic), + KUNIT_CASE(reset_test_use_after_unregister), + KUNIT_CASE(reset_test_concurrent_unregister), + {} +}; + +static struct kunit_suite reset_test_suite = { + .name = "reset_test", + .test_cases = reset_test_cases, + .init = reset_test_init, +}; + +kunit_test_suite(reset_test_suite); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Tzung-Bi Shih "); +MODULE_DESCRIPTION("KUnit tests for the reset controller"); -- 2.53.0