← Documents Documentation/networking/msg_zerocopy.rst GitHub 원문 ↗

Linux 6.18.37 · Networking

MSG_ZEROCOPY

Socket 송신 copy avoidance의 page lifetime, completion notification과 deferred copy를 설명합니다.

Source pathDocumentation/networking/msg_zerocopy.rst
Source versionLinux v6.18.37
TranslationDUJINLABS 전문 번역 + 해설

요약·해설과 원문, 전문 번역을 서로 분리했습니다. API 이름, symbol, source path는 원문 표기를 사용합니다.

1. 요약·해설

원문의 핵심 논리와 kernel programming 관점의 보충 설명입니다. 아래의 전문 번역과는 별도로 작성했습니다.

요약·해설

msg_zerocopy.rst:1-265

약 10KB 이상 write에서 copy 비용을 줄일 수 있지만 pinned page를 completion 전까지 수정하면 안 됩니다. Completion은 page 공유 종료를 뜻할 뿐 transmit 완료를 보장하지 않으며, 실제로 copy된 경우 `ee_code`가 이를 알립니다.

Application contract
SO_ZEROCOPY 설정send(MSG_ZEROCOPY)error queue completionbuffer 재사용

설정부터 buffer 회수까지의 순서입니다.

2. 영어 원문 전체

번역 기준이 된 Linux v6.18.37 원문입니다. 줄 번호는 이 버전의 파일 좌표입니다.

