lists.openwall.net   lists  /  announce  owl-users  owl-dev  john-users  john-dev  passwdqc-users  yescrypt  popa3d-users  /  oss-security  kernel-hardening  musl  sabotage  tlsify  passwords  /  crypt-dev  xvendor  /  Bugtraq  Full-Disclosure  linux-kernel  linux-netdev  linux-ext4  linux-hardening  linux-cve-announce  PHC 
Open Source and information security mailing list archives
 
Hash Suite for Android: free password hash cracker in your pocket
[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Message-ID: <20250513224402.864767-2-briannorris@chromium.org>
Date: Tue, 13 May 2025 15:42:37 -0700
From: Brian Norris <briannorris@...omium.org>
To: Thomas Gleixner <tglx@...utronix.de>
Cc: Douglas Anderson <dianders@...omium.org>,
	Tsai Sung-Fu <danielsftsai@...gle.com>,
	linux-kernel@...r.kernel.org,
	Brian Norris <briannorris@...omium.org>
Subject: [PATCH 1/2] genirq: Add kunit tests for depth counts

These tests demonstrate bugs in the irq shutdown/startup code. See the
appended test report.

In summary, the latter two cases cover:

 ## shutdown depth:
 disable_irq()
 irq_shutdown_and_deactivate()
 irq_activate_and_startup() <-- BUG: depth is 0 after this
 enable_irq()

 ## cpu hotplug:
 affine IRQ to CPU 1
 disable_irq()
 remove CPU 1
 add CPU 1 <-- BUG: depth is 0 after this
 enable_irq()

NB: since one of the tests intersects with CPU hotplug, these tests
requires SMP support. They can be easily run with:

$ tools/testing/kunit/kunit.py run 'irq_test_cases*' --arch x86_64 --qemu_args '-smp 2'
[13:24:21] =============== irq_test_cases (3 subtests) ================
[13:24:21] [PASSED] irq_disable_depth_test
[13:24:21]     # irq_shutdown_depth_test: EXPECTATION FAILED at kernel/irq/irq_test.c:93
[13:24:21]     Expected desc->depth == 1, but
[13:24:21]         desc->depth == 0 (0x0)
[13:24:21] ------------[ cut here ]------------
[13:24:21] Unbalanced enable for IRQ 25
[13:24:21] WARNING: CPU: 1 PID: 34 at kernel/irq/manage.c:792 __enable_irq+0x36/0x60
...
[13:24:21] Call Trace:
[13:24:21]  <TASK>
[13:24:21]  enable_irq+0x4a/0x90
[13:24:21]  irq_shutdown_depth_test+0x17b/0x3b0
[13:24:21]  kunit_try_run_case+0x90/0x120
...
[13:24:21]  </TASK>
[13:24:21] ---[ end trace 0000000000000000 ]---
[13:24:21]     # irq_shutdown_depth_test.speed: slow
[13:24:21] [FAILED] irq_shutdown_depth_test
[13:24:21]  #1
[13:24:21]     # irq_cpuhotplug_test: EXPECTATION FAILED at kernel/irq/irq_test.c:140
[13:24:21]     Expected desc->depth == 1, but
[13:24:21]         desc->depth == 0 (0x0)
[13:24:21] ------------[ cut here ]------------
[13:24:21] Unbalanced enable for IRQ 26
[13:24:21] WARNING: CPU: 0 PID: 36 at kernel/irq/manage.c:792 __enable_irq+0x36/0x60
...
[13:24:21] Call Trace:
[13:24:21]  <TASK>
[13:24:21]  enable_irq+0x4a/0x90
[13:24:21]  irq_cpuhotplug_test+0x28f/0x660
[13:24:21]  kunit_try_run_case+0x90/0x120
...
[13:24:21]  </TASK>
[13:24:21] ---[ end trace 0000000000000000 ]---
[13:24:21]     # irq_cpuhotplug_test.speed: slow
[13:24:21] [FAILED] irq_cpuhotplug_test
[13:24:21]     # module: irq_test
[13:24:21] # irq_test_cases: pass:1 fail:2 skip:0 total:3
[13:24:21] # Totals: pass:1 fail:2 skip:0 total:3
[13:24:21] ================= [FAILED] irq_test_cases ==================
[13:24:21] ============================================================
[13:24:21] Testing complete. Ran 3 tests: passed: 1, failed: 2

Also note that currently, these tests don't fully clean up after
themselves, as I didn't yet figure out all the right ways to set up a
fake virq and domain for the purpose of unit testing. They contain TODOs
to that effect.

Signed-off-by: Brian Norris <briannorris@...omium.org>
---

 kernel/irq/Kconfig    |  10 +++
 kernel/irq/Makefile   |   1 +
 kernel/irq/irq_test.c | 162 ++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 173 insertions(+)
 create mode 100644 kernel/irq/irq_test.c

diff --git a/kernel/irq/Kconfig b/kernel/irq/Kconfig
index 3f02a0e45254..9295dabea4a0 100644
--- a/kernel/irq/Kconfig
+++ b/kernel/irq/Kconfig
@@ -144,6 +144,16 @@ config GENERIC_IRQ_DEBUGFS
 config GENERIC_IRQ_KEXEC_CLEAR_VM_FORWARD
 	bool
 
+config IRQ_KUNIT_TEST
+	tristate "KUnit test for IRQ management APIs" if !KUNIT_ALL_TESTS
+	depends on KUNIT
+	default KUNIT_ALL_TESTS
+	select SMP
+	help
+	  Enable this option to test IRQ management APIs.
+
+	  If unsure, say N.
+
 endmenu
 
 config GENERIC_IRQ_MULTI_HANDLER
diff --git a/kernel/irq/Makefile b/kernel/irq/Makefile
index c0f44c06d69d..6ab3a4055667 100644
--- a/kernel/irq/Makefile
+++ b/kernel/irq/Makefile
@@ -19,3 +19,4 @@ obj-$(CONFIG_GENERIC_IRQ_IPI_MUX) += ipi-mux.o
 obj-$(CONFIG_SMP) += affinity.o
 obj-$(CONFIG_GENERIC_IRQ_DEBUGFS) += debugfs.o
 obj-$(CONFIG_GENERIC_IRQ_MATRIX_ALLOCATOR) += matrix.o
+obj-$(CONFIG_IRQ_KUNIT_TEST) += irq_test.o
diff --git a/kernel/irq/irq_test.c b/kernel/irq/irq_test.c
new file mode 100644
index 000000000000..24f4d8e6c433
--- /dev/null
+++ b/kernel/irq/irq_test.c
@@ -0,0 +1,162 @@
+// SPDX-License-Identifier: LGPL-2.1+
+
+#include <linux/cpu.h>
+#include <linux/cpumask.h>
+#include <linux/interrupt.h>
+#include <linux/irq.h>
+#include <linux/irqdesc.h>
+#include <linux/irqdomain.h>
+#include <linux/nodemask.h>
+#include <kunit/test.h>
+
+#include "internals.h"
+
+static irqreturn_t noop_handler(int, void *)
+{
+	return IRQ_HANDLED;
+}
+
+static void noop(struct irq_data *data) { }
+static unsigned int noop_ret(struct irq_data *data) { return 0; }
+
+static int noop_affinity(struct irq_data *data, const struct cpumask *dest, bool force)
+{
+	irq_data_update_effective_affinity(data, dest);
+
+	return 0;
+}
+
+static struct irq_chip fake_irq_chip = {
+	.name           = "fake",
+	.irq_startup    = noop_ret,
+	.irq_shutdown   = noop,
+	.irq_enable     = noop,
+	.irq_disable    = noop,
+	.irq_ack        = noop,
+	.irq_mask       = noop,
+	.irq_unmask     = noop,
+	.irq_set_affinity = noop_affinity,
+	.flags          = IRQCHIP_SKIP_SET_WAKE,
+};
+
+static void irq_disable_depth_test(struct kunit *test)
+{
+	struct irq_desc *desc;
+	int virq, ret;
+
+	virq = irq_domain_alloc_descs(-1 /*virq*/, 1 /*nr_irqs*/, 0/*hwirq*/, first_online_node/*node*/, NULL);
+	KUNIT_ASSERT_GE(test, virq, 0);
+
+	irq_set_chip_and_handler(virq, &dummy_irq_chip, handle_simple_irq);
+
+	desc = irq_to_desc(virq);
+	KUNIT_ASSERT_PTR_NE(test, desc, NULL);
+
+	ret = request_irq(virq, noop_handler, 0, "test_irq", NULL);
+	KUNIT_EXPECT_EQ(test, ret, 0);
+
+	KUNIT_EXPECT_EQ(test, desc->depth, 0);
+
+	disable_irq(virq);
+	KUNIT_EXPECT_EQ(test, desc->depth, 1);
+
+	enable_irq(virq);
+	KUNIT_EXPECT_EQ(test, desc->depth, 0);
+
+	/* TODO: free virq? */
+}
+
+static void irq_shutdown_depth_test(struct kunit *test)
+{
+	struct irq_desc *desc;
+	int virq, ret;
+
+	virq = irq_domain_alloc_descs(-1 /*virq*/, 1 /*nr_irqs*/, 0/*hwirq*/, first_online_node/*node*/, NULL);
+	KUNIT_ASSERT_GE(test, virq, 0);
+
+	irq_set_chip_and_handler(virq, &dummy_irq_chip, handle_simple_irq);
+
+	desc = irq_to_desc(virq);
+	KUNIT_ASSERT_PTR_NE(test, desc, NULL);
+
+	ret = request_irq(virq, noop_handler, 0, "test_irq", NULL);
+	KUNIT_EXPECT_EQ(test, ret, 0);
+
+	KUNIT_EXPECT_EQ(test, desc->depth, 0);
+
+	disable_irq(virq);
+	KUNIT_EXPECT_EQ(test, desc->depth, 1);
+
+	irq_shutdown_and_deactivate(desc);
+	KUNIT_EXPECT_EQ(test, irq_activate_and_startup(desc, IRQ_NORESEND), 0);
+
+	KUNIT_EXPECT_EQ(test, desc->depth, 1);
+
+	enable_irq(virq);
+	KUNIT_EXPECT_EQ(test, desc->depth, 0);
+
+	/* TODO: free virq? */
+}
+
+static void irq_cpuhotplug_test(struct kunit *test)
+{
+	struct irq_desc *desc;
+	struct irq_data *data;
+	int virq, ret;
+	struct irq_affinity_desc affinity = {
+		.is_managed = 1,
+	};
+
+	cpumask_copy(&affinity.mask, cpumask_of(1));
+	KUNIT_ASSERT_PTR_NE(test, get_cpu_device(1), NULL);
+	KUNIT_ASSERT_TRUE(test, cpu_is_hotpluggable(1));
+
+	virq = irq_domain_alloc_descs(-1 /*virq*/, 1 /*nr_irqs*/, 0/*hwirq*/, first_online_node/*node*/, &affinity);
+	KUNIT_ASSERT_GE(test, virq, 0);
+
+	irq_set_chip_and_handler(virq, &fake_irq_chip, handle_simple_irq);
+
+	desc = irq_to_desc(virq);
+	KUNIT_ASSERT_PTR_NE(test, desc, NULL);
+
+	data = irq_desc_get_irq_data(desc);
+	KUNIT_ASSERT_PTR_NE(test, data, NULL);
+
+	ret = request_irq(virq, noop_handler, 0, "test_irq", NULL);
+	KUNIT_EXPECT_EQ(test, ret, 0);
+
+	KUNIT_EXPECT_TRUE(test, irqd_is_activated(data));
+	KUNIT_EXPECT_TRUE(test, irqd_is_started(data));
+	KUNIT_EXPECT_TRUE(test, irqd_affinity_is_managed(data));
+
+	KUNIT_EXPECT_EQ(test, desc->depth, 0);
+
+	disable_irq(virq);
+	KUNIT_EXPECT_EQ(test, desc->depth, 1);
+
+	KUNIT_EXPECT_EQ(test, remove_cpu(1), 0);
+	KUNIT_EXPECT_EQ(test, add_cpu(1), 0);
+
+	KUNIT_EXPECT_EQ(test, desc->depth, 1);
+
+	enable_irq(virq);
+	KUNIT_EXPECT_EQ(test, desc->depth, 0);
+
+	/* TODO: free virq? */
+}
+
+static struct kunit_case irq_test_cases[] = {
+	KUNIT_CASE_SLOW(irq_disable_depth_test),
+	KUNIT_CASE_SLOW(irq_shutdown_depth_test),
+	KUNIT_CASE_SLOW(irq_cpuhotplug_test),
+	{}
+};
+
+static struct kunit_suite irq_test_suite = {
+	.name = "irq_test_cases",
+	.test_cases = irq_test_cases,
+};
+
+kunit_test_suite(irq_test_suite);
+MODULE_DESCRIPTION("IRQ unit test suite");
+MODULE_LICENSE("GPL");
-- 
2.49.0.1045.g170613ef41-goog


Powered by blists - more mailing lists

Powered by Openwall GNU/*/Linux Powered by OpenVZ