Linux USB
 help / color / mirror / Atom feed
From: Andrei Rusu de Castro <arc@empyreal.works>
To: linux-usb@vger.kernel.org
Cc: andreas.noever@gmail.com, westeri@kernel.org,
	YehezkelShB@gmail.com, Sanath.S@amd.com,
	Basavaraj.Natikar@amd.com, linux-kernel@vger.kernel.org
Subject: [PATCH 2/2] thunderbolt: Test ring interrupt warning after host reset
Date: Wed, 02 Sep 2026 12:34:43 +0000	[thread overview]
Message-ID: <20260902-thunderbolt-2-2fdc1c1b@empyreal.works> (raw)
In-Reply-To: <20260902-thunderbolt-cover-2fdc1c1b@empyreal.works>

Cover the warning decision independently of MMIO by constructing a ring
and NHI generation pair. Verify that duplicate enables always warn,
duplicate disables without an intervening reset warn, a disable after a
reset does not warn, and an update that changed the register never
warns.

Expose the predicate only in KUnit builds through VISIBLE_IF_KUNIT; it
remains private in production builds and is not exported outside the
Thunderbolt module.

The cases were verified under UML KUnit.

Signed-off-by: Andrei Rusu de Castro <arc@empyreal.works>
---
 drivers/thunderbolt/nhi.c  |  4 +-
 drivers/thunderbolt/nhi.h  |  4 ++
 drivers/thunderbolt/test.c | 82 ++++++++++++++++++++++++++++++++++++++
 3 files changed, 89 insertions(+), 1 deletion(-)

diff --git a/drivers/thunderbolt/nhi.c b/drivers/thunderbolt/nhi.c
index f56590100aef..d768a84adaab 100644
--- a/drivers/thunderbolt/nhi.c
+++ b/drivers/thunderbolt/nhi.c
@@ -21,6 +21,8 @@
 #include <linux/string_choices.h>
 #include <linux/string_helpers.h>
 
+#include <kunit/visibility.h>
+
 #include "nhi.h"
 #include "nhi_regs.h"
 #include "tb.h"
@@ -86,7 +88,7 @@ static void nhi_clear_interrupt(struct tb_nhi *nhi, int ring)
  *
  * Return: %true if the caller should warn about the no-op update.
  */
-static bool
+VISIBLE_IF_KUNIT bool
 nhi_ring_interrupt_should_warn(const struct tb_ring *ring, bool active,
 			       bool unchanged)
 {
diff --git a/drivers/thunderbolt/nhi.h b/drivers/thunderbolt/nhi.h
index f72d6b274501..393bd831375f 100644
--- a/drivers/thunderbolt/nhi.h
+++ b/drivers/thunderbolt/nhi.h
@@ -37,6 +37,10 @@ irqreturn_t ring_msix(int irq, void *data);
 int nhi_probe(struct tb_nhi *nhi);
 void nhi_shutdown(struct tb_nhi *nhi);
 void nhi_reset_interface(struct tb_nhi *nhi);
+#if IS_ENABLED(CONFIG_KUNIT)
+bool nhi_ring_interrupt_should_warn(const struct tb_ring *ring, bool active,
+				    bool unchanged);
+#endif
 
 extern const struct dev_pm_ops nhi_pm_ops;
 
diff --git a/drivers/thunderbolt/test.c b/drivers/thunderbolt/test.c
index 05652ee82fbf..3ccdd967396b 100644
--- a/drivers/thunderbolt/test.c
+++ b/drivers/thunderbolt/test.c
@@ -9,6 +9,7 @@
 #include <kunit/test.h>
 #include <linux/idr.h>
 
+#include "nhi.h"
 #include "tb.h"
 #include "tunnel.h"
 
@@ -3095,6 +3096,83 @@ static void tb_test_property_merge(struct kunit *test)
 	tb_property_free_dir(dir1);
 }
 
