diff mbox series

[ovs-dev,RFC] Add support for Virtio Port Mirroring

Message ID 1595596858-78846-2-git-send-email-emma.finn@intel.com
State New
Headers show
Series [ovs-dev,RFC] Add support for Virtio Port Mirroring | expand

Commit Message

Finn, Emma July 24, 2020, 1:20 p.m. UTC
From: Liang-min Wang <liang-min.wang@intel.com>

Extends the existing mirror configuration parameters

The following parameters are added:
 - mirror-offload: to turn on/off mirror offloading.
 - output-port-name: specify a port, using name string, that is on a different
   bridge
 - bond-port-other: to choose default active interface or use standby
   interface when the output port is a bond port.
 - output-src-vlan: output port vlan for each select-src-port.
 - output-dst-vlan: output port vlan for each select-dst-port.
 - flow-src-mac: use src mac address of each select-dst-port for the header
   scan.
 - flow-dst-mac: use dst mac address of each select-src-port for the header
   scan.

ovs-vsctl test change because new mirroring parameters
are introduced in this patch

Create a defer procedure call thread to handle all mirror
offload requests. This is a light-weight thread which remains in sleep-state
when there is no new request. This is created between ovs-vsctl and mirror
offloading back end

Add new command option to enable per-packet Tx VLAN
insertion on DPDK PMD drivers. This offloading feature requires VIRTIO
back end to support rx-burst/tx-burst callback registration. Because OVS has
not adopted the DPDK VHOST PMD design, to enable port mirror offloading,
admin needs to use DPDK VDEV ports for VIRTIO back end which is done
through OVS-DPDK port command options
"type=dpdk options:dpdk-devargs=net_vhost1,iface=/tmp/vnic1,queues=2 ..."

Implementing DPDK tx-burst (VIRTIO ingress traffic
 mirror) and rx-burst (VIRTIO egress traffic mirror) callbacks. Each callback
 functions implement the following tasks:
 1. Enable per-packet VLAN insertion
   - for port mirroring, all packets are enabled per-packet VLAN insertion.
   - for flow mirroring, only packet header matches the required mac address
     are enabled.
 2. Sending the packets to the specified transport port (output-port in
    mirror offload configuration)
   - for port mirroring, all packets are sent to the transport port.
   - for flow mirroring, only matched packets are sent.
 3. Restore each packet attributes (remove DPDK per-packet offload flag)

Signed-off-by: Liang-min Wang <liang-min.wang@intel.com>
Tested-by: Timothy Miskell <Timothy.Miskell@intel.com>
Suggested-by: Munish Mehan <mm6021@att.com>
---
 lib/netdev-dpdk.c          | 108 ++++++++++--
 lib/netdev-offload-dpdk.c  | 427 +++++++++++++++++++++++++++++++++++++++++++++
 lib/netdev-offload.h       |  28 +++
 lib/netdev-provider.h      |  14 ++
 lib/netdev.c               | 235 +++++++++++++++++++++++++
 lib/netdev.h               |  19 ++
 tests/ovs-vsctl.at         |   4 +
 vswitchd/bridge.c          | 208 +++++++++++++++++++++-
 vswitchd/vswitch.ovsschema |  26 ++-
 vswitchd/vswitch.xml       |  60 ++++++-
 10 files changed, 1113 insertions(+), 16 deletions(-)
diff mbox series

Patch

diff --git a/lib/netdev-dpdk.c b/lib/netdev-dpdk.c
index 44ebf96..81fcbc1 100644
--- a/lib/netdev-dpdk.c
+++ b/lib/netdev-dpdk.c
@@ -405,6 +405,7 @@  enum dpdk_hw_ol_features {
     NETDEV_RX_HW_SCATTER = 1 << 2,
     NETDEV_TX_TSO_OFFLOAD = 1 << 3,
     NETDEV_TX_SCTP_CHECKSUM_OFFLOAD = 1 << 4,
+    NETDEV_TX_VLAN_INSERT = 1 << 5,
 };
 
 /*
@@ -488,7 +489,10 @@  struct netdev_dpdk {
         struct netdev_dpdk_sw_stats *sw_stats;
         /* Protects stats */
         rte_spinlock_t stats_lock;
-        /* 36 pad bytes here. */
+        rte_spinlock_t tx_thd_lock;
+       uint16_t (* tx_burst)(uint16_t, uint16_t, struct rte_mbuf **,
+           uint16_t, rte_spinlock_t *);
+        /* 24 pad bytes here. */
     );
 
     PADDED_MEMBERS(CACHE_LINE_SIZE,
@@ -954,6 +958,28 @@  dpdk_watchdog(void *dummy OVS_UNUSED)
     return NULL;
 }
 
+static inline uint16_t
+dpdk_eth_dev_tx_burst_ts(uint16_t port_id, uint16_t queue_id,
+   struct rte_mbuf **tx_pkts, uint16_t nb_pkts, rte_spinlock_t *lock)
+{
+   uint16_t ret;
+
+   rte_spinlock_lock(lock);
+   ret =rte_eth_tx_burst(port_id, queue_id, tx_pkts, nb_pkts);
+   rte_spinlock_unlock(lock);
+
+   return ret;
+}
+
+static inline uint16_t
+dpdk_eth_dev_tx_burst_native(uint16_t port_id, uint16_t queue_id,
+   struct rte_mbuf **tx_pkts, uint16_t nb_pkts,
+   rte_spinlock_t *lock OVS_UNUSED)
+{
+
+   return rte_eth_tx_burst(port_id, queue_id, tx_pkts, nb_pkts);
+}
+
 static int
 dpdk_eth_dev_port_config(struct netdev_dpdk *dev, int n_rxq, int n_txq)
 {
@@ -986,14 +1012,21 @@  dpdk_eth_dev_port_config(struct netdev_dpdk *dev, int n_rxq, int n_txq)
         conf.rxmode.offloads |= DEV_RX_OFFLOAD_KEEP_CRC;
     }
 
-    if (dev->hw_ol_features & NETDEV_TX_TSO_OFFLOAD) {
-        conf.txmode.offloads |= DPDK_TX_TSO_OFFLOAD_FLAGS;
-        if (dev->hw_ol_features & NETDEV_TX_SCTP_CHECKSUM_OFFLOAD) {
-            conf.txmode.offloads |= DEV_TX_OFFLOAD_SCTP_CKSUM;
-        }
-    }
+   if (dev->hw_ol_features & NETDEV_TX_TSO_OFFLOAD) {
+       conf.txmode.offloads |= DPDK_TX_TSO_OFFLOAD_FLAGS;
+       if (dev->hw_ol_features & NETDEV_TX_SCTP_CHECKSUM_OFFLOAD) {
+           conf.txmode.offloads |= DEV_TX_OFFLOAD_SCTP_CKSUM;
+       }
+   }
+
+   dev->tx_burst = dpdk_eth_dev_tx_burst_native;
+   if (dev->hw_ol_features & NETDEV_TX_VLAN_INSERT) {
+       conf.txmode.offloads |= DEV_TX_OFFLOAD_VLAN_INSERT;
+       rte_spinlock_init(&dev->tx_thd_lock);
+       dev->tx_burst = dpdk_eth_dev_tx_burst_ts;
+   }
 
