net/mlx4: remove Tx inline compilation option
[dpdk.git] / drivers / net / mlx4 / mlx4.c
index f8ed42b..394b87c 100644 (file)
@@ -1,8 +1,8 @@
 /*-
  *   BSD LICENSE
  *
- *   Copyright 2012-2015 6WIND S.A.
- *   Copyright 2012 Mellanox.
+ *   Copyright 2012 6WIND S.A.
+ *   Copyright 2012 Mellanox
  *
  *   Redistribution and use in source and binary forms, with or without
  *   modification, are permitted provided that the following conditions
@@ -34,7 +34,6 @@
 /*
  * Known limitations:
  * - RSS hash key and options cannot be modified.
- * - Hardware counters aren't implemented.
  */
 
 /* System headers. */
 #include <linux/sockios.h>
 #include <fcntl.h>
 
-/* Verbs header. */
-/* ISO C doesn't support unnamed structs/unions, disabling -pedantic. */
-#ifdef PEDANTIC
-#pragma GCC diagnostic ignored "-pedantic"
-#endif
-#include <infiniband/verbs.h>
-#ifdef PEDANTIC
-#pragma GCC diagnostic error "-pedantic"
-#endif
-
-/* DPDK headers don't like -pedantic. */
-#ifdef PEDANTIC
-#pragma GCC diagnostic ignored "-pedantic"
-#endif
 #include <rte_ether.h>
 #include <rte_ethdev.h>
+#include <rte_ethdev_pci.h>
 #include <rte_dev.h>
 #include <rte_mbuf.h>
 #include <rte_errno.h>
 #include <rte_malloc.h>
 #include <rte_spinlock.h>
 #include <rte_atomic.h>
-#include <rte_version.h>
 #include <rte_log.h>
 #include <rte_alarm.h>
 #include <rte_memory.h>
-#ifdef PEDANTIC
-#pragma GCC diagnostic error "-pedantic"
-#endif
+#include <rte_flow.h>
+#include <rte_kvargs.h>
+#include <rte_interrupts.h>
+#include <rte_branch_prediction.h>
 
 /* Generated configuration header. */
 #include "mlx4_autoconf.h"
 
-/* PMD header. */
+/* PMD headers. */
 #include "mlx4.h"
-
-/* Runtime logging through RTE_LOG() is enabled when not in debugging mode.
- * Intermediate LOG_*() macros add the required end-of-line characters. */
-#ifndef NDEBUG
-#define INFO(...) DEBUG(__VA_ARGS__)
-#define WARN(...) DEBUG(__VA_ARGS__)
-#define ERROR(...) DEBUG(__VA_ARGS__)
-#else
-#define LOG__(level, m, ...) \
-       RTE_LOG(level, PMD, MLX4_DRIVER_NAME ": " m "%c", __VA_ARGS__)
-#define LOG_(level, ...) LOG__(level, __VA_ARGS__, '\n')
-#define INFO(...) LOG_(INFO, __VA_ARGS__)
-#define WARN(...) LOG_(WARNING, __VA_ARGS__)
-#define ERROR(...) LOG_(ERR, __VA_ARGS__)
-#endif
+#include "mlx4_flow.h"
 
 /* Convenience macros for accessing mbuf fields. */
 #define NEXT(m) ((m)->next)
@@ -137,196 +109,31 @@ typedef union {
         (((val) & (from)) / ((from) / (to))) : \
         (((val) & (from)) * ((to) / (from))))
 
-struct mlx4_rxq_stats {
-       unsigned int idx; /**< Mapping index. */
-#ifdef MLX4_PMD_SOFT_COUNTERS
-       uint64_t ipackets;  /**< Total of successfully received packets. */
-       uint64_t ibytes;    /**< Total of successfully received bytes. */
-#endif
-       uint64_t idropped;  /**< Total of packets dropped when RX ring full. */
-       uint64_t rx_nombuf; /**< Total of RX mbuf allocation failures. */
-};
-
-struct mlx4_txq_stats {
-       unsigned int idx; /**< Mapping index. */
-#ifdef MLX4_PMD_SOFT_COUNTERS
-       uint64_t opackets; /**< Total of successfully sent packets. */
-       uint64_t obytes;   /**< Total of successfully sent bytes. */
-#endif
-       uint64_t odropped; /**< Total of packets not sent when TX ring full. */
-};
-
-/* RX element (scattered packets). */
-struct rxq_elt_sp {
-       struct ibv_recv_wr wr; /* Work Request. */
-       struct ibv_sge sges[MLX4_PMD_SGE_WR_N]; /* Scatter/Gather Elements. */
-       struct rte_mbuf *bufs[MLX4_PMD_SGE_WR_N]; /* SGEs buffers. */
-};
-
-/* RX element. */
-struct rxq_elt {
-       struct ibv_recv_wr wr; /* Work Request. */
-       struct ibv_sge sge; /* Scatter/Gather Element. */
-       /* mbuf pointer is derived from WR_ID(wr.wr_id).offset. */
-};
-
-/* RX queue descriptor. */
-struct rxq {
-       struct priv *priv; /* Back pointer to private data. */
-       struct rte_mempool *mp; /* Memory Pool for allocations. */
-       struct ibv_mr *mr; /* Memory Region (for mp). */
-       struct ibv_cq *cq; /* Completion Queue. */
-       struct ibv_qp *qp; /* Queue Pair. */
-       struct ibv_exp_qp_burst_family *if_qp; /* QP burst interface. */
-       struct ibv_exp_cq_family *if_cq; /* CQ interface. */
-       /*
-        * Each VLAN ID requires a separate flow steering rule.
-        */
-       BITFIELD_DECLARE(mac_configured, uint32_t, MLX4_MAX_MAC_ADDRESSES);
-       struct ibv_flow *mac_flow[MLX4_MAX_MAC_ADDRESSES][MLX4_MAX_VLAN_IDS];
-       struct ibv_flow *promisc_flow; /* Promiscuous flow. */
-       struct ibv_flow *allmulti_flow; /* Multicast flow. */
-       unsigned int port_id; /* Port ID for incoming packets. */
-       unsigned int elts_n; /* (*elts)[] length. */
-       unsigned int elts_head; /* Current index in (*elts)[]. */
-       union {
-               struct rxq_elt_sp (*sp)[]; /* Scattered RX elements. */
-               struct rxq_elt (*no_sp)[]; /* RX elements. */
-       } elts;
-       unsigned int sp:1; /* Use scattered RX elements. */
-       unsigned int csum:1; /* Enable checksum offloading. */
-       unsigned int csum_l2tun:1; /* Same for L2 tunnels. */
-       struct mlx4_rxq_stats stats; /* RX queue counters. */
-       unsigned int socket; /* CPU socket ID for allocations. */
-       struct ibv_exp_res_domain *rd; /* Resource Domain. */
-};
-
-/* TX element. */
-struct txq_elt {
-       struct rte_mbuf *buf;
-};
-
-/* Linear buffer type. It is used when transmitting buffers with too many
- * segments that do not fit the hardware queue (see max_send_sge).
- * Extra segments are copied (linearized) in such buffers, replacing the
- * last SGE during TX.
- * The size is arbitrary but large enough to hold a jumbo frame with
- * 8 segments considering mbuf.buf_len is about 2048 bytes. */
-typedef uint8_t linear_t[16384];
-
-/* TX queue descriptor. */
-struct txq {
-       struct priv *priv; /* Back pointer to private data. */
+/** Configuration structure for device arguments. */
+struct mlx4_conf {
        struct {
-               const struct rte_mempool *mp; /* Cached Memory Pool. */
-               struct ibv_mr *mr; /* Memory Region (for mp). */
-               uint32_t lkey; /* mr->lkey */
-       } mp2mr[MLX4_PMD_TX_MP_CACHE]; /* MP to MR translation table. */
-       struct ibv_cq *cq; /* Completion Queue. */
-       struct ibv_qp *qp; /* Queue Pair. */
-       struct ibv_exp_qp_burst_family *if_qp; /* QP burst interface. */
-       struct ibv_exp_cq_family *if_cq; /* CQ interface. */
-#if MLX4_PMD_MAX_INLINE > 0
-       uint32_t max_inline; /* Max inline send size <= MLX4_PMD_MAX_INLINE. */
-#endif
-       unsigned int elts_n; /* (*elts)[] length. */
-       struct txq_elt (*elts)[]; /* TX elements. */
-       unsigned int elts_head; /* Current index in (*elts)[]. */
-       unsigned int elts_tail; /* First element awaiting completion. */
-       unsigned int elts_comp; /* Number of completion requests. */
-       unsigned int elts_comp_cd; /* Countdown for next completion request. */
-       unsigned int elts_comp_cd_init; /* Initial value for countdown. */
-       struct mlx4_txq_stats stats; /* TX queue counters. */
-       linear_t (*elts_linear)[]; /* Linearized buffers. */
-       struct ibv_mr *mr_linear; /* Memory Region for linearized buffers. */
-       unsigned int socket; /* CPU socket ID for allocations. */
-       struct ibv_exp_res_domain *rd; /* Resource Domain. */
+               uint32_t present; /**< Bit-field for existing ports. */
+               uint32_t enabled; /**< Bit-field for user-enabled ports. */
+       } ports;
 };
 
-struct priv {
-       struct rte_eth_dev *dev; /* Ethernet device. */
-       struct ibv_context *ctx; /* Verbs context. */
-       struct ibv_device_attr device_attr; /* Device properties. */
-       struct ibv_pd *pd; /* Protection Domain. */
-       /*
-        * MAC addresses array and configuration bit-field.
-        * An extra entry that cannot be modified by the DPDK is reserved
-        * for broadcast frames (destination MAC address ff:ff:ff:ff:ff:ff).
-        */
-       struct ether_addr mac[MLX4_MAX_MAC_ADDRESSES];
-       BITFIELD_DECLARE(mac_configured, uint32_t, MLX4_MAX_MAC_ADDRESSES);
-       /* VLAN filters. */
-       struct {
-               unsigned int enabled:1; /* If enabled. */
-               unsigned int id:12; /* VLAN ID (0-4095). */
-       } vlan_filter[MLX4_MAX_VLAN_IDS]; /* VLAN filters table. */
-       /* Device properties. */
-       uint16_t mtu; /* Configured MTU. */
-       uint8_t port; /* Physical port number. */
-       unsigned int started:1; /* Device started, flows enabled. */
-       unsigned int promisc:1; /* Device in promiscuous mode. */
-       unsigned int allmulti:1; /* Device receives all multicast packets. */
-       unsigned int hw_qpg:1; /* QP groups are supported. */
-       unsigned int hw_tss:1; /* TSS is supported. */
-       unsigned int hw_rss:1; /* RSS is supported. */
-       unsigned int hw_csum:1; /* Checksum offload is supported. */
-       unsigned int hw_csum_l2tun:1; /* Same for L2 tunnels. */
-       unsigned int rss:1; /* RSS is enabled. */
-       unsigned int vf:1; /* This is a VF device. */
-       unsigned int pending_alarm:1; /* An alarm is pending. */
-#ifdef INLINE_RECV
-       unsigned int inl_recv_size; /* Inline recv size */
-#endif
-       unsigned int max_rss_tbl_sz; /* Maximum number of RSS queues. */
-       /* RX/TX queues. */
-       struct rxq rxq_parent; /* Parent queue when RSS is enabled. */
-       unsigned int rxqs_n; /* RX queues array size. */
-       unsigned int txqs_n; /* TX queues array size. */
-       struct rxq *(*rxqs)[]; /* RX queues. */
-       struct txq *(*txqs)[]; /* TX queues. */
-       struct rte_intr_handle intr_handle; /* Interrupt handler. */
-       rte_spinlock_t lock; /* Lock for control functions. */
+/* Available parameters list. */
+const char *pmd_mlx4_init_params[] = {
+       MLX4_PMD_PORT_KVARG,
+       NULL,
 };
 
-/* Local storage for secondary process data. */
-struct mlx4_secondary_data {
-       struct rte_eth_dev_data data; /* Local device data. */
-       struct priv *primary_priv; /* Private structure from primary. */
-       struct rte_eth_dev_data *shared_dev_data; /* Shared device data. */
-       rte_spinlock_t lock; /* Port configuration lock. */
-} mlx4_secondary_data[RTE_MAX_ETHPORTS];
+static int
+mlx4_rx_intr_enable(struct rte_eth_dev *dev, uint16_t idx);
 
-/**
- * Check if running as a secondary process.
- *
- * @return
- *   Nonzero if running as a secondary process.
- */
-static inline int
-mlx4_is_secondary(void)
-{
-       return rte_eal_process_type() != RTE_PROC_PRIMARY;
-}
+static int
+mlx4_rx_intr_disable(struct rte_eth_dev *dev, uint16_t idx);
 
-/**
- * Return private structure associated with an Ethernet device.
- *
- * @param dev
- *   Pointer to Ethernet device structure.
- *
- * @return
- *   Pointer to private structure.
- */
-static struct priv *
-mlx4_get_priv(struct rte_eth_dev *dev)
-{
-       struct mlx4_secondary_data *sd;
+static int
+priv_rx_intr_vec_enable(struct priv *priv);
 
-       if (!mlx4_is_secondary())
-               return dev->data->dev_private;
-       sd = &mlx4_secondary_data[dev->data->port_id];
-       return sd->data.dev_private;
-}
+static void
+priv_rx_intr_vec_disable(struct priv *priv);
 
 /**
  * Lock private structure to protect it from concurrent access in the
@@ -335,8 +142,7 @@ mlx4_get_priv(struct rte_eth_dev *dev)
  * @param priv
  *   Pointer to private structure.
  */
-static void
-priv_lock(struct priv *priv)
+void priv_lock(struct priv *priv)
 {
        rte_spinlock_lock(&priv->lock);
 }
@@ -347,8 +153,7 @@ priv_lock(struct priv *priv)
  * @param priv
  *   Pointer to private structure.
  */
-static void
-priv_unlock(struct priv *priv)
+void priv_unlock(struct priv *priv)
 {
        rte_spinlock_unlock(&priv->lock);
 }
@@ -689,7 +494,7 @@ priv_set_flags(struct priv *priv, unsigned int keep, unsigned int flags)
        if (priv_get_sysfs_ulong(priv, "flags", &tmp) == -1)
                return -1;
        tmp &= keep;
-       tmp |= flags;
+       tmp |= (flags & (~keep));
        return priv_set_sysfs_ulong(priv, "flags", tmp);
 }
 
@@ -704,12 +509,92 @@ txq_cleanup(struct txq *txq);
 
 static int
 rxq_setup(struct rte_eth_dev *dev, struct rxq *rxq, uint16_t desc,
-         unsigned int socket, int inactive, const struct rte_eth_rxconf *conf,
-         struct rte_mempool *mp);
+         unsigned int socket, int inactive,
+         const struct rte_eth_rxconf *conf,
+         struct rte_mempool *mp, int children_n,
+         struct rxq *rxq_parent);
 
 static void
 rxq_cleanup(struct rxq *rxq);
 
+/**
+ * Create RSS parent queue.
+ *
+ * The new parent is inserted in front of the list in the private structure.
+ *
+ * @param priv
+ *   Pointer to private structure.
+ * @param queues
+ *   Queues indices array, if NULL use all Rx queues.
+ * @param children_n
+ *   The number of entries in queues[].
+ *
+ * @return
+ *   Pointer to a parent rxq structure, NULL on failure.
+ */
+struct rxq *
+priv_parent_create(struct priv *priv,
+                  uint16_t queues[],
+                  uint16_t children_n)
+{
+       int ret;
+       uint16_t i;
+       struct rxq *parent;
+
+       parent = rte_zmalloc("parent queue",
+                            sizeof(*parent),
+                            RTE_CACHE_LINE_SIZE);
+       if (!parent) {
+               ERROR("cannot allocate memory for RSS parent queue");
+               return NULL;
+       }
+       ret = rxq_setup(priv->dev, parent, 0, 0, 0,
+                       NULL, NULL, children_n, NULL);
+       if (ret) {
+               rte_free(parent);
+               return NULL;
+       }
+       parent->rss.queues_n = children_n;
+       if (queues) {
+               for (i = 0; i < children_n; ++i)
+                       parent->rss.queues[i] = queues[i];
+       } else {
+               /* the default RSS ring case */
+               assert(priv->rxqs_n == children_n);
+               for (i = 0; i < priv->rxqs_n; ++i)
+                       parent->rss.queues[i] = i;
+       }
+       LIST_INSERT_HEAD(&priv->parents, parent, next);
+       return parent;
+}
+
+/**
+ * Clean up RX queue parent structure.
+ *
+ * @param parent
+ *   RX queue parent structure.
+ */
+void
+rxq_parent_cleanup(struct rxq *parent)
+{
+       LIST_REMOVE(parent, next);
+       rxq_cleanup(parent);
+       rte_free(parent);
+}
+
+/**
+ * Clean up parent structures from the parent list.
+ *
+ * @param priv
+ *   Pointer to private structure.
+ */
+static void
+priv_parent_list_cleanup(struct priv *priv)
+{
+       while (!LIST_EMPTY(&priv->parents))
+               rxq_parent_cleanup(LIST_FIRST(&priv->parents));
+}
+
 /**
  * Ethernet device configuration.
  *
@@ -729,7 +614,6 @@ dev_configure(struct rte_eth_dev *dev)
        unsigned int rxqs_n = dev->data->nb_rx_queues;
        unsigned int txqs_n = dev->data->nb_tx_queues;
        unsigned int tmp;
-       int ret;
 
        priv->rxqs = (void *)dev->data->rx_queues;
        priv->txqs = (void *)dev->data->tx_queues;
@@ -740,7 +624,7 @@ dev_configure(struct rte_eth_dev *dev)
        }
        if (rxqs_n == priv->rxqs_n)
                return 0;
-       if (!rte_is_power_of_2(rxqs_n)) {
+       if (!rte_is_power_of_2(rxqs_n) && !priv->isolated) {
                unsigned n_active;
 
                n_active = rte_align32pow2(rxqs_n + 1) >> 1;
@@ -759,7 +643,7 @@ dev_configure(struct rte_eth_dev *dev)
                for (i = 0; (i != priv->rxqs_n); ++i)
                        if ((*priv->rxqs)[i] != NULL)
                                return EINVAL;
-               rxq_cleanup(&priv->rxq_parent);
+               priv_parent_list_cleanup(priv);
                priv->rss = 0;
                priv->rxqs_n = 0;
        }
@@ -784,14 +668,14 @@ dev_configure(struct rte_eth_dev *dev)
        priv->rss = 1;
        tmp = priv->rxqs_n;
        priv->rxqs_n = rxqs_n;
-       ret = rxq_setup(dev, &priv->rxq_parent, 0, 0, 0, NULL, NULL);
-       if (!ret)
+       if (priv->isolated)
+               return 0;
+       if (priv_parent_create(priv, NULL, priv->rxqs_n))
                return 0;
        /* Failure, rollback. */
        priv->rss = 0;
        priv->rxqs_n = tmp;