+static struct tb_ring *alloc_interrupt_test_ring(struct kunit *test,
+						 int nhi_generation,
+						 int ring_generation)
+{
+	struct tb_nhi *nhi;
+	struct tb_ring *ring;
+
+	nhi = kunit_kzalloc(test, sizeof(*nhi), GFP_KERNEL);
+	if (!nhi)
+		return NULL;
+
+	ring = kunit_kzalloc(test, sizeof(*ring), GFP_KERNEL);
+	if (!ring)
+		return NULL;
+
+	ring->nhi = nhi;
+	atomic_set(&nhi->reset_generation, nhi_generation);
+	ring->reset_generation = ring_generation;
+
+	return ring;
+}
+
+static void tb_test_ring_interrupt_warn_duplicate_enable(struct kunit *test)
+{
+	struct tb_ring *ring;
+
+	/* Enabling an already enabled interrupt is always a driver bug */
+	ring = alloc_interrupt_test_ring(test, 7, 7);
+	KUNIT_ASSERT_NOT_NULL(test, ring);
+	KUNIT_EXPECT_TRUE(test, nhi_ring_interrupt_should_warn(ring, true, true));
+
+	/* Including when the host interface was reset in between */
+	ring = alloc_interrupt_test_ring(test, 8, 7);
+	KUNIT_ASSERT_NOT_NULL(test, ring);
+	KUNIT_EXPECT_TRUE(test, nhi_ring_interrupt_should_warn(ring, true, true));
+}
+
+static void tb_test_ring_interrupt_warn_duplicate_disable(struct kunit *test)
+{
+	struct tb_ring *ring;
+
+	/*
+	 * No reset happened while this ring was running, so a redundant
+	 * disable means the driver lost track of the hardware state.
+	 */
+	ring = alloc_interrupt_test_ring(test, 7, 7);
+	KUNIT_ASSERT_NOT_NULL(test, ring);
+	KUNIT_EXPECT_TRUE(test, nhi_ring_interrupt_should_warn(ring, false, true));
+}
+
+static void tb_test_ring_interrupt_no_warn_after_reset(struct kunit *test)
+{
+	struct tb_ring *ring;
+
+	/*
+	 * The ring was started before the host interface was reset, which
+	 * cleared the ring interrupt bit underneath it.
+	 */
+	ring = alloc_interrupt_test_ring(test, 8, 7);
+	KUNIT_ASSERT_NOT_NULL(test, ring);
+	KUNIT_EXPECT_FALSE(test,
+			   nhi_ring_interrupt_should_warn(ring, false, true));
+}
+
+static void tb_test_ring_interrupt_no_warn_when_changed(struct kunit *test)
+{
+	struct tb_ring *ring;
+
+	/* An update that actually changed the register never warns */
+	ring = alloc_interrupt_test_ring(test, 8, 7);
+	KUNIT_ASSERT_NOT_NULL(test, ring);
+	KUNIT_EXPECT_FALSE(test,
+			   nhi_ring_interrupt_should_warn(ring, false, false));
+	KUNIT_EXPECT_FALSE(test,
+			   nhi_ring_interrupt_should_warn(ring, true, false));
+}
+
 static struct kunit_case tb_test_cases[] = {
 	KUNIT_CASE(tb_test_property_parse_u32_wrap),
 	KUNIT_CASE(tb_test_property_parse_recursion),
@@ -3141,6 +3219,10 @@ static struct kunit_case tb_test_cases[] = {
 	KUNIT_CASE(tb_test_property_parse_zero_length),
 	KUNIT_CASE(tb_test_property_parse_rootdir_overflow),
 	KUNIT_CASE(tb_test_property_merge),
+	KUNIT_CASE(tb_test_ring_interrupt_warn_duplicate_enable),
+	KUNIT_CASE(tb_test_ring_interrupt_warn_duplicate_disable),
+	KUNIT_CASE(tb_test_ring_interrupt_no_warn_after_reset),
+	KUNIT_CASE(tb_test_ring_interrupt_no_warn_when_changed),
 	{ }
 };
 


      parent reply	other threads:[~2026-09-02 12:34 UTC|newest]

Thread overview: 6+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-09-02 12:33 [PATCH 0/2] thunderbolt: suppress reset-induced ring interrupt warning Andrei Rusu de Castro
2026-09-02 12:34 ` [PATCH 1/2] thunderbolt: Do not warn when a reset clears ring interrupts Andrei Rusu de Castro
2026-09-02 12:50   ` Mika Westerberg
2026-09-02 20:53     ` Mario Limonciello
2026-09-03  3:44       ` Mika Westerberg
2026-09-02 12:34 ` Andrei Rusu de Castro [this message]

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20260902-thunderbolt-2-2fdc1c1b@empyreal.works \
    --to=arc@empyreal.works \
    --cc=Basavaraj.Natikar@amd.com \
    --cc=Sanath.S@amd.com \
    --cc=YehezkelShB@gmail.com \
    --cc=andreas.noever@gmail.com \
    --cc=linux-kernel@vger.kernel.org \
    --cc=linux-usb@vger.kernel.org \
    --cc=westeri@kernel.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox