From: Jeremy Kerr Date: Tue, 24 Mar 2026 07:19:56 +0000 (+0800) Subject: net: mctp: avoid copy in fragmentation loop for near-MTU messages X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=e1877cf6231152ff4686bd9900a6a88ffdd90f84;p=thirdparty%2Flinux.git net: mctp: avoid copy in fragmentation loop for near-MTU messages Currently, we incorrectly send messages that are within 4 bytes (a struct mctp_hdr) smaller than the MTU through mctp_do_fragment_route(). This has no effect on the actual fragmentation, as we will still send as one packet, but unnecessarily copies the original skb into a new single-fragment skb. Instead of having the MTU comparisons in both mctp_local_output() and mctp_do_fragment_route(), feed all local messages through the latter, and add the single-packet optimisation there. This means we can coalesce the routing path of mctp_local_output, so our out_release path is now solely for errors, so rename the label accordingly. Include a check in the route tests for the single-packet case too. Reported-by: yuanzhaoming Closes: https://github.com/openbmc/linux/commit/269936db5eb3962fe290b1dc4dbf1859cd5a04dd#r175836230 Signed-off-by: Jeremy Kerr Reviewed-by: Simon Horman Link: https://patch.msgid.link/20260324-dev-mtu-copy-v1-1-7af6bd7027d3@codeconstruct.com.au Signed-off-by: Jakub Kicinski --- diff --git a/net/mctp/route.c b/net/mctp/route.c index 59ad60b885631..021e04f1ea7c9 100644 --- a/net/mctp/route.c +++ b/net/mctp/route.c @@ -1037,6 +1037,13 @@ static int mctp_do_fragment_route(struct mctp_dst *dst, struct sk_buff *skb, return -EMSGSIZE; } + /* within MTU? avoid the copy, send original skb */ + if (skb->len <= mtu) { + hdr->flags_seq_tag = MCTP_HDR_FLAG_SOM | + MCTP_HDR_FLAG_EOM | tag; + return dst->output(dst, skb); + } + /* keep same headroom as the original skb */ headroom = skb_headroom(skb); @@ -1111,7 +1118,6 @@ int mctp_local_output(struct sock *sk, struct mctp_dst *dst, struct mctp_hdr *hdr; unsigned long flags; unsigned int netid; - unsigned int mtu; mctp_eid_t saddr; int rc; u8 tag; @@ -1133,7 +1139,7 @@ int mctp_local_output(struct sock *sk, struct mctp_dst *dst, netid = READ_ONCE(dst->dev->net); if (rc) - goto out_release; + goto err_free; if (req_tag & MCTP_TAG_OWNER) { if (req_tag & MCTP_TAG_PREALLOC) @@ -1145,7 +1151,7 @@ int mctp_local_output(struct sock *sk, struct mctp_dst *dst, if (IS_ERR(key)) { rc = PTR_ERR(key); - goto out_release; + goto err_free; } mctp_skb_set_flow(skb, key); /* done with the key in this scope */ @@ -1170,20 +1176,10 @@ int mctp_local_output(struct sock *sk, struct mctp_dst *dst, hdr->dest = daddr; hdr->src = saddr; - mtu = dst->mtu; - - if (skb->len + sizeof(struct mctp_hdr) <= mtu) { - hdr->flags_seq_tag = MCTP_HDR_FLAG_SOM | - MCTP_HDR_FLAG_EOM | tag; - rc = dst->output(dst, skb); - } else { - rc = mctp_do_fragment_route(dst, skb, mtu, tag); - } - /* route output functions consume the skb, even on error */ - skb = NULL; + return mctp_do_fragment_route(dst, skb, dst->mtu, tag); -out_release: +err_free: kfree_skb(skb); return rc; } diff --git a/net/mctp/test/route-test.c b/net/mctp/test/route-test.c index 75ea96c10e497..61c989c43ec09 100644 --- a/net/mctp/test/route-test.c +++ b/net/mctp/test/route-test.c @@ -63,6 +63,10 @@ static void mctp_test_fragment(struct kunit *test) if (!skb2) break; + /* avoid copying single-skb messages */ + if (first && last) + KUNIT_EXPECT_PTR_EQ(test, skb, skb2); + hdr2 = mctp_hdr(skb2); tag_mask = MCTP_HDR_TAG_MASK | MCTP_HDR_FLAG_TO;