All of lore.kernel.org
 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 an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.