Skip to content

io_uring zerocopy send

Pavel Begunkov io-uring lkml netdev 6 versions – Applied

Status from this message: Applied, thanks!

Get series with b4
b4 am [email protected]

Versions newest first · linked by author and title

  1. v5 Latest27 patches 8 repliesApplied
  2. v427 patches 17 repliesSuperseded
  3. v3 posting 2/225 patches 1 replySuperseded
Earlier versions · 3
VersionPostedPatchesRepliesStatus
v3 RFC posting 1/229 patches17 repliesSuperseded
v2 RFC19 patches5 repliesSuperseded
v1 RFC12 patches28 repliesSuperseded

Changes in v5 from the cover letter

remove ubuf_info checks from custom iov_iter callbacks to avoid disabling the page refs optimisations for TCP

Patches in v5 Applied

#Subject and filesSizeRepliesApplied
1/27ipv4: avoid partial copy for zcnet/ipv4/ip_output.c+6 -23 replies
2/27ipv6: avoid partial copy for zcnet/ipv6/ip6_output.c+6 -10 replies
3/27skbuff: don't mix ubuf_info from different sourcesnet/core/skbuff.c+4 -00 replies
4/27skbuff: add SKBFL_DONT_ORPHAN flaginclude/linux/skbuff.h net/core/skbuff.c+6 -40 replies
5/27skbuff: carry external ubuf_info in msghdrinclude/linux/socket.h net/compat.c net/socket.c+5 -00 replies
6/27net: Allow custom iter handler in msghdrinclude/linux/skbuff.h include/linux/socket.h net/core/datagram.c +1 more+19 -80 replies
7/27net: introduce managed frags infrastructureinclude/linux/skbuff.h net/core/skbuff.c+50 -40 replies
8/27net: introduce __skb_fill_page_desc_noaccinclude/linux/skbuff.h+17 -110 replies
9/27ipv4/udp: support externally provided ubufsnet/ipv4/ip_output.c+31 -130 replies
10/27ipv6/udp: support externally provided ubufsnet/ipv6/ip6_output.c+31 -130 replies
11/27tcp: support externally provided ubufsnet/ipv4/tcp.c+20 -120 replies
12/27io_uring: initialise msghdr::msg_ubufio_uring/net.c+2 -00 replies
13/27io_uring: export io_put_task()include/linux/io_uring_types.h io_uring/io_uring.c io_uring/io_uring.h +1 more+36 -360 replies
14/27io_uring: add zc notification infrastructureinclude/linux/io_uring_types.h io_uring/Makefile io_uring/io_uring.c +3 more+179 -40 replies
15/27io_uring: cache struct io_notifinclude/linux/io_uring_types.h io_uring/io_uring.c io_uring/notif.c +1 more+65 -70 replies
16/27io_uring: complete notifiers in twio_uring/notif.c io_uring/notif.h+22 -30 replies
17/27io_uring: add rsrc referencing for notifiersio_uring/notif.c io_uring/notif.h io_uring/rsrc.h+15 -30 replies
18/27io_uring: add notification slot registrationinclude/uapi/linux/io_uring.h io_uring/io_uring.c io_uring/notif.c +1 more+72 -00 replies
19/27io_uring: wire send zc request typeinclude/uapi/linux/io_uring.h io_uring/net.c io_uring/net.h +1 more+118 -00 replies
20/27io_uring: account locked pages for non-fixed zcio_uring/net.c io_uring/notif.c+7 -00 replies
21/27io_uring: allow to pass addr into sendzcinclude/uapi/linux/io_uring.h io_uring/net.c+17 -30 replies
22/27io_uring: sendzc with fixed buffersinclude/uapi/linux/io_uring.h io_uring/net.c+29 -60 replies
23/27io_uring: flush notifiers after sendzcinclude/uapi/linux/io_uring.h io_uring/io_uring.c io_uring/io_uring.h +3 more+31 -120 replies
24/27io_uring: rename IORING_OP_FILES_UPDATEinclude/uapi/linux/io_uring.h io_uring/opdef.c io_uring/rsrc.c +1 more+33 -90 replies
25/27io_uring: add zc notification flush requestsinclude/uapi/linux/io_uring.h io_uring/rsrc.c+39 -00 replies
26/27io_uring: enable managed frags with register buffersio_uring/net.c+55 -10 replies
27/27selftests/io_uring: test zerocopy sendtools/testing/selftests/net/Makefile tools/testing/selftests/net/io_uring_zerocopy_tx.c tools/testing/selftests/net/io_uring_zerocopy_tx.sh+737 -02 replies

