Skip to content

Commit

Permalink
Merge pull request #295 from ska-sa/send-gso
Browse files Browse the repository at this point in the history
Speed up sending with generic segmentation offload
  • Loading branch information
bmerry authored Dec 5, 2023
2 parents 8ac5196 + c380a0f commit ed74c91
Show file tree
Hide file tree
Showing 7 changed files with 265 additions and 27 deletions.
7 changes: 7 additions & 0 deletions .ci/all-builds.sh
Original file line number Diff line number Diff line change
Expand Up @@ -17,4 +17,11 @@ for python in true false; do
done
done
done
# sendmmsg and gso don't interact with ibv, so don't test them jointly
for sendmmsg in auto disabled; do
for gso in auto disabled; do
meson configure -Dpython=$python -Dsendmmsg=$sendmmsg -Dgso=$gso
meson compile
done
done
done
51 changes: 51 additions & 0 deletions doc/dev-send-gso.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
Generic segmentation offload
============================
Linux supports a mechanism called :dfn:`generic segmentation offload` (GSO) to
reduce packet overheads when transmitting UDP data through the kernel
networking stack. A good overview can be found on `Cloudflare's blog`_, but
the basic idea is this:

.. _Cloudflare's blog: https://blog.cloudflare.com/accelerating-udp-packet-transmission-for-quic/

1. Userspace concatenates multiple smaller packets into one mega-packet for
submission to the kernel.
2. Most of the networking stack operates on the mega-packet.
3. As late as possible (and possibly on the NIC) the mega-packet is
re-segmented into the original packets.

The re-segmentation uses a user-supplied parameter (socket option) indicating
the size of the original packets. This imposes a limitation that the original
packets were all the same size, except perhaps for the last one in the
mega-packet.

The support for this in spead2 is dependent on the :manpage:`sendmmsg(2)`
support. While there is no fundamental reason GSO can't be used without
:manpage:`sendmmsg(2)`, supporting it would complicate the code significantly,
and GSO is a much more recent feature so it is unlikely that this combination
would ever be needed.

Run-time detection of support is unfortunately rather complicated. The simple
part is that an older kernel will not support the socket option. If that
occurs, we simply disable GSO for the stream. A more tricky problem is that
actually sending the message may fail for several reasons:

- Fragmentation doesn't seem to be supported, so if the segment size is bigger
than the MTU, it will fail.
- If hardware checksumming is disabled (or presumably if it is not supported),
it will fail.

To cope with this complication, a state machine is used. It has four possible
states:

- **active**: the socket option is set to a positive value
- **inactive**: the socket option is set to zero, but we may still transition
to active
- **probe**: the last send in active state failed; the socket option is now
set to zero and we're retrying
- **disabled**: the socket option is set to zero, and we will never try to set
it again.

If send fails while in state **active**, we switch to state **probe** and try
again (without GSO). If that succeeds, we conclude that GSO is non-functional
for this stream and permanently go to **disabled**. If that also fails, we
conclude that the problem was unrelated to GSO and return to **inactive**.
1 change: 1 addition & 0 deletions doc/developer.rst
Original file line number Diff line number Diff line change
Expand Up @@ -27,4 +27,5 @@ the C++ classes to extend functionality.
dev-recv-destruction
dev-recv-chunk-group
dev-send-rate-limit
dev-send-gso
dev-ibverbs-linking
1 change: 1 addition & 0 deletions include/spead2/common_features.h.in
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@
#define SPEAD2_USE_MLX5DV @SPEAD2_USE_MLX5DV@
#define SPEAD2_USE_RECVMMSG @SPEAD2_USE_RECVMMSG@
#define SPEAD2_USE_SENDMMSG @SPEAD2_USE_SENDMMSG@
#define SPEAD2_USE_GSO @SPEAD2_USE_GSO@
#define SPEAD2_USE_EVENTFD @SPEAD2_USE_EVENTFD@
#define SPEAD2_USE_PTHREAD_SETAFFINITY_NP @SPEAD2_USE_PTHREAD_SETAFFINITY_NP@
#define SPEAD2_USE_FMV @SPEAD2_USE_FMV@
Expand Down
8 changes: 8 additions & 0 deletions meson.build
Original file line number Diff line number Diff line change
Expand Up @@ -160,6 +160,13 @@ use_sendmmsg = get_option('sendmmsg').require(
prefix : '#include <sys/socket.h>'
)
).allowed()
use_gso = get_option('gso').require(
compiler.get_define(
'UDP_SEGMENT',
args : '-D_GNU_SOURCE',
prefix : '#include <netinet/udp.h>'
) != ''
).allowed()
use_eventfd = get_option('eventfd').require(
compiler.has_function(
'eventfd',
Expand Down Expand Up @@ -260,6 +267,7 @@ conf.set10('SPEAD2_USE_IBV_HW_RATE_LIMIT', use_ibv_hw_rate_limit)
conf.set10('SPEAD2_USE_MLX5DV', mlx5_dep.found())
conf.set10('SPEAD2_USE_RECVMMSG', use_recvmmsg)
conf.set10('SPEAD2_USE_SENDMMSG', use_sendmmsg)
conf.set10('SPEAD2_USE_GSO', use_gso)
conf.set10('SPEAD2_USE_EVENTFD', use_eventfd)
conf.set10('SPEAD2_USE_POSIX_SEMAPHORES', use_posix_semaphores)
conf.set10('SPEAD2_USE_PTHREAD_SETAFFINITY_NP', use_pthread_setaffinity_np)
Expand Down
1 change: 1 addition & 0 deletions meson.options
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ option('pcap', type : 'feature', description : 'Support reading from pcap files'
option('cap', type : 'feature', description : 'Use libcap')
option('recvmmsg', type : 'feature', description : 'Use recvmmsg system call')
option('sendmmsg', type : 'feature', description : 'Use sendmmsg system call')
option('gso', type : 'feature', description : 'Use generic segmentation offload')
option('eventfd', type : 'feature', description : 'Use eventfd system call for semaphores')
option('posix_semaphores', type : 'feature', description : 'Use POSIX semaphores')
option('pthread_setaffinity_np', type : 'feature', description : 'Use pthread_setaffinity_np to set thread affinity')
Expand Down
Loading

0 comments on commit ed74c91

Please sign in to comment.