udp: Correct splice forwarding when receiving from multiple sources

udp_sock_handler_splice() reads a whole batch of datagrams at once with
recvmmsg().  It then forwards them all via a single socket on the other
side, based on the source port.

However, it's entirely possible that the datagrams in the set have
different source ports, and thus ought to be forwarded via different
sockets on the destination side.  In fact this situation arises with the
iperf -P4 throughput tests in our own test suite.  AFAICT we only get away
with this because iperf3 is strictly one way and doesn't send reply packets
which would be misdirected because of the incorrect source ports.

Alter udp_sock_handler_splice() to split the packets it receives into
batches with the same source address and send each batch with a separate
sendmmsg().

For now we only look for already contiguous batches, which means that if
there are multiple active flows interleaved this is likely to degenerate
to batches of size 1.  For now this is the simplest way to correct the
behaviour and we can try to optimize later.

Signed-off-by: David Gibson <david@gibson.dropbear.id.au>
Signed-off-by: Stefano Brivio <sbrivio@redhat.com>
This commit is contained in:
David Gibson 2022-11-30 15:13:16 +11:00 committed by Stefano Brivio
parent 2dec914209
commit 34764ea4f3

19
udp.c
View file

@ -584,8 +584,8 @@ static void udp_splice_sendfrom(const struct ctx *c, unsigned start, unsigned n,
static void udp_sock_handler_splice(const struct ctx *c, union epoll_ref ref,
uint32_t events, const struct timespec *now)
{
in_port_t src, dst = ref.r.p.udp.udp.port;
int v6 = ref.r.p.udp.udp.v6, n;
in_port_t dst = ref.r.p.udp.udp.port;
int v6 = ref.r.p.udp.udp.v6, n, i, m;
struct mmsghdr *mmh_recv;
if (!(events & EPOLLIN))
@ -619,9 +619,18 @@ static void udp_sock_handler_splice(const struct ctx *c, union epoll_ref ref,
});
}
src = sa_port(v6, mmh_recv[0].msg_hdr.msg_name);
udp_splice_sendfrom(c, 0, n, src, dst, v6,
ref.r.p.udp.udp.ns, ref.r.p.udp.udp.orig, now);
for (i = 0; i < n; i += m) {
in_port_t src = sa_port(v6, mmh_recv[i].msg_hdr.msg_name);
for (m = 1; i + m < n; m++) {
void *mname = mmh_recv[i + m].msg_hdr.msg_name;
if (sa_port(v6, mname) != src)
break;
}
udp_splice_sendfrom(c, i, m, src, dst, v6, ref.r.p.udp.udp.ns,
ref.r.p.udp.udp.orig, now);
}
}
/**