Skip to content

Commit

Permalink
net: checksum: Introduce fine control over checksum type
Browse files Browse the repository at this point in the history
At present net_checksum_calculate() blindly calculates all types of
checksums (IP, TCP, UDP). Some NICs may have a per type setting in
their BDs to control what checksum should be offloaded. To support
such hardware behavior, introduce a 'csum_flag' parameter to the
net_checksum_calculate() API to allow fine control over what type
checksum is calculated.

Existing users of this API are updated accordingly.

Signed-off-by: Bin Meng <[email protected]>
Signed-off-by: Jason Wang <[email protected]>
  • Loading branch information
lbmeng authored and jasowang committed Jan 25, 2021
1 parent d97f115 commit f574633
Show file tree
Hide file tree
Showing 10 changed files with 55 additions and 33 deletions.
2 changes: 1 addition & 1 deletion hw/net/allwinner-sun8i-emac.c
Original file line number Diff line number Diff line change
Expand Up @@ -514,7 +514,7 @@ static void allwinner_sun8i_emac_transmit(AwSun8iEmacState *s)
/* After the last descriptor, send the packet */
if (desc.status2 & TX_DESC_STATUS2_LAST_DESC) {
if (desc.status2 & TX_DESC_STATUS2_CHECKSUM_MASK) {
net_checksum_calculate(packet_buf, packet_bytes);
net_checksum_calculate(packet_buf, packet_bytes, CSUM_ALL);
}

qemu_send_packet(nc, packet_buf, packet_bytes);
Expand Down
2 changes: 1 addition & 1 deletion hw/net/cadence_gem.c
Original file line number Diff line number Diff line change
Expand Up @@ -1266,7 +1266,7 @@ static void gem_transmit(CadenceGEMState *s)

/* Is checksum offload enabled? */
if (s->regs[GEM_DMACFG] & GEM_DMACFG_TXCSUM_OFFL) {
net_checksum_calculate(s->tx_packet, total_bytes);
net_checksum_calculate(s->tx_packet, total_bytes, CSUM_ALL);
}

/* Update MAC statistics */
Expand Down
18 changes: 9 additions & 9 deletions hw/net/fsl_etsec/rings.c
Original file line number Diff line number Diff line change
Expand Up @@ -183,13 +183,11 @@ static void process_tx_fcb(eTSEC *etsec)
uint8_t *l3_header = etsec->tx_buffer + 8 + l3_header_offset;
/* L4 header */
uint8_t *l4_header = l3_header + l4_header_offset;
int csum = 0;

/* if packet is IP4 and IP checksum is requested */
if (flags & FCB_TX_IP && flags & FCB_TX_CIP) {
/* do IP4 checksum (TODO This function does TCP/UDP checksum
* but not sure if it also does IP4 checksum.) */
net_checksum_calculate(etsec->tx_buffer + 8,
etsec->tx_buffer_len - 8);
csum |= CSUM_IP;
}
/* TODO Check the correct usage of the PHCS field of the FCB in case the NPH
* flag is on */
Expand All @@ -201,20 +199,22 @@ static void process_tx_fcb(eTSEC *etsec)
/* if checksum is requested */
if (flags & FCB_TX_CTU) {
/* do UDP checksum */

net_checksum_calculate(etsec->tx_buffer + 8,
etsec->tx_buffer_len - 8);
csum |= CSUM_UDP;
} else {
/* set checksum field to 0 */
l4_header[6] = 0;
l4_header[7] = 0;
}
} else if (flags & FCB_TX_CTU) { /* if TCP and checksum is requested */
/* do TCP checksum */
net_checksum_calculate(etsec->tx_buffer + 8,
etsec->tx_buffer_len - 8);
csum |= CSUM_TCP;
}
}

if (csum) {
net_checksum_calculate(etsec->tx_buffer + 8,
etsec->tx_buffer_len - 8, csum);
}
}