-       assert(ret > 0);
-       return ret;
+       return ENOMEM;
 }
 
 /**
@@ -809,8 +693,6 @@ mlx4_dev_configure(struct rte_eth_dev *dev)
        struct priv *priv = dev->data->dev_private;
        int ret;
 
-       if (mlx4_is_secondary())
-               return -E_RTE_SECONDARY;
        priv_lock(priv);
        ret = dev_configure(dev);
        assert(ret >= 0);
@@ -821,157 +703,6 @@ mlx4_dev_configure(struct rte_eth_dev *dev)
 static uint16_t mlx4_tx_burst(void *, struct rte_mbuf **, uint16_t);
 static uint16_t removed_rx_burst(void *, struct rte_mbuf **, uint16_t);
 
-/**
- * Configure secondary process queues from a private data pointer (primary
- * or secondary) and update burst callbacks. Can take place only once.
- *
- * All queues must have been previously created by the primary process to
- * avoid undefined behavior.
- *
- * @param priv
- *   Private data pointer from either primary or secondary process.
- *
- * @return
- *   Private data pointer from secondary process, NULL in case of error.
- */
-static struct priv *
-mlx4_secondary_data_setup(struct priv *priv)
-{
-       unsigned int port_id = 0;
-       struct mlx4_secondary_data *sd;
-       void **tx_queues;
-       void **rx_queues;
-       unsigned int nb_tx_queues;
-       unsigned int nb_rx_queues;
-       unsigned int i;
-
-       /* priv must be valid at this point. */
-       assert(priv != NULL);
-       /* priv->dev must also be valid but may point to local memory from
-        * another process, possibly with the same address and must not
-        * be dereferenced yet. */
-       assert(priv->dev != NULL);
-       /* Determine port ID by finding out where priv comes from. */
-       while (1) {
-               sd = &mlx4_secondary_data[port_id];
-               rte_spinlock_lock(&sd->lock);
-               /* Primary process? */
-               if (sd->primary_priv == priv)
-                       break;
-               /* Secondary process? */
-               if (sd->data.dev_private == priv)
-                       break;
-               rte_spinlock_unlock(&sd->lock);
-               if (++port_id == RTE_DIM(mlx4_secondary_data))
-                       port_id = 0;
-       }
-       /* Switch to secondary private structure. If private data has already
-        * been updated by another thread, there is nothing else to do. */
-       priv = sd->data.dev_private;
-       if (priv->dev->data == &sd->data)
-               goto end;
-       /* Sanity checks. Secondary private structure is supposed to point
-        * to local eth_dev, itself still pointing to the shared device data
-        * structure allocated by the primary process. */
-       assert(sd->shared_dev_data != &sd->data);
-       assert(sd->data.nb_tx_queues == 0);
-       assert(sd->data.tx_queues == NULL);
-       assert(sd->data.nb_rx_queues == 0);
-       assert(sd->data.rx_queues == NULL);
-       assert(priv != sd->primary_priv);
-       assert(priv->dev->data == sd->shared_dev_data);
-       assert(priv->txqs_n == 0);
-       assert(priv->txqs == NULL);
-       assert(priv->rxqs_n == 0);
-       assert(priv->rxqs == NULL);
-       nb_tx_queues = sd->shared_dev_data->nb_tx_queues;
-       nb_rx_queues = sd->shared_dev_data->nb_rx_queues;
-       /* Allocate local storage for queues. */
-       tx_queues = rte_zmalloc("secondary ethdev->tx_queues",
-                               sizeof(sd->data.tx_queues[0]) * nb_tx_queues,
-                               RTE_CACHE_LINE_SIZE);
-       rx_queues = rte_zmalloc("secondary ethdev->rx_queues",
-                               sizeof(sd->data.rx_queues[0]) * nb_rx_queues,
-                               RTE_CACHE_LINE_SIZE);
-       if (tx_queues == NULL || rx_queues == NULL)
-               goto error;
-       /* Lock to prevent control operations during setup. */
-       priv_lock(priv);
-       /* TX queues. */
-       for (i = 0; i != nb_tx_queues; ++i) {
-               struct txq *primary_txq = (*sd->primary_priv->txqs)[i];
-               struct txq *txq;
-
-               if (primary_txq == NULL)
-                       continue;
-               txq = rte_calloc_socket("TXQ", 1, sizeof(*txq), 0,
-                                       primary_txq->socket);
-               if (txq != NULL) {
-                       if (txq_setup(priv->dev,
-                                     txq,
-                                     primary_txq->elts_n * MLX4_PMD_SGE_WR_N,
-                                     primary_txq->socket,
-                                     NULL) == 0) {
-                               txq->stats.idx = primary_txq->stats.idx;
-                               tx_queues[i] = txq;
-                               continue;
-                       }
-                       rte_free(txq);
-               }
-               while (i) {
-                       txq = tx_queues[--i];
-                       txq_cleanup(txq);
-                       rte_free(txq);
-               }
-               goto error;
-       }
-       /* RX queues. */
-       for (i = 0; i != nb_rx_queues; ++i) {
-               struct rxq *primary_rxq = (*sd->primary_priv->rxqs)[i];
-
-               if (primary_rxq == NULL)
-                       continue;
-               /* Not supported yet. */
-               rx_queues[i] = NULL;
-       }
-       /* Update everything. */
-       priv->txqs = (void *)tx_queues;
-       priv->txqs_n = nb_tx_queues;
-       priv->rxqs = (void *)rx_queues;
-       priv->rxqs_n = nb_rx_queues;
-       sd->data.rx_queues = rx_queues;
-       sd->data.tx_queues = tx_queues;
-       sd->data.nb_rx_queues = nb_rx_queues;
-       sd->data.nb_tx_queues = nb_tx_queues;
-       sd->data.dev_link = sd->shared_dev_data->dev_link;
-       sd->data.mtu = sd->shared_dev_data->mtu;
-       memcpy(sd->data.rx_queue_state, sd->shared_dev_data->rx_queue_state,
-              sizeof(sd->data.rx_queue_state));
-       memcpy(sd->data.tx_queue_state, sd->shared_dev_data->tx_queue_state,
-              sizeof(sd->data.tx_queue_state));
-       sd->data.dev_flags = sd->shared_dev_data->dev_flags;
-       /* Use local data from now on. */
-       rte_mb();
-       priv->dev->data = &sd->data;
-       rte_mb();
-       priv->dev->tx_pkt_burst = mlx4_tx_burst;
-       priv->dev->rx_pkt_burst = removed_rx_burst;
-       priv_unlock(priv);
-end:
-       /* More sanity checks. */
-       assert(priv->dev->tx_pkt_burst == mlx4_tx_burst);
-       assert(priv->dev->rx_pkt_burst == removed_rx_burst);
-       assert(priv->dev->data == &sd->data);
-       rte_spinlock_unlock(&sd->lock);
-       return priv;
-error:
-       priv_unlock(priv);
-       rte_free(tx_queues);
-       rte_free(rx_queues);
-       rte_spinlock_unlock(&sd->lock);
-       return NULL;
-}
-
 /* TX queues handling. */
 
 /**
@@ -1178,10 +909,6 @@ txq_complete(struct txq *txq)
 
        if (unlikely(elts_comp == 0))
                return 0;
-#ifdef DEBUG_SEND
-       DEBUG("%p: processing %u work requests completions",
-             (void *)txq, elts_comp);
-#endif
        wcs_n = txq->if_cq->poll_cnt(txq->cq, elts_comp);
        if (unlikely(wcs_n == 0))
                return 0;
@@ -1269,7 +996,7 @@ static int mlx4_check_mempool(struct rte_mempool *mp, uintptr_t *start,
 
 /* For best performance, this function should not be inlined. */
 static struct ibv_mr *mlx4_mp2mr(struct ibv_pd *, struct rte_mempool *)
-       __attribute__((noinline));
+       __rte_noinline;
 
 /**
  * Register mempool as a memory region.
@@ -1449,8 +1176,6 @@ txq_mp2mr_iter(struct rte_mempool *mp, void *arg)
        txq_mp2mr(txq, mp);
 }
 
-#if MLX4_PMD_SGE_WR_N > 1
-
 /**
  * Copy scattered mbuf contents to a single linear buffer.
  *
@@ -1597,8 +1322,6 @@ stop:
        };
 }
 
-#endif /* MLX4_PMD_SGE_WR_N > 1 */
-
 /**
  * DPDK callback for TX.
  *
@@ -1644,9 +1367,7 @@ mlx4_tx_burst(void *dpdk_txq, struct rte_mbuf **pkts, uint16_t pkts_n)
                struct txq_elt *elt_next = &(*txq->elts)[elts_head_next];
                struct txq_elt *elt = &(*txq->elts)[elts_head];
                unsigned int segs = NB_SEGS(buf);
-#ifdef MLX4_PMD_SOFT_COUNTERS
                unsigned int sent_size = 0;
-#endif
                uint32_t send_flags = 0;
 
                /* Clean up old buffer. */
@@ -1707,7 +1428,6 @@ mlx4_tx_burst(void *dpdk_txq, struct rte_mbuf **pkts, uint16_t pkts_n)
                                              (uintptr_t)addr);
                        RTE_MBUF_PREFETCH_TO_FREE(elt_next->buf);
                        /* Put packet into send queue. */
-#if MLX4_PMD_MAX_INLINE > 0
                        if (length <= txq->max_inline)
                                err = txq->if_qp->send_pending_inline
                                        (txq->qp,
@@ -1715,7 +1435,6 @@ mlx4_tx_burst(void *dpdk_txq, struct rte_mbuf **pkts, uint16_t pkts_n)
                                         length,
                                         send_flags);
                        else
-#endif
                                err = txq->if_qp->send_pending
                                        (txq->qp,
                                         addr,
@@ -1724,11 +1443,8 @@ mlx4_tx_burst(void *dpdk_txq, struct rte_mbuf **pkts, uint16_t pkts_n)
                                         send_flags);
                        if (unlikely(err))
                                goto stop;
-#ifdef MLX4_PMD_SOFT_COUNTERS
                        sent_size += length;
-#endif
                } else {
-#if MLX4_PMD_SGE_WR_N > 1
                        struct ibv_sge sges[MLX4_PMD_SGE_WR_N];
                        struct tx_burst_sg_ret ret;
 
@@ -1745,29 +1461,18 @@ mlx4_tx_burst(void *dpdk_txq, struct rte_mbuf **pkts, uint16_t pkts_n)
                                 send_flags);
                        if (unlikely(err))
                                goto stop;
-#ifdef MLX4_PMD_SOFT_COUNTERS
                        sent_size += ret.length;
-#endif
-#else /* MLX4_PMD_SGE_WR_N > 1 */
-                       DEBUG("%p: TX scattered buffers support not"
-                             " compiled in", (void *)txq);
-                       goto stop;
-#endif /* MLX4_PMD_SGE_WR_N > 1 */
                }
                elts_head = elts_head_next;
-#ifdef MLX4_PMD_SOFT_COUNTERS
                /* Increment sent bytes counter. */
                txq->stats.obytes += sent_size;
-#endif
        }
 stop:
        /* Take a shortcut if nothing must be sent. */
        if (unlikely(i == 0))
                return 0;
-#ifdef MLX4_PMD_SOFT_COUNTERS
        /* Increment sent packets counter. */
        txq->stats.opackets += i;
-#endif
        /* Ring QP doorbell. */
        err = txq->if_qp->send_flush(txq->qp);
        if (unlikely(err)) {
@@ -1782,46 +1487,6 @@ stop:
        return i;
 }
 
-/**
- * DPDK callback for TX in secondary processes.
- *
- * This function configures all queues from primary process information
- * if necessary before reverting to the normal TX burst callback.
- *
- * @param dpdk_txq
- *   Generic pointer to TX queue structure.
- * @param[in] pkts
- *   Packets to transmit.
- * @param pkts_n
- *   Number of packets in array.
- *
- * @return
- *   Number of packets successfully transmitted (<= pkts_n).
- */
-static uint16_t
-mlx4_tx_burst_secondary_setup(void *dpdk_txq, struct rte_mbuf **pkts,
-                             uint16_t pkts_n)
-{
-       struct txq *txq = dpdk_txq;
-       struct priv *priv = mlx4_secondary_data_setup(txq->priv);
-       struct priv *primary_priv;
-       unsigned int index;
-
-       if (priv == NULL)
-               return 0;
-       primary_priv =
-               mlx4_secondary_data[priv->dev->data->port_id].primary_priv;
-       /* Look for queue index in both private structures. */
-       for (index = 0; index != priv->txqs_n; ++index)
-               if (((*primary_priv->txqs)[index] == txq) ||
-                   ((*priv->txqs)[index] == txq))
-                       break;
-       if (index == priv->txqs_n)
-               return 0;
-       txq = (*priv->txqs)[index];
-       return priv->dev->tx_pkt_burst(txq, pkts, pkts_n);
-}
-
 /**
  * Configure a TX queue.
  *
@@ -1843,7 +1508,7 @@ static int
 txq_setup(struct rte_eth_dev *dev, struct txq *txq, uint16_t desc,
          unsigned int socket, const struct rte_eth_txconf *conf)
 {
-       struct priv *priv = mlx4_get_priv(dev);
+       struct priv *priv = dev->data->dev_private;
        struct txq tmpl = {
                .priv = priv,
                .socket = socket
@@ -1911,9 +1576,7 @@ txq_setup(struct rte_eth_dev *dev, struct txq *txq, uint16_t desc,
                                          MLX4_PMD_SGE_WR_N) ?
                                         priv->device_attr.max_sge :
                                         MLX4_PMD_SGE_WR_N),
-#if MLX4_PMD_MAX_INLINE > 0
                        .max_inline_data = MLX4_PMD_MAX_INLINE,
-#endif
                },
                .qp_type = IBV_QPT_RAW_PACKET,
                /* Do *NOT* enable this, completions events are managed per
@@ -1931,10 +1594,8 @@ txq_setup(struct rte_eth_dev *dev, struct txq *txq, uint16_t desc,
                      (void *)dev, strerror(ret));
                goto error;
        }
-#if MLX4_PMD_MAX_INLINE > 0
        /* ibv_create_qp() updates this value. */
        tmpl.max_inline = attr.init.cap.max_inline_data;
