virtio-net: MAC filter optimization
authorAlex Williamson <alex.williamson@hp.com>
Fri, 5 Jun 2009 20:47:13 +0000 (14:47 -0600)
committerMark McLoughlin <markmc@redhat.com>
Tue, 9 Jun 2009 10:38:50 +0000 (11:38 +0100)
The MAC filter table is received from the guest as two separate
buffers, one with unicast entries, the other with multicast
entries.  If we track the index dividing the two sets, we can
avoid searching the part of the table with the wrong type of
entries.

We could store this index as part of the save image, but its
trivially easy to discover it on load.

Signed-off-by: Alex Williamson <alex.williamson@hp.com>
Signed-off-by: Mark McLoughlin <markmc@redhat.com>
hw/virtio-net.c

index 8c38454..7863d5d 100644 (file)
@@ -37,6 +37,7 @@ typedef struct VirtIONet
     uint8_t allmulti;
     struct {
         int in_use;
+        int first_multi;
         uint8_t multi_overflow;
         uint8_t uni_overflow;
         uint8_t *macs;
@@ -100,6 +101,7 @@ static void virtio_net_reset(VirtIODevice *vdev)
 
     /* Flush any MAC and VLAN filter table state */
     n->mac_table.in_use = 0;
+    n->mac_table.first_multi = 0;
     n->mac_table.multi_overflow = 0;
     n->mac_table.uni_overflow = 0;
     memset(n->mac_table.macs, 0, MAC_TABLE_ENTRIES * ETH_ALEN);
@@ -172,6 +174,7 @@ static int virtio_net_handle_mac(VirtIONet *n, uint8_t cmd,
         return VIRTIO_NET_ERR;
 
     n->mac_table.in_use = 0;
+    n->mac_table.first_multi = 0;
     n->mac_table.uni_overflow = 0;
     n->mac_table.multi_overflow = 0;
     memset(n->mac_table.macs, 0, MAC_TABLE_ENTRIES * ETH_ALEN);
@@ -190,6 +193,8 @@ static int virtio_net_handle_mac(VirtIONet *n, uint8_t cmd,
         n->mac_table.uni_overflow = 1;
     }
 
+    n->mac_table.first_multi = n->mac_table.in_use;
+
     mac_data.entries = ldl_le_p(elem->out_sg[2].iov_base);
 
     if (sizeof(mac_data.entries) +
@@ -359,17 +364,24 @@ static int receive_filter(VirtIONet *n, const uint8_t *buf, int size)
         } else if (n->allmulti || n->mac_table.multi_overflow) {
             return 1;
         }
+
+        for (i = n->mac_table.first_multi; i < n->mac_table.in_use; i++) {
+            if (!memcmp(ptr, &n->mac_table.macs[i * ETH_ALEN], ETH_ALEN)) {
+                return 1;
+            }
+        }
     } else { // unicast
         if (n->mac_table.uni_overflow) {
             return 1;
         } else if (!memcmp(ptr, n->mac, ETH_ALEN)) {
             return 1;
         }
-    }
 
-    for (i = 0; i < n->mac_table.in_use; i++) {
-        if (!memcmp(ptr, &n->mac_table.macs[i * ETH_ALEN], ETH_ALEN))
-            return 1;
+        for (i = 0; i < n->mac_table.first_multi; i++) {
+            if (!memcmp(ptr, &n->mac_table.macs[i * ETH_ALEN], ETH_ALEN)) {
+                return 1;
+            }
+        }
     }
 
     return 0;
@@ -547,6 +559,7 @@ static void virtio_net_save(QEMUFile *f, void *opaque)
 static int virtio_net_load(QEMUFile *f, void *opaque, int version_id)
 {
     VirtIONet *n = opaque;
+    int i;
 
     if (version_id < 2 || version_id > VIRTIO_NET_VM_VERSION)
         return -EINVAL;
@@ -597,6 +610,14 @@ static int virtio_net_load(QEMUFile *f, void *opaque, int version_id)
         n->mac_table.uni_overflow = qemu_get_byte(f);
     }
 
+    /* Find the first multicast entry in the saved MAC filter */
+    for (i = 0; i < n->mac_table.in_use; i++) {
+        if (n->mac_table.macs[i * ETH_ALEN] & 1) {
+            break;
+        }
+    }
+    n->mac_table.first_multi = i;
+
     if (n->tx_timer_active) {
         qemu_mod_timer(n->tx_timer,
                        qemu_get_clock(vm_clock) + TX_TIMER_INTERVAL);