static void process_tx_bd(eTSEC *etsec,
Expand Down
13 changes: 12 additions & 1 deletion hw/net/ftgmac100.c
Original file line number Diff line number Diff line change
Expand Up @@ -564,6 +564,7 @@ static void ftgmac100_do_tx(FTGMAC100State *s, uint32_t tx_ring,
ptr += len;
frame_size += len;
if (bd.des0 & FTGMAC100_TXDES0_LTS) {
int csum = 0;

/* Check for VLAN */
if (flags & FTGMAC100_TXDES1_INS_VLANTAG &&
Expand All @@ -573,8 +574,18 @@ static void ftgmac100_do_tx(FTGMAC100State *s, uint32_t tx_ring,
}

if (flags & FTGMAC100_TXDES1_IP_CHKSUM) {
net_checksum_calculate(s->frame, frame_size);
csum |= CSUM_IP;
}
if (flags & FTGMAC100_TXDES1_TCP_CHKSUM) {
csum |= CSUM_TCP;
}
if (flags & FTGMAC100_TXDES1_UDP_CHKSUM) {
csum |= CSUM_UDP;
}
if (csum) {
net_checksum_calculate(s->frame, frame_size, csum);
}

/* Last buffer in frame. */
qemu_send_packet(qemu_get_queue(s->nic), s->frame, frame_size);
ptr = s->frame;
Expand Down
20 changes: 8 additions & 12 deletions hw/net/imx_fec.c
Original file line number Diff line number Diff line change
Expand Up @@ -561,22 +561,18 @@ static void imx_enet_do_tx(IMXFECState *s, uint32_t index)
ptr += len;
frame_size += len;
if (bd.flags & ENET_BD_L) {
int csum = 0;

if (bd.option & ENET_BD_PINS) {
struct ip_header *ip_hd = PKT_GET_IP_HDR(s->frame);
if (IP_HEADER_VERSION(ip_hd) == 4) {
net_checksum_calculate(s->frame, frame_size);
}
csum |= (CSUM_TCP | CSUM_UDP);
}
if (bd.option & ENET_BD_IINS) {
struct ip_header *ip_hd = PKT_GET_IP_HDR(s->frame);
/* We compute checksum only for IPv4 frames */
if (IP_HEADER_VERSION(ip_hd) == 4) {
uint16_t csum;
ip_hd->ip_sum = 0;
csum = net_raw_checksum((uint8_t *)ip_hd, sizeof(*ip_hd));
ip_hd->ip_sum = cpu_to_be16(csum);
}
csum |= CSUM_IP;
}
if (csum) {
net_checksum_calculate(s->frame, frame_size, csum);
}

/* Last buffer in frame. */

qemu_send_packet(qemu_get_queue(s->nic), s->frame, frame_size);
Expand Down
2 changes: 1 addition & 1 deletion hw/net/virtio-net.c
Original file line number Diff line number Diff line change
Expand Up @@ -1464,7 +1464,7 @@ static void work_around_broken_dhclient(struct virtio_net_hdr *hdr,
(buf[12] == 0x08 && buf[13] == 0x00) && /* ethertype == IPv4 */
(buf[23] == 17) && /* ip.protocol == UDP */
(buf[34] == 0 && buf[35] == 67)) { /* udp.srcport == bootps */
net_checksum_calculate(buf, size);
net_checksum_calculate(buf, size, CSUM_UDP);
hdr->flags &= ~VIRTIO_NET_HDR_F_NEEDS_CSUM;
}
}
Expand Down
2 changes: 1 addition & 1 deletion hw/net/xen_nic.c
Original file line number Diff line number Diff line change
Expand Up @@ -174,7 +174,7 @@ static void net_tx_packets(struct XenNetDev *netdev)
tmpbuf = g_malloc(XC_PAGE_SIZE);
}
memcpy(tmpbuf, page + txreq.offset, txreq.size);
net_checksum_calculate(tmpbuf, txreq.size);
net_checksum_calculate(tmpbuf, txreq.size, CSUM_ALL);
qemu_send_packet(qemu_get_queue(netdev->nic), tmpbuf,
txreq.size);
} else {
Expand Down
7 changes: 6 additions & 1 deletion include/net/checksum.h
Original file line number Diff line number Diff line change
Expand Up @@ -21,11 +21,16 @@
#include "qemu/bswap.h"
struct iovec;

#define CSUM_IP 0x01
#define CSUM_TCP 0x02
#define CSUM_UDP 0x04
#define CSUM_ALL (CSUM_IP | CSUM_TCP | CSUM_UDP)

uint32_t net_checksum_add_cont(int len, uint8_t *buf, int seq);
uint16_t net_checksum_finish(uint32_t sum);
uint16_t net_checksum_tcpudp(uint16_t length, uint16_t proto,
uint8_t *addrs, uint8_t *buf);
void net_checksum_calculate(uint8_t *data, int length);
void net_checksum_calculate(uint8_t *data, int length, int csum_flag);

static inline uint32_t
net_checksum_add(int len, uint8_t *buf)
Expand Down
18 changes: 14 additions & 4 deletions net/checksum.c
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ uint16_t net_checksum_tcpudp(uint16_t length, uint16_t proto,
return net_checksum_finish(sum);
}

void net_checksum_calculate(uint8_t *data, int length)
void net_checksum_calculate(uint8_t *data, int length, int csum_flag)
{
int mac_hdr_len, ip_len;
struct ip_header *ip;
Expand Down Expand Up @@ -108,9 +108,11 @@ void net_checksum_calculate(uint8_t *data, int length)
}

/* Calculate IP checksum */
stw_he_p(&ip->ip_sum, 0);
csum = net_raw_checksum((uint8_t *)ip, IP_HDR_GET_LEN(ip));
stw_be_p(&ip->ip_sum, csum);
if (csum_flag & CSUM_IP) {
stw_he_p(&ip->ip_sum, 0);
csum = net_raw_checksum((uint8_t *)ip, IP_HDR_GET_LEN(ip));
stw_be_p(&ip->ip_sum, csum);
}

if (IP4_IS_FRAGMENT(ip)) {
return; /* a fragmented IP packet */
Expand All @@ -128,6 +130,10 @@ void net_checksum_calculate(uint8_t *data, int length)
switch (ip->ip_p) {
case IP_PROTO_TCP:
{
if (!(csum_flag & CSUM_TCP)) {
return;
}

tcp_header *tcp = (tcp_header *)(ip + 1);

if (ip_len < sizeof(tcp_header)) {
Expand All @@ -148,6 +154,10 @@ void net_checksum_calculate(uint8_t *data, int length)
}
case IP_PROTO_UDP:
{
if (!(csum_flag & CSUM_UDP)) {
return;
}

udp_header *udp = (udp_header *)(ip + 1);

if (ip_len < sizeof(udp_header)) {
Expand Down
4 changes: 2 additions & 2 deletions net/filter-rewriter.c
Original file line number Diff line number Diff line change
Expand Up @@ -114,7 +114,7 @@ static int handle_primary_tcp_pkt(RewriterState *rf,
tcp_pkt->th_ack = htonl(ntohl(tcp_pkt->th_ack) + conn->offset);

net_checksum_calculate((uint8_t *)pkt->data + pkt->vnet_hdr_len,
pkt->size - pkt->vnet_hdr_len);
pkt->size - pkt->vnet_hdr_len, CSUM_TCP);
}

/*
Expand Down Expand Up @@ -216,7 +216,7 @@ static int handle_secondary_tcp_pkt(RewriterState *rf,
tcp_pkt->th_seq = htonl(ntohl(tcp_pkt->th_seq) - conn->offset);

net_checksum_calculate((uint8_t *)pkt->data + pkt->vnet_hdr_len,
pkt->size - pkt->vnet_hdr_len);
pkt->size - pkt->vnet_hdr_len, CSUM_TCP);
}
}

Expand Down

0 comments on commit f574633

Please sign in to comment.