summaryrefslogtreecommitdiff
path: root/drivers/net/mctp
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@linux-foundation.org>2026-10-22 15:58:00 -1000
committerLinus Torvalds <torvalds@linux-foundation.org>2026-10-22 15:58:00 -1000
commit734bef548a96e8774d6eef5dfa2026c6edc4e80b (patch)
treece2520b2602ce3b82f9842490d21836f9838d441 /drivers/net/mctp
parent864c07de0c7dcbca58b61625dfa2e461ae63b242 (diff)
parent1a2c35c49cc7c6be860038b8442b52424a8f123c (diff)
downloadlinux-next-stable.tar.gz
linux-next-stable.zip
Merge tag 'net-next-7.4' of git://git.kernel.org/pub/scm/linux/kernel/git/netdev/net-nextstable
Pull networking updates from Paolo Abeni: "This includes a significant number of low-priority fixes - especially in the NIC drivers area - that we deferred from the last few RCs in the previous cycle, which can't be easily outlined in the synopsis below. Core & protocols: - Continue the work on removing rtnl_lock dependencies: - per-netns ARP and ND neighbour tables, with the default settings now configurable per netns (inheritance from init_net is controlled by the new net.core.neigh_inherit_init_net sysctl) - RCU-protected configuration and rtnl_lock-less fill_info() for hsr, netkit, sit, vlan and vxlan devices - per-netns device unregistration for ip tunnel and gre devices - use the netdev instance lock instead of rtnl_lock when reading speed and duplex via sysfs - batch the flushing of uncached routes at device unregistration time, relieving rtnl_lock contention on netns dismantle. - Add hardware pacing offload support, admin-controlled via a new rtnetlink attribute, with sch_fq integration (implemented in idpf). - Re-segment BIG TCP GSO packets into GSO packets fitting the egress device limits instead of plain MSS-sized segments, improving throughput for mixed BIG TCP / non-BIG TCP paths by up to 70%. - Generate RSS keys that spread flows over all the RX queues; the previous random keys could leave some queues unused. - Optimize the bridge VLAN forwarding paths: cache the port-VLAN in fdb entries and consider only VLAN members when flooding, for ~10% more Mpps in the fdb forwarding path. - A few TCP/IP performance improvements aimed at efficient cache usage and reducing contention. - Add the TCP ROCCET congestion control, a CUBIC variant aimed at avoiding bufferbloat in cellular networks. - Continue the conversion of getsockopt callbacks to sockopt_t. - Add an ethtool command to list the front-facing ports of an interface, including SFP cages and modules, using the phy_port representation. - Enable BIG TCP support for bareudp tunnels. - Remove the IPV6_ADDRFORM socket option and disallow buggy TCP state transitions (listen() + shutdown() + connect() and connect() + connect(AF_UNSPEC) + listen()). - Remove most BUG_ON()s from core packet processing, replacing them with error paths. Wireless: - Latency improvements, including lockless station get/dump and beacon updates, plus RTNL reduction and locking cleanups. - Infrastructure for non-station TX/RX with MLO. - NAN (Wi-Fi Aware) Instant Communication support and other NAN improvements. - Control frame Integrity Protocol (CIP) and protected TWT support. - Improvements for AP regulatory checks. Netfilter: - Disable br_netfilter in user namespaces and schedule it for removal in 2027. - Restrict the raw table in nft_compat to the specific family, hook and priority. - Prepare the flowtable for mixed IPv4-over-IPv6 tunneling (and vice versa). - Use GFP_KERNEL_ACCOUNT for most netfilter allocations, for better integration with memcg. - Allow discovering conntrack zone filtering support in ctnetlink dumps, and fix nfnetlink_hook handling of large dumps. Deletions: - Move the LLC type 2 sockets out of tree. There are no known users and the code attracts a never-ending stream of LLM-driven fixes. Device drivers: - New drivers: - Meta Platforms NIC (mpnic), the second-generation NIC from Meta - initial skeleton of the Nebula-matrix NICs driver (nbl) - X-Powers AC200/AC300 Fast Ethernet PHYs - Ethernet high-speed NICs: - Intel: - i40e: XDP metadata support for RX hash and VLAN tag - ice: expose the E825 TSPLL state via the DPLL subsystem - nVidia/Mellanox: - move the data direct infrastructure from IB to core, in preparation for netdev support. - reduce the memory used per offloaded TC rule - Meta (fbnic): - support large io_uring zero-copy RX buffers - Microsoft vNIC (mana): - add BQL support - Ethernet NICs consumer, and embedded: - Airoha: - LRO offload support - Cadence (macb): - context swapping for ringparam and MTU changes, avoiding buffer loss under memory pressure - Mucse (rnpgbe): - TX/RX and link status support - NXP (enetc): - SR-IOV improvements and VF support for ENETC v4 - Qualcomm (ppe): - IPQ5424 support - Realtek PCI (r8169): - phylink support - RSS support for RTL8127 - Renesas (ravb): - gPTP support for R-Car Gen4 - Synopsys (stmmac): - XDP multi-buffer support on the TX side - tc mqprio hw queue priority offload - support for RK3568 SGMII, Allwinner H616 EMAC1, UltraRISC DP1000 and Qualcomm sa8255p - Ethernet switches: - Mediatek (mt7530): - fast age and change conduit support - Microchip (ksz): - support for KSZ8995XA/KS8995XA - Microchip (lan966x): - PCIe FDMA support, ~20x throughput increase in PCIe endpoint - Microchip (sparx5): - Time Aware Shaper (taprio) offload - Motorcomm (yt921x): - LED and SerDes PCS support - Realtek (rtl8365mb): - EEE support - Ethernet PHYs: - Broadcom: - enable jumbo frames on BCM54xx - Realtek: - support for RTL8261CE_CG and RTL8261D_VM - MDIO support for RTL838x and RTL839x - WiFi drivers: - Broadcom (brcmfmac): - FT/OKC roaming offload - Intel (iwlwifi): - UHR sniffer support - support for newer UEFI/BIOS tables - MediaTek (mt76): - mt7925 NAN improvements - offchannel/scan improvements and MLO fixes - Morse Micro (mm81x): - monitor mode support - Qualcomm (ath12k): - IPQ5332 platform infrastructure - Realtek (rtw88): - RTL8723BS support - SDIO TX performance improvements - Realtek (rtw89): - P2P_DEVICE support - BT-coex updates for RTL8922D - Bluetooth: - move H:4 reassembly into the Bluetooth core - add generic support for vendor packets - MGMT: add security level changed event - Bluetooth drivers: - btusb: support Bulk Serialization Mode - Intel: support Draco and Titan Lake, allow unsigned firmware - Mediatek: MT7928 support - Qualcomm: QCC2072 support - Broadcom: Synaptics 4384 support" * tag 'net-next-7.4' of git://git.kernel.org/pub/scm/linux/kernel/git/netdev/net-next: (2126 commits) Bluetooth: btusb: disable by default Qualcomm multi-subsystem tipc: fix broadcast sender hang on last acker departure tipc: fix use-after-free of broadcast reception link at node release r8169: clear TX LPI on RTL8168h at link up after phylink conversion tun: Require CAP_NET_ADMIN to attach to an ownerless device net/sched/sch_cake: drop dead nla_nest_end() error check net/sched/sch_cake: widen buffer_used to u64 net/sched/sch_cake: reduce parent backlog when clearing a tin net/sched/sch_cake: serialize reconfiguration with the datapath tg3: Add Datto SIRIS 5 1U Server to restart AER quirk table net/mlx5e: Stop asserting for RTNL when closing channels net: shrink the payload-only part of skb dataref to one bit net: wwan: iosm: validate msg_head before indexing msg_ring net: bgmac: rate limit RX error messages selftests: net: veth: test XDP_TX to a peer with GRO enabled veth: schedule the peer's NAPI after XDP_TX when only GRO is enabled pppoe: delete session hash entries by pointer match net: lan969x: don't disable hardirqs in fdma tx clear net: sparx5/lan969x: do not clobber the PSFP cycle timer update period net: lan969x: fix DSM calendar for empty taxis ...
Diffstat (limited to 'drivers/net/mctp')
-rw-r--r--drivers/net/mctp/mctp-usblib-test.c543
1 files changed, 532 insertions, 11 deletions
diff --git a/drivers/net/mctp/mctp-usblib-test.c b/drivers/net/mctp/mctp-usblib-test.c
index 9df401a914ff..b7e8743c561f 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>
@@ -14,6 +15,15 @@
#include <net/mctpdevice.h>
#include <linux/usb/mctp-usb.h>
+#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;
@@ -22,9 +32,104 @@ 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;
+
+ ctx = mctp_usblib_tx_ctx_priv(tx_ctx);
+ ndev = ctx->dev->ndev;
+
+ new_node = kzalloc_flex(*new_node, data, len, GFP_KERNEL);
+ if (!new_node)
+ return -ENOMEM;
+
+ new_node->length = len;
+ memcpy(new_node->data, data, len);
+ list_add_tail(&new_node->list, &ctx->tx_xfers);
+
+ mctp_usblib_tx_send_complete(tx_ctx, ndev, true);
+ return 0;
+}
+
+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_put_data(skb, data, length);
+ return skb;
+}
+
static netdev_tx_t mctp_usblib_dev_tx(struct sk_buff *skb,
struct net_device *ndev)
{
@@ -117,6 +222,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.
@@ -124,6 +230,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);
}
@@ -139,6 +247,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);
@@ -200,11 +309,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);
@@ -227,6 +334,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, span);
+
+ return tx;
+}
+
/* Single packet, starting on a transfer boundary, contained entirely within
* the transfer
*/
@@ -378,29 +511,417 @@ 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. */
+ { "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,
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_test_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);
+ rc = mctp_usblib_test_rx_complete(dev->ndev, rx, len - offset);
+ KUNIT_ASSERT_EQ(test, rc, 0);
+ 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);
+ kfree_skb(skb);
+ }
+}
+
+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;
+
+ 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);
+}
+
+/* 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 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 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 void mctp_usblib_test_append_failure(struct kunit *test)
+{
+ struct mctp_usblib_test_ctx *ctx;
+ struct rtnl_link_stats64 stats;
+ struct mctp_usblib_tx_ops ops;
+ struct mctp_usblib_tx *tx;
+ struct net_device *ndev;
+ struct sk_buff *skb;
+ u64 tx_dropped;
+ size_t i;
+ u8 *buf;
+ int rc;
+
+ const size_t packet_count = 3;
+ const size_t len = (MCTP_USB_1_0_XFER_SIZE / packet_count) + 1
+ - sizeof(struct mctp_usb_hdr);
+ static_assert(len >= MCTP_USB_BTU);
+
+ 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 < packet_count; i++) {
+ tx_dropped = dev_get_stats(ndev, &stats)->tx_dropped;
+ KUNIT_EXPECT_EQ(test, tx_dropped, 0);
+ skb = mctp_usblib_test_init_skb(test, len, ndev, buf);
+
+ rc = mctp_usblib_tx_push(ndev, tx, skb, i != packet_count - 1);
+ KUNIT_EXPECT_EQ(test, rc, 0);
+ KUNIT_EXPECT_TRUE(test, list_empty(&ctx->tx_xfers));
+ }
+
+ tx_dropped = dev_get_stats(ndev, &stats)->tx_dropped;
+ KUNIT_EXPECT_EQ(test, tx_dropped, packet_count);
+ KUNIT_EXPECT_NULL(test, tx->cur_ctx);
+}
+
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),
+ 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),
+ KUNIT_CASE(mctp_usblib_test_append_failure),
{}
};