Reviews trailers per patch

No Reviewed-by, Acked-by or Tested-by trailers for v5 yet.

Read the v5 thread

Cover letter [PATCH net-next v5 00/27] io_uring zerocopy send

NOTE: Not to be picked directly. After getting necessary acks, I'll be
working out merging with Jakub and Jens.

The patchset implements io_uring zerocopy send. It works with both registered and normal buffers, mixing is allowed but not recommended. Apart from usual request completions, just as with MSG_ZEROCOPY, io_uring separately notifies the userspace when buffers are freed and can be reused (see API design below), which is delivered into io_uring's Completion Queue. Those "buffer-free" notifications are not necessarily per request, but the userspace has control over it and should explicitly attaching a number of requests to a single notification. The series also adds some internal optimisations when used with registered buffers like removing page referencing.

From the kernel networking perspective there are two main changes. The first one is passing ubuf_info into the network layer from io_uring (inside of an in kernel struct msghdr). This allows extra optimisations, e.g. ubuf_info caching on the io_uring side, but also helps to avoid cross-referencing and synchronisation problems. The second part is an optional optimisation removing page referencing for requests with registered buffers.

Benchmarking UDP with an optimised version of the selftest (see [1]), which sends a bunch of requests, waits for completions and repeats. "+ flush" column posts one additional "buffer-free" notification per request, and just "zc" doesn't post buffer notifications at all.

NIC (requests / second):

IO size | non-zc    | zc             | zc + flush
4000    | 495134    | 606420 (+22%)  | 558971 (+12%)
1500    | 551808    | 577116 (+4.5%) | 565803 (+2.5%)
1000    | 584677    | 592088 (+1.2%) | 560885 (-4%)
600     | 596292    | 598550 (+0.4%) | 555366 (-6.7%)

dummy (requests / second):

IO size | non-zc    | zc             | zc + flush
8000    | 1299916   | 2396600 (+84%) | 2224219 (+71%)
4000    | 1869230   | 2344146 (+25%) | 2170069 (+16%)
1200    | 2071617   | 2361960 (+14%) | 2203052 (+6%)
600     | 2106794   | 2381527 (+13%) | 2195295 (+4%)

Previously it also brought a massive performance speedup compared to the msg_zerocopy tool (see [3]), which is probably not super interesting. There is also an additional bunch of refcounting optimisations that was omitted from the series for simplicity and as they don't change the picture drastically, they will be sent as follow up, as well as flushing optimisations closing the performance gap b/w two last columns.

For TCP on localhost (with hacks enabling localhost zerocopy) and including additional overhead for receive:

IO size | non-zc    | zc
1200    | 4174      | 4148
4096    | 7597      | 11228

Using a real NIC 1200 bytes, zc is worse than non-zc ~5-10%, maybe the omitted optimisations will somewhat help, should look better for 4000, but couldn't test properly because of setup problems.

Links:

liburing (benchmark + tests):
[1] https://github.com/isilence/liburing/tree/zc_v4

kernel repo:
[2] https://github.com/isilence/linux/tree/zc_v4

RFC v1:
[3] https://lore.kernel.org/io-uring/[email protected]/

RFC v2:
https://lore.kernel.org/io-uring/[email protected]/

Net patches based:
[email protected]:isilence/linux.git zc_v4-net-base or
https://github.com/isilence/linux/tree/zc_v4-net-base

API design overview:

The series introduces an io_uring concept of notifactors. From the userspace perspective it's an entity to which it can bind one or more requests and then requesting to flush it. Flushing a notifier makes it impossible to attach new requests to it, and instructs the notifier to post a completion once all requests attached to it are completed and the kernel doesn't need the buffers anymore.

