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.


Merge branch 'devel' of palacios@newskysaw.cs.northwestern.edu:/home/palacios/palacio...
[palacios.git] / palacios / src / devices / lnx_virtio_nic.c
index fb5c18b..bb13a69 100644 (file)
@@ -29,6 +29,8 @@
 #include <palacios/vmm_lock.h>
 #include <palacios/vmm_util.h>
 #include <devices/pci.h>
+#include <palacios/vmm_ethernet.h>
+#include <palacios/vmm_time.h>
 
 
 #ifndef CONFIG_DEBUG_VIRTIO_NET
 #define PrintDebug(fmt, args...)
 #endif
 
-#define VIRTIO_NET_S_LINK_UP   1       /* Link is up */
 #define VIRTIO_NET_MAX_BUFSIZE (sizeof(struct virtio_net_hdr) + (64 << 10))
 
+
 struct virtio_net_hdr {
        uint8_t flags;
-
-#define VIRTIO_NET_HDR_GSO_NONE                0       /* Not a GSO frame */
+       
        uint8_t gso_type;
        uint16_t hdr_len;               /* Ethernet + IP + tcp/udp hdrs */
        uint16_t gso_size;              /* Bytes to append to hdr_len per frame */
@@ -51,23 +52,24 @@ struct virtio_net_hdr {
 }__attribute__((packed));
 
 
-/* This is the version of the header to use when the MRG_RXBUF
- * feature has been negotiated. */
 struct virtio_net_hdr_mrg_rxbuf {
        struct virtio_net_hdr hdr;
        uint16_t num_buffers;   /* Number of merged rx buffers */
 };
 
        
-#define TX_QUEUE_SIZE 64
-#define RX_QUEUE_SIZE 1024
+#define TX_QUEUE_SIZE 256
+#define RX_QUEUE_SIZE 4096
 #define CTRL_QUEUE_SIZE 64
-#define ETH_ALEN 6
 
 #define VIRTIO_NET_F_MRG_RXBUF 15      /* Host can merge receive buffers. */
 #define VIRTIO_NET_F_MAC       5       /* Host has given MAC address. */
 #define VIRTIO_NET_F_GSO       6       /* Host handles pkts w/ any GSO type */
 #define VIRTIO_NET_F_HOST_TSO4 11      /* Host can handle TSOv4 in. */
+#define VIRTIO_NET_F_HOST_UFO  14      /* Host can handle UFO in. */
+
+/* Port to get virtio config */
+#define VIRTIO_NET_CONFIG 20  
 
 struct virtio_net_config
 {
@@ -79,6 +81,8 @@ struct virtio_dev_state {
     struct vm_device * pci_bus;
     struct list_head dev_list;
     struct v3_vm_info *vm;
+
+    uint8_t mac[ETH_ALEN];
 };
 
 struct virtio_net_state {
@@ -89,38 +93,26 @@ struct virtio_net_state {
     struct pci_device * pci_dev; 
     int io_range_size;
     
-    struct virtio_queue rx_vq;         /* idx 0, pkts to guest */
-    struct virtio_queue tx_vq;         /* idx 1, pkts from guest */
-    struct virtio_queue ctrl_vq;       /* idx 2 */
+    struct virtio_queue rx_vq;         /* idx 0*/
+    struct virtio_queue tx_vq;         /* idx 1*/
+    struct virtio_queue ctrl_vq;       /* idx 2*/
 
-    int buffed_rx;
-    int tx_disabled;                   /* stop TX pkts from guest */
+    struct v3_timer * timer;
 
-    uint64_t pkt_sent, pkt_recv, pkt_drop;
+    struct nic_statistics statistics;
 
     struct v3_dev_net_ops * net_ops;
     v3_lock_t rx_lock, tx_lock;
 
+    uint8_t tx_notify, rx_notify;
+    uint32_t tx_pkts, rx_pkts;
+    uint64_t past_ms;
+
     void * backend_data;
     struct virtio_dev_state * virtio_dev;
     struct list_head dev_link;
 };
 
-/* virtio nic error type */
-#define ERR_VIRTIO_OTHER  1
-#define ERR_VIRTIO_RXQ_FULL  2
-#define ERR_VIRTIO_RXQ_NOSET  3
-#define ERR_VIRTIO_TXQ_NOSET 4
-#define ERR_VIRTIO_TXQ_FULL 5
-#define ERR_VIRTIO_TXQ_DISABLED 6
-
-
-static int virtio_free(struct vm_device * dev) 
-{
-       
-    return 0;
-}
-
 static int virtio_init_state(struct virtio_net_state * virtio) 
 {
     virtio->rx_vq.queue_size = RX_QUEUE_SIZE;
@@ -147,21 +139,19 @@ static int virtio_init_state(struct virtio_net_state * virtio)
 
     virtio->virtio_cfg.pci_isr = 0;
        
-    virtio->virtio_cfg.host_features = 0; // (1 << VIRTIO_NET_F_MAC);
+    virtio->virtio_cfg.host_features = 0 | (1 << VIRTIO_NET_F_MAC) | 
+                                                               (1 << VIRTIO_NET_F_HOST_UFO) | 
+                                                               (1 << VIRTIO_NET_F_HOST_TSO4);
 
     if ((v3_lock_init(&(virtio->rx_lock)) == -1) ||
        (v3_lock_init(&(virtio->tx_lock)) == -1)){
         PrintError("Virtio NIC: Failure to init locks for net_state\n");
     }
 
-    virtio->pkt_sent = virtio->pkt_recv = virtio->pkt_drop = 0;
-    virtio->buffed_rx = 0;
-
     return 0;
 }
 
-static int 
-pkt_tx(struct guest_info * core, 
+static int tx_one_pkt(struct guest_info * core, 
        struct virtio_net_state * virtio, 
        struct vring_desc * buf_desc) 
 {
@@ -169,11 +159,20 @@ pkt_tx(struct guest_info * core,
     uint32_t len = buf_desc->length;
 
     if (v3_gpa_to_hva(core, buf_desc->addr_gpa, (addr_t *)&(buf)) == -1) {
-       PrintError("Could not translate buffer address\n");
-       return -ERR_VIRTIO_OTHER;
+       PrintDebug("Could not translate buffer address\n");
+       return -1;
+    }
+
+    if(virtio->net_ops->send(buf, len, virtio->backend_data) >= 0){
+       virtio->statistics.tx_pkts ++;
+       virtio->statistics.tx_bytes += len;
+
+       return 0;
     }
 
-    return virtio->net_ops->send(buf, len, virtio->backend_data);
+    virtio->statistics.tx_dropped ++;
+
+    return -1;
 }
 
 
@@ -220,50 +219,37 @@ static inline void disable_cb(struct virtio_queue *queue) {
 }
 
 
-/* interrupt the guest, so the guest core get EXIT to Palacios
- * this happens when there are either incoming pkts for the guest
- * or the guest can start TX pkts again */
+/* interrupt the guest, so the guest core get EXIT to Palacios */
 static inline void notify_guest(struct virtio_net_state * virtio){
     v3_interrupt_cpu(virtio->virtio_dev->vm, virtio->virtio_dev->vm->cores[0].cpu_id, 0);
 }
 
 
-/* guest free some pkts from rx queue */
-static int handle_rx_kick(struct guest_info *core, 
+/* guest free some pkts for rx queue */
+static int handle_rx_queue_kick(struct guest_info * core, 
                          struct virtio_net_state * virtio) 
 {
-    unsigned long flags;
-
-    flags = v3_lock_irqsave(virtio->rx_lock);
-
-    virtio->net_ops->start_rx(virtio->backend_data);
-    //disable_cb(&virtio->rx_vq);
-
-    v3_unlock_irqrestore(virtio->rx_lock, flags);
-       
     return 0;
 }
 
 
-static int handle_ctrl(struct guest_info *core, 
+static int handle_ctrl(struct guest_info * core, 
                       struct virtio_net_state * virtio) {
        
     return 0;
 }
 
-static int handle_pkt_tx(struct guest_info *core, 
+static int handle_pkt_tx(struct guest_info * core, 
                         struct virtio_net_state * virtio_state) 
 {
-    struct virtio_queue * q = &(virtio_state->tx_vq);
-    struct virtio_net_hdr * hdr = NULL;
-    int recved = 0;
+    struct virtio_queue *q = &(virtio_state->tx_vq);
+    struct virtio_net_hdr *hdr = NULL;
+    int txed = 0;
     unsigned long flags;
 
-    if (!q->ring_avail_addr) 
-       return -ERR_VIRTIO_TXQ_NOSET;
-
-    if(virtio_state->tx_disabled)
-       return -ERR_VIRTIO_TXQ_DISABLED;
+    if (!q->ring_avail_addr) {
+       return -1;
+    }
 
     flags = v3_lock_irqsave(virtio_state->tx_lock);
     while (q->cur_avail_idx != q->avail->index) {
@@ -280,18 +266,18 @@ static int handle_pkt_tx(struct guest_info *core,
            goto exit_error;
        }
 
-       hdr = (struct virtio_net_hdr*)hdr_addr;
+       hdr = (struct virtio_net_hdr *)hdr_addr;
        desc_idx = hdr_desc->next;
 
        if(desc_cnt > 2){
-           PrintError("VNIC: merged rx buffer not supported\n");
+           PrintError("VNIC: merged rx buffer not supported, desc_cnt %d\n", desc_cnt);
            goto exit_error;
        }
 
        /* here we assumed that one ethernet pkt is not splitted into multiple virtio buffer */
        for (i = 0; i < desc_cnt - 1; i++) {    
            struct vring_desc * buf_desc = &(q->desc[desc_idx]);
-           if (pkt_tx(core, virtio_state, buf_desc) == -1) {
+           if (tx_one_pkt(core, virtio_state, buf_desc) == -1) {
                PrintError("Error handling nic operation\n");
                goto exit_error;
            }
@@ -299,24 +285,23 @@ static int handle_pkt_tx(struct guest_info *core,
            req_len += buf_desc->length;
            desc_idx = buf_desc->next;
        }
-       virtio_state->pkt_sent ++;
-       recved ++;
 
        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 = req_len; // What do we set this to????
+       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 ++;
+
+       txed ++;
     }
 
     v3_unlock_irqrestore(virtio_state->tx_lock, flags);
-
-    if(!recved)
-       return 0;
        
-    if (!(q->avail->flags & VIRTIO_NO_IRQ_FLAG)) {
+    if (txed && !(q->avail->flags & VIRTIO_NO_IRQ_FLAG)) {
        v3_pci_raise_irq(virtio_state->virtio_dev->pci_bus, 0, virtio_state->pci_dev);
        virtio_state->virtio_cfg.pci_isr = 0x1;
+
+       virtio_state->statistics.interrupts ++;
     }
 
     return 0;
@@ -324,7 +309,7 @@ static int handle_pkt_tx(struct guest_info *core,
 exit_error:
        
     v3_unlock_irqrestore(virtio_state->tx_lock, flags);
-    return -ERR_VIRTIO_OTHER;
+    return -1;
 }
 
 
@@ -398,11 +383,9 @@ static int virtio_io_write(struct guest_info *core,
            switch (queue_idx) {
                case 0:
                    virtio_setup_queue(core, virtio, &virtio->rx_vq, pfn, page_addr);
-                   //disable_cb(&virtio->rx_vq);
                    break;
                case 1:
                    virtio_setup_queue(core, virtio, &virtio->tx_vq, pfn, page_addr);
-                   //disable_cb(&virtio->tx_vq);
                    break;
                case 2:
                    virtio_setup_queue(core, virtio, &virtio->ctrl_vq, pfn, page_addr);
@@ -425,15 +408,18 @@ static int virtio_io_write(struct guest_info *core,
            {
                uint16_t queue_idx = *(uint16_t *)src;                  
                if (queue_idx == 0){
-                   handle_rx_kick(core, virtio);
+                   if(handle_rx_queue_kick(core, virtio) == -1){
+                       PrintError("Could not handle Virtio NIC rx kick\n");
+                       return -1;
+                   }
                } else if (queue_idx == 1){
                    if (handle_pkt_tx(core, virtio) == -1) {
-                       PrintError("Could not handle NIC Notification\n");
+                       PrintError("Could not handle Virtio NIC tx kick\n");
                        return -1;
                    }
                } else if (queue_idx == 2){
                    if (handle_ctrl(core, virtio) == -1) {
-                       PrintError("Could not handle NIC Notification\n");
+                       PrintError("Could not handle Virtio NIC ctrl kick\n");
                        return -1;
                    }
                } else {
@@ -445,7 +431,6 @@ static int virtio_io_write(struct guest_info *core,
        case VIRTIO_STATUS_PORT:
            virtio->virtio_cfg.status = *(uint8_t *)src;
            if (virtio->virtio_cfg.status == 0) {
-               PrintDebug("Resetting device\n");
                virtio_init_state(virtio);
            }
            break;
@@ -470,7 +455,7 @@ static int virtio_io_read(struct guest_info *core,
     int port_idx = port % virtio->io_range_size;
     uint16_t queue_idx = virtio->virtio_cfg.vring_queue_selector;
 
-    PrintDebug("Virtio NIC %p: Read  for port %d (index =%d), length=%d\n", private_data,
+    PrintDebug("Virtio NIC %p: Read  for port 0x%x (index =%d), length=%d\n", private_data,
               port, port_idx, length);
        
     switch (port_idx) {
@@ -536,6 +521,10 @@ static int virtio_io_read(struct guest_info *core,
            v3_pci_lower_irq(virtio->virtio_dev->pci_bus, 0, virtio->pci_dev);
            break;
 
+       case VIRTIO_NET_CONFIG ... VIRTIO_NET_CONFIG + ETH_ALEN:
+           *(uint8_t *)dst = virtio->net_cfg.mac[port_idx-VIRTIO_NET_CONFIG];
+           break;
+
        default:
            PrintError("Virtio NIC: Read of Unhandled Virtio Read:%d\n", port_idx);
            return -1;
@@ -545,39 +534,29 @@ static int virtio_io_read(struct guest_info *core,
 }
 
 
+/* receiving raw ethernet pkt from backend */
 static int virtio_rx(uint8_t * buf, uint32_t size, void * private_data) {
     struct virtio_net_state * virtio = (struct virtio_net_state *)private_data;
     struct virtio_queue * q = &(virtio->rx_vq);
     struct virtio_net_hdr_mrg_rxbuf hdr;
     uint32_t hdr_len = sizeof(struct virtio_net_hdr_mrg_rxbuf);
-    uint32_t data_len = size;
+    uint32_t data_len;
     uint32_t offset = 0;
     unsigned long flags;
-    int ret_val = -ERR_VIRTIO_OTHER;
-    int raw = 1;
 
-#ifndef CONFIG_DEBUG_VIRTIO_NET
-   {
-       PrintDebug("Virtio-NIC: virtio_rx: size: %d\n", size);  
-       //v3_hexdump(buf, size, NULL, 0);
-   }
+#ifdef CONFIG_DEBUG_VIRTIO_NET
+    PrintDebug("Virtio-NIC: virtio_rx: size: %d\n", size);     
+    v3_hexdump(buf, size, NULL, 0);
 #endif
 
     flags = v3_lock_irqsave(virtio->rx_lock);
 
-    virtio->pkt_recv ++;
-    if (!raw)
-       data_len -= hdr_len;
-
-    if (!raw)
-        memcpy(&hdr, buf, sizeof(struct virtio_net_hdr_mrg_rxbuf));
-    else
-        memset(&hdr, 0, sizeof(struct virtio_net_hdr_mrg_rxbuf));
+    data_len = size;
+    memset(&hdr, 0, sizeof(struct virtio_net_hdr_mrg_rxbuf));
 
     if (q->ring_avail_addr == 0) {
-       PrintError("Queue is not set\n");
-       ret_val = -ERR_VIRTIO_RXQ_NOSET;
-       goto exit;
+       PrintDebug("Queue is not set\n");
+       goto err_exit;
     }
 
     if (q->cur_avail_idx != q->avail->index){
@@ -588,8 +567,8 @@ static int virtio_rx(uint8_t * buf, uint32_t size, void * private_data) {
 
        hdr_desc = &(q->desc[hdr_idx]);
        if (v3_gpa_to_hva(&(virtio->virtio_dev->vm->cores[0]), hdr_desc->addr_gpa, &(hdr_addr)) == -1) {
-           PrintError("Could not translate receive buffer address\n");
-           goto exit;
+           PrintDebug("Could not translate receive buffer address\n");
+           goto err_exit;
        }
        hdr.num_buffers = 1;
        memcpy((void *)hdr_addr, &hdr, sizeof(struct virtio_net_hdr_mrg_rxbuf));
@@ -616,86 +595,69 @@ static int virtio_rx(uint8_t * buf, uint32_t size, void * private_data) {
        q->used->index++;
        q->cur_avail_idx++;
 
-       /* if there are certain num of pkts in the RX queue, notify guest 
-         * so guest will exit to palacios
-         * when it returns, guest gets the virtio rx interrupt */
-       if((++virtio->buffed_rx > q->queue_size/5) &&
-           (q->avail->flags & VIRTIO_NO_IRQ_FLAG)) {
-           if(virtio->virtio_dev->vm->cores[0].cpu_id != V3_Get_CPU()){
-                 notify_guest(virtio);
-           }
-           virtio->buffed_rx = 0;
-       }
+       virtio->statistics.rx_pkts ++;
+       virtio->statistics.rx_bytes += size;
     } else {
-       virtio->pkt_drop++;
-       /* RX queue is full,  tell backend to stop RX on this device */
-       virtio->net_ops->stop_rx(virtio->backend_data);
-       enable_cb(&virtio->rx_vq);
+       virtio->statistics.rx_dropped ++;
        
-       ret_val = -ERR_VIRTIO_RXQ_FULL;
-       goto exit;
+       goto err_exit;
     }
 
     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);
        virtio->virtio_cfg.pci_isr = 0x1;
+       virtio->statistics.interrupts ++;
+    }
+
+    v3_unlock_irqrestore(virtio->rx_lock, flags);
+
+    /* notify guest if guest is running */
+    if(virtio->rx_notify == 1){
+       v3_interrupt_cpu(virtio->virtio_dev->vm, virtio->virtio_dev->vm->cores[0].cpu_id, 0);
     }
 
-    ret_val = offset;
+    return 0;
 
-exit:
+err_exit:
 
     v3_unlock_irqrestore(virtio->rx_lock, flags);
  
-    return ret_val;
+    return -1;
 }
 
+static int virtio_free(struct virtio_dev_state * virtio) {
+    struct virtio_net_state * backend = NULL;
+    struct virtio_net_state * tmp = NULL;
 
-static struct v3_device_ops dev_ops = {
-    .free = virtio_free,
-};
 
+    list_for_each_entry_safe(backend, tmp, &(virtio->dev_list), dev_link) {
 
-/* TODO: Issue here: which vm info it needs? calling VM or the device's own VM? */
-static void virtio_nic_poll(struct v3_vm_info * vm, void * data){
-    struct virtio_net_state * virtio = (struct virtio_net_state *)data;
-       
-    handle_pkt_tx(&(vm->cores[0]), virtio);
+       // unregister from PCI
+
+       list_del(&(backend->dev_link));
+       V3_Free(backend);
+    }
+
+    V3_Free(virtio);
+    return 0;
 }
 
-static void virtio_start_tx(void * data){
-    struct virtio_net_state * virtio = (struct virtio_net_state *)data;
-    unsigned long flags;
 
-    flags = v3_lock_irqsave(virtio->tx_lock);
-    virtio->tx_disabled = 0;
+static struct v3_device_ops dev_ops = {
+    .free = (int (*)(void *))virtio_free,
+};
 
-    /* notify the device's guest to start sending pkt */
-    if(virtio->virtio_dev->vm->cores[0].cpu_id != V3_Get_CPU()){
-       notify_guest(virtio);
-    }
-    v3_unlock_irqrestore(virtio->tx_lock, flags);      
-}
 
-static void virtio_stop_tx(void * data){
+static void virtio_nic_poll(struct v3_vm_info * vm, int budget, void * data){
     struct virtio_net_state * virtio = (struct virtio_net_state *)data;
-    unsigned long flags;
 
-    flags = v3_lock_irqsave(virtio->tx_lock);
-    virtio->tx_disabled = 1;
-
-    /* stop the guest to exit to palacios for sending pkt? */
-    if(virtio->virtio_dev->vm->cores[0].cpu_id != V3_Get_CPU()){
-       disable_cb(&virtio->tx_vq);
+    if(virtio->tx_notify == 0){
+       handle_pkt_tx(&(vm->cores[0]), virtio);
     }
-
-    v3_unlock_irqrestore(virtio->tx_lock, flags);
 }
 
-       
-
-
 static int register_dev(struct virtio_dev_state * virtio, 
                        struct virtio_net_state * net_state) 
 {
@@ -712,8 +674,9 @@ static int register_dev(struct virtio_dev_state * virtio,
        net_state->io_range_size <<= 1;
     }
        
-    // this is to account for any low order bits being set in num_ports
-    // if there are none, then num_ports was already a power of 2 so we shift right to reset it
+    /* this is to account for any low order bits being set in num_ports
+      * if there are none, then num_ports was already a power of 2 so we shift right to reset it
+      */
     if ((num_ports & ((net_state->io_range_size >> 1) - 1)) == 0) {
        net_state->io_range_size >>= 1;
     }
@@ -757,16 +720,71 @@ static int register_dev(struct virtio_dev_state * virtio,
     net_state->pci_dev = pci_dev;
     net_state->virtio_dev = virtio;
 
-    uchar_t mac[6] = {0x11,0x11,0x11,0x11,0x11,0x11};
-    memcpy(net_state->net_cfg.mac, mac, 6);
-                                                                                                          
-    memcpy(pci_dev->config_data, net_state->net_cfg.mac, ETH_ALEN);
-    
+    memcpy(net_state->net_cfg.mac, virtio->mac, 6);                           
+       
     virtio_init_state(net_state);
 
+    /* Add backend to list of devices */
+    list_add(&(net_state->dev_link), &(virtio->dev_list));
+
     return 0;
 }
 
+#define RATE_UPPER_THRESHOLD 10  /* 10000 pkts per second, around 100Mbits */
+#define RATE_LOWER_THRESHOLD 1
+#define PROFILE_PERIOD 50 /*50ms*/
+
+/* Timer Functions */
+static void virtio_nic_timer(struct guest_info * core, 
+                            uint64_t cpu_cycles, uint64_t cpu_freq, 
+                            void * priv_data) {
+    struct virtio_net_state * net_state = (struct virtio_net_state *)priv_data;
+    uint64_t period_ms;
+
+    period_ms = cpu_cycles/cpu_freq;
+    net_state->past_ms += period_ms;
+
+    if(net_state->past_ms >  PROFILE_PERIOD){ 
+       uint32_t tx_rate, rx_rate;
+       
+       tx_rate = (net_state->statistics.tx_pkts - net_state->tx_pkts)/net_state->past_ms; /* pkts/per ms */
+       rx_rate = (net_state->statistics.rx_pkts - net_state->rx_pkts)/net_state->past_ms;
+
+       net_state->tx_pkts = net_state->statistics.tx_pkts;
+       net_state->rx_pkts = net_state->statistics.rx_pkts;
+
+       if(tx_rate > RATE_UPPER_THRESHOLD && net_state->tx_notify == 1){
+           V3_Print("Virtio NIC: Switch TX to VMM driven mode\n");
+           disable_cb(&(net_state->tx_vq));
+           net_state->tx_notify = 0;
+       }
+
+       if(tx_rate < RATE_LOWER_THRESHOLD && net_state->tx_notify == 0){
+           V3_Print("Virtio NIC: Switch TX to Guest  driven mode\n");
+           enable_cb(&(net_state->tx_vq));
+           net_state->tx_notify = 1;
+       }
+
+       if(rx_rate > RATE_UPPER_THRESHOLD && net_state->rx_notify == 1){
+           PrintDebug("Virtio NIC: Switch RX to VMM None notify mode\n");
+           net_state->rx_notify = 0;
+       }
+
+       if(rx_rate < RATE_LOWER_THRESHOLD && net_state->rx_notify == 0){
+           PrintDebug("Virtio NIC: Switch RX to VMM notify mode\n");
+           net_state->rx_notify = 1;
+       }
+
+       net_state->past_ms = 0;
+    }
+}
+
+
+static struct v3_timer_ops timer_ops = {
+    .update_timer = virtio_nic_timer,
+};
+
+
 static int connect_fn(struct v3_vm_info * info, 
                      void * frontend_data, 
                      struct v3_dev_net_ops * ops, 
@@ -781,13 +799,15 @@ static int connect_fn(struct v3_vm_info * info,
     net_state->net_ops = ops;
     net_state->backend_data = private_data;
     net_state->virtio_dev = virtio;
-       
+    net_state->tx_notify = 1;
+    net_state->rx_notify = 1;
+
+    net_state->timer = v3_add_timer(&(info->cores[0]),&timer_ops,net_state);
 
     ops->recv = virtio_rx;
     ops->poll = virtio_nic_poll;
-    ops->start_tx = virtio_start_tx;
-    ops->stop_tx = virtio_stop_tx;
     ops->frontend_data = net_state;
+    memcpy(ops->fnt_mac, virtio->mac, ETH_ALEN);
 
     return 0;
 }
@@ -796,8 +816,9 @@ static int virtio_init(struct v3_vm_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 * dev_id = v3_cfg_val(cfg, "ID");
-
-    PrintDebug("Virtio NIC: Initializing VIRTIO Network device: %s\n", dev_id);
+    char macstr[128];
+    char * str = v3_cfg_val(cfg, "mac");
+    memcpy(macstr, str, strlen(str));
 
     if (pci_bus == NULL) {
        PrintError("Virtio NIC: VirtIO devices require a PCI Bus");
@@ -811,6 +832,19 @@ static int virtio_init(struct v3_vm_info * vm, v3_cfg_tree_t * cfg) {
     virtio_state->pci_bus = pci_bus;
     virtio_state->vm = vm;
 
+    if (macstr != NULL && !str2mac(macstr, virtio_state->mac)) {
+       PrintDebug("Virtio NIC: Mac specified %s\n", macstr);
+       PrintDebug("MAC: %x:%x:%x:%x:%x:%x\n", virtio_state->mac[0],
+                               virtio_state->mac[1],
+                               virtio_state->mac[2],
+                               virtio_state->mac[3],
+                               virtio_state->mac[4],
+                               virtio_state->mac[5]);
+    }else {
+       PrintDebug("Virtio NIC: MAC not specified\n");
+       random_ethaddr(virtio_state->mac);
+    }
+
     struct vm_device * dev = v3_add_device(vm, dev_id, &dev_ops, virtio_state);
 
     if (dev == NULL) {