virtio-net: avoid sg copy
Avoid tweaking iovec during receive. This removes the need to copy the vector. Note: we currently have an evil cast in work_around_broken_dhclient and unfortunately this patch does not fix it - just pushes the evil cast to another place. Signed-off-by: Michael S. Tsirkin <mst@redhat.com>
This commit is contained in:
		
							parent
							
								
									d336336c81
								
							
						
					
					
						commit
						22cc84db6e
					
				@ -504,40 +504,37 @@ static int virtio_net_has_buffers(VirtIONet *n, int bufsize)
 | 
				
			|||||||
 * cache.
 | 
					 * cache.
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
static void work_around_broken_dhclient(struct virtio_net_hdr *hdr,
 | 
					static void work_around_broken_dhclient(struct virtio_net_hdr *hdr,
 | 
				
			||||||
                                        const uint8_t *buf, size_t size)
 | 
					                                        uint8_t *buf, size_t size)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
    if ((hdr->flags & VIRTIO_NET_HDR_F_NEEDS_CSUM) && /* missing csum */
 | 
					    if ((hdr->flags & VIRTIO_NET_HDR_F_NEEDS_CSUM) && /* missing csum */
 | 
				
			||||||
        (size > 27 && size < 1500) && /* normal sized MTU */
 | 
					        (size > 27 && size < 1500) && /* normal sized MTU */
 | 
				
			||||||
        (buf[12] == 0x08 && buf[13] == 0x00) && /* ethertype == IPv4 */
 | 
					        (buf[12] == 0x08 && buf[13] == 0x00) && /* ethertype == IPv4 */
 | 
				
			||||||
        (buf[23] == 17) && /* ip.protocol == UDP */
 | 
					        (buf[23] == 17) && /* ip.protocol == UDP */
 | 
				
			||||||
        (buf[34] == 0 && buf[35] == 67)) { /* udp.srcport == bootps */
 | 
					        (buf[34] == 0 && buf[35] == 67)) { /* udp.srcport == bootps */
 | 
				
			||||||
        /* FIXME this cast is evil */
 | 
					        net_checksum_calculate(buf, size);
 | 
				
			||||||
        net_checksum_calculate((uint8_t *)buf, size);
 | 
					 | 
				
			||||||
        hdr->flags &= ~VIRTIO_NET_HDR_F_NEEDS_CSUM;
 | 
					        hdr->flags &= ~VIRTIO_NET_HDR_F_NEEDS_CSUM;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static int receive_header(VirtIONet *n, struct iovec *iov, int iovcnt,
 | 
					static int receive_header(VirtIONet *n, const struct iovec *iov, int iov_cnt,
 | 
				
			||||||
                          const void *buf, size_t size, size_t hdr_len)
 | 
					                          const void *buf, size_t size)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
    struct virtio_net_hdr *hdr = (struct virtio_net_hdr *)iov[0].iov_base;
 | 
					 | 
				
			||||||
    int offset = 0;
 | 
					    int offset = 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    hdr->flags = 0;
 | 
					 | 
				
			||||||
    hdr->gso_type = VIRTIO_NET_HDR_GSO_NONE;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    if (n->has_vnet_hdr) {
 | 
					    if (n->has_vnet_hdr) {
 | 
				
			||||||
        memcpy(hdr, buf, sizeof(*hdr));
 | 
					        /* FIXME this cast is evil */
 | 
				
			||||||
        offset = sizeof(*hdr);
 | 
					        void *wbuf = (void *)buf;
 | 
				
			||||||
        work_around_broken_dhclient(hdr, buf + offset, size - offset);
 | 
					        work_around_broken_dhclient(wbuf, wbuf + offset, size - offset);
 | 
				
			||||||
 | 
					        offset = sizeof(struct virtio_net_hdr);
 | 
				
			||||||
 | 
					        iov_from_buf(iov, iov_cnt, 0, buf, offset);
 | 
				
			||||||
 | 
					    } else {
 | 
				
			||||||
 | 
					        struct virtio_net_hdr hdr = {
 | 
				
			||||||
 | 
					            .flags = 0,
 | 
				
			||||||
 | 
					            .gso_type = VIRTIO_NET_HDR_GSO_NONE
 | 
				
			||||||
 | 
					        };
 | 
				
			||||||
 | 
					        iov_from_buf(iov, iov_cnt, 0, &hdr, sizeof hdr);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /* We only ever receive a struct virtio_net_hdr from the tapfd,
 | 
					 | 
				
			||||||
     * but we may be passing along a larger header to the guest.
 | 
					 | 
				
			||||||
     */
 | 
					 | 
				
			||||||
    iov[0].iov_base += hdr_len;
 | 
					 | 
				
			||||||
    iov[0].iov_len  -= hdr_len;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    return offset;
 | 
					    return offset;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -598,7 +595,8 @@ static ssize_t virtio_net_receive(NetClientState *nc, const uint8_t *buf, size_t
 | 
				
			|||||||
{
 | 
					{
 | 
				
			||||||
    VirtIONet *n = DO_UPCAST(NICState, nc, nc)->opaque;
 | 
					    VirtIONet *n = DO_UPCAST(NICState, nc, nc)->opaque;
 | 
				
			||||||
    struct virtio_net_hdr_mrg_rxbuf *mhdr = NULL;
 | 
					    struct virtio_net_hdr_mrg_rxbuf *mhdr = NULL;
 | 
				
			||||||
    size_t offset, i;
 | 
					    const struct iovec *sg = elem.in_sg;
 | 
				
			||||||
 | 
					    size_t offset, i, guest_offset;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    if (!virtio_net_can_receive(&n->nic->nc))
 | 
					    if (!virtio_net_can_receive(&n->nic->nc))
 | 
				
			||||||
        return -1;
 | 
					        return -1;
 | 
				
			||||||
@ -615,7 +613,7 @@ static ssize_t virtio_net_receive(NetClientState *nc, const uint8_t *buf, size_t
 | 
				
			|||||||
    while (offset < size) {
 | 
					    while (offset < size) {
 | 
				
			||||||
        VirtQueueElement elem;
 | 
					        VirtQueueElement elem;
 | 
				
			||||||
        int len, total;
 | 
					        int len, total;
 | 
				
			||||||
        struct iovec sg[VIRTQUEUE_MAX_SIZE];
 | 
					        const struct iovec *sg = elem.in_sg;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        total = 0;
 | 
					        total = 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -640,20 +638,20 @@ static ssize_t virtio_net_receive(NetClientState *nc, const uint8_t *buf, size_t
 | 
				
			|||||||
            exit(1);
 | 
					            exit(1);
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        memcpy(&sg, &elem.in_sg[0], sizeof(sg[0]) * elem.in_num);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        if (i == 0) {
 | 
					        if (i == 0) {
 | 
				
			||||||
            if (n->mergeable_rx_bufs)
 | 
					            if (n->mergeable_rx_bufs)
 | 
				
			||||||
                mhdr = (struct virtio_net_hdr_mrg_rxbuf *)sg[0].iov_base;
 | 
					                mhdr = (struct virtio_net_hdr_mrg_rxbuf *)sg[0].iov_base;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            offset += receive_header(n, sg, elem.in_num,
 | 
					            offset += receive_header(n, sg, elem.in_num,
 | 
				
			||||||
                                     buf + offset, size - offset,
 | 
					                                     buf + offset, size - offset);
 | 
				
			||||||
                                     n->guest_hdr_len);
 | 
					 | 
				
			||||||
            total += n->guest_hdr_len;
 | 
					            total += n->guest_hdr_len;
 | 
				
			||||||
 | 
					            guest_offset = n->guest_hdr_len;
 | 
				
			||||||
 | 
					        } else {
 | 
				
			||||||
 | 
					            guest_offset = 0;
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        /* copy in packet.  ugh */
 | 
					        /* copy in packet.  ugh */
 | 
				
			||||||
        len = iov_from_buf(sg, elem.in_num, 0,
 | 
					        len = iov_from_buf(sg, elem.in_num, guest_offset,
 | 
				
			||||||
                           buf + offset, size - offset);
 | 
					                           buf + offset, size - offset);
 | 
				
			||||||
        total += len;
 | 
					        total += len;
 | 
				
			||||||
        offset += len;
 | 
					        offset += len;
 | 
				
			||||||
 | 
				
			|||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user