Changeset View
Changeset View
Standalone View
Standalone View
sys/dev/virtio/network/virtio_net.h
| Show All 25 Lines | |||||
| * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY | ||||
| * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF | ||||
| * SUCH DAMAGE. | * SUCH DAMAGE. | ||||
| */ | */ | ||||
| #ifndef _VIRTIO_NET_H | #ifndef _VIRTIO_NET_H | ||||
| #define _VIRTIO_NET_H | #define _VIRTIO_NET_H | ||||
| #include "opt_inet.h" | |||||
| #include "opt_inet6.h" | |||||
| #include <dev/virtio/virtio_endian.h> | |||||
| /* The feature bitmap for virtio net */ | /* The feature bitmap for virtio net */ | ||||
| #define VIRTIO_NET_F_CSUM (1ULL << 0) /* Host handles pkts w/ partial csum */ | #define VIRTIO_NET_F_CSUM (1ULL << 0) /* Host handles pkts w/ partial csum */ | ||||
| #define VIRTIO_NET_F_GUEST_CSUM (1ULL << 1) /* Guest handles pkts w/ partial csum*/ | #define VIRTIO_NET_F_GUEST_CSUM (1ULL << 1) /* Guest handles pkts w/ partial csum*/ | ||||
| #define VIRTIO_NET_F_CTRL_GUEST_OFFLOADS (1ULL << 2) /* Dynamic offload configuration. */ | #define VIRTIO_NET_F_CTRL_GUEST_OFFLOADS (1ULL << 2) /* Dynamic offload configuration. */ | ||||
| #define VIRTIO_NET_F_MTU (1ULL << 3) /* Initial MTU advice */ | #define VIRTIO_NET_F_MTU (1ULL << 3) /* Initial MTU advice */ | ||||
| #define VIRTIO_NET_F_MAC (1ULL << 5) /* Host has given MAC address. */ | #define VIRTIO_NET_F_MAC (1ULL << 5) /* Host has given MAC address. */ | ||||
| #define VIRTIO_NET_F_GSO (1ULL << 6) /* Host handles pkts w/ any GSO type */ | #define VIRTIO_NET_F_GSO (1ULL << 6) /* Host handles pkts w/ any GSO type */ | ||||
| #define VIRTIO_NET_F_GUEST_TSO4 (1ULL << 7) /* Guest can handle TSOv4 in. */ | #define VIRTIO_NET_F_GUEST_TSO4 (1ULL << 7) /* Guest can handle TSOv4 in. */ | ||||
| ▲ Show 20 Lines • Show All 213 Lines • ▼ Show 20 Lines | |||||
| * Command data format matches the feature bit mask exactly. | * Command data format matches the feature bit mask exactly. | ||||
| * | * | ||||
| * See VIRTIO_NET_F_GUEST_* for the list of offloads | * See VIRTIO_NET_F_GUEST_* for the list of offloads | ||||
| * that can be enabled/disabled. | * that can be enabled/disabled. | ||||
| */ | */ | ||||
| #define VIRTIO_NET_CTRL_GUEST_OFFLOADS 5 | #define VIRTIO_NET_CTRL_GUEST_OFFLOADS 5 | ||||
| #define VIRTIO_NET_CTRL_GUEST_OFFLOADS_SET 0 | #define VIRTIO_NET_CTRL_GUEST_OFFLOADS_SET 0 | ||||
| /* | #if defined(INET) || defined(INET6) | ||||
| * Use the checksum offset in the VirtIO header to set the | static inline void | ||||
| * correct CSUM_* flags. | virtio_net_rx_csum_needs_csum(struct mbuf *m, bool isipv6, int protocol, | ||||
| */ | |||||
| static inline int | |||||
| virtio_net_rx_csum_by_offset(struct mbuf *m, uint16_t eth_type, int ip_start, | |||||
| struct virtio_net_hdr *hdr) | struct virtio_net_hdr *hdr) | ||||
| { | { | ||||
| #if defined(INET) || defined(INET6) | /* | ||||
| int offset = hdr->csum_start + hdr->csum_offset; | * The packet is likely from another VM on the same host or from the | ||||
| #endif | * host that itself performed checksum offloading so Tx/Rx is basically | ||||
| * a memcpy and the checksum has little value so far. | |||||
| */ | |||||
| /* Only do a basic sanity check on the offset. */ | KASSERT(protocol == IPPROTO_TCP || protocol == IPPROTO_UDP, | ||||
| switch (eth_type) { | ("%s: unsupported IP protocol %d", __func__, protocol)); | ||||
| #if defined(INET) | |||||
| case ETHERTYPE_IP: | /* | ||||
| if (__predict_false(offset < ip_start + sizeof(struct ip))) | * Just forward the order to compute the checksum by setting | ||||
| return (1); | * the corresponding mbuf flag (e.g., CSUM_TCP). | ||||
| */ | |||||
| switch (protocol) { | |||||
| case IPPROTO_TCP: | |||||
| m->m_pkthdr.csum_flags |= (isipv6 ? CSUM_TCP_IPV6 : CSUM_TCP); | |||||
| break; | break; | ||||
| #endif | case IPPROTO_UDP: | ||||
| #if defined(INET6) | m->m_pkthdr.csum_flags |= (isipv6 ? CSUM_UDP_IPV6 : CSUM_UDP); | ||||
| case ETHERTYPE_IPV6: | |||||
| if (__predict_false(offset < ip_start + sizeof(struct ip6_hdr))) | |||||
| return (1); | |||||
| break; | break; | ||||
| #endif | |||||
| default: | |||||
| /* Here we should increment the rx_csum_bad_ethtype counter. */ | |||||
| return (1); | |||||
| } | } | ||||
| m->m_pkthdr.csum_data = hdr->csum_offset; | |||||
| } | |||||
| /* | static inline void | ||||
| * Use the offset to determine the appropriate CSUM_* flags. This is | virtio_net_rx_csum_data_valid(struct mbuf *m, int protocol) | ||||
| * a bit dirty, but we can get by with it since the checksum offsets | { | ||||
| * happen to be different. We assume the host host does not do IPv4 | KASSERT(protocol == IPPROTO_TCP || protocol == IPPROTO_UDP, | ||||
| * header checksum offloading. | ("%s: unsupported IP protocol %d", __func__, protocol)); | ||||
| */ | |||||
| switch (hdr->csum_offset) { | |||||
| case offsetof(struct udphdr, uh_sum): | |||||
| case offsetof(struct tcphdr, th_sum): | |||||
| m->m_pkthdr.csum_flags |= CSUM_DATA_VALID | CSUM_PSEUDO_HDR; | m->m_pkthdr.csum_flags |= CSUM_DATA_VALID | CSUM_PSEUDO_HDR; | ||||
| m->m_pkthdr.csum_data = 0xFFFF; | m->m_pkthdr.csum_data = 0xFFFF; | ||||
| break; | |||||
| default: | |||||
| /* Here we should increment the rx_csum_bad_offset counter. */ | |||||
| return (1); | |||||
| } | } | ||||
| return (0); | #define VIRTIO_NET_RX_CSUM_INACCESSIBLE_IPPROTO 1 | ||||
| } | #define VIRTIO_NET_RX_CSUM_BAD_ETHTYPE 2 | ||||
| #define VIRTIO_NET_RX_CSUM_BAD_IPPROTO 3 | |||||
| /* | |||||
| * For a packet received over the VirtIO channel, it checks the given | |||||
| * VirtIO header and sets the appropriate CSUM_* flags in the given mbuf. | |||||
| * | |||||
| * Unfortunately, the information provided is not directly useful to us. The | |||||
| * VirtIO header gives the offset of the checksum, which is all Linux needs, but | |||||
| * this is not how FreeBSD does things. We are forced to peek inside the packet | |||||
| * a bit. | |||||
| * | |||||
| * It would be nice if VirtIO gave us the L4 protocol or if FreeBSD | |||||
| * could accept the offsets and let the stack figure it out. | |||||
| * | |||||
| * @param m mbuf of the packet where CSUM_* flags might need to be set. | |||||
| * @param hdr VirtIO header of the received packet that needs to be checked. | |||||
| * | |||||
| * @return 0 on success, or one of the VIRTIO_NET_RX_CSUM_* error codes. | |||||
| */ | |||||
| static inline int | static inline int | ||||
| virtio_net_rx_csum_by_parse(struct mbuf *m, uint16_t eth_type, int ip_start, | virtio_net_rx_csum(struct mbuf *m, struct virtio_net_hdr *hdr) | ||||
| struct virtio_net_hdr *hdr) | |||||
| { | { | ||||
| int offset, proto; | const struct ether_header *eh; | ||||
| int hoff, protocol; | |||||
| uint16_t etype; | |||||
| bool isipv6; | |||||
| switch (eth_type) { | KASSERT(hdr->flags & | ||||
| (VIRTIO_NET_HDR_F_NEEDS_CSUM | VIRTIO_NET_HDR_F_DATA_VALID), | |||||
| ("%s: missing checksum offloading flag %x", __func__, hdr->flags)); | |||||
| eh = mtod(m, const struct ether_header *); | |||||
| etype = ntohs(eh->ether_type); | |||||
| if (etype == ETHERTYPE_VLAN) { | |||||
| /* TODO BMV: Handle QinQ. */ | |||||
| const struct ether_vlan_header *evh = | |||||
| mtod(m, const struct ether_vlan_header *); | |||||
| etype = ntohs(evh->evl_proto); | |||||
| hoff = sizeof(struct ether_vlan_header); | |||||
| } else | |||||
| hoff = sizeof(struct ether_header); | |||||
| /* Check whether ethernet type is IP or IPv6, and get protocol. */ | |||||
| switch (etype) { | |||||
| #if defined(INET) | #if defined(INET) | ||||
| case ETHERTYPE_IP: { | case ETHERTYPE_IP: | ||||
| struct ip *ip; | if (__predict_false(m->m_len < hoff + sizeof(struct ip))) { | ||||
| if (__predict_false(m->m_len < ip_start + sizeof(struct ip))) | return (VIRTIO_NET_RX_CSUM_INACCESSIBLE_IPPROTO); | ||||
| return (1); | } else { | ||||
| ip = (struct ip *)(m->m_data + ip_start); | struct ip *ip = (struct ip *)(m->m_data + hoff); | ||||
| proto = ip->ip_p; | protocol = ip->ip_p; | ||||
| offset = ip_start + (ip->ip_hl << 2); | |||||
| break; | |||||
| } | } | ||||
| isipv6 = false; | |||||
| break; | |||||
| #endif | #endif | ||||
| #if defined(INET6) | #if defined(INET6) | ||||
| case ETHERTYPE_IPV6: | case ETHERTYPE_IPV6: | ||||
| if (__predict_false(m->m_len < ip_start + | if (__predict_false(m->m_len < hoff + sizeof(struct ip6_hdr)) | ||||
| sizeof(struct ip6_hdr))) | || ip6_lasthdr(m, hoff, IPPROTO_IPV6, &protocol) < 0) { | ||||
| return (1); | return (VIRTIO_NET_RX_CSUM_INACCESSIBLE_IPPROTO); | ||||
| offset = ip6_lasthdr(m, ip_start, IPPROTO_IPV6, &proto); | } | ||||
| if (__predict_false(offset < 0)) | isipv6 = true; | ||||
| return (1); | |||||
| break; | break; | ||||
| #endif | #endif | ||||
| default: | default: | ||||
| /* Here we should increment the rx_csum_bad_ethtype counter. */ | return (VIRTIO_NET_RX_CSUM_BAD_ETHTYPE); | ||||
| return (1); | |||||
| } | } | ||||
| switch (proto) { | /* Check whether protocol is TCP or UDP. */ | ||||
| switch (protocol) { | |||||
| case IPPROTO_TCP: | case IPPROTO_TCP: | ||||
| if (__predict_false(m->m_len < offset + sizeof(struct tcphdr))) | |||||
| return (1); | |||||
| m->m_pkthdr.csum_flags |= CSUM_DATA_VALID | CSUM_PSEUDO_HDR; | |||||
| m->m_pkthdr.csum_data = 0xFFFF; | |||||
| break; | |||||
| case IPPROTO_UDP: | case IPPROTO_UDP: | ||||
| if (__predict_false(m->m_len < offset + sizeof(struct udphdr))) | |||||
| return (1); | |||||
| m->m_pkthdr.csum_flags |= CSUM_DATA_VALID | CSUM_PSEUDO_HDR; | |||||
| m->m_pkthdr.csum_data = 0xFFFF; | |||||
| break; | break; | ||||
| default: | default: | ||||
| /* | /* | ||||
| * For the remaining protocols, FreeBSD does not support | * FreeBSD does not support checksum offloading of this | ||||
| * checksum offloading, so the checksum will be recomputed. | * protocol here. | ||||
| */ | */ | ||||
| #if 0 | return (VIRTIO_NET_RX_CSUM_BAD_IPPROTO); | ||||
| if_printf(ifp, "cksum offload of unsupported " | |||||
| "protocol eth_type=%#x proto=%d csum_start=%d " | |||||
| "csum_offset=%d\n", __func__, eth_type, proto, | |||||
| hdr->csum_start, hdr->csum_offset); | |||||
| #endif | |||||
| break; | |||||
| } | } | ||||
| if (hdr->flags & VIRTIO_NET_HDR_F_NEEDS_CSUM) | |||||
| virtio_net_rx_csum_needs_csum(m, isipv6, protocol, hdr); | |||||
| else /* VIRTIO_NET_HDR_F_DATA_VALID */ | |||||
| virtio_net_rx_csum_data_valid(m, protocol); | |||||
| return (0); | return (0); | ||||
| } | } | ||||
| #endif | |||||
| #define VIRTIO_NET_TX_OFFLOAD_UNKNOWN_ETHTYPE 1 | |||||
| #define VIRTIO_NET_TX_OFFLOAD_PROTO_MISMATCH 2 | |||||
| #define VIRTIO_NET_TX_OFFLOAD_TSO_NOT_TCP 3 | |||||
| #define VIRTIO_NET_TX_OFFLOAD_TSO_WITHOUT_CSUM 4 | |||||
| #define VIRTIO_NET_TX_OFFLOAD_TSO_ECN_UNEXPECTED 5 | |||||
| /* | /* | ||||
| * Set the appropriate CSUM_* flags. Unfortunately, the information | * BMV: This can go away once we finally have offsets in the mbuf header. | ||||
| * provided is not directly useful to us. The VirtIO header gives the | |||||
| * offset of the checksum, which is all Linux needs, but this is not | |||||
| * how FreeBSD does things. We are forced to peek inside the packet | |||||
| * a bit. | |||||
| * | |||||
| * It would be nice if VirtIO gave us the L4 protocol or if FreeBSD | |||||
| * could accept the offsets and let the stack figure it out. | |||||
| */ | */ | ||||
| static inline int | static inline int | ||||
| virtio_net_rx_csum(struct mbuf *m, struct virtio_net_hdr *hdr) | |||||
| { | |||||
| struct ether_header *eh; | |||||
| struct ether_vlan_header *evh; | |||||
| uint16_t eth_type; | |||||
| int offset, error; | |||||
| if ((hdr->flags & (VIRTIO_NET_HDR_F_NEEDS_CSUM | | |||||
| VIRTIO_NET_HDR_F_DATA_VALID)) == 0) { | |||||
| return (0); | |||||
| } | |||||
| eh = mtod(m, struct ether_header *); | |||||
| eth_type = ntohs(eh->ether_type); | |||||
| if (eth_type == ETHERTYPE_VLAN) { | |||||
| /* BMV: We should handle nested VLAN tags too. */ | |||||
| evh = mtod(m, struct ether_vlan_header *); | |||||
| eth_type = ntohs(evh->evl_proto); | |||||
| offset = sizeof(struct ether_vlan_header); | |||||
| } else | |||||
| offset = sizeof(struct ether_header); | |||||
| if (hdr->flags & VIRTIO_NET_HDR_F_NEEDS_CSUM) | |||||
| error = virtio_net_rx_csum_by_offset(m, eth_type, offset, hdr); | |||||
| else | |||||
| error = virtio_net_rx_csum_by_parse(m, eth_type, offset, hdr); | |||||
| return (error); | |||||
| } | |||||
| static inline int | |||||
| virtio_net_tx_offload_ctx(struct mbuf *m, int *etype, int *proto, int *start) | virtio_net_tx_offload_ctx(struct mbuf *m, int *etype, int *proto, int *start) | ||||
| { | { | ||||
| struct ether_vlan_header *evh; | struct ether_vlan_header *evh; | ||||
| #if defined(INET) || defined(INET6) | #if defined(INET) || defined(INET6) | ||||
| int offset; | int offset; | ||||
| #endif | #endif | ||||
| evh = mtod(m, struct ether_vlan_header *); | evh = mtod(m, struct ether_vlan_header *); | ||||
| Show All 31 Lines | case ETHERTYPE_IPV6: | ||||
| *start = ip6_lasthdr(m, offset, IPPROTO_IPV6, proto); | *start = ip6_lasthdr(m, offset, IPPROTO_IPV6, proto); | ||||
| /* Assert the network stack sent us a valid packet. */ | /* Assert the network stack sent us a valid packet. */ | ||||
| KASSERT(*start > offset, | KASSERT(*start > offset, | ||||
| ("%s: mbuf %p start %d offset %d proto %d", __func__, m, | ("%s: mbuf %p start %d offset %d proto %d", __func__, m, | ||||
| *start, offset, *proto)); | *start, offset, *proto)); | ||||
| break; | break; | ||||
| #endif | #endif | ||||
| default: | default: | ||||
| /* Here we should increment the tx_csum_bad_ethtype counter. */ | return (VIRTIO_NET_TX_OFFLOAD_UNKNOWN_ETHTYPE); | ||||
| return (EINVAL); | |||||
| } | } | ||||
| return (0); | return (0); | ||||
| } | } | ||||
| static inline int | static inline int | ||||
| virtio_net_tx_offload_tso(if_t ifp, struct mbuf *m, int eth_type, | virtio_net_tx_offload_tso(struct ifnet *ifp, struct mbuf *m, int eth_type, | ||||
| int offset, bool allow_ecn, struct virtio_net_hdr *hdr) | int offset, struct virtio_net_hdr *hdr, bool tso_ecn, bool modern) | ||||
| { | { | ||||
| static struct timeval lastecn; | static struct timeval lastecn; | ||||
| static int curecn; | static int curecn; | ||||
| struct tcphdr *tcp, tcphdr; | struct tcphdr *tcp, tcphdr; | ||||
| if (__predict_false(m->m_len < offset + sizeof(struct tcphdr))) { | if (__predict_false(m->m_len < offset + sizeof(struct tcphdr))) { | ||||
| m_copydata(m, offset, sizeof(struct tcphdr), (caddr_t) &tcphdr); | m_copydata(m, offset, sizeof(struct tcphdr), (caddr_t) &tcphdr); | ||||
| tcp = &tcphdr; | tcp = &tcphdr; | ||||
| } else | } else | ||||
| tcp = (struct tcphdr *)(m->m_data + offset); | tcp = (struct tcphdr *)(m->m_data + offset); | ||||
| hdr->hdr_len = offset + (tcp->th_off << 2); | hdr->hdr_len = virtio_gtoh16(modern, offset + (tcp->th_off << 2)); | ||||
| hdr->gso_size = m->m_pkthdr.tso_segsz; | hdr->gso_size = virtio_gtoh16(modern, m->m_pkthdr.tso_segsz); | ||||
| hdr->gso_type = eth_type == ETHERTYPE_IP ? VIRTIO_NET_HDR_GSO_TCPV4 : | hdr->gso_type = eth_type == ETHERTYPE_IP ? VIRTIO_NET_HDR_GSO_TCPV4 : | ||||
| VIRTIO_NET_HDR_GSO_TCPV6; | VIRTIO_NET_HDR_GSO_TCPV6; | ||||
| if (tcp_get_flags(tcp) & TH_CWR) { | if (__predict_false(tcp_get_flags(tcp) & TH_CWR)) { | ||||
| /* | /* | ||||
| * Drop if VIRTIO_NET_F_HOST_ECN was not negotiated. In FreeBSD, | * Drop if VIRTIO_NET_F_HOST_ECN was not negotiated. In | ||||
| * ECN support is not on a per-interface basis, but globally via | * FreeBSD, ECN support is not on a per-interface basis, | ||||
| * the net.inet.tcp.ecn.enable sysctl knob. The default is off. | * but globally via the net.inet.tcp.ecn.enable sysctl | ||||
| * knob. The default is off. | |||||
| */ | */ | ||||
| if (!allow_ecn) { | if (!tso_ecn) { | ||||
| if (ppsratecheck(&lastecn, &curecn, 1)) | if (ppsratecheck(&lastecn, &curecn, 1)) | ||||
| if_printf(ifp, | if_printf(ifp, | ||||
| "TSO with ECN not negotiated with host\n"); | "TSO with ECN not negotiated with host\n"); | ||||
| return (ENOTSUP); | return (VIRTIO_NET_TX_OFFLOAD_TSO_ECN_UNEXPECTED); | ||||
| } | } | ||||
| hdr->gso_type |= VIRTIO_NET_HDR_GSO_ECN; | hdr->gso_type |= VIRTIO_NET_HDR_GSO_ECN; | ||||
| } | } | ||||
| /* Here we should increment tx_tso counter. */ | |||||
| return (0); | return (0); | ||||
| } | } | ||||
| static inline struct mbuf * | /* | ||||
| virtio_net_tx_offload(if_t ifp, struct mbuf *m, bool allow_ecn, | * For a packet to be transmitted over the VirtIO channel, it checks the | ||||
| struct virtio_net_hdr *hdr) | * CSUM_* flags in the mbuf and sets the appropriate flags in the VirtIO header. | ||||
| * In case of an error, it frees the mbuf and sets the pointer referenced by mp | |||||
| * to NULL. | |||||
| * | |||||
| * @param ifp ifnet struct of outgoing interface. | |||||
| * @param mp mbuf on which the CSUM_* flags needs to be checked. | |||||
| * @param hdr VirtIO header to be filled for the outgoing packet. | |||||
| * @param tso_ecn true if ECN have been negotiated between host and guest. | |||||
| * @param modern true if VirtIO modern mode is used. | |||||
| * | |||||
| * @return 0 on success, or one of the VIRTIO_NET_TX_OFFLOAD_* error codes. | |||||
| */ | |||||
| static inline int | |||||
| virtio_net_tx_offload(struct ifnet *ifp, struct mbuf **mp, | |||||
| struct virtio_net_hdr *hdr, bool tso_ecn, bool modern) | |||||
| { | { | ||||
| int flags, etype, csum_start, proto, error; | int flags, etype, csum_start, proto, error; | ||||
| struct mbuf *m; | |||||
| m = *mp; | |||||
| flags = m->m_pkthdr.csum_flags; | flags = m->m_pkthdr.csum_flags; | ||||
| error = virtio_net_tx_offload_ctx(m, &etype, &proto, &csum_start); | error = virtio_net_tx_offload_ctx(m, &etype, &proto, &csum_start); | ||||
| if (error) | if (error != 0) | ||||
| goto drop; | goto drop; | ||||
| if ((etype == ETHERTYPE_IP && (flags & (CSUM_TCP | CSUM_UDP))) || | if (flags & (CSUM_TCP | CSUM_UDP | CSUM_TCP_IPV6 | CSUM_UDP_IPV6)) { | ||||
| (etype == ETHERTYPE_IPV6 && | /* Sanity check the parsed mbuf matches the offload flags. */ | ||||
| (flags & (CSUM_TCP_IPV6 | CSUM_UDP_IPV6)))) { | if (__predict_false((flags & (CSUM_TCP | CSUM_UDP) && | ||||
| /* | etype != ETHERTYPE_IP) || | ||||
| * We could compare the IP protocol vs the CSUM_ flag too, | (flags & (CSUM_TCP_IPV6 | CSUM_UDP_IPV6) && | ||||
| * but that really should not be necessary. | etype != ETHERTYPE_IPV6))) { | ||||
| */ | error = VIRTIO_NET_TX_OFFLOAD_PROTO_MISMATCH; | ||||
| goto drop; | |||||
| } | |||||
| hdr->flags |= VIRTIO_NET_HDR_F_NEEDS_CSUM; | hdr->flags |= VIRTIO_NET_HDR_F_NEEDS_CSUM; | ||||
| hdr->csum_start = csum_start; | hdr->csum_start = virtio_gtoh16(modern, csum_start); | ||||
| hdr->csum_offset = m->m_pkthdr.csum_data; | hdr->csum_offset = virtio_gtoh16(modern, m->m_pkthdr.csum_data); | ||||
| /* Here we should increment the tx_csum counter. */ | |||||
| } | } | ||||
| if (flags & CSUM_TSO) { | if (flags & (CSUM_IP_TSO | CSUM_IP6_TSO)) { | ||||
| /* | |||||
| * Sanity check the parsed mbuf IP protocol is TCP, and | |||||
| * VirtIO TSO reqires the checksum offloading above. | |||||
| */ | |||||
| if (__predict_false(proto != IPPROTO_TCP)) { | if (__predict_false(proto != IPPROTO_TCP)) { | ||||
| /* Likely failed to correctly parse the mbuf. | error = VIRTIO_NET_TX_OFFLOAD_TSO_NOT_TCP; | ||||
| * Here we should increment the tx_tso_not_tcp | |||||
| * counter. */ | |||||
| goto drop; | goto drop; | ||||
| } else if (__predict_false((hdr->flags & | |||||
| VIRTIO_NET_HDR_F_NEEDS_CSUM) == 0)) { | |||||
| error = VIRTIO_NET_TX_OFFLOAD_TSO_WITHOUT_CSUM; | |||||
| goto drop; | |||||
| } | } | ||||
| KASSERT(hdr->flags & VIRTIO_NET_HDR_F_NEEDS_CSUM, | |||||
| ("%s: mbuf %p TSO without checksum offload %#x", | |||||
| __func__, m, flags)); | |||||
| error = virtio_net_tx_offload_tso(ifp, m, etype, csum_start, | error = virtio_net_tx_offload_tso(ifp, m, etype, csum_start, | ||||
| allow_ecn, hdr); | hdr, tso_ecn, modern); | ||||
| if (error) | if (error != 0) | ||||
| goto drop; | goto drop; | ||||
| } | } | ||||
| return (m); | return (error); | ||||
| drop: | drop: | ||||
| m_freem(m); | m_freem(m); | ||||
| return (NULL); | *mp = NULL; | ||||
| return (error); | |||||
| } | } | ||||
| #endif /* _VIRTIO_NET_H */ | #endif /* _VIRTIO_NET_H */ | ||||