원문 전체 펼치기
1
2 ============
3 MSG_ZEROCOPY
4 ============
5
6 Intro
7 =====
8
9 The MSG_ZEROCOPY flag enables copy avoidance for socket send calls.
10 The feature is currently implemented for TCP, UDP and VSOCK (with
11 virtio transport) sockets.
12
13
14 Opportunity and Caveats
15 -----------------------
16
17 Copying large buffers between user process and kernel can be
18 expensive. Linux supports various interfaces that eschew copying,
19 such as sendfile and splice. The MSG_ZEROCOPY flag extends the
20 underlying copy avoidance mechanism to common socket send calls.
21
22 Copy avoidance is not a free lunch. As implemented, with page pinning,
23 it replaces per byte copy cost with page accounting and completion
24 notification overhead. As a result, MSG_ZEROCOPY is generally only
25 effective at writes over around 10 KB.
26
27 Page pinning also changes system call semantics. It temporarily shares
28 the buffer between process and network stack. Unlike with copying, the
29 process cannot immediately overwrite the buffer after system call
30 return without possibly modifying the data in flight. Kernel integrity
31 is not affected, but a buggy program can possibly corrupt its own data
32 stream.
33
34 The kernel returns a notification when it is safe to modify data.
35 Converting an existing application to MSG_ZEROCOPY is not always as
36 trivial as just passing the flag, then.
37
38
39 More Info
40 ---------
41
42 Much of this document was derived from a longer paper presented at
43 netdev 2.1. For more in-depth information see that paper and talk,
44 the excellent reporting over at LWN.net or read the original code.
45
46 paper, slides, video
47 https://netdevconf.org/2.1/session.html?debruijn
48
49 LWN article
50 https://lwn.net/Articles/726917/
51
52 patchset
53 [PATCH net-next v4 0/9] socket sendmsg MSG_ZEROCOPY
54 https://lore.kernel.org/netdev/20170803202945.70750-1-willemdebruijn.kernel@gmail.com
55
56
57 Interface
58 =========
59
60 Passing the MSG_ZEROCOPY flag is the most obvious step to enable copy
61 avoidance, but not the only one.
62
63 Socket Setup
64 ------------
65
66 The kernel is permissive when applications pass undefined flags to the
67 send system call. By default it simply ignores these. To avoid enabling
68 copy avoidance mode for legacy processes that accidentally already pass
69 this flag, a process must first signal intent by setting a socket option:
70
71 ::
72
73 if (setsockopt(fd, SOL_SOCKET, SO_ZEROCOPY, &one, sizeof(one)))
74 error(1, errno, "setsockopt zerocopy");
75
76 Transmission
77 ------------
78
79 The change to send (or sendto, sendmsg, sendmmsg) itself is trivial.
80 Pass the new flag.
81
82 ::
83
84 ret = send(fd, buf, sizeof(buf), MSG_ZEROCOPY);
85
86 A zerocopy failure will return -1 with errno ENOBUFS. This happens if
87 the socket exceeds its optmem limit or the user exceeds their ulimit on
88 locked pages.
89
90
91 Mixing copy avoidance and copying
92 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
93
94 Many workloads have a mixture of large and small buffers. Because copy
95 avoidance is more expensive than copying for small packets, the
96 feature is implemented as a flag. It is safe to mix calls with the flag
97 with those without.
98
99
100 Notifications
101 -------------
102
103 The kernel has to notify the process when it is safe to reuse a
104 previously passed buffer. It queues completion notifications on the
105 socket error queue, akin to the transmit timestamping interface.
106
107 The notification itself is a simple scalar value. Each socket
108 maintains an internal unsigned 32-bit counter. Each send call with
109 MSG_ZEROCOPY that successfully sends data increments the counter. The
110 counter is not incremented on failure or if called with length zero.
111 The counter counts system call invocations, not bytes. It wraps after
112 UINT_MAX calls.
113
114
115 Notification Reception
116 ~~~~~~~~~~~~~~~~~~~~~~
117
118 The below snippet demonstrates the API. In the simplest case, each
119 send syscall is followed by a poll and recvmsg on the error queue.
120
121 Reading from the error queue is always a non-blocking operation. The
122 poll call is there to block until an error is outstanding. It will set
123 POLLERR in its output flags. That flag does not have to be set in the
124 events field. Errors are signaled unconditionally.
125
126 ::
127
128 pfd.fd = fd;
129 pfd.events = 0;
130 if (poll(&pfd, 1, -1) != 1 || pfd.revents & POLLERR == 0)
131 error(1, errno, "poll");
132
133 ret = recvmsg(fd, &msg, MSG_ERRQUEUE);
134 if (ret == -1)
135 error(1, errno, "recvmsg");
136
137 read_notification(msg);
138
139 The example is for demonstration purpose only. In practice, it is more
140 efficient to not wait for notifications, but read without blocking
141 every couple of send calls.
142
143 Notifications can be processed out of order with other operations on
144 the socket. A socket that has an error queued would normally block
145 other operations until the error is read. Zerocopy notifications have
146 a zero error code, however, to not block send and recv calls.
147
148
149 Notification Batching
150 ~~~~~~~~~~~~~~~~~~~~~
151
152 Multiple outstanding packets can be read at once using the recvmmsg
153 call. This is often not needed. In each message the kernel returns not
154 a single value, but a range. It coalesces consecutive notifications
155 while one is outstanding for reception on the error queue.
156
157 When a new notification is about to be queued, it checks whether the
158 new value extends the range of the notification at the tail of the
159 queue. If so, it drops the new notification packet and instead increases
160 the range upper value of the outstanding notification.
161
162 For protocols that acknowledge data in-order, like TCP, each
163 notification can be squashed into the previous one, so that no more
164 than one notification is outstanding at any one point.
165
166 Ordered delivery is the common case, but not guaranteed. Notifications
167 may arrive out of order on retransmission and socket teardown.
168
169
170 Notification Parsing
171 ~~~~~~~~~~~~~~~~~~~~
172
173 The below snippet demonstrates how to parse the control message: the
174 read_notification() call in the previous snippet. A notification
175 is encoded in the standard error format, sock_extended_err.
176
177 The level and type fields in the control data are protocol family
178 specific, IP_RECVERR or IPV6_RECVERR (for TCP or UDP socket).
179 For VSOCK socket, cmsg_level will be SOL_VSOCK and cmsg_type will be
180 VSOCK_RECVERR.
181
182 Error origin is the new type SO_EE_ORIGIN_ZEROCOPY. ee_errno is zero,
183 as explained before, to avoid blocking read and write system calls on
184 the socket.
185
186 The 32-bit notification range is encoded as [ee_info, ee_data]. This
187 range is inclusive. Other fields in the struct must be treated as
188 undefined, bar for ee_code, as discussed below.
189
190 ::
191
192 struct sock_extended_err *serr;
193 struct cmsghdr *cm;
194
195 cm = CMSG_FIRSTHDR(msg);
196 if (cm->cmsg_level != SOL_IP &&
197 cm->cmsg_type != IP_RECVERR)
198 error(1, 0, "cmsg");
199
200 serr = (void *) CMSG_DATA(cm);
201 if (serr->ee_errno != 0 ||
202 serr->ee_origin != SO_EE_ORIGIN_ZEROCOPY)
203 error(1, 0, "serr");
204
205 printf("completed: %u..%u\n", serr->ee_info, serr->ee_data);
206
207
208 Deferred copies
209 ~~~~~~~~~~~~~~~
210
211 Passing flag MSG_ZEROCOPY is a hint to the kernel to apply copy
212 avoidance, and a contract that the kernel will queue a completion
213 notification. It is not a guarantee that the copy is elided.
214
215 Copy avoidance is not always feasible. Devices that do not support
216 scatter-gather I/O cannot send packets made up of kernel generated
217 protocol headers plus zerocopy user data. A packet may need to be
218 converted to a private copy of data deep in the stack, say to compute
219 a checksum.
220
221 In all these cases, the kernel returns a completion notification when
222 it releases its hold on the shared pages. That notification may arrive
223 before the (copied) data is fully transmitted. A zerocopy completion
224 notification is not a transmit completion notification, therefore.
225
226 Deferred copies can be more expensive than a copy immediately in the
227 system call, if the data is no longer warm in the cache. The process
228 also incurs notification processing cost for no benefit. For this
229 reason, the kernel signals if data was completed with a copy, by
230 setting flag SO_EE_CODE_ZEROCOPY_COPIED in field ee_code on return.
231 A process may use this signal to stop passing flag MSG_ZEROCOPY on
232 subsequent requests on the same socket.
233
234
235 Implementation
236 ==============
237
238 Loopback
239 --------
240
241 For TCP and UDP:
242 Data sent to local sockets can be queued indefinitely if the receive
243 process does not read its socket. Unbound notification latency is not
244 acceptable. For this reason all packets generated with MSG_ZEROCOPY
245 that are looped to a local socket will incur a deferred copy. This
246 includes looping onto packet sockets (e.g., tcpdump) and tun devices.
247
248 For VSOCK:
249 Data path sent to local sockets is the same as for non-local sockets.
250
251 Testing
252 =======
253
254 More realistic example code can be found in the kernel source under
255 tools/testing/selftests/net/msg_zerocopy.c.
256
257 Be cognizant of the loopback constraint. The test can be run between
258 a pair of hosts. But if run between a local pair of processes, for
259 instance when run with msg_zerocopy.sh between a veth pair across
260 namespaces, the test will not show any improvement. For testing, the
261 loopback restriction can be temporarily relaxed by making
262 skb_orphan_frags_rx identical to skb_orphan_frags.
263
264 For VSOCK type of socket example can be found in
265 tools/testing/vsock/vsock_test_zerocopy.c.
266

