[ovs-dev] [PATCH RFC 2/2] netdev-dpdk: Add vHost User PMD

Loftus, Ciara ciara.loftus at intel.com
Thu Oct 20 16:33:13 UTC 2016


> 
> Sorry, CC mail-list
> 
> On 20.10.2016 17:20, Ilya Maximets wrote:
> > Not a complete review.
> > Few comments inline.

Thanks for the review. Comments inline.

Thanks,
Ciara

> >
> > Best regards, Ilya Maximets.
> >
> > On 14.10.2016 17:08, Ciara Loftus wrote:
> >> The vHost PMD allows vHost User ports to be controlled by the
> >> librte_ether API, like physical 'dpdk' ports and IVSHM 'dpdkr' ports.
> >> This commit integrates this PMD into OVS and removes direct calls to the
> >> librte_vhost DPDK library.
> >>
> >> This commit requires DPDK v16.11 functionality that isn't available in
> >> previous releases, and thus breaks compatibility with such releases.
> >>
> >> Signed-off-by: Ciara Loftus <ciara.loftus at intel.com>
> >> ---
> >>  INSTALL.DPDK.md   |   10 +
> >>  NEWS              |    2 +
> >>  lib/netdev-dpdk.c | 1101 +++++++++++++++++++++-------------------------
> -------
> >>  3 files changed, 447 insertions(+), 666 deletions(-)
> >>
> >> diff --git a/INSTALL.DPDK.md b/INSTALL.DPDK.md
> >> index 2f0ae9a..5678e41 100644
> >> --- a/INSTALL.DPDK.md
> >> +++ b/INSTALL.DPDK.md
> >> @@ -603,6 +603,16 @@ can be found in [Vhost Walkthrough].
> >>
> >>      http://dpdk.org/doc/guides/rel_notes/release_16.11.html
> >>
> >> +  - dpdk, dpdkr and dpdkvhostuser ports are 'eth' type ports in the
> context of
> >> +    DPDK as they are all managed by the rte_ether API. This means that
> they
> >> +    adhere to the DPDK configuration option
> CONFIG_RTE_MAX_ETHPORTS which by
> >> +    default is set to 32. This means by default the combined total number
> of
> >> +    dpdk, dpdkr and dpdkvhostuser ports allowable in OVS with DPDK is
> 32. This
> >> +    value can be changed if desired by modifying the configuration file in
> >> +    DPDK, or by overriding the default value on the command line when
> building
> >> +    DPDK. eg.
> >> +
> >> +        `make install CONFIG_RTE_MAX_ETHPORTS=64`
> >>
> >>  Bug Reporting:
> >>  --------------
> >> diff --git a/NEWS b/NEWS
> >> index ab74fcd..6e47683 100644
> >> --- a/NEWS
> >> +++ b/NEWS
> >> @@ -135,6 +135,8 @@ v2.6.0 - 27 Sep 2016
> >>       * Remove dpdkvhostcuse port type.
> >>       * OVS client mode for vHost and vHost reconnect (Requires QEMU 2.7)
> >>       * 'dpdkvhostuserclient' port type.
> >> +     * vHost PMD integration brings vhost-user ports under control of the
> >> +       rte_ether DPDK API.
> >>     - Increase number of registers to 16.
> >>     - ovs-benchmark: This utility has been removed due to lack of use and
> >>       bitrot.
> >> diff --git a/lib/netdev-dpdk.c b/lib/netdev-dpdk.c
> >> index 7c1523e..d0b80a7 100644
> >> --- a/lib/netdev-dpdk.c
> >> +++ b/lib/netdev-dpdk.c
> >> @@ -27,6 +27,7 @@
> >>  #include <rte_cycles.h>
> >>  #include <rte_errno.h>
> >>  #include <rte_eth_ring.h>
> >> +#include <rte_eth_vhost.h>
> >>  #include <rte_ethdev.h>
> >>  #include <rte_malloc.h>
> >>  #include <rte_mbuf.h>
> >> @@ -122,6 +123,7 @@ BUILD_ASSERT_DECL((MAX_NB_MBUF /
> ROUND_DOWN_POW2(MAX_NB_MBUF/MIN_NB_MBUF))
> >>  #define XSTAT_RX_BROADCAST_PACKETS       "rx_broadcast_packets"
> >>  #define XSTAT_TX_BROADCAST_PACKETS       "tx_broadcast_packets"
> >>  #define XSTAT_RX_UNDERSIZED_ERRORS       "rx_undersized_errors"
> >> +#define XSTAT_RX_UNDERSIZE_PACKETS       "rx_undersize_packets"
> >>  #define XSTAT_RX_OVERSIZE_ERRORS         "rx_oversize_errors"
> >>  #define XSTAT_RX_FRAGMENTED_ERRORS       "rx_fragmented_errors"
> >>  #define XSTAT_RX_JABBER_ERRORS           "rx_jabber_errors"
> >> @@ -143,6 +145,9 @@ BUILD_ASSERT_DECL((MAX_NB_MBUF /
> ROUND_DOWN_POW2(MAX_NB_MBUF/MIN_NB_MBUF))
> >>  #define VHOST_ENQ_RETRY_NUM 8
> >>  #define IF_NAME_SZ (PATH_MAX > IFNAMSIZ ? PATH_MAX : IFNAMSIZ)
> >>
> >> +/* Array that tracks the used & unused vHost user driver IDs */
> >> +static unsigned int vhost_drv_ids[RTE_MAX_ETHPORTS];
> >> +
> >>  static const struct rte_eth_conf port_conf = {
> >>      .rxmode = {
> >>          .mq_mode = ETH_MQ_RX_RSS,
> >> @@ -343,15 +348,12 @@ struct netdev_dpdk {
> >>      struct rte_eth_link link;
> >>      int link_reset_cnt;
> >>
> >> -    /* virtio identifier for vhost devices */
> >> -    ovsrcu_index vid;
> >> -
> >> -    /* True if vHost device is 'up' and has been reconfigured at least once
> */
> >> -    bool vhost_reconfigured;
> >> -
> >>      /* Identifier used to distinguish vhost devices from each other. */
> >>      char vhost_id[PATH_MAX];
> >>
> >> +    /* ID of vhost user port given to the PMD driver */
> >> +    unsigned int vhost_pmd_id;
> >> +
> >>      /* In dpdk_list. */
> >>      struct ovs_list list_node OVS_GUARDED_BY(dpdk_mutex);
> >>
> >> @@ -392,16 +394,25 @@ struct netdev_rxq_dpdk {
> >>  };
> >>
> >>  static int netdev_dpdk_construct(struct netdev *);
> >> -
> >> -int netdev_dpdk_get_vid(const struct netdev_dpdk *dev);
> >> +static int netdev_dpdk_vhost_construct(struct netdev *);
> >> +static int netdev_dpdk_vhost_client_construct(struct netdev *);
> >>
> >>  struct ingress_policer *
> >>  netdev_dpdk_get_ingress_policer(const struct netdev_dpdk *dev);
> >>
> >> +static void link_status_changed_callback(uint8_t port_id,
> >> +        enum rte_eth_event_type type, void *param);
> >> +static void vring_state_changed_callback(uint8_t port_id,
> >> +        enum rte_eth_event_type type, void *param);
> >> +static void netdev_dpdk_remap_txqs(struct netdev_dpdk *dev);
> >> +static void netdev_dpdk_txq_map_clear(struct netdev_dpdk *dev);
> >> +
> >>  static bool
> >> -is_dpdk_class(const struct netdev_class *class)
> >> +is_dpdk_eth_class(const struct netdev_class *class)
> >>  {
> >> -    return class->construct == netdev_dpdk_construct;
> >> +    return ((class->construct == netdev_dpdk_construct) ||
> >> +            (class->construct == netdev_dpdk_vhost_construct) ||
> >> +            (class->construct == netdev_dpdk_vhost_client_construct));
> >>  }
> >>
> >>  /* DPDK NIC drivers allocate RX buffers at a particular granularity, typically
> >> @@ -681,8 +692,13 @@ dpdk_eth_dev_queue_setup(struct
> netdev_dpdk *dev, int n_rxq, int n_txq)
> >>              continue;
> >>          }
> >>
> >> -        dev->up.n_rxq = n_rxq;
> >> -        dev->up.n_txq = n_txq;
> >> +        /* Only set n_*xq for physical devices. vHost User devices will set
> >> +         * this value correctly using info from the virtio backend.
> >> +         */
> >> +        if (dev->type == DPDK_DEV_ETH) {
> >> +            dev->up.n_rxq = n_rxq;
> >> +            dev->up.n_txq = n_txq;
> >> +        }
> >>
> >>          return 0;
> >>      }
> >> @@ -714,8 +730,16 @@ dpdk_eth_dev_init(struct netdev_dpdk *dev)
> >>
> >>      rte_eth_dev_info_get(dev->port_id, &info);
> >>
> >> -    n_rxq = MIN(info.max_rx_queues, dev->up.n_rxq);
> >> -    n_txq = MIN(info.max_tx_queues, dev->up.n_txq);
> >> +    if (dev->type == DPDK_DEV_VHOST) {
> >> +        /* We don't know how many queues QEMU will use so we need to
> set up
> >> +         * the max. If we configure less than what QEMU configures, those
> >> +         * additional queues will not be available to us. */
> >> +        n_rxq = MIN(OVS_VHOST_MAX_QUEUE_NUM,
> RTE_MAX_QUEUES_PER_PORT);
> >> +        n_txq = MIN(OVS_VHOST_MAX_QUEUE_NUM,
> RTE_MAX_QUEUES_PER_PORT);
> >> +    } else {
> >> +        n_rxq = MIN(info.max_rx_queues, dev->up.n_rxq);
> >> +        n_txq = MIN(info.max_tx_queues, dev->up.n_txq);
> >> +    }
> >>
> >>      diag = dpdk_eth_dev_queue_setup(dev, n_rxq, n_txq);
> >>      if (diag) {
> >> @@ -794,6 +818,93 @@ netdev_dpdk_alloc_txq(unsigned int n_txqs)
> >>      return txqs;
> >>  }
> >>
> >> +void
> >> +link_status_changed_callback(uint8_t port_id,
> >> +                             enum rte_eth_event_type type OVS_UNUSED,
> >> +                             void *param OVS_UNUSED)
> >> +{
> >> +    struct netdev_dpdk *dev;
> >> +    int newnode = -1;
> >> +
> >> +    ovs_mutex_lock(&dpdk_mutex);
> >> +    LIST_FOR_EACH (dev, list_node, &dpdk_list) {
> >> +        if (port_id == dev->port_id) {
> >> +            ovs_mutex_lock(&dev->mutex);
> >> +            check_link_status(dev);
> >> +            if (dev->link.link_status == ETH_LINK_UP) {
> >> +                /* Device brought up */
> >> +                /* Get queue information */
> >> +                int vid = rte_eth_vhost_get_vid_from_port_id(dev->port_id);
> >> +                uint32_t qp_num = rte_vhost_get_queue_num(vid);
> >> +                if (qp_num <= 0) {
> >> +                    qp_num = dev->requested_n_rxq;
> >> +                }
> >> +                /* Get NUMA information */
> >> +                newnode = rte_eth_dev_socket_id(dev->port_id);
> >> +                if (newnode == -1) {
> >> +                    newnode = dev->socket_id;
> >> +                }
> >> +                if (dev->requested_n_txq != qp_num
> >> +                                || dev->requested_n_rxq != qp_num
> >> +                                || dev->requested_socket_id != newnode) {
> >> +                    dev->requested_socket_id = newnode;
> >> +                    dev->requested_n_rxq = qp_num;
> >> +                    dev->requested_n_txq = qp_num;
> >> +                    netdev_request_reconfigure(&dev->up);
> >> +                }
> >> +                netdev_change_seq_changed(&dev->up);
> >> +                VLOG_INFO("vHost Device '%s' has been added on numa node
> %i",
> >> +                          dev->vhost_id, newnode);
> >> +            } else {
> >> +                /* Device brought down */
> >> +                /* Clear tx/rx queue settings. */
> >> +                netdev_dpdk_txq_map_clear(dev);
> >> +                netdev_change_seq_changed(&dev->up);
> >> +                VLOG_INFO("vHost Device '%s' has been removed", dev-
> >vhost_id);
> >> +            }
> >> +            ovs_mutex_unlock(&dev->mutex);
> >> +            break;
> >> +        }
> >> +    }
> >> +
> >> +    ovs_mutex_unlock(&dpdk_mutex);
> >> +
> >> +    return;
> >> +}
> >> +
> >> +void
> >> +vring_state_changed_callback(uint8_t port_id,
> >> +                             enum rte_eth_event_type type OVS_UNUSED,
> >> +                             void *param OVS_UNUSED)
> >> +{
> >> +    struct netdev_dpdk *dev;
> >> +    struct rte_eth_vhost_queue_event event;
> >> +    int err = 0;
> >> +
> >> +    err = rte_eth_vhost_get_queue_event(port_id, &event);
> >> +    if (err || event.rx) {
> >> +        return;
> >> +    }
> >> +
> >> +    ovs_mutex_lock(&dpdk_mutex);
> >> +    LIST_FOR_EACH (dev, list_node, &dpdk_list) {
> >> +        if (port_id == dev->port_id) {
> >> +            ovs_mutex_lock(&dev->mutex);
> >> +            if (event.enable) {
> >> +                dev->tx_q[event.queue_id].map = event.queue_id;
> >> +            } else {
> >> +                dev->tx_q[event.queue_id].map =
> OVS_VHOST_QUEUE_DISABLED;
> >> +            }
> >> +            netdev_dpdk_remap_txqs(dev);
> >> +            ovs_mutex_unlock(&dev->mutex);
> >> +            break;
> >> +        }
> >> +    }
> >> +    ovs_mutex_unlock(&dpdk_mutex);
> >> +
> >> +    return;
> >> +}
> >> +
> >>  static int
> >>  netdev_dpdk_init(struct netdev *netdev, unsigned int port_no,
> >>                   enum dpdk_dev_type type)
> >> @@ -802,6 +913,7 @@ netdev_dpdk_init(struct netdev *netdev,
> unsigned int port_no,
> >>      struct netdev_dpdk *dev = netdev_dpdk_cast(netdev);
> >>      int sid;
> >>      int err = 0;
> >> +    unsigned int nr_q = 0;
> >>
> >>      ovs_mutex_init(&dev->mutex);
> >>      ovs_mutex_lock(&dev->mutex);
> >> @@ -811,11 +923,7 @@ netdev_dpdk_init(struct netdev *netdev,
> unsigned int port_no,
> >>      /* If the 'sid' is negative, it means that the kernel fails
> >>       * to obtain the pci numa info.  In that situation, always
> >>       * use 'SOCKET0'. */
> >> -    if (type == DPDK_DEV_ETH) {
> >> -        sid = rte_eth_dev_socket_id(port_no);
> >> -    } else {
> >> -        sid = rte_lcore_to_socket_id(rte_get_master_lcore());
> >
> > Why this branch was deleted? Without this vhostuserclient ports always
> will
> > use SOCKET0 in VHOST_NUMA disabled even if whole app works on
> SOCKET1.

Ok - I've put this back in.

> >
> >> -    }
> >> +    sid = rte_eth_dev_socket_id(port_no);
> >>
> >>      dev->socket_id = sid < 0 ? SOCKET0 : sid;
> >>      dev->requested_socket_id = dev->socket_id;
> >> @@ -824,8 +932,7 @@ netdev_dpdk_init(struct netdev *netdev,
> unsigned int port_no,
> >>      dev->flags = 0;
> >>      dev->requested_mtu = dev->mtu = ETHER_MTU;
> >>      dev->max_packet_len = MTU_TO_FRAME_LEN(dev->mtu);
> >> -    ovsrcu_index_init(&dev->vid, -1);
> >> -    dev->vhost_reconfigured = false;
> >> +    dev->vhost_driver_flags &= ~RTE_VHOST_USER_CLIENT;
> >>
> >>      err = netdev_dpdk_mempool_configure(dev);
> >>      if (err) {
> >> @@ -849,16 +956,21 @@ netdev_dpdk_init(struct netdev *netdev,
> unsigned int port_no,
> >>
> >>      /* Initialize the flow control to NULL */
> >>      memset(&dev->fc_conf, 0, sizeof dev->fc_conf);
> >> -    if (type == DPDK_DEV_ETH) {
> >> +    if (port_no != -1) {
> >
> > How about new type DPDK_DEV_VHOST_CLIENT ?
> > All this stuff with 'port_no == -1' looks very confusing and hides
> > real meaning of 'if's. And without comments here it's pretty hard
> > to understand what's going on.

Good suggestion, I've added in the next version.

> >
> >
> >>          err = dpdk_eth_dev_init(dev);
> >>          if (err) {
> >>              goto unlock;
> >>          }
> >> -        dev->tx_q = netdev_dpdk_alloc_txq(netdev->n_txq);
> >> -    } else {
> >> -        dev->tx_q =
> netdev_dpdk_alloc_txq(OVS_VHOST_MAX_QUEUE_NUM);
> >> -        /* Enable DPDK_DEV_VHOST device and set promiscuous mode flag.
> */
> >> -        dev->flags = NETDEV_UP | NETDEV_PROMISC;
> >> +    }
> >> +    nr_q = (type == DPDK_DEV_ETH ?
> >> +            1 : MIN(OVS_VHOST_MAX_QUEUE_NUM,
> RTE_MAX_QUEUES_PER_PORT));
> >
> > Why 1? It was 'netdev->n_txq'.

At this point netdev->n_txq = NR_QUEUE = 1 anyway.
However it is probably better to not use a magic number. Changed in the next v.

> >
> >> +    dev->tx_q = netdev_dpdk_alloc_txq(nr_q);
> >> +
> >> +    if (type == DPDK_DEV_VHOST && port_no != -1) {
> >> +        rte_eth_dev_callback_register(port_no,
> RTE_ETH_EVENT_QUEUE_STATE,
> >> +                                      vring_state_changed_callback, NULL);
> >> +        rte_eth_dev_callback_register(port_no,
> RTE_ETH_EVENT_INTR_LSC,
> >> +                                      link_status_changed_callback, NULL);
> >>      }
> >>
> >>      if (!dev->tx_q) {
> >
> > There is a race condition here. Checking for 'dev->tx_q' must be before
> > callback registering. Otherwise there will be some time between unlock
> > and return, where 'tx_q' can be used inside the callback.

Fixed in next v.


> >
> >> @@ -894,6 +1006,66 @@ dpdk_dev_parse_name(const char
> dev_name[], const char prefix[],
> >>      }
> >>  }
> >>
> >> +/* When attaching a vhost device to DPDK, a unique name of the format
> >> + * 'net_vhostX' is expected, where X is a unique identifier.
> >> + * get_vhost_drv_id returns a valid X value to provide to DPDK.
> >> + */
> >> +static int
> >> +get_vhost_drv_id(void)
> >> +{
> >> +    int i = 0;
> >> +
> >> +    for (i = 0; i < RTE_MAX_ETHPORTS; i++) {
> >> +        if (vhost_drv_ids[i] == 0) {
> >> +            return i;
> >> +        }
> >> +    }
> >> +
> >> +    return -1;
> >> +}
> >> +
> >> +static void
> >> +set_vhost_drv_id(int id, int val)
> >> +{
> >> +    vhost_drv_ids[id] = val;
> >> +}
> >
> > I think we can just increment global atomic counter instead of above
> solution.
> > Numbers just need to be uniqe. Driver never use them for anything.

But that way we would be limiting the number of vHost ports to the max value of the int we use as the counter.

> >
> >> +
> >> +static int
> >> +dpdk_attach_vhost_pmd(struct netdev_dpdk *dev, int mode)
> >> +{
> >> +    char *devargs;
> >> +    int err = 0;
> >> +    uint8_t port_no = 0;
> >> +    int driver_id = 0;
> >> +
> >> +    driver_id = get_vhost_drv_id();
> >> +    if (driver_id == -1) {
> >> +        VLOG_ERR("Unable to create vhost-user device %s - too many
> vhost-user"
> >> +                 "devices registered with PMD", dev->vhost_id);
> >> +        err = ENODEV;
> >> +    } else {
> >> +        devargs = xasprintf("net_vhost%u,iface=%s,queues=%i,client=%i",
> >> +                 driver_id, dev->vhost_id,
> >> +                 MIN(OVS_VHOST_MAX_QUEUE_NUM,
> RTE_MAX_QUEUES_PER_PORT),
> >> +                 mode);
> >> +        err = rte_eth_dev_attach(devargs, &port_no);
> >> +        if (!err) {
> >> +            dev->port_id = port_no;
> >> +        }
> >> +    }
> >> +
> >> +    if (err) {
> >> +        VLOG_ERR("Failed to attach vhost-user device %s to DPDK",
> >> +                 dev->vhost_id);
> >> +    } else {
> >> +        /* Configuration successful */
> >> +        dev->vhost_pmd_id = driver_id;
> >> +        set_vhost_drv_id(driver_id, 1);
> >> +    }
> >> +
> >> +    return err;
> >> +}
> >> +
> >>  static int
> >>  netdev_dpdk_vhost_construct(struct netdev *netdev)
> >>  {
> >> @@ -904,7 +1076,7 @@ netdev_dpdk_vhost_construct(struct netdev
> *netdev)
> >>      /* 'name' is appended to 'vhost_sock_dir' and used to create a socket
> in
> >>       * the file system. '/' or '\' would traverse directories, so they're not
> >>       * acceptable in 'name'. */
> >> -    if (strchr(name, '/') || strchr(name, '\\')) {
> >> +    if (strchr(name, '/') || strchr(name, '\\') || strchr(name, ',')) {
> >>          VLOG_ERR("\"%s\" is not a valid name for a vhost-user port. "
> >>                   "A valid name must not include '/' or '\\'",
> >>                   name);
> >> @@ -917,18 +1089,16 @@ netdev_dpdk_vhost_construct(struct netdev
> *netdev)
> >>       */
> >>      snprintf(dev->vhost_id, sizeof dev->vhost_id, "%s/%s",
> >>               dpdk_get_vhost_sock_dir(), name);
> >> +    dev->port_id = -1;
> >>
> >> -    dev->vhost_driver_flags &= ~RTE_VHOST_USER_CLIENT;
> >> -    err = rte_vhost_driver_register(dev->vhost_id, dev-
> >vhost_driver_flags);
> >> -    if (err) {
> >> -        VLOG_ERR("vhost-user socket device setup failure for socket %s\n",
> >> -                 dev->vhost_id);
> >> -    } else {
> >> +    err = dpdk_attach_vhost_pmd(dev, 0);
> >> +
> >> +    if (!err) {
> >>          fatal_signal_add_file_to_unlink(dev->vhost_id);
> >>          VLOG_INFO("Socket %s created for vhost-user port %s\n",
> >>                    dev->vhost_id, name);
> >>      }
> >> -    err = netdev_dpdk_init(netdev, -1, DPDK_DEV_VHOST);
> >> +    err = netdev_dpdk_init(netdev, dev->port_id, DPDK_DEV_VHOST);
> >
> > Someone must detach port on failure.

Added detach in next v.

> >
> >>      ovs_mutex_unlock(&dpdk_mutex);
> >>      return err;
> >> @@ -964,13 +1134,10 @@ netdev_dpdk_construct(struct netdev
> *netdev)
> >>  }
> >>
> >>  static void
> >> -netdev_dpdk_destruct(struct netdev *netdev)
> >> +dpdk_destruct_helper(struct netdev_dpdk *dev)
> >> +    OVS_REQUIRES(dpdk_mutex)
> >> +    OVS_REQUIRES(dev->mutex)
> >>  {
> >> -    struct netdev_dpdk *dev = netdev_dpdk_cast(netdev);
> >> -
> >> -    ovs_mutex_lock(&dpdk_mutex);
> >> -    ovs_mutex_lock(&dev->mutex);
> >> -
> >>      rte_eth_dev_stop(dev->port_id);
> >>      free(ovsrcu_get_protected(struct ingress_policer *,
> >>                                &dev->ingress_policer));
> >> @@ -978,61 +1145,43 @@ netdev_dpdk_destruct(struct netdev *netdev)
> >>      rte_free(dev->tx_q);
> >>      ovs_list_remove(&dev->list_node);
> >>      dpdk_mp_put(dev->dpdk_mp);
> >> -
> >> -    ovs_mutex_unlock(&dev->mutex);
> >> -    ovs_mutex_unlock(&dpdk_mutex);
> >>  }
> >>
> >> -/* rte_vhost_driver_unregister() can call back destroy_device(), which
> will
> >> - * try to acquire 'dpdk_mutex' and possibly 'dev->mutex'.  To avoid a
> >> - * deadlock, none of the mutexes must be held while calling this
> function. */
> >> -static int
> >> -dpdk_vhost_driver_unregister(struct netdev_dpdk *dev OVS_UNUSED,
> >> -                             char *vhost_id)
> >> -    OVS_EXCLUDED(dpdk_mutex)
> >> -    OVS_EXCLUDED(dev->mutex)
> >> +static void
> >> +netdev_dpdk_destruct(struct netdev *netdev)
> >>  {
> >> -    return rte_vhost_driver_unregister(vhost_id);
> >> +    struct netdev_dpdk *dev = netdev_dpdk_cast(netdev);
> >> +
> >> +    ovs_mutex_lock(&dpdk_mutex);
> >> +    ovs_mutex_lock(&dev->mutex);
> >> +
> >> +    dpdk_destruct_helper(dev);
> >> +
> >> +    ovs_mutex_unlock(&dev->mutex);
> >> +    ovs_mutex_unlock(&dpdk_mutex);
> >>  }
> >>
> >>  static void
> >>  netdev_dpdk_vhost_destruct(struct netdev *netdev)
> >>  {
> >>      struct netdev_dpdk *dev = netdev_dpdk_cast(netdev);
> >> -    char *vhost_id;
> >>
> >>      ovs_mutex_lock(&dpdk_mutex);
> >>      ovs_mutex_lock(&dev->mutex);
> >>
> >> -    /* Guest becomes an orphan if still attached. */
> >> -    if (netdev_dpdk_get_vid(dev) >= 0
> >> -        && !(dev->vhost_driver_flags & RTE_VHOST_USER_CLIENT)) {
> >> -        VLOG_ERR("Removing port '%s' while vhost device still attached.",
> >> -                 netdev->name);
> >> -        VLOG_ERR("To restore connectivity after re-adding of port, VM on "
> >> -                 "socket '%s' must be restarted.", dev->vhost_id);
> >> +    if (rte_eth_dev_detach(dev->port_id, dev->vhost_id)) {
> >> +        VLOG_ERR("Error removing vhost device %s", dev->vhost_id);
> >> +    } else {
> >> +        if (!(dev->flags & RTE_VHOST_USER_CLIENT)) {
> >
> > Flag is unset if port not attached.

With the new DPDK_DEV_VHOST_CLIENT we can just check for that here instead.

> >
> >> +            fatal_signal_remove_file_to_unlink(dev->vhost_id);
> >> +        }
> >>      }
> >> +    set_vhost_drv_id(dev->vhost_pmd_id, 0);
> >>
> >> -    free(ovsrcu_get_protected(struct ingress_policer *,
> >> -                              &dev->ingress_policer));
> >> -
> >> -    rte_free(dev->tx_q);
> >> -    ovs_list_remove(&dev->list_node);
> >> -    dpdk_mp_put(dev->dpdk_mp);
> >> -
> >> -    vhost_id = xstrdup(dev->vhost_id);
> >> +    dpdk_destruct_helper(dev);
> >>
> >>      ovs_mutex_unlock(&dev->mutex);
> >>      ovs_mutex_unlock(&dpdk_mutex);
> >> -
> >> -    if (dpdk_vhost_driver_unregister(dev, vhost_id)) {
> >> -        VLOG_ERR("%s: Unable to unregister vhost driver for socket '%s'.\n",
> >> -                 netdev->name, vhost_id);
> >> -    } else if (!(dev->vhost_driver_flags & RTE_VHOST_USER_CLIENT)) {
> >> -        /* OVS server mode - remove this socket from list for deletion */
> >> -        fatal_signal_remove_file_to_unlink(vhost_id);
> >> -    }
> >> -    free(vhost_id);
> >>  }
> >>
> >>  static void
> >> @@ -1054,12 +1203,16 @@ netdev_dpdk_get_config(const struct netdev
> *netdev, struct smap *args)
> >>      smap_add_format(args, "configured_rx_queues", "%d", netdev-
> >n_rxq);
> >>      smap_add_format(args, "requested_tx_queues", "%d", dev-
> >requested_n_txq);
> >>      smap_add_format(args, "configured_tx_queues", "%d", netdev-
> >n_txq);
> >> -    smap_add_format(args, "requested_rxq_descriptors", "%d",
> >> -                    dev->requested_rxq_size);
> >> -    smap_add_format(args, "configured_rxq_descriptors", "%d", dev-
> >rxq_size);
> >> -    smap_add_format(args, "requested_txq_descriptors", "%d",
> >> -                    dev->requested_txq_size);
> >> -    smap_add_format(args, "configured_txq_descriptors", "%d", dev-
> >txq_size);
> >> +    if (dev->type == DPDK_DEV_ETH) {
> >> +        smap_add_format(args, "requested_rxq_descriptors", "%d",
> >> +                        dev->requested_rxq_size);
> >> +        smap_add_format(args, "configured_rxq_descriptors", "%d",
> >> +                        dev->rxq_size);
> >> +        smap_add_format(args, "requested_txq_descriptors", "%d",
> >> +                        dev->requested_txq_size);
> >> +        smap_add_format(args, "configured_txq_descriptors", "%d",
> >> +                        dev->txq_size);
> >> +    }
> >>      smap_add_format(args, "mtu", "%d", dev->mtu);
> >>      ovs_mutex_unlock(&dev->mutex);
> >>
> >> @@ -1320,119 +1473,8 @@ ingress_policer_run(struct ingress_policer
> *policer, struct rte_mbuf **pkts,
> >>      return cnt;
> >>  }
> >>
> >> -static bool
> >> -is_vhost_running(struct netdev_dpdk *dev)
> >> -{
> >> -    return (netdev_dpdk_get_vid(dev) >= 0 && dev-
> >vhost_reconfigured);
> >> -}
> >> -
> >> -static inline void
> >> -netdev_dpdk_vhost_update_rx_size_counters(struct netdev_stats
> *stats,
> >> -                                          unsigned int packet_size)
> >> -{
> >> -    /* Hard-coded search for the size bucket. */
> >> -    if (packet_size < 256) {
> >> -        if (packet_size >= 128) {
> >> -            stats->rx_128_to_255_packets++;
> >> -        } else if (packet_size <= 64) {
> >> -            stats->rx_1_to_64_packets++;
> >> -        } else {
> >> -            stats->rx_65_to_127_packets++;
> >> -        }
> >> -    } else {
> >> -        if (packet_size >= 1523) {
> >> -            stats->rx_1523_to_max_packets++;
> >> -        } else if (packet_size >= 1024) {
> >> -            stats->rx_1024_to_1522_packets++;
> >> -        } else if (packet_size < 512) {
> >> -            stats->rx_256_to_511_packets++;
> >> -        } else {
> >> -            stats->rx_512_to_1023_packets++;
> >> -        }
> >> -    }
> >> -}
> >> -
> >> -static inline void
> >> -netdev_dpdk_vhost_update_rx_counters(struct netdev_stats *stats,
> >> -                                     struct dp_packet **packets, int count,
> >> -                                     int dropped)
> >> -{
> >> -    int i;
> >> -    unsigned int packet_size;
> >> -    struct dp_packet *packet;
> >> -
> >> -    stats->rx_packets += count;
> >> -    stats->rx_dropped += dropped;
> >> -    for (i = 0; i < count; i++) {
> >> -        packet = packets[i];
> >> -        packet_size = dp_packet_size(packet);
> >> -
> >> -        if (OVS_UNLIKELY(packet_size < ETH_HEADER_LEN)) {
> >> -            /* This only protects the following multicast counting from
> >> -             * too short packets, but it does not stop the packet from
> >> -             * further processing. */
> >> -            stats->rx_errors++;
> >> -            stats->rx_length_errors++;
> >> -            continue;
> >> -        }
> >> -
> >> -        netdev_dpdk_vhost_update_rx_size_counters(stats, packet_size);
> >> -
> >> -        struct eth_header *eh = (struct eth_header *)
> dp_packet_data(packet);
> >> -        if (OVS_UNLIKELY(eth_addr_is_multicast(eh->eth_dst))) {
> >> -            stats->multicast++;
> >> -        }
> >> -
> >> -        stats->rx_bytes += packet_size;
> >> -    }
> >> -}
> >> -
> >> -/*
> >> - * The receive path for the vhost port is the TX path out from guest.
> >> - */
> >> -static int
> >> -netdev_dpdk_vhost_rxq_recv(struct netdev_rxq *rxq,
> >> -                           struct dp_packet_batch *batch)
> >> -{
> >> -    struct netdev_dpdk *dev = netdev_dpdk_cast(rxq->netdev);
> >> -    int qid = rxq->queue_id;
> >> -    struct ingress_policer *policer =
> netdev_dpdk_get_ingress_policer(dev);
> >> -    uint16_t nb_rx = 0;
> >> -    uint16_t dropped = 0;
> >> -
> >> -    if (OVS_UNLIKELY(!is_vhost_running(dev)
> >> -                     || !(dev->flags & NETDEV_UP))) {
> >> -        return EAGAIN;
> >> -    }
> >> -
> >> -    nb_rx = rte_vhost_dequeue_burst(netdev_dpdk_get_vid(dev),
> >> -                                    qid * VIRTIO_QNUM + VIRTIO_TXQ,
> >> -                                    dev->dpdk_mp->mp,
> >> -                                    (struct rte_mbuf **) batch->packets,
> >> -                                    NETDEV_MAX_BURST);
> >> -    if (!nb_rx) {
> >> -        return EAGAIN;
> >> -    }
> >> -
> >> -    if (policer) {
> >> -        dropped = nb_rx;
> >> -        nb_rx = ingress_policer_run(policer,
> >> -                                    (struct rte_mbuf **) batch->packets,
> >> -                                    nb_rx);
> >> -        dropped -= nb_rx;
> >> -    }
> >> -
> >> -    rte_spinlock_lock(&dev->stats_lock);
> >> -    netdev_dpdk_vhost_update_rx_counters(&dev->stats, batch-
> >packets,
> >> -                                         nb_rx, dropped);
> >> -    rte_spinlock_unlock(&dev->stats_lock);
> >> -
> >> -    batch->count = (int) nb_rx;
> >> -    return 0;
> >> -}
> >> -
> >> -static int
> >> -netdev_dpdk_rxq_recv(struct netdev_rxq *rxq, struct dp_packet_batch
> *batch)
> >> +static inline int
> >> +dpdk_rxq_recv(struct netdev_rxq *rxq, struct dp_packet_batch *batch)
> >>  {
> >>      struct netdev_rxq_dpdk *rx = netdev_rxq_dpdk_cast(rxq);
> >>      struct netdev_dpdk *dev = netdev_dpdk_cast(rxq->netdev);
> >> @@ -1467,6 +1509,30 @@ netdev_dpdk_rxq_recv(struct netdev_rxq
> *rxq, struct dp_packet_batch *batch)
> >>      return 0;
> >>  }
> >>
> >> +static int
> >> +netdev_dpdk_rxq_recv(struct netdev_rxq *rxq, struct dp_packet_batch
> *batch)
> >> +{
> >> +    return dpdk_rxq_recv(rxq, batch);
> >> +}
> >> +
> >> +static int
> >> +netdev_dpdk_vhost_client_rxq_recv(struct netdev_rxq *rxq,
> >> +                                  struct dp_packet_batch *batch)
> >> +{
> >> +    struct netdev_rxq_dpdk *rx = netdev_rxq_dpdk_cast(rxq);
> >> +
> >> +    if (rte_eth_dev_is_valid_port(rx->port_id)) {
> >> +        return dpdk_rxq_recv(rxq, batch);
> >> +    } else {
> >> +        struct netdev_dpdk *dev = netdev_dpdk_cast(rxq->netdev);
> >> +
> >> +        rte_spinlock_lock(&dev->stats_lock);
> >> +        dev->stats.rx_dropped += batch->count;
> >> +        rte_spinlock_unlock(&dev->stats_lock);
> >> +        return 0;
> >> +    }
> >> +}
> >> +
> >>  static inline int
> >>  netdev_dpdk_qos_run(struct netdev_dpdk *dev, struct rte_mbuf
> **pkts,
> >>                      int cnt)
> >> @@ -1508,80 +1574,6 @@ netdev_dpdk_filter_packet_len(struct
> netdev_dpdk *dev, struct rte_mbuf **pkts,
> >>      return cnt;
> >>  }
> >>
> >> -static inline void
> >> -netdev_dpdk_vhost_update_tx_counters(struct netdev_stats *stats,
> >> -                                     struct dp_packet **packets,
> >> -                                     int attempted,
> >> -                                     int dropped)
> >> -{
> >> -    int i;
> >> -    int sent = attempted - dropped;
> >> -
> >> -    stats->tx_packets += sent;
> >> -    stats->tx_dropped += dropped;
> >> -
> >> -    for (i = 0; i < sent; i++) {
> >> -        stats->tx_bytes += dp_packet_size(packets[i]);
> >> -    }
> >> -}
> >> -
> >> -static void
> >> -__netdev_dpdk_vhost_send(struct netdev *netdev, int qid,
> >> -                         struct dp_packet **pkts, int cnt)
> >> -{
> >> -    struct netdev_dpdk *dev = netdev_dpdk_cast(netdev);
> >> -    struct rte_mbuf **cur_pkts = (struct rte_mbuf **) pkts;
> >> -    unsigned int total_pkts = cnt;
> >> -    unsigned int dropped = 0;
> >> -    int i, retries = 0;
> >> -
> >> -    qid = dev->tx_q[qid % netdev->n_txq].map;
> >> -
> >> -    if (OVS_UNLIKELY(!is_vhost_running(dev) || qid < 0
> >> -                     || !(dev->flags & NETDEV_UP))) {
> >> -        rte_spinlock_lock(&dev->stats_lock);
> >> -        dev->stats.tx_dropped+= cnt;
> >> -        rte_spinlock_unlock(&dev->stats_lock);
> >> -        goto out;
> >> -    }
> >> -
> >> -    rte_spinlock_lock(&dev->tx_q[qid].tx_lock);
> >> -
> >> -    cnt = netdev_dpdk_filter_packet_len(dev, cur_pkts, cnt);
> >> -    /* Check has QoS has been configured for the netdev */
> >> -    cnt = netdev_dpdk_qos_run(dev, cur_pkts, cnt);
> >> -    dropped = total_pkts - cnt;
> >> -
> >> -    do {
> >> -        int vhost_qid = qid * VIRTIO_QNUM + VIRTIO_RXQ;
> >> -        unsigned int tx_pkts;
> >> -
> >> -        tx_pkts = rte_vhost_enqueue_burst(netdev_dpdk_get_vid(dev),
> >> -                                          vhost_qid, cur_pkts, cnt);
> >> -        if (OVS_LIKELY(tx_pkts)) {
> >> -            /* Packets have been sent.*/
> >> -            cnt -= tx_pkts;
> >> -            /* Prepare for possible retry.*/
> >> -            cur_pkts = &cur_pkts[tx_pkts];
> >> -        } else {
> >> -            /* No packets sent - do not retry.*/
> >> -            break;
> >> -        }
> >> -    } while (cnt && (retries++ <= VHOST_ENQ_RETRY_NUM));
> >> -
> >> -    rte_spinlock_unlock(&dev->tx_q[qid].tx_lock);
> >> -
> >> -    rte_spinlock_lock(&dev->stats_lock);
> >> -    netdev_dpdk_vhost_update_tx_counters(&dev->stats, pkts,
> total_pkts,
> >> -                                         cnt + dropped);
> >> -    rte_spinlock_unlock(&dev->stats_lock);
> >> -
> >> -out:
> >> -    for (i = 0; i < total_pkts - dropped; i++) {
> >> -        dp_packet_delete(pkts[i]);
> >> -    }
> >> -}
> >> -
> >>  /* Tx function. Transmit packets indefinitely */
> >>  static void
> >>  dpdk_do_tx_copy(struct netdev *netdev, int qid, struct
> dp_packet_batch *batch)
> >> @@ -1629,18 +1621,13 @@ dpdk_do_tx_copy(struct netdev *netdev, int
> qid, struct dp_packet_batch *batch)
> >>          newcnt++;
> >>      }
> >>
> >> -    if (dev->type == DPDK_DEV_VHOST) {
> >> -        __netdev_dpdk_vhost_send(netdev, qid, (struct dp_packet **)
> pkts,
> >> -                                 newcnt);
> >> -    } else {
> >> -        unsigned int qos_pkts = newcnt;
> >> +    unsigned int qos_pkts = newcnt;
> >>
> >> -        /* Check if QoS has been configured for this netdev. */
> >> -        newcnt = netdev_dpdk_qos_run(dev, pkts, newcnt);
> >> +    /* Check if QoS has been configured for this netdev. */
> >> +    newcnt = netdev_dpdk_qos_run(dev, pkts, newcnt);
> >>
> >> -        dropped += qos_pkts - newcnt;
> >> -        dropped += netdev_dpdk_eth_tx_burst(dev, qid, pkts, newcnt);
> >> -    }
> >> +    dropped += qos_pkts - newcnt;
> >> +    dropped += netdev_dpdk_eth_tx_burst(dev, qid, pkts, newcnt);
> >>
> >>      if (OVS_UNLIKELY(dropped)) {
> >>          rte_spinlock_lock(&dev->stats_lock);
> >> @@ -1649,32 +1636,10 @@ dpdk_do_tx_copy(struct netdev *netdev, int
> qid, struct dp_packet_batch *batch)
> >>      }
> >>  }
> >>
> >> -static int
> >> -netdev_dpdk_vhost_send(struct netdev *netdev, int qid,
> >> -                       struct dp_packet_batch *batch,
> >> -                       bool may_steal, bool concurrent_txq OVS_UNUSED)
> >> -{
> >> -
> >> -    if (OVS_UNLIKELY(!may_steal || batch->packets[0]->source !=
> DPBUF_DPDK)) {
> >> -        dpdk_do_tx_copy(netdev, qid, batch);
> >> -        dp_packet_delete_batch(batch, may_steal);
> >> -    } else {
> >> -        dp_packet_batch_apply_cutlen(batch);
> >> -        __netdev_dpdk_vhost_send(netdev, qid, batch->packets, batch-
> >count);
> >> -    }
> >> -    return 0;
> >> -}
> >> -
> >>  static inline void
> >>  netdev_dpdk_send__(struct netdev_dpdk *dev, int qid,
> >> -                   struct dp_packet_batch *batch, bool may_steal,
> >> -                   bool concurrent_txq)
> >> +                   struct dp_packet_batch *batch, bool may_steal)
> >>  {
> >> -    if (OVS_UNLIKELY(concurrent_txq)) {
> >> -        qid = qid % dev->up.n_txq;
> >> -        rte_spinlock_lock(&dev->tx_q[qid].tx_lock);
> >> -    }
> >> -
> >>      if (OVS_UNLIKELY(!may_steal ||
> >>                       batch->packets[0]->source != DPBUF_DPDK)) {
> >>          struct netdev *netdev = &dev->up;
> >> @@ -1700,20 +1665,50 @@ netdev_dpdk_send__(struct netdev_dpdk
> *dev, int qid,
> >>              rte_spinlock_unlock(&dev->stats_lock);
> >>          }
> >>      }
> >> +}
> >> +
> >> +static int
> >> +netdev_dpdk_eth_send(struct netdev *netdev, int qid,
> >> +                     struct dp_packet_batch *batch, bool may_steal,
> >> +                     bool concurrent_txq)
> >> +{
> >> +    struct netdev_dpdk *dev = netdev_dpdk_cast(netdev);
> >> +
> >> +    if (OVS_UNLIKELY(concurrent_txq)) {
> >> +        qid = qid % dev->up.n_txq;
> >> +        rte_spinlock_lock(&dev->tx_q[qid].tx_lock);
> >> +    }
> >> +
> >> +    netdev_dpdk_send__(dev, qid, batch, may_steal);
> >>
> >>      if (OVS_UNLIKELY(concurrent_txq)) {
> >>          rte_spinlock_unlock(&dev->tx_q[qid].tx_lock);
> >>      }
> >> +
> >> +    return 0;
> >>  }
> >>
> >>  static int
> >> -netdev_dpdk_eth_send(struct netdev *netdev, int qid,
> >> -                     struct dp_packet_batch *batch, bool may_steal,
> >> -                     bool concurrent_txq)
> >> +netdev_dpdk_vhost_send(struct netdev *netdev, int qid,
> >> +                       struct dp_packet_batch *batch, bool may_steal,
> >> +                       bool concurrent_txq OVS_UNUSED)
> >>  {
> >>      struct netdev_dpdk *dev = netdev_dpdk_cast(netdev);
> >>
> >> -    netdev_dpdk_send__(dev, qid, batch, may_steal, concurrent_txq);
> >> +    qid = dev->tx_q[qid % netdev->n_txq].map;
> >> +    if (qid == -1) {
> >> +        rte_spinlock_lock(&dev->stats_lock);
> >> +        dev->stats.tx_dropped+= batch->count;
> >> +        rte_spinlock_unlock(&dev->stats_lock);
> >> +        if (may_steal) {
> >> +            dp_packet_delete_batch(batch, may_steal);
> >> +        }
> >> +    } else {
> >> +        rte_spinlock_lock(&dev->tx_q[qid].tx_lock);
> >> +        netdev_dpdk_send__(dev, qid, batch, may_steal);
> >> +        rte_spinlock_unlock(&dev->tx_q[qid].tx_lock);
> >> +    }
> >> +
> >>      return 0;
> >>  }
> >>
> >> @@ -1780,41 +1775,6 @@ netdev_dpdk_set_mtu(struct netdev *netdev,
> int mtu)
> >>  static int
> >>  netdev_dpdk_get_carrier(const struct netdev *netdev, bool *carrier);
> >>
> >> -static int
> >> -netdev_dpdk_vhost_get_stats(const struct netdev *netdev,
> >> -                            struct netdev_stats *stats)
> >> -{
> >> -    struct netdev_dpdk *dev = netdev_dpdk_cast(netdev);
> >> -
> >> -    ovs_mutex_lock(&dev->mutex);
> >> -
> >> -    rte_spinlock_lock(&dev->stats_lock);
> >> -    /* Supported Stats */
> >> -    stats->rx_packets += dev->stats.rx_packets;
> >> -    stats->tx_packets += dev->stats.tx_packets;
> >> -    stats->rx_dropped = dev->stats.rx_dropped;
> >> -    stats->tx_dropped += dev->stats.tx_dropped;
> >> -    stats->multicast = dev->stats.multicast;
> >> -    stats->rx_bytes = dev->stats.rx_bytes;
> >> -    stats->tx_bytes = dev->stats.tx_bytes;
> >> -    stats->rx_errors = dev->stats.rx_errors;
> >> -    stats->rx_length_errors = dev->stats.rx_length_errors;
> >> -
> >> -    stats->rx_1_to_64_packets = dev->stats.rx_1_to_64_packets;
> >> -    stats->rx_65_to_127_packets = dev->stats.rx_65_to_127_packets;
> >> -    stats->rx_128_to_255_packets = dev->stats.rx_128_to_255_packets;
> >> -    stats->rx_256_to_511_packets = dev->stats.rx_256_to_511_packets;
> >> -    stats->rx_512_to_1023_packets = dev-
> >stats.rx_512_to_1023_packets;
> >> -    stats->rx_1024_to_1522_packets = dev-
> >stats.rx_1024_to_1522_packets;
> >> -    stats->rx_1523_to_max_packets = dev-
> >stats.rx_1523_to_max_packets;
> >> -
> >> -    rte_spinlock_unlock(&dev->stats_lock);
> >> -
> >> -    ovs_mutex_unlock(&dev->mutex);
> >> -
> >> -    return 0;
> >> -}
> >> -
> >>  static void
> >>  netdev_dpdk_convert_xstats(struct netdev_stats *stats,
> >>                             const struct rte_eth_xstat *xstats,
> >> @@ -1858,6 +1818,8 @@ netdev_dpdk_convert_xstats(struct
> netdev_stats *stats,
> >>              stats->tx_broadcast_packets = xstats[i].value;
> >>          } else if (strcmp(XSTAT_RX_UNDERSIZED_ERRORS, names[i].name)
> == 0) {
> >>              stats->rx_undersized_errors = xstats[i].value;
> >> +        } else if (strcmp(XSTAT_RX_UNDERSIZE_PACKETS, names[i].name)
> == 0) {
> >> +            stats->rx_undersized_errors = xstats[i].value;
> >>          } else if (strcmp(XSTAT_RX_FRAGMENTED_ERRORS, names[i].name)
> == 0) {
> >>              stats->rx_fragmented_errors = xstats[i].value;
> >>          } else if (strcmp(XSTAT_RX_JABBER_ERRORS, names[i].name) == 0) {
> >> @@ -1880,62 +1842,66 @@ netdev_dpdk_get_stats(const struct netdev
> *netdev, struct netdev_stats *stats)
> >>      struct rte_eth_xstat_name *rte_xstats_names = NULL;
> >>      int rte_xstats_len, rte_xstats_new_len, rte_xstats_ret;
> >>
> >> -    if (rte_eth_stats_get(dev->port_id, &rte_stats)) {
> >> -        VLOG_ERR("Can't get ETH statistics for port: %i.", dev->port_id);
> >> -        ovs_mutex_unlock(&dev->mutex);
> >> -        return EPROTO;
> >> -    }
> >> +    if (rte_eth_dev_is_valid_port(dev->port_id)) {
> >> +        if (rte_eth_stats_get(dev->port_id, &rte_stats)) {
> >> +            VLOG_ERR("Can't get ETH statistics for port: %i.", dev->port_id);
> >> +            ovs_mutex_unlock(&dev->mutex);
> >> +            return EPROTO;
> >> +        }
> >>
> >> -    /* Get length of statistics */
> >> -    rte_xstats_len = rte_eth_xstats_get_names(dev->port_id, NULL, 0);
> >> -    if (rte_xstats_len < 0) {
> >> -        VLOG_WARN("Cannot get XSTATS values for port: %i", dev-
> >port_id);
> >> -        goto out;
> >> -    }
> >> -    /* Reserve memory for xstats names and values */
> >> -    rte_xstats_names = xcalloc(rte_xstats_len, sizeof *rte_xstats_names);
> >> -    rte_xstats = xcalloc(rte_xstats_len, sizeof *rte_xstats);
> >> +        /* Get length of statistics */
> >> +        rte_xstats_len = rte_eth_xstats_get_names(dev->port_id, NULL, 0);
> >> +        if (rte_xstats_len < 0) {
> >> +            VLOG_WARN("Cannot get XSTATS values for port: %i", dev-
> >port_id);
> >> +            goto out;
> >> +        }
> >> +        /* Reserve memory for xstats names and values */
> >> +        rte_xstats_names = xcalloc(rte_xstats_len, sizeof
> *rte_xstats_names);
> >> +        rte_xstats = xcalloc(rte_xstats_len, sizeof *rte_xstats);
> >> +
> >> +        /* Retreive xstats names */
> >> +        rte_xstats_new_len = rte_eth_xstats_get_names(dev->port_id,
> >> +                                                      rte_xstats_names,
> >> +                                                      rte_xstats_len);
> >> +        if (rte_xstats_new_len != rte_xstats_len) {
> >> +            VLOG_WARN("Cannot get XSTATS names for port: %i.", dev-
> >port_id);
> >> +            goto out;
> >> +        }
> >> +        /* Retreive xstats values */
> >> +        memset(rte_xstats, 0xff, sizeof *rte_xstats * rte_xstats_len);
> >> +        rte_xstats_ret = rte_eth_xstats_get(dev->port_id, rte_xstats,
> >> +                                            rte_xstats_len);
> >> +        if (rte_xstats_ret > 0 && rte_xstats_ret <= rte_xstats_len) {
> >> +            netdev_dpdk_convert_xstats(stats, rte_xstats, rte_xstats_names,
> >> +                                       rte_xstats_len);
> >> +        } else {
> >> +            VLOG_WARN("Cannot get XSTATS values for port: %i.", dev-
> >port_id);
> >> +        }
> >>
> >> -    /* Retreive xstats names */
> >> -    rte_xstats_new_len = rte_eth_xstats_get_names(dev->port_id,
> >> -                                                  rte_xstats_names,
> >> -                                                  rte_xstats_len);
> >> -    if (rte_xstats_new_len != rte_xstats_len) {
> >> -        VLOG_WARN("Cannot get XSTATS names for port: %i.", dev-
> >port_id);
> >> -        goto out;
> >> -    }
> >> -    /* Retreive xstats values */
> >> -    memset(rte_xstats, 0xff, sizeof *rte_xstats * rte_xstats_len);
> >> -    rte_xstats_ret = rte_eth_xstats_get(dev->port_id, rte_xstats,
> >> -                                        rte_xstats_len);
> >> -    if (rte_xstats_ret > 0 && rte_xstats_ret <= rte_xstats_len) {
> >> -        netdev_dpdk_convert_xstats(stats, rte_xstats, rte_xstats_names,
> >> -                                   rte_xstats_len);
> >> -    } else {
> >> -        VLOG_WARN("Cannot get XSTATS values for port: %i.", dev-
> >port_id);
> >> -    }
> >> +    out:
> >> +        free(rte_xstats);
> >> +        free(rte_xstats_names);
> >>
> >> -out:
> >> -    free(rte_xstats);
> >> -    free(rte_xstats_names);
> >> -
> >> -    stats->rx_packets = rte_stats.ipackets;
> >> -    stats->tx_packets = rte_stats.opackets;
> >> -    stats->rx_bytes = rte_stats.ibytes;
> >> -    stats->tx_bytes = rte_stats.obytes;
> >> -    /* DPDK counts imissed as errors, but count them here as dropped
> instead */
> >> -    stats->rx_errors = rte_stats.ierrors - rte_stats.imissed;
> >> -    stats->tx_errors = rte_stats.oerrors;
> >> -
> >> -    rte_spinlock_lock(&dev->stats_lock);
> >> -    stats->tx_dropped = dev->stats.tx_dropped;
> >> -    stats->rx_dropped = dev->stats.rx_dropped;
> >> -    rte_spinlock_unlock(&dev->stats_lock);
> >> -
> >> -    /* These are the available DPDK counters for packets not received due
> to
> >> -     * local resource constraints in DPDK and NIC respectively. */
> >> -    stats->rx_dropped += rte_stats.rx_nombuf + rte_stats.imissed;
> >> -    stats->rx_missed_errors = rte_stats.imissed;
> >> +        stats->rx_packets = rte_stats.ipackets;
> >> +        stats->tx_packets = rte_stats.opackets;
> >> +        stats->rx_bytes = rte_stats.ibytes;
> >> +        stats->tx_bytes = rte_stats.obytes;
> >> +
> >> +        /* DPDK counts imissed as errors, but count them here as dropped
> >> +         * instead */
> >> +        stats->rx_errors = rte_stats.ierrors - rte_stats.imissed;
> >> +        stats->tx_errors = rte_stats.oerrors;
> >> +
> >> +        rte_spinlock_lock(&dev->stats_lock);
> >> +        stats->tx_dropped = dev->stats.tx_dropped;
> >> +        stats->rx_dropped = dev->stats.rx_dropped;
> >> +        rte_spinlock_unlock(&dev->stats_lock);
> >> +
> >> +        /* These are the available DPDK counters for packets not received
> due
> >> +         * to local resource constraints in DPDK and NIC respectively. */
> >> +        stats->rx_dropped += rte_stats.rx_nombuf + rte_stats.imissed;
> >> +        stats->rx_missed_errors = rte_stats.imissed;
> >> +    }
> >>
> >>      ovs_mutex_unlock(&dev->mutex);
> >>
> >> @@ -2088,24 +2054,6 @@ netdev_dpdk_get_carrier(const struct netdev
> *netdev, bool *carrier)
> >>      return 0;
> >>  }
> >>
> >> -static int
> >> -netdev_dpdk_vhost_get_carrier(const struct netdev *netdev, bool
> *carrier)
> >> -{
> >> -    struct netdev_dpdk *dev = netdev_dpdk_cast(netdev);
> >> -
> >> -    ovs_mutex_lock(&dev->mutex);
> >> -
> >> -    if (is_vhost_running(dev)) {
> >> -        *carrier = 1;
> >> -    } else {
> >> -        *carrier = 0;
> >> -    }
> >> -
> >> -    ovs_mutex_unlock(&dev->mutex);
> >> -
> >> -    return 0;
> >> -}
> >> -
> >>  static long long int
> >>  netdev_dpdk_get_carrier_resets(const struct netdev *netdev)
> >>  {
> >> @@ -2161,12 +2109,10 @@ netdev_dpdk_update_flags__(struct
> netdev_dpdk *dev,
> >>              rte_eth_dev_stop(dev->port_id);
> >>          }
> >>      } else {
> >> -        /* If DPDK_DEV_VHOST device's NETDEV_UP flag was changed and
> vhost is
> >> -         * running then change netdev's change_seq to trigger link state
> >> -         * update. */
> >> +        /* If DPDK_DEV_VHOST device's NETDEV_UP flag was changed then
> change
> >> +         * netdev's change_seq to trigger link state update. */
> >>
> >> -        if ((NETDEV_UP & ((*old_flagsp ^ on) | (*old_flagsp ^ off)))
> >> -            && is_vhost_running(dev)) {
> >> +        if ((NETDEV_UP & ((*old_flagsp ^ on) | (*old_flagsp ^ off)))) {
> >>              netdev_change_seq_changed(&dev->up);
> >>
> >>              /* Clear statistics if device is getting up. */
> >> @@ -2224,11 +2170,17 @@ netdev_dpdk_get_status(const struct netdev
> *netdev, struct smap *args)
> >>      smap_add_format(args, "max_vfs", "%u", dev_info.max_vfs);
> >>      smap_add_format(args, "max_vmdq_pools", "%u",
> dev_info.max_vmdq_pools);
> >>
> >> -    if (dev_info.pci_dev) {
> >> -        smap_add_format(args, "pci-vendor_id", "0x%u",
> >> -                        dev_info.pci_dev->id.vendor_id);
> >> -        smap_add_format(args, "pci-device_id", "0x%x",
> >> -                        dev_info.pci_dev->id.device_id);
> >> +    if (dev->type == DPDK_DEV_ETH) {
> >> +        smap_add_format(args, "max_hash_mac_addrs", "%u",
> >> +                        dev_info.max_hash_mac_addrs);
> >> +        smap_add_format(args, "max_vfs", "%u", dev_info.max_vfs);
> >> +        smap_add_format(args, "max_vmdq_pools", "%u",
> dev_info.max_vmdq_pools);
> >> +        if (dev_info.pci_dev) {
> >> +            smap_add_format(args, "pci-vendor_id", "0x%u",
> >> +                            dev_info.pci_dev->id.vendor_id);
> >> +            smap_add_format(args, "pci-device_id", "0x%x",
> >> +                            dev_info.pci_dev->id.device_id);
> >> +        }
> >>      }
> >>
> >>      return 0;
> >> @@ -2264,7 +2216,7 @@ netdev_dpdk_set_admin_state(struct
> unixctl_conn *conn, int argc,
> >>
> >>      if (argc > 2) {
> >>          struct netdev *netdev = netdev_from_name(argv[1]);
> >> -        if (netdev && is_dpdk_class(netdev->netdev_class)) {
> >> +        if (netdev && is_dpdk_eth_class(netdev->netdev_class)) {
> >>              struct netdev_dpdk *dpdk_dev = netdev_dpdk_cast(netdev);
> >>
> >>              ovs_mutex_lock(&dpdk_dev->mutex);
> >> @@ -2292,22 +2244,6 @@ netdev_dpdk_set_admin_state(struct
> unixctl_conn *conn, int argc,
> >>  }
> >>
> >>  /*
> >> - * Set virtqueue flags so that we do not receive interrupts.
> >> - */
> >> -static void
> >> -set_irq_status(int vid)
> >> -{
> >> -    uint32_t i;
> >> -    uint64_t idx;
> >> -
> >> -    for (i = 0; i < rte_vhost_get_queue_num(vid); i++) {
> >> -        idx = i * VIRTIO_QNUM;
> >> -        rte_vhost_enable_guest_notification(vid, idx + VIRTIO_RXQ, 0);
> >> -        rte_vhost_enable_guest_notification(vid, idx + VIRTIO_TXQ, 0);
> >> -    }
> >> -}
> >> -
> >> -/*
> >>   * Fixes mapping for vhost-user tx queues. Must be called after each
> >>   * enabling/disabling of queues and n_txq modifications.
> >>   */
> >> @@ -2348,73 +2284,6 @@ netdev_dpdk_remap_txqs(struct
> netdev_dpdk *dev)
> >>      free(enabled_queues);
> >>  }
> >>
> >> -/*
> >> - * A new virtio-net device is added to a vhost port.
> >> - */
> >> -static int
> >> -new_device(int vid)
> >> -{
> >> -    struct netdev_dpdk *dev;
> >> -    bool exists = false;
> >> -    int newnode = 0;
> >> -    char ifname[IF_NAME_SZ];
> >> -
> >> -    rte_vhost_get_ifname(vid, ifname, sizeof ifname);
> >> -
> >> -    ovs_mutex_lock(&dpdk_mutex);
> >> -    /* Add device to the vhost port with the same name as that passed
> down. */
> >> -    LIST_FOR_EACH(dev, list_node, &dpdk_list) {
> >> -        ovs_mutex_lock(&dev->mutex);
> >> -        if (strncmp(ifname, dev->vhost_id, IF_NAME_SZ) == 0) {
> >> -            uint32_t qp_num = rte_vhost_get_queue_num(vid);
> >> -
> >> -            /* Get NUMA information */
> >> -            newnode = rte_vhost_get_numa_node(vid);
> >> -            if (newnode == -1) {
> >> -#ifdef VHOST_NUMA
> >> -                VLOG_INFO("Error getting NUMA info for vHost Device '%s'",
> >> -                          ifname);
> >> -#endif
> >> -                newnode = dev->socket_id;
> >> -            }
> >> -
> >> -            if (dev->requested_n_txq != qp_num
> >> -                || dev->requested_n_rxq != qp_num
> >> -                || dev->requested_socket_id != newnode) {
> >> -                dev->requested_socket_id = newnode;
> >> -                dev->requested_n_rxq = qp_num;
> >> -                dev->requested_n_txq = qp_num;
> >> -                netdev_request_reconfigure(&dev->up);
> >> -            } else {
> >> -                /* Reconfiguration not required. */
> >> -                dev->vhost_reconfigured = true;
> >> -            }
> >> -
> >> -            ovsrcu_index_set(&dev->vid, vid);
> >> -            exists = true;
> >> -
> >> -            /* Disable notifications. */
> >> -            set_irq_status(vid);
> >> -            netdev_change_seq_changed(&dev->up);
> >> -            ovs_mutex_unlock(&dev->mutex);
> >> -            break;
> >> -        }
> >> -        ovs_mutex_unlock(&dev->mutex);
> >> -    }
> >> -    ovs_mutex_unlock(&dpdk_mutex);
> >> -
> >> -    if (!exists) {
> >> -        VLOG_INFO("vHost Device '%s' can't be added - name not found",
> ifname);
> >> -
> >> -        return -1;
> >> -    }
> >> -
> >> -    VLOG_INFO("vHost Device '%s' has been added on numa node %i",
> >> -              ifname, newnode);
> >> -
> >> -    return 0;
> >> -}
> >> -
> >>  /* Clears mapping for all available queues of vhost interface. */
> >>  static void
> >>  netdev_dpdk_txq_map_clear(struct netdev_dpdk *dev)
> >> @@ -2427,133 +2296,12 @@ netdev_dpdk_txq_map_clear(struct
> netdev_dpdk *dev)
> >>      }
> >>  }
> >>
> >> -/*
> >> - * Remove a virtio-net device from the specific vhost port.  Use dev-
> >remove
> >> - * flag to stop any more packets from being sent or received to/from a
> VM and
> >> - * ensure all currently queued packets have been sent/received before
> removing
> >> - *  the device.
> >> - */
> >> -static void
> >> -destroy_device(int vid)
> >> -{
> >> -    struct netdev_dpdk *dev;
> >> -    bool exists = false;
> >> -    char ifname[IF_NAME_SZ];
> >> -
> >> -    rte_vhost_get_ifname(vid, ifname, sizeof ifname);
> >> -
> >> -    ovs_mutex_lock(&dpdk_mutex);
> >> -    LIST_FOR_EACH (dev, list_node, &dpdk_list) {
> >> -        if (netdev_dpdk_get_vid(dev) == vid) {
> >> -
> >> -            ovs_mutex_lock(&dev->mutex);
> >> -            dev->vhost_reconfigured = false;
> >> -            ovsrcu_index_set(&dev->vid, -1);
> >> -            netdev_dpdk_txq_map_clear(dev);
> >> -
> >> -            netdev_change_seq_changed(&dev->up);
> >> -            ovs_mutex_unlock(&dev->mutex);
> >> -            exists = true;
> >> -            break;
> >> -        }
> >> -    }
> >> -
> >> -    ovs_mutex_unlock(&dpdk_mutex);
> >> -
> >> -    if (exists) {
> >> -        /*
> >> -         * Wait for other threads to quiesce after setting the 'virtio_dev'
> >> -         * to NULL, before returning.
> >> -         */
> >> -        ovsrcu_synchronize();
> >> -        /*
> >> -         * As call to ovsrcu_synchronize() will end the quiescent state,
> >> -         * put thread back into quiescent state before returning.
> >> -         */
> >> -        ovsrcu_quiesce_start();
> >> -        VLOG_INFO("vHost Device '%s' has been removed", ifname);
> >> -    } else {
> >> -        VLOG_INFO("vHost Device '%s' not found", ifname);
> >> -    }
> >> -}
> >> -
> >> -static int
> >> -vring_state_changed(int vid, uint16_t queue_id, int enable)
> >> -{
> >> -    struct netdev_dpdk *dev;
> >> -    bool exists = false;
> >> -    int qid = queue_id / VIRTIO_QNUM;
> >> -    char ifname[IF_NAME_SZ];
> >> -
> >> -    rte_vhost_get_ifname(vid, ifname, sizeof ifname);
> >> -
> >> -    if (queue_id % VIRTIO_QNUM == VIRTIO_TXQ) {
> >> -        return 0;
> >> -    }
> >> -
> >> -    ovs_mutex_lock(&dpdk_mutex);
> >> -    LIST_FOR_EACH (dev, list_node, &dpdk_list) {
> >> -        ovs_mutex_lock(&dev->mutex);
> >> -        if (strncmp(ifname, dev->vhost_id, IF_NAME_SZ) == 0) {
> >> -            if (enable) {
> >> -                dev->tx_q[qid].map = qid;
> >> -            } else {
> >> -                dev->tx_q[qid].map = OVS_VHOST_QUEUE_DISABLED;
> >> -            }
> >> -            netdev_dpdk_remap_txqs(dev);
> >> -            exists = true;
> >> -            ovs_mutex_unlock(&dev->mutex);
> >> -            break;
> >> -        }
> >> -        ovs_mutex_unlock(&dev->mutex);
> >> -    }
> >> -    ovs_mutex_unlock(&dpdk_mutex);
> >> -
> >> -    if (exists) {
> >> -        VLOG_INFO("State of queue %d ( tx_qid %d ) of vhost device '%s'"
> >> -                  "changed to \'%s\'", queue_id, qid, ifname,
> >> -                  (enable == 1) ? "enabled" : "disabled");
> >> -    } else {
> >> -        VLOG_INFO("vHost Device '%s' not found", ifname);
> >> -        return -1;
> >> -    }
> >> -
> >> -    return 0;
> >> -}
> >> -
> >> -int
> >> -netdev_dpdk_get_vid(const struct netdev_dpdk *dev)
> >> -{
> >> -    return ovsrcu_index_get(&dev->vid);
> >> -}
> >> -
> >>  struct ingress_policer *
> >>  netdev_dpdk_get_ingress_policer(const struct netdev_dpdk *dev)
> >>  {
> >>      return ovsrcu_get(struct ingress_policer *, &dev->ingress_policer);
> >>  }
> >>
> >> -/*
> >> - * These callbacks allow virtio-net devices to be added to vhost ports
> when
> >> - * configuration has been fully complete.
> >> - */
> >> -static const struct virtio_net_device_ops virtio_net_device_ops =
> >> -{
> >> -    .new_device =  new_device,
> >> -    .destroy_device = destroy_device,
> >> -    .vring_state_changed = vring_state_changed
> >> -};
> >> -
> >> -static void *
> >> -start_vhost_loop(void *dummy OVS_UNUSED)
> >> -{
> >> -     pthread_detach(pthread_self());
> >> -     /* Put the vhost thread into quiescent state. */
> >> -     ovsrcu_quiesce_start();
> >> -     rte_vhost_driver_session_start();
> >> -     return NULL;
> >> -}
> >> -
> >>  static int
> >>  netdev_dpdk_class_init(void)
> >>  {
> >> @@ -2576,19 +2324,9 @@ netdev_dpdk_class_init(void)
> >>  static int
> >>  netdev_dpdk_vhost_class_init(void)
> >>  {
> >> -    static struct ovsthread_once once = OVSTHREAD_ONCE_INITIALIZER;
> >> -
> >> -    /* This function can be called for different classes.  The initialization
> >> -     * needs to be done only once */
> >> -    if (ovsthread_once_start(&once)) {
> >> -        rte_vhost_driver_callback_register(&virtio_net_device_ops);
> >> -        rte_vhost_feature_disable(1ULL << VIRTIO_NET_F_HOST_TSO4
> >> -                                  | 1ULL << VIRTIO_NET_F_HOST_TSO6
> >> -                                  | 1ULL << VIRTIO_NET_F_CSUM);
> >> -        ovs_thread_create("vhost_thread", start_vhost_loop, NULL);
> >> -
> >> -        ovsthread_once_done(&once);
> >> -    }
> >> +    rte_eth_vhost_feature_disable(1ULL << VIRTIO_NET_F_HOST_TSO4
> >> +                                | 1ULL << VIRTIO_NET_F_HOST_TSO6
> >> +                                | 1ULL << VIRTIO_NET_F_CSUM);
> >>
> >>      return 0;
> >>  }
> >> @@ -2690,7 +2428,17 @@ netdev_dpdk_ring_send(struct netdev
> *netdev, int qid,
> >>          dp_packet_rss_invalidate(batch->packets[i]);
> >>      }
> >>
> >> -    netdev_dpdk_send__(dev, qid, batch, may_steal, concurrent_txq);
> >> +    if (OVS_UNLIKELY(concurrent_txq)) {
> >> +        qid = qid % dev->up.n_txq;
> >> +        rte_spinlock_lock(&dev->tx_q[qid].tx_lock);
> >> +    }
> >> +
> >> +    netdev_dpdk_send__(dev, qid, batch, may_steal);
> >> +
> >> +    if (OVS_UNLIKELY(concurrent_txq)) {
> >> +        rte_spinlock_unlock(&dev->tx_q[qid].tx_lock);
> >> +    }
> >> +
> >>      return 0;
> >>  }
> >>
> >> @@ -2985,10 +2733,6 @@ dpdk_vhost_reconfigure_helper(struct
> netdev_dpdk *dev)
> >>              netdev_change_seq_changed(&dev->up);
> >>          }
> >>      }
> >> -
> >> -    if (netdev_dpdk_get_vid(dev) >= 0) {
> >> -        dev->vhost_reconfigured = true;
> >> -    }
> >>  }
> >>
> >>  static int
> >> @@ -3007,6 +2751,7 @@ netdev_dpdk_vhost_client_reconfigure(struct
> netdev *netdev)
> >>  {
> >>      struct netdev_dpdk *dev = netdev_dpdk_cast(netdev);
> >>      int err = 0;
> >> +    int sid = -1;
> >>
> >>      ovs_mutex_lock(&dev->mutex);
> >>
> >> @@ -3020,14 +2765,37 @@
> netdev_dpdk_vhost_client_reconfigure(struct netdev *netdev)
> >>      if (!(dev->vhost_driver_flags & RTE_VHOST_USER_CLIENT)
> >>              && strlen(dev->vhost_id)) {
> >>          /* Register client-mode device */
> >> -        err = rte_vhost_driver_register(dev->vhost_id,
> >> -                                        RTE_VHOST_USER_CLIENT);
> >> -        if (err) {
> >> -            VLOG_ERR("vhost-user device setup failure for device %s\n",
> >> -                     dev->vhost_id);
> >> -        } else {
> >> -            /* Configuration successful */
> >> +        err = dpdk_attach_vhost_pmd(dev, 1);
> >> +
> >> +        if (!err) {
> >> +            sid = rte_eth_dev_socket_id(dev->port_id);
> >> +            dev->socket_id = sid < 0 ? SOCKET0 : sid;
> >>              dev->vhost_driver_flags |= RTE_VHOST_USER_CLIENT;
> >> +
> >> +            rte_eth_dev_callback_register(dev->port_id,
> >> +                                          RTE_ETH_EVENT_QUEUE_STATE,
> >> +                                          vring_state_changed_callback, NULL);
> >> +            rte_eth_dev_callback_register(dev->port_id,
> >> +                                          RTE_ETH_EVENT_INTR_LSC,
> >> +                                          link_status_changed_callback, NULL);
> >> +
> >> +            rte_eth_dev_stop(dev->port_id);
> >> +            rte_free(dev->tx_q);
> >> +            err = dpdk_eth_dev_init(dev);
> >> +            dev->tx_q = netdev_dpdk_alloc_txq(netdev->n_txq);
> >> +            if (!dev->tx_q) {
> >> +                err = ENOMEM;
> >
> > Need to go out here, because 'tx_q' will be used below.

Ok

> >
> >> +            }
> >> +
> >> +            /* Enable TX queue 0 by default if it wasn't disabled. */
> >> +            if (dev->tx_q[0].map == OVS_VHOST_QUEUE_MAP_UNKNOWN)
> {
> >> +                dev->tx_q[0].map = 0;
> >> +            }
> >> +
> >> +            netdev_dpdk_remap_txqs(dev);
> >> +
> >> +            netdev_change_seq_changed(netdev);
> >> +
> >>              VLOG_INFO("vHost User device '%s' created in 'client' mode, "
> >>                        "using client socket '%s'",
> >>                        dev->up.name, dev->vhost_id);
> >> @@ -3151,14 +2919,15 @@ static const struct netdev_class
> dpdk_vhost_class =
> >>          netdev_dpdk_vhost_construct,
> >>          netdev_dpdk_vhost_destruct,
> >>          NULL,
> >> -        NULL,
> >> +        netdev_dpdk_set_tx_multiq,
> >
> > Why 'netdev_dpdk_set_tx_multiq' enabled for vhost ports?

It shouldn't be. Removed in the next v.

> >
> >>          netdev_dpdk_vhost_send,
> >> -        netdev_dpdk_vhost_get_carrier,
> >> -        netdev_dpdk_vhost_get_stats,
> >> -        NULL,
> >> -        NULL,
> >> +        netdev_dpdk_get_carrier,
> >> +        netdev_dpdk_get_stats,
> >> +        netdev_dpdk_get_features,
> >> +        netdev_dpdk_get_status,
> >>          netdev_dpdk_vhost_reconfigure,
> >> -        netdev_dpdk_vhost_rxq_recv);
> >> +        netdev_dpdk_rxq_recv);
> >> +
> >>  static const struct netdev_class dpdk_vhost_client_class =
> >>      NETDEV_DPDK_CLASS(
> >>          "dpdkvhostuserclient",
> >> @@ -3166,14 +2935,14 @@ static const struct netdev_class
> dpdk_vhost_client_class =
> >>          netdev_dpdk_vhost_client_construct,
> >>          netdev_dpdk_vhost_destruct,
> >>          netdev_dpdk_vhost_client_set_config,
> >> -        NULL,
> >> +        netdev_dpdk_set_tx_multiq,
> >>          netdev_dpdk_vhost_send,
> >> -        netdev_dpdk_vhost_get_carrier,
> >> -        netdev_dpdk_vhost_get_stats,
> >> -        NULL,
> >> -        NULL,
> >> +        netdev_dpdk_get_carrier,
> >> +        netdev_dpdk_get_stats,
> >> +        netdev_dpdk_get_features,
> >> +        netdev_dpdk_get_status,
> >>          netdev_dpdk_vhost_client_reconfigure,
> >> -        netdev_dpdk_vhost_rxq_recv);
> >> +        netdev_dpdk_vhost_client_rxq_recv);
> >>
> >>  void
> >>  netdev_dpdk_register(void)
> >>



More information about the dev mailing list