> -----Original Message----- > From: Maxime Coquelin <[email protected]> > Sent: Tuesday, May 18, 2021 12:15 PM > To: Miskell, Timothy <[email protected]>; [email protected] > Cc: Wang, Liang-min <[email protected]> > Subject: Re: [PATCH] Extends the existing mirror configuration parameters > > Hi Timothy, Liang-min, > > Thanks for rebasing the patch. > A list of delta against the first RFC could help the reviewers. > I notice one change in the right direction is the conversion to Vhost > API datapath instead of Vhost PMD. > >>In this patch we support both Vhost API and Vhost PMD because OVS supports >>both >>VhostUser and Vdev ports. >> > Also, I would suggest to have the patch split in several incremental > patches to ease the review. > >> Thank you for suggestion. We will provide incremental patches on next >> submission >> > On 5/10/21 6:00 PM, Timothy Miskell wrote: > > From: Liang-min Wang <[email protected]> > > > > 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 > > - 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. > > - mirror-tunnel-addr: BDF string of the tunnel device. > > > > ovs-vsctl test change because new mirroring parameters are introduced in > this patch > > It would help to provide examples of usage of these new parameters. > >> Will add examples in the new patches >> > > 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 > > > > 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) > > I will for sure have more questions later, but please find a few > comments/questions below: > > > Signed-off-by: Liang-min Wang <[email protected]> > > Tested-by: Timothy Miskell <[email protected]> > > Suggested-by: Munish Mehan <[email protected]> > > --- > > lib/automake.mk | 2 + > > lib/netdev-dpdk-mirror.c | 516 > +++++++++++++++++++++++++++++++++++++ > > lib/netdev-dpdk-mirror.h | 83 ++++++ > > lib/netdev-dpdk.c | 397 ++++++++++++++++++++++++++++ > > lib/netdev-provider.h | 16 ++ > > lib/netdev.c | 386 +++++++++++++++++++++++++++ > > lib/netdev.h | 16 ++ > > tests/ovs-vsctl.at | 2 + > > vswitchd/bridge.c | 271 ++++++++++++++++++- > > vswitchd/vswitch.ovsschema | 24 +- > > vswitchd/vswitch.xml | 50 ++++ > > 11 files changed, 1759 insertions(+), 4 deletions(-) > > create mode 100644 lib/netdev-dpdk-mirror.c > > create mode 100644 lib/netdev-dpdk-mirror.h > > > > diff --git a/lib/automake.mk b/lib/automake.mk > > index 39901bd6d..dcafbfaca 100644 > > --- a/lib/automake.mk > > +++ b/lib/automake.mk > > @@ -170,6 +170,7 @@ lib_libopenvswitch_la_SOURCES = \ > > lib/multipath.h \ > > lib/namemap.c \ > > lib/netdev-dpdk.h \ > > + lib/netdev-dpdk-mirror.h \ > > lib/netdev-dummy.c \ > > lib/netdev-offload.c \ > > lib/netdev-offload.h \ > > @@ -460,6 +461,7 @@ if DPDK_NETDEV > > lib_libopenvswitch_la_SOURCES += \ > > lib/dpdk.c \ > > lib/netdev-dpdk.c \ > > + lib/netdev-dpdk-mirror.c \ > > lib/netdev-offload-dpdk.c > > else > > lib_libopenvswitch_la_SOURCES += \ > > diff --git a/lib/netdev-dpdk-mirror.c b/lib/netdev-dpdk-mirror.c > > new file mode 100644 > > index 000000000..ff2701660 > > --- /dev/null > > +++ b/lib/netdev-dpdk-mirror.c > > @@ -0,0 +1,516 @@ > > +/* > > + * Copyright (c) 2014, 2015, 2016, 2017 Nicira, Inc. > > + * Copyright (c) 2019 Mellanox Technologies, Ltd. > > + * > > + * Licensed under the Apache License, Version 2.0 (the "License"); > > + * you may not use this file except in compliance with the License. > > + * You may obtain a copy of the License at: > > + * > > + * http://www.apache.org/licenses/LICENSE-2.0 > > + * > > + * Unless required by applicable law or agreed to in writing, software > > + * distributed under the License is distributed on an "AS IS" BASIS, > > + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express > or implied. > > + * See the License for the specific language governing permissions and > > + * limitations under the License. > > + */ > > +#include <config.h> > > +#include <rte_ethdev.h> > > + > > +#include "netdev-dpdk-mirror.h" > > +#include "openvswitch/vlog.h" > > +#include "openvswitch/dynamic-string.h" > > +#include "util.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_DEVICE_ID 0xFFFFFFFF > > + > > +VLOG_DEFINE_THIS_MODULE(netdev_dpdk_mirror); > > + > > +/* port/flow mirror database management routines */ > > +/* > > + * 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 void > > +netdev_mirror_db_init(struct mirror_offload_port *db, int size) > > +{ > > + int i; > > + > > + for (i = 0; i < size; i++) { > > + db[i].dev_id = INVALID_DEVICE_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 = mirror_port_db_size << 1; > > + struct mirror_offload_port *new_db = xmalloc( > > + sizeof(struct mirror_offload_port)*new_size); > > + > > + 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 struct mirror_offload_port* > > +netdev_mirror_data_find(uint32_t dev_id) > > +{ > > + int i; > > + > > + if (mirror_port_db == NULL) { > > + return NULL; > > + } > > + > > + for (i = 0; i < mirror_port_db_size; i++) { > > + if (dev_id == mirror_port_db[i].dev_id) { > > + return &mirror_port_db[i]; > > + } > > + } > > + return NULL; > > +} > > + > > +static struct mirror_offload_port* > > +netdev_mirror_data_add(uint32_t dev_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); > > + netdev_mirror_db_init(mirror_port_db, mirror_port_db_size); > > + } > > + target = netdev_mirror_data_find(dev_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", dev_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", dev_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].dev_id == INVALID_DEVICE_ID) { > > + break; > > + } > > + } > > + if (i == mirror_port_db_size && 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->dev_id = dev_id; > > + mirror_port_used ++; > > + } > > + return target; > > +} > > + > > +static void > > +netdev_mirror_data_remove(uint32_t dev_id, int tx) { > > + struct mirror_offload_port *target = netdev_mirror_data_find(dev_id); > > + > > + if (!target) { > > + VLOG_ERR("Attempt to remove unsaved port, %d, %s callback\n", > > + dev_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->dev_id = INVALID_DEVICE_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; > > + } > > + } > > +} > > + > > +void > > +netdev_mirror_data_proc(uint32_t dev_id, mirror_data_op op, > > + int tx, struct mirror_param *in_param, > > + struct mirror_offload_port **out_param) > > +{ > > + switch (op) { > > + case mirror_data_find: > > + *out_param = netdev_mirror_data_find(dev_id); > > + break; > > + case mirror_data_add: > > + *out_param = netdev_mirror_data_add(dev_id, tx, in_param); > > + break; > > + case mirror_data_rem: > > + netdev_mirror_data_remove(dev_id, tx); > > + break; > > + } > > +} > > + > > +/* port/flow mirror traffic processors */ > > +static inline uint16_t > > +netdev_custom_mirror_offload_cb(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; > > + 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++) { > > + if (data->custom_scan(pkts[i], user_params)) { > > + pkt_buf[match_count] = pkts[i]; > > + pkt_buf[match_count]->ol_flags |= PKT_TX_VLAN_PKT; > > Does it work if the packet already has a VLAN inserted? > >> Good catch. The design is based upon no VLAN insertion offloading is applied >> on source traffic. >> > > + 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); > > Wouldn't it scale better with: > dst_qidx = qidx % data->n_dst_queue > ? > >> We tried to avoid using "%" operator. We could add "unlikely" and the >> suggested "%" to make improvement >> > > + > > + rte_spinlock_lock(&data->locks[dst_qidx]); > > + pkt_trans = rte_eth_tx_burst(dst_port_id, dst_qidx, pkt_buf, > match_count); > > + rte_spinlock_unlock(&data->locks[dst_qidx]); > > + > > + for (i = 0; i < match_count; i++) { > > + pkt_buf[i]->ol_flags &= ~PKT_TX_VLAN_PKT; > > + } > > In order to further reduce the performance impact of mirroring, have you > envisaged to offload it to dedicated PMD threads? > >> The mirror-tunnel design is a comprised approach between hardware TAP and >> software TAP. >> The tunnel itself is designed to have very little impact on source traffic >> processing core. From >> our benchmark on SR-IOV, L2 forwarding, mirroring, we only observed 10-20% >> impact on 64-byte packets, and >> we did not observe impact when running traffic with packet size with >> 128-byte or above. >> > > + > > + while (unlikely (pkt_trans < match_count)) { > > + rte_pktmbuf_free(pkt_buf[pkt_trans]); > > + pkt_trans++; > > + } > > + > > + return nb_pkts; > > +} > > + > > +static inline uint16_t > > +netdev_flow_mirror_offload_cb(uint16_t qidx, struct rte_mbuf **pkts, > > + uint16_t nb_pkts, void *user_params, uint32_t offset) > > +{ > > + 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 = *(uint64_t *) data->extra_data; > > + 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], void *, offset); > > + 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->locks[dst_qidx]); > > + pkt_trans = rte_eth_tx_burst(dst_port_id, dst_qidx, pkt_buf, > match_count); > > + rte_spinlock_unlock(&data->locks[dst_qidx]); > > + > > + 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_port_mirror_offload_cb(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->locks[dst_qidx]); > > + pkt_trans = rte_eth_tx_burst(dst_port_id, dst_qidx, pkts, nb_pkts); > > + rte_spinlock_unlock(&data->locks[dst_qidx]); > > + > > + 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_rx_custom_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) > > +{ > > + return netdev_custom_mirror_offload_cb(qidx, pkts, nb_pkts, > user_params); > > +} > > + > > +static inline uint16_t > > +netdev_tx_custom_mirror_offload_cb(uint16_t port_id OVS_UNUSED, > > + uint16_t qidx, struct rte_mbuf **pkts, uint16_t nb_pkts, > > + void *user_params) > > +{ > > + return netdev_custom_mirror_offload_cb(qidx, pkts, nb_pkts, > user_params); > > +} > > + > > +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) > > +{ > > + return netdev_flow_mirror_offload_cb(qidx, pkts, nb_pkts, > user_params, 0); > > +} > > + > > +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) > > +{ > > + return netdev_flow_mirror_offload_cb(qidx, pkts, nb_pkts, > user_params, 6); > > +} > > + > > +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) > > +{ > > + return netdev_port_mirror_offload_cb(qidx, pkts, nb_pkts, > user_params); > > +} > > + > > +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) > > +{ > > + return netdev_port_mirror_offload_cb(qidx, pkts, nb_pkts, > user_params); > > +} > > + > > +static rte_rx_callback_fn > > +netdev_mirror_rx_cb(rte_mirror_type mirror_type) > > +{ > > + switch (mirror_type) { > > + case mirror_port: > > + return netdev_rx_port_mirror_offload_cb; > > + case mirror_flow_mac: > > + return netdev_rx_flow_mirror_offload_cb; > > + case mirror_flow_custom: > > + return netdev_rx_custom_mirror_offload_cb; > > + case mirror_invalid: > > + return NULL; > > + } > > + VLOG_ERR("Un-supported mirror type\n"); > > + return NULL; > > +} > > + > > +static rte_tx_callback_fn > > +netdev_mirror_tx_cb(rte_mirror_type mirror_type) > > +{ > > + switch (mirror_type) { > > + case mirror_port: > > + return netdev_tx_port_mirror_offload_cb; > > + case mirror_flow_mac: > > + return netdev_tx_flow_mirror_offload_cb; > > + break; > > + case mirror_flow_custom: > > + return netdev_tx_custom_mirror_offload_cb; > > + case mirror_invalid: > > + return NULL; > > + } > > + VLOG_ERR("Un-supported mirror type\n"); > > + return NULL; > > +} > > + > > +void > > +netdev_mirror_cb_set(struct mirror_param *data, uint16_t port_id, > > + int pmd_cb, int tx) > > +{ > > + unsigned int qid; > > + > > + data->pkt_buf = NULL; > > + if (data->extra_data_size) { > > + data->pkt_buf = xmalloc(sizeof(mirror_fn_cb)*data->max_burst_size > * > > + data->n_src_queue); > > + } > > + > > + data->mirror_cb = xmalloc(sizeof(struct rte_eth_rxtx_callback *) > > + * data->n_src_queue); > > + for (qid = 0; qid < data->n_src_queue; qid++) { > > + if (pmd_cb) { > > + if (tx) { > > + data->mirror_cb[qid].pmd = rte_eth_add_tx_callback(port_id, > > + qid, netdev_mirror_tx_cb(data->mirror_type), data); > > + } else { > > + data->mirror_cb[qid].pmd = rte_eth_add_rx_callback(port_id, > > + qid, netdev_mirror_rx_cb(data->mirror_type), data); > > + } > > + } else { > > + struct rte_eth_rxtx_callback *rxtx_cb = > > + xmalloc(sizeof(struct rte_eth_rxtx_callback)); > > + > > + data->mirror_cb[qid].direct = rxtx_cb; > > + rxtx_cb->next = NULL; > > + rxtx_cb->param = data; > > + > > + if (tx) { > > + rxtx_cb->fn.tx = netdev_mirror_tx_cb(data->mirror_type); > > + } else { > > + rxtx_cb->fn.rx = netdev_mirror_rx_cb(data->mirror_type); > > + } > > + } > > + } > > +} > > + > > +/* port/flow mirroring device (port) register/un-registe routines */ > > +int > > +netdev_eth_register_mirror(uint16_t src_port, struct mirror_param > *param, > > + int tx_cb) > > +{ > > + struct mirror_offload_port *port_info = NULL; > > + struct mirror_param *data; > > + > > + netdev_mirror_data_proc(src_port, mirror_data_add, tx_cb, param, > > + &port_info); > > + if (!port_info) { > > + return -1; > > + } > > + > > + data = tx_cb ? &port_info->tx : &port_info->rx; > > + netdev_mirror_cb_set(data, src_port, 1, tx_cb); > > + > > + return 0; > > +} > > + > > +int > > +netdev_eth_unregister_mirror(uint16_t src_port, int tx_cb) > > +{ > > + /* release both cb and pkt_buf */ > > + unsigned int i; > > + struct mirror_offload_port *port_info = NULL; > > + struct mirror_param *data; > > + > > + netdev_mirror_data_proc(src_port, mirror_data_find, tx_cb, NULL, > > + &port_info); > > + 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].pmd) { > > + if (tx_cb) { > > + rte_eth_remove_tx_callback(src_port, i, > > + data->mirror_cb[i].pmd); > > + } else { > > + rte_eth_remove_rx_callback(src_port, i, > > + data->mirror_cb[i].pmd); > > + } > > + } > > + data->mirror_cb[i].pmd = NULL; > > + } > > + free(data->mirror_cb); > > + > > + if (data->pkt_buf) { > > + free(data->pkt_buf); > > + data->pkt_buf = NULL; > > + } > > + > > + if (data->extra_data) { > > + free(data->extra_data); > > + data->extra_data = NULL; > > + data->extra_data_size = 0; > > + } > > + > > + netdev_mirror_data_proc(src_port, mirror_data_rem, tx_cb, NULL, > NULL); > > + return 0; > > +} > > diff --git a/lib/netdev-dpdk-mirror.h b/lib/netdev-dpdk-mirror.h > > new file mode 100644 > > index 000000000..ee4b933ba > > --- /dev/null > > +++ b/lib/netdev-dpdk-mirror.h > > @@ -0,0 +1,83 @@ > > +/* > > + * Copyright (c) 2014, 2015, 2016 Nicira, Inc. > > + * > > + * Licensed under the Apache License, Version 2.0 (the "License"); > > + * you may not use this file except in compliance with the License. > > + * You may obtain a copy of the License at: > > + * > > + * http://www.apache.org/licenses/LICENSE-2.0 > > + * > > + * Unless required by applicable law or agreed to in writing, software > > + * distributed under the License is distributed on an "AS IS" BASIS, > > + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express > or implied. > > + * See the License for the specific language governing permissions and > > + * limitations under the License. > > + */ > > + > > +#ifndef NETDEV_DPDK_MIRROR_H > > +#define NETDEV_DPDK_MIRROR_H > > + > > +#include "openvswitch/types.h" > > + > > +#ifdef __cplusplus > > +extern "C" { > > +#endif > > + > > +typedef enum { > > + mirror_data_find, /* find the mirror-data allocated */ > > + mirror_data_add, /* add a new mirror_param data int DB */ > > + mirror_data_rem, /* remove a mirror_param from the DB */ > > +} mirror_data_op; > > + > > +typedef int (*rte_mirror_scan_fn)(struct rte_mbuf *pkt, void > *user_param); > > +typedef enum { > > + mirror_port, /* port mirror */ > > + mirror_flow_mac, /* flow mirror according to source mac */ > > + mirror_flow_custom, /* flow mirror according to a callback scn */ > > + mirror_invalid, /* invalid mirror_type */ > > +} rte_mirror_type; > > + > > +typedef union { > > + const struct rte_eth_rxtx_callback *pmd; > > + struct rte_eth_rxtx_callback *direct; > > +} mirror_fn_cb; > > + > > +struct mirror_param { > > + uint16_t dst_port_id; > > + uint16_t dst_vlan_id; > > + rte_spinlock_t *locks; > > + int n_src_queue; > > + int n_dst_queue; > > + struct rte_mbuf **pkt_buf; > > + mirror_fn_cb *mirror_cb; > > + unsigned int max_burst_size; > > + rte_mirror_scan_fn custom_scan; > > + rte_mirror_type mirror_type; > > + unsigned int extra_data_size; > > + void *extra_data; /* extra mirror parameter */ > > +}; > > + > > +struct mirror_offload_port { > > + uint32_t dev_id; > > + struct mirror_param rx; > > + struct mirror_param tx; > > +}; > > + > > +bool netdev_port_started(uint16_t port_id, uint32_t *num_tx_queue); > > +int netdev_get_portid_from_addr(const char *pci_addr_str, uint16_t > *port_id); > > +int netdev_tunnel_port_setup(uint16_t portid, uint32_t *num_queue); > > + > > +void netdev_mirror_data_proc(uint32_t dev_id, mirror_data_op op, > > + int tx, struct mirror_param *in_param, > > + struct mirror_offload_port **out_param); > > +void netdev_mirror_cb_set(struct mirror_param *data, uint16_t port_id, > > + int pmd, int tx); > > +int netdev_eth_register_mirror(uint16_t src_port, > > + struct mirror_param *param, int tx_cb); > > +int netdev_eth_unregister_mirror(uint16_t src_port, int tx_cb); > > + > > +#ifdef __cplusplus > > +} > > +#endif > > + > > +#endif /* netdev-dpdk-mirror.h */ > > diff --git a/lib/netdev-dpdk.c b/lib/netdev-dpdk.c > > index 9d8096668..eb6644333 100644 > > --- a/lib/netdev-dpdk.c > > +++ b/lib/netdev-dpdk.c > > @@ -48,6 +48,7 @@ > > #include "fatal-signal.h" > > #include "if-notifier.h" > > #include "netdev-provider.h" > > +#include "netdev-dpdk-mirror.h" > > #include "netdev-vport.h" > > #include "odp-util.h" > > #include "openvswitch/dynamic-string.h" > > @@ -171,6 +172,16 @@ static const struct rte_eth_conf port_conf = { > > }, > > }; > > > > +struct mirror_tunnel_port_info { > > + uint16_t port_id; > > + rte_spinlock_t *locks; > > + uint32_t share_count; > > + uint32_t num_queue; > > + bool port_started; > > + struct mirror_tunnel_port_info *next; > > +}; > > +static struct mirror_tunnel_port_info *mirror_tunnel_head = NULL; > > + > > /* > > * These callbacks allow virtio-net devices to be added to vhost ports when > > * configuration has been fully completed. > > @@ -443,6 +454,8 @@ struct netdev_dpdk { > > }; > > struct dpdk_tx_queue *tx_q; > > struct rte_eth_link link; > > + mirror_fn_cb *rx_cb; /* shared pointer */ > > + mirror_fn_cb *tx_cb; > > ); > > > > PADDED_MEMBERS_CACHELINE_MARKER(CACHE_LINE_SIZE, > cacheline1, > > @@ -2417,6 +2430,13 @@ netdev_dpdk_vhost_rxq_recv(struct > netdev_rxq *rxq, > > nb_rx = rte_vhost_dequeue_burst(vid, qid, dev->dpdk_mp->mp, > > (struct rte_mbuf **) batch->packets, > > NETDEV_MAX_BURST); > > + > > + if (dev->rx_cb && dev->rx_cb[qid].direct->fn.rx) { > > + dev->rx_cb[qid].direct->fn.rx((uint16_t) vid, qid, > > + (struct rte_mbuf **) batch->packets, nb_rx, > > + NETDEV_MAX_BURST, dev->rx_cb[qid].direct->param); > > + } > > + > > if (!nb_rx) { > > return EAGAIN; > > } > > @@ -2634,6 +2654,10 @@ __netdev_dpdk_vhost_send(struct netdev > *netdev, int qid, > > int vhost_qid = qid * VIRTIO_QNUM + VIRTIO_RXQ; > > unsigned int tx_pkts; > > > > + if (dev->tx_cb && dev->tx_cb[qid].direct->fn.tx) { > > + dev->tx_cb[qid].direct->fn.tx((uint16_t) vid, qid, cur_pkts, > > cnt, > > + dev->tx_cb[qid].direct->param); > > + } > > tx_pkts = rte_vhost_enqueue_burst(vid, vhost_qid, cur_pkts, cnt); > > if (OVS_LIKELY(tx_pkts)) { > > /* Packets have been sent.*/ > > @@ -5291,6 +5315,376 @@ netdev_dpdk_rte_flow_query_count(struct > netdev *netdev, > > return ret; > > } > > > > +/* > > + * mirror tunnel device management routines > > + * mirror tunnel devices are devices reserved solely for > > + * traffic mirroring > > + */ > > +static void > > +netdev_dpdk_update_mt_list(struct mirror_tunnel_port_info > *mt_port_info, > > + bool add_port) > > +{ > > + struct mirror_tunnel_port_info *ptr = mirror_tunnel_head; > > + > > + if (add_port) { > > + if (!ptr) { > > + mirror_tunnel_head = mt_port_info; > > + return; > > + } > > + while (ptr->next) { > > + ptr = ptr->next; > > + } > > + ptr->next = mt_port_info; > > + } else { > > + while (ptr->next && > > + ptr->next->port_id != mt_port_info->port_id) { > > + ptr = ptr->next; > > + } > > + > > + if (ptr->next) { > > + ptr->next = ptr->next->next; > > + free(mt_port_info); > > + } else { > > + if (ptr->port_id == mt_port_info->port_id) { > > + mirror_tunnel_head = NULL; > > + free(mt_port_info); > > + } else { > > + VLOG_ERR("Fail to find %s mirror port (%d) info\n", > > + add_port?"add":"remove", mt_port_info->port_id); > > + } > > + } > > + } > > +} > > + > > +static struct mirror_tunnel_port_info* > > +netdev_dpdk_get_mt_port_info(uint16_t port_id) > > +{ > > + struct mirror_tunnel_port_info *mt_port_info; > > + > > + if (mirror_tunnel_head) { > > + mt_port_info = mirror_tunnel_head; > > + while (mt_port_info) { > > + if (mt_port_info->port_id == port_id) { > > + return mt_port_info; > > + } > > + mt_port_info = mt_port_info->next; > > + } > > + VLOG_ERR("Could not tunnel port with port-id %d\n", > > + port_id); > > + } > > + > > + mt_port_info = xmalloc(sizeof(struct mirror_tunnel_port_info)); > > + memset(mt_port_info, 0, sizeof(*mt_port_info)); > > + mt_port_info->port_id = port_id; > > + mt_port_info->next = NULL; > > + > > + return mt_port_info; > > +} > > + > > +static int > > +netdev_dpdk_addr_to_portid(const char *pci_addr_str, uint16_t > *port_id) > > +{ > > + struct rte_pci_device *pci_dev; > > + struct rte_pci_addr pci_addr; > > + int i; > > + > > + if (rte_pci_addr_parse(pci_addr_str, &pci_addr)) { > > + VLOG_ERR("Incorrect pci address %s\n", pci_addr_str); > > + return -1; > > + } > > + > > + for (i = 0; i < RTE_MAX_ETHPORTS; i++) { > > + struct rte_pci_addr *eth_pci_addr; > > + > > + if (!rte_eth_devices[i].device) { > > + continue; > > + } > > + > > + pci_dev = RTE_ETH_DEV_TO_PCI(&rte_eth_devices[i]); > > + if (!pci_dev) { > > + continue; > > + } > > + > > + eth_pci_addr = &pci_dev->addr; > > + > > + if (pci_addr.bus == eth_pci_addr->bus && > > + pci_addr.devid == eth_pci_addr->devid && > > + pci_addr.domain == eth_pci_addr->domain && > > + pci_addr.function == eth_pci_addr->function) { > > + *port_id = i; > > + > > + return 0; > > + } > > + } > > + > > + return -1; > > +} > > + > > +static int > > +netdev_dpdk_mt_open(uint16_t port_id, struct mirror_param *param) > > +{ > > + struct rte_eth_dev_info dev_info; > > + struct rte_eth_txconf txq_conf; > > + struct rte_eth_rxconf rxq_conf; > > + struct rte_mempool *pktbuf; > > + > > + struct mirror_tunnel_port_info *mt_info; > > + > > + uint16_t nb_rxd = NIC_PORT_DEFAULT_RXQ_SIZE; > > + uint16_t nb_txd = NIC_PORT_DEFAULT_TXQ_SIZE; > > + unsigned int i, num_queue; > > + > > + struct rte_eth_conf mt_port_conf = { > > + .rxmode = { > > + .split_hdr_size = 0, > > + }, > > + .txmode = { > > + .mq_mode = ETH_MQ_TX_NONE, > > + }, > > + }; > > + > > + mt_info = netdev_dpdk_get_mt_port_info(port_id); > > + if (!mt_info) { > > + return -1; > > + } > > + > > + if (mt_info->port_started) { > > + param->n_dst_queue = mt_info->num_queue; > > + param->dst_port_id = port_id; > > + param->locks = mt_info->locks; > > + mt_info->share_count++; > > + > > + return 0; > > + } > > + > > + rte_eth_dev_info_get(port_id, &dev_info); > > + num_queue = param->n_src_queue; > > + > > + /* A tunnel device doesn't require mbuf. It's used as > > + * hardware channel, transmit packets with > > + * mbuf provided by source. Need this mbuf creation > > + * to finish port initialization > > + */ > > + pktbuf = rte_pktmbuf_pool_create( > > + "tunnel-port", > > + (dev_info.rx_desc_lim.nb_max + dev_info.tx_desc_lim.nb_max), > > + RTE_MEMPOOL_CACHE_MAX_SIZE, 0, > RTE_MBUF_DEFAULT_BUF_SIZE, > > + rte_eth_dev_socket_id(port_id)); > > + > > + mt_port_conf.txmode.offloads |= DEV_TX_OFFLOAD_VLAN_INSERT; > > + if (dev_info.tx_offload_capa & DEV_TX_OFFLOAD_MBUF_FAST_FREE) { > > + mt_port_conf.txmode.offloads |= > DEV_TX_OFFLOAD_MBUF_FAST_FREE; > > + } > > + rte_eth_dev_configure(port_id, 1, num_queue, &mt_port_conf); > > + > > + /* init one Rx queue */ > > + rxq_conf = dev_info.default_rxconf; > > + rxq_conf.offloads = mt_port_conf.rxmode.offloads; > > + if (rte_eth_rx_queue_setup(port_id, 0, nb_rxd, > > + rte_eth_dev_socket_id(port_id), &rxq_conf, pktbuf) < 0) > > + VLOG_ERR("fail to setup tunnel port (%d) rx-queue\n", port_id); > > + > > + /* init # of Tx queue as part of mirror-tunnel setup */ > > + txq_conf = dev_info.default_txconf; > > + txq_conf.offloads |= mt_port_conf.txmode.offloads; > > + for (i = 0; i < num_queue; i++) { > > + if (rte_eth_tx_queue_setup(port_id, > > + i, nb_txd, > > + rte_eth_dev_socket_id(port_id), > > + &txq_conf) < 0) { > > + VLOG_ERR("fail to setup tunnel port (%d) tx queue #%u\n", > > + port_id, i); > > + return -1; > > + } > > + } > > + > > + if (rte_eth_dev_start(port_id) < 0) { > > + VLOG_ERR("fail to start tunnel port %d\n", port_id); > > + return -1; > > + } > > + > > + mt_info->locks = xmalloc(num_queue * sizeof(rte_spinlock_t)); > > + if (mt_info->locks) { > > + for (i = 0; i < mt_info->num_queue; i++) { > > + rte_spinlock_init(&mt_info->locks[i]); > > + } > > + } else { > > + return -1; > > + } > > + mt_info->share_count = 1; > > + mt_info->port_started = true; > > + mt_info->num_queue = num_queue; > > + > > + param->n_dst_queue = mt_info->num_queue; > > + param->dst_port_id = port_id; > > + param->locks = mt_info->locks; > > + > > + netdev_dpdk_update_mt_list(mt_info, true); > > + return 0; > > +} > > + > > +static void > > +netdev_dpdk_mt_close(uint16_t mirror_port_id) > > +{ > > + struct mirror_tunnel_port_info *mt_port_info = > > + netdev_dpdk_get_mt_port_info(mirror_port_id); > > + > > + if (mt_port_info) { > > + mt_port_info->share_count--; > > + if (!mt_port_info->share_count) { > > + netdev_dpdk_update_mt_list(mt_port_info, false); > > + rte_eth_dev_stop(mirror_port_id); > > + rte_eth_dev_close(mirror_port_id); > > + } > > + } > > +} > > + > > +/* vhost device mirror registration and un-registration routines */ > > +static int > > +netdev_vhost_register_mirror(struct netdev_dpdk *dev, > > + struct mirror_param *param, int tx_cb) > > +{ > > + uint32_t vid = netdev_dpdk_get_vid(dev); > > + struct mirror_offload_port *port_info = NULL; > > + struct mirror_param *data; > > + > > + netdev_mirror_data_proc(vid, mirror_data_add, tx_cb, param, > &port_info); > > + if (!port_info) { > > + return -1; > > + } > > + > > + data = tx_cb ? &port_info->tx : &port_info->rx; > > + netdev_mirror_cb_set(data, (uint16_t) vid, 0, tx_cb); > > + > > + if (tx_cb) { > > + dev->tx_cb = data->mirror_cb; > > + } else { > > + dev->rx_cb = data->mirror_cb; > > + } > > + > > + return 0; > > +} > > + > > +static int > > +netdev_vhost_unregister_mirror(struct netdev_dpdk *dev, int tx_cb) > > +{ > > + /* release both cb and pkt_buf */ > > + unsigned int i; > > + uint32_t vid = netdev_dpdk_get_vid(dev); > > + struct mirror_offload_port *port_info = NULL; > > + struct mirror_param *data; > > + > > + netdev_mirror_data_proc(vid, mirror_data_find, tx_cb, NULL, > &port_info); > > + if (port_info == NULL) { > > + VLOG_ERR("Source port %d is not on outstanding port mirror db\n", > vid); > > + return -1; > > + } > > + data = tx_cb ? &port_info->tx : &port_info->rx; > > + > > + if (tx_cb) { > > + dev->tx_cb = NULL; > > + } else { > > + dev->rx_cb = NULL; > > + } > > + > > + for (i = 0; i < data->n_src_queue; i++) { > > + free(data->mirror_cb[i].direct); > > + } > > + > > + free(data->mirror_cb); > > + > > + if (data->pkt_buf) { > > + free(data->pkt_buf); > > + data->pkt_buf = NULL; > > + } > > + > > + if (data->extra_data) { > > + free(data->extra_data); > > + data->extra_data = NULL; > > + data->extra_data_size = 0; > > + } > > + > > + netdev_mirror_data_proc(vid, mirror_data_rem, tx_cb, NULL, NULL); > > + return 0; > > +} > > + > > +static int > > +netdev_dpdk_mirror_offload(struct netdev *src, struct eth_addr > *flow_addr, > > + uint16_t vlan_id, char *mirror_tunnel_addr, > > + bool add_mirror, bool tx_cb) { > > + struct netdev_dpdk *src_dev = netdev_dpdk_cast(src); > > + bool eth_dev = src_dev->type == DPDK_DEV_ETH; > > + uint16_t mirror_port_id; > > + int status = 0; > > + > > + if (netdev_dpdk_addr_to_portid(mirror_tunnel_addr, > &mirror_port_id)) { > > + VLOG_ERR("Could not find tunnel port with BDF addr %s\n", > > + mirror_tunnel_addr); > > + return -1; > > + } > > + if (add_mirror) { > > + uint32_t i; > > + struct mirror_param data; > > + uint64_t mac_addr = 0; > > + > > + memset(&data, 0, sizeof(struct mirror_param)); > > + data.extra_data_size = 0; > > + data.extra_data = NULL; > > + data.mirror_type = mirror_port; > > + for (i = 0; i < 6; i++) { > > + mac_addr <<= 8; > > + mac_addr |= flow_addr->ea[6 - i - 1]; > > + } > > + if (mac_addr) { > > + data.mirror_type = mirror_flow_mac; > > + data.extra_data_size = sizeof(uint64_t); > > + data.extra_data = xmalloc(sizeof(uint64_t)); > > + memcpy(data.extra_data, &mac_addr, sizeof(uint64_t)); > > + } > > + data.dst_vlan_id = vlan_id; > > + data.n_src_queue = tx_cb?src->n_txq:src->n_rxq; > > + data.max_burst_size = NETDEV_MAX_BURST; > > + > > + if (netdev_dpdk_mt_open(mirror_port_id, &data)) { > > + VLOG_ERR("Fail to initialize mirror tunnel port %d\n", > > + mirror_port_id); > > + return -1; > > + } > > + > > + VLOG_INFO("register %s device with %s mirror-offload with" > > + "src-port:%d (%s) and output-port:%d (%s) vlan-id=%d flow-mac=" > > + "0x%" PRIx64 "\n", > > + eth_dev?"ethdev":"vhost", > > + tx_cb?"ingress":"egress", src_dev->port_id, > > + src->name, mirror_port_id, mirror_tunnel_addr, vlan_id, > > + (uint64_t)__builtin_bswap64(mac_addr)); > > + > > + if (eth_dev) { > > + status = netdev_eth_register_mirror(src_dev->port_id, &data, > > + tx_cb); > > + } else { > > + status = netdev_vhost_register_mirror(src_dev, &data, tx_cb); > > + } > > + } else { > > + VLOG_INFO("unregister %s device with %s mirror-offload with" > > + " src-port:%d(%s)\n", > > + eth_dev?"ethdev":"vhost", > > + tx_cb?"ingress":"egress", src_dev->port_id, > > + src->name); > > + > > + if (eth_dev) { > > + status = netdev_eth_unregister_mirror(src_dev->port_id, tx_cb); > > + } else { > > + status = netdev_vhost_unregister_mirror(src_dev, tx_cb); > > + } > > + > > + netdev_dpdk_mt_close(mirror_port_id); > > + } > > + > > + return status; > > +} > > + > > #define NETDEV_DPDK_CLASS_COMMON \ > > .is_pmd = true, \ > > .alloc = netdev_dpdk_alloc, \ > > @@ -5340,6 +5734,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 = { > > @@ -5355,6 +5750,7 @@ static const struct netdev_class dpdk_vhost_class > = { > > .reconfigure = netdev_dpdk_vhost_reconfigure, > > .rxq_recv = netdev_dpdk_vhost_rxq_recv, > > .rxq_enabled = netdev_dpdk_vhost_rxq_enabled, > > + .mirror_offload = netdev_dpdk_mirror_offload, > > }; > > > > static const struct netdev_class dpdk_vhost_client_class = { > > @@ -5371,6 +5767,7 @@ static const struct netdev_class > dpdk_vhost_client_class = { > > .reconfigure = netdev_dpdk_vhost_client_reconfigure, > > .rxq_recv = netdev_dpdk_vhost_rxq_recv, > > .rxq_enabled = netdev_dpdk_vhost_rxq_enabled, > > + .mirror_offload = ne²tdev_dpdk_mirror_offload, > > }; > > > > void > > diff --git a/lib/netdev-provider.h b/lib/netdev-provider.h > > index 73dce2fca..dab278dcd 100644 > > --- a/lib/netdev-provider.h > > +++ b/lib/netdev-provider.h > > @@ -834,6 +834,22 @@ 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. > > + * 'vlan_id': vlag to be added to the mirrored packets. > > + * 'mt_pci_addr': mirror tunnel pcie address. > > + * 'add_mirror': true: configure a mirror traffic; false: remove mirror > > + * 'ingress': true: mirror 'src' netdev Rx traffic; false: mirror > > + * 'src' netdev Tx traffic. > > + */ > > + int (*mirror_offload)(struct netdev *src, struct eth_addr *flow_addr, > > + uint16_t vlan_id, char *mt_pci_addr, > > + bool add_mirror, bool ingress); > > + > > }; > > > > int netdev_register_provider(const struct netdev_class *); > > diff --git a/lib/netdev.c b/lib/netdev.c > > index 91e91955c..464c2f8fe 100644 > > --- a/lib/netdev.c > > +++ b/lib/netdev.c > > @@ -69,6 +69,8 @@ COVERAGE_DEFINE(netdev_get_stats); > > COVERAGE_DEFINE(netdev_send_prepare_drops); > > COVERAGE_DEFINE(netdev_push_header_drops); > > > > +#define MIRROR_DB_INIT_SIZE 8 > > + > > struct netdev_saved_flags { > > struct netdev *netdev; > > struct ovs_list node; /* In struct netdev's > > saved_flags_list. */ > > @@ -2297,3 +2299,387 @@ 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); > > + > > +/* > > + * Re-size mirror_db when it's out of space. > > + * Always double the buffer when it's needed > > + */ > > +static int > > +netdev_mirror_db_resize(struct netdev_mirror_offload_item > ***old_db, > > + int *old_db_size) > > +{ > > + struct netdev_mirror_offload_item **new_db; > > + int cur_size = *old_db_size; > > + int new_size; > > + > > + if (!cur_size) { > > + new_size = MIRROR_DB_INIT_SIZE; > > + } else { > > + new_size = 2 * cur_size; > > + } > > + > > + new_db = xzalloc(sizeof(struct netdev_mirror_offload_item *) * > new_size); > > + > > + if (!new_db) { > > + VLOG_ERR("Out of memory!!!"); > > + return -1; > > + } > > + memset(new_db, 0, sizeof(struct netdev_mirror_offload_item *) * > new_size); > > + > > + if (cur_size) { > > + int i; > > + > > + for (i = 0; i < cur_size; i++) { > > + new_db[i] = (*old_db)[i]; > > + } > > + free(*old_db); > > + } > > + > > + *old_db = new_db; > > + *old_db_size = new_size; > > + > > + return 0; > > +} > > + > > +static void > > +netdev_free_mirror_offload(struct netdev_mirror_offload_item > *offload) > > +{ > > + if (!offload) { > > + return; > > + } > > + > > + if (offload->info.src) { > > + free(offload->info.src); > > + } > > + if (offload->info.dst) { > > + free(offload->info.dst); > > + } > > + if (offload->info.flow_dst_mac) { > > + free(offload->info.flow_dst_mac); > > + } > > + if (offload->info.flow_src_mac) { > > + free(offload->info.flow_src_mac); > > + } > > + if (offload->info.output_src_tags) { > > + free(offload->info.output_src_tags); > > + } > > + if (offload->info.output_dst_tags) { > > + free(offload->info.output_dst_tags); > > + } > > + if (offload->info.name) { > > + free(offload->info.name); > > + } > > + if (offload->info.mirror_tunnel_addr) { > > + free(offload->info.mirror_tunnel_addr); > > + } > > + > > + free(offload); > > +} > > + > > +static struct > > +netdev_mirror_offload_item * > > +netdev_alloc_mirror_offload(struct mirror_offload_info *info) > > +{ > > + struct netdev_mirror_offload_item *offload; > > + int i; > > + > > + offload = xzalloc(sizeof(*offload)); > > + memcpy(&offload->info, info, sizeof(struct mirror_offload_info)); > > + > > + if (info->name) { > > + offload->info.name = xzalloc(strlen(info->name) + 1); > > + if (offload->info.name) { > > + ovs_strzcpy(offload->info.name, info->name, > > strlen(info->name)); > > + } > > + } > > + > > + if (info->mirror_tunnel_addr) { > > + offload->info.mirror_tunnel_addr = > > + xzalloc(strlen(info->mirror_tunnel_addr) + 1); > > + if (offload->info.mirror_tunnel_addr) { > > + ovs_strzcpy(offload->info.mirror_tunnel_addr, > > + info->mirror_tunnel_addr, > > + strlen(info->mirror_tunnel_addr)); > > + } > > + } > > + > > + /* only add_mirror request include valid configuration */ > > + if (info->n_src_port) { > > + offload->info.src = xzalloc(sizeof(struct netdev > > *)*info->n_src_port); > > + offload->info.flow_dst_mac = xzalloc(sizeof(struct eth_addr)* > > + info->n_src_port); > > + offload->info.output_src_tags = xzalloc(sizeof(uint16_t)* > > + info->n_src_port); > > + if (!offload->info.src || !offload->info.flow_dst_mac || > > + !offload->info.output_src_tags) { > > + VLOG_ERR("Out of memory!!!"); > > + netdev_free_mirror_offload(offload); > > + return NULL; > > + } > > + > > + for (i = 0; i < info->n_src_port; i++) { > > + offload->info.src[i] = info->src[i]; > > + offload->info.output_src_tags[i] = info->output_src_tags[i]; > > + memcpy(&offload->info.flow_dst_mac[i], &info->flow_dst_mac[i], > > + sizeof(struct eth_addr)); > > + } > > + } > > + > > + if (info->n_dst_port) { > > + offload->info.dst = xzalloc(sizeof(struct netdev > > *)*info->n_dst_port); > > + offload->info.flow_src_mac = xzalloc(sizeof(struct eth_addr)* > > + info->n_dst_port); > > + offload->info.output_dst_tags = xzalloc(sizeof(uint16_t)* > > + info->n_dst_port); > > + if (!offload->info.dst || !offload->info.flow_src_mac || > > + !offload->info.output_dst_tags) { > > + VLOG_ERR("Out of memory!!!"); > > + netdev_free_mirror_offload(offload); > > + return NULL; > > + } > > + > > + for (i = 0; i < info->n_dst_port; i++) { > > + offload->info.dst[i] = info->dst[i]; > > + offload->info.output_dst_tags[i] = info->output_dst_tags[i]; > > + memcpy(&offload->info.flow_src_mac[i], &info->flow_src_mac[i], > > + sizeof(struct eth_addr)); > > + } > > + } > > + > > + return 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); > > +} > > + > > +void > > +netdev_mirror_offload_put(struct mirror_offload_info *info) > > +{ > > + struct netdev_mirror_offload_item *offload; > > + /* only support tunnel port for traffic mirroring */ > > + if (info->add_mirror && !info->mirror_tunnel_addr) { > > + return; > > + } > > + > > + 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); > > +} > > + > > +static int > > +netdev_mirror_offload_configue(struct mirror_offload_info *info, > > + bool add_mirror) > > +{ > > + 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_src_tags[i], > > + info->mirror_tunnel_addr, > > + add_mirror, false); > > + if (ret) { > > + VLOG_ERR("Fail to %s mirror-offload" > > + " configuration %s\n", > > + add_mirror ? "add" : "remove", > > + info->name); > > + 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_dst_tags[i], > > + info->mirror_tunnel_addr, > > + add_mirror, true); > > + if (ret) { > > + VLOG_ERR("Fail to %s mirror-offload" > > + " configuration %s\n", > > + add_mirror ? "add" : "remove", > > + info->name); > > + return ret; > > + } > > + } else { > > + un_support_count++; > > + } > > + } > > + } > > + > > + return un_support_count; > > +} > > + > > +static void * > > +netdev_mirror_offload_main(void *data OVS_UNUSED) > > +{ > > + struct netdev_mirror_offload_item *offload; > > + struct mirror_offload_info *info; > > + struct ovs_list *list; > > + struct netdev_mirror_offload_item **offload_db = NULL; > > + int offload_used_count = 0; > > + int offload_db_size = 0; > > + int ret, i, ind; > > + > > + /* continue polling to check if there is an outstanding request */ > > + 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); > > + > > + if (!offload_db_size && > > + netdev_mirror_db_resize(&offload_db, &offload_db_size)){ > > + return NULL; > > + } > > + > > + ind = offload_db_size; > > + for (i = 0; i < offload_db_size; i++) { > > + if (offload_db[i] && > > + !strncmp(offload_db[i]->info.name, offload->info.name, > > + strlen(offload->info.name) + 1)) { > > + ind = i; > > + break; > > + } > > + } > > + > > + if (!offload->info.add_mirror) { > > + /* remove mirror offload setup */ > > + if (ind == offload_db_size) { > > + VLOG_WARN("Mirror offload remove configuration, %s, " > > + "not found; clear mirror offload operation" > > + " aborted\n", offload->info.name); > > + continue; > > + } > > + } else { > > + /* add mirror offload */ > > + if (ind < offload_db_size) { > > + netdev_free_mirror_offload(offload); > > + VLOG_WARN("Attempt adding an existing mirror-offload " > > + "configuration; request aborted\n"); > > + continue; > > + } > > + > > + if (offload_used_count == offload_db_size && > > + netdev_mirror_db_resize(&offload_db, &offload_db_size)) { > > + return NULL; > > + } > > + } > > + > > + info = offload->info.add_mirror ? &offload->info : > > + &offload_db[ind]->info; > > + ret = netdev_mirror_offload_configue(info, offload- > >info.add_mirror); > > + > > + 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; > > + } else { > > + VLOG_INFO("Succeed %s mirror-offload configuration: %s", > > + offload->info.add_mirror ? "adding" : "removing", > > + offload->info.name); > > + } > > + > > + if (offload->info.add_mirror) { > > + for (i = 0; i < offload_db_size; i++) { > > + if (offload_db[i] == NULL) { > > + offload_db[i] = offload; > > + offload_used_count++; > > + break; > > + } > > + } > > + } else { > > + /* remove the prior "add" request */ > > + netdev_free_mirror_offload(offload_db[ind]); > > + offload_db[ind] = NULL; > > + > > + /* remove the current("remove") request */ > > + netdev_free_mirror_offload(offload); > > + offload_used_count--; > > + } > > + > > + /* free db when the used count drop to 0 */ > > + if (!offload_used_count) { > > + free(offload_db); > > + offload_db = NULL; > > + offload_db_size = 0; > > + } > > + } > > + > > + /* clean up memory */ > > + for (i = 0; i < offload_db_size; i++) { > > + if (offload_db[i]) { > > + netdev_free_mirror_offload(offload_db[i]); > > + } > > + } > > + if (offload_db) { > > + free(offload_db); > > + } > > + > > + return NULL; > > +} > > diff --git a/lib/netdev.h b/lib/netdev.h > > index b705a9e56..cce042fc7 100644 > > --- a/lib/netdev.h > > +++ b/lib/netdev.h > > @@ -201,6 +201,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; > > + struct netdev **dst; > > + int n_src_port; > > + int n_dst_port; > > + struct eth_addr *flow_src_mac; > > + struct eth_addr *flow_dst_mac; > > + uint16_t *output_src_tags; > > + uint16_t *output_dst_tags; > > + bool add_mirror; > > + char *mirror_tunnel_addr; > > + char *name; > > +}; > > +void 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 dccb11741..ff6e9e625 100644 > > --- a/tests/ovs-vsctl.at > > +++ b/tests/ovs-vsctl.at > > @@ -1364,7 +1364,9 @@ _uuid : <1> > > name : eth1 > > _uuid : <2> > > name : mymirror > > +output_dst_vlan : [] > > output_port : <1> > > +output_src_vlan : [] > > output_vlan : [] > > select_all : false > > select_dst_port : [<0>] > > diff --git a/vswitchd/bridge.c b/vswitchd/bridge.c > > index 5ed7e8234..7b7603513 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_member_settings *); > > static bool iface_create(struct bridge *, const struct ovsrec_interface *, > > @@ -423,6 +427,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. */ > > > > @@ -5055,14 +5088,228 @@ 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 void > > +release_mirror_offload_info(struct mirror_offload_info *info) > > +{ > > + if (info->src) { > > + free(info->src); > > + } > > + if (info->dst) { > > + free(info->dst); > > + } > > + if (info->flow_dst_mac) { > > + free(info->flow_dst_mac); > > + } > > + if (info->flow_src_mac) { > > + free(info->flow_src_mac); > > + } > > + if (info->output_src_tags) { > > + free(info->output_src_tags); > > + } > > + if (info->output_dst_tags) { > > + free(info->output_dst_tags); > > + } > > + if (info->name) { > > + free(info->name); > > + } > > + if (info->mirror_tunnel_addr) { > > + free(info->mirror_tunnel_addr); > > + } > > +} > > + > > +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; > > + int i; > > + > > + if (m->name) { > > + info->name = xmalloc(strlen(m->name) + 1); > > + ovs_strzcpy(info->name, m->name, strlen(m->name)); > > + } > > + > > + if (cfg->mirror_tunnel_addr) { > > + info->mirror_tunnel_addr = xmalloc(strlen(cfg->mirror_tunnel_addr) > > + + 1); > > + ovs_strzcpy(info->mirror_tunnel_addr, cfg->mirror_tunnel_addr, > > + strlen(cfg->mirror_tunnel_addr)); > > + } else { > > + VLOG_ERR("mirror-offload configuration fails because" > > + " lack of tunnel device\n"); > > + return -1; > > + } > > + > > + /* source port */ > > + info->n_src_port = cfg->n_select_src_port; > > + if (info->n_src_port) { > > + info->src = xmalloc(sizeof(struct netdev *)*info->n_src_port); > > + info->flow_dst_mac = xmalloc(sizeof(struct eth_addr)* > > + info->n_src_port); > > + if (info->n_src_port != cfg->n_output_src_vlan) { > > + VLOG_ERR("src port count:%d ouput src vlan count:%lu", > > + info->n_src_port, (unsigned long) cfg->n_output_src_vlan); > > + return -1; > > + } > > + info->output_src_tags = xmalloc(sizeof(uint16_t)*info->n_src_port); > > + } > > + > > + if (info->n_src_port) { > > + /* find netdev instance for each 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)* > > + info->n_src_port); > > + > > + /* > > + * for source port, flow is separated by > > + * different dst mac addr > > + */ > > + 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]); > > + } > > + } > > + > > + 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] & 0xFFF; > > + } > > + } > > + } > > + > > + /* dst ports */ > > + info->n_dst_port = cfg->n_select_dst_port; > > + if (info->n_dst_port) { > > + info->dst = xmalloc(sizeof(struct netdev *)*info->n_dst_port); > > + info->flow_src_mac = xmalloc(sizeof(struct eth_addr)* > > + info->n_dst_port); > > + if (info->n_dst_port != cfg->n_output_dst_vlan) { > > + VLOG_ERR("dst port count:%d ouput dst vlan count:%lu\n", > > + info->n_dst_port, (unsigned long) cfg->n_output_dst_vlan); > > + return -1; > > + } > > + info->output_dst_tags = xmalloc(sizeof(uint16_t)*info->n_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)* > > + info->n_dst_port); > > + > > + /* > > + * for destination port, flow is separated by > > + * different src mac addr > > + */ > > + 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_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] & 0xFFF; > > + } > > + } > > + } > > + > > + VLOG_INFO("sucess creating mirror-offload(%s): with %d src-port" > > + " streams %d dst-port streams to tunnel %s\n", > > + cfg->name, info->n_src_port, info->n_dst_port, > > + info->mirror_tunnel_addr?info->mirror_tunnel_addr:"none"); > > + return 0; > > +} > > + > > +static void > > +mirror_offload_destroy(struct mirror *m) > > +{ > > + struct mirror_offload_info info; > > + > > + memset(&info, 0, sizeof(struct mirror_offload_info)); > > + info.add_mirror = false; > > + if (m->name) { > > + info.name = xmalloc(strlen(m->name) + 1); > > + if (info.name) { > > + ovs_strzcpy(info.name, m->name, strlen(m->name)); > > + } > > + } > > + > > + netdev_mirror_offload_put(&info); > > + if (info.name) { > > + free(info.name); > > + } > > + if (info.mirror_tunnel_addr) { > > + free(info.mirror_tunnel_addr); > > + } > > +} > > + > > 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); > > @@ -5094,12 +5341,32 @@ mirror_collect_ports(struct mirror *m, > > *n_out_portsp = n_out_ports; > > } > > > > +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; > > + if (set_mirror_offload_info(m, &info)) { > > + release_mirror_offload_info(&info); > > + return false; > > + } > > + > > + netdev_mirror_offload_put(&info); > > + release_mirror_offload_info(&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 0666c8c76..4a1a34a1f 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": "4051567316 27206", > > "tables": { > > "Open_vSwitch": { > > "columns": { > > @@ -418,8 +418,18 @@ > > "columns": { > > "name": { > > "type": "string"}, > > + "mirror_tunnel_addr": { > > + "type": "string"}, > > "select_all": { > > "type": "boolean"}, > > + "mirror_offload": { > > + "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 +450,16 @@ > > "refTable": "Port", > > "refType": "weak"}, > > "min": 0, "max": 1}}, > > + "output_src_vlan": { > > + "type": {"key": {"type": "integer", > > + "minInteger": 0, > > + "maxInteger": 4294967295}, > > + "min": 0, "max": 4096}}, > > + "output_dst_vlan": { > > + "type": {"key": {"type": "integer", > > + "minInteger": 0, > > + "maxInteger": 4294967295}, > > + "min": 0, "max": 4096}}, > > "output_vlan": { > > "type": {"key": {"type": "integer", > > "minInteger": 1, > > diff --git a/vswitchd/vswitch.xml b/vswitchd/vswitch.xml > > index 4597a215d..fd2049a7f 100644 > > --- a/vswitchd/vswitch.xml > > +++ b/vswitchd/vswitch.xml > > @@ -4869,11 +4869,35 @@ ovs-vsctl add-port br0 p0 -- set Interface p0 > type=patch options:peer=p1 \ > > selected VLANs. > > </p> > > > > + <column name="mirror_tunnel_addr"> > > + BDF string of the tunnel device on which mirrored traffic will be > > + transmitted. > > + </column> > > + > > <column name="select_all"> > > If true, every packet arriving or departing on any port is > > selected for mirroring. > > </column> > > > > + <column name="mirror_offload"> > > + If true, a hw-assisted port mirroring is configured instead > > + default mirroring. > > + </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> > > @@ -4955,6 +4979,32 @@ 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, regardless 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, regardless 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 cab 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"/> > >
_______________________________________________ dev mailing list [email protected] https://mail.openvswitch.org/mailman/listinfo/ovs-dev
