요약·해설과 원문, 전문 번역을 서로 분리했습니다. API 이름, symbol, source path는 원문 표기를 사용합니다.
1. 요약·해설
원문의 핵심 논리와 kernel programming 관점의 보충 설명입니다. 아래의 전문 번역과는 별도로 작성했습니다.
2. 영어 원문 전체
번역 기준이 된 Linux v6.18.37 원문입니다. 줄 번호는 이 버전의 파일 좌표입니다.
원문 전체 펼치기
.. SPDX-License-Identifier: GPL-2.0
=================
Checksum Offloads
=================
Introduction
============
This document describes a set of techniques in the Linux networking stack to
take advantage of checksum offload capabilities of various NICs.
The following technologies are described:
* TX Checksum Offload
* LCO: Local Checksum Offload
* RCO: Remote Checksum Offload
Things that should be documented here but aren't yet:
* RX Checksum Offload
* CHECKSUM_UNNECESSARY conversion
TX Checksum Offload
===================
The interface for offloading a transmit checksum to a device is explained in
detail in comments near the top of include/linux/skbuff.h.
In brief, it allows to request the device fill in a single ones-complement
checksum defined by the sk_buff fields skb->csum_start and skb->csum_offset.
The device should compute the 16-bit ones-complement checksum (i.e. the
'IP-style' checksum) from csum_start to the end of the packet, and fill in the
result at (csum_start + csum_offset).
Because csum_offset cannot be negative, this ensures that the previous value of
the checksum field is included in the checksum computation, thus it can be used
to supply any needed corrections to the checksum (such as the sum of the
pseudo-header for UDP or TCP).
This interface only allows a single checksum to be offloaded. Where
encapsulation is used, the packet may have multiple checksum fields in
different header layers, and the rest will have to be handled by another
mechanism such as LCO or RCO.
CRC32c can also be offloaded using this interface, by means of filling
skb->csum_start and skb->csum_offset as described above, and setting
skb->csum_not_inet: see skbuff.h comment (section 'D') for more details.
No offloading of the IP header checksum is performed; it is always done in
software. This is OK because when we build the IP header, we obviously have it
in cache, so summing it isn't expensive. It's also rather short.
The requirements for GSO are more complicated, because when segmenting an
encapsulated packet both the inner and outer checksums may need to be edited or
recomputed for each resulting segment. See the skbuff.h comment (section 'E')
for more details.
A driver declares its offload capabilities in netdev->hw_features; see
Documentation/networking/netdev-features.rst for more. Note that a device
which only advertises NETIF_F_IP[V6]_CSUM must still obey the csum_start and
csum_offset given in the SKB; if it tries to deduce these itself in hardware
(as some NICs do) the driver should check that the values in the SKB match
those which the hardware will deduce, and if not, fall back to checksumming in
software instead (with skb_csum_hwoffload_help() or one of the
skb_checksum_help() / skb_crc32c_csum_help functions, as mentioned in
include/linux/skbuff.h).
The stack should, for the most part, assume that checksum offload is supported
by the underlying device. The only place that should check is
validate_xmit_skb(), and the functions it calls directly or indirectly. That
function compares the offload features requested by the SKB (which may include
other offloads besides TX Checksum Offload) and, if they are not supported or
enabled on the device (determined by netdev->features), performs the
corresponding offload in software. In the case of TX Checksum Offload, that
means calling skb_csum_hwoffload_help(skb, features).
LCO: Local Checksum Offload
===========================
LCO is a technique for efficiently computing the outer checksum of an
encapsulated datagram when the inner checksum is due to be offloaded.
The ones-complement sum of a correctly checksummed TCP or UDP packet is equal
to the complement of the sum of the pseudo header, because everything else gets
'cancelled out' by the checksum field. This is because the sum was
complemented before being written to the checksum field.
More generally, this holds in any case where the 'IP-style' ones complement
checksum is used, and thus any checksum that TX Checksum Offload supports.
That is, if we have set up TX Checksum Offload with a start/offset pair, we
know that after the device has filled in that checksum, the ones complement sum
from csum_start to the end of the packet will be equal to the complement of
whatever value we put in the checksum field beforehand. This allows us to
compute the outer checksum without looking at the payload: we simply stop
summing when we get to csum_start, then add the complement of the 16-bit word
at (csum_start + csum_offset).
Then, when the true inner checksum is filled in (either by hardware or by
skb_checksum_help()), the outer checksum will become correct by virtue of the
arithmetic.
LCO is performed by the stack when constructing an outer UDP header for an
encapsulation such as VXLAN or GENEVE, in udp_set_csum(). Similarly for the
IPv6 equivalents, in udp6_set_csum().
It is also performed when constructing an IPv4 GRE header, in
net/ipv4/ip_gre.c:build_header(). It is *not* currently performed when
constructing an IPv6 GRE header; the GRE checksum is computed over the whole
packet in net/ipv6/ip6_gre.c:ip6gre_xmit2(), but it should be possible to use
LCO here as IPv6 GRE still uses an IP-style checksum.
All of the LCO implementations use a helper function lco_csum(), in
include/linux/skbuff.h.
LCO can safely be used for nested encapsulations; in this case, the outer
encapsulation layer will sum over both its own header and the 'middle' header.
This does mean that the 'middle' header will get summed multiple times, but
there doesn't seem to be a way to avoid that without incurring bigger costs
(e.g. in SKB bloat).
RCO: Remote Checksum Offload
============================
RCO is a technique for eliding the inner checksum of an encapsulated datagram,
allowing the outer checksum to be offloaded. It does, however, involve a
change to the encapsulation protocols, which the receiver must also support.
For this reason, it is disabled by default.
RCO is detailed in the following Internet-Drafts:
* https://tools.ietf.org/html/draft-herbert-remotecsumoffload-00
* https://tools.ietf.org/html/draft-herbert-vxlan-rco-00
In Linux, RCO is implemented individually in each encapsulation protocol, and
most tunnel types have flags controlling its use. For instance, VXLAN has the
flag VXLAN_F_REMCSUM_TX (per struct vxlan_rdst) to indicate that RCO should be
used when transmitting to a given remote destination.
3. 한국어 전문 번역
영어 원문의 문단 순서와 의미를 유지한 전체 번역입니다. 코드, 함수명, symbol과 URL은 원문 표기를 유지합니다.
Checksum offload 개요와 문서 범위
1-22Checksum Offload
소개
이 문서는 다양한 NIC의 checksum offload 기능을 활용하기 위해 Linux networking stack에서 사용하는 여러 기법을 설명합니다.
여기서 다루는 기술은 다음과 같습니다.
- TX Checksum Offload
- LCO(Local Checksum Offload)
- RCO(Remote Checksum Offload)
다음 항목도 이 문서에서 다뤄야 하지만 아직 작성되지 않았습니다.
- RX Checksum Offload
- `CHECKSUM_UNNECESSARY` 변환
.. SPDX-License-Identifier: GPL-2.0
=================
Checksum Offloads
=================
Introduction
============
This document describes a set of techniques in the Linux networking stack to
take advantage of checksum offload capabilities of various NICs.
The following technologies are described:
* TX Checksum Offload
* LCO: Local Checksum Offload
* RCO: Remote Checksum Offload
Things that should be documented here but aren't yet:
* RX Checksum Offload
TX Checksum Offload interface와 fallback
23-81TX Checksum Offload
송신 checksum 계산을 device에 offload하는 interface는 `include/linux/skbuff.h` 앞부분의 주석에 자세히 설명되어 있습니다.
간단히 말해 이 interface는 `sk_buff`의 `skb->csum_start`와 `skb->csum_offset` field로 정의한 단일 1의 보수 checksum을 device가 채우도록 요청합니다. Device는 `csum_start`부터 packet 끝까지 16-bit 1의 보수 checksum, 즉 IP 방식 checksum을 계산하고 그 결과를 `(csum_start + csum_offset)` 위치에 써야 합니다.
`csum_offset`은 음수가 될 수 없으므로 checksum field의 이전 값도 checksum 계산에 반드시 포함됩니다. 따라서 이 값으로 UDP나 TCP의 pseudo-header 합처럼 checksum에 필요한 보정값을 제공할 수 있습니다.
이 interface는 checksum 하나만 offload할 수 있습니다. Encapsulation을 사용한 packet에는 서로 다른 header layer에 checksum field가 여러 개 있을 수 있으므로 나머지는 LCO나 RCO 같은 다른 기법으로 처리해야 합니다.
위와 같이 `skb->csum_start`와 `skb->csum_offset`을 채우고 `skb->csum_not_inet`을 설정하면 이 interface로 CRC32c도 offload할 수 있습니다. 자세한 내용은 `skbuff.h` 주석의 D절을 참고하십시오.
IP header checksum은 offload하지 않고 언제나 software에서 계산합니다. IP header를 만들 때는 해당 데이터가 이미 cache에 있으므로 합산 비용이 크지 않고 header 자체도 상당히 짧기 때문입니다.
GSO 요구 사항은 더 복잡합니다. Encapsulation된 packet을 segment로 나누면 결과 segment마다 inner checksum과 outer checksum을 모두 수정하거나 다시 계산해야 할 수 있습니다. 자세한 내용은 `skbuff.h` 주석의 E절을 참고하십시오.
Driver는 `netdev->hw_features`에 offload capability를 선언합니다. 자세한 내용은 `Documentation/networking/netdev-features.rst`를 참고하십시오. `NETIF_F_IP[V6]_CSUM`만 알리는 device도 SKB가 지정한 `csum_start`와 `csum_offset`을 따라야 합니다.
일부 NIC처럼 hardware가 이 위치를 자체적으로 추론한다면 driver는 SKB의 값이 hardware가 추론할 값과 일치하는지 검사해야 합니다. 일치하지 않으면 `skb_csum_hwoffload_help()`, `skb_checksum_help()` 또는 `skb_crc32c_csum_help()`를 사용해 software checksum으로 fallback해야 합니다. 이 helper들은 `include/linux/skbuff.h`에도 언급되어 있습니다.
Networking stack은 대체로 아래쪽 device가 checksum offload를 지원한다고 가정해야 합니다. 지원 여부를 검사해야 하는 곳은 `validate_xmit_skb()`와 이 함수가 직접 또는 간접 호출하는 함수뿐입니다.
`validate_xmit_skb()`는 SKB가 요청한 offload feature와 `netdev->features`로 확인한 device의 지원·활성 feature를 비교합니다. 요청 feature에는 TX Checksum Offload 외의 offload도 포함될 수 있습니다. Device가 해당 feature를 지원하지 않거나 비활성화했다면 이 함수가 software로 해당 작업을 수행합니다. TX Checksum Offload의 경우 `skb_csum_hwoffload_help(skb, features)`를 호출합니다.
* CHECKSUM_UNNECESSARY conversion
TX Checksum Offload
===================
The interface for offloading a transmit checksum to a device is explained in
detail in comments near the top of include/linux/skbuff.h.
In brief, it allows to request the device fill in a single ones-complement
checksum defined by the sk_buff fields skb->csum_start and skb->csum_offset.
The device should compute the 16-bit ones-complement checksum (i.e. the
'IP-style' checksum) from csum_start to the end of the packet, and fill in the
result at (csum_start + csum_offset).
Because csum_offset cannot be negative, this ensures that the previous value of
the checksum field is included in the checksum computation, thus it can be used
to supply any needed corrections to the checksum (such as the sum of the
pseudo-header for UDP or TCP).
This interface only allows a single checksum to be offloaded. Where
encapsulation is used, the packet may have multiple checksum fields in
different header layers, and the rest will have to be handled by another
mechanism such as LCO or RCO.
CRC32c can also be offloaded using this interface, by means of filling
skb->csum_start and skb->csum_offset as described above, and setting
skb->csum_not_inet: see skbuff.h comment (section 'D') for more details.
No offloading of the IP header checksum is performed; it is always done in
software. This is OK because when we build the IP header, we obviously have it
in cache, so summing it isn't expensive. It's also rather short.
The requirements for GSO are more complicated, because when segmenting an
encapsulated packet both the inner and outer checksums may need to be edited or
recomputed for each resulting segment. See the skbuff.h comment (section 'E')
for more details.
A driver declares its offload capabilities in netdev->hw_features; see
Documentation/networking/netdev-features.rst for more. Note that a device
which only advertises NETIF_F_IP[V6]_CSUM must still obey the csum_start and
csum_offset given in the SKB; if it tries to deduce these itself in hardware
(as some NICs do) the driver should check that the values in the SKB match
those which the hardware will deduce, and if not, fall back to checksumming in
software instead (with skb_csum_hwoffload_help() or one of the
skb_checksum_help() / skb_crc32c_csum_help functions, as mentioned in
include/linux/skbuff.h).
The stack should, for the most part, assume that checksum offload is supported
by the underlying device. The only place that should check is
validate_xmit_skb(), and the functions it calls directly or indirectly. That
function compares the offload features requested by the SKB (which may include
other offloads besides TX Checksum Offload) and, if they are not supported or
enabled on the device (determined by netdev->features), performs the
corresponding offload in software. In the case of TX Checksum Offload, that
means calling skb_csum_hwoffload_help(skb, features).
LCO: Local Checksum Offload
LCO의 1의 보수 산술과 tunnel 적용
82-126LCO: Local Checksum Offload
LCO는 inner checksum을 offload할 예정인 encapsulated datagram에서 outer checksum을 효율적으로 계산하는 기법입니다.
Checksum이 올바른 TCP 또는 UDP packet의 1의 보수 합은 pseudo-header 합의 보수와 같습니다. Checksum field를 제외한 나머지 값은 checksum field에 의해 서로 상쇄되기 때문입니다. 이는 합을 checksum field에 쓰기 전에 보수로 만들었기 때문에 성립합니다.
더 일반적으로 이 성질은 IP 방식 1의 보수 checksum을 쓰는 모든 경우, 즉 TX Checksum Offload가 지원하는 모든 checksum에 적용됩니다.
따라서 start/offset 쌍으로 TX Checksum Offload를 설정했다면 device가 checksum을 채운 뒤 `csum_start`부터 packet 끝까지의 1의 보수 합이 checksum field에 미리 넣어 둔 값의 보수와 같아진다는 사실을 알 수 있습니다.
이 성질을 이용하면 payload를 읽지 않고 outer checksum을 계산할 수 있습니다. `csum_start`에 도달하면 합산을 멈추고 `(csum_start + csum_offset)` 위치의 16-bit word에 대한 보수를 더하기만 하면 됩니다.
그 뒤 실제 inner checksum을 hardware 또는 `skb_checksum_help()`가 채우면 1의 보수 산술에 따라 outer checksum도 올바른 값이 됩니다.
Stack은 VXLAN이나 GENEVE 같은 encapsulation의 outer UDP header를 만들 때 `udp_set_csum()`에서 LCO를 수행합니다. IPv6에 대응하는 작업은 `udp6_set_csum()`에서 수행합니다.
IPv4 GRE header를 만들 때도 `net/ipv4/ip_gre.c:build_header()`에서 LCO를 수행합니다. 현재 IPv6 GRE header를 만들 때는 LCO를 수행하지 않습니다. 대신 `net/ipv6/ip6_gre.c:ip6gre_xmit2()`가 packet 전체의 GRE checksum을 계산합니다. IPv6 GRE 역시 IP 방식 checksum을 사용하므로 여기에도 LCO를 적용할 수 있을 것입니다.
모든 LCO 구현은 `include/linux/skbuff.h`에 있는 `lco_csum()` helper function을 사용합니다.
LCO는 nested encapsulation에도 안전하게 사용할 수 있습니다. 이 경우 outer encapsulation layer는 자신의 header와 중간 header를 모두 합산합니다. 이 때문에 중간 header가 여러 번 합산되지만 SKB 크기 증가 같은 더 큰 비용 없이 이를 피할 방법은 없어 보입니다.
===========================
LCO is a technique for efficiently computing the outer checksum of an
encapsulated datagram when the inner checksum is due to be offloaded.
The ones-complement sum of a correctly checksummed TCP or UDP packet is equal
to the complement of the sum of the pseudo header, because everything else gets
'cancelled out' by the checksum field. This is because the sum was
complemented before being written to the checksum field.
More generally, this holds in any case where the 'IP-style' ones complement
checksum is used, and thus any checksum that TX Checksum Offload supports.
That is, if we have set up TX Checksum Offload with a start/offset pair, we
know that after the device has filled in that checksum, the ones complement sum
from csum_start to the end of the packet will be equal to the complement of
whatever value we put in the checksum field beforehand. This allows us to
compute the outer checksum without looking at the payload: we simply stop
summing when we get to csum_start, then add the complement of the 16-bit word
at (csum_start + csum_offset).
Then, when the true inner checksum is filled in (either by hardware or by
skb_checksum_help()), the outer checksum will become correct by virtue of the
arithmetic.
LCO is performed by the stack when constructing an outer UDP header for an
encapsulation such as VXLAN or GENEVE, in udp_set_csum(). Similarly for the
IPv6 equivalents, in udp6_set_csum().
It is also performed when constructing an IPv4 GRE header, in
net/ipv4/ip_gre.c:build_header(). It is *not* currently performed when
constructing an IPv6 GRE header; the GRE checksum is computed over the whole
packet in net/ipv6/ip6_gre.c:ip6gre_xmit2(), but it should be possible to use
LCO here as IPv6 GRE still uses an IP-style checksum.
All of the LCO implementations use a helper function lco_csum(), in
include/linux/skbuff.h.
LCO can safely be used for nested encapsulations; in this case, the outer
encapsulation layer will sum over both its own header and the 'middle' header.
This does mean that the 'middle' header will get summed multiple times, but
there doesn't seem to be a way to avoid that without incurring bigger costs
(e.g. in SKB bloat).
RCO protocol 지원과 Linux tunnel flag
127-143RCO: Remote Checksum Offload
RCO는 encapsulated datagram의 inner checksum을 생략하여 outer checksum을 offload할 수 있게 하는 기법입니다. 다만 encapsulation protocol을 변경해야 하고 receiver도 이를 지원해야 합니다. 이런 이유로 기본값은 비활성화입니다.
RCO의 자세한 내용은 다음 Internet-Draft에서 설명합니다.
- https://tools.ietf.org/html/draft-herbert-remotecsumoffload-00
- https://tools.ietf.org/html/draft-herbert-vxlan-rco-00
Linux에서 RCO는 각 encapsulation protocol이 개별적으로 구현하며, 대부분의 tunnel type에는 사용 여부를 제어하는 flag가 있습니다. 예를 들어 VXLAN은 특정 remote destination으로 송신할 때 RCO를 사용해야 함을 나타내기 위해 `struct vxlan_rdst`별 `VXLAN_F_REMCSUM_TX` flag를 둡니다.
RCO: Remote Checksum Offload
============================
RCO is a technique for eliding the inner checksum of an encapsulated datagram,
allowing the outer checksum to be offloaded. It does, however, involve a
change to the encapsulation protocols, which the receiver must also support.
For this reason, it is disabled by default.
RCO is detailed in the following Internet-Drafts:
* https://tools.ietf.org/html/draft-herbert-remotecsumoffload-00
* https://tools.ietf.org/html/draft-herbert-vxlan-rco-00
In Linux, RCO is implemented individually in each encapsulation protocol, and
most tunnel types have flags controlling its use. For instance, VXLAN has the
flag VXLAN_F_REMCSUM_TX (per struct vxlan_rdst) to indicate that RCO should be
used when transmitting to a given remote destination.
요약·해설
checksum-offloads.rst:1-143TX Checksum Offload는 SKB가 지정한 한 checksum 계산을 NIC에 맡기는 interface입니다. Tunnel packet처럼 여러 checksum layer가 있을 때는 LCO가 inner checksum의 1의 보수 성질로 outer checksum을 미리 계산하거나, RCO가 receiver와의 protocol 합의를 통해 inner checksum 자체를 생략합니다. Device feature가 맞지 않으면 송신 검증 단계에서 software helper로 fallback합니다.
세 기법이 계산을 줄이는 위치와 요구 조건을 비교합니다.
SKB의 두 field가 계산 범위와 결과를 쓸 위치를 정의합니다.
Hardware와 driver가 SKB 요청을 해석할 때 지켜야 할 경계입니다.
Stack은 늦은 송신 검증 지점에서 실제 device capability를 대조합니다.
Inner checksum이 나중에 채워져도 outer checksum이 맞아지는 산술 관계입니다.
현재 구현 위치와 아직 최적화되지 않은 경로를 정리합니다.
여러 tunnel layer에서도 계산은 안전하지만 중간 header를 반복 합산합니다.
RCO는 local 최적화가 아니라 양 끝점의 protocol 지원이 필요한 최적화입니다.