Notifications are stored in notification slots, which should be registered as an array in io_uring. Each slot stores only one notifier at any particular moment. Flushing removes it from the slot and the slot automatically replaces it with a new notifier. All operations with notifiers are done by specifying an index of a slot it's currently in.

When registering a notification the userspace specifies a u64 tag for each slot, which will be copied in notification completion entries as cqe::user_data. cqe::res is 0 and cqe::flags is equal to wrap around u32 sequence number counting notifiers of a slot.

Changelog:

v4 -> v5
remove ubuf_info checks from custom iov_iter callbacks to avoid disabling the page refs optimisations for TCP

v3 -> v4
custom iov_iter handling

RFC v2 -> v3:
mem accounting for non-registered buffers allow mixing registered and normal requests per notifier notification flushing via IORING_OP_RSRC_UPDATE TCP support
fix buffer indexing
fix io-wq ->uring_lock locking
fix bugs when mixing with MSG_ZEROCOPY
fix managed refs bugs in skbuff.c

RFC -> RFC v2:
remove additional overhead for non-zc from skb_release_data() avoid msg propagation, hide extra bits of non-zc overhead task_work based "buffer free" notifications
improve io_uring's notification refcounting
added 5/19, (no pfmemalloc tracking)
added 8/19 and 9/19 preventing small copies with zc misc small changes

David Ahern (1):
net: Allow custom iter handler in msghdr

Pavel Begunkov (26):
ipv4: avoid partial copy for zc
ipv6: avoid partial copy for zc
skbuff: don't mix ubuf_info from different sources
skbuff: add SKBFL_DONT_ORPHAN flag
skbuff: carry external ubuf_info in msghdr
net: introduce managed frags infrastructure
net: introduce __skb_fill_page_desc_noacc ipv4/udp: support externally provided ubufs ipv6/udp: support externally provided ubufs
tcp: support externally provided ubufs
io_uring: initialise msghdr::msg_ubuf
io_uring: export io_put_task()
io_uring: add zc notification infrastructure
io_uring: cache struct io_notif
io_uring: complete notifiers in tw
io_uring: add rsrc referencing for notifiers
io_uring: add notification slot registration
io_uring: wire send zc request type
io_uring: account locked pages for non-fixed zc
io_uring: allow to pass addr into sendzc
io_uring: sendzc with fixed buffers
io_uring: flush notifiers after sendzc
io_uring: rename IORING_OP_FILES_UPDATE
io_uring: add zc notification flush requests
io_uring: enable managed frags with register buffers selftests/io_uring: test zerocopy send

 include/linux/io_uring_types.h                |  37 ++
 include/linux/skbuff.h                        |  66 +-
 include/linux/socket.h                        |   5 +
 include/uapi/linux/io_uring.h                 |  45 +-
 io_uring/Makefile                             |   2 +-
 io_uring/io_uring.c                           |  42 +-
 io_uring/io_uring.h                           |  22 +
 io_uring/net.c                                | 187 ++++++
 io_uring/net.h                                |   4 +
 io_uring/notif.c                              | 215 +++++++
 io_uring/notif.h                              |  87 +++
 io_uring/opdef.c                              |  24 +-
 io_uring/rsrc.c                               |  55 +-
 io_uring/rsrc.h                               |  16 +-
 io_uring/tctx.h                               |  26 -
 net/compat.c                                  |   1 +
 net/core/datagram.c                           |  14 +-
 net/core/skbuff.c                             |  37 +-
 net/ipv4/ip_output.c                          |  50 +-
 net/ipv4/tcp.c                                |  32 +-
 net/ipv6/ip6_output.c                         |  49 +-
 net/socket.c                                  |   3 +
 tools/testing/selftests/net/Makefile          |   1 +
 .../selftests/net/io_uring_zerocopy_tx.c      | 605 ++++++++++++++++++
 .../selftests/net/io_uring_zerocopy_tx.sh     | 131 ++++
 25 files changed, 1628 insertions(+), 128 deletions(-)
 create mode 100644 io_uring/notif.c
 create mode 100644 io_uring/notif.h
 create mode 100644 tools/testing/selftests/net/io_uring_zerocopy_tx.c
 create mode 100755 tools/testing/selftests/net/io_uring_zerocopy_tx.sh
Signature · 1 line
2.37.0