Palacios Public Git Repository

To checkout Palacios execute

  git clone http://v3vee.org/palacios/palacios.web/palacios.git
This will give you the master branch. You probably want the devel branch or one of the release branches. To switch to the devel branch, simply execute
  cd palacios
  git checkout --track -b devel origin/devel
The other branches are similar.


Fix a virtio ring buffer free run index overflow bug
[palacios.git] / palacios / src / devices / lnx_virtio_nic.c
index 6652c7b..ea998af 100644 (file)
@@ -7,13 +7,13 @@
  * and the University of New Mexico.  You can find out more at 
  * http://www.v3vee.org
  *
- * Copyright (c) 2008, Jack Lange <jarusl@cs.northwestern.edu>
  * Copyright (c) 2008, Lei Xia <lxia@northwestern.edu>
+ * Copyright (c) 2008, Cui Zheng <cuizheng@cs.unm.edu>
  * Copyright (c) 2008, The V3VEE Project <http://www.v3vee.org> 
  * All rights reserved.
  *
- * Author: Jack Lange <jarusl@cs.northwestern.edu>
- *               Lei Xia <lxia@northwestern.edu>
+ * Author: Lei Xia <lxia@northwestern.edu>
+ *             Cui Zheng <cuizheng@cs.unm.edu>
  *              
  *
  * This is free software.  You are permitted to use,
@@ -25,6 +25,7 @@
 #include <devices/lnx_virtio_pci.h>
 #include <palacios/vm_guest_mem.h>
 #include <palacios/vmm_sprintf.h>
+#include <palacios/vmm_vnet.h>
 
 #include <devices/pci.h>
 
@@ -34,6 +35,8 @@
 #define PrintDebug(fmt, args...)
 #endif
 
+//#define VIRTIO_NIC_PROFILE
+
 /* The feature bitmap for virtio net */
 #define VIRTIO_NET_F_CSUM      0       /* Host handles pkts w/ partial csum */
 #define VIRTIO_NET_F_GUEST_CSUM        1       /* Guest handles pkts w/ partial csum */
@@ -71,7 +74,7 @@ struct virtio_net_hdr {
 }__attribute__((packed));
 
        
-#define QUEUE_SIZE 256
+#define QUEUE_SIZE 4096
 #define CTRL_QUEUE_SIZE 64
 #define ETH_ALEN 6
 
@@ -103,20 +106,45 @@ struct virtio_net_state {
     void * backend_data;
     struct virtio_dev_state * virtio_dev;
     struct list_head dev_link;
+
+    ulong_t pkt_sent, pkt_recv, pkt_drop;
 };
 
-#if 1
-//Temporarly for debug
+
+#ifdef CONFIG_DEBUG_VIRTIO_NET
+
 static void print_packet(uchar_t *pkt, int size) {
-    PrintDebug("Vnet: print_data_packet: size: %d\n", size);
+    PrintDebug("Virtio Nic: print_data_packet: size: %d\n", size);
     v3_hexdump(pkt, size, NULL, 0);
 }
 
+#endif
+
+#if 0
+//Temporarly for debug
+
+static struct virtio_net_state *temp_net_states[3]; 
+static int net_idx = 0;
+
+static int __virtio_dev_send(uchar_t * buf, uint32_t size, void *private_data);
+
 static int send(uint8_t * buf, uint32_t count, void * private_data, struct vm_device *dest_dev)
 {
-   PrintDebug("Virito NIC: In sending stub\n");
-   print_packet(buf, count);
+   PrintDebug("Virito NIC: In sending stub, guest %p, count %d\n", private_data, count);
+
+#ifdef CONFIG_DEBUG_VIRTIO_NET
+   print_packet(buf, 20);
+#endif
 
+   struct virtio_net_state *virtio_state = (struct virtio_net_state *)private_data;
+
+   if (virtio_state == temp_net_states[0])
+       __virtio_dev_send(buf, count, temp_net_states[1]);
+   if (virtio_state == temp_net_states[1]){ //return a RARP packet
+       __virtio_dev_send(buf, count, temp_net_states[0]);
+   }
+   
    return count;
 }
 
@@ -126,6 +154,7 @@ static int receive(uint8_t * buf, uint32_t count, void * private_data, struct vm
 
     return 0;
 }
