@@ -66,6 +66,7 @@
/* Global statistics */
static uint32_t rsc_chain_no_mem;
+static uint64_t virtio_net_received;
/* Switcher to enable/disable rsc */
static bool virtio_net_rsc_bypass;
@@ -1679,10 +1680,12 @@ static void virtio_net_rsc_purge(void *opq)
if (ret == 0) {
/* Try next queue */
+ chain->stat.purge_failed++;
continue;
}
}
+ chain->stat.timer++;
if (!QTAILQ_EMPTY(&chain->buffers)) {
timer_mod(chain->drain_timer,
qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + rsc_timeout);
@@ -1715,6 +1718,7 @@ static int virtio_net_rsc_cache_buf(NetRscChain *chain, NetClientState *nc,
seg = g_malloc(sizeof(NetRscSeg));
if (!seg) {
+ chain->stat.no_buf++;
return 0;
}
@@ -1730,9 +1734,11 @@ static int virtio_net_rsc_cache_buf(NetRscChain *chain, NetClientState *nc,
seg->nc = nc;
QTAILQ_INSERT_TAIL(&chain->buffers, seg, next);
+ chain->stat.cache++;
return size;
out:
+ chain->stat.no_buf++;
g_free(seg);
return 0;
}
@@ -1750,27 +1756,33 @@ static int32_t virtio_net_rsc_handle_ack(NetRscChain *chain, NetRscSeg *seg,
owin = htons(o_tcp->th_win);
if ((nack - oack) >= TCP_WINDOW) {
+ chain->stat.ack_out_of_win++;
return RSC_FINAL;
} else if (nack == oack) {
/* duplicated ack or window probe */
if (nwin == owin) {
/* duplicated ack, add dup ack count due to whql test up to 1 */
+ chain->stat.dup_ack++;
if (seg->dup_ack_count == 0) {
seg->dup_ack_count++;
+ chain->stat.dup_ack1++;
return RSC_COALESCE;
} else {
/* Spec says should send it directly */
+ chain->stat.dup_ack2++;
return RSC_FINAL;
}
} else {
/* Coalesce window update */
o_tcp->th_win = n_tcp->th_win;
+ chain->stat.win_update++;
return RSC_COALESCE;
}
} else {
/* pure ack, update ack */
o_tcp->th_ack = n_tcp->th_ack;
+ chain->stat.pure_ack++;
return RSC_COALESCE;
}
}
@@ -1788,13 +1800,20 @@ static int32_t virtio_net_rsc_coalesce_tcp(NetRscChain *chain, NetRscSeg *seg,
nseq = htonl(n_tcp->th_seq);
oseq = htonl(o_tcp->th_seq);
+ if (n_tcp_len > sizeof(struct tcp_header)) {
+ /* Log this only for debugging observation */
+ chain->stat.tcp_option++;
+ }
+
/* Ignore packet with more/larger tcp options */
if (n_tcp_len > o_tcp_len) {
+ chain->stat.tcp_larger_option++;
return RSC_FINAL;
}
/* out of order or retransmitted. */
if ((nseq - oseq) > TCP_WINDOW) {
+ chain->stat.data_out_of_win++;
return RSC_FINAL;
}
@@ -1802,16 +1821,19 @@ static int32_t virtio_net_rsc_coalesce_tcp(NetRscChain *chain, NetRscSeg *seg,
if (nseq == oseq) {
if ((0 == o_data) && n_data) {
/* From no payload to payload, normal case, not a dup ack or etc */
+ chain->stat.data_after_pure_ack++;
goto coalesce;
} else {
return virtio_net_rsc_handle_ack(chain, seg, buf, n_tcp, o_tcp);
}
} else if ((nseq - oseq) != o_data) {
/* Not a consistent packet, out of order */
+ chain->stat.data_out_of_order++;
return RSC_FINAL;
} else {
coalesce:
if ((o_ip_len + n_data) > max_data) {
+ chain->stat.over_size++;
return RSC_FINAL;
}
@@ -1824,6 +1846,7 @@ coalesce:
memmove(seg->buf + seg->size, data, n_data);
seg->size += n_data;
+ chain->stat.coalesced++;
return RSC_COALESCE;
}
}
@@ -1855,6 +1878,7 @@ static int32_t virtio_net_rsc_try_coalesce4(NetRscChain *chain,
if ((n_ip->ip_src ^ o_ip->ip_src) || (n_ip->ip_dst ^ o_ip->ip_dst)
|| (n_tcp->th_sport ^ o_tcp->th_sport)
|| (n_tcp->th_dport ^ o_tcp->th_dport)) {
+ chain->stat.no_match++;
return RSC_NO_MATCH;
}
@@ -1890,6 +1914,7 @@ static int32_t virtio_net_rsc_try_coalesce6(NetRscChain *chain,
|| memcmp(&n_ip->ip6_dst, &o_ip->ip6_dst, sizeof(struct in6_address))
|| (n_tcp->th_sport ^ o_tcp->th_sport)
|| (n_tcp->th_dport ^ o_tcp->th_dport)) {
+ chain->stat.no_match++;
return RSC_NO_MATCH;
}
@@ -1927,6 +1952,7 @@ static size_t virtio_net_rsc_callback(NetRscChain *chain, NetClientState *nc,
NetRscSeg *seg, *nseg;
if (QTAILQ_EMPTY(&chain->buffers)) {
+ chain->stat.empty_cache++;
if (!virtio_net_rsc_cache_buf(chain, nc, buf, size)) {
return 0;
} else {
@@ -1948,6 +1974,7 @@ static size_t virtio_net_rsc_callback(NetRscChain *chain, NetClientState *nc,
g_free(seg);
if (ret == 0) {
/* Send failed */
+ chain->stat.final_failed++;
return 0;
}
@@ -1962,6 +1989,7 @@ static size_t virtio_net_rsc_callback(NetRscChain *chain, NetClientState *nc,
}
}
+ chain->stat.no_match_cache++;
return virtio_net_rsc_cache_buf(chain, nc, buf, size);
}
@@ -1980,8 +2008,9 @@ static size_t virtio_net_rsc_drain_one(NetRscChain *chain, NetClientState *nc,
if ((chain->proto == ETH_P_IP) && seg->is_coalesced) {
virtio_net_rsc_ipv4_checksum(seg);
}
-
- virtio_net_do_receive(seg->nc, seg->buf, seg->size);
+ if (!virtio_net_do_receive(seg->nc, seg->buf, seg->size)) {
+ chain->stat.drain_failed++;
+ }
QTAILQ_REMOVE(&chain->buffers, seg, next);
g_free(seg->buf);
@@ -2003,20 +2032,24 @@ static int32_t virtio_net_rsc_filter4(NetRscChain *chain, struct ip_header *ip,
/* Not an ipv4 one */
if (0x4 != ((0xF0 & ip->ip_ver_len) >> 4)) {
+ chain->stat.ip_option++;
return RSC_BYPASS;
}
/* Don't handle packets with ip option */
if (5 != (0xF & ip->ip_ver_len)) {
+ chain->stat.ip_option++;
return RSC_BYPASS;
}
/* Don't handle packets with ip fragment */
if (!(htons(ip->ip_off) & IP_DF)) {
+ chain->stat.ip_frag++;
return RSC_BYPASS;
}
if (ip->ip_p != IPPROTO_TCP) {
+ chain->stat.bypass_not_tcp++;
return RSC_BYPASS;
}
@@ -2024,13 +2057,13 @@ static int32_t virtio_net_rsc_filter4(NetRscChain *chain, struct ip_header *ip,
ip_len = htons(ip->ip_len);
if (ip_len < (sizeof(struct ip_header) + sizeof(struct tcp_header))
|| ip_len > (size - IP_OFFSET)) {
+ chain->stat.ip_hacked++;
return RSC_BYPASS;
}
return RSC_WANT;
}
-
static size_t virtio_net_rsc_receive4(void *opq, NetClientState* nc,
const uint8_t *buf, size_t size)
{
@@ -2048,8 +2081,10 @@ static size_t virtio_net_rsc_receive4(void *opq, NetClientState* nc,
ret = virtio_net_rsc_parse_tcp_ctrl((uint8_t *)ip,
(0xF & ip->ip_ver_len) << 2);
if (RSC_BYPASS == ret) {
+ chain->stat.tcp_syn++;
return virtio_net_do_receive(nc, buf, size);
} else if (RSC_FINAL == ret) {
+ chain->stat.tcp_ctrl_drain++;
return virtio_net_rsc_drain_one(chain, nc, buf, size, IP4_ADDR_OFFSET,
IP4_ADDR_SIZE, TCP4_PORT_OFFSET, TCP_PORT_SIZE);
}
@@ -2073,6 +2108,7 @@ static int32_t virtio_net_rsc_filter6(NetRscChain *chain, struct ip6_header *ip,
/* Both option and protocol is checked in this */
if (ip->ip6_ctlun.ip6_un1.ip6_un1_nxt != IPPROTO_TCP) {
+ chain->stat.bypass_not_tcp++;
return RSC_BYPASS;
}
@@ -2080,6 +2116,7 @@ static int32_t virtio_net_rsc_filter6(NetRscChain *chain, struct ip6_header *ip,
ip_len = htons(ip->ip6_ctlun.ip6_un1.ip6_un1_plen);
if (ip_len < sizeof(struct tcp_header)
|| ip_len > (size - TCP6_OFFSET)) {
+ chain->stat.ip_hacked++;
return RSC_BYPASS;
}
@@ -2101,8 +2138,10 @@ static size_t virtio_net_rsc_receive6(void *opq, NetClientState* nc,
ret = virtio_net_rsc_parse_tcp_ctrl((uint8_t *)ip, sizeof(*ip));
if (RSC_BYPASS == ret) {
+ chain->stat.tcp_syn++;
return virtio_net_do_receive(nc, buf, size);
} else if (RSC_FINAL == ret) {
+ chain->stat.tcp_ctrl_drain++;
return virtio_net_rsc_drain_one(chain, nc, buf, size, IP6_ADDR_OFFSET,
IP6_ADDR_SIZE, TCP6_PORT_OFFSET, TCP_PORT_SIZE);
}
@@ -2140,6 +2179,7 @@ static NetRscChain *virtio_net_rsc_lookup_chain(NetClientState *nc,
chain->proto = proto;
chain->drain_timer = timer_new_ns(QEMU_CLOCK_VIRTUAL,
virtio_net_rsc_purge, chain);
+ memset(&chain->stat, 0, sizeof(chain->stat));
if (ETH_P_IP == proto) {
chain->do_receive = virtio_net_rsc_receive4;
} else {
@@ -2168,6 +2208,7 @@ static ssize_t virtio_net_rsc_receive(NetClientState *nc,
if (!chain) {
return virtio_net_do_receive(nc, buf, size);
} else {
+ chain->stat.received++;
return chain->do_receive(chain, nc, buf, size);
}
}
@@ -2175,6 +2216,8 @@ static ssize_t virtio_net_rsc_receive(NetClientState *nc,
static ssize_t virtio_net_receive(NetClientState *nc,
const uint8_t *buf, size_t size)
{
+ virtio_net_received++;
+
if (virtio_net_rsc_bypass) {
return virtio_net_do_receive(nc, buf, size);
} else {
@@ -136,6 +136,38 @@ typedef enum {
RSC_WANT /* Data want to be coalesced */
} COALESCE_STATUS;
+typedef struct NetRscStat {
+ uint32_t received;
+ uint32_t coalesced;
+ uint32_t over_size;
+ uint32_t cache;
+ uint32_t empty_cache;
+ uint32_t no_match_cache;
+ uint32_t no_buf;
+ uint32_t win_update;
+ uint32_t no_match;
+ uint32_t tcp_syn;
+ uint32_t tcp_ctrl_drain;
+ uint32_t dup_ack;
+ uint32_t dup_ack1;
+ uint32_t dup_ack2;
+ uint32_t pure_ack;
+ uint32_t ack_out_of_win;
+ uint32_t data_out_of_win;
+ uint32_t data_out_of_order;
+ uint32_t data_after_pure_ack;
+ uint32_t bypass_not_tcp;
+ uint32_t tcp_option;
+ uint32_t tcp_larger_option;
+ uint32_t ip_frag;
+ uint32_t ip_hacked;
+ uint32_t ip_option;
+ uint32_t purge_failed;
+ uint32_t drain_failed;
+ uint32_t final_failed;
+ int64_t timer;
+} NetRscStat;
+
/* Coalesced segmant */
typedef struct NetRscSeg {
QTAILQ_ENTRY(NetRscSeg) next;
@@ -157,6 +189,7 @@ typedef struct NetRscChain {
VirtioNetReceive *do_receive;
QEMUTimer *drain_timer;
QTAILQ_HEAD(, NetRscSeg) buffers;
+ NetRscStat stat;
} NetRscChain;
void virtio_instance_init_common(Object *proxy_obj, void *data,