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 <yuanzm2@lenovo.com>
Closes: 269936db5e (r175836230)
Signed-off-by: Jeremy Kerr <jk@codeconstruct.com.au>
Reviewed-by: Simon Horman <horms@kernel.org>
Link: https://patch.msgid.link/20260324-dev-mtu-copy-v1-1-7af6bd7027d3@codeconstruct.com.au
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
This commit is contained in:
Jeremy Kerr 2026-03-24 15:19:56 +08:00 committed by Jakub Kicinski
parent 2b8e147c00
commit e1877cf623
2 changed files with 15 additions and 15 deletions

View File

@ -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;
}

View File

@ -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;