Virtio support multiple MAC addresses.

Signed-off-by: Changchun Ouyang <changchun.ouyang at intel.com>
Signed-off-by: Stephen Hemminger <stephen at networkplumber.org>
---
 lib/librte_pmd_virtio/virtio_ethdev.c | 94 ++++++++++++++++++++++++++++++++++-
 lib/librte_pmd_virtio/virtio_ethdev.h |  3 +-
 lib/librte_pmd_virtio/virtqueue.h     | 34 ++++++++++++-
 3 files changed, 127 insertions(+), 4 deletions(-)

diff --git a/lib/librte_pmd_virtio/virtio_ethdev.c 
b/lib/librte_pmd_virtio/virtio_ethdev.c
index ec5a51e..e469ac2 100644
--- a/lib/librte_pmd_virtio/virtio_ethdev.c
+++ b/lib/librte_pmd_virtio/virtio_ethdev.c
@@ -86,6 +86,10 @@ static void virtio_dev_stats_reset(struct rte_eth_dev *dev);
 static void virtio_dev_free_mbufs(struct rte_eth_dev *dev);
 static int virtio_vlan_filter_set(struct rte_eth_dev *dev,
                                uint16_t vlan_id, int on);
+static void virtio_mac_addr_add(struct rte_eth_dev *dev,
+                               struct ether_addr *mac_addr,
+                               uint32_t index, uint32_t vmdq __rte_unused);
+static void virtio_mac_addr_remove(struct rte_eth_dev *dev, uint32_t index);

 static int virtio_dev_queue_stats_mapping_set(
        __rte_unused struct rte_eth_dev *eth_dev,
@@ -503,8 +507,6 @@ static struct eth_dev_ops virtio_eth_dev_ops = {
        .stats_get               = virtio_dev_stats_get,
        .stats_reset             = virtio_dev_stats_reset,
        .link_update             = virtio_dev_link_update,
-       .mac_addr_add            = NULL,
-       .mac_addr_remove         = NULL,
        .rx_queue_setup          = virtio_dev_rx_queue_setup,
        /* meaningfull only to multiple queue */
        .rx_queue_release        = virtio_dev_rx_queue_release,
@@ -514,6 +516,8 @@ static struct eth_dev_ops virtio_eth_dev_ops = {
        /* collect stats per queue */
        .queue_stats_mapping_set = virtio_dev_queue_stats_mapping_set,
        .vlan_filter_set         = virtio_vlan_filter_set,
+       .mac_addr_add            = virtio_mac_addr_add,
+       .mac_addr_remove         = virtio_mac_addr_remove,
 };

 static inline int
@@ -644,6 +648,92 @@ virtio_get_hwaddr(struct virtio_hw *hw)
 }

 static int
+virtio_mac_table_set(struct virtio_hw *hw,
+                    const struct virtio_net_ctrl_mac *uc,
+                    const struct virtio_net_ctrl_mac *mc)
+{
+       struct virtio_pmd_ctrl ctrl;
+       int err, len[2];
+
+       ctrl.hdr.class = VIRTIO_NET_CTRL_MAC;
+       ctrl.hdr.cmd = VIRTIO_NET_CTRL_MAC_TABLE_SET;
+
+       len[0] = uc->entries * ETHER_ADDR_LEN + sizeof(uc->entries);
+       memcpy(ctrl.data, uc, len[0]);
+
+       len[1] = mc->entries * ETHER_ADDR_LEN + sizeof(mc->entries);
+       memcpy(ctrl.data + len[0], mc, len[1]);
+
+       err = virtio_send_command(hw->cvq, &ctrl, len, 2);
+       if (err != 0)
+               PMD_DRV_LOG(NOTICE, "mac table set failed: %d", err);
+
+       return err;
+}
+
+static void
+virtio_mac_addr_add(struct rte_eth_dev *dev, struct ether_addr *mac_addr,
+                   uint32_t index, uint32_t vmdq __rte_unused)
+{
+       struct virtio_hw *hw = dev->data->dev_private;
+       const struct ether_addr *addrs = dev->data->mac_addrs;
+       unsigned int i;
+       struct virtio_net_ctrl_mac *uc, *mc;
+
+       if (index >= VIRTIO_MAX_MAC_ADDRS) {
+               PMD_DRV_LOG(ERR, "mac address index %u out of range", index);
+               return;
+       }
+
+       uc = alloca(VIRTIO_MAX_MAC_ADDRS * ETHER_ADDR_LEN + 
sizeof(uc->entries));
+       uc->entries = 0;
+       mc = alloca(VIRTIO_MAX_MAC_ADDRS * ETHER_ADDR_LEN + 
sizeof(mc->entries));
+       mc->entries = 0;
+
+       for (i = 0; i < VIRTIO_MAX_MAC_ADDRS; i++) {
+               const struct ether_addr *addr
+                       = (i == index) ? mac_addr : addrs + i;
+               struct virtio_net_ctrl_mac *tbl
+                       = is_multicast_ether_addr(addr) ? mc : uc;
+
+               memcpy(&tbl->macs[tbl->entries++], addr, ETHER_ADDR_LEN);
+       }
+
+       virtio_mac_table_set(hw, uc, mc);
+}
+
+static void
+virtio_mac_addr_remove(struct rte_eth_dev *dev, uint32_t index)
+{
+       struct virtio_hw *hw = dev->data->dev_private;
+       struct ether_addr *addrs = dev->data->mac_addrs;
+       struct virtio_net_ctrl_mac *uc, *mc;
+       unsigned int i;
+
+       if (index >= VIRTIO_MAX_MAC_ADDRS) {
+               PMD_DRV_LOG(ERR, "mac address index %u out of range", index);
+               return;
+       }
+
+       uc = alloca(VIRTIO_MAX_MAC_ADDRS * ETHER_ADDR_LEN + 
sizeof(uc->entries));
+       uc->entries = 0;
+       mc = alloca(VIRTIO_MAX_MAC_ADDRS * ETHER_ADDR_LEN + 
sizeof(mc->entries));
+       mc->entries = 0;
+
+       for (i = 0; i < VIRTIO_MAX_MAC_ADDRS; i++) {
+               struct virtio_net_ctrl_mac *tbl;
+
+               if (i == index || is_zero_ether_addr(addrs + i))
+                       continue;
+
+               tbl = is_multicast_ether_addr(addrs + i) ? mc : uc;
+               memcpy(&tbl->macs[tbl->entries++], addrs + i, ETHER_ADDR_LEN);
+       }
+
+       virtio_mac_table_set(hw, uc, mc);
+}
+
+static int
 virtio_vlan_filter_set(struct rte_eth_dev *dev, uint16_t vlan_id, int on)
 {
        struct virtio_hw *hw = dev->data->dev_private;
diff --git a/lib/librte_pmd_virtio/virtio_ethdev.h 
b/lib/librte_pmd_virtio/virtio_ethdev.h
index 55c9749..74ac7e0 100644
--- a/lib/librte_pmd_virtio/virtio_ethdev.h
+++ b/lib/librte_pmd_virtio/virtio_ethdev.h
@@ -51,7 +51,7 @@

 #define VIRTIO_MAX_RX_QUEUES 128
 #define VIRTIO_MAX_TX_QUEUES 128
-#define VIRTIO_MAX_MAC_ADDRS 1
+#define VIRTIO_MAX_MAC_ADDRS 64
 #define VIRTIO_MIN_RX_BUFSIZE 64
 #define VIRTIO_MAX_RX_PKTLEN  9728

@@ -60,6 +60,7 @@
        (VIRTIO_NET_F_MAC       | \
        VIRTIO_NET_F_STATUS     | \
        VIRTIO_NET_F_MQ         | \
+       VIRTIO_NET_F_CTRL_MAC_ADDR | \
        VIRTIO_NET_F_CTRL_VQ    | \
        VIRTIO_NET_F_CTRL_RX    | \
        VIRTIO_NET_F_CTRL_VLAN  | \
diff --git a/lib/librte_pmd_virtio/virtqueue.h 
b/lib/librte_pmd_virtio/virtqueue.h
index 5b8a255..4cb82f9 100644
--- a/lib/librte_pmd_virtio/virtqueue.h
+++ b/lib/librte_pmd_virtio/virtqueue.h
@@ -99,6 +99,34 @@ enum { VTNET_RQ = 0, VTNET_TQ = 1, VTNET_CQ = 2 };
 #define VIRTIO_NET_CTRL_RX_NOBCAST      5

 /**
+ * Control the MAC
+ *
+ * The MAC filter table is managed by the hypervisor, the guest should
+ * assume the size is infinite.  Filtering should be considered
+ * non-perfect, ie. based on hypervisor resources, the guest may
+ * received packets from sources not specified in the filter list.
+ *
+ * In addition to the class/cmd header, the TABLE_SET command requires
+ * two out scatterlists.  Each contains a 4 byte count of entries followed
+ * by a concatenated byte stream of the ETH_ALEN MAC addresses.  The
+ * first sg list contains unicast addresses, the second is for multicast.
+ * This functionality is present if the VIRTIO_NET_F_CTRL_RX feature
+ * is available.
+ *
+ * The ADDR_SET command requests one out scatterlist, it contains a
+ * 6 bytes MAC address. This functionality is present if the
+ * VIRTIO_NET_F_CTRL_MAC_ADDR feature is available.
+ */
+struct virtio_net_ctrl_mac {
+       uint32_t entries;
+       uint8_t macs[][ETHER_ADDR_LEN];
+} __attribute__((packed));
+
+#define VIRTIO_NET_CTRL_MAC    1
+ #define VIRTIO_NET_CTRL_MAC_TABLE_SET        0
+ #define VIRTIO_NET_CTRL_MAC_ADDR_SET         1
+
+/**
  * Control VLAN filtering
  *
  * The VLAN filter table is controlled via a simple ADD/DEL interface.
@@ -121,7 +149,7 @@ typedef uint8_t virtio_net_ctrl_ack;
 #define VIRTIO_NET_OK     0
 #define VIRTIO_NET_ERR    1

-#define VIRTIO_MAX_CTRL_DATA 128
+#define VIRTIO_MAX_CTRL_DATA 2048

 struct virtio_pmd_ctrl {
        struct virtio_net_ctrl_hdr hdr;
@@ -180,6 +208,10 @@ struct virtqueue {
 #define VIRTIO_NET_CTRL_MQ_VQ_PAIRS_MIN        1
 #define VIRTIO_NET_CTRL_MQ_VQ_PAIRS_MAX        0x8000
 #endif
+#ifndef VIRTIO_NET_F_CTRL_MAC_ADDR
+#define VIRTIO_NET_F_CTRL_MAC_ADDR 0x800000
+#define VIRTIO_NET_CTRL_MAC_ADDR_SET         1
+#endif

 /**
  * This is the first element of the scatter-gather list.  If you don't
-- 
1.8.4.2

Reply via email to