3. 한국어 전문 번역

영어 원문의 문단 순서와 의미를 유지한 전체 번역입니다. 코드, 함수명, symbol과 URL은 원문 표기를 유지합니다.

기회, 제약과 참고 자료

1-56

`MSG_ZEROCOPY` flag는 socket send call에서 data copy를 피하도록 요청하며 현재 TCP, UDP와 virtio transport를 사용하는 VSOCK에 구현되어 있습니다. 큰 buffer를 userspace와 kernel 사이에서 복사하는 비용을 줄이고 `sendfile`, `splice`가 쓰는 copy avoidance 개념을 일반적인 socket send API로 확장합니다.

Copy avoidance도 비용이 없습니다. 현재 구현은 byte별 copy 대신 page pinning의 accounting과 completion notification 비용을 부담하므로 대체로 약 10KB보다 큰 write에서만 효과적입니다. Page pinning 동안 process와 network stack이 buffer를 공유하므로 syscall 반환 직후 process가 buffer를 덮어쓰면 전송 중인 자신의 data stream을 손상시킬 수 있습니다. Kernel integrity에는 영향이 없습니다.

Kernel은 data를 다시 수정해도 안전해진 시점에 notification을 돌려줍니다. 따라서 기존 application을 바꿀 때 단순히 flag 하나만 추가해서는 안 됩니다. 더 깊은 설명은 netdev 2.1 paper·slide·video, LWN article과 최초 patchset에서 확인할 수 있습니다.

