diff mbox

[4/4] qemu:e1000: Add support for qemu_vlan_rxfilter

Message ID 20090210212902.9760.6747.stgit@kvm.aw (mailing list archive)
State Accepted, archived
Headers show

Commit Message

Alex Williamson Feb. 10, 2009, 9:29 p.m. UTC
Make use of qemu_vlan_rxfilter so that we can filter at a lower
level.  We implement callbacks for devices being added and removed
so that we can fall back to our own filtering or make another attempt
to push filtering off to someone else.

Signed-off-by: Alex Williamson <alex.williamson@hp.com>
---

 hw/e1000.c |   60 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 1 files changed, 60 insertions(+), 0 deletions(-)


--
To unsubscribe from this list: send the line "unsubscribe kvm" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

Comments

Alex Williamson Feb. 11, 2009, 3:11 p.m. UTC | #1
On Tue, 2009-02-10 at 14:29 -0700, Alex Williamson wrote:
> Make use of qemu_vlan_rxfilter so that we can filter at a lower
> level.  We implement callbacks for devices being added and removed
> so that we can fall back to our own filtering or make another attempt
> to push filtering off to someone else.

Hmm, I've thought of a bug in this patch, I'm not accounting for the
multicast hash e1000 can use for imperfect filtering.  I'll need to
rework this one, maybe skip the tap filter if the hash is in use.  This
is independent of the other patches though.  Thanks,

Alex


--
To unsubscribe from this list: send the line "unsubscribe kvm" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
diff mbox

Patch

diff --git a/hw/e1000.c b/hw/e1000.c
index 6f841d6..7855a91 100644
--- a/hw/e1000.c
+++ b/hw/e1000.c
@@ -85,6 +85,7 @@  typedef struct E1000State_st {
     uint32_t rxbuf_size;
     uint32_t rxbuf_min_shift;
     int check_rxov;
+    int vlan_rxfilter;
     struct e1000_tx {
         unsigned char header[256];
         unsigned char vlan_header[4];
@@ -143,6 +144,53 @@  static const char phy_regcap[0x20] = {
     [PHY_ID2] = PHY_R,		[M88E1000_PHY_SPEC_STATUS] = PHY_R
 };
 
+static void e1000_set_vlan_rxfilter(E1000State *s)
+{
+    static const uint8_t bcast[] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff};
+    uint32_t rctl = s->mac_reg[RCTL], ra[2], *rp;
+    uint8_t *buf;
+    int i, flags = 0;
+
+    if (rctl & E1000_RCTL_UPE)
+        flags |= QEMU_NET_PROMISC;
+    if (rctl & E1000_RCTL_MPE)
+        flags |= QEMU_NET_ALLMULTI;
+
+    /* Allocate for maximum size, 16 + bcast */
+    buf = qemu_mallocz(17 * 6);
+
+    for (i = 0, rp = s->mac_reg + RA; rp < s->mac_reg + RA + 32; rp += 2) {
+        if (!(rp[1] & E1000_RAH_AV))
+            continue;
+        ra[0] = cpu_to_le32(rp[0]);
+        ra[1] = cpu_to_le32(rp[1]);
+        memcpy(&buf[i * 6], (uint8_t *)ra, 6);
+        i++;
+    }
+
+    if (rctl & E1000_RCTL_BAM) {
+        memcpy(&buf[i * 6], bcast, 6);
+        i++;
+    }
+
+    s->vlan_rxfilter = qemu_vlan_rxfilter(s->vc, flags, i, buf);
+    qemu_free(buf);
+}
+
+static void e1000_vlan_client_added(void *opaque)
+{
+    E1000State *s = opaque;
+
+    s->vlan_rxfilter = 0;
+}
+
+static void e1000_vlan_client_removed(void *opaque)
+{
+    E1000State *s = opaque;
+
+    e1000_set_vlan_rxfilter(s);
+}
+
 static void
 ioport_map(PCIDevice *pci_dev, int region_num, uint32_t addr,
            uint32_t size, int type)
@@ -198,6 +246,7 @@  set_rx_control(E1000State *s, int index, uint32_t val)
     s->rxbuf_min_shift = ((val / E1000_RCTL_RDMTS_QUAT) & 3) + 1;
     DBGOUT(RX, "RCTL: %d, mac_reg[RCTL] = 0x%x\n", s->mac_reg[RDT],
            s->mac_reg[RCTL]);
+    e1000_set_vlan_rxfilter(s);
 }
 
 static void
@@ -532,6 +581,9 @@  receive_filter(E1000State *s, const uint8_t *buf, int size)
             return 0;
     }
 
+    if (s->vlan_rxfilter)
+        return 1;
+
     if (rctl & E1000_RCTL_UPE)			// promiscuous
         return 1;
 
@@ -712,6 +764,9 @@  static void
 mac_writereg(E1000State *s, int index, uint32_t val)
 {
     s->mac_reg[index] = val;
+
+    if (index >= E1000_RA && index < E1000_RA + 32)
+        e1000_set_vlan_rxfilter(s);
 }
 
 static void
@@ -964,6 +1019,9 @@  nic_load(QEMUFile *f, void *opaque, int version_id)
         for (j = 0; j < mac_regarraystosave[i].size; j++)
             qemu_get_be32s(f,
                            s->mac_reg + mac_regarraystosave[i].array0 + j);
+
+    e1000_set_vlan_rxfilter(s);
+
     return 0;
 }
 
@@ -1088,6 +1146,8 @@  pci_e1000_init(PCIBus *bus, NICInfo *nd, int devfn)
     d->vc = qemu_new_vlan_client(nd->vlan, nd->model, nd->name,
                                  e1000_receive, e1000_can_receive, d);
     d->vc->link_status_changed = e1000_set_link_status;
+    d->vc->vlan_client_added = e1000_vlan_client_added;
+    d->vc->vlan_client_removed = e1000_vlan_client_removed;
 
     qemu_format_nic_info_str(d->vc, d->nd->macaddr);