typedef struct VirtIOFeature {
uint32_t flags;
size_t end;
@@ -1019,7 +1036,8 @@ static int receive_filter(VirtIONet *n, const uint8_t
*buf, int size)
return 0;
}
-static ssize_t virtio_net_receive(NetClientState *nc, const uint8_t *buf, size_t size)
+static ssize_t virtio_net_do_receive(NetClientState *nc,
+ const uint8_t *buf, size_t size)
{
VirtIONet *n = qemu_get_nic_opaque(nc);
VirtIONetQueue *q = virtio_net_get_subqueue(nc);
@@ -1623,6 +1641,159 @@ static void virtio_net_rsc_cleanup(VirtIONet *n)
}
}
+static int virtio_net_rsc_cache_buf(NetRscChain *chain, NetClientState *nc,
+ const uint8_t *buf, size_t size)
+{
+ NetRscSeg *seg;
+
+ seg = g_malloc(sizeof(NetRscSeg));
+ if (!seg) {
+ return 0;
+ }
+
+ seg->buf = g_malloc(MAX_VIRTIO_IP_PAYLOAD);
+ if (!seg->buf) {
+ goto out;
+ }
+
+ memmove(seg->buf, buf, size);
+ seg->size = size;
+ seg->dup_ack_count = 0;
+ seg->is_coalesced = 0;
+ seg->nc = nc;
+
+ QTAILQ_INSERT_TAIL(&chain->buffers, seg, next);
+ return size;
+
+out:
+ g_free(seg);
+ return 0;
+}
+
+
+static int32_t virtio_net_rsc_try_coalesce4(NetRscChain *chain,
+ NetRscSeg *seg, const uint8_t *buf, size_t size)
+{
+ /* This real part of this function will be introduced in next patch, just
+ * return a 'final' to feed the compilation. */
+ return RSC_FINAL;
+}
+
+static size_t virtio_net_rsc_callback(NetRscChain *chain, NetClientState *nc,
+ const uint8_t *buf, size_t size, VirtioNetCoalesce *coalesce)
+{
+ int ret;
+ NetRscSeg *seg, *nseg;
+
+ if (QTAILQ_EMPTY(&chain->buffers)) {
+ if (!virtio_net_rsc_cache_buf(chain, nc, buf, size)) {
+ return 0;
+ } else {
+ return size;
+ }
+ }
+
+ QTAILQ_FOREACH_SAFE(seg, &chain->buffers, next, nseg) {
+ ret = coalesce(chain, seg, buf, size);
+ if (RSC_FINAL == ret) {
+ ret = virtio_net_do_receive(seg->nc, seg->buf, seg->size);
+ QTAILQ_REMOVE(&chain->buffers, seg, next);
+ g_free(seg->buf);
+ g_free(seg);
+ if (ret == 0) {
+ /* Send failed */
+ return 0;
+ }
+
+ /* Send current packet */
+ return virtio_net_do_receive(nc, buf, size);
+ } else if (RSC_NO_MATCH == ret) {
+ continue;
+ } else {
+ /* Coalesced, mark coalesced flag to tell calc cksum for ipv4 */
+ seg->is_coalesced = 1;
+ return size;
+ }
+ }
+
+ return virtio_net_rsc_cache_buf(chain, nc, buf, size);
+}
+
+static size_t virtio_net_rsc_receive4(void *opq, NetClientState* nc,
+ const uint8_t *buf, size_t size)
+{
+ NetRscChain *chain;
+
+ chain = (NetRscChain *)opq;
+ return virtio_net_rsc_callback(chain, nc, buf, size,
+ virtio_net_rsc_try_coalesce4);
+}
+
+static NetRscChain *virtio_net_rsc_lookup_chain(NetClientState *nc,
+ uint16_t proto)
+{
+ VirtIONet *n;
+ NetRscChain *chain;
+ NICState *nic;
+
+ /* Only handle IPv4/6 */
+ if (proto != (uint16_t)ETH_P_IP) {
+ return NULL;
+ }
+
+ nic = (NICState *)nc;
+ n = container_of(&nic, VirtIONet, nic);
+ QTAILQ_FOREACH(chain, &n->rsc_chains, next) {
+ if (chain->proto == proto) {
+ return chain;
+ }
+ }
+
+ chain = g_malloc(sizeof(*chain));
+ if (!chain) {
+ rsc_chain_no_mem++;
+ return NULL;
+ }
+
+ chain->proto = proto;
+ chain->do_receive = virtio_net_rsc_receive4;
+
+ QTAILQ_INIT(&chain->buffers);
+ QTAILQ_INSERT_TAIL(&n->rsc_chains, chain, next);
+ return chain;
+}
+
+static ssize_t virtio_net_rsc_receive(NetClientState *nc,
+ const uint8_t *buf, size_t size)
+{
+ uint16_t proto;
+ NetRscChain *chain;
+ struct eth_header *eth;
+
+ if (size < IP_OFFSET) {
+ return virtio_net_do_receive(nc, buf, size);
+ }
+
+ eth = (struct eth_header *)(buf + VIRTIO_HEADER);
+ proto = htons(eth->h_proto);
+ chain = virtio_net_rsc_lookup_chain(nc, proto);
+ if (!chain) {
+ return virtio_net_do_receive(nc, buf, size);
+ } else {
+ return chain->do_receive(chain, nc, buf, size);
+ }
+}
+
+static ssize_t virtio_net_receive(NetClientState *nc,
+ const uint8_t *buf, size_t size)
+{
+ if (virtio_net_rsc_bypass) {
+ return virtio_net_do_receive(nc, buf, size);
+ } else {
+ return virtio_net_rsc_receive(nc, buf, size);
+ }
+}
+
static NetClientInfo net_virtio_info = {
.type = NET_CLIENT_OPTIONS_KIND_NIC,
.size = sizeof(NICState),
--
2.4.0