Skip to content

Commit eea48c6

Browse files
committed
virtio-net: enable IPv6 checksum and TSO offload
The virtio-net offload paths were hardcoded to ETHERTYPE_IP: the TX path only built VIRTIO_NET_HDR_GSO_TCPV4 and the RX checksum path rejected everything but IPv4 (with a literal "How come - no support for IPv6?!" comment). As a result IPv6 traffic computed checksums in software and never used TCP segmentation offload, so high-throughput IPv6 sends/receives burned more CPU than IPv4 even though the stack (tcp_output) already emits CSUM_TCP_IPV6. This teaches the driver about IPv6: - negotiate VIRTIO_NET_F_HOST_TSO6 / VIRTIO_NET_F_GUEST_TSO6 - advertise IFCAP_TSO6 and CSUM_TCP_IPV6 | CSUM_UDP_IPV6 in if_hwassist/caps - TX: add an ETHERTYPE_IPV6 case that sets csum_start past the 40-byte base header and gso_type = VIRTIO_NET_HDR_GSO_TCPV6 (packets with extension headers have ip6_nxt != TCP/UDP and fall back to software checksums) - RX: accept ETHERTYPE_IPV6 in the checksum-offload path (the TCP/UDP checksum offset logic is identical for v4 and v6) - use large receive buffers when GUEST_TSO6 is negotiated too Boot-tested with IPv6 SLAAC over virtio-net: loopback + concurrent dual-stack TCP pass and a global address is configured through the modified TX/RX paths.
1 parent b06d3df commit eea48c6

2 files changed

Lines changed: 39 additions & 6 deletions

File tree

drivers/virtio-net.cc

Lines changed: 37 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@
3636
#include <bsd/sys/net/if_vlan_var.h>
3737
#include <bsd/sys/netinet/in.h>
3838
#include <bsd/sys/netinet/ip.h>
39+
#include <bsd/sys/netinet/ip6.h>
3940
#include <bsd/sys/netinet/udp.h>
4041
#include <bsd/sys/netinet/tcp.h>
4142

@@ -283,15 +284,22 @@ net::net(virtio_device& dev)
283284

284285
if (_csum) {
285286
_ifn->if_capabilities |= IFCAP_TXCSUM;
287+
// Advertise IPv6 checksum offload alongside IPv4; tcp_output already
288+
// sets CSUM_TCP_IPV6 for v6 segments.
289+
_ifn->if_hwassist = CSUM_TCP | CSUM_UDP | CSUM_TCP_IPV6 | CSUM_UDP_IPV6;
286290
if (_host_tso4) {
287291
_ifn->if_capabilities |= IFCAP_TSO4;
288-
_ifn->if_hwassist = CSUM_TCP | CSUM_UDP | CSUM_TSO;
292+
_ifn->if_hwassist |= CSUM_TSO;
293+
}
294+
if (_host_tso6) {
295+
_ifn->if_capabilities |= IFCAP_TSO6;
296+
_ifn->if_hwassist |= CSUM_TSO;
289297
}
290298
}
291299

292300
if (_guest_csum) {
293301
_ifn->if_capabilities |= IFCAP_RXCSUM;
294-
if (_guest_tso4)
302+
if (_guest_tso4 || _guest_tso6)
295303
_ifn->if_capabilities |= IFCAP_LRO;
296304
}
297305

@@ -382,18 +390,21 @@ void net::read_config()
382390
_guest_csum = get_guest_feature_bit(VIRTIO_NET_F_GUEST_CSUM);
383391
_guest_tso4 = get_guest_feature_bit(VIRTIO_NET_F_GUEST_TSO4);
384392
_host_tso4 = get_guest_feature_bit(VIRTIO_NET_F_HOST_TSO4);
393+
_guest_tso6 = get_guest_feature_bit(VIRTIO_NET_F_GUEST_TSO6);
394+
_host_tso6 = get_guest_feature_bit(VIRTIO_NET_F_HOST_TSO6);
385395
_guest_ufo = get_guest_feature_bit(VIRTIO_NET_F_GUEST_UFO);
386396

