* [PATCH 1/7] net: mctp: usblib: Add to parameterized kunit tests.
2026-08-31 3:43 [PATCH 0/7] net: mctp: usblib: Increase coverage of kunit testing James Lee
@ 2026-08-31 3:43 ` James Lee
2026-08-31 3:43 ` [PATCH 2/7] net: mctp: usblib: Add test for splits inside headers James Lee
` (5 subsequent siblings)
6 siblings, 0 replies; 8+ messages in thread
From: James Lee @ 2026-08-31 3:43 UTC (permalink / raw)
To: Jeremy Kerr, Matt Johnston, Andrew Lunn, David S. Miller,
Eric Dumazet, Jakub Kicinski, Paolo Abeni, Kees Cook,
Gustavo A. R. Silva
Cc: netdev, linux-kernel, linux-hardening, James Lee
Add parameterized tests to cover worst case scenarios for packet
splitting and length.
Signed-off-by: James Lee <james@codeconstruct.com.au>
---
drivers/net/mctp/mctp-usblib-test.c | 8 +++++++-
1 file changed, 7 insertions(+), 1 deletion(-)
diff --git a/drivers/net/mctp/mctp-usblib-test.c b/drivers/net/mctp/mctp-usblib-test.c
index 9df401a914ff537207911f03bd08f32b5a5e20d1..4c27307900fb6116572bc9d89f3def6f06c30397 100644
--- a/drivers/net/mctp/mctp-usblib-test.c
+++ b/drivers/net/mctp/mctp-usblib-test.c
@@ -386,12 +386,18 @@ static const struct mctp_usblib_test_pkt_span mctp_usblib_test_pkt_spans[] = {
{ "1p2x-split-mctphdr", 1, { 16 }, 2, { 6, 10 } },
/* Single packet split over 3 transfers, middle entirely continuation */
{ "1p3x-split", 1, { 12 }, 3, { 4, 4, 4 } },
+ /* A packet split over 5 transfers, splitting on and between each header. */
+ { "1p5x-split", 1, { 12 }, 5, { 3, 1, 1, 3, 4}},
/* Max-sized single transfer */
{ "1p1x-large", 1, { 8191 }, 1, { 8191 } },
/* Two large packets, split at the worst-case for allocation, with a
* single byte continuing the span
*/
- { "2p2x-large-split", 2, { 8190, 8190 }, 2, { 8191, 8189 } },
+ { "2p2x-large-split", 2, { 8190, 8191 }, 2, { 8191, 8190 } },
+ /* Three large packets, split at the worst-case for allocation,
+ * with a single byte continuing each span
+ */
+ { "3p3x-large-split", 3, { 8190, 8191, 8191 }, 3, { 8191, 8191, 8190 } },
};
KUNIT_ARRAY_PARAM(mctp_usblib_test_rx_pkt_span, mctp_usblib_test_pkt_spans,
--
2.47.3
^ permalink raw reply related [flat|nested] 8+ messages in thread* [PATCH 2/7] net: mctp: usblib: Add test for splits inside headers
2026-08-31 3:43 [PATCH 0/7] net: mctp: usblib: Increase coverage of kunit testing James Lee
2026-08-31 3:43 ` [PATCH 1/7] net: mctp: usblib: Add to parameterized kunit tests James Lee
@ 2026-08-31 3:43 ` James Lee
2026-08-31 3:43 ` [PATCH 3/7] net: mctp: usblib: Add tests of invalid headers James Lee
` (4 subsequent siblings)
6 siblings, 0 replies; 8+ messages in thread
From: James Lee @ 2026-08-31 3:43 UTC (permalink / raw)
To: Jeremy Kerr, Matt Johnston, Andrew Lunn, David S. Miller,
Eric Dumazet, Jakub Kicinski, Paolo Abeni, Kees Cook,
Gustavo A. R. Silva
Cc: netdev, linux-kernel, linux-hardening, James Lee
Add test covering every possible split of a packet's headers between
transfers and remove parameterized tests that are now covered.
Signed-off-by: James Lee <james@codeconstruct.com.au>
---
drivers/net/mctp/mctp-usblib-test.c | 69 +++++++++++++++++++++++++++++++++----
1 file changed, 63 insertions(+), 6 deletions(-)
diff --git a/drivers/net/mctp/mctp-usblib-test.c b/drivers/net/mctp/mctp-usblib-test.c
index 4c27307900fb6116572bc9d89f3def6f06c30397..1a6dd6d10daab314f5e082477ecd3b9518f6b50e 100644
--- a/drivers/net/mctp/mctp-usblib-test.c
+++ b/drivers/net/mctp/mctp-usblib-test.c
@@ -14,6 +14,8 @@
#include <net/mctpdevice.h>
#include <linux/usb/mctp-usb.h>
+#define HDR_LEN sizeof(struct mctp_usb_hdr)
+
struct mctp_usblib_test_dev {
struct net_device *ndev;
struct mctp_dev *mdev;
@@ -378,12 +380,6 @@ static const struct mctp_usblib_test_pkt_span mctp_usblib_test_pkt_spans[] = {
{ "1p1x-complete", 1, { 8 }, 1, { 8 } },
/* Two small packets combined within one transfer */
{ "2p1x-combined", 2, { 8, 8 }, 1, { 16 } },
- /* A packet split over two transfers, at the MCTP payload */
- { "1p2x-split-payload", 1, { 16 }, 2, { 8, 8 } },
- /* A packet split over two transfers, at the USB transport header */
- { "1p2x-split-usbhdr", 1, { 16 }, 2, { 2, 14 } },
- /* A packet split over two transfers, at the MCTP header */
- { "1p2x-split-mctphdr", 1, { 16 }, 2, { 6, 10 } },
/* Single packet split over 3 transfers, middle entirely continuation */
{ "1p3x-split", 1, { 12 }, 3, { 4, 4, 4 } },
/* A packet split over 5 transfers, splitting on and between each header. */
@@ -403,10 +399,71 @@ static const struct mctp_usblib_test_pkt_span mctp_usblib_test_pkt_spans[] = {
KUNIT_ARRAY_PARAM(mctp_usblib_test_rx_pkt_span, mctp_usblib_test_pkt_spans,
mctp_usblib_test_pkt_span_to_desc);
+static void mctp_usblib_test_rx_split_header(struct kunit *test, size_t offset,
+ struct mctp_usblib_test_dev *dev,
+ struct mctp_usblib_rx *rx)
+{
+ struct sk_buff *skb;
+ size_t buflen, len;
+ u8 packet[16];
+ void *buf;
+ int rc;
+
+ len = sizeof(packet);
+ mctp_usblib_test_init_pkt(packet, len, len);
+
+ rc = mctp_usblib_rx_prepare(dev->ndev, rx, &buf, &buflen, GFP_KERNEL);
+ KUNIT_ASSERT_EQ(test, rc, 0);
+ KUNIT_ASSERT_GE(test, buflen, len);
+
+ memcpy(buf, packet, offset);
+ mctp_usblib_rx_complete(dev->ndev, rx, offset);
+
+ rc = mctp_usblib_rx_prepare(dev->ndev, rx, &buf, &buflen,
+ GFP_KERNEL);
+ KUNIT_ASSERT_EQ(test, rc, 0);
+ KUNIT_ASSERT_GE(test, buflen, len);
+ KUNIT_ASSERT_EQ(test, dev->rx_pkts.qlen, 0);
+
+ memcpy(buf, packet + offset, len - offset);
+ mctp_usblib_rx_complete(dev->ndev, rx, len - offset);
+ KUNIT_EXPECT_EQ(test, dev->rx_pkts.qlen, 1);
+
+ skb = __skb_dequeue(&dev->rx_pkts);
+ KUNIT_EXPECT_NOT_NULL(test, skb);
+ if (skb)
+ KUNIT_EXPECT_EQ(test, skb->len, len - HDR_LEN);
+}
+
+static void mctp_usblib_test_rx_header_splits(struct kunit *test)
+{
+ struct mctp_usblib_test_dev *dev;
+ struct mctp_usblib_test_ctx *ctx;
+ struct mctp_usblib_rx *rx;
+ size_t i;
+
+ ctx = mctp_usblib_test_init(test);
+ rx = mctp_usblib_test_rx_init(test, true);
+ dev = ctx->dev;
+
+ /* Unrolling here so stack traces point to the invocation with the
+ * failing length.
+ */
+ mctp_usblib_test_rx_split_header(test, 1, dev, rx);
+ mctp_usblib_test_rx_split_header(test, 2, dev, rx);
+ mctp_usblib_test_rx_split_header(test, 3, dev, rx);
+ mctp_usblib_test_rx_split_header(test, 4, dev, rx);
+ mctp_usblib_test_rx_split_header(test, 5, dev, rx);
+ mctp_usblib_test_rx_split_header(test, 6, dev, rx);
+ mctp_usblib_test_rx_split_header(test, 7, dev, rx);
+ mctp_usblib_test_rx_split_header(test, 8, dev, rx);
+}
+
static struct kunit_case mctp_usblib_test_cases[] = {
KUNIT_CASE(mctp_usblib_test_rx_single),
KUNIT_CASE_PARAM(mctp_usblib_test_rx_pkt_span,
mctp_usblib_test_rx_pkt_span_gen_params),
+ KUNIT_CASE(mctp_usblib_test_rx_header_splits),
{}
};
--
2.47.3
^ permalink raw reply related [flat|nested] 8+ messages in thread* [PATCH 3/7] net: mctp: usblib: Add tests of invalid headers
2026-08-31 3:43 [PATCH 0/7] net: mctp: usblib: Increase coverage of kunit testing James Lee
2026-08-31 3:43 ` [PATCH 1/7] net: mctp: usblib: Add to parameterized kunit tests James Lee
2026-08-31 3:43 ` [PATCH 2/7] net: mctp: usblib: Add test for splits inside headers James Lee
@ 2026-08-31 3:43 ` James Lee
2026-08-31 3:43 ` [PATCH 4/7] net: mctp: usblib: Complete rx tests James Lee
` (3 subsequent siblings)
6 siblings, 0 replies; 8+ messages in thread
From: James Lee @ 2026-08-31 3:43 UTC (permalink / raw)
To: Jeremy Kerr, Matt Johnston, Andrew Lunn, David S. Miller,
Eric Dumazet, Jakub Kicinski, Paolo Abeni, Kees Cook,
Gustavo A. R. Silva
Cc: netdev, linux-kernel, linux-hardening, James Lee
Add tests where the length field of the USB transport header is below
the minimum value, and where the DMTF ID is invalid.
Signed-off-by: James Lee <james@codeconstruct.com.au>
---
drivers/net/mctp/mctp-usblib-test.c | 69 ++++++++++++++++++++++++++++++++++++-
1 file changed, 68 insertions(+), 1 deletion(-)
diff --git a/drivers/net/mctp/mctp-usblib-test.c b/drivers/net/mctp/mctp-usblib-test.c
index 1a6dd6d10daab314f5e082477ecd3b9518f6b50e..4f499de9a6ce4452dcc71ac38fa26b6595977f77 100644
--- a/drivers/net/mctp/mctp-usblib-test.c
+++ b/drivers/net/mctp/mctp-usblib-test.c
@@ -440,7 +440,6 @@ static void mctp_usblib_test_rx_header_splits(struct kunit *test)
struct mctp_usblib_test_dev *dev;
struct mctp_usblib_test_ctx *ctx;
struct mctp_usblib_rx *rx;
- size_t i;
ctx = mctp_usblib_test_init(test);
rx = mctp_usblib_test_rx_init(test, true);
@@ -459,11 +458,79 @@ static void mctp_usblib_test_rx_header_splits(struct kunit *test)
mctp_usblib_test_rx_split_header(test, 8, dev, rx);
}
+/* Test the submission of a packet with an impossibly small value in the
+ * header's length field. Values less than HDR_LEN are invalid.
+ */
+static void mctp_usblib_test_rx_short_packet(struct kunit *test)
+{
+ struct mctp_usblib_test_dev *dev;
+ struct mctp_usblib_test_ctx *ctx;
+ struct mctp_usblib_rx *rx;
+ size_t len, buflen;
+ u8 pktbuf[12];
+ void *buf;
+ int rc;
+
+ ctx = mctp_usblib_test_init(test);
+ rx = mctp_usblib_test_rx_init(test, true);
+ dev = ctx->dev;
+
+ len = sizeof(pktbuf);
+ mctp_usblib_test_init_pkt(pktbuf, len, HDR_LEN - 1);
+
+ buflen = 0;
+ rc = mctp_usblib_rx_prepare(dev->ndev, rx, &buf, &buflen, GFP_KERNEL);
+ KUNIT_ASSERT_EQ(test, rc, 0);
+ KUNIT_ASSERT_GE(test, buflen, len);
+
+ memcpy(buf, pktbuf, len);
+
+ rc = mctp_usblib_rx_complete(dev->ndev, rx, len);
+ KUNIT_EXPECT_EQ(test, rc, -EPROTO);
+ KUNIT_EXPECT_NULL(test, rx->skb);
+ KUNIT_EXPECT_EQ(test, dev->rx_pkts.qlen, 0);
+}
+
+static void mctp_usblib_test_rx_invalid_dmtf_id(struct kunit *test)
+{
+ struct mctp_usblib_test_dev *dev;
+ struct mctp_usblib_test_ctx *ctx;
+ struct mctp_usblib_rx *rx;
+ size_t len, buflen;
+ u8 pktbuf[12];
+ void *buf;
+ int rc;
+
+ ctx = mctp_usblib_test_init(test);
+ rx = mctp_usblib_test_rx_init(test, true);
+ dev = ctx->dev;
+
+ len = sizeof(pktbuf);
+ mctp_usblib_test_init_pkt(pktbuf, len, len);
+
+ // Make packet DMTF ID invalid
+ pktbuf[1] = ~pktbuf[1];
+
+ buflen = 0;
+ rc = mctp_usblib_rx_prepare(dev->ndev, rx, &buf, &buflen, GFP_KERNEL);
+ KUNIT_ASSERT_EQ(test, rc, 0);
+ KUNIT_ASSERT_GE(test, buflen, len);
+
+ memcpy(buf, pktbuf, len);
+
+ rc = mctp_usblib_rx_complete(dev->ndev, rx, len);
+ KUNIT_EXPECT_EQ(test, rc, -EPROTO);
+ KUNIT_EXPECT_NULL(test, rx->skb);
+ KUNIT_EXPECT_EQ(test, dev->rx_pkts.qlen, 0);
+}
+
static struct kunit_case mctp_usblib_test_cases[] = {
KUNIT_CASE(mctp_usblib_test_rx_single),
KUNIT_CASE_PARAM(mctp_usblib_test_rx_pkt_span,
mctp_usblib_test_rx_pkt_span_gen_params),
KUNIT_CASE(mctp_usblib_test_rx_header_splits),
+ KUNIT_CASE(mctp_usblib_test_rx_short_packet),
+ KUNIT_CASE(mctp_usblib_test_rx_invalid_dmtf_id),
{}
};
--
2.47.3
^ permalink raw reply related [flat|nested] 8+ messages in thread* [PATCH 4/7] net: mctp: usblib: Complete rx tests
2026-08-31 3:43 [PATCH 0/7] net: mctp: usblib: Increase coverage of kunit testing James Lee
` (2 preceding siblings ...)
2026-08-31 3:43 ` [PATCH 3/7] net: mctp: usblib: Add tests of invalid headers James Lee
@ 2026-08-31 3:43 ` James Lee
2026-08-31 3:43 ` [PATCH 5/7] net: mctp: usblib: Simplify allocation logic in mctp_usblib_test_rx_init James Lee
` (2 subsequent siblings)
6 siblings, 0 replies; 8+ messages in thread
From: James Lee @ 2026-08-31 3:43 UTC (permalink / raw)
To: Jeremy Kerr, Matt Johnston, Andrew Lunn, David S. Miller,
Eric Dumazet, Jakub Kicinski, Paolo Abeni, Kees Cook,
Gustavo A. R. Silva
Cc: netdev, linux-kernel, linux-hardening, James Lee
Add rx tests without spanning, covering cases where a packet is shorter
than allowed without spanning, isn't completed within one submission.
Fully cover rx functions except for memory allocation failures and
trivial functions.
Signed-off-by: James Lee <james@codeconstruct.com.au>
---
drivers/net/mctp/mctp-usblib-test.c | 62 +++++++++++++++++++++++++++++++++++++
1 file changed, 62 insertions(+)
diff --git a/drivers/net/mctp/mctp-usblib-test.c b/drivers/net/mctp/mctp-usblib-test.c
index 4f499de9a6ce4452dcc71ac38fa26b6595977f77..ab323a31161a415d93eafe4314e0e52cedfcd42e 100644
--- a/drivers/net/mctp/mctp-usblib-test.c
+++ b/drivers/net/mctp/mctp-usblib-test.c
@@ -524,6 +524,66 @@ static void mctp_usblib_test_rx_invalid_dmtf_id(struct kunit *test)
KUNIT_EXPECT_EQ(test, dev->rx_pkts.qlen, 0);
}
+static void mctp_usblib_test_rx_nonspanning_tiny(struct kunit *test)
+{
+ struct mctp_usblib_test_dev *dev;
+ struct mctp_usblib_test_ctx *ctx;
+ struct mctp_usblib_rx *rx;
+ size_t len, buflen;
+ u8 pktbuf[3];
+ void *buf;
+ int rc;
+
+ ctx = mctp_usblib_test_init(test);
+ rx = mctp_usblib_test_rx_init(test, false);
+ dev = ctx->dev;
+
+ len = sizeof(pktbuf);
+ mctp_usblib_test_init_pkt(pktbuf, len, len);
+
+ buflen = 0;
+ rc = mctp_usblib_rx_prepare(dev->ndev, rx, &buf, &buflen, GFP_KERNEL);
+ KUNIT_ASSERT_EQ(test, rc, 0);
+ KUNIT_ASSERT_GE(test, buflen, len);
+
+ memcpy(buf, pktbuf, len);
+
+ rc = mctp_usblib_rx_complete(dev->ndev, rx, len);
+ KUNIT_EXPECT_EQ(test, rc, -ENOMSG);
+ KUNIT_EXPECT_NULL(test, rx->skb);
+ KUNIT_EXPECT_EQ(test, dev->rx_pkts.qlen, 0);
+}
+
+static void mctp_usblib_test_rx_nonspanning_partial(struct kunit *test)
+{
+ struct mctp_usblib_test_dev *dev;
+ struct mctp_usblib_test_ctx *ctx;
+ struct mctp_usblib_rx *rx;
+ size_t len, buflen;
+ u8 pktbuf[20];
+ void *buf;
+ int rc;
+
+ ctx = mctp_usblib_test_init(test);
+ rx = mctp_usblib_test_rx_init(test, false);
+ dev = ctx->dev;
+
+ len = sizeof(pktbuf);
+ mctp_usblib_test_init_pkt(pktbuf, len, len + 1);
+
+ buflen = 0;
+ rc = mctp_usblib_rx_prepare(dev->ndev, rx, &buf, &buflen, GFP_KERNEL);
+ KUNIT_ASSERT_EQ(test, rc, 0);
+ KUNIT_ASSERT_GE(test, buflen, len);
+
+ memcpy(buf, pktbuf, len);
+
+ rc = mctp_usblib_rx_complete(dev->ndev, rx, len);
+ KUNIT_EXPECT_EQ(test, rc, -EPROTO);
+ KUNIT_EXPECT_NULL(test, rx->skb);
+ KUNIT_EXPECT_EQ(test, dev->rx_pkts.qlen, 0);
+}
+
static struct kunit_case mctp_usblib_test_cases[] = {
KUNIT_CASE(mctp_usblib_test_rx_single),
KUNIT_CASE_PARAM(mctp_usblib_test_rx_pkt_span,
@@ -531,6 +591,8 @@ static struct kunit_case mctp_usblib_test_cases[] = {
KUNIT_CASE(mctp_usblib_test_rx_header_splits),
KUNIT_CASE(mctp_usblib_test_rx_short_packet),
KUNIT_CASE(mctp_usblib_test_rx_invalid_dmtf_id),
+ KUNIT_CASE(mctp_usblib_test_rx_nonspanning_tiny),
+ KUNIT_CASE(mctp_usblib_test_rx_nonspanning_partial),
{}
};
--
2.47.3
^ permalink raw reply related [flat|nested] 8+ messages in thread* [PATCH 5/7] net: mctp: usblib: Simplify allocation logic in mctp_usblib_test_rx_init
2026-08-31 3:43 [PATCH 0/7] net: mctp: usblib: Increase coverage of kunit testing James Lee
` (3 preceding siblings ...)
2026-08-31 3:43 ` [PATCH 4/7] net: mctp: usblib: Complete rx tests James Lee
@ 2026-08-31 3:43 ` James Lee
2026-08-31 3:43 ` [PATCH 6/7] net: mctp: usblib: Add initial kunit tx tests James Lee
2026-08-31 3:43 ` [PATCH 7/7] net: mctp: usblib: Add test for failing append James Lee
6 siblings, 0 replies; 8+ messages in thread
From: James Lee @ 2026-08-31 3:43 UTC (permalink / raw)
To: Jeremy Kerr, Matt Johnston, Andrew Lunn, David S. Miller,
Eric Dumazet, Jakub Kicinski, Paolo Abeni, Kees Cook,
Gustavo A. R. Silva
Cc: netdev, linux-kernel, linux-hardening, James Lee
The if statement testing rx's allocation is unneeded.
Signed-off-by: James Lee <james@codeconstruct.com.au>
---
drivers/net/mctp/mctp-usblib-test.c | 6 ++----
1 file changed, 2 insertions(+), 4 deletions(-)
diff --git a/drivers/net/mctp/mctp-usblib-test.c b/drivers/net/mctp/mctp-usblib-test.c
index ab323a31161a415d93eafe4314e0e52cedfcd42e..772484df32a7d7b4d30e51808e06853febe272b6 100644
--- a/drivers/net/mctp/mctp-usblib-test.c
+++ b/drivers/net/mctp/mctp-usblib-test.c
@@ -202,11 +202,9 @@ mctp_usblib_test_rx_init(struct kunit *test, bool span)
int rc;
rx = kzalloc_obj(*rx);
- if (rx) {
- rc = kunit_add_action_or_reset(test, action_rx_fini, rx);
- KUNIT_ASSERT_EQ(test, rc, 0);
- }
KUNIT_ASSERT_NOT_NULL(test, rx);
+ rc = kunit_add_action_or_reset(test, action_rx_fini, rx);
+ KUNIT_ASSERT_EQ(test, rc, 0);
rc = mctp_usblib_rx_init(rx, ep_maxpacket, span);
KUNIT_ASSERT_EQ(test, rc, 0);
--
2.47.3
^ permalink raw reply related [flat|nested] 8+ messages in thread* [PATCH 6/7] net: mctp: usblib: Add initial kunit tx tests
2026-08-31 3:43 [PATCH 0/7] net: mctp: usblib: Increase coverage of kunit testing James Lee
` (4 preceding siblings ...)
2026-08-31 3:43 ` [PATCH 5/7] net: mctp: usblib: Simplify allocation logic in mctp_usblib_test_rx_init James Lee
@ 2026-08-31 3:43 ` James Lee
2026-08-31 3:43 ` [PATCH 7/7] net: mctp: usblib: Add test for failing append James Lee
6 siblings, 0 replies; 8+ messages in thread
From: James Lee @ 2026-08-31 3:43 UTC (permalink / raw)
To: Jeremy Kerr, Matt Johnston, Andrew Lunn, David S. Miller,
Eric Dumazet, Jakub Kicinski, Paolo Abeni, Kees Cook,
Gustavo A. R. Silva
Cc: netdev, linux-kernel, linux-hardening, James Lee
Add tests for the transmit path, where MCTP packets are handed to
outgoing USB transfer data. Testing a spanning transfer that is expected
to succeed, the failure paths when sends fail, and large sequential
sends.
Signed-off-by: James Lee <james@codeconstruct.com.au>
---
drivers/net/mctp/mctp-usblib-test.c | 292 ++++++++++++++++++++++++++++++++++++
1 file changed, 292 insertions(+)
diff --git a/drivers/net/mctp/mctp-usblib-test.c b/drivers/net/mctp/mctp-usblib-test.c
index 772484df32a7d7b4d30e51808e06853febe272b6..7dc382caffc17644dd616f78df332201d412f709 100644
--- a/drivers/net/mctp/mctp-usblib-test.c
+++ b/drivers/net/mctp/mctp-usblib-test.c
@@ -6,6 +6,7 @@
* Copyright (C) 2026 Code Construct Pty Ltd
*/
+#include <linux/array_size.h>
#include <uapi/linux/netdevice.h>
#include <linux/netdevice.h>
#include <kunit/test.h>
@@ -16,6 +17,13 @@
#define HDR_LEN sizeof(struct mctp_usb_hdr)
+struct tx_buff {
+ struct list_head list;
+
+ size_t length;
+ u8 data[] __counted_by(length);
+};
+
struct mctp_usblib_test_dev {
struct net_device *ndev;
struct mctp_dev *mdev;
@@ -24,9 +32,110 @@ struct mctp_usblib_test_dev {
struct mctp_usblib_test_ctx {
struct mctp_usblib_test_dev *dev;
+ struct list_head tx_xfers;
struct mctp_route rt;
};
+static int mctp_usblib_test_tx_send(struct mctp_usblib_tx_ctx *tx_ctx,
+ void *data, size_t len)
+{
+ struct mctp_usblib_test_ctx *ctx;
+ struct tx_buff *new_node;
+ struct net_device *ndev;
+ int rc;
+
+ ctx = mctp_usblib_tx_ctx_priv(tx_ctx);
+ ndev = ctx->dev->ndev;
+ rc = 0;
+
+ new_node = kzalloc_flex(*new_node, data, len, GFP_KERNEL);
+ if (!new_node) {
+ rc = -ENOMEM;
+ goto exit;
+ }
+
+ new_node->length = len;
+ memcpy(&new_node->data, data, len);
+ list_add_tail(&new_node->list, &ctx->tx_xfers);
+
+exit:
+ mctp_usblib_tx_send_complete(tx_ctx, ndev, rc == 0);
+ return rc;
+}
+
+static int mctp_usblib_test_tx_send_fail(struct mctp_usblib_tx_ctx *tx_ctx,
+ void *data, size_t len)
+{
+ return -ENOMEM;
+}
+
+static u8 *mctp_usblib_test_flatten_tx_buff(struct kunit *test,
+ struct list_head *in,
+ size_t *length_out)
+{
+ struct tx_buff *pos;
+ size_t length;
+ u8 *buf, *tail;
+
+ KUNIT_ASSERT_TRUE(test, length_out);
+ KUNIT_ASSERT_TRUE(test, in);
+
+ length = 0;
+ list_for_each_entry(pos, in, list)
+ length = size_add(length, pos->length);
+
+ KUNIT_ASSERT_NE(test, length, 0);
+ KUNIT_ASSERT_NE(test, length, SIZE_MAX);
+
+ buf = kunit_kzalloc(test, length, GFP_KERNEL);
+ KUNIT_ASSERT_NOT_NULL(test, buf);
+
+ tail = buf;
+ list_for_each_entry(pos, in, list) {
+ memcpy(tail, pos->data, pos->length);
+ tail += pos->length;
+ }
+
+ *length_out = length;
+ return buf;
+}
+
+static u8 *mctp_usblib_test_init_buf(struct kunit *test, size_t length)
+{
+ u8 *buffer;
+ size_t i;
+
+ buffer = kunit_kzalloc(test, length, GFP_KERNEL);
+ KUNIT_ASSERT_NOT_NULL(test, buffer);
+
+ for (i = 0; i < length; i++)
+ buffer[i] = i % 256;
+
+ return buffer;
+}
+
+static void mctp_usblib_test_fill_head(struct mctp_usb_hdr *head, size_t len)
+{
+ len += HDR_LEN;
+ head->id = cpu_to_be16(MCTP_USB_DMTF_ID);
+ head->len = cpu_to_be16(len & MCTP_USB_1_1_PKTLEN_MAX);
+}
+
+static struct sk_buff *mctp_usblib_test_init_skb(struct kunit *test,
+ unsigned int length,
+ struct net_device *ndev,
+ void *data)
+{
+ struct sk_buff *skb;
+
+ skb = __netdev_alloc_skb(ndev, length, GFP_KERNEL);
+ KUNIT_ASSERT_NOT_NULL(test, skb);
+
+ skb->len = length;
+ memcpy(skb->data, data, length);
+ return skb;
+}
+
static netdev_tx_t mctp_usblib_dev_tx(struct sk_buff *skb,
struct net_device *ndev)
{
@@ -119,6 +228,7 @@ static int mctp_usblib_test_dst_output(struct mctp_dst *dst,
static void mctp_usblib_test_fini_action(void *data)
{
struct mctp_usblib_test_ctx *ctx = data;
+ struct tx_buff *curr, *temp;
/* The device will have been destroyed, so ->rt will be unlinked.
* Just ensure that the refcount is as expected.
@@ -126,6 +236,8 @@ static void mctp_usblib_test_fini_action(void *data)
KUNIT_EXPECT_TRUE(current->kunit_test,
refcount_dec_and_test(&ctx->rt.refs));
+ list_for_each_entry_safe(curr, temp, &ctx->tx_xfers, list)
+ kfree(curr);
kfree(ctx);
}
@@ -141,6 +253,7 @@ static struct mctp_usblib_test_ctx *mctp_usblib_test_init(struct kunit *test)
INIT_LIST_HEAD(&ctx->rt.list);
rt = &ctx->rt;
refcount_set(&rt->refs, 1);
+ INIT_LIST_HEAD(&ctx->tx_xfers);
rc = kunit_add_action_or_reset(test, mctp_usblib_test_fini_action, ctx);
KUNIT_ASSERT_EQ(test, rc, 0);
@@ -227,6 +340,32 @@ static int mctp_usblib_test_rx_complete(struct net_device *netdev,
return rc;
}
+static void action_tx_fini(void *data)
+{
+ struct mctp_usblib_tx *tx = data;
+
+ mctp_usblib_tx_fini(tx);
+ kfree(tx);
+}
+
+static struct mctp_usblib_tx *
+mctp_usblib_test_tx_init(struct kunit *test,
+ const struct mctp_usblib_tx_ops *ops,
+ void *priv, bool span)
+{
+ struct mctp_usblib_tx *tx;
+ int rc;
+
+ tx = kzalloc_obj(*tx);
+ KUNIT_ASSERT_NOT_NULL(test, tx);
+ rc = kunit_add_action_or_reset(test, action_tx_fini, tx);
+ KUNIT_ASSERT_EQ(test, rc, 0);
+
+ mctp_usblib_tx_init(tx, ops, priv, true);
+
+ return tx;
+}
+
/* Single packet, starting on a transfer boundary, contained entirely within
* the transfer
*/
@@ -582,6 +721,156 @@ static void mctp_usblib_test_rx_nonspanning_partial(struct kunit *test)
KUNIT_EXPECT_EQ(test, dev->rx_pkts.qlen, 0);
}
+static void mctp_usblib_test_tx_pkt_span(struct kunit *test)
+{
+ struct mctp_usblib_test_ctx *ctx;
+ struct mctp_usblib_tx_ops ops;
+ struct mctp_usblib_tx *tx;
+ struct mctp_usb_hdr head;
+ struct net_device *ndev;
+ struct sk_buff *skb;
+ size_t len, tx_len;
+ u8 *buf, *flat_tx;
+ int rc;
+
+ len = 1000;
+
+ ctx = mctp_usblib_test_init(test);
+ ndev = ctx->dev->ndev;
+
+ ops.send = mctp_usblib_test_tx_send;
+
+ tx = mctp_usblib_test_tx_init(test, &ops, ctx, true);
+
+ buf = mctp_usblib_test_init_buf(test, len);
+ mctp_usblib_test_fill_head(&head, len);
+
+ skb = mctp_usblib_test_init_skb(test, len, ndev, buf);
+
+ rc = mctp_usblib_tx_push(ndev, tx, skb, false);
+ KUNIT_ASSERT_EQ(test, rc, 0);
+ KUNIT_ASSERT_FALSE(test, list_empty(&ctx->tx_xfers));
+
+ flat_tx = mctp_usblib_test_flatten_tx_buff(test, &ctx->tx_xfers,
+ &tx_len);
+ KUNIT_ASSERT_NOT_NULL(test, flat_tx);
+
+ KUNIT_EXPECT_EQ(test, tx_len, len + HDR_LEN);
+ KUNIT_EXPECT_MEMEQ(test, flat_tx, &head, HDR_LEN);
+ KUNIT_EXPECT_MEMEQ(test, flat_tx + HDR_LEN, buf, len);
+}
+
+static void mctp_usblib_test_tx_failing_send(struct kunit *test)
+{
+ struct mctp_usblib_test_ctx *ctx;
+ struct mctp_usblib_tx_ops ops;
+ struct mctp_usblib_tx *tx;
+ struct net_device *ndev;
+ struct sk_buff *skb;
+ size_t len;
+ u8 *buf;
+ int rc;
+
+ len = 100;
+
+ ctx = mctp_usblib_test_init(test);
+ ndev = ctx->dev->ndev;
+
+ ops.send = mctp_usblib_test_tx_send_fail;
+
+ tx = mctp_usblib_test_tx_init(test, &ops, ctx, false);
+ buf = mctp_usblib_test_init_buf(test, len);
+ skb = mctp_usblib_test_init_skb(test, len, ndev, buf);
+
+ /* Doesn't call ops.send as more packets are expected,
+ * so the push shouldn't fail.
+ */
+ rc = mctp_usblib_tx_push(ndev, tx, skb, true);
+ KUNIT_ASSERT_EQ(test, rc, 0);
+
+ skb = mctp_usblib_test_init_skb(test, len, ndev, buf);
+
+ /* Calls ops.send as no further packets are expected. */
+ rc = mctp_usblib_tx_push(ndev, tx, skb, false);
+ KUNIT_EXPECT_EQ(test, rc, 0);
+ KUNIT_EXPECT_NULL(test, tx->cur_ctx);
+ KUNIT_EXPECT_TRUE(test, list_empty(&ctx->tx_xfers));
+}
+
+/* Test sending multiple packets in the same transfer, followed by one that
+ * spans multiple subsequent transfers.
+ */
+static void mctp_usblib_test_tx_multi_push(struct kunit *test)
+{
+ struct mctp_usblib_test_ctx *ctx;
+ size_t i, max_length, tx_length;
+ struct mctp_usblib_tx_ops ops;
+ u8 *buf, *flat_tx, *index;
+ struct mctp_usblib_tx *tx;
+ struct net_device *ndev;
+ struct sk_buff *skb;
+ const struct {
+ size_t len;
+ bool more;
+ } sends[] = {
+ { 1000, true },
+ { 500, false },
+ { 5000, false },
+ };
+ int rc;
+
+ static_assert(!sends[ARRAY_SIZE(sends) - 1].more,
+ "The last push must claim there will be no more");
+
+ max_length = 0;
+ for (i = 0; i < ARRAY_SIZE(sends); i++) {
+ if (sends[i].len > max_length)
+ max_length = sends[i].len;
+ }
+
+ ctx = mctp_usblib_test_init(test);
+ ndev = ctx->dev->ndev;
+
+ ops.send = mctp_usblib_test_tx_send;
+
+ tx = mctp_usblib_test_tx_init(test, &ops, ctx, true);
+ buf = mctp_usblib_test_init_buf(test, max_length);
+
+ for (i = 0; i < ARRAY_SIZE(sends); i++) {
+ skb = mctp_usblib_test_init_skb(test, sends[i].len, ndev, buf);
+
+ rc = mctp_usblib_tx_push(ndev, tx, skb, sends[i].more);
+ KUNIT_ASSERT_EQ(test, rc, 0);
+ }
+ KUNIT_ASSERT_FALSE(test, list_empty(&ctx->tx_xfers));
+
+ flat_tx = mctp_usblib_test_flatten_tx_buff(test, &ctx->tx_xfers,
+ &tx_length);
+
+ for (i = 0, index = flat_tx; i < ARRAY_SIZE(sends); i++) {
+ size_t length_to_check, remaining_bytes;
+ struct mctp_usb_hdr head;
+
+ if (index - flat_tx >= tx_length - HDR_LEN)
+ break;
+
+ mctp_usblib_test_fill_head(&head, sends[i].len);
+ KUNIT_EXPECT_MEMEQ(test, index, &head, HDR_LEN);
+ index += HDR_LEN;
+ remaining_bytes = tx_length - (index - flat_tx);
+
+ length_to_check = sends[i].len;
+ KUNIT_EXPECT_GE(test, remaining_bytes, length_to_check);
+ length_to_check = min(remaining_bytes, length_to_check);
+
+ KUNIT_EXPECT_MEMEQ(test, index,
+ buf, length_to_check);
+
+ index += length_to_check;
+ }
+ KUNIT_EXPECT_EQ(test, i, ARRAY_SIZE(sends));
+}
+
static struct kunit_case mctp_usblib_test_cases[] = {
KUNIT_CASE(mctp_usblib_test_rx_single),
KUNIT_CASE_PARAM(mctp_usblib_test_rx_pkt_span,
@@ -591,6 +880,9 @@ static struct kunit_case mctp_usblib_test_cases[] = {
KUNIT_CASE(mctp_usblib_test_rx_invalid_dmtf_id),
KUNIT_CASE(mctp_usblib_test_rx_nonspanning_tiny),
KUNIT_CASE(mctp_usblib_test_rx_nonspanning_partial),
+ KUNIT_CASE(mctp_usblib_test_tx_pkt_span),
+ KUNIT_CASE(mctp_usblib_test_tx_multi_push),
+ KUNIT_CASE(mctp_usblib_test_tx_failing_send),
{}
};
--
2.47.3
^ permalink raw reply related [flat|nested] 8+ messages in thread* [PATCH 7/7] net: mctp: usblib: Add test for failing append
2026-08-31 3:43 [PATCH 0/7] net: mctp: usblib: Increase coverage of kunit testing James Lee
` (5 preceding siblings ...)
2026-08-31 3:43 ` [PATCH 6/7] net: mctp: usblib: Add initial kunit tx tests James Lee
@ 2026-08-31 3:43 ` James Lee
6 siblings, 0 replies; 8+ messages in thread
From: James Lee @ 2026-08-31 3:43 UTC (permalink / raw)
To: Jeremy Kerr, Matt Johnston, Andrew Lunn, David S. Miller,
Eric Dumazet, Jakub Kicinski, Paolo Abeni, Kees Cook,
Gustavo A. R. Silva
Cc: netdev, linux-kernel, linux-hardening, James Lee
Add test ensuring that network device stats are updated appropriately
when a previously pushed packet fails to send during a subsequent push.
Signed-off-by: James Lee <james@codeconstruct.com.au>
---
drivers/net/mctp/mctp-usblib-test.c | 42 +++++++++++++++++++++++++++++++++++++
1 file changed, 42 insertions(+)
diff --git a/drivers/net/mctp/mctp-usblib-test.c b/drivers/net/mctp/mctp-usblib-test.c
index 7dc382caffc17644dd616f78df332201d412f709..b3a2cc5cbd99defde308cf23a1b559dbdc73ca22 100644
--- a/drivers/net/mctp/mctp-usblib-test.c
+++ b/drivers/net/mctp/mctp-usblib-test.c
@@ -6,6 +6,7 @@
* Copyright (C) 2026 Code Construct Pty Ltd
*/
+#include "linux/percpu-defs.h"
#include <linux/array_size.h>
#include <uapi/linux/netdevice.h>
#include <linux/netdevice.h>
@@ -871,6 +872,46 @@ static void mctp_usblib_test_tx_multi_push(struct kunit *test)
KUNIT_EXPECT_EQ(test, i, ARRAY_SIZE(sends));
}
+static void mctp_usblib_test_tx_overflow(struct kunit *test)
+{
+ struct mctp_usblib_test_ctx *ctx;
+ struct mctp_usblib_tx_ops ops;
+ struct pcpu_dstats *dstats;
+ struct mctp_usblib_tx *tx;
+ struct net_device *ndev;
+ struct sk_buff *skb;
+ unsigned int start;
+ size_t len, i;
+ u64 stats;
+ u8 *buf;
+ int rc;
+
+ len = 200;
+
+ ctx = mctp_usblib_test_init(test);
+ ndev = ctx->dev->ndev;
+
+ ops.send = mctp_usblib_test_tx_send_fail;
+
+ tx = mctp_usblib_test_tx_init(test, &ops, ctx, false);
+ buf = mctp_usblib_test_init_buf(test, len);
+
+ for (i = 0; i < 3; i++) {
+ skb = mctp_usblib_test_init_skb(test, len, ndev, buf);
+
+ rc = mctp_usblib_tx_push(ndev, tx, skb, i != 2);
+ KUNIT_EXPECT_EQ(test, rc, 0);
+ }
+
+ dstats = get_cpu_ptr(ndev->dstats);
+ do {
+ start = u64_stats_fetch_begin(&dstats->syncp);
+ stats = u64_stats_read(&dstats->tx_drops);
+ } while (u64_stats_fetch_retry(&dstats->syncp, start));
+
+ KUNIT_EXPECT_EQ(test, stats, 3);
+}
+
static struct kunit_case mctp_usblib_test_cases[] = {
KUNIT_CASE(mctp_usblib_test_rx_single),
KUNIT_CASE_PARAM(mctp_usblib_test_rx_pkt_span,
@@ -883,6 +924,7 @@ static struct kunit_case mctp_usblib_test_cases[] = {
KUNIT_CASE(mctp_usblib_test_tx_pkt_span),
KUNIT_CASE(mctp_usblib_test_tx_multi_push),
KUNIT_CASE(mctp_usblib_test_tx_failing_send),
+ KUNIT_CASE(mctp_usblib_test_tx_overflow),
{}
};
--
2.47.3
^ permalink raw reply related [flat|nested] 8+ messages in thread