Zerocopy buffer lifetime
send(MSG_ZEROCOPY)userspace page pinnetwork stack이 공유
completion notificationpage hold 해제buffer 재사용 가능

Page를 공유하는 동안 application이 지켜야 할 수명 계약입니다.


============
MSG_ZEROCOPY
============

Intro
=====

The MSG_ZEROCOPY flag enables copy avoidance for socket send calls.
The feature is currently implemented for TCP, UDP and VSOCK (with
virtio transport) sockets.


Opportunity and Caveats
-----------------------

Copying large buffers between user process and kernel can be
expensive. Linux supports various interfaces that eschew copying,
such as sendfile and splice. The MSG_ZEROCOPY flag extends the
underlying copy avoidance mechanism to common socket send calls.

Copy avoidance is not a free lunch. As implemented, with page pinning,
it replaces per byte copy cost with page accounting and completion
notification overhead. As a result, MSG_ZEROCOPY is generally only
effective at writes over around 10 KB.

Page pinning also changes system call semantics. It temporarily shares
the buffer between process and network stack. Unlike with copying, the
process cannot immediately overwrite the buffer after system call
return without possibly modifying the data in flight. Kernel integrity
is not affected, but a buggy program can possibly corrupt its own data
stream.

The kernel returns a notification when it is safe to modify data.
Converting an existing application to MSG_ZEROCOPY is not always as
trivial as just passing the flag, then.


More Info
---------

Much of this document was derived from a longer paper presented at
netdev 2.1. For more in-depth information see that paper and talk,
the excellent reporting over at LWN.net or read the original code.

  paper, slides, video
    https://netdevconf.org/2.1/session.html?debruijn

  LWN article
    https://lwn.net/Articles/726917/

  patchset
    [PATCH net-next v4 0/9] socket sendmsg MSG_ZEROCOPY
    https://lore.kernel.org/netdev/20170803202945.70750-1-willemdebruijn.kernel@gmail.com

Socket 설정, 송신과 copy 혼용

57-99

Send syscall은 정의되지 않은 flag를 기본적으로 무시하므로 legacy process가 우연히 같은 bit를 쓰더라도 동작이 바뀌지 않게 먼저 `setsockopt(fd, SOL_SOCKET, SO_ZEROCOPY, ...)`로 zerocopy 의사를 명시해야 합니다. 그 뒤 `send`, `sendto`, `sendmsg`, `sendmmsg`에 `MSG_ZEROCOPY`를 전달합니다.

Socket의 optmem limit을 넘거나 user의 locked-page ulimit을 초과하면 syscall이 -1과 `ENOBUFS`를 반환합니다. 작은 packet에서는 copy avoidance가 오히려 비싸므로 flag가 있는 call과 없는 call을 같은 socket에서 안전하게 섞을 수 있습니다. Application은 buffer 크기에 따라 copy와 zerocopy를 선택하면 됩니다.

Interface
=========

Passing the MSG_ZEROCOPY flag is the most obvious step to enable copy
avoidance, but not the only one.