+
 #endif
 
 static int virtio_free(struct vm_device * dev) 
@@ -157,7 +186,6 @@ static int virtio_reset(struct virtio_net_state * virtio)
     virtio->virtio_cfg.host_features = 0;
     //virtio->virtio_cfg.status = VIRTIO_NET_S_LINK_UP;
     virtio->virtio_cfg.pci_isr = 0;
-    virtio->private_data = NULL;
 
     return 0;
 }
@@ -170,7 +198,7 @@ static int pkt_write(struct virtio_net_state * virtio, struct vring_desc * buf_d
     uint8_t * buf = NULL;
     uint32_t len = buf_desc->length;
 
-    PrintDebug("Handling Virtio Net write\n");
+    PrintDebug("Virtio NIC: Handling Virtio Write, net_state: %p\n", virtio);
 
     if (guest_pa_to_host_va(virtio->virtio_dev->vm, buf_desc->addr_gpa, (addr_t *)&(buf)) == -1) {
        PrintError("Could not translate buffer address\n");
@@ -178,8 +206,8 @@ static int pkt_write(struct virtio_net_state * virtio, struct vring_desc * buf_d
     }
 
     PrintDebug("Length=%d\n", buf_desc->length);
-    PrintDebug("Buffer Descriptor (ptr=%p) gpa=%p, len=%d, flags=%x, next=%d, buf address: %p, send address: %p\n", buf_desc, 
-              (void *)(buf_desc->addr_gpa), buf_desc->length, buf_desc->flags, buf_desc->next, buf, virtio->net_ops->send);
+    //PrintDebug("Buffer Descriptor (ptr=%p) gpa=%p, len=%d, flags=%x, next=%d, buf address: %p, send address: %p\n", buf_desc, 
+              //(void *)(buf_desc->addr_gpa), buf_desc->length, buf_desc->flags, buf_desc->next, buf, virtio->net_ops->send);
 
     if (virtio->net_ops->send(buf, len, (void *)virtio, NULL) == -1) {
        return -1;
@@ -214,33 +242,27 @@ static int copy_data_to_desc(struct virtio_net_state * virtio_state, struct vrin
     }
 
     len = (desc->length < buf_len)?desc->length:buf_len;
-
     memcpy(desc_buf, buf, len);
 
-    PrintDebug("Length=%d\n", len);
-
     return len;
 }
 
 
 //send data to guest
-static int send_pkt_to_guest(struct vm_device * dev, uchar_t * buf, uint_t size, int raw, void * private_data) 
+static int send_pkt_to_guest(struct virtio_net_state * virtio, uchar_t * buf, uint_t size, int raw, void * private_data) 
 {
-   // TODO: This should not be like this
-    struct virtio_net_state * virtio = (struct virtio_net_state *)dev->private_data;    
     struct virtio_queue * q = &(virtio->rx_vq);
     struct virtio_net_hdr hdr;
     uint32_t hdr_len = sizeof(struct virtio_net_hdr);
     uint32_t data_len = size;
     uint32_t offset = 0;
+       
+    PrintDebug("VIRTIO NIC:  sending packet to virtio nic %p, size:%d", virtio, size);
 
-    PrintDebug("VIRTIO Handle RX: cur_index=%d (mod=%d), avail_index=%d\n", 
-              q->cur_avail_idx, q->cur_avail_idx % q->queue_size, q->avail->index);
-
-
-
+    virtio->pkt_recv ++;
+       
     if (!raw) {
-       data_len -= hdr_len;
+       data_len -= hdr_len;
     }
 
     build_receive_header(&hdr, buf, 1);
@@ -251,48 +273,66 @@ static int send_pkt_to_guest(struct vm_device * dev, uchar_t * buf, uint_t size,
        return -1;
     }
 
-    
-    if (q->cur_avail_idx < q->avail->index) {
+    if (q->last_avail_idx > q->avail->index)
+       q->idx_overflow = true;
+    q->last_avail_idx = q->avail->index;
+
+    if (q->cur_avail_idx < q->avail->index || (q->idx_overflow && q->cur_avail_idx < q->avail->index+65536)){
        addr_t hdr_addr = 0;
        uint16_t hdr_idx = q->avail->ring[q->cur_avail_idx % q->queue_size];
        uint16_t buf_idx = 0;
        struct vring_desc * hdr_desc = NULL;
 
-       PrintDebug("Descriptor index=%d\n", q->cur_avail_idx % q->queue_size);
-
        hdr_desc = &(q->desc[hdr_idx]);
-
-       PrintDebug("Header Descriptor (ptr=%p) gpa=%p, len=%d, flags=%x, next=%d\n", hdr_desc, 
-                  (void *)(hdr_desc->addr_gpa), hdr_desc->length, hdr_desc->flags, hdr_desc->next);    
-
        if (guest_pa_to_host_va(virtio->virtio_dev->vm, hdr_desc->addr_gpa, &(hdr_addr)) == -1) {
            PrintError("Could not translate receive buffer address\n");
            return -1;
        }
 
-       //copy header to the header descriptor
        memcpy((void *)hdr_addr, &hdr, sizeof(struct virtio_net_hdr));
 
-       //copy data to the next descriptors
-       for (buf_idx = 0; offset < data_len; buf_idx = q->desc[hdr_idx].next) {
+       if (offset >= data_len) {
+           hdr_desc->flags &= ~VIRTIO_NEXT_FLAG;
+       }
+
+       for (buf_idx = hdr_desc->next; offset < data_len; buf_idx = q->desc[hdr_idx].next) {
            struct vring_desc * buf_desc = &(q->desc[buf_idx]);
            uint32_t len = 0;
 
-           buf_desc->flags = VIRTIO_NEXT_FLAG;
-        
            len = copy_data_to_desc(virtio, buf_desc, buf + offset, data_len - offset);
            
            offset += len;
+
+           if (offset < data_len) {
+               buf_desc->flags = VIRTIO_NEXT_FLAG;             
+           }
+
            buf_desc->length = len;  // TODO: do we need this?
        }
        
        q->used->ring[q->used->index % q->queue_size].id = q->avail->ring[q->cur_avail_idx % q->queue_size];
-       q->used->ring[q->used->index % q->queue_size].length = data_len; // What do we set this to????
+       q->used->ring[q->used->index % q->queue_size].length = data_len + hdr_len; // This should be the total length of data sent to guest (header+pkt_data)
 
        q->used->index++;
+
+       int last_idx = q->cur_avail_idx;
        q->cur_avail_idx++;
+       if (q->cur_avail_idx < last_idx)
+           q->idx_overflow = false;
+    } else {
+       virtio->pkt_drop++;
+       
+#ifdef VIRTIO_NIC_PROFILE
+       PrintError("Virtio NIC: %p, one pkt dropped receieved: %ld, dropped: %ld, sent: %ld curidx: %d, avaiIdx: %d\n", virtio, virtio->pkt_recv, virtio->pkt_drop, virtio->pkt_sent, q->cur_avail_idx, q->avail->index);
+#endif
     }
 
+#ifdef VIRTIO_NIC_PROFILE
+    if ((virtio->pkt_recv % 10000) == 0){
+       PrintError("Virtio NIC: %p, receieved: %ld, dropped: %ld, sent: %ld curidx: %d, avaiIdx: %d\n", virtio, virtio->pkt_recv, virtio->pkt_drop, virtio->pkt_sent, q->cur_avail_idx, q->avail->index);
+    }
+#endif
+
     if (!(q->avail->flags & VIRTIO_NO_IRQ_FLAG)) {
        PrintDebug("Raising IRQ %d\n",  virtio->pci_dev->config_header.intr_line);
        v3_pci_raise_irq(virtio->virtio_dev->pci_bus, 0, virtio->pci_dev);
@@ -303,11 +343,24 @@ static int send_pkt_to_guest(struct vm_device * dev, uchar_t * buf, uint_t size,
 }
 
 
-int virtio_send(struct vm_device * dev, uchar_t * buf, uint_t size) {
-    return send_pkt_to_guest(dev, buf, size, 1, NULL);
+// TODO: 
+int virtio_send(struct guest_info * vm, char *dev_name, uchar_t * buf, uint_t size) {
+    struct vm_device * virtio_dev = v3_find_dev(vm, dev_name);
+
+    // TODO: how to get virtio net state from device??
+    // this is not right now
+    struct virtio_net_state * virtio_state = (struct virtio_net_state *)virtio_dev->private_data;
+       
+    return send_pkt_to_guest(virtio_state, buf, size, 1, NULL);
 }
 
 
+static int __virtio_dev_send(uchar_t * buf, uint32_t size, void *private_data) {
+    struct virtio_net_state *virtio_state = (struct virtio_net_state *)private_data;
+       
+    return send_pkt_to_guest(virtio_state, buf, size, 1, NULL);
+}
+
 static int get_desc_count(struct virtio_queue * q, int index) {
     struct vring_desc * tmp_desc = &(q->desc[index]);
     int cnt = 1;
@@ -328,15 +381,21 @@ static int handle_ctrl(struct virtio_net_state * dev) {
 //get packet from guest
 static int handle_pkt_tx(struct virtio_net_state * virtio_state) 
 {
-    //struct virtio_net_state *virtio = (struct virtio_net_state *)dev->private_data;    
     struct virtio_queue * q = &(virtio_state->tx_vq);
+    struct virtio_net_hdr * hdr = NULL;
 
-    PrintDebug("VIRTIO NIC pkt_tx: cur_index=%d (mod=%d), avail_index=%d\n", 
-              q->cur_avail_idx, q->cur_avail_idx % q->queue_size, q->avail->index);
+    virtio_state->pkt_sent ++;
 
-    struct virtio_net_hdr * hdr = NULL;
+    if (q->avail->index < q->last_avail_idx)
+       q->idx_overflow = true;
+    q->last_avail_idx = q->avail->index;
 
-    while (q->cur_avail_idx < q->avail->index) {
+#ifdef VIRTIO_NIC_PROFILE
+    if(virtio_state->pkt_sent % 10000 == 0)
+       PrintError("Virtio NIC: %p, pkt_sent: %ld curidx: %d, avaiIdx: %d\n", virtio_state, virtio_state->pkt_sent, q->cur_avail_idx, q->avail->index);
+#endif 
+
+    while (q->cur_avail_idx < q->avail->index ||(q->idx_overflow && q->cur_avail_idx < (q->avail->index + 65536))) {
        struct vring_desc * hdr_desc = NULL;
        addr_t hdr_addr = 0;
        uint16_t desc_idx = q->avail->ring[q->cur_avail_idx % q->queue_size];
@@ -344,31 +403,17 @@ static int handle_pkt_tx(struct virtio_net_state * virtio_state)
        uint32_t req_len = 0;
        int i = 0;
 
-       PrintDebug("Descriptor Count=%d, index=%d\n", desc_cnt, q->cur_avail_idx % q->queue_size);
-
        hdr_desc = &(q->desc[desc_idx]);
-
-       PrintDebug("Header Descriptor (ptr=%p) gpa=%p, len=%d, flags=%x, next=%d\n", hdr_desc, 
-                  (void *)(hdr_desc->addr_gpa), hdr_desc->length, hdr_desc->flags, hdr_desc->next);    
-
        if (guest_pa_to_host_va(virtio_state->virtio_dev->vm, hdr_desc->addr_gpa, &(hdr_addr)) == -1) {
            PrintError("Could not translate block header address\n");
            return -1;
        }
 
-       //memcpy(&hdr, (void *)hdr_addr, sizeof(struct virtio_net_hdr));
        hdr = (struct virtio_net_hdr*)hdr_addr;
-       
-       PrintDebug("NIC Op Hdr (ptr=%p) header len =%x\n", (void *)hdr_addr, (int)hdr->hdr_len);
-
        desc_idx = hdr_desc->next;
        
        for (i = 0; i < desc_cnt - 1; i++) {    
            struct vring_desc * buf_desc = &(q->desc[desc_idx]);
-
-           PrintDebug("Buffer Descriptor (ptr=%p) gpa=%p, len=%d, flags=%x, next=%d\n", buf_desc, 
-                      (void *)(buf_desc->addr_gpa), buf_desc->length, buf_desc->flags, buf_desc->next);
-
            if (pkt_write(virtio_state, buf_desc) == -1) {
                PrintError("Error handling nic operation\n");
                return -1;
@@ -382,11 +427,14 @@ static int handle_pkt_tx(struct virtio_net_state * virtio_state)
        q->used->ring[q->used->index % q->queue_size].length = req_len; // What do we set this to????
 
        q->used->index++;
-       q->cur_avail_idx++;
+
+       int last_idx = q->cur_avail_idx;
+       q->cur_avail_idx ++;
+       if (q->cur_avail_idx < last_idx)
+           q->idx_overflow = false;
     }
 
     if (!(q->avail->flags & VIRTIO_NO_IRQ_FLAG)) {
-       PrintDebug("Raising IRQ %d\n",  virtio_state->pci_dev->config_header.intr_line);
        v3_pci_raise_irq(virtio_state->virtio_dev->pci_bus, 0, virtio_state->pci_dev);
        virtio_state->virtio_cfg.pci_isr = 0x1;
     }
@@ -444,7 +492,7 @@ static int virtio_io_write(uint16_t port, void * src, uint_t length, void * priv
     int port_idx = port % virtio->io_range_size;
 
 
-    PrintDebug("VIRTIO NIC Write for port %d (index=%d) len=%d, value=%x\n", 
+    PrintDebug("VIRTIO NIC %p Write for port %d (index=%d) len=%d, value=%x\n", private_data,
               port, port_idx,  length, *(uint32_t *)src);
 
 
@@ -500,10 +548,10 @@ static int virtio_io_write(uint16_t port, void * src, uint_t length, void * priv
            {
                uint16_t queue_idx = *(uint16_t *)src;     
                
-               PrintDebug("Handling Kick\n");
+               //PrintDebug("Handling Kick\n");
                
                if (queue_idx == 0){
-                   PrintError("receive queue notification\n");
+                   PrintDebug("receive queue notification 0, packet get by Guest\n");
                } else if (queue_idx == 1){
                    if (handle_pkt_tx(virtio) == -1) {
                        PrintError("Could not handle NIC Notification\n");
@@ -550,7 +598,7 @@ static int virtio_io_read(uint16_t port, void * dst, uint_t length, void * priva
     int port_idx = port % virtio->io_range_size;
     uint16_t queue_idx = virtio->virtio_cfg.vring_queue_selector;
 
-    PrintDebug("Virtio NIC: Read  for port %d (index =%d), length=%d", 
+    PrintDebug("Virtio NIC %p: Read  for port %d (index =%d), length=%d", private_data,
               port, port_idx, length);
        
     switch (port_idx) {
@@ -725,6 +773,7 @@ static int register_dev(struct virtio_dev_state * virtio, struct virtio_net_stat
     net_state->ctrl_vq.queue_size = CTRL_QUEUE_SIZE;
 
     net_state->virtio_dev = virtio;
+    net_state->pkt_sent = net_state->pkt_recv = net_state->pkt_drop = 0;
 
     virtio_reset(net_state);
 
@@ -751,13 +800,85 @@ static int connect_fn(struct guest_info * info,
     return 0;
 }
 
+#if 1 
+//temporary interface between Virtio-NIC and Vnet
+//Treat vnet as backend, and virtio nic as frontend
+
+//used when virtio_nic get a packet from guest and send it to the backend
+// send packet to all of the virtio nic devices other than the sender
+static int vnet_send(uint8_t * buf, uint32_t len, void * private_data, struct vm_device *dest_dev){
+
+    PrintDebug("Virito NIC: In vnet_send: guest net state %p\n", private_data);
+
+#ifdef CONFIG_DEBUG_VIRTIO_NET
+    print_packet(buf, len);
+#endif
+
+    v3_vnet_send_rawpkt(buf, len, private_data);
+    return 0;
+}
+
+//used to send packet to guest by a virtio nic
+static int vnet_receive(uint8_t * buf, uint32_t count, void * private_data, struct vm_device *src_dev){
+
+    return 0;
+}
+
+static int virtio_input(uchar_t * buf, uint_t len, void * private_data){
+    PrintDebug("Virito NIC: In virtio_input: guest net state %p\n", private_data);
+
+#ifdef CONFIG_DEBUG_VIRTIO_NET
+    print_packet(buf, len);
+#endif
+
+    return __virtio_dev_send(buf, len, private_data);
+}
+
+
+//register a virtio device to the vnet as backend
+void register_virtio_to_vnet(struct vm_device  *dev, 
+                                               char *dev_name,
+                                               uchar_t mac[6]){
+    struct virtio_net_state * net_state  = (struct virtio_net_state *)V3_Malloc(sizeof(struct virtio_net_state));
+    memset(net_state, 0, sizeof(struct virtio_net_state));
+
+    struct virtio_dev_state *virtio_state =  (struct virtio_dev_state *)dev->private_data;
+
+    net_state->net_ops = (struct v3_dev_net_ops *)V3_Malloc(sizeof(struct v3_dev_net_ops));
+
+    net_state->net_ops->send = &vnet_send;
+    net_state->net_ops->receive = &vnet_receive;
+
+    register_dev(virtio_state, net_state);
+       
+    PrintDebug("Virtio NIC After register Device %s: queue size: %d, %d\n", dev->name,
+              net_state->rx_vq.queue_size, net_state->tx_vq.queue_size);
+
+    PrintDebug("VNET: connect virtio nic state %p to vnet\n", net_state);
+
+    //add a device link to link table
+    int idx = vnet_register_device(dev, dev_name, mac, &virtio_input, net_state);
+
+    uchar_t srcmac[6] = {0x00,0x02,0x55,0x67,0x42,0x39};
+    uchar_t dstmac[6] = {0xff,0xff,0xff,0xff,0xff,0xff};
+    uchar_t zeromac[6] = {0,0,0,0,0,0};
+
+    vnet_add_route_entry(zeromac, dstmac, MAC_ANY, MAC_NONE, idx, LINK_INTERFACE, -1, LINK_INTERFACE);
+    if (idx == 0)
+       vnet_add_route_entry(zeromac, srcmac, MAC_ANY, MAC_NONE, idx, LINK_INTERFACE, -1, LINK_INTERFACE);
+    if (idx == 1)
+       vnet_add_route_entry(srcmac, zeromac, MAC_NONE, MAC_ANY, idx, LINK_INTERFACE, -1, LINK_INTERFACE);
+               
+}
+
+#endif
 
 static int virtio_init(struct guest_info * vm, v3_cfg_tree_t * cfg) {
     struct vm_device * pci_bus = v3_find_dev(vm, v3_cfg_val(cfg, "bus"));
     struct virtio_dev_state * virtio_state = NULL;
     char * name = v3_cfg_val(cfg, "name");
 
-    PrintDebug("Virtio NIC: Initializing VIRTIO Network device\n");
+    PrintDebug("Virtio NIC: Initializing VIRTIO Network device: %s\n", name);
 
     if (pci_bus == NULL) {
        PrintError("Virtio NIC: VirtIO devices require a PCI Bus");
@@ -784,8 +905,8 @@ static int virtio_init(struct guest_info * vm, v3_cfg_tree_t * cfg) {
 
 
     //for temporary testing, add a backend
-    #if 1
-   
+#if 0
+       
     struct virtio_net_state * net_state  = (struct virtio_net_state *)V3_Malloc(sizeof(struct virtio_net_state));
     memset(net_state, 0, sizeof(struct virtio_net_state));
 
@@ -796,12 +917,22 @@ static int virtio_init(struct guest_info * vm, v3_cfg_tree_t * cfg) {
 
     register_dev(virtio_state, net_state);
        
-    PrintDebug("Virtio NIC After register Device: queue size: %d, %d\n", 
+    PrintDebug("Virtio NIC After register Device %s: queue size: %d, %d\n", dev->name,
               net_state->rx_vq.queue_size, net_state->tx_vq.queue_size);
 
+    temp_net_states[net_idx ++] = net_state;
 
-    #endif
+    PrintDebug("Net_states: 0: %p, 1: %p, 2: %p\n", temp_net_states[0], temp_net_states[1], temp_net_states[2]);
 
+#endif
+
+#if 1  //test interface between vnet & virtio-nic
+
+    uchar_t mac[6] = {0,0,0,0,0,0};
+    register_virtio_to_vnet(dev, name,mac);
+
+#endif
+       
     return 0;
 }