-    /* Limit configured rss hash functions to only those supported
+   /* Limit configured rss hash functions to only those supported
      * by the eth device. */
     conf.rx_adv_conf.rss_conf.rss_hf &= info.flow_type_rss_offloads;
 
@@ -1912,6 +1945,10 @@  netdev_dpdk_set_config(struct netdev *netdev, const struct smap *args,
                             NIC_PORT_DEFAULT_TXQ_SIZE,
                             &dev->requested_txq_size);
 
+    if (smap_get_bool(args, "tx_vlan_insert", false)) {
+        dev->hw_ol_features |= NETDEV_TX_VLAN_INSERT;
+    }
+
     new_devargs = smap_get(args, "dpdk-devargs");
 
     if (dev->devargs && new_devargs && strcmp(new_devargs, dev->devargs)) {
@@ -2215,8 +2252,8 @@  netdev_dpdk_eth_tx_burst(struct netdev_dpdk *dev, int qid,
     while (nb_tx != nb_tx_prep) {
         uint32_t ret;
 
-        ret = rte_eth_tx_burst(dev->port_id, qid, pkts + nb_tx,
-                               nb_tx_prep - nb_tx);
+        ret = dev->tx_burst(dev->port_id, qid, pkts + nb_tx,
+                               nb_tx_prep - nb_tx, &dev->tx_thd_lock);
         if (!ret) {
             break;
         }
@@ -5228,6 +5265,54 @@  netdev_dpdk_rte_flow_query_count(struct netdev *netdev,
     return ret;
 }
 
+static int
+netdev_dpdk_mirror_offload(struct netdev *src, struct eth_addr *flow_addr,
+                           struct netdev *dst, uint16_t vlan_id,
+                           bool add_mirror, bool tx_cb) {
+    struct netdev_dpdk *src_dev = netdev_dpdk_cast(src);
+    struct netdev_dpdk *dst_dev = netdev_dpdk_cast(dst);
+   int status = 0;
+
+   if (add_mirror) {
+       int i;
+       struct mirror_param data;
+       uint64_t mac_addr = 0;
+
+       memset(&data, 0, sizeof(struct mirror_param));
+       data.target_addr = 0;
+       if (flow_addr) {
+           for (i = 0; i < 6; i++) {
+               mac_addr <<= 8;
+               mac_addr |= flow_addr->ea[6 - i - 1];
+           }
+           data.target_addr = mac_addr;
+       }
+
+       data.dst_port_id = dst_dev->port_id;
+       data.dst_vlan_id = vlan_id;
+       data.n_src_queue = tx_cb?src->n_txq:src->n_rxq;
+       data.n_dst_queue = dst->n_txq;
+       data.lock = &dst_dev->tx_thd_lock;
+       data.max_burst_size = NETDEV_MAX_BURST;
+
+        VLOG_INFO("register %s mirror-offload with src-port:%d (%s) and "
+            "output-port:%d (%s) vlan-id=%d flow-mac="
+            "0x%" PRId64 "\n",
+            tx_cb?"ingress":"egress", src_dev->port_id,
+            src->name, dst_dev->port_id, dst->name, vlan_id,
+           (uint64_t)__builtin_bswap64(data.target_addr));
+
+        status = netdev_register_mirror(src_dev->port_id, &data, tx_cb);
+    } else {
+        VLOG_INFO("unregister %s mirror-offload with src-port:%d(%s)\n",
+            tx_cb?"ingress":"egress", src_dev->port_id,
+            src->name);
+        status = netdev_unregister_mirror(src_dev->port_id, tx_cb);
+    }
+
+   return status;
+}
+
 #define NETDEV_DPDK_CLASS_COMMON                            \
     .is_pmd = true,                                         \
     .alloc = netdev_dpdk_alloc,                             \
@@ -5277,6 +5362,7 @@  static const struct netdev_class dpdk_class = {
     .construct = netdev_dpdk_construct,
     .set_config = netdev_dpdk_set_config,
     .send = netdev_dpdk_eth_send,
+    .mirror_offload = netdev_dpdk_mirror_offload,
 };
 
 static const struct netdev_class dpdk_vhost_class = {
@@ -5285,6 +5371,7 @@  static const struct netdev_class dpdk_vhost_class = {
     .construct = netdev_dpdk_vhost_construct,
     .destruct = netdev_dpdk_vhost_destruct,
     .send = netdev_dpdk_vhost_send,
+    .mirror_offload = netdev_dpdk_mirror_offload,
     .get_carrier = netdev_dpdk_vhost_get_carrier,
     .get_stats = netdev_dpdk_vhost_get_stats,
     .get_custom_stats = netdev_dpdk_get_sw_custom_stats,
@@ -5301,6 +5388,7 @@  static const struct netdev_class dpdk_vhost_client_class = {
     .destruct = netdev_dpdk_vhost_destruct,
     .set_config = netdev_dpdk_vhost_client_set_config,
     .send = netdev_dpdk_vhost_send,
+    .mirror_offload = netdev_dpdk_mirror_offload,
     .get_carrier = netdev_dpdk_vhost_get_carrier,
     .get_stats = netdev_dpdk_vhost_get_stats,
     .get_custom_stats = netdev_dpdk_get_sw_custom_stats,
diff --git a/lib/netdev-offload-dpdk.c b/lib/netdev-offload-dpdk.c
index de6101e..1717a9e 100644
--- a/lib/netdev-offload-dpdk.c
+++ b/lib/netdev-offload-dpdk.c
@@ -29,6 +29,11 @@ 
 #include "packets.h"
 #include "uuid.h"
 
+#define MAC_ADDR_MAP           0x0000FFFFFFFFFFFFULL
+#define is_mac_addr_match(a,b) (((a^b)&MAC_ADDR_MAP) == 0)
+#define INIT_MIRROR_DB_SIZE    8
+#define INVALID_PORT_ID        0xFFFF
+
 VLOG_DEFINE_THIS_MODULE(netdev_offload_dpdk);
 static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(100, 5);
 
@@ -1581,3 +1586,425 @@  const struct netdev_flow_api netdev_offload_dpdk = {
     .init_flow_api = netdev_offload_dpdk_init_flow_api,
     .flow_get = netdev_offload_dpdk_flow_get,
 };
+
+/*
+ * The below API is for port/flow mirror offloading which uses a different DPDK
+ * interface as rte-flow.
+ */
+static int mirror_port_db_size = 0;
+static int mirror_port_used = 0;
+static struct mirror_offload_port *mirror_port_db = NULL;
+
+static struct mirror_offload_port*
+netdev_mirror_data_find(uint16_t port_id)
+{
+   int i;
+
+   if (mirror_port_db == NULL) {
+       return NULL;
+   }
+   for (i = 0; i < mirror_port_db_size; i++) {
+       if (port_id == mirror_port_db[i].port_id) {
+           return &mirror_port_db[i];
+       }
+   }
+   return NULL;
+}
+
+static void
+netdev_mirror_db_init(struct mirror_offload_port *db, int size)
+{
+   int i;
+
+   for (i = 0; i < size; i++) {
+       db[i].port_id = INVALID_PORT_ID;
+       memset(&db[i].rx, 0, sizeof(struct mirror_param));
+       memset(&db[i].tx, 0, sizeof(struct mirror_param));
+   }
+}
+
+/* Double the db size when it runs out of space */
+static int
+netdev_mirror_db_resize(void)
+{
+   int new_size = 2 * mirror_port_db_size;
+   struct mirror_offload_port *new_db = malloc(
+       sizeof(struct mirror_offload_port)*new_size);
+
+   if (new_db == NULL) {
+       VLOG_ERR("Out of memory !!!");
+       return -1;
+   }
+
+   memcpy(new_db, mirror_port_db, sizeof(struct mirror_offload_port)
+       *mirror_port_db_size);
+   netdev_mirror_db_init(&new_db[mirror_port_db_size], mirror_port_db_size);
+   mirror_port_db_size = new_size;
+   mirror_port_db = new_db;
+
+   return 0;
+}
+
+static void
+netdev_mirror_data_remove(uint16_t port_id, int tx) {
+   struct mirror_offload_port *target = netdev_mirror_data_find(port_id);
+
+   if (!target) {
+       VLOG_ERR("Attempt to remove unsaved port, %d, %s callback\n",
+       port_id, tx?"tx": "rx");
+   }
+
+   if (tx) {
+       memset(&target->tx, 0, sizeof(struct mirror_param));
+   } else {
+       memset(&target->rx, 0, sizeof(struct mirror_param));
+   }
+
+   if ((target->rx.mirror_cb == NULL) &&
+       (target->tx.mirror_cb == NULL)) {
+       target->port_id = INVALID_PORT_ID;
+       mirror_port_used --;
+       /* release port mirror db memory when there
+        * is no outstanding port mirror offloading
+        * configuration
+        */
+       if (mirror_port_used == 0) {
+           free(mirror_port_db);
+           mirror_port_db = NULL;
+           mirror_port_db_size = 0;
+       }
+   }
+}
+
+static struct mirror_offload_port*
+netdev_mirror_data_add(uint16_t port_id, int tx,
+                       struct mirror_param *new_param)
+{
+   struct mirror_offload_port *target = NULL;
+   int i;
+
+   if (!mirror_port_db) {
+       mirror_port_db_size = INIT_MIRROR_DB_SIZE;
+       mirror_port_db = xmalloc(sizeof(struct mirror_offload_port)*
+           mirror_port_db_size);
+       if (!mirror_port_db) {
+           VLOG_ERR("Out of memory!!!");
+           return target;
+       }
+       netdev_mirror_db_init(mirror_port_db, mirror_port_db_size);
+   }
+   target = netdev_mirror_data_find(port_id);
+   if (target) {
+       if (tx) {
+           if (target->tx.mirror_cb) {
+               VLOG_ERR("Attempt to add ingress mirror offloading"
+                   " on port, %d, while one is outstanding\n", port_id);
+               return target;
+           }
+
+           memcpy(&target->tx, new_param, sizeof(*new_param));
+
+       } else {
+           if (target->rx.mirror_cb) {
+               VLOG_ERR("Attempt to add egress mirror offloading"
+                   " on port, %d, while one is outstanding\n", port_id);
+               return target;
+           }
+
+           memcpy(&target->rx, new_param, sizeof(struct mirror_param));
+
+       }
+   } else {
+       struct mirror_param *param;
+       /* find an unused spot on db */
+       for (i = 0; i < mirror_port_db_size; i++) {
+           if (mirror_port_db[i].port_id == INVALID_PORT_ID) {
+               break;
+           }
+       }
+       if (i == mirror_port_db_size) {
+           if (netdev_mirror_db_resize()) {
+               return NULL;
+           }
+       }
+
+       param = tx ? &mirror_port_db[i].tx : &mirror_port_db[i].rx;
+       memcpy(param, new_param, sizeof(struct mirror_param));
+
+       target = &mirror_port_db[i];
+       target->port_id = port_id;
+       mirror_port_used ++;
+   }
+   return target;
+}
+
+static inline uint16_t
+netdev_rx_flow_mirror_offload_cb(uint16_t port_id OVS_UNUSED,
+   uint16_t qidx, struct rte_mbuf **pkts, uint16_t nb_pkts,
+   uint16_t maxi_pkts OVS_UNUSED, void *user_params)
+{
+   struct mirror_param *data = user_params;
+   uint16_t i, dst_qidx, match_count = 0;
+   uint16_t pkt_trans;
+   uint16_t dst_port_id = data->dst_port_id;
+   uint16_t dst_vlan_id = data->dst_vlan_id;
+   uint64_t target_addr = data->target_addr;
+   struct rte_mbuf **pkt_buf = &data->pkt_buf[qidx * data->max_burst_size];
+
+   if (nb_pkts == 0) {
+       return 0;
+   }
+
+   if (nb_pkts > data->max_burst_size) {
+       VLOG_ERR("Per-flow batch size, %d, exceeds maximum limit\n", nb_pkts);
+       return 0;
+   }
+
+   for (i = 0; i < nb_pkts; i++) {
+       uint64_t *dst_mac_addr =
+           rte_pktmbuf_mtod_offset(pkts[i], uint64_t *, 0);
+       if (is_mac_addr_match(target_addr, (*dst_mac_addr))) {
+           pkt_buf[match_count] = pkts[i];
+           pkt_buf[match_count]->ol_flags |= PKT_TX_VLAN_PKT;
+           pkt_buf[match_count]->vlan_tci = dst_vlan_id;
+           rte_mbuf_refcnt_update(pkt_buf[match_count], 1);
+           match_count ++;
+       }
+   }
+
+   dst_qidx = (data->n_dst_queue > qidx)?qidx:(data->n_dst_queue -1);
+
+   rte_spinlock_lock(data->lock);
+   pkt_trans = rte_eth_tx_burst(dst_port_id, dst_qidx, pkt_buf, match_count);
+   rte_spinlock_unlock(data->lock);
+
+   for (i = 0; i < match_count; i++) {
+       pkt_buf[i]->ol_flags &= ~PKT_TX_VLAN_PKT;
+   }
+
+   while (unlikely (pkt_trans < match_count)) {
+       rte_pktmbuf_free(pkt_buf[pkt_trans]);
+       pkt_trans++;
+   }
+
+   return nb_pkts;
+}
+
+static inline uint16_t
+netdev_tx_flow_mirror_offload_cb(uint16_t port_id OVS_UNUSED,
+   uint16_t qidx, struct rte_mbuf **pkts, uint16_t nb_pkts,
+   void *user_params)
+{
+   struct mirror_param *data = user_params;
+   uint16_t i, dst_qidx, match_count = 0;
+   uint16_t pkt_trans;
+   uint16_t dst_port_id = data->dst_port_id;
+   uint16_t dst_vlan_id = data->dst_vlan_id;
+   uint64_t target_addr = data->target_addr;
+   struct rte_mbuf **pkt_buf = &data->pkt_buf[qidx * data->max_burst_size];
+
+   if (nb_pkts == 0) {
+       return 0;
+   }
+
+   if (nb_pkts > data->max_burst_size) {
+       VLOG_ERR("Per-flow batch size, %d, exceeds maximum limit\n", nb_pkts);
+       return 0;
+   }
+
+   for (i = 0; i < nb_pkts; i++) {
+       uint64_t *src_mac_addr =
+           rte_pktmbuf_mtod_offset(pkts[i], uint64_t *, 6);
+       if (is_mac_addr_match(target_addr, (*src_mac_addr))) {
+           pkt_buf[match_count] = pkts[i];
+           pkt_buf[match_count]->ol_flags |= PKT_TX_VLAN_PKT;
+           pkt_buf[match_count]->vlan_tci = dst_vlan_id;
+           rte_mbuf_refcnt_update(pkt_buf[match_count], 1);
+           match_count++;
+       }
+   }
+
+   dst_qidx = (data->n_dst_queue > qidx)?qidx:(data->n_dst_queue -1);
+
+   rte_spinlock_lock(data->lock);
+   pkt_trans = rte_eth_tx_burst(dst_port_id, dst_qidx, pkt_buf, match_count);
+   rte_spinlock_unlock(data->lock);
+
+   for (i = 0; i < match_count; i++) {
+       pkt_buf[i]->ol_flags &= ~PKT_TX_VLAN_PKT;
+   }
+
+   while (unlikely (pkt_trans < match_count)) {
+       rte_pktmbuf_free(pkt_buf[pkt_trans]);
+       pkt_trans++;
+   }
+
+   return nb_pkts;
+}
+
+static inline uint16_t
+netdev_rx_port_mirror_offload_cb(uint16_t port_id OVS_UNUSED,
+   uint16_t qidx, struct rte_mbuf **pkts, uint16_t nb_pkts,
+   uint16_t max_pkts OVS_UNUSED, void *user_params)
+{
+   struct mirror_param *data = user_params;
+   uint16_t i, dst_qidx;
+   uint16_t pkt_trans;
+   uint16_t dst_port_id = data->dst_port_id;
+   uint16_t dst_vlan_id = data->dst_vlan_id;
+
+   if (nb_pkts == 0) {
+       return 0;
+   }
+
+   for (i = 0; i < nb_pkts; i++) {
+       pkts[i]->ol_flags |= PKT_TX_VLAN_PKT;
+       pkts[i]->vlan_tci = dst_vlan_id;
+       rte_mbuf_refcnt_update(pkts[i], 1);
+   }
+
+   dst_qidx = (data->n_dst_queue > qidx)?qidx:(data->n_dst_queue -1);
+
+   rte_spinlock_lock(data->lock);
+   pkt_trans = rte_eth_tx_burst(dst_port_id, dst_qidx, pkts, nb_pkts);
+   rte_spinlock_unlock(data->lock);
+
+   for (i = 0; i < nb_pkts; i++) {
+       pkts[i]->ol_flags &= ~PKT_TX_VLAN_PKT;
+   }
+
+   while (unlikely (pkt_trans < nb_pkts)) {
+       rte_pktmbuf_free(pkts[pkt_trans]);
+       pkt_trans++;
+   }
+
+   return nb_pkts;
+}
+
+static inline uint16_t
+netdev_tx_port_mirror_offload_cb(uint16_t port_id OVS_UNUSED,
+   uint16_t qidx, struct rte_mbuf **pkts, uint16_t nb_pkts,
+   void *user_params)
+{
+   struct mirror_param *data = user_params;
+   uint16_t i, dst_qidx;
+   uint16_t pkt_trans;
+   uint16_t dst_port_id = data->dst_port_id;
+   uint16_t dst_vlan_id = data->dst_vlan_id;
+
+   if (nb_pkts == 0) {
+       return 0;
+   }
+
+   for (i = 0; i < nb_pkts; i++) {
+       pkts[i]->ol_flags |= PKT_TX_VLAN_PKT;
+       pkts[i]->vlan_tci = dst_vlan_id;
+       rte_mbuf_refcnt_update(pkts[i], 1);
+   }
+
+   dst_qidx = (data->n_dst_queue > qidx)?qidx:(data->n_dst_queue -1);
+
+   rte_spinlock_lock(data->lock);
+   pkt_trans = rte_eth_tx_burst(dst_port_id, dst_qidx, pkts, nb_pkts);
+   rte_spinlock_unlock(data->lock);
+
+   for (i = 0; i < nb_pkts; i++) {
+       pkts[i]->ol_flags &= ~PKT_TX_VLAN_PKT;
+   }
+
+   while (unlikely (pkt_trans < nb_pkts)) {
+       rte_pktmbuf_free(pkts[pkt_trans]);
+       pkt_trans++;
+   }
+
+   return nb_pkts;
+}
+
+int
+netdev_register_mirror(uint16_t src_port, struct mirror_param *param,
+                       int tx_cb)
+{
+   int i;
+   struct mirror_offload_port *port_info;
+   struct mirror_param *data;
+
+   port_info = netdev_mirror_data_add(src_port, tx_cb, param);
+   if (!port_info) {
+       return -1;
+   }
+
+   data = tx_cb ? &port_info->tx : &port_info->rx;
+
+   data->pkt_buf = NULL;
+   if (data->target_addr) {
+       data->pkt_buf = xmalloc(sizeof(struct rte_mbuf *)*data->max_burst_size*
+           data->n_src_queue);
+       if (!data->pkt_buf) {
+           VLOG_ERR("Out of memory !!!");
+           return -1;
+       }
+   }
+
+   data->mirror_cb = xmalloc(sizeof(struct rte_eth_rxtx_callbac *) *
+                            data->n_src_queue);
+   if (!data->mirror_cb) {
+       VLOG_ERR("Out of memory !!!");
+       return -1;
+   }
+
+   if (!tx_cb) {
+       rte_rx_callback_fn fn = (data->target_addr)?
+                               netdev_rx_flow_mirror_offload_cb:
+                               netdev_rx_port_mirror_offload_cb;
+       for (i = 0;i < data->n_src_queue; i++) {
+           data->mirror_cb[i] = rte_eth_add_rx_callback(src_port,
+               i, fn, data);
+       }
+   } else {
+       rte_tx_callback_fn fn = (data->target_addr)?
+                               netdev_tx_flow_mirror_offload_cb:
+                               netdev_tx_port_mirror_offload_cb;
+       for (i = 0; i < data->n_src_queue; i++) {
+           data->mirror_cb[i] = rte_eth_add_tx_callback(src_port,
+               i, fn, data);
+       }
+   }
+
+   return 0;
+}
+
+int netdev_unregister_mirror(uint16_t src_port, int tx_cb)
+{
+   /* release both cb and pkt_buf */
+   int i;
+   struct mirror_offload_port *port_info;
+   struct mirror_param *data;
+
+   port_info = netdev_mirror_data_find(src_port);
+   if (port_info == NULL) {
+       VLOG_ERR("Source port %d is not on outstanding port mirror db\n",
+                src_port);
+       return -1;
+   }
+   data = tx_cb ? &port_info->tx : &port_info->rx;
+
+   for (i = 0; i < data->n_src_queue; i++) {
+       if (data->mirror_cb[i]) {
+           if (tx_cb) {
+               rte_eth_remove_tx_callback(src_port, i, data->mirror_cb[i]);
+           } else {
+               rte_eth_remove_rx_callback(src_port, i, data->mirror_cb[i]);
+           }
+       }
+       data->mirror_cb[i] = NULL;
+   }
+   free(data->mirror_cb);
+
+   if (data->pkt_buf) {
+       free(data->pkt_buf);
+       data->pkt_buf = NULL;
+   }
+
+   netdev_mirror_data_remove(src_port, tx_cb);
+   return 0;
+}
diff --git a/lib/netdev-offload.h b/lib/netdev-offload.h
index 4c0ed2a..75691a6 100644
--- a/lib/netdev-offload.h
+++ b/lib/netdev-offload.h
@@ -23,6 +23,10 @@ 
 #include "packets.h"
 #include "flow.h"
 
+#ifdef DPDK_NETDEV
+#include <rte_ethdev.h>
+#endif
+
 #ifdef  __cplusplus
 extern "C" {
 #endif
@@ -125,6 +129,30 @@  int netdev_ports_flow_get(const char *dpif_type, struct match *match,
                           struct dpif_flow_attrs *attrs,
                           struct ofpbuf *buf);
 
+#ifdef DPDK_NETDEV
+struct mirror_param {
+   uint16_t dst_port_id;
+   uint16_t dst_vlan_id;
+   uint64_t target_addr;
+   rte_spinlock_t *lock;
+   int n_src_queue;
+   int n_dst_queue;
+   struct rte_mbuf **pkt_buf;
+   const struct rte_eth_rxtx_callback **mirror_cb;
+   unsigned int max_burst_size;
+};
+
+struct mirror_offload_port {
+   uint16_t port_id;
+   struct mirror_param rx;
+   struct mirror_param tx;
+};
+
+int netdev_register_mirror(uint16_t src_port, struct mirror_param *data,
+                           int tx_cb);
+int netdev_unregister_mirror(uint16_t src_port, int tx_cb);
+#endif
+
 #ifdef  __cplusplus
 }
 #endif
diff --git a/lib/netdev-provider.h b/lib/netdev-provider.h
index 73dce2f..12bb3a6 100644
--- a/lib/netdev-provider.h
+++ b/lib/netdev-provider.h
@@ -834,6 +834,20 @@  struct netdev_class {
     /* Get a block_id from the netdev.
      * Returns the block_id or 0 if none exists for netdev. */
     uint32_t (*get_block_id)(struct netdev *);
+
+    /* Configure a mirror offload setting on a netdev.
+     * 'src': netdev traffic to be mirrored
+     * 'flow_addr': the destination mac address is of source traffic for
+     *  inspection.
+     * 'dst': netdev where mirror traffic is transmitted.
+     * 'add_mirror': true: configure a mirror traffic; false: remove mirror
+     * 'ingress': true: mirror 'src' netdev Rx traffic; false: mirror
+     *  'sr' netdev Tx traffic.
+     */
+    int (*mirror_offload)(struct netdev *src, struct eth_addr *flow_addr,
+                          struct netdev *dst, uint16_t vlan_id,
+                          bool add_mirror, bool ingress);
+
 };
 
 int netdev_register_provider(const struct netdev_class *);
diff --git a/lib/netdev.c b/lib/netdev.c
index 91e9195..71e2c61 100644
--- a/lib/netdev.c
+++ b/lib/netdev.c
@@ -2297,3 +2297,238 @@  netdev_free_custom_stats_counters(struct netdev_custom_stats *custom_stats)
         }
     }
 }
+
+struct netdev_mirror_offload_item {
+    struct mirror_offload_info info;
+
+    struct ovs_list node;
+};
+
+struct netdev_mirror_offload {
+    struct ovs_mutex mutex;
+    struct ovs_list list;
+    pthread_cond_t cond;
+};
+
+static struct netdev_mirror_offload netdev_mirror_offload = {
+    .mutex = OVS_MUTEX_INITIALIZER,
+    .list  = OVS_LIST_INITIALIZER(&netdev_mirror_offload.list),
+};
+
+static struct ovsthread_once offload_thread_once
+    = OVSTHREAD_ONCE_INITIALIZER;
+
+static void *netdev_mirror_offload_main(void *data);
+
+static struct
+netdev_mirror_offload_item *
+netdev_alloc_mirror_offload(struct mirror_offload_info *info)
+{
+    struct netdev_mirror_offload_item *offload;
+
+    offload = xzalloc(sizeof(*offload));
+    memcpy(&offload->info, info, sizeof(struct mirror_offload_info));
+    return offload;
+}
+
+static void
+netdev_free_mirror_offload(struct netdev_mirror_offload_item *offload)
+{
+    free(offload);
+}
+
+static void
+netdev_append_mirror_offload(struct netdev_mirror_offload_item *offload)
+{
+    ovs_mutex_lock(&netdev_mirror_offload.mutex);
+    ovs_list_push_back(&netdev_mirror_offload.list, &offload->node);
+    xpthread_cond_signal(&netdev_mirror_offload.cond);
+    ovs_mutex_unlock(&netdev_mirror_offload.mutex);
+}
+
+static bool
+netdev_is_dpdk_port(struct netdev *netdev)
+{
+    return strncmp(netdev->netdev_class->type, "dpdk",
+        strlen(netdev->netdev_class->type)) == 0;
+}
+
+bool
+netdev_mirror_offload_put(struct mirror_offload_info *info)
+{
+    struct netdev_mirror_offload_item *offload;
+    /* only support physical port for traffic mirroring */
+    if (info->add_mirror && !netdev_is_dpdk_port(info->output)) {
+        return false;
+    }
+
+    if (ovsthread_once_start(&offload_thread_once)) {
+        xpthread_cond_init(&netdev_mirror_offload.cond, NULL);
+        ovs_thread_create("netdev_mirror_offload",
+                          netdev_mirror_offload_main, NULL);
+        ovsthread_once_done(&offload_thread_once);
+    }
+
+    offload = netdev_alloc_mirror_offload(info);
+    netdev_append_mirror_offload(offload);
+    return true;
+}
+
+static int
+netdev_mirror_offload_configue(struct mirror_offload_info *info)
+{
+    int un_support_count = 0;
+    int ret;
+
+    if (info->n_src_port) {
+        for (int i = 0; i < info->n_src_port; i++) {
+            const struct netdev_class *class =
+                info->src[i]->netdev_class;
+            if (!class) {
+                return -1;
+            }
+            if (class->mirror_offload) {
+                ret = class->mirror_offload(info->src[i],
+                                            &info->flow_dst_mac[i],
+                                            info->output,
+                                            info->output_src_tags[i],
+                                            info->add_mirror, false);
+                if (ret) {
+                    return ret;
+                }
+            } else {
+                un_support_count++;
+            }
+        }
+    }
+
+    if (info->n_dst_port) {
+        for (int i = 0; i < info->n_dst_port; i++) {
+            const struct netdev_class *class =
+                info->dst[i]->netdev_class;
+            if (!class) {
+                return -1;
+            }
+            if (class->mirror_offload) {
+                ret = class->mirror_offload(info->dst[i],
+                                            &info->flow_src_mac[i],
+                                            info->output,
+                                            info->output_dst_tags[i],
+                                            info->add_mirror, true);
+                if (ret) {
+                    return ret;
+                }
+            } else {
+                un_support_count++;
+            }
+        }
+    }
+
+    return un_support_count;
+}
+
+#define MAXI_MIRROR_CONFIG 128
+static void *
+netdev_mirror_offload_main(void *data OVS_UNUSED)
+{
+    struct netdev_mirror_offload_item *offload;
+    struct ovs_list *list;
+    struct netdev_mirror_offload_item *offload_db[MAXI_MIRROR_CONFIG];
+    char offload_name[MAXI_MIRROR_CONFIG][MAXI_NAME_STRING];
+    int offload_add_count = 0;
+    int ret, i, ind;
+
+    for (i = 0; i < MAXI_MIRROR_CONFIG; i++) {
+        offload_db[i] = NULL;
+        offload_name[i][0] = '\0';
+    }
+
+    for (;;) {
+        ovs_mutex_lock(&netdev_mirror_offload.mutex);
+        if (ovs_list_is_empty(&netdev_mirror_offload.list)) {
+            ovsrcu_quiesce_start();
+            ovs_mutex_cond_wait(&netdev_mirror_offload.cond,
+                                &netdev_mirror_offload.mutex);
+            ovsrcu_quiesce_end();
+        }
+        list = ovs_list_pop_front(&netdev_mirror_offload.list);
+        offload = CONTAINER_OF(list, struct netdev_mirror_offload_item,
+            node);
+        ovs_mutex_unlock(&netdev_mirror_offload.mutex);
+
+        ind = MAXI_MIRROR_CONFIG;
+        for (i = 0; i < MAXI_MIRROR_CONFIG; i++) {
+            if (offload_name[i][0] &&
+                !strncmp(&offload_name[i][0], offload->info.name,
+                MAXI_NAME_STRING)) {
+                ind = i;
+                break;
+            }
+        }
+
+        if (!offload->info.add_mirror) {
+            /* remove mirror offload setup */
+            if (ind < MAXI_MIRROR_CONFIG) {
+                memcpy(&offload->info, &offload_db[ind]->info,
+                    sizeof(struct netdev_mirror_offload_item));
+                offload->info.add_mirror = false;
+                netdev_free_mirror_offload(offload_db[ind]);
+                offload_db[ind] = NULL;
+                offload_name[ind][0] = '\0';
+            } else {
+                VLOG_WARN("Mirror offload configuration, %s, "
+                    "not found; clear mirror offload operation"
+                    " aborted\n", offload->info.name);
+                continue;
+            }
+        } else {
+            if (offload_add_count == MAXI_MIRROR_CONFIG) {
+                VLOG_ERR("Number of outstanding mirror offload "
+                    "configuration, %d, exceeds maximum allow"
+                    ", %d; action aborted\n", offload_add_count,
+                    MAXI_MIRROR_CONFIG);
+                continue;
+            }
+
+            if (ind < MAXI_MIRROR_CONFIG) {
+                netdev_free_mirror_offload(offload);
+                VLOG_WARN("Attempt adding an existing mirror-offload "
+                    "configuration; request aborted\n");
+                continue;
+            }
+        }
+
+        ret = netdev_mirror_offload_configue(&offload->info);
+
+        if (ret) {
+            VLOG_ERR("%s mirror configuration fails due to %s\n",
+                offload->info.add_mirror? "Add": "Remove",
+                ret > 0 ? "unsupport source traffic type" :
+                "device is not ready");
+            netdev_free_mirror_offload(offload);
+            continue;
+        }
+
+        if (offload->info.add_mirror) {
+            for (i = 0; i <  MAXI_MIRROR_CONFIG; i++) {
+                if (offload_db[i] == NULL) {
+                    strncpy(offload_name[i], offload->info.name,
+                            MAXI_NAME_STRING);
+                    offload_db[i] = offload;
+                    offload_add_count++;
+                    break;
+                }
+            }
+        } else {
+            netdev_free_mirror_offload(offload);
+            offload_add_count--;
+        }
+    }
+
+    for (i = 0; i < MAXI_MIRROR_CONFIG; i++) {
+        if (offload_db[i]) {
+            netdev_free_mirror_offload(offload_db[i]);
+        }
+    }
+    return NULL;
+}
diff --git a/lib/netdev.h b/lib/netdev.h
index fb50730..eefe6dd 100644
--- a/lib/netdev.h
+++ b/lib/netdev.h
@@ -22,6 +22,9 @@ 
 #include "packets.h"
 #include "flow.h"
 
+#define MAXI_MIRROR_PORTS 16
+#define MAXI_NAME_STRING 128
+
 #ifdef  __cplusplus
 extern "C" {
 #endif
@@ -200,6 +203,22 @@  int netdev_send(struct netdev *, int qid, struct dp_packet_batch *,
                 bool concurrent_txq);
 void netdev_send_wait(struct netdev *, int qid);
 
+/* Hardware assisted mirror offloading*/
+struct mirror_offload_info {
+    struct netdev *src[MAXI_MIRROR_PORTS];
+    struct netdev *dst[MAXI_MIRROR_PORTS];
+    struct netdev *output;
+    int n_src_port;
+    int n_dst_port;
+    struct eth_addr flow_dst_mac[MAXI_MIRROR_PORTS];
+    struct eth_addr flow_src_mac[MAXI_MIRROR_PORTS];
+    uint16_t output_src_tags[MAXI_MIRROR_PORTS];
+    uint16_t output_dst_tags[MAXI_MIRROR_PORTS];
+    bool add_mirror; /* true: add mirror false: remove mirror */
+    char name[MAXI_NAME_STRING];
+};
+bool netdev_mirror_offload_put(struct mirror_offload_info *);
+
 /* native tunnel APIs */
 /* Structure to pass parameters required to build a tunnel header. */
 struct netdev_tnl_build_header_params {
diff --git a/tests/ovs-vsctl.at b/tests/ovs-vsctl.at
index c8babe3..9db0aab 100644
--- a/tests/ovs-vsctl.at
+++ b/tests/ovs-vsctl.at
@@ -1344,7 +1344,11 @@  _uuid               : <1>
 name                : eth1
 _uuid               : <2>
 name                : mymirror
+output_dst_vlan     : []
 output_port         : <1>
+output_port_name    : ""
+output_port_name    : ""
+output_src_vlan     : []
 output_vlan         : []
 select_all          : false
 select_dst_port     : [<0>]
diff --git a/vswitchd/bridge.c b/vswitchd/bridge.c
index a3e7fac..b78a883 100644
--- a/vswitchd/bridge.c
+++ b/vswitchd/bridge.c
@@ -38,6 +38,7 @@ 
 #include "mac-learning.h"
 #include "mcast-snooping.h"
 #include "netdev.h"
+#include "netdev-provider.h"
 #include "netdev-offload.h"
 #include "nx-match.h"
 #include "ofproto/bond.h"
@@ -330,6 +331,9 @@  static void mirror_destroy(struct mirror *);
 static bool mirror_configure(struct mirror *);
 static void mirror_refresh_stats(struct mirror *);
 
+static void mirror_offload_destroy(struct mirror *);
+static bool mirror_offload_configure(struct mirror *);
+
 static void iface_configure_lacp(struct iface *, struct lacp_slave_settings *);
 static bool iface_create(struct bridge *, const struct ovsrec_interface *,
                          const struct ovsrec_port *);
@@ -422,7 +426,35 @@  if_notifier_changed(struct if_notifier *notifier OVS_UNUSED)
     seq_wait(ifaces_changed, last_ifaces_changed);
     return changed;
 }
-
+
+static struct port *
+port_lookup_all(const char *port_name)
+{
+    struct bridge *br;
+    struct port *port = NULL;
+    int found = 0;
+
+    HMAP_FOR_EACH (br, node, &all_bridges) {
+        struct port *temp_port = NULL;
+        temp_port = port_lookup(br, port_name);
+        if (temp_port) {
+            if (!port) {
+                port = temp_port;
+            }
+            found++;
+        }
+    }
+
+    if (found) {
+        if (found > 1) {
+            VLOG_INFO("More than one bridge owns port with name:%s\n",
+                      port_name);
+        }
+        return port;
+    }
+    return NULL;
+}
+
 /* Public functions. */
 
 /* Initializes the bridge module, configuring it to obtain its configuration
@@ -5056,14 +5088,166 @@  mirror_create(struct bridge *br, const struct ovsrec_mirror *cfg)
     return m;
 }
 
+static struct netdev *get_netdev_from_port(struct mirror *m,
+                                           struct port **port,
+                                           const char *name)
+{
+    struct port *temp_port;
+    struct iface *iface;
+
+    *port = NULL;
+    temp_port = port_lookup(m->bridge, name);
+    if (temp_port) {
+        LIST_FOR_EACH (iface, port_elem, &temp_port->ifaces) {
+            if (iface) {
+                *port = temp_port;
+                return iface->netdev;
+            }
+        }
+    }
+    /* try different bridges */
+    temp_port = port_lookup_all(name);
+    if (temp_port) {
+        LIST_FOR_EACH (iface, port_elem, &temp_port->ifaces) {
+            if (iface) {
+                *port = temp_port;
+                return iface->netdev;
+            }
+        }
+    }
+    return NULL;
+}
+
+static int
+set_mirror_offload_info(struct mirror *m, struct mirror_offload_info *info)
+{
+    const struct ovsrec_mirror *cfg = m->cfg;
+    struct port *port = NULL;
+    struct iface *iface;
+    int i;
+
+    info->n_src_port = (cfg->n_select_src_port > MAXI_MIRROR_PORTS)?
+        MAXI_MIRROR_PORTS:cfg->n_select_src_port;
+    if (info->n_src_port) {
+        for (i = 0; i < info->n_src_port; i++) {
+            info->src[i] = get_netdev_from_port(m, &port,
+                cfg->select_src_port[i]->name);
+            if (!info->src[i]) {
+                VLOG_ERR("src-port: %s is not a netdev device\n",
+                    cfg->select_src_port[i]->name);
+                return -1;
+            }
+        }
+        memset(info->flow_dst_mac, 0, sizeof(struct eth_addr) *
+                                             MAXI_MIRROR_PORTS);
+        if (cfg->n_flow_dst_mac) {
+            int dst_count = (info->n_src_port > cfg->n_flow_dst_mac)?
+                cfg->n_flow_dst_mac:info->n_src_port;
+            for (i = 0; i < dst_count; i++) {
+                eth_addr_from_string(cfg->flow_dst_mac[i],
+                    &info->flow_dst_mac[i]);
+            }
+        }
+    }
+
+    info->n_dst_port = (cfg->n_select_dst_port > MAXI_MIRROR_PORTS)?
+        MAXI_MIRROR_PORTS:cfg->n_select_dst_port;
+    if (info->n_dst_port) {
+        for (i = 0; i < info->n_dst_port; i++) {
+            info->dst[i] = get_netdev_from_port(m, &port,
+                cfg->select_dst_port[i]->name);
+            if (!info->dst[i]) {
+                VLOG_ERR("dst-port: %s is not a netdev device\n",
+                    cfg->select_dst_port[i]->name);
+                return -1;
+            }
+        }
+        memset(info->flow_src_mac, 0, sizeof(struct eth_addr) *
+                                            MAXI_MIRROR_PORTS);
+        if (cfg->n_flow_src_mac) {
+            int src_count = (info->n_dst_port > cfg->n_flow_src_mac)?
+                cfg->n_flow_src_mac:info->n_dst_port;
+            for (i = 0; i < src_count; i++) {
+                eth_addr_from_string(cfg->flow_src_mac[i],
+                    &info->flow_src_mac[i]);
+            }
+        }
+    }
+
+    if (cfg->n_output_src_vlan) {
+        int count = (cfg->n_output_src_vlan > info->n_src_port)?
+            info->n_src_port:cfg->n_output_src_vlan;
+        for (i = 0; i < count; i++) {
+            info->output_src_tags[i] = cfg->output_src_vlan[i];
+        }
+    }
+
+    if (cfg->n_output_dst_vlan) {
+        int count = (cfg->n_output_dst_vlan > info->n_dst_port)?
+            info->n_dst_port:cfg->n_output_dst_vlan;
+        for (i = 0; i < count; i++) {
+            info->output_dst_tags[i] = cfg->output_dst_vlan[i];
+        }
+    }
+
+    /* output port */
+    if (cfg->output_port) {
+        info->output = get_netdev_from_port(m, &port, cfg->output_port->name);
+    } else {
+        info->output = get_netdev_from_port(m, &port, cfg->output_port_name);
+    }
+    if (!info->output) {
+        VLOG_ERR("output-port: %s is not a netdev device\n",
+            cfg->output_port?cfg->output_port->name:
+            cfg->output_port_name);
+        return -1;
+    }
+
+    if (!ovs_list_is_singleton(&port->ifaces)) {
+        /* this is a bonding port */
+        LIST_FOR_EACH (iface, port_elem, &port->ifaces) {
+            if (iface) {
+                if (!cfg->bond_port_other) {
+                    if (strcmp(iface->netdev->name, info->output->name) < 0) {
+                        info->output = iface->netdev;
+                    }
+                } else {
+                    if (strcmp(iface->netdev->name, info->output->name) > 0) {
+                        info->output = iface->netdev;
+                    }
+                }
+            }
+        }
+    }
+    VLOG_INFO("sucess creating mirror-offload(%s): with %d src-port"
+        " streams %d dst-port streams to %s\n",
+        cfg->name, info->n_src_port, info->n_dst_port,
+        info->output->name);
+    return 0;
+}
+
+static void
+mirror_offload_destroy(struct mirror *m)
+{
+    struct mirror_offload_info info;
+
+    info.add_mirror = false;
+    ovs_strzcpy(info.name, m->name, MAXI_NAME_STRING);
+    netdev_mirror_offload_put(&info);
+}
+
 static void
 mirror_destroy(struct mirror *m)
 {
     if (m) {
         struct bridge *br = m->bridge;
 
-        if (br->ofproto) {
-            ofproto_mirror_unregister(br->ofproto, m);
+        if (m->cfg && m->cfg->mirror_offload) {
+            mirror_offload_destroy(m);
+        } else {
+            if (br->ofproto) {
+                ofproto_mirror_unregister(br->ofproto, m);
+            }
         }
 
         hmap_remove(&br->mirrors, &m->hmap_node);
@@ -5096,11 +5280,29 @@  mirror_collect_ports(struct mirror *m,
 }
 
 static bool
+mirror_offload_configure(struct mirror *m)
+{
+    struct mirror_offload_info info;
+
+   memset(&info, 0, sizeof(struct mirror_offload_info));
+    info.add_mirror = true;
+    ovs_strzcpy(info.name, m->name, MAXI_NAME_STRING);
+    if (set_mirror_offload_info(m, &info)) {
+        return false;
+    }
+    netdev_mirror_offload_put(&info);
+    return true;
+}
+
+static bool
 mirror_configure(struct mirror *m)
 {
     const struct ovsrec_mirror *cfg = m->cfg;
     struct ofproto_mirror_settings s;
 
+    if (cfg->mirror_offload) {
+        return mirror_offload_configure(m);
+    }
     /* Set name. */
     if (strcmp(cfg->name, m->name)) {
         free(m->name);
diff --git a/vswitchd/vswitch.ovsschema b/vswitchd/vswitch.ovsschema
index 0666c8c..b4fafd7 100644
--- a/vswitchd/vswitch.ovsschema
+++ b/vswitchd/vswitch.ovsschema
@@ -1,6 +1,6 @@ 
 {"name": "Open_vSwitch",
- "version": "8.2.0",
- "cksum": "1076640191 26427",
+ "version": "8.2.1",
+ "cksum": "1380443110 27249",
  "tables": {
    "Open_vSwitch": {
      "columns": {
@@ -420,6 +420,18 @@ 
          "type": "string"},
        "select_all": {
          "type": "boolean"},
+       "output_port_name": {
+         "type": "string"},
+       "mirror_offload": {
+         "type": "boolean"},
+       "bond_port_other": {
+         "type": "boolean"},
+       "flow_src_mac": {
+         "type": {"key": {"type": "string"},
+                  "min": 0, "max": "unlimited"}},
+       "flow_dst_mac": {
+         "type": {"key": {"type": "string"},
+                  "min": 0, "max": "unlimited"}},
        "select_src_port": {
          "type": {"key": {"type": "uuid",
                           "refTable": "Port",
@@ -440,6 +452,16 @@ 
                           "refTable": "Port",
                           "refType": "weak"},
                   "min": 0, "max": 1}},
+       "output_src_vlan": {
+         "type": {"key": {"type": "integer",
+                          "minInteger": 0,
+                          "maxInteger": 4095},
+                  "min": 0, "max": 4096}},
+       "output_dst_vlan": {
+         "type": {"key": {"type": "integer",
+                          "minInteger": 0,
+                          "maxInteger": 4095},
+                  "min": 0, "max": 4096}},
        "output_vlan": {
          "type": {"key": {"type": "integer",
                           "minInteger": 1,
diff --git a/vswitchd/vswitch.xml b/vswitchd/vswitch.xml
index 5fd15ce..a3b523d 100644
--- a/vswitchd/vswitch.xml
+++ b/vswitchd/vswitch.xml
@@ -4795,7 +4795,37 @@  ovs-vsctl add-port br0 p0 -- set Interface p0 type=patch options:peer=p1 \
         selected for mirroring.
       </column>
 
-      <column name="select_dst_port">
+      <column name="output_port_name">
+        Name string of the port on which mirrored traffic will be
+        transmitted.
+      </column>
+
+      <column name="mirror_offload">
+        If true, a hw-assisted port mirroring is configured instead
+        default mirroring.
+      </column>
+
+      <column name="bond_port_other">
+        By default, for a output bonding port, the destination port
+        is chosen by select the port with less name string. If true,
+        the greater operator is chosen.
+      </column>
+
+      <column name="flow_src_mac">
+        The source MAC address(es) for per-flow mirroring. Each MAC
+        address is separate by ','. This parametr is paired with
+        select_dst_port. A '0' MAC address indicates the requested mirror
+        is a per-port mirroring, otherwise it's a per-flow mirroring
+      </column>
+
+      <column name="flow_dst_mac">
+        The destination MAC address(es) for per-flow mirroring. Each MAC
+        address is separate by ','. This parametr is paired with
+        select_src_port. A '0' MAC address indicates the requested mirror
+        is a per-port mirroring, otherwise it's a per-flow mirroring
+      </column>
+
+     <column name="select_dst_port">
         Ports on which departing packets are selected for mirroring.
       </column>
 
@@ -4876,6 +4906,34 @@  ovs-vsctl add-port br0 p0 -- set Interface p0 type=patch options:peer=p1 \
         </p>
       </column>
 
+      <column name="output_src_vlan">
+        <p>Output VLAN for selected source port packets, if nonempty.</p>
+        <p>
+          <em>Please note:</em>
+          This is different than <ref column="output-vlan"/> This vlan is used
+          to add an additional vlan tag on the mirror traffic, either it
+          contains vlan or not. The receive end could choose to filter
+          out this additional vlan. This option is provided so the mirrored
+          traffic could maintain its original vlan informaiton, and this mirror
+          can be used to filter out un-wanted traffic such as in
+          <ref column="mirror_offload"/>.
+        </p>
+      </column>
+
+      <column name="output_dst_vlan">
+        <p>Output VLAN for selected destination port packets, if nonempty.</p>
+        <p>
+          <em>Please note:</em>This is different than
+          <ref column="output-vlan"/>
+          This vlan is used to add an additional vlan tag on the mirror
+          traffic, either it contains vlan or not. The receive end could choose
+          to filter out this additional vlan. This option is provided so the
+          mirrored traffic could maintain its original vlan informaiton, and
+          this mirror can be used to filter out un-wanted traffic such as in
+          <ref column="mirror_offload"/>.
+        </p>
+      </column>
+
       <column name="snaplen">
         <p>Maximum per-packet number of bytes to mirror.</p>
         <p>A mirrored packet with size larger than <ref column="snaplen"/>