Socket Setup
------------

The kernel is permissive when applications pass undefined flags to the
send system call. By default it simply ignores these. To avoid enabling
copy avoidance mode for legacy processes that accidentally already pass
this flag, a process must first signal intent by setting a socket option:

::

        if (setsockopt(fd, SOL_SOCKET, SO_ZEROCOPY, &one, sizeof(one)))
                error(1, errno, "setsockopt zerocopy");

Transmission
------------

The change to send (or sendto, sendmsg, sendmmsg) itself is trivial.
Pass the new flag.

::

        ret = send(fd, buf, sizeof(buf), MSG_ZEROCOPY);

A zerocopy failure will return -1 with errno ENOBUFS. This happens if
the socket exceeds its optmem limit or the user exceeds their ulimit on
locked pages.


Mixing copy avoidance and copying
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

Many workloads have a mixture of large and small buffers. Because copy
avoidance is more expensive than copying for small packets, the
feature is implemented as a flag. It is safe to mix calls with the flag
with those without.

Completion notification 수신

100-148

Kernel은 이전에 넘긴 buffer를 안전하게 재사용할 수 있을 때 transmit timestamping과 비슷하게 socket error queue에 completion notification을 넣습니다. 각 socket은 unsigned 32-bit counter를 유지하며 data를 성공적으로 보낸 각 `MSG_ZEROCOPY` syscall마다 1 증가합니다. 실패하거나 길이가 0이면 증가하지 않고 byte가 아니라 syscall 횟수를 세며 `UINT_MAX` 다음에는 wrap됩니다.

가장 단순한 수신 방식은 `poll()`로 `POLLERR`를 기다린 뒤 `recvmsg(..., MSG_ERRQUEUE)`로 error queue를 읽는 것입니다. Error queue read 자체는 항상 non-blocking이고 error는 `events`에 `POLLERR`를 넣지 않아도 무조건 signal됩니다. 실제 application은 매 send마다 기다리기보다 몇 번 송신한 뒤 non-blocking으로 확인하는 편이 효율적입니다.

Notification은 socket의 다른 operation과 순서가 뒤바뀔 수 있습니다. 일반 error가 queue에 있으면 읽을 때까지 다른 operation을 막지만 zerocopy notification은 error code가 0이어서 send와 recv를 차단하지 않습니다.

Notifications
-------------

The kernel has to notify the process when it is safe to reuse a
previously passed buffer. It queues completion notifications on the
socket error queue, akin to the transmit timestamping interface.

The notification itself is a simple scalar value. Each socket
maintains an internal unsigned 32-bit counter. Each send call with
MSG_ZEROCOPY that successfully sends data increments the counter. The
counter is not incremented on failure or if called with length zero.
The counter counts system call invocations, not bytes. It wraps after
UINT_MAX calls.


Notification Reception
~~~~~~~~~~~~~~~~~~~~~~

The below snippet demonstrates the API. In the simplest case, each
send syscall is followed by a poll and recvmsg on the error queue.

Reading from the error queue is always a non-blocking operation. The
poll call is there to block until an error is outstanding. It will set
POLLERR in its output flags. That flag does not have to be set in the
events field. Errors are signaled unconditionally.

::

        pfd.fd = fd;
        pfd.events = 0;
        if (poll(&pfd, 1, -1) != 1 || pfd.revents & POLLERR == 0)
                error(1, errno, "poll");

        ret = recvmsg(fd, &msg, MSG_ERRQUEUE);
        if (ret == -1)
                error(1, errno, "recvmsg");

        read_notification(msg);

The example is for demonstration purpose only. In practice, it is more
efficient to not wait for notifications, but read without blocking
every couple of send calls.

Notifications can be processed out of order with other operations on
the socket. A socket that has an error queued would normally block
other operations until the error is read. Zerocopy notifications have
a zero error code, however, to not block send and recv calls.

Notification batching과 parsing

149-207