387397
net_i("Features: %s=%d,%s=%d", "Status", _status, "TSO_ECN", _tso_ecn);
388398
net_i("Features: %s=%d,%s=%d", "Host TSO ECN", _host_tso_ecn, "CSUM", _csum);
389399
net_i("Features: %s=%d,%s=%d", "Guest_csum", _guest_csum, "guest tso4", _guest_tso4);
390400
net_i("Features: %s=%d,%s=%d", "host tso4", _host_tso4, "MRG_RX_BUF", _mergeable_bufs);
401+
net_i("Features: %s=%d,%s=%d", "guest tso6", _guest_tso6, "host tso6", _host_tso6);
391402

392-
// If VIRTIO_NET_F_MRG_RXBUF is not negotiated and VIRTIO_NET_F_GUEST_TSO4
403+
// If VIRTIO_NET_F_MRG_RXBUF is not negotiated and VIRTIO_NET_F_GUEST_TSO4/6
393404
// or VIRTIO_NET_F_GUEST_UFO are, the VirtIO spec mandates the guest to use
394405
// large receive buffers
395406
// For details please see "5.1.6.3.1 Driver Requirements: Setting Up Receive Buffers" in VirtIO spec
396-
if (!_mergeable_bufs && (_guest_tso4 || _guest_ufo)) {
407+
if (!_mergeable_bufs && (_guest_tso4 || _guest_tso6 || _guest_ufo)) {
397408
net_i("Set up to use large receive buffers");
398409
_use_large_buffers = true;
399410
}
@@ -431,8 +442,9 @@ bool net::bad_rx_csum(struct mbuf* m, struct net_hdr* hdr)
431442
eth_type = ntohs(evh->evl_proto);
432443
}
433444

434-
// How come - no support for IPv6?!
435-
if (eth_type != ETHERTYPE_IP) {
445+
// The checksum handling below keys off csum_offset (the TCP/UDP checksum
446+
// position), which is the same for IPv4 and IPv6, so both are accepted.
447+
if (eth_type != ETHERTYPE_IP && eth_type != ETHERTYPE_IPV6) {
436448
return true;
437449
}
438450

@@ -829,6 +841,23 @@ mbuf* net::txq::offload(mbuf* m, net_hdr* hdr)
829841
gso_type = net::net_hdr::VIRTIO_NET_HDR_GSO_TCPV4;
830842
break;
831843

844+
case ETHERTYPE_IPV6: {
845+
if (m->m_hdr.mh_len < ip_offset + (int)sizeof(struct ip6_hdr)) {
846+
m = m_pullup(m, ip_offset + sizeof(struct ip6_hdr));
847+
if (m == nullptr)
848+
return nullptr;
849+
}
850+
851+
struct ip6_hdr* ip6 = (struct ip6_hdr*)(mtod(m, uint8_t*) + ip_offset);
852+
ip_proto = ip6->ip6_nxt;
853+
// Offload only handles the common no-extension-header case; a packet
854+
// with extension headers has ip6_nxt != TCP/UDP and falls through to
855+
// the software checksum path below (csum_flags are then ignored).
856+
csum_start = ip_offset + sizeof(struct ip6_hdr);
857+
gso_type = net::net_hdr::VIRTIO_NET_HDR_GSO_TCPV6;
858+
break;
859+
}
860+
832861
default:
833862
return m;
834863
}
@@ -914,8 +943,10 @@ u64 net::get_driver_features()
914943
| (1 << VIRTIO_NET_F_CSUM) \
915944
| (1 << VIRTIO_NET_F_GUEST_CSUM) \
916945
| (1 << VIRTIO_NET_F_GUEST_TSO4) \
946+
| (1 << VIRTIO_NET_F_GUEST_TSO6) \
917947
| (1 << VIRTIO_NET_F_HOST_ECN) \
918948
| (1 << VIRTIO_NET_F_HOST_TSO4) \
949+
| (1 << VIRTIO_NET_F_HOST_TSO6) \
919950
| (1 << VIRTIO_NET_F_GUEST_ECN)
920951
| (1 << VIRTIO_NET_F_GUEST_UFO)
921952
);

drivers/virtio-net.hh

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -273,6 +273,8 @@ private:
273273
bool _guest_csum = false;
274274
bool _guest_tso4 = false;
275275
bool _host_tso4 = false;
276+
bool _guest_tso6 = false;
277+
bool _host_tso6 = false;
276278
bool _guest_ufo = false;
277279
bool _use_large_buffers = false;
278280

0 commit comments

Comments
 (0)