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),
{ }
};
prev 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