여러 outstanding packet의 notification은 `recvmmsg()`로 한 번에 읽을 수 있지만 kernel이 연속 notification을 range로 합치므로 꼭 필요하지는 않습니다. 새 value가 error queue tail notification의 range를 연장하면 새 notification packet을 버리고 기존 range의 upper value만 늘립니다. TCP처럼 in-order acknowledgement인 protocol은 보통 하나의 outstanding notification으로 계속 합칠 수 있습니다. 다만 retransmission이나 socket teardown에서는 out-of-order notification이 가능합니다.

Notification은 표준 `sock_extended_err` control message로 encoding됩니다. TCP·UDP는 protocol family에 따라 `IP_RECVERR` 또는 `IPV6_RECVERR`, VSOCK은 `SOL_VSOCK` level과 `VSOCK_RECVERR` type을 사용합니다. `ee_origin`은 `SO_EE_ORIGIN_ZEROCOPY`, `ee_errno`는 다른 socket I/O를 막지 않도록 0입니다.

32-bit inclusive completion range는 `[ee_info, ee_data]`에 들어갑니다. 아래에서 설명하는 `ee_code`를 제외한 나머지 `sock_extended_err` field는 정의되지 않은 값으로 취급해야 합니다.

Zerocopy notification field
Field값·의미
ee_errno0
ee_originSO_EE_ORIGIN_ZEROCOPY
ee_info완료 range 시작
ee_data완료 range 끝, inclusive
ee_codedeferred copy 여부

Completion control message에서 유효한 값입니다.

Notification Batching
~~~~~~~~~~~~~~~~~~~~~

Multiple outstanding packets can be read at once using the recvmmsg
call. This is often not needed. In each message the kernel returns not
a single value, but a range. It coalesces consecutive notifications
while one is outstanding for reception on the error queue.

When a new notification is about to be queued, it checks whether the
new value extends the range of the notification at the tail of the
queue. If so, it drops the new notification packet and instead increases
the range upper value of the outstanding notification.

For protocols that acknowledge data in-order, like TCP, each
notification can be squashed into the previous one, so that no more
than one notification is outstanding at any one point.

Ordered delivery is the common case, but not guaranteed. Notifications
may arrive out of order on retransmission and socket teardown.


Notification Parsing
~~~~~~~~~~~~~~~~~~~~

The below snippet demonstrates how to parse the control message: the
read_notification() call in the previous snippet. A notification
is encoded in the standard error format, sock_extended_err.

The level and type fields in the control data are protocol family
specific, IP_RECVERR or IPV6_RECVERR (for TCP or UDP socket).
For VSOCK socket, cmsg_level will be SOL_VSOCK and cmsg_type will be
VSOCK_RECVERR.

Error origin is the new type SO_EE_ORIGIN_ZEROCOPY. ee_errno is zero,
as explained before, to avoid blocking read and write system calls on
the socket.

The 32-bit notification range is encoded as [ee_info, ee_data]. This
range is inclusive. Other fields in the struct must be treated as
undefined, bar for ee_code, as discussed below.

::

        struct sock_extended_err *serr;
        struct cmsghdr *cm;

        cm = CMSG_FIRSTHDR(msg);
        if (cm->cmsg_level != SOL_IP &&
            cm->cmsg_type != IP_RECVERR)
                error(1, 0, "cmsg");

        serr = (void *) CMSG_DATA(cm);
        if (serr->ee_errno != 0 ||
            serr->ee_origin != SO_EE_ORIGIN_ZEROCOPY)
                error(1, 0, "serr");

        printf("completed: %u..%u\n", serr->ee_info, serr->ee_data);

Deferred copy와 completion 의미

208-234

`MSG_ZEROCOPY`는 kernel에 copy avoidance를 요청하는 hint이자 completion notification을 반드시 queue하라는 계약이지 실제 copy가 항상 생략된다는 보장은 아닙니다. Scatter-gather I/O를 지원하지 않는 device는 kernel protocol header와 zerocopy user data로 구성된 packet을 보낼 수 없고, stack 깊은 곳에서 checksum 계산 등을 위해 private copy로 바꿀 수도 있습니다.