-#endif
        attr.mod = (struct ibv_exp_qp_attr){
                /* Move the QP to this state. */
                .qp_state = IBV_QPS_INIT,
@@ -2039,8 +1700,6 @@ mlx4_tx_queue_setup(struct rte_eth_dev *dev, uint16_t idx, uint16_t desc,
        struct txq *txq = (*priv->txqs)[idx];
        int ret;
 
-       if (mlx4_is_secondary())
-               return -E_RTE_SECONDARY;
        priv_lock(priv);
        DEBUG("%p: configuring queue %u for %u descriptors",
              (void *)dev, idx, desc);
@@ -2096,8 +1755,6 @@ mlx4_tx_queue_release(void *dpdk_txq)
        struct priv *priv;
        unsigned int i;
 
-       if (mlx4_is_secondary())
-               return;
        if (txq == NULL)
                return;
        priv = txq->priv;
@@ -2526,6 +2183,7 @@ rxq_add_flow(struct rxq *rxq, unsigned int mac_index, unsigned int vlan_index)
        assert(((uint8_t *)attr + sizeof(*attr)) == (uint8_t *)spec);
        *attr = (struct ibv_flow_attr){
                .type = IBV_FLOW_ATTR_NORMAL,
+               .priority = 3,
                .num_of_specs = 1,
                .port = priv->port,
                .flags = 0
@@ -2669,11 +2327,12 @@ priv_mac_addr_del(struct priv *priv, unsigned int mac_index)
 {
        unsigned int i;
 
+       assert(!priv->isolated);
        assert(mac_index < elemof(priv->mac));
        if (!BITFIELD_ISSET(priv->mac_configured, mac_index))
                return;
        if (priv->rss) {
-               rxq_mac_addr_del(&priv->rxq_parent, mac_index);
+               rxq_mac_addr_del(LIST_FIRST(&priv->parents), mac_index);
                goto end;
        }
        for (i = 0; (i != priv->dev->data->nb_rx_queues); ++i)
@@ -2740,7 +2399,7 @@ priv_mac_addr_add(struct priv *priv, unsigned int mac_index,
                goto end;
        }
        if (priv->rss) {
-               ret = rxq_mac_addr_add(&priv->rxq_parent, mac_index);
+               ret = rxq_mac_addr_add(LIST_FIRST(&priv->parents), mac_index);
                if (ret)
                        return ret;
                goto end;
@@ -2918,14 +2577,17 @@ rxq_cleanup(struct rxq *rxq)
                                                rxq->if_cq,
                                                &params));
        }
-       if (rxq->qp != NULL) {
+       if (rxq->qp != NULL && !rxq->priv->isolated) {
                rxq_promiscuous_disable(rxq);
                rxq_allmulticast_disable(rxq);
                rxq_mac_addrs_del(rxq);
-               claim_zero(ibv_destroy_qp(rxq->qp));
        }
+       if (rxq->qp != NULL)
+               claim_zero(ibv_destroy_qp(rxq->qp));
        if (rxq->cq != NULL)
                claim_zero(ibv_destroy_cq(rxq->cq));
+       if (rxq->channel != NULL)
+               claim_zero(ibv_destroy_comp_channel(rxq->channel));
        if (rxq->rd != NULL) {
                struct ibv_exp_destroy_res_domain_attr attr = {
                        .comp_mask = 0,
@@ -2961,19 +2623,25 @@ rxq_cq_to_pkt_type(uint32_t flags)
        if (flags & IBV_EXP_CQ_RX_TUNNEL_PACKET)
                pkt_type =
                        TRANSPOSE(flags,
-                                 IBV_EXP_CQ_RX_OUTER_IPV4_PACKET, RTE_PTYPE_L3_IPV4) |
+                                 IBV_EXP_CQ_RX_OUTER_IPV4_PACKET,
+                                 RTE_PTYPE_L3_IPV4_EXT_UNKNOWN) |
                        TRANSPOSE(flags,
-                                 IBV_EXP_CQ_RX_OUTER_IPV6_PACKET, RTE_PTYPE_L3_IPV6) |
+                                 IBV_EXP_CQ_RX_OUTER_IPV6_PACKET,
+                                 RTE_PTYPE_L3_IPV6_EXT_UNKNOWN) |
                        TRANSPOSE(flags,
-                                 IBV_EXP_CQ_RX_IPV4_PACKET, RTE_PTYPE_INNER_L3_IPV4) |
+                                 IBV_EXP_CQ_RX_IPV4_PACKET,
+                                 RTE_PTYPE_INNER_L3_IPV4_EXT_UNKNOWN) |
                        TRANSPOSE(flags,
-                                 IBV_EXP_CQ_RX_IPV6_PACKET, RTE_PTYPE_INNER_L3_IPV6);
+                                 IBV_EXP_CQ_RX_IPV6_PACKET,
+                                 RTE_PTYPE_INNER_L3_IPV6_EXT_UNKNOWN);
        else
                pkt_type =
                        TRANSPOSE(flags,
-                                 IBV_EXP_CQ_RX_IPV4_PACKET, RTE_PTYPE_L3_IPV4) |
+                                 IBV_EXP_CQ_RX_IPV4_PACKET,
+                                 RTE_PTYPE_L3_IPV4_EXT_UNKNOWN) |
                        TRANSPOSE(flags,
-                                 IBV_EXP_CQ_RX_IPV6_PACKET, RTE_PTYPE_L3_IPV6);
+                                 IBV_EXP_CQ_RX_IPV6_PACKET,
+                                 RTE_PTYPE_L3_IPV6_EXT_UNKNOWN);
        return pkt_type;
 }
 
@@ -2995,25 +2663,20 @@ rxq_cq_to_ol_flags(const struct rxq *rxq, uint32_t flags)
 
        if (rxq->csum)
                ol_flags |=
-                       TRANSPOSE(~flags,
+                       TRANSPOSE(flags,
                                  IBV_EXP_CQ_RX_IP_CSUM_OK,
-                                 PKT_RX_IP_CKSUM_BAD) |
-                       TRANSPOSE(~flags,
+                                 PKT_RX_IP_CKSUM_GOOD) |
+                       TRANSPOSE(flags,
                                  IBV_EXP_CQ_RX_TCP_UDP_CSUM_OK,
-                                 PKT_RX_L4_CKSUM_BAD);
-       /*
-        * PKT_RX_IP_CKSUM_BAD and PKT_RX_L4_CKSUM_BAD are used in place
-        * of PKT_RX_EIP_CKSUM_BAD because the latter is not functional
-        * (its value is 0).
-        */
+                                 PKT_RX_L4_CKSUM_GOOD);
        if ((flags & IBV_EXP_CQ_RX_TUNNEL_PACKET) && (rxq->csum_l2tun))
                ol_flags |=
-                       TRANSPOSE(~flags,
+                       TRANSPOSE(flags,
                                  IBV_EXP_CQ_RX_OUTER_IP_CSUM_OK,
-                                 PKT_RX_IP_CKSUM_BAD) |
-                       TRANSPOSE(~flags,
+                                 PKT_RX_IP_CKSUM_GOOD) |
+                       TRANSPOSE(flags,
                                  IBV_EXP_CQ_RX_OUTER_TCP_UDP_CSUM_OK,
-                                 PKT_RX_L4_CKSUM_BAD);
+                                 PKT_RX_L4_CKSUM_GOOD);
        return ol_flags;
 }
 
@@ -3096,10 +2759,8 @@ mlx4_rx_burst_sp(void *dpdk_rxq, struct rte_mbuf **pkts, uint16_t pkts_n)
                                      " completion status (%d): %s",
                                      (void *)rxq, wc.wr_id, wc.status,
                                      ibv_wc_status_str(wc.status));
-#ifdef MLX4_PMD_SOFT_COUNTERS
                                /* Increment dropped packets counter. */
                                ++rxq->stats.idropped;
-#endif
                                /* Link completed WRs together for repost. */
                                *next = wr;
                                next = &wr->next;
@@ -3156,6 +2817,13 @@ mlx4_rx_burst_sp(void *dpdk_rxq, struct rte_mbuf **pkts, uint16_t pkts_n)
                        NB_SEGS(rep) = 0x2a;
                        PORT(rep) = 0x2a;
                        rep->ol_flags = -1;
+                       /*
+                        * Clear special flags in mbuf to avoid
+                        * crashing while freeing.
+                        */
+                       rep->ol_flags &=
+                               ~(uint64_t)(IND_ATTACHED_MBUF |
+                                           CTRL_MBUF_FLAG);
 #endif
                        assert(rep->buf_len == seg->buf_len);
                        /* Reconfigure sge to use rep instead of seg. */
@@ -3204,10 +2872,8 @@ mlx4_rx_burst_sp(void *dpdk_rxq, struct rte_mbuf **pkts, uint16_t pkts_n)
                /* Return packet. */
                *(pkts++) = pkt_buf;
                ++pkts_ret;
-#ifdef MLX4_PMD_SOFT_COUNTERS
                /* Increase bytes counter. */
                rxq->stats.ibytes += pkt_buf_len;
-#endif
 repost:
                if (++elts_head >= elts_n)
                        elts_head = 0;
@@ -3217,9 +2883,6 @@ repost:
                return 0;
        *next = NULL;
        /* Repost WRs. */
-#ifdef DEBUG_RECV
-       DEBUG("%p: reposting %d WRs", (void *)rxq, i);
-#endif
        ret = ibv_post_recv(rxq->qp, head.next, &bad_wr);
        if (unlikely(ret)) {
                /* Inability to repost WRs is fatal. */
@@ -3230,10 +2893,8 @@ repost:
                abort();
        }
        rxq->elts_head = elts_head;
-#ifdef MLX4_PMD_SOFT_COUNTERS
        /* Increase packets counter. */
        rxq->stats.ipackets += pkts_ret;
-#endif
        return pkts_ret;
 }
 
@@ -3314,10 +2975,8 @@ mlx4_rx_burst(void *dpdk_rxq, struct rte_mbuf **pkts, uint16_t pkts_n)
                                      " completion status (%d): %s",
                                      (void *)rxq, wc.wr_id, wc.status,
                                      ibv_wc_status_str(wc.status));
-#ifdef MLX4_PMD_SOFT_COUNTERS
                                /* Increment dropped packets counter. */
                                ++rxq->stats.idropped;
-#endif
                                /* Add SGE to array for repost. */
                                sges[i] = elt->sge;
                                goto repost;
@@ -3339,6 +2998,8 @@ mlx4_rx_burst(void *dpdk_rxq, struct rte_mbuf **pkts, uint16_t pkts_n)
                        /* Increase out of memory counters. */
                        ++rxq->stats.rx_nombuf;
                        ++rxq->priv->dev->data->rx_mbuf_alloc_failed;
+                       /* Add SGE to array for repost. */
+                       sges[i] = elt->sge;
                        goto repost;
                }
 
@@ -3366,10 +3027,8 @@ mlx4_rx_burst(void *dpdk_rxq, struct rte_mbuf **pkts, uint16_t pkts_n)
                /* Return packet. */
                *(pkts++) = seg;
                ++pkts_ret;
-#ifdef MLX4_PMD_SOFT_COUNTERS
                /* Increase bytes counter. */
                rxq->stats.ibytes += len;
-#endif
 repost:
                if (++elts_head >= elts_n)
                        elts_head = 0;
@@ -3378,9 +3037,6 @@ repost:
        if (unlikely(i == 0))
                return 0;
        /* Repost WRs. */
-#ifdef DEBUG_RECV
-       DEBUG("%p: reposting %u WRs", (void *)rxq, i);
-#endif
        ret = rxq->if_qp->recv_burst(rxq->qp, sges, i);
        if (unlikely(ret)) {
                /* Inability to repost WRs is fatal. */
@@ -3390,53 +3046,11 @@ repost:
                abort();
        }
        rxq->elts_head = elts_head;
-#ifdef MLX4_PMD_SOFT_COUNTERS
        /* Increase packets counter. */
        rxq->stats.ipackets += pkts_ret;
-#endif
        return pkts_ret;
 }
 
-/**
- * DPDK callback for RX in secondary processes.
- *
- * This function configures all queues from primary process information
- * if necessary before reverting to the normal RX burst callback.
- *
- * @param dpdk_rxq
- *   Generic pointer to RX queue structure.
- * @param[out] pkts
- *   Array to store received packets.
- * @param pkts_n
- *   Maximum number of packets in array.
- *
- * @return
- *   Number of packets successfully received (<= pkts_n).
- */
-static uint16_t
-mlx4_rx_burst_secondary_setup(void *dpdk_rxq, struct rte_mbuf **pkts,
-                             uint16_t pkts_n)
-{
-       struct rxq *rxq = dpdk_rxq;
-       struct priv *priv = mlx4_secondary_data_setup(rxq->priv);
-       struct priv *primary_priv;
-       unsigned int index;
-
-       if (priv == NULL)
-               return 0;
-       primary_priv =
-               mlx4_secondary_data[priv->dev->data->port_id].primary_priv;
-       /* Look for queue index in both private structures. */
-       for (index = 0; index != priv->rxqs_n; ++index)
-               if (((*primary_priv->rxqs)[index] == rxq) ||
-                   ((*priv->rxqs)[index] == rxq))
-                       break;
-       if (index == priv->rxqs_n)
-               return 0;
-       rxq = (*priv->rxqs)[index];
-       return priv->dev->rx_pkt_burst(rxq, pkts, pkts_n);
-}
-
 /**
  * Allocate a Queue Pair.
  * Optionally setup inline receive if supported.
@@ -3478,15 +3092,11 @@ rxq_setup_qp(struct priv *priv, struct ibv_cq *cq, uint16_t desc,
                .res_domain = rd,
        };
 
-#ifdef INLINE_RECV
        attr.max_inl_recv = priv->inl_recv_size;
        attr.comp_mask |= IBV_EXP_QP_INIT_ATTR_INL_RECV;
-#endif
        return ibv_exp_create_qp(priv->ctx, &attr);
 }
 
-#ifdef RSS_SUPPORT
-
 /**
  * Allocate a RSS Queue Pair.
  * Optionally setup inline receive if supported.
@@ -3497,15 +3107,18 @@ rxq_setup_qp(struct priv *priv, struct ibv_cq *cq, uint16_t desc,
  *   Completion queue to associate with QP.
  * @param desc
  *   Number of descriptors in QP (hint only).
- * @param parent
- *   If nonzero, create a parent QP, otherwise a child.
+ * @param children_n
+ *   If nonzero, a number of children for parent QP and zero for a child.
+ * @param rxq_parent
+ *   Pointer for a parent in a child case, NULL otherwise.
  *
  * @return
  *   QP pointer or NULL in case of error.
  */
 static struct ibv_qp *
 rxq_setup_qp_rss(struct priv *priv, struct ibv_cq *cq, uint16_t desc,
-                int parent, struct ibv_exp_res_domain *rd)
+                int children_n, struct ibv_exp_res_domain *rd,
+                struct rxq *rxq_parent)
 {
        struct ibv_exp_qp_init_attr attr = {
                /* CQ to be associated with the send queue. */
@@ -3531,27 +3144,23 @@ rxq_setup_qp_rss(struct priv *priv, struct ibv_cq *cq, uint16_t desc,
                .res_domain = rd,
        };
 
-#ifdef INLINE_RECV
        attr.max_inl_recv = priv->inl_recv_size,
        attr.comp_mask |= IBV_EXP_QP_INIT_ATTR_INL_RECV;
-#endif
-       if (parent) {
+       if (children_n > 0) {
                attr.qpg.qpg_type = IBV_EXP_QPG_PARENT;
                /* TSS isn't necessary. */
                attr.qpg.parent_attrib.tss_child_count = 0;
                attr.qpg.parent_attrib.rss_child_count =
-                       rte_align32pow2(priv->rxqs_n + 1) >> 1;
+                       rte_align32pow2(children_n + 1) >> 1;
                DEBUG("initializing parent RSS queue");
        } else {
                attr.qpg.qpg_type = IBV_EXP_QPG_CHILD_RX;
-               attr.qpg.qpg_parent = priv->rxq_parent.qp;
+               attr.qpg.qpg_parent = rxq_parent->qp;
                DEBUG("initializing child RSS queue");
        }
        return ibv_exp_create_qp(priv->ctx, &attr);
 }
 
-#endif /* RSS_SUPPORT */
-
 /**
  * Reconfigure a RX queue with new parameters.
  *
@@ -3580,13 +3189,7 @@ rxq_rehash(struct rte_eth_dev *dev, struct rxq *rxq)
        struct ibv_recv_wr *bad_wr;
        unsigned int mb_len;
        int err;
-       int parent = (rxq == &priv->rxq_parent);
 
-       if (parent) {
-               ERROR("%p: cannot rehash parent queue %p",
-                     (void *)dev, (void *)rxq);
-               return EINVAL;
-       }
        mb_len = rte_pktmbuf_data_room_size(rxq->mp);
        DEBUG("%p: rehashing queue %p", (void *)dev, (void *)rxq);
        /* Number of descriptors and mbufs currently allocated. */
@@ -3603,7 +3206,7 @@ rxq_rehash(struct rte_eth_dev *dev, struct rxq *rxq)
        }
        /* Enable scattered packets support for this queue if necessary. */
        assert(mb_len >= RTE_PKTMBUF_HEADROOM);
-       if ((dev->data->dev_conf.rxmode.jumbo_frame) &&
+       if (dev->data->dev_conf.rxmode.enable_scatter &&
            (dev->data->dev_conf.rxmode.max_rx_pkt_len >
             (mb_len - RTE_PKTMBUF_HEADROOM))) {
                tmpl.sp = 1;
@@ -3618,7 +3221,7 @@ rxq_rehash(struct rte_eth_dev *dev, struct rxq *rxq)
                return 0;
        }
        /* Remove attached flows if RSS is disabled (no parent queue). */
-       if (!priv->rss) {
+       if (!priv->rss && !priv->isolated) {
                rxq_allmulticast_disable(&tmpl);
                rxq_promiscuous_disable(&tmpl);
                rxq_mac_addrs_del(&tmpl);
@@ -3631,6 +3234,8 @@ rxq_rehash(struct rte_eth_dev *dev, struct rxq *rxq)
        }
        /* From now on, any failure will render the queue unusable.
         * Reinitialize QP. */
+       if (!tmpl.qp)
+               goto skip_init;
        mod = (struct ibv_exp_qp_attr){ .qp_state = IBV_QPS_RESET };
        err = ibv_exp_modify_qp(tmpl.qp, &mod, IBV_EXP_QP_STATE);
        if (err) {
@@ -3638,12 +3243,6 @@ rxq_rehash(struct rte_eth_dev *dev, struct rxq *rxq)
                assert(err > 0);
                return err;
        }
-       err = ibv_resize_cq(tmpl.cq, desc_n);
-       if (err) {
-               ERROR("%p: cannot resize CQ: %s", (void *)dev, strerror(err));
-               assert(err > 0);
-               return err;
-       }
        mod = (struct ibv_exp_qp_attr){
                /* Move the QP to this state. */
                .qp_state = IBV_QPS_INIT,
@@ -3652,9 +3251,6 @@ rxq_rehash(struct rte_eth_dev *dev, struct rxq *rxq)
        };
        err = ibv_exp_modify_qp(tmpl.qp, &mod,
                                (IBV_EXP_QP_STATE |
-#ifdef RSS_SUPPORT
-                                (parent ? IBV_EXP_QP_GROUP_RSS : 0) |
-#endif /* RSS_SUPPORT */
                                 IBV_EXP_QP_PORT));
        if (err) {
                ERROR("%p: QP state to IBV_QPS_INIT failed: %s",
@@ -3662,8 +3258,15 @@ rxq_rehash(struct rte_eth_dev *dev, struct rxq *rxq)
                assert(err > 0);
                return err;
        };
+skip_init:
+       err = ibv_resize_cq(tmpl.cq, desc_n);
+       if (err) {
+               ERROR("%p: cannot resize CQ: %s", (void *)dev, strerror(err));
+               assert(err > 0);
+               return err;
+       }
        /* Reconfigure flows. Do not care for errors. */
-       if (!priv->rss) {
+       if (!priv->rss && !priv->isolated) {
                rxq_mac_addrs_add(&tmpl);
                if (priv->promisc)
                        rxq_promiscuous_enable(&tmpl);
@@ -3729,6 +3332,8 @@ rxq_rehash(struct rte_eth_dev *dev, struct rxq *rxq)
        rxq->elts_n = 0;
        rte_free(rxq->elts.sp);
        rxq->elts.sp = NULL;
+       if (!tmpl.qp)
+               goto skip_rtr;
        /* Post WRs. */
        err = ibv_post_recv(tmpl.qp,
                            (tmpl.sp ?
@@ -3756,57 +3361,168 @@ skip_rtr:
 }
 
 /**
- * Configure a RX queue.
+ * Create verbs QP resources associated with a rxq.
  *
- * @param dev
- *   Pointer to Ethernet device structure.
  * @param rxq
  *   Pointer to RX queue structure.
  * @param desc
  *   Number of descriptors to configure in queue.
- * @param socket
- *   NUMA socket on which memory must be allocated.
  * @param inactive
  *   If true, the queue is disabled because its index is higher or
  *   equal to the real number of queues, which must be a power of 2.
- * @param[in] conf
- *   Thresholds parameters.
- * @param mp
- *   Memory pool for buffer allocations.
+ * @param children_n
+ *   The number of children in a parent case, zero for a child.
+ * @param rxq_parent
+ *   The pointer to a parent RX structure for a child in RSS case,
+ *   NULL for parent.
  *
  * @return
  *   0 on success, errno value on failure.
  */
-static int
-rxq_setup(struct rte_eth_dev *dev, struct rxq *rxq, uint16_t desc,
-         unsigned int socket, int inactive, const struct rte_eth_rxconf *conf,
-         struct rte_mempool *mp)
+int
+rxq_create_qp(struct rxq *rxq,
+             uint16_t desc,
+             int inactive,
+             int children_n,
+             struct rxq *rxq_parent)
 {
-       struct priv *priv = dev->data->dev_private;
-       struct rxq tmpl = {
-               .priv = priv,
-               .mp = mp,
-               .socket = socket
-       };
+       int ret;
        struct ibv_exp_qp_attr mod;
-       union {
-               struct ibv_exp_query_intf_params params;
-               struct ibv_exp_cq_init_attr cq;
-               struct ibv_exp_res_domain_init_attr rd;
-       } attr;
+       struct ibv_exp_query_intf_params params;
        enum ibv_exp_query_intf_status status;
        struct ibv_recv_wr *bad_wr;
-       unsigned int mb_len;
-       int ret = 0;
-       int parent = (rxq == &priv->rxq_parent);
+       int parent = (children_n > 0);
+       struct priv *priv = rxq->priv;
 
-       (void)conf; /* Thresholds configuration (ignored). */
-       /*
-        * If this is a parent queue, hardware must support RSS and
-        * RSS must be enabled.
-        */
-       assert((!parent) || ((priv->hw_rss) && (priv->rss)));
-       if (parent) {
+       if (priv->rss && !inactive && (rxq_parent || parent))
+               rxq->qp = rxq_setup_qp_rss(priv, rxq->cq, desc,
+                                          children_n, rxq->rd,
+                                          rxq_parent);
+       else
+               rxq->qp = rxq_setup_qp(priv, rxq->cq, desc, rxq->rd);
+       if (rxq->qp == NULL) {
+               ret = (errno ? errno : EINVAL);
+               ERROR("QP creation failure: %s",
+                     strerror(ret));
+               return ret;
+       }
+       mod = (struct ibv_exp_qp_attr){
+               /* Move the QP to this state. */
+               .qp_state = IBV_QPS_INIT,
+               /* Primary port number. */
+               .port_num = priv->port
+       };
+       ret = ibv_exp_modify_qp(rxq->qp, &mod,
+                               (IBV_EXP_QP_STATE |
+                                (parent ? IBV_EXP_QP_GROUP_RSS : 0) |
+                                IBV_EXP_QP_PORT));
+       if (ret) {
+               ERROR("QP state to IBV_QPS_INIT failed: %s",
+                     strerror(ret));
+               return ret;
+       }
+       if (!priv->isolated && (parent || !priv->rss)) {
+               /* Configure MAC and broadcast addresses. */
+               ret = rxq_mac_addrs_add(rxq);
+               if (ret) {
+                       ERROR("QP flow attachment failed: %s",
+                             strerror(ret));
+                       return ret;
+               }
+       }
+       if (!parent) {
+               ret = ibv_post_recv(rxq->qp,
+                                   (rxq->sp ?
+                                    &(*rxq->elts.sp)[0].wr :
+                                    &(*rxq->elts.no_sp)[0].wr),
+                                   &bad_wr);
+               if (ret) {
+                       ERROR("ibv_post_recv() failed for WR %p: %s",
+                             (void *)bad_wr,
+                             strerror(ret));
+                       return ret;
+               }
+       }
+       mod = (struct ibv_exp_qp_attr){
+               .qp_state = IBV_QPS_RTR
+       };
+       ret = ibv_exp_modify_qp(rxq->qp, &mod, IBV_EXP_QP_STATE);
+       if (ret) {
+               ERROR("QP state to IBV_QPS_RTR failed: %s",
+                     strerror(ret));
+               return ret;
+       }
+       params = (struct ibv_exp_query_intf_params){
+               .intf_scope = IBV_EXP_INTF_GLOBAL,
+               .intf = IBV_EXP_INTF_QP_BURST,
+               .obj = rxq->qp,
+       };
+       rxq->if_qp = ibv_exp_query_intf(priv->ctx, &params, &status);
+       if (rxq->if_qp == NULL) {
+               ERROR("QP interface family query failed with status %d",
+                     status);
+               return errno;
+       }
+       return 0;
+}
+
+/**
+ * Configure a RX queue.
+ *
+ * @param dev
+ *   Pointer to Ethernet device structure.
+ * @param rxq
+ *   Pointer to RX queue structure.
+ * @param desc
+ *   Number of descriptors to configure in queue.
+ * @param socket
+ *   NUMA socket on which memory must be allocated.
+ * @param inactive
+ *   If true, the queue is disabled because its index is higher or
+ *   equal to the real number of queues, which must be a power of 2.
+ * @param[in] conf
+ *   Thresholds parameters.
+ * @param mp
+ *   Memory pool for buffer allocations.
+ * @param children_n
+ *   The number of children in a parent case, zero for a child.
+ * @param rxq_parent
+ *   The pointer to a parent RX structure (or NULL) in a child case,
+ *   NULL for parent.
+ *
+ * @return
+ *   0 on success, errno value on failure.
+ */
+static int
+rxq_setup(struct rte_eth_dev *dev, struct rxq *rxq, uint16_t desc,
+         unsigned int socket, int inactive,
+         const struct rte_eth_rxconf *conf,
+         struct rte_mempool *mp, int children_n,
+         struct rxq *rxq_parent)
+{
+       struct priv *priv = dev->data->dev_private;
+       struct rxq tmpl = {
+               .priv = priv,
+               .mp = mp,
+               .socket = socket
+       };
+       union {
+               struct ibv_exp_query_intf_params params;
+               struct ibv_exp_cq_init_attr cq;
+               struct ibv_exp_res_domain_init_attr rd;
+       } attr;
+       enum ibv_exp_query_intf_status status;
+       unsigned int mb_len;
+       int ret = 0;
+       int parent = (children_n > 0);
+
+       (void)conf; /* Thresholds configuration (ignored). */
+       /*
+        * If this is a parent queue, hardware must support RSS and
+        * RSS must be enabled.
+        */
+       assert((!parent) || ((priv->hw_rss) && (priv->rss)));
+       if (parent) {
                /* Even if unused, ibv_create_cq() requires at least one
                 * descriptor. */
                desc = 1;
@@ -3825,11 +3541,19 @@ rxq_setup(struct rte_eth_dev *dev, struct rxq *rxq, uint16_t desc,
                tmpl.csum_l2tun = !!dev->data->dev_conf.rxmode.hw_ip_checksum;
        /* Enable scattered packets support for this queue if necessary. */
        assert(mb_len >= RTE_PKTMBUF_HEADROOM);
-       if ((dev->data->dev_conf.rxmode.jumbo_frame) &&
-           (dev->data->dev_conf.rxmode.max_rx_pkt_len >
-            (mb_len - RTE_PKTMBUF_HEADROOM))) {
+       if (dev->data->dev_conf.rxmode.max_rx_pkt_len <=
+           (mb_len - RTE_PKTMBUF_HEADROOM)) {
+               tmpl.sp = 0;
+       } else if (dev->data->dev_conf.rxmode.enable_scatter) {
                tmpl.sp = 1;
                desc /= MLX4_PMD_SGE_WR_N;
+       } else {
+               WARN("%p: the requested maximum Rx packet size (%u) is"
+                    " larger than a single mbuf (%u) and scattered"
+                    " mode has not been requested",
+                    (void *)dev,
+                    dev->data->dev_conf.rxmode.max_rx_pkt_len,
+                    mb_len - RTE_PKTMBUF_HEADROOM);
        }
        DEBUG("%p: %s scattered packets support (%u WRs)",
              (void *)dev, (tmpl.sp ? "enabling" : "disabling"), desc);
@@ -3855,11 +3579,22 @@ skip_mr:
                      (void *)dev, strerror(ret));
                goto error;
        }
+       if (dev->data->dev_conf.intr_conf.rxq) {
+               tmpl.channel = ibv_create_comp_channel(priv->ctx);
+               if (tmpl.channel == NULL) {
+                       ret = ENOMEM;
+                       ERROR("%p: Rx interrupt completion channel creation"
+                             " failure: %s",
+                             (void *)dev, strerror(ret));
+                       goto error;
+               }
+       }
        attr.cq = (struct ibv_exp_cq_init_attr){
                .comp_mask = IBV_EXP_CQ_INIT_ATTR_RES_DOMAIN,
                .res_domain = tmpl.rd,
        };
-       tmpl.cq = ibv_exp_create_cq(priv->ctx, desc, NULL, NULL, 0, &attr.cq);
+       tmpl.cq = ibv_exp_create_cq(priv->ctx, desc, NULL, tmpl.channel, 0,
+                                   &attr.cq);
        if (tmpl.cq == NULL) {
                ret = ENOMEM;
                ERROR("%p: CQ creation failure: %s",
@@ -3870,45 +3605,6 @@ skip_mr:
              priv->device_attr.max_qp_wr);
        DEBUG("priv->device_attr.max_sge is %d",
              priv->device_attr.max_sge);
-#ifdef RSS_SUPPORT
-       if (priv->rss && !inactive)
-               tmpl.qp = rxq_setup_qp_rss(priv, tmpl.cq, desc, parent,
-                                          tmpl.rd);
-       else
-#endif /* RSS_SUPPORT */
-               tmpl.qp = rxq_setup_qp(priv, tmpl.cq, desc, tmpl.rd);
-       if (tmpl.qp == NULL) {
-               ret = (errno ? errno : EINVAL);
-               ERROR("%p: QP creation failure: %s",
-                     (void *)dev, strerror(ret));
-               goto error;
-       }
-       mod = (struct ibv_exp_qp_attr){
-               /* Move the QP to this state. */
-               .qp_state = IBV_QPS_INIT,
-               /* Primary port number. */
-               .port_num = priv->port
-       };
-       ret = ibv_exp_modify_qp(tmpl.qp, &mod,
-                               (IBV_EXP_QP_STATE |
-#ifdef RSS_SUPPORT
-                                (parent ? IBV_EXP_QP_GROUP_RSS : 0) |
-#endif /* RSS_SUPPORT */
-                                IBV_EXP_QP_PORT));
-       if (ret) {
-               ERROR("%p: QP state to IBV_QPS_INIT failed: %s",
-                     (void *)dev, strerror(ret));
-               goto error;
-       }
-       if ((parent) || (!priv->rss))  {
-               /* Configure MAC and broadcast addresses. */
-               ret = rxq_mac_addrs_add(&tmpl);
-               if (ret) {
-                       ERROR("%p: QP flow attachment failed: %s",
-                             (void *)dev, strerror(ret));
-                       goto error;
-               }
-       }
        /* Allocate descriptors for RX queues, except for the RSS parent. */
        if (parent)
                goto skip_alloc;
@@ -3919,29 +3615,14 @@ skip_mr:
        if (ret) {
                ERROR("%p: RXQ allocation failed: %s",
                      (void *)dev, strerror(ret));
-               goto error;
-       }
-       ret = ibv_post_recv(tmpl.qp,
-                           (tmpl.sp ?
-                            &(*tmpl.elts.sp)[0].wr :
-                            &(*tmpl.elts.no_sp)[0].wr),
-                           &bad_wr);
-       if (ret) {
-               ERROR("%p: ibv_post_recv() failed for WR %p: %s",
-                     (void *)dev,
-                     (void *)bad_wr,
-                     strerror(ret));
-               goto error;
+               return ret;
        }
 skip_alloc:
-       mod = (struct ibv_exp_qp_attr){
-               .qp_state = IBV_QPS_RTR
-       };
-       ret = ibv_exp_modify_qp(tmpl.qp, &mod, IBV_EXP_QP_STATE);
-       if (ret) {
-               ERROR("%p: QP state to IBV_QPS_RTR failed: %s",
-                     (void *)dev, strerror(ret));
-               goto error;
+       if (parent || rxq_parent || !priv->rss) {
+               ret = rxq_create_qp(&tmpl, desc, inactive,
+                                   children_n, rxq_parent);
+               if (ret)
+                       goto error;
        }
        /* Save port ID. */
        tmpl.port_id = dev->data->port_id;
@@ -3953,21 +3634,11 @@ skip_alloc:
        };
        tmpl.if_cq = ibv_exp_query_intf(priv->ctx, &attr.params, &status);
        if (tmpl.if_cq == NULL) {
+               ret = EINVAL;
                ERROR("%p: CQ interface family query failed with status %d",
                      (void *)dev, status);
                goto error;
        }
-       attr.params = (struct ibv_exp_query_intf_params){
-               .intf_scope = IBV_EXP_INTF_GLOBAL,
-               .intf = IBV_EXP_INTF_QP_BURST,
-               .obj = tmpl.qp,
-       };
-       tmpl.if_qp = ibv_exp_query_intf(priv->ctx, &attr.params, &status);
-       if (tmpl.if_qp == NULL) {
-               ERROR("%p: QP interface family query failed with status %d",
-                     (void *)dev, status);
-               goto error;
-       }
        /* Clean up rxq in case we're reinitializing it. */
        DEBUG("%p: cleaning-up old rxq just in case", (void *)rxq);
        rxq_cleanup(rxq);
@@ -4005,13 +3676,12 @@ mlx4_rx_queue_setup(struct rte_eth_dev *dev, uint16_t idx, uint16_t desc,
                    unsigned int socket, const struct rte_eth_rxconf *conf,
                    struct rte_mempool *mp)
 {
+       struct rxq *parent;
        struct priv *priv = dev->data->dev_private;
        struct rxq *rxq = (*priv->rxqs)[idx];
        int inactive = 0;
        int ret;
 
-       if (mlx4_is_secondary())
-               return -E_RTE_SECONDARY;
        priv_lock(priv);
        DEBUG("%p: configuring queue %u for %u descriptors",
              (void *)dev, idx, desc);
@@ -4039,9 +3709,16 @@ mlx4_rx_queue_setup(struct rte_eth_dev *dev, uint16_t idx, uint16_t desc,
                        return -ENOMEM;
                }
        }
-       if (idx >= rte_align32pow2(priv->rxqs_n + 1) >> 1)
-               inactive = 1;
-       ret = rxq_setup(dev, rxq, desc, socket, inactive, conf, mp);
+       if (priv->rss && !priv->isolated) {
+               /* The list consists of the single default one. */
+               parent = LIST_FIRST(&priv->parents);
+               if (idx >= rte_align32pow2(priv->rxqs_n + 1) >> 1)
+                       inactive = 1;
+       } else {
+               parent = NULL;
+       }
+       ret = rxq_setup(dev, rxq, desc, socket,
+                       inactive, conf, mp, 0, parent);
        if (ret)
                rte_free(rxq);
        else {
@@ -4072,13 +3749,10 @@ mlx4_rx_queue_release(void *dpdk_rxq)
        struct priv *priv;
        unsigned int i;
 
-       if (mlx4_is_secondary())
-               return;
        if (rxq == NULL)
                return;
        priv = rxq->priv;
        priv_lock(priv);
-       assert(rxq != &priv->rxq_parent);
        for (i = 0; (i != priv->rxqs_n); ++i)
                if ((*priv->rxqs)[i] == rxq) {
                        DEBUG("%p: removing RX queue %p from list",
@@ -4091,9 +3765,15 @@ mlx4_rx_queue_release(void *dpdk_rxq)
        priv_unlock(priv);
 }
 
-static void
+static int
 priv_dev_interrupt_handler_install(struct priv *, struct rte_eth_dev *);
 
+static int
+priv_dev_removal_interrupt_handler_install(struct priv *, struct rte_eth_dev *);
+
+static int
+priv_dev_link_interrupt_handler_install(struct priv *, struct rte_eth_dev *);
+
 /**
  * DPDK callback to start the device.
  *
@@ -4112,9 +3792,8 @@ mlx4_dev_start(struct rte_eth_dev *dev)
        unsigned int i = 0;
        unsigned int r;
        struct rxq *rxq;
+       int ret;
 
-       if (mlx4_is_secondary())
-               return -E_RTE_SECONDARY;
        priv_lock(priv);
        if (priv->started) {
                priv_unlock(priv);
@@ -4122,8 +3801,11 @@ mlx4_dev_start(struct rte_eth_dev *dev)
        }
        DEBUG("%p: attaching configured flows to all RX queues", (void *)dev);
        priv->started = 1;
-       if (priv->rss) {
-               rxq = &priv->rxq_parent;
+       if (priv->isolated) {
+               rxq = NULL;
+               r = 1;
+       } else if (priv->rss) {
+               rxq = LIST_FIRST(&priv->parents);
                r = 1;
        } else {
                rxq = (*priv->rxqs)[0];
@@ -4131,8 +3813,6 @@ mlx4_dev_start(struct rte_eth_dev *dev)
        }
        /* Iterate only once when RSS is enabled. */
        do {
-               int ret;
-
                /* Ignore nonexistent RX queues. */
                if (rxq == NULL)
                        continue;
@@ -4145,22 +3825,47 @@ mlx4_dev_start(struct rte_eth_dev *dev)
                        continue;
                WARN("%p: QP flow attachment failed: %s",
                     (void *)dev, strerror(ret));
-               /* Rollback. */
-               while (i != 0) {
-                       rxq = (*priv->rxqs)[--i];
-                       if (rxq != NULL) {
-                               rxq_allmulticast_disable(rxq);
-                               rxq_promiscuous_disable(rxq);
-                               rxq_mac_addrs_del(rxq);
-                       }
-               }
-               priv->started = 0;
-               priv_unlock(priv);
-               return -ret;
+               goto err;
        } while ((--r) && ((rxq = (*priv->rxqs)[++i]), i));
-       priv_dev_interrupt_handler_install(priv, dev);
+       ret = priv_dev_link_interrupt_handler_install(priv, dev);
+       if (ret) {
+               ERROR("%p: LSC handler install failed",
+                    (void *)dev);
+               goto err;
+       }
+       ret = priv_dev_removal_interrupt_handler_install(priv, dev);
+       if (ret) {
+               ERROR("%p: RMV handler install failed",
+                    (void *)dev);
+               goto err;
+       }
+       ret = priv_rx_intr_vec_enable(priv);
+       if (ret) {
+               ERROR("%p: Rx interrupt vector creation failed",
+                     (void *)dev);
+               goto err;
+       }
+       ret = mlx4_priv_flow_start(priv);
+       if (ret) {
+               ERROR("%p: flow start failed: %s",
+                     (void *)dev, strerror(ret));
+               goto err;
+       }
        priv_unlock(priv);
        return 0;
+err:
+       /* Rollback. */
+       while (i != 0) {
+               rxq = (*priv->rxqs)[i--];
+               if (rxq != NULL) {
+                       rxq_allmulticast_disable(rxq);
+                       rxq_promiscuous_disable(rxq);
+                       rxq_mac_addrs_del(rxq);
+               }
+       }
+       priv->started = 0;
+       priv_unlock(priv);
+       return -ret;
 }
 
 /**
@@ -4179,8 +3884,6 @@ mlx4_dev_stop(struct rte_eth_dev *dev)
        unsigned int r;
        struct rxq *rxq;
 
-       if (mlx4_is_secondary())
-               return;
        priv_lock(priv);
        if (!priv->started) {
                priv_unlock(priv);
@@ -4188,13 +3891,17 @@ mlx4_dev_stop(struct rte_eth_dev *dev)
        }
        DEBUG("%p: detaching flows from all RX queues", (void *)dev);
        priv->started = 0;
-       if (priv->rss) {
-               rxq = &priv->rxq_parent;
+       if (priv->isolated) {
+               rxq = NULL;
+               r = 1;
+       } else if (priv->rss) {
+               rxq = LIST_FIRST(&priv->parents);
                r = 1;
        } else {
                rxq = (*priv->rxqs)[0];
                r = priv->rxqs_n;
        }
+       mlx4_priv_flow_stop(priv);
        /* Iterate only once when RSS is enabled. */
        do {
                /* Ignore nonexistent RX queues. */
@@ -4257,9 +3964,16 @@ removed_rx_burst(void *dpdk_rxq, struct rte_mbuf **pkts, uint16_t pkts_n)
        return 0;
 }
 
-static void
+static int
 priv_dev_interrupt_handler_uninstall(struct priv *, struct rte_eth_dev *);
 
+static int
+priv_dev_removal_interrupt_handler_uninstall(struct priv *,
+                                            struct rte_eth_dev *);
+
+static int
+priv_dev_link_interrupt_handler_uninstall(struct priv *, struct rte_eth_dev *);
+
 /**
  * DPDK callback to close the device.
  *
@@ -4271,7 +3985,7 @@ priv_dev_interrupt_handler_uninstall(struct priv *, struct rte_eth_dev *);
 static void
 mlx4_dev_close(struct rte_eth_dev *dev)
 {
-       struct priv *priv = mlx4_get_priv(dev);
+       struct priv *priv = dev->data->dev_private;
        void *tmp;
        unsigned int i;
 
@@ -4315,51 +4029,139 @@ mlx4_dev_close(struct rte_eth_dev *dev)
                priv->txqs = NULL;
        }
        if (priv->rss)
-               rxq_cleanup(&priv->rxq_parent);
+               priv_parent_list_cleanup(priv);
        if (priv->pd != NULL) {
                assert(priv->ctx != NULL);
                claim_zero(ibv_dealloc_pd(priv->pd));
                claim_zero(ibv_close_device(priv->ctx));
        } else
                assert(priv->ctx == NULL);
-       priv_dev_interrupt_handler_uninstall(priv, dev);
+       priv_dev_removal_interrupt_handler_uninstall(priv, dev);
+       priv_dev_link_interrupt_handler_uninstall(priv, dev);
+       priv_rx_intr_vec_disable(priv);
        priv_unlock(priv);
        memset(priv, 0, sizeof(*priv));
 }
 
 /**
- * DPDK callback to get information about the device.
+ * Change the link state (UP / DOWN).
+ *
+ * @param priv
+ *   Pointer to Ethernet device private data.
+ * @param up
+ *   Nonzero for link up, otherwise link down.
+ *
+ * @return
+ *   0 on success, errno value on failure.
+ */
+static int
+priv_set_link(struct priv *priv, int up)
+{
+       struct rte_eth_dev *dev = priv->dev;
+       int err;
+       unsigned int i;
+
+       if (up) {
+               err = priv_set_flags(priv, ~IFF_UP, IFF_UP);
+               if (err)
+                       return err;
+               for (i = 0; i < priv->rxqs_n; i++)
+                       if ((*priv->rxqs)[i]->sp)
+                               break;
+               /* Check if an sp queue exists.
+                * Note: Some old frames might be received.
+                */
+               if (i == priv->rxqs_n)
+                       dev->rx_pkt_burst = mlx4_rx_burst;
+               else
+                       dev->rx_pkt_burst = mlx4_rx_burst_sp;
+               dev->tx_pkt_burst = mlx4_tx_burst;
+       } else {
+               err = priv_set_flags(priv, ~IFF_UP, ~IFF_UP);
+               if (err)
+                       return err;
+               dev->rx_pkt_burst = removed_rx_burst;
+               dev->tx_pkt_burst = removed_tx_burst;
+       }
+       return 0;
+}
+
+/**
+ * DPDK callback to bring the link DOWN.
  *
  * @param dev
  *   Pointer to Ethernet device structure.
- * @param[out] info
- *   Info structure output buffer.
+ *
+ * @return
+ *   0 on success, errno value on failure.
  */
-static void
-mlx4_dev_infos_get(struct rte_eth_dev *dev, struct rte_eth_dev_info *info)
+static int
+mlx4_set_link_down(struct rte_eth_dev *dev)
 {
-       struct priv *priv = mlx4_get_priv(dev);
-       unsigned int max;
-       char ifname[IF_NAMESIZE];
+       struct priv *priv = dev->data->dev_private;
+       int err;
 
-       if (priv == NULL)
-               return;
        priv_lock(priv);
-       /* FIXME: we should ask the device for these values. */
-       info->min_rx_bufsize = 32;
-       info->max_rx_pktlen = 65536;
-       /*
-        * Since we need one CQ per QP, the limit is the minimum number
-        * between the two values.
-        */
-       max = ((priv->device_attr.max_cq > priv->device_attr.max_qp) ?
-              priv->device_attr.max_qp : priv->device_attr.max_cq);
-       /* If max >= 65535 then max = 0, max_rx_queues is uint16_t. */
-       if (max >= 65535)
-               max = 65535;
-       info->max_rx_queues = max;
-       info->max_tx_queues = max;
-       /* Last array entry is reserved for broadcast. */
+       err = priv_set_link(priv, 0);
+       priv_unlock(priv);
+       return err;
+}
+
+/**
+ * DPDK callback to bring the link UP.
+ *
+ * @param dev
+ *   Pointer to Ethernet device structure.
+ *
+ * @return
+ *   0 on success, errno value on failure.
+ */
+static int
+mlx4_set_link_up(struct rte_eth_dev *dev)
+{
+       struct priv *priv = dev->data->dev_private;
+       int err;
+
+       priv_lock(priv);
+       err = priv_set_link(priv, 1);
+       priv_unlock(priv);
+       return err;
+}
+/**
+ * DPDK callback to get information about the device.
+ *
+ * @param dev
+ *   Pointer to Ethernet device structure.
+ * @param[out] info
+ *   Info structure output buffer.
+ */
+static void
+mlx4_dev_infos_get(struct rte_eth_dev *dev, struct rte_eth_dev_info *info)
+{
+       struct priv *priv = dev->data->dev_private;
+       unsigned int max;
+       char ifname[IF_NAMESIZE];
+
+       info->pci_dev = RTE_ETH_DEV_TO_PCI(dev);
+
+       if (priv == NULL)
+               return;
+       priv_lock(priv);
+       /* FIXME: we should ask the device for these values. */
+       info->min_rx_bufsize = 32;
+       info->max_rx_pktlen = 65536;
+       /*
+        * Since we need one CQ per QP, the limit is the minimum number
+        * between the two values.
+        */
+       max = ((priv->device_attr.max_cq > priv->device_attr.max_qp) ?
+              priv->device_attr.max_qp : priv->device_attr.max_cq);
+       /* If max >= 65535 then max = 0, max_rx_queues is uint16_t. */
+       if (max >= 65535)
+               max = 65535;
+       info->max_rx_queues = max;
+       info->max_tx_queues = max;
+       /* Last array entry is reserved for broadcast. */
        info->max_mac_addrs = (elemof(priv->mac) - 1);
        info->rx_offload_capa =
                (priv->hw_csum ?
@@ -4413,7 +4215,7 @@ mlx4_dev_supported_ptypes_get(struct rte_eth_dev *dev)
 static void
 mlx4_stats_get(struct rte_eth_dev *dev, struct rte_eth_stats *stats)
 {
-       struct priv *priv = mlx4_get_priv(dev);
+       struct priv *priv = dev->data->dev_private;
        struct rte_eth_stats tmp = {0};
        unsigned int i;
        unsigned int idx;
@@ -4429,17 +4231,13 @@ mlx4_stats_get(struct rte_eth_dev *dev, struct rte_eth_stats *stats)
                        continue;
                idx = rxq->stats.idx;
                if (idx < RTE_ETHDEV_QUEUE_STAT_CNTRS) {
-#ifdef MLX4_PMD_SOFT_COUNTERS
                        tmp.q_ipackets[idx] += rxq->stats.ipackets;
                        tmp.q_ibytes[idx] += rxq->stats.ibytes;
-#endif
                        tmp.q_errors[idx] += (rxq->stats.idropped +
                                              rxq->stats.rx_nombuf);
                }
-#ifdef MLX4_PMD_SOFT_COUNTERS
                tmp.ipackets += rxq->stats.ipackets;
                tmp.ibytes += rxq->stats.ibytes;
-#endif
                tmp.ierrors += rxq->stats.idropped;
                tmp.rx_nombuf += rxq->stats.rx_nombuf;
        }
@@ -4450,21 +4248,14 @@ mlx4_stats_get(struct rte_eth_dev *dev, struct rte_eth_stats *stats)
                        continue;
                idx = txq->stats.idx;
                if (idx < RTE_ETHDEV_QUEUE_STAT_CNTRS) {
-#ifdef MLX4_PMD_SOFT_COUNTERS
                        tmp.q_opackets[idx] += txq->stats.opackets;
                        tmp.q_obytes[idx] += txq->stats.obytes;
-#endif
                        tmp.q_errors[idx] += txq->stats.odropped;
                }
-#ifdef MLX4_PMD_SOFT_COUNTERS
                tmp.opackets += txq->stats.opackets;
                tmp.obytes += txq->stats.obytes;
-#endif
                tmp.oerrors += txq->stats.odropped;
        }
-#ifndef MLX4_PMD_SOFT_COUNTERS
-       /* FIXME: retrieve and add hardware counters. */
-#endif
        *stats = tmp;
        priv_unlock(priv);
 }
@@ -4478,7 +4269,7 @@ mlx4_stats_get(struct rte_eth_dev *dev, struct rte_eth_stats *stats)
 static void
 mlx4_stats_reset(struct rte_eth_dev *dev)
 {
-       struct priv *priv = mlx4_get_priv(dev);
+       struct priv *priv = dev->data->dev_private;
        unsigned int i;
        unsigned int idx;
 
@@ -4499,9 +4290,6 @@ mlx4_stats_reset(struct rte_eth_dev *dev)
                (*priv->txqs)[i]->stats =
                        (struct mlx4_txq_stats){ .idx = idx };
        }
-#ifndef MLX4_PMD_SOFT_COUNTERS
-       /* FIXME: reset hardware counters. */
-#endif
        priv_unlock(priv);
 }
 
@@ -4518,9 +4306,9 @@ mlx4_mac_addr_remove(struct rte_eth_dev *dev, uint32_t index)
 {
        struct priv *priv = dev->data->dev_private;
 
-       if (mlx4_is_secondary())
-               return;
        priv_lock(priv);
+       if (priv->isolated)
+               goto end;
        DEBUG("%p: removing MAC address from index %" PRIu32,
              (void *)dev, index);
        /* Last array entry is reserved for broadcast. */
@@ -4543,26 +4331,34 @@ end:
  * @param vmdq
  *   VMDq pool index to associate address with (ignored).
  */
-static void
+static int
 mlx4_mac_addr_add(struct rte_eth_dev *dev, struct ether_addr *mac_addr,
                  uint32_t index, uint32_t vmdq)
 {
        struct priv *priv = dev->data->dev_private;
+       int re;
 
-       if (mlx4_is_secondary())
-               return;
        (void)vmdq;
        priv_lock(priv);
+       if (priv->isolated) {
+               DEBUG("%p: cannot add MAC address, "
+                     "device is in isolated mode", (void *)dev);
+               re = EPERM;
+               goto end;
+       }
        DEBUG("%p: adding MAC address at index %" PRIu32,
              (void *)dev, index);
        /* Last array entry is reserved for broadcast. */
-       if (index >= (elemof(priv->mac) - 1))
+       if (index >= (elemof(priv->mac) - 1)) {
+               re = EINVAL;
                goto end;
-       priv_mac_addr_add(priv, index,
-                         (const uint8_t (*)[ETHER_ADDR_LEN])
-                         mac_addr->addr_bytes);
+       }
+       re = priv_mac_addr_add(priv, index,
+                              (const uint8_t (*)[ETHER_ADDR_LEN])
+                              mac_addr->addr_bytes);
 end:
        priv_unlock(priv);
+       return -re;
 }
 
 /**
@@ -4594,9 +4390,13 @@ mlx4_promiscuous_enable(struct rte_eth_dev *dev)
        unsigned int i;
        int ret;
 
-       if (mlx4_is_secondary())
-               return;
        priv_lock(priv);
+       if (priv->isolated) {
+               DEBUG("%p: cannot enable promiscuous, "
+                     "device is in isolated mode", (void *)dev);
+               priv_unlock(priv);
+               return;
+       }
        if (priv->promisc) {
                priv_unlock(priv);
                return;
@@ -4605,7 +4405,7 @@ mlx4_promiscuous_enable(struct rte_eth_dev *dev)
        if (!priv->started)
                goto end;
        if (priv->rss) {
-               ret = rxq_promiscuous_enable(&priv->rxq_parent);
+               ret = rxq_promiscuous_enable(LIST_FIRST(&priv->parents));
                if (ret) {
                        priv_unlock(priv);
                        return;
@@ -4642,15 +4442,13 @@ mlx4_promiscuous_disable(struct rte_eth_dev *dev)
        struct priv *priv = dev->data->dev_private;
        unsigned int i;
 
-       if (mlx4_is_secondary())
-               return;
        priv_lock(priv);
-       if (!priv->promisc) {
+       if (!priv->promisc || priv->isolated) {
                priv_unlock(priv);
                return;
        }
        if (priv->rss) {
-               rxq_promiscuous_disable(&priv->rxq_parent);
+               rxq_promiscuous_disable(LIST_FIRST(&priv->parents));
                goto end;
        }
        for (i = 0; (i != priv->rxqs_n); ++i)
@@ -4674,9 +4472,13 @@ mlx4_allmulticast_enable(struct rte_eth_dev *dev)
        unsigned int i;
        int ret;
 
-       if (mlx4_is_secondary())
-               return;
        priv_lock(priv);
+       if (priv->isolated) {
+               DEBUG("%p: cannot enable allmulticast, "
+                     "device is in isolated mode", (void *)dev);
+               priv_unlock(priv);
+               return;
+       }
        if (priv->allmulti) {
                priv_unlock(priv);
                return;
@@ -4685,7 +4487,7 @@ mlx4_allmulticast_enable(struct rte_eth_dev *dev)
        if (!priv->started)
                goto end;
        if (priv->rss) {
-               ret = rxq_allmulticast_enable(&priv->rxq_parent);
+               ret = rxq_allmulticast_enable(LIST_FIRST(&priv->parents));
                if (ret) {
                        priv_unlock(priv);
                        return;
@@ -4722,15 +4524,13 @@ mlx4_allmulticast_disable(struct rte_eth_dev *dev)
        struct priv *priv = dev->data->dev_private;
        unsigned int i;
 
-       if (mlx4_is_secondary())
-               return;
        priv_lock(priv);
-       if (!priv->allmulti) {
+       if (!priv->allmulti || priv->isolated) {
                priv_unlock(priv);
                return;
        }
        if (priv->rss) {
-               rxq_allmulticast_disable(&priv->rxq_parent);
+               rxq_allmulticast_disable(LIST_FIRST(&priv->parents));
                goto end;
        }
        for (i = 0; (i != priv->rxqs_n); ++i)
@@ -4742,7 +4542,7 @@ end:
 }
 
 /**
- * DPDK callback to retrieve physical link information (unlocked version).
+ * DPDK callback to retrieve physical link information.
  *
  * @param dev
  *   Pointer to Ethernet device structure.
@@ -4750,9 +4550,9 @@ end:
  *   Wait for request completion (ignored).
  */
 static int
-mlx4_link_update_unlocked(struct rte_eth_dev *dev, int wait_to_complete)
+mlx4_link_update(struct rte_eth_dev *dev, int wait_to_complete)
 {
-       struct priv *priv = mlx4_get_priv(dev);
+       const struct priv *priv = dev->data->dev_private;
        struct ethtool_cmd edata = {
                .cmd = ETHTOOL_GSET
        };
@@ -4760,6 +4560,8 @@ mlx4_link_update_unlocked(struct rte_eth_dev *dev, int wait_to_complete)
        struct rte_eth_link dev_link;
        int link_speed = 0;
 
+       /* priv_lock() is not taken to allow concurrent calls. */
+
        if (priv == NULL)
                return -EINVAL;
        (void)wait_to_complete;
@@ -4794,28 +4596,6 @@ mlx4_link_update_unlocked(struct rte_eth_dev *dev, int wait_to_complete)
        return -1;
 }
 
-/**
- * DPDK callback to retrieve physical link information.
- *
- * @param dev
- *   Pointer to Ethernet device structure.
- * @param wait_to_complete
- *   Wait for request completion (ignored).
- */
-static int
-mlx4_link_update(struct rte_eth_dev *dev, int wait_to_complete)
-{
-       struct priv *priv = mlx4_get_priv(dev);
-       int ret;
-
-       if (priv == NULL)
-               return -EINVAL;
-       priv_lock(priv);
-       ret = mlx4_link_update_unlocked(dev, wait_to_complete);
-       priv_unlock(priv);
-       return ret;
-}
-
 /**
  * DPDK callback to change the MTU.
  *
@@ -4842,8 +4622,6 @@ mlx4_dev_set_mtu(struct rte_eth_dev *dev, uint16_t mtu)
        uint16_t (*rx_func)(void *, struct rte_mbuf **, uint16_t) =
                mlx4_rx_burst;
 
-       if (mlx4_is_secondary())
-               return -E_RTE_SECONDARY;
        priv_lock(priv);
        /* Set kernel interface MTU first. */
        if (priv_set_mtu(priv, mtu)) {
@@ -4864,21 +4642,16 @@ mlx4_dev_set_mtu(struct rte_eth_dev *dev, uint16_t mtu)
        /* Reconfigure each RX queue. */
        for (i = 0; (i != priv->rxqs_n); ++i) {
                struct rxq *rxq = (*priv->rxqs)[i];
-               unsigned int mb_len;
                unsigned int max_frame_len;
-               int sp;
 
                if (rxq == NULL)
                        continue;
-               /* Calculate new maximum frame length according to MTU and
-                * toggle scattered support (sp) if necessary. */
+               /* Calculate new maximum frame length according to MTU. */
                max_frame_len = (priv->mtu + ETHER_HDR_LEN +
                                 (ETHER_MAX_VLAN_FRAME_LEN - ETHER_MAX_LEN));
-               mb_len = rte_pktmbuf_data_room_size(rxq->mp);
-               assert(mb_len >= RTE_PKTMBUF_HEADROOM);
-               sp = (max_frame_len > (mb_len - RTE_PKTMBUF_HEADROOM));
                /* Provide new values to rxq_setup(). */
-               dev->data->dev_conf.rxmode.jumbo_frame = sp;
+               dev->data->dev_conf.rxmode.jumbo_frame =
+                       (max_frame_len > ETHER_MAX_LEN);
                dev->data->dev_conf.rxmode.max_rx_pkt_len = max_frame_len;
                ret = rxq_rehash(dev, rxq);
                if (ret) {
@@ -4889,7 +4662,7 @@ mlx4_dev_set_mtu(struct rte_eth_dev *dev, uint16_t mtu)
                }
                /* Reenable non-RSS queue attributes. No need to check
                 * for errors at this stage. */
-               if (!priv->rss) {
+               if (!priv->rss && !priv->isolated) {
                        rxq_mac_addrs_add(rxq);
                        if (priv->promisc)
                                rxq_promiscuous_enable(rxq);
@@ -4930,8 +4703,6 @@ mlx4_dev_get_flow_ctrl(struct rte_eth_dev *dev, struct rte_eth_fc_conf *fc_conf)
        };
        int ret;
 
-       if (mlx4_is_secondary())
-               return -E_RTE_SECONDARY;
        ifr.ifr_data = (void *)&ethpause;
        priv_lock(priv);
        if (priv_ifreq(priv, SIOCETHTOOL, &ifr)) {
@@ -4980,8 +4751,6 @@ mlx4_dev_set_flow_ctrl(struct rte_eth_dev *dev, struct rte_eth_fc_conf *fc_conf)
        };
        int ret;
 
-       if (mlx4_is_secondary())
-               return -E_RTE_SECONDARY;
        ifr.ifr_data = (void *)&ethpause;
        ethpause.autoneg = fc_conf->autoneg;
        if (((fc_conf->mode & RTE_FC_FULL) == RTE_FC_FULL) ||
@@ -5060,7 +4829,7 @@ vlan_filter_set(struct rte_eth_dev *dev, uint16_t vlan_id, int on)
                 * Rehashing flows in all RX queues is necessary.
                 */
                if (priv->rss)
-                       rxq_mac_addrs_del(&priv->rxq_parent);
+                       rxq_mac_addrs_del(LIST_FIRST(&priv->parents));
                else
                        for (i = 0; (i != priv->rxqs_n); ++i)
                                if ((*priv->rxqs)[i] != NULL)
@@ -5068,7 +4837,7 @@ vlan_filter_set(struct rte_eth_dev *dev, uint16_t vlan_id, int on)
                priv->vlan_filter[j].enabled = 1;
                if (priv->started) {
                        if (priv->rss)
-                               rxq_mac_addrs_add(&priv->rxq_parent);
+                               rxq_mac_addrs_add(LIST_FIRST(&priv->parents));
                        else
                                for (i = 0; (i != priv->rxqs_n); ++i) {
                                        if ((*priv->rxqs)[i] == NULL)
@@ -5082,7 +4851,7 @@ vlan_filter_set(struct rte_eth_dev *dev, uint16_t vlan_id, int on)
                 * Rehashing flows in all RX queues is necessary.
                 */
                if (priv->rss)
-                       rxq_mac_addrs_del(&priv->rxq_parent);
+                       rxq_mac_addrs_del(LIST_FIRST(&priv->parents));
                else
                        for (i = 0; (i != priv->rxqs_n); ++i)
                                if ((*priv->rxqs)[i] != NULL)
@@ -5090,7 +4859,7 @@ vlan_filter_set(struct rte_eth_dev *dev, uint16_t vlan_id, int on)
                priv->vlan_filter[j].enabled = 0;
                if (priv->started) {
                        if (priv->rss)
-                               rxq_mac_addrs_add(&priv->rxq_parent);
+                               rxq_mac_addrs_add(LIST_FIRST(&priv->parents));
                        else
                                for (i = 0; (i != priv->rxqs_n); ++i) {
                                        if ((*priv->rxqs)[i] == NULL)
@@ -5121,19 +4890,71 @@ mlx4_vlan_filter_set(struct rte_eth_dev *dev, uint16_t vlan_id, int on)
        struct priv *priv = dev->data->dev_private;
        int ret;
 
-       if (mlx4_is_secondary())
-               return -E_RTE_SECONDARY;
        priv_lock(priv);
+       if (priv->isolated) {
+               DEBUG("%p: cannot set vlan filter, "
+                     "device is in isolated mode", (void *)dev);
+               priv_unlock(priv);
+               return -EINVAL;
+       }
        ret = vlan_filter_set(dev, vlan_id, on);
        priv_unlock(priv);
        assert(ret >= 0);
        return -ret;
 }
 
+const struct rte_flow_ops mlx4_flow_ops = {
+       .validate = mlx4_flow_validate,
+       .create = mlx4_flow_create,
+       .destroy = mlx4_flow_destroy,
+       .flush = mlx4_flow_flush,
+       .query = NULL,
+       .isolate = mlx4_flow_isolate,
+};
+
+/**
+ * Manage filter operations.
+ *
+ * @param dev
+ *   Pointer to Ethernet device structure.
+ * @param filter_type
+ *   Filter type.
+ * @param filter_op
+ *   Operation to perform.
+ * @param arg
+ *   Pointer to operation-specific structure.
+ *
+ * @return
+ *   0 on success, negative errno value on failure.
+ */
+static int
+mlx4_dev_filter_ctrl(struct rte_eth_dev *dev,
+                    enum rte_filter_type filter_type,
+                    enum rte_filter_op filter_op,
+                    void *arg)
+{
+       int ret = EINVAL;
+
+       switch (filter_type) {
+       case RTE_ETH_FILTER_GENERIC:
+               if (filter_op != RTE_ETH_FILTER_GET)
+                       return -EINVAL;
+               *(const void **)arg = &mlx4_flow_ops;
+               return 0;
+       default:
+               ERROR("%p: filter type (%d) not supported",
+                     (void *)dev, filter_type);
+               break;
+       }
+       return -ret;
+}
+
 static const struct eth_dev_ops mlx4_dev_ops = {
        .dev_configure = mlx4_dev_configure,
        .dev_start = mlx4_dev_start,
        .dev_stop = mlx4_dev_stop,
+       .dev_set_link_down = mlx4_set_link_down,
+       .dev_set_link_up = mlx4_set_link_up,
        .dev_close = mlx4_dev_close,
        .promiscuous_enable = mlx4_promiscuous_enable,
        .promiscuous_disable = mlx4_promiscuous_disable,
@@ -5142,26 +4963,22 @@ static const struct eth_dev_ops mlx4_dev_ops = {
        .link_update = mlx4_link_update,
        .stats_get = mlx4_stats_get,
        .stats_reset = mlx4_stats_reset,
-       .queue_stats_mapping_set = NULL,
        .dev_infos_get = mlx4_dev_infos_get,
        .dev_supported_ptypes_get = mlx4_dev_supported_ptypes_get,
        .vlan_filter_set = mlx4_vlan_filter_set,
-       .vlan_tpid_set = NULL,
-       .vlan_strip_queue_set = NULL,
-       .vlan_offload_set = NULL,
        .rx_queue_setup = mlx4_rx_queue_setup,
        .tx_queue_setup = mlx4_tx_queue_setup,
        .rx_queue_release = mlx4_rx_queue_release,
        .tx_queue_release = mlx4_tx_queue_release,
-       .dev_led_on = NULL,
-       .dev_led_off = NULL,
        .flow_ctrl_get = mlx4_dev_get_flow_ctrl,
        .flow_ctrl_set = mlx4_dev_set_flow_ctrl,
-       .priority_flow_ctrl_set = NULL,
        .mac_addr_remove = mlx4_mac_addr_remove,
        .mac_addr_add = mlx4_mac_addr_add,
        .mac_addr_set = mlx4_mac_addr_set,
        .mtu_set = mlx4_dev_set_mtu,
+       .filter_ctrl = mlx4_dev_filter_ctrl,
+       .rx_queue_intr_enable = mlx4_rx_intr_enable,
+       .rx_queue_intr_disable = mlx4_rx_intr_disable,
 };
 
 /**
@@ -5201,7 +5018,7 @@ mlx4_ibv_device_to_pci_addr(const struct ibv_device *device,
                /* Extract information. */
                if (sscanf(line,
                           "PCI_SLOT_NAME="
-                          "%" SCNx16 ":%" SCNx8 ":%" SCNx8 ".%" SCNx8 "\n",
+                          "%" SCNx32 ":%" SCNx8 ":%" SCNx8 ".%" SCNx8 "\n",
                           &pci_addr->domain,
                           &pci_addr->bus,
                           &pci_addr->devid,
@@ -5236,40 +5053,6 @@ priv_get_mac(struct priv *priv, uint8_t (*mac)[ETHER_ADDR_LEN])
        return 0;
 }
 
-/* Support up to 32 adapters. */
-static struct {
-       struct rte_pci_addr pci_addr; /* associated PCI address */
-       uint32_t ports; /* physical ports bitfield. */
-} mlx4_dev[32];
-
-/**
- * Get device index in mlx4_dev[] from PCI bus address.
- *
- * @param[in] pci_addr
- *   PCI bus address to look for.
- *
- * @return
- *   mlx4_dev[] index on success, -1 on failure.
- */
-static int
-mlx4_dev_idx(struct rte_pci_addr *pci_addr)
-{
-       unsigned int i;
-       int ret = -1;
-
-       assert(pci_addr != NULL);
-       for (i = 0; (i != elemof(mlx4_dev)); ++i) {
-               if ((mlx4_dev[i].pci_addr.domain == pci_addr->domain) &&
-                   (mlx4_dev[i].pci_addr.bus == pci_addr->bus) &&
-                   (mlx4_dev[i].pci_addr.devid == pci_addr->devid) &&
-                   (mlx4_dev[i].pci_addr.function == pci_addr->function))
-                       return i;
-               if ((mlx4_dev[i].ports == 0) && (ret == -1))
-                       ret = i;
-       }
-       return ret;
-}
-
 /**
  * Retrieve integer value from environment variable.
  *
@@ -5292,54 +5075,63 @@ mlx4_getenv_int(const char *name)
 static void
 mlx4_dev_link_status_handler(void *);
 static void
-mlx4_dev_interrupt_handler(struct rte_intr_handle *, void *);
+mlx4_dev_interrupt_handler(void *);
 
 /**
- * Link status handler.
+ * Link/device status handler.
  *
  * @param priv
  *   Pointer to private structure.
  * @param dev
  *   Pointer to the rte_eth_dev structure.
+ * @param events
+ *   Pointer to event flags holder.
  *
  * @return
- *   Nonzero if the callback process can be called immediately.
+ *   Number of events
  */
 static int
-priv_dev_link_status_handler(struct priv *priv, struct rte_eth_dev *dev)
+priv_dev_status_handler(struct priv *priv, struct rte_eth_dev *dev,
+                       uint32_t *events)
 {
        struct ibv_async_event event;
        int port_change = 0;
+       struct rte_eth_link *link = &dev->data->dev_link;
        int ret = 0;
 
+       *events = 0;
        /* Read all message and acknowledge them. */
        for (;;) {
                if (ibv_get_async_event(priv->ctx, &event))
                        break;
-
-               if (event.event_type == IBV_EVENT_PORT_ACTIVE ||
-                   event.event_type == IBV_EVENT_PORT_ERR)
+               if ((event.event_type == IBV_EVENT_PORT_ACTIVE ||
+                    event.event_type == IBV_EVENT_PORT_ERR) &&
+                   (priv->intr_conf.lsc == 1)) {
                        port_change = 1;
-               else
+                       ret++;
+               } else if (event.event_type == IBV_EVENT_DEVICE_FATAL &&
+                          priv->intr_conf.rmv == 1) {
+                       *events |= (1 << RTE_ETH_EVENT_INTR_RMV);
+                       ret++;
+               } else
                        DEBUG("event type %d on port %d not handled",
                              event.event_type, event.element.port_num);
                ibv_ack_async_event(&event);
        }
-
-       if (port_change ^ priv->pending_alarm) {
-               struct rte_eth_link *link = &dev->data->dev_link;
-
-               priv->pending_alarm = 0;
-               mlx4_link_update_unlocked(dev, 0);
-               if (((link->link_speed == 0) && link->link_status) ||
-                   ((link->link_speed != 0) && !link->link_status)) {
+       if (!port_change)
+               return ret;
+       mlx4_link_update(dev, 0);
+       if (((link->link_speed == 0) && link->link_status) ||
+           ((link->link_speed != 0) && !link->link_status)) {
+               if (!priv->pending_alarm) {
                        /* Inconsistent status, check again later. */
                        priv->pending_alarm = 1;
                        rte_eal_alarm_set(MLX4_ALARM_TIMEOUT_US,
                                          mlx4_dev_link_status_handler,
                                          dev);
-               } else
-                       ret = 1;
+               }
+       } else {
+               *events |= (1 << RTE_ETH_EVENT_INTR_LSC);
        }
        return ret;
 }
@@ -5355,14 +5147,17 @@ mlx4_dev_link_status_handler(void *arg)
 {
        struct rte_eth_dev *dev = arg;
        struct priv *priv = dev->data->dev_private;
+       uint32_t events;
        int ret;
 
        priv_lock(priv);
        assert(priv->pending_alarm == 1);
-       ret = priv_dev_link_status_handler(priv, dev);
+       priv->pending_alarm = 0;
+       ret = priv_dev_status_handler(priv, dev, &events);
        priv_unlock(priv);
-       if (ret)
-               _rte_eth_dev_callback_process(dev, RTE_ETH_EVENT_INTR_LSC);
+       if (ret > 0 && events & (1 << RTE_ETH_EVENT_INTR_LSC))
+               _rte_eth_dev_callback_process(dev, RTE_ETH_EVENT_INTR_LSC, NULL,
+                                             NULL);
 }
 
 /**
@@ -5374,18 +5169,32 @@ mlx4_dev_link_status_handler(void *arg)
  *   Callback argument.
  */
 static void
-mlx4_dev_interrupt_handler(struct rte_intr_handle *intr_handle, void *cb_arg)
+mlx4_dev_interrupt_handler(void *cb_arg)
 {
        struct rte_eth_dev *dev = cb_arg;
        struct priv *priv = dev->data->dev_private;
        int ret;
+       uint32_t ev;
+       int i;
 
-       (void)intr_handle;
        priv_lock(priv);
-       ret = priv_dev_link_status_handler(priv, dev);
+       ret = priv_dev_status_handler(priv, dev, &ev);
        priv_unlock(priv);
-       if (ret)
-               _rte_eth_dev_callback_process(dev, RTE_ETH_EVENT_INTR_LSC);
+       if (ret > 0) {
+               for (i = RTE_ETH_EVENT_UNKNOWN;
+                    i < RTE_ETH_EVENT_MAX;
+                    i++) {
+                       if (ev & (1 << i)) {
+                               ev &= ~(1 << i);
+                               _rte_eth_dev_callback_process(dev, i, NULL,
+                                                             NULL);
+                               ret--;
+                       }
+               }
+               if (ret)
+                       WARN("%d event%s not processed", ret,
+                            (ret > 1 ? "s were" : " was"));
+       }
 }
 
 /**
@@ -5395,20 +5204,30 @@ mlx4_dev_interrupt_handler(struct rte_intr_handle *intr_handle, void *cb_arg)
  *   Pointer to private structure.
  * @param dev
  *   Pointer to the rte_eth_dev structure.
+ * @return
+ *   0 on success, negative errno value on failure.
  */
-static void
+static int
 priv_dev_interrupt_handler_uninstall(struct priv *priv, struct rte_eth_dev *dev)
 {
-       if (!dev->data->dev_conf.intr_conf.lsc)
-               return;
-       rte_intr_callback_unregister(&priv->intr_handle,
-                                    mlx4_dev_interrupt_handler,
-                                    dev);
-       if (priv->pending_alarm)
-               rte_eal_alarm_cancel(mlx4_dev_link_status_handler, dev);
-       priv->pending_alarm = 0;
+       int ret;
+
+       if (priv->intr_conf.lsc ||
+           priv->intr_conf.rmv)
+               return 0;
+       ret = rte_intr_callback_unregister(&priv->intr_handle,
+                                          mlx4_dev_interrupt_handler,
+                                          dev);
+       if (ret < 0) {
+               ERROR("rte_intr_callback_unregister failed with %d"
+                     "%s%s%s", ret,
+                     (errno ? " (errno: " : ""),
+                     (errno ? strerror(errno) : ""),
+                     (errno ? ")" : ""));
+       }
        priv->intr_handle.fd = 0;
        priv->intr_handle.type = RTE_INTR_HANDLE_UNKNOWN;
+       return ret;
 }
 
 /**
@@ -5418,30 +5237,387 @@ priv_dev_interrupt_handler_uninstall(struct priv *priv, struct rte_eth_dev *dev)
  *   Pointer to private structure.
  * @param dev
  *   Pointer to the rte_eth_dev structure.
+ * @return
+ *   0 on success, negative errno value on failure.
  */
-static void
-priv_dev_interrupt_handler_install(struct priv *priv, struct rte_eth_dev *dev)
+static int
+priv_dev_interrupt_handler_install(struct priv *priv,
+                                  struct rte_eth_dev *dev)
 {
-       int rc, flags;
+       int flags;
+       int rc;
 
-       if (!dev->data->dev_conf.intr_conf.lsc)
-               return;
+       /* Check whether the interrupt handler has already been installed
+        * for either type of interrupt
+        */
+       if (priv->intr_conf.lsc &&
+           priv->intr_conf.rmv &&
+           priv->intr_handle.fd)
+               return 0;
        assert(priv->ctx->async_fd > 0);
        flags = fcntl(priv->ctx->async_fd, F_GETFL);
        rc = fcntl(priv->ctx->async_fd, F_SETFL, flags | O_NONBLOCK);
        if (rc < 0) {
                INFO("failed to change file descriptor async event queue");
                dev->data->dev_conf.intr_conf.lsc = 0;
+               dev->data->dev_conf.intr_conf.rmv = 0;
+               return -errno;
        } else {
                priv->intr_handle.fd = priv->ctx->async_fd;
                priv->intr_handle.type = RTE_INTR_HANDLE_EXT;
-               rte_intr_callback_register(&priv->intr_handle,
-                                          mlx4_dev_interrupt_handler,
-                                          dev);
+               rc = rte_intr_callback_register(&priv->intr_handle,
+                                                mlx4_dev_interrupt_handler,
+                                                dev);
+               if (rc) {
+                       ERROR("rte_intr_callback_register failed "
+                             " (errno: %s)", strerror(errno));
+                       return rc;
+               }
+       }
+       return 0;
+}
+
+/**
+ * Uninstall interrupt handler.
+ *
+ * @param priv
+ *   Pointer to private structure.
+ * @param dev
+ *   Pointer to the rte_eth_dev structure.
+ * @return
+ *   0 on success, negative value on error.
+ */
+static int
+priv_dev_removal_interrupt_handler_uninstall(struct priv *priv,
+                                           struct rte_eth_dev *dev)
+{
+       if (dev->data->dev_conf.intr_conf.rmv) {
+               priv->intr_conf.rmv = 0;
+               return priv_dev_interrupt_handler_uninstall(priv, dev);
+       }
+       return 0;
+}
+
+/**
+ * Uninstall interrupt handler.
+ *
+ * @param priv
+ *   Pointer to private structure.
+ * @param dev
+ *   Pointer to the rte_eth_dev structure.
+ * @return
+ *   0 on success, negative value on error,
+ */
+static int
+priv_dev_link_interrupt_handler_uninstall(struct priv *priv,
+                                         struct rte_eth_dev *dev)
+{
+       int ret = 0;
+
+       if (dev->data->dev_conf.intr_conf.lsc) {
+               priv->intr_conf.lsc = 0;
+               ret = priv_dev_interrupt_handler_uninstall(priv, dev);
+               if (ret)
+                       return ret;
        }
+       if (priv->pending_alarm)
+               if (rte_eal_alarm_cancel(mlx4_dev_link_status_handler,
+                                        dev)) {
+                       ERROR("rte_eal_alarm_cancel failed "
+                             " (errno: %s)", strerror(rte_errno));
+                       return -rte_errno;
+               }
+       priv->pending_alarm = 0;
+       return 0;
 }
 
-static struct eth_driver mlx4_driver;
+/**
+ * Install link interrupt handler.
+ *
+ * @param priv
+ *   Pointer to private structure.
+ * @param dev
+ *   Pointer to the rte_eth_dev structure.
+ * @return
+ *   0 on success, negative value on error.
+ */
+static int
+priv_dev_link_interrupt_handler_install(struct priv *priv,
+                                       struct rte_eth_dev *dev)
+{
+       int ret;
+
+       if (dev->data->dev_conf.intr_conf.lsc) {
+               ret = priv_dev_interrupt_handler_install(priv, dev);
+               if (ret)
+                       return ret;
+               priv->intr_conf.lsc = 1;
+       }
+       return 0;
+}
+
+/**
+ * Install removal interrupt handler.
+ *
+ * @param priv
+ *   Pointer to private structure.
+ * @param dev
+ *   Pointer to the rte_eth_dev structure.
+ * @return
+ *   0 on success, negative value on error.
+ */
+static int
+priv_dev_removal_interrupt_handler_install(struct priv *priv,
+                                          struct rte_eth_dev *dev)
+{
+       int ret;
+
+       if (dev->data->dev_conf.intr_conf.rmv) {
+               ret = priv_dev_interrupt_handler_install(priv, dev);
+               if (ret)
+                       return ret;
+               priv->intr_conf.rmv = 1;
+       }
+       return 0;
+}
+
+/**
+ * Allocate queue vector and fill epoll fd list for Rx interrupts.
+ *
+ * @param priv
+ *   Pointer to private structure.
+ *
+ * @return
+ *   0 on success, negative on failure.
+ */
+static int
+priv_rx_intr_vec_enable(struct priv *priv)
+{
+       unsigned int i;
+       unsigned int rxqs_n = priv->rxqs_n;
+       unsigned int n = RTE_MIN(rxqs_n, (uint32_t)RTE_MAX_RXTX_INTR_VEC_ID);
+       unsigned int count = 0;
+       struct rte_intr_handle *intr_handle = priv->dev->intr_handle;
+
+       if (!priv->dev->data->dev_conf.intr_conf.rxq)
+               return 0;
+       priv_rx_intr_vec_disable(priv);
+       intr_handle->intr_vec = malloc(sizeof(intr_handle->intr_vec[rxqs_n]));
+       if (intr_handle->intr_vec == NULL) {
+               ERROR("failed to allocate memory for interrupt vector,"
+                     " Rx interrupts will not be supported");
+               return -ENOMEM;
+       }
+       intr_handle->type = RTE_INTR_HANDLE_EXT;
+       for (i = 0; i != n; ++i) {
+               struct rxq *rxq = (*priv->rxqs)[i];
+               int fd;
+               int flags;
+               int rc;
+
+               /* Skip queues that cannot request interrupts. */
+               if (!rxq || !rxq->channel) {
+                       /* Use invalid intr_vec[] index to disable entry. */
+                       intr_handle->intr_vec[i] =
+                               RTE_INTR_VEC_RXTX_OFFSET +
+                               RTE_MAX_RXTX_INTR_VEC_ID;
+                       continue;
+               }
+               if (count >= RTE_MAX_RXTX_INTR_VEC_ID) {
+                       ERROR("too many Rx queues for interrupt vector size"
+                             " (%d), Rx interrupts cannot be enabled",
+                             RTE_MAX_RXTX_INTR_VEC_ID);
+                       priv_rx_intr_vec_disable(priv);
+                       return -1;
+               }
+               fd = rxq->channel->fd;
+               flags = fcntl(fd, F_GETFL);
+               rc = fcntl(fd, F_SETFL, flags | O_NONBLOCK);
+               if (rc < 0) {
+                       ERROR("failed to make Rx interrupt file descriptor"
+                             " %d non-blocking for queue index %d", fd, i);
+                       priv_rx_intr_vec_disable(priv);
+                       return rc;
+               }
+               intr_handle->intr_vec[i] = RTE_INTR_VEC_RXTX_OFFSET + count;
+               intr_handle->efds[count] = fd;
+               count++;
+       }
+       if (!count)
+               priv_rx_intr_vec_disable(priv);
+       else
+               intr_handle->nb_efd = count;
+       return 0;
+}
+
+/**
+ * Clean up Rx interrupts handler.
+ *
+ * @param priv
+ *   Pointer to private structure.
+ */
+static void
+priv_rx_intr_vec_disable(struct priv *priv)
+{
+       struct rte_intr_handle *intr_handle = priv->dev->intr_handle;
+
+       rte_intr_free_epoll_fd(intr_handle);
+       free(intr_handle->intr_vec);
+       intr_handle->nb_efd = 0;
+       intr_handle->intr_vec = NULL;
+}
+
+/**
+ * DPDK callback for Rx queue interrupt enable.
+ *
+ * @param dev
+ *   Pointer to Ethernet device structure.
+ * @param idx
+ *   Rx queue index.
+ *
+ * @return
+ *   0 on success, negative on failure.
+ */
+static int
+mlx4_rx_intr_enable(struct rte_eth_dev *dev, uint16_t idx)
+{
+       struct priv *priv = dev->data->dev_private;
+       struct rxq *rxq = (*priv->rxqs)[idx];
+       int ret;
+
+       if (!rxq || !rxq->channel)
+               ret = EINVAL;
+       else
+               ret = ibv_req_notify_cq(rxq->cq, 0);
+       if (ret)
+               WARN("unable to arm interrupt on rx queue %d", idx);
+       return -ret;
+}
+
+/**
+ * DPDK callback for Rx queue interrupt disable.
+ *
+ * @param dev
+ *   Pointer to Ethernet device structure.
+ * @param idx
+ *   Rx queue index.
+ *
+ * @return
+ *   0 on success, negative on failure.
+ */
+static int
+mlx4_rx_intr_disable(struct rte_eth_dev *dev, uint16_t idx)
+{
+       struct priv *priv = dev->data->dev_private;
+       struct rxq *rxq = (*priv->rxqs)[idx];
+       struct ibv_cq *ev_cq;
+       void *ev_ctx;
+       int ret;
+
+       if (!rxq || !rxq->channel) {
+               ret = EINVAL;
+       } else {
+               ret = ibv_get_cq_event(rxq->cq->channel, &ev_cq, &ev_ctx);
+               if (ret || ev_cq != rxq->cq)
+                       ret = EINVAL;
+       }
+       if (ret)
+               WARN("unable to disable interrupt on rx queue %d",
+                    idx);
+       else
+               ibv_ack_cq_events(rxq->cq, 1);
+       return -ret;
+}
+
+/**
+ * Verify and store value for device argument.
+ *
+ * @param[in] key
+ *   Key argument to verify.
+ * @param[in] val
+ *   Value associated with key.
+ * @param[in, out] conf
+ *   Shared configuration data.
+ *
+ * @return
+ *   0 on success, negative errno value on failure.
+ */
+static int
+mlx4_arg_parse(const char *key, const char *val, struct mlx4_conf *conf)
+{
+       unsigned long tmp;
+
+       errno = 0;
+       tmp = strtoul(val, NULL, 0);
+       if (errno) {
+               WARN("%s: \"%s\" is not a valid integer", key, val);
+               return -errno;
+       }
+       if (strcmp(MLX4_PMD_PORT_KVARG, key) == 0) {
+               uint32_t ports = rte_log2_u32(conf->ports.present);
+
+               if (tmp >= ports) {
+                       ERROR("port index %lu outside range [0,%" PRIu32 ")",
+                             tmp, ports);
+                       return -EINVAL;
+               }
+               if (!(conf->ports.present & (1 << tmp))) {
+                       ERROR("invalid port index %lu", tmp);
+                       return -EINVAL;
+               }
+               conf->ports.enabled |= 1 << tmp;
+       } else {
+               WARN("%s: unknown parameter", key);
+               return -EINVAL;
+       }
+       return 0;
+}
+
+/**
+ * Parse device parameters.
+ *
+ * @param devargs
+ *   Device arguments structure.
+ *
+ * @return
+ *   0 on success, negative errno value on failure.
+ */
+static int
+mlx4_args(struct rte_devargs *devargs, struct mlx4_conf *conf)
+{
+       struct rte_kvargs *kvlist;
+       unsigned int arg_count;
+       int ret = 0;
+       int i;
+
+       if (devargs == NULL)
+               return 0;
+       kvlist = rte_kvargs_parse(devargs->args, pmd_mlx4_init_params);
+       if (kvlist == NULL) {
+               ERROR("failed to parse kvargs");
+               return -EINVAL;
+       }
+       /* Process parameters. */
+       for (i = 0; pmd_mlx4_init_params[i]; ++i) {
+               arg_count = rte_kvargs_count(kvlist, MLX4_PMD_PORT_KVARG);
+               while (arg_count-- > 0) {
+                       ret = rte_kvargs_process(kvlist,
+                                                MLX4_PMD_PORT_KVARG,
+                                                (int (*)(const char *,
+                                                         const char *,
+                                                         void *))
+                                                mlx4_arg_parse,
+                                                conf);
+                       if (ret != 0)
+                               goto free_kvlist;
+               }
+       }
+free_kvlist:
+       rte_kvargs_free(kvlist);
+       return ret;
+}
+
+static struct rte_pci_driver mlx4_driver;
 
 /**
  * DPDK callback to register a PCI device.
@@ -5458,36 +5634,27 @@ static struct eth_driver mlx4_driver;
  *   0 on success, negative errno value on failure.
  */
 static int
-mlx4_pci_devinit(struct rte_pci_driver *pci_drv, struct rte_pci_device *pci_dev)
+mlx4_pci_probe(struct rte_pci_driver *pci_drv, struct rte_pci_device *pci_dev)
 {
        struct ibv_device **list;
        struct ibv_device *ibv_dev;
        int err = 0;
        struct ibv_context *attr_ctx = NULL;
        struct ibv_device_attr device_attr;
+       struct mlx4_conf conf = {
+               .ports.present = 0,
+       };
        unsigned int vf;
-       int idx;
        int i;
 
        (void)pci_drv;
-       assert(pci_drv == &mlx4_driver.pci_drv);
-       /* Get mlx4_dev[] index. */
-       idx = mlx4_dev_idx(&pci_dev->addr);
-       if (idx == -1) {
-               ERROR("this driver cannot support any more adapters");
-               return -ENOMEM;
-       }
-       DEBUG("using driver device index %d", idx);
+       assert(pci_drv == &mlx4_driver);
 
-       /* Save PCI address. */
-       mlx4_dev[idx].pci_addr = pci_dev->addr;
        list = ibv_get_device_list(&i);
        if (list == NULL) {
                assert(errno);
-               if (errno == ENOSYS) {
-                       WARN("cannot list devices, is ib_uverbs loaded?");
-                       return 0;
-               }
+               if (errno == ENOSYS)
+                       ERROR("cannot list devices, is ib_uverbs loaded?");
                return -errno;
        }
        assert(i >= 0);
@@ -5519,11 +5686,11 @@ mlx4_pci_devinit(struct rte_pci_driver *pci_drv, struct rte_pci_device *pci_dev)
                ibv_free_device_list(list);
                switch (err) {
                case 0:
-                       WARN("cannot access device, is mlx4_ib loaded?");
-                       return 0;
+                       ERROR("cannot access device, is mlx4_ib loaded?");
+                       return -ENODEV;
                case EINVAL:
-                       WARN("cannot use device, are drivers up to date?");
-                       return 0;
+                       ERROR("cannot use device, are drivers up to date?");
+                       return -EINVAL;
                }
                assert(err > 0);
                return -err;
@@ -5531,46 +5698,57 @@ mlx4_pci_devinit(struct rte_pci_driver *pci_drv, struct rte_pci_device *pci_dev)
        ibv_dev = list[i];
 
        DEBUG("device opened");
-       if (ibv_query_device(attr_ctx, &device_attr))
+       if (ibv_query_device(attr_ctx, &device_attr)) {
+               err = ENODEV;
                goto error;
+       }
        INFO("%u port(s) detected", device_attr.phys_port_cnt);
 
+       conf.ports.present |= (UINT64_C(1) << device_attr.phys_port_cnt) - 1;
+       if (mlx4_args(pci_dev->device.devargs, &conf)) {
+               ERROR("failed to process device arguments");
+               err = EINVAL;
+               goto error;
+       }
+       /* Use all ports when none are defined */
+       if (!conf.ports.enabled)
+               conf.ports.enabled = conf.ports.present;
        for (i = 0; i < device_attr.phys_port_cnt; i++) {
                uint32_t port = i + 1; /* ports are indexed from one */
-               uint32_t test = (1 << i);
                struct ibv_context *ctx = NULL;
                struct ibv_port_attr port_attr;
                struct ibv_pd *pd = NULL;
                struct priv *priv = NULL;
                struct rte_eth_dev *eth_dev = NULL;
-#ifdef HAVE_EXP_QUERY_DEVICE
                struct ibv_exp_device_attr exp_device_attr;
-#endif /* HAVE_EXP_QUERY_DEVICE */
                struct ether_addr mac;
 
-#ifdef HAVE_EXP_QUERY_DEVICE
+               /* If port is not enabled, skip. */
+               if (!(conf.ports.enabled & (1 << i)))
+                       continue;
                exp_device_attr.comp_mask = IBV_EXP_DEVICE_ATTR_EXP_CAP_FLAGS;
-#ifdef RSS_SUPPORT
                exp_device_attr.comp_mask |= IBV_EXP_DEVICE_ATTR_RSS_TBL_SZ;
-#endif /* RSS_SUPPORT */
-#endif /* HAVE_EXP_QUERY_DEVICE */
 
-               DEBUG("using port %u (%08" PRIx32 ")", port, test);
+               DEBUG("using port %u", port);
 
                ctx = ibv_open_device(ibv_dev);
-               if (ctx == NULL)
+               if (ctx == NULL) {
+                       err = ENODEV;
                        goto port_error;
+               }
 
                /* Check port status. */
                err = ibv_query_port(ctx, port, &port_attr);
                if (err) {
                        ERROR("port query failed: %s", strerror(err));
+                       err = ENODEV;
                        goto port_error;
                }
 
                if (port_attr.link_layer != IBV_LINK_LAYER_ETHERNET) {
                        ERROR("port %d is not configured in Ethernet mode",
                              port);
+                       err = EINVAL;
                        goto port_error;
                }
 
@@ -5587,8 +5765,6 @@ mlx4_pci_devinit(struct rte_pci_driver *pci_drv, struct rte_pci_device *pci_dev)
                        goto port_error;
                }
 
-               mlx4_dev[idx].ports |= test;
-
                /* from rte_ethdev.c */
                priv = rte_zmalloc("ethdev private structure",
                                   sizeof(*priv),
@@ -5604,12 +5780,11 @@ mlx4_pci_devinit(struct rte_pci_driver *pci_drv, struct rte_pci_device *pci_dev)
                priv->port = port;
                priv->pd = pd;
                priv->mtu = ETHER_MTU;
-#ifdef HAVE_EXP_QUERY_DEVICE
                if (ibv_exp_query_device(ctx, &exp_device_attr)) {
                        ERROR("ibv_exp_query_device() failed");
+                       err = ENODEV;
                        goto port_error;
                }
-#ifdef RSS_SUPPORT
                if ((exp_device_attr.exp_device_cap_flags &
                     IBV_EXP_DEVICE_QPG) &&
                    (exp_device_attr.exp_device_cap_flags &
@@ -5634,7 +5809,6 @@ mlx4_pci_devinit(struct rte_pci_driver *pci_drv, struct rte_pci_device *pci_dev)
                if (priv->hw_rss)
                        DEBUG("maximum RSS indirection table size: %u",
                              exp_device_attr.max_rss_tbl_sz);
-#endif /* RSS_SUPPORT */
 
                priv->hw_csum =
                        ((exp_device_attr.exp_device_cap_flags &
@@ -5649,7 +5823,6 @@ mlx4_pci_devinit(struct rte_pci_driver *pci_drv, struct rte_pci_device *pci_dev)
                DEBUG("L2 tunnel checksum offloads are %ssupported",
                      (priv->hw_csum_l2tun ? "" : "not "));
 
-#ifdef INLINE_RECV
                priv->inl_recv_size = mlx4_getenv_int("MLX4_INLINE_RECV_SIZE");
 
                if (priv->inl_recv_size) {
@@ -5673,15 +5846,13 @@ mlx4_pci_devinit(struct rte_pci_driver *pci_drv, struct rte_pci_device *pci_dev)
                        INFO("Set inline receive size to %u",
                             priv->inl_recv_size);
                }
-#endif /* INLINE_RECV */
-#endif /* HAVE_EXP_QUERY_DEVICE */
 
-               (void)mlx4_getenv_int;
                priv->vf = vf;
                /* Configure the first MAC address by default. */
                if (priv_get_mac(priv, &mac.addr_bytes)) {
                        ERROR("cannot get MAC address, is mlx4_en loaded?"
                              " (errno: %s)", strerror(errno));
+                       err = ENODEV;
                        goto port_error;
                }
                INFO("port %u MAC address is %02x:%02x:%02x:%02x:%02x:%02x",
@@ -5717,7 +5888,7 @@ mlx4_pci_devinit(struct rte_pci_driver *pci_drv, struct rte_pci_device *pci_dev)
 
                        snprintf(name, sizeof(name), "%s port %u",
                                 ibv_get_device_name(ibv_dev), port);
-                       eth_dev = rte_eth_dev_allocate(name, RTE_ETH_DEV_PCI);
+                       eth_dev = rte_eth_dev_allocate(name);
                }
                if (eth_dev == NULL) {
                        ERROR("can not allocate rte ethdev");
@@ -5725,51 +5896,33 @@ mlx4_pci_devinit(struct rte_pci_driver *pci_drv, struct rte_pci_device *pci_dev)
                        goto port_error;
                }
 
-               /* Secondary processes have to use local storage for their
-                * private data as well as a copy of eth_dev->data, but this
-                * pointer must not be modified before burst functions are
-                * actually called. */
-               if (mlx4_is_secondary()) {
-                       struct mlx4_secondary_data *sd =
-                               &mlx4_secondary_data[eth_dev->data->port_id];
-
-                       sd->primary_priv = eth_dev->data->dev_private;
-                       if (sd->primary_priv == NULL) {
-                               ERROR("no private data for port %u",
-                                     eth_dev->data->port_id);
-                               err = EINVAL;
-                               goto port_error;
-                       }
-                       sd->shared_dev_data = eth_dev->data;
-                       rte_spinlock_init(&sd->lock);
-                       memcpy(sd->data.name, sd->shared_dev_data->name,
-                              sizeof(sd->data.name));
-                       sd->data.dev_private = priv;
-                       sd->data.rx_mbuf_alloc_failed = 0;
-                       sd->data.mtu = ETHER_MTU;
-                       sd->data.port_id = sd->shared_dev_data->port_id;
-                       sd->data.mac_addrs = priv->mac;
-                       eth_dev->tx_pkt_burst = mlx4_tx_burst_secondary_setup;
-                       eth_dev->rx_pkt_burst = mlx4_rx_burst_secondary_setup;
-               } else {
-                       eth_dev->data->dev_private = priv;
-                       eth_dev->data->rx_mbuf_alloc_failed = 0;
-                       eth_dev->data->mtu = ETHER_MTU;
-                       eth_dev->data->mac_addrs = priv->mac;
-               }
-               eth_dev->pci_dev = pci_dev;
+               eth_dev->data->dev_private = priv;
+               eth_dev->data->mac_addrs = priv->mac;
+               eth_dev->device = &pci_dev->device;
 
                rte_eth_copy_pci_info(eth_dev, pci_dev);
 
-               eth_dev->driver = &mlx4_driver;
+               eth_dev->device->driver = &mlx4_driver.driver;
+
+               /*
+                * Copy and override interrupt handle to prevent it from
+                * being shared between all ethdev instances of a given PCI
+                * device. This is required to properly handle Rx interrupts
+                * on all ports.
+                */
+               priv->intr_handle_dev = *eth_dev->intr_handle;
+               eth_dev->intr_handle = &priv->intr_handle_dev;
 
                priv->dev = eth_dev;
                eth_dev->dev_ops = &mlx4_dev_ops;
-               TAILQ_INIT(&eth_dev->link_intr_cbs);
+               eth_dev->data->dev_flags |= RTE_ETH_DEV_DETACHABLE;
 
                /* Bring Ethernet device up. */
                DEBUG("forcing Ethernet interface up");
                priv_set_flags(priv, ~IFF_UP, IFF_UP);
+               /* Update link status once if waiting for LSC. */
+               if (eth_dev->data->dev_flags & RTE_ETH_DEV_INTR_LSC)
+                       mlx4_link_update(eth_dev, 0);
                continue;
 
 port_error:
@@ -5782,6 +5935,8 @@ port_error:
                        rte_eth_dev_release_port(eth_dev);
                break;
        }
+       if (i == device_attr.phys_port_cnt)
+               return 0;
 
        /*
         * XXX if something went wrong in the loop above, there is a resource
@@ -5790,12 +5945,6 @@ port_error:
         * way to enumerate the registered ethdevs to free the previous ones.
         */
 
-       /* no port found, complain */
-       if (!mlx4_dev[idx].ports) {
-               err = ENODEV;
-               goto error;
-       }
-
 error:
        if (attr_ctx)
                claim_zero(ibv_close_device(attr_ctx));
@@ -5823,25 +5972,23 @@ static const struct rte_pci_id mlx4_pci_id_map[] = {
        }
 };
 
-static struct eth_driver mlx4_driver = {
-       .pci_drv = {
-               .name = MLX4_DRIVER_NAME,
-               .id_table = mlx4_pci_id_map,
-               .devinit = mlx4_pci_devinit,
-               .drv_flags = RTE_PCI_DRV_INTR_LSC,
+static struct rte_pci_driver mlx4_driver = {
+       .driver = {
+               .name = MLX4_DRIVER_NAME
        },
-       .dev_private_size = sizeof(struct priv)
+       .id_table = mlx4_pci_id_map,
+       .probe = mlx4_pci_probe,
+       .drv_flags = RTE_PCI_DRV_INTR_LSC |
+                    RTE_PCI_DRV_INTR_RMV,
 };
 
 /**
  * Driver initialization routine.
  */
-static int
-rte_mlx4_pmd_init(const char *name, const char *args)
+RTE_INIT(rte_mlx4_pmd_init);
+static void
+rte_mlx4_pmd_init(void)
 {
-       (void)name;
-       (void)args;
-
        RTE_BUILD_BUG_ON(sizeof(wr_id_t) != sizeof(uint64_t));
        /*
         * RDMAV_HUGEPAGES_SAFE tells ibv_fork_init() we intend to use
@@ -5851,14 +5998,10 @@ rte_mlx4_pmd_init(const char *name, const char *args)
         */
        setenv("RDMAV_HUGEPAGES_SAFE", "1", 1);
        ibv_fork_init();
-       rte_eal_pci_register(&mlx4_driver.pci_drv);
-       return 0;
+       rte_pci_register(&mlx4_driver);
 }
 
-static struct rte_driver rte_mlx4_driver = {
-       .type = PMD_PDEV,
-       .name = MLX4_DRIVER_NAME,
-       .init = rte_mlx4_pmd_init,
-};
-
-PMD_REGISTER_DRIVER(rte_mlx4_driver)
+RTE_PMD_EXPORT_NAME(net_mlx4, __COUNTER__);
+RTE_PMD_REGISTER_PCI_TABLE(net_mlx4, mlx4_pci_id_map);
+RTE_PMD_REGISTER_KMOD_DEP(net_mlx4,
+       "* ib_uverbs & mlx4_en & mlx4_core & mlx4_ib");