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"/>
