examples/ip_frag: fix use of ethdev internal device array
[dpdk.git] / drivers / net / mlx5 / mlx5_ethdev.c
index 7785341..eeefe4d 100644 (file)
@@ -7,6 +7,7 @@
 #include <assert.h>
 #include <inttypes.h>
 #include <unistd.h>
+#include <stdbool.h>
 #include <stdint.h>
 #include <stdio.h>
 #include <string.h>
@@ -33,6 +34,7 @@
 #include <rte_malloc.h>
 #include <rte_string_fns.h>
 #include <rte_rwlock.h>
+#include <rte_cycles.h>
 
 #include "mlx5.h"
 #include "mlx5_glue.h"
@@ -240,6 +242,51 @@ mlx5_get_ifname(const struct rte_eth_dev *dev, char (*ifname)[IF_NAMESIZE])
        return -rte_errno;
 }
 
+/**
+ * Get interface name for the specified device, uses the extra base
+ * device resources to perform Netlink requests.
+ *
+ * This is a port representor-aware version of mlx5_get_master_ifname().
+ *
+ * @param[in] base
+ *   Pointer to Ethernet device to use Netlink socket from
+ *   to perfrom requests.
+ * @param[in] dev
+ *   Pointer to Ethernet device.
+ * @param[out] ifname
+ *   Interface name output buffer.
+ *
+ * @return
+ *   0 on success, a negative errno value otherwise and rte_errno is set.
+ */
+int
+mlx5_get_ifname_base(const struct rte_eth_dev *base,
+                    const struct rte_eth_dev *dev,
+                    char (*ifname)[IF_NAMESIZE])
+{
+       struct mlx5_priv *priv = dev->data->dev_private;
+       struct mlx5_priv *priv_base = base->data->dev_private;
+       unsigned int ifindex;
+
+       assert(priv);
+       assert(priv->sh);
+       assert(priv_base);
+       ifindex = priv_base->nl_socket_rdma >= 0 ?
+                 mlx5_nl_ifindex(priv_base->nl_socket_rdma,
+                                 priv->sh->ibdev_name,
+                                 priv->ibv_port) : 0;
+       if (!ifindex) {
+               if (!priv->representor)
+                       return mlx5_get_master_ifname(priv->sh->ibdev_path,
+                                                     ifname);
+               rte_errno = ENXIO;
+               return -rte_errno;
+       }
+       if (if_indextoname(ifindex, &(*ifname)[0]))
+               return 0;
+       rte_errno = errno;
+       return -rte_errno;
+}
 /**
  * Get the interface index from device name.
  *
@@ -301,6 +348,51 @@ error:
        return -rte_errno;
 }
 
+/**
+ * Perform ifreq ioctl() on specified Ethernet device,
+ * ifindex, name and other attributes are requested
+ * on the base device to avoid specified device Netlink
+ * socket sharing (this is not thread-safe).
+ *
+ * @param[in] base
+ *   Pointer to Ethernet device to get dev attributes.
+ * @param[in] dev
+ *   Pointer to Ethernet device to perform ioctl.
+ * @param req
+ *   Request number to pass to ioctl().
+ * @param[out] ifr
+ *   Interface request structure output buffer.
+ *
+ * @return
+ *   0 on success, a negative errno value otherwise and rte_errno is set.
+ */
+int
+mlx5_ifreq_base(const struct rte_eth_dev *base,
+               const struct rte_eth_dev *dev,
+               int req, struct ifreq *ifr)
+{
+       int sock = socket(PF_INET, SOCK_DGRAM, IPPROTO_IP);
+       int ret = 0;
+
+       if (sock == -1) {
+               rte_errno = errno;
+               return -rte_errno;
+       }
+       ret = mlx5_get_ifname_base(base, dev, &ifr->ifr_name);
+       if (ret)
+               goto error;
+       ret = ioctl(sock, req, ifr);
+       if (ret == -1) {
+               rte_errno = errno;
+               goto error;
+       }
+       close(sock);
+       return 0;
+error:
+       close(sock);
+       return -rte_errno;
+}
+
 /**
  * Get device MTU.
  *
@@ -748,7 +840,15 @@ mlx5_link_update_unlocked_gset(struct rte_eth_dev *dev,
                                ifr = (struct ifreq) {
                                        .ifr_data = (void *)&edata,
                                };
-                               ret = mlx5_ifreq(master, SIOCETHTOOL, &ifr);
+                               /*
+                                * Use special version of mlx5_ifreq()
+                                * to get master device name with local
+                                * device Netlink socket. Using master
+                                * device Netlink socket is not thread
+                                * safe.
+                                */
+                               ret = mlx5_ifreq_base(dev, master,
+                                                     SIOCETHTOOL, &ifr);
                        }
                }
                if (ret) {
@@ -845,7 +945,12 @@ mlx5_link_update_unlocked_gs(struct rte_eth_dev *dev,
                                ifr = (struct ifreq) {
                                        .ifr_data = (void *)&gcmd,
                                };
-                               ret = mlx5_ifreq(master, SIOCETHTOOL, &ifr);
+                               /*
+                                * Avoid using master Netlink socket.
+                                * This is not thread-safe.
+                                */
+                               ret = mlx5_ifreq_base(dev, master,
+                                                     SIOCETHTOOL, &ifr);
                        }
                }
                if (ret) {
@@ -866,7 +971,7 @@ mlx5_link_update_unlocked_gs(struct rte_eth_dev *dev,
 
        *ecmd = gcmd;
        ifr.ifr_data = (void *)ecmd;
-       ret = mlx5_ifreq(master ? master : dev, SIOCETHTOOL, &ifr);
+       ret = mlx5_ifreq_base(dev, master ? master : dev, SIOCETHTOOL, &ifr);
        if (ret) {
                DRV_LOG(DEBUG,
                        "port %u ioctl(SIOCETHTOOL,"
@@ -946,7 +1051,7 @@ mlx5_link_update(struct rte_eth_dev *dev, int wait_to_complete)
 
        do {
                ret = mlx5_link_update_unlocked_gs(dev, &dev_link);
-               if (ret)
+               if (ret == -ENOTSUP)
                        ret = mlx5_link_update_unlocked_gset(dev, &dev_link);
                if (ret == 0)
                        break;
@@ -1256,9 +1361,80 @@ mlx5_dev_interrupt_handler(void *cb_arg)
        }
 }
 
+/*
+ * Unregister callback handler safely. The handler may be active
+ * while we are trying to unregister it, in this case code -EAGAIN
+ * is returned by rte_intr_callback_unregister(). This routine checks
+ * the return code and tries to unregister handler again.
+ *
+ * @param handle
+ *   interrupt handle
+ * @param cb_fn
+ *   pointer to callback routine
+ * @cb_arg
+ *   opaque callback parameter
+ */
+void
+mlx5_intr_callback_unregister(const struct rte_intr_handle *handle,
+                             rte_intr_callback_fn cb_fn, void *cb_arg)
+{
+       /*
+        * Try to reduce timeout management overhead by not calling
+        * the timer related routines on the first iteration. If the
+        * unregistering succeeds on first call there will be no
+        * timer calls at all.
+        */
+       uint64_t twait = 0;
+       uint64_t start = 0;
+
+       do {
+               int ret;
+
+               ret = rte_intr_callback_unregister(handle, cb_fn, cb_arg);
+               if (ret >= 0)
+                       return;
+               if (ret != -EAGAIN) {
+                       DRV_LOG(INFO, "failed to unregister interrupt"
+                                     " handler (error: %d)", ret);
+                       assert(false);
+                       return;
+               }
+               if (twait) {
+                       struct timespec onems;
+
+                       /* Wait one millisecond and try again. */
+                       onems.tv_sec = 0;
+                       onems.tv_nsec = NS_PER_S / MS_PER_S;
+                       nanosleep(&onems, 0);
+                       /* Check whether one second elapsed. */
+                       if ((rte_get_timer_cycles() - start) <= twait)
+                               continue;
+               } else {
+                       /*
+                        * We get the amount of timer ticks for one second.
+                        * If this amount elapsed it means we spent one
+                        * second in waiting. This branch is executed once
+                        * on first iteration.
+                        */
+                       twait = rte_get_timer_hz();
+                       assert(twait);
+               }
+               /*
+                * Timeout elapsed, show message (once a second) and retry.
+                * We have no other acceptable option here, if we ignore
+                * the unregistering return code the handler will not
+                * be unregistered, fd will be closed and we may get the
+                * crush. Hanging and messaging in the loop seems not to be
+                * the worst choice.
+                */
+               DRV_LOG(INFO, "Retrying to unregister interrupt handler");
+               start = rte_get_timer_cycles();
+       } while (true);
+}
+
 /**
  * Uninstall shared asynchronous device events handler.
- * This function is implemeted to support event sharing
+ * This function is implemented to support event sharing
  * between multiple ports of single IB device.
  *
  * @param dev
@@ -1284,7 +1460,7 @@ mlx5_dev_shared_handler_uninstall(struct rte_eth_dev *dev)
        sh->port[priv->ibv_port - 1].ih_port_id = RTE_MAX_ETHPORTS;
        if (!sh->intr_cnt || --sh->intr_cnt)
                goto exit;
-       rte_intr_callback_unregister(&sh->intr_handle,
+       mlx5_intr_callback_unregister(&sh->intr_handle,
                                     mlx5_dev_interrupt_handler, sh);
        sh->intr_handle.fd = 0;
        sh->intr_handle.type = RTE_INTR_HANDLE_UNKNOWN;
@@ -1293,8 +1469,8 @@ exit:
 }
 
 /**
- * Install shared asyncronous device events handler.
- * This function is implemeted to support event sharing
+ * Install shared asynchronous device events handler.
+ * This function is implemented to support event sharing
  * between multiple ports of single IB device.
  *
  * @param dev