git: 938fa80e4b48 - stable/15 - mbuf: Match TCP RSS hash types exactly

From: Kevin Bowling <kbowling_at_FreeBSD.org>
Date: Thu, 29 Oct 2026 22:30:06 UTC
The branch stable/15 has been updated by kbowling:

URL: https://cgit.FreeBSD.org/src/commit/?id=938fa80e4b48987c6497534de7a1b8fe0e13ab66

commit 938fa80e4b48987c6497534de7a1b8fe0e13ab66
Author:     Kevin Bowling <kbowling@FreeBSD.org>
AuthorDate: 2026-10-29 07:15:14 +0000
Commit:     Kevin Bowling <kbowling@FreeBSD.org>
CommitDate: 2026-10-29 22:29:45 +0000

    mbuf: Match TCP RSS hash types exactly
    
    RSS hash types are encoded values, not independent flags.  The bitmask
    check in M_HASHTYPE_ISHASH_TCP() also accepts UDP, address-only and opaque
    hash types, causing the syncache to retain them instead of falling back
    to a software TCP hash.
    
    Compare against the three TCP four-tuple hash types after masking the
    inner-header flag.  Keep the existing macro interface and use an inline
    helper to preserve single evaluation of its argument.
    
    Add ATF coverage for all 256 hash-type byte values, including inner-header
    hashes, and verify single evaluation of the mbuf argument.
    
    Reviewed by:    cc, gallatin
    Fixes:  a485399f8834 ("tcp: restrict flowtype copying to specific RSS TCP types")
    Sponsored by:   BBOX.io
    
    (cherry picked from commit b06db8c9dafb1734923e0483323920b4b0a05cc9)
---
 sys/sys/mbuf.h            | 21 +++++++++++++++----
 tests/sys/sys/Makefile    |  1 +
 tests/sys/sys/mbuf_test.c | 53 +++++++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 71 insertions(+), 4 deletions(-)

diff --git a/sys/sys/mbuf.h b/sys/sys/mbuf.h
index ac63a2b45e10..3f1c48b41b2a 100644
--- a/sys/sys/mbuf.h
+++ b/sys/sys/mbuf.h
@@ -579,10 +579,23 @@ m_epg_pagelen(const struct mbuf *m, int pidx, int pgoff)
 #define	M_HASHTYPE_TEST(m, v)	(M_HASHTYPE_GET(m) == (v))
 #define	M_HASHTYPE_ISHASH(m)	\
     (((m)->m_pkthdr.rsstype & M_HASHTYPE_HASHPROP) != 0)
-#define	M_HASHTYPE_ISHASH_TCP(m)				\
-    (((m)->m_pkthdr.rsstype & (M_HASHTYPE_RSS_TCP_IPV4 |		\
-				M_HASHTYPE_RSS_TCP_IPV6 |		\
-				M_HASHTYPE_RSS_TCP_IPV6_EX)) != 0)
+
+static inline int
+m_hashtype_ishash_tcp(const struct mbuf *m)
+{
+
+	/* Hash types are encoded values, not independent bits. */
+	switch (M_HASHTYPE_GET(m)) {
+	case M_HASHTYPE_RSS_TCP_IPV4:
+	case M_HASHTYPE_RSS_TCP_IPV6:
+	case M_HASHTYPE_RSS_TCP_IPV6_EX:
+		return (1);
+	default:
+		return (0);
+	}
+}
+
+#define	M_HASHTYPE_ISHASH_TCP(m)	m_hashtype_ishash_tcp(m)
 #define	M_HASHTYPE_SETINNER(m)	do {			\
 	(m)->m_pkthdr.rsstype |= M_HASHTYPE_INNER;	\
     } while (0)
diff --git a/tests/sys/sys/Makefile b/tests/sys/sys/Makefile
index a1b4e3234e1c..9993fdf64ed9 100644
--- a/tests/sys/sys/Makefile
+++ b/tests/sys/sys/Makefile
@@ -6,6 +6,7 @@ ATF_TESTS_C=	arb_test \
 		bitset_test \
 		bitstring_test \
 		buf_ring_test \
+		mbuf_test \
 		qmath_test \
 		queue_test \
 		rb_test \
diff --git a/tests/sys/sys/mbuf_test.c b/tests/sys/sys/mbuf_test.c
new file mode 100644
index 000000000000..3d77d4f04d4c
--- /dev/null
+++ b/tests/sys/sys/mbuf_test.c
@@ -0,0 +1,53 @@
+/*
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Copyright (c) 2026 Kevin Bowling
+ */
+
+#include <sys/param.h>
+#include <sys/mbuf.h>
+
+#include <stdbool.h>
+#include <stdint.h>
+
+#include <atf-c.h>
+
+ATF_TC_WITHOUT_HEAD(hashtype_ishash_tcp);
+ATF_TC_BODY(hashtype_ishash_tcp, tc)
+{
+	static const bool expected[UINT8_MAX + 1] = {
+		[M_HASHTYPE_RSS_TCP_IPV4] = true,
+		[M_HASHTYPE_RSS_TCP_IPV6] = true,
+		[M_HASHTYPE_RSS_TCP_IPV6_EX] = true,
+		[M_HASHTYPE_RSS_TCP_IPV4 | M_HASHTYPE_INNER] = true,
+		[M_HASHTYPE_RSS_TCP_IPV6 | M_HASHTYPE_INNER] = true,
+		[M_HASHTYPE_RSS_TCP_IPV6_EX | M_HASHTYPE_INNER] = true,
+	};
+	struct mbuf m = { 0 };
+
+	/* Only TCP four-tuple hashes qualify, including inner-header hashes. */
+	for (u_int type = 0; type < nitems(expected); type++) {
+		M_HASHTYPE_SET(&m, type);
+		ATF_CHECK_MSG(!!M_HASHTYPE_ISHASH_TCP(&m) == expected[type],
+		    "hash type %#x: expected %d", type, expected[type]);
+	}
+}
+
+ATF_TC_WITHOUT_HEAD(hashtype_ishash_tcp_evaluate_once);
+ATF_TC_BODY(hashtype_ishash_tcp_evaluate_once, tc)
+{
+	struct mbuf mbufs[3] = { 0 };
+	const struct mbuf *m;
+
+	m = mbufs;
+	ATF_CHECK(!M_HASHTYPE_ISHASH_TCP(m++));
+	ATF_CHECK(m == &mbufs[1]);
+}
+
+ATF_TP_ADD_TCS(tp)
+{
+	ATF_TP_ADD_TC(tp, hashtype_ishash_tcp);
+	ATF_TP_ADD_TC(tp, hashtype_ishash_tcp_evaluate_once);
+
+	return (atf_no_error());
+}