diff options
| author | Linus Torvalds <torvalds@linux-foundation.org> | 2026-10-22 15:58:00 -1000 |
|---|---|---|
| committer | Linus Torvalds <torvalds@linux-foundation.org> | 2026-10-22 15:58:00 -1000 |
| commit | 734bef548a96e8774d6eef5dfa2026c6edc4e80b (patch) | |
| tree | ce2520b2602ce3b82f9842490d21836f9838d441 /drivers/net/mctp | |
| parent | 864c07de0c7dcbca58b61625dfa2e461ae63b242 (diff) | |
| parent | 1a2c35c49cc7c6be860038b8442b52424a8f123c (diff) | |
| download | linux-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.c | 543 |
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), {} }; |