이 경우에도 kernel은 shared page hold를 놓을 때 notification을 보내며 copied data의 실제 송신이 끝나기 전에 올 수 있습니다. 따라서 zerocopy completion은 transmit completion이 아닙니다. Cache가 식은 뒤의 deferred copy와 불필요한 notification 처리는 즉시 copy보다 비쌀 수 있습니다.

Kernel은 copy로 완료한 경우 `ee_code`에 `SO_EE_CODE_ZEROCOPY_COPIED`를 설정합니다. Application은 이 signal을 보고 같은 socket의 이후 request에서 `MSG_ZEROCOPY` 사용을 중단할 수 있습니다.

Deferred copies
~~~~~~~~~~~~~~~

Passing flag MSG_ZEROCOPY is a hint to the kernel to apply copy
avoidance, and a contract that the kernel will queue a completion
notification. It is not a guarantee that the copy is elided.

Copy avoidance is not always feasible. Devices that do not support
scatter-gather I/O cannot send packets made up of kernel generated
protocol headers plus zerocopy user data. A packet may need to be
converted to a private copy of data deep in the stack, say to compute
a checksum.

In all these cases, the kernel returns a completion notification when
it releases its hold on the shared pages. That notification may arrive
before the (copied) data is fully transmitted. A zerocopy completion
notification is not a transmit completion notification, therefore.

Deferred copies can be more expensive than a copy immediately in the
system call, if the data is no longer warm in the cache. The process
also incurs notification processing cost for no benefit. For this
reason, the kernel signals if data was completed with a copy, by
setting flag SO_EE_CODE_ZEROCOPY_COPIED in field ee_code on return.
A process may use this signal to stop passing flag MSG_ZEROCOPY on
subsequent requests on the same socket.

Loopback 구현과 test

235-265

TCP와 UDP에서 local socket으로 보낸 data는 receiver가 읽지 않으면 무기한 queue될 수 있어 notification latency가 제한되지 않습니다. 이를 피하려고 local socket, packet socket(tcpdump 등), tun device로 loop되는 모든 `MSG_ZEROCOPY` packet은 deferred copy를 수행합니다. VSOCK은 local과 non-local socket의 data path가 같습니다.

현실적인 TCP·UDP 예제는 `tools/testing/selftests/net/msg_zerocopy.c`에 있습니다. 두 host 사이에서 시험해야 하며, namespace를 가로지르는 veth처럼 local process pair에서 실행하면 loopback 제약 때문에 성능 향상이 나타나지 않습니다. Test 목적으로는 `skb_orphan_frags_rx`를 `skb_orphan_frags`와 같게 만들어 제약을 잠시 완화할 수 있습니다. VSOCK 예제는 `tools/testing/vsock/vsock_test_zerocopy.c`입니다.

Implementation
==============

Loopback
--------

For TCP and UDP:
Data sent to local sockets can be queued indefinitely if the receive
process does not read its socket. Unbound notification latency is not
acceptable. For this reason all packets generated with MSG_ZEROCOPY
that are looped to a local socket will incur a deferred copy. This
includes looping onto packet sockets (e.g., tcpdump) and tun devices.

For VSOCK:
Data path sent to local sockets is the same as for non-local sockets.

Testing
=======

More realistic example code can be found in the kernel source under
tools/testing/selftests/net/msg_zerocopy.c.

Be cognizant of the loopback constraint. The test can be run between
a pair of hosts. But if run between a local pair of processes, for
instance when run with msg_zerocopy.sh between a veth pair across
namespaces, the test will not show any improvement. For testing, the
loopback restriction can be temporarily relaxed by making
skb_orphan_frags_rx identical to skb_orphan_frags.

For VSOCK type of socket example can be found in
tools/testing/vsock/vsock_test_zerocopy.c.