net/mlx5: support reading module EEPROM data
[dpdk.git] / drivers / net / mlx5 / mlx5_ethdev.c
index 7785341..c68e773 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"
@@ -223,10 +225,7 @@ mlx5_get_ifname(const struct rte_eth_dev *dev, char (*ifname)[IF_NAMESIZE])
 
        assert(priv);
        assert(priv->sh);
-       ifindex = priv->nl_socket_rdma >= 0 ?
-                 mlx5_nl_ifindex(priv->nl_socket_rdma,
-                                 priv->sh->ibdev_name,
-                                 priv->ibv_port) : 0;
+       ifindex = mlx5_ifindex(dev);
        if (!ifindex) {
                if (!priv->representor)
                        return mlx5_get_master_ifname(priv->sh->ibdev_path,
@@ -252,14 +251,14 @@ mlx5_get_ifname(const struct rte_eth_dev *dev, char (*ifname)[IF_NAMESIZE])
 unsigned int
 mlx5_ifindex(const struct rte_eth_dev *dev)
 {
-       char ifname[IF_NAMESIZE];
+       struct mlx5_priv *priv = dev->data->dev_private;
        unsigned int ifindex;
 
-       if (mlx5_get_ifname(dev, &ifname))
-               return 0;
-       ifindex = if_nametoindex(ifname);
+       assert(priv);
+       assert(priv->if_index);
+       ifindex = priv->if_index;
        if (!ifindex)
-               rte_errno = errno;
+               rte_errno = ENXIO;
        return ifindex;
 }
 
@@ -499,6 +498,42 @@ mlx5_set_default_params(struct rte_eth_dev *dev, struct rte_eth_dev_info *info)
        }
 }
 
+/**
+ * Sets tx mbuf limiting parameters.
+ *
+ * @param dev
+ *   Pointer to Ethernet device.
+ * @param[out] info
+ *   Info structure output buffer.
+ */
+static void
+mlx5_set_txlimit_params(struct rte_eth_dev *dev, struct rte_eth_dev_info *info)
+{
+       struct mlx5_priv *priv = dev->data->dev_private;
+       struct mlx5_dev_config *config = &priv->config;
+       unsigned int inlen;
+       uint16_t nb_max;
+
+       inlen = (config->txq_inline_max == MLX5_ARG_UNSET) ?
+               MLX5_SEND_DEF_INLINE_LEN :
+               (unsigned int)config->txq_inline_max;
+       assert(config->txq_inline_min >= 0);
+       inlen = RTE_MAX(inlen, (unsigned int)config->txq_inline_min);
+       inlen = RTE_MIN(inlen, MLX5_WQE_SIZE_MAX +
+                              MLX5_ESEG_MIN_INLINE_SIZE -
+                              MLX5_WQE_CSEG_SIZE -
+                              MLX5_WQE_ESEG_SIZE -
+                              MLX5_WQE_DSEG_SIZE * 2);
+       nb_max = (MLX5_WQE_SIZE_MAX +
+                 MLX5_ESEG_MIN_INLINE_SIZE -
+                 MLX5_WQE_CSEG_SIZE -
+                 MLX5_WQE_ESEG_SIZE -
+                 MLX5_WQE_DSEG_SIZE -
+                 inlen) / MLX5_WSEG_SIZE;
+       info->tx_desc_lim.nb_seg_max = nb_max;
+       info->tx_desc_lim.nb_mtu_seg_max = nb_max;
+}
+
 /**
  * DPDK callback to get information about the device.
  *
@@ -513,7 +548,6 @@ mlx5_dev_infos_get(struct rte_eth_dev *dev, struct rte_eth_dev_info *info)
        struct mlx5_priv *priv = dev->data->dev_private;
        struct mlx5_dev_config *config = &priv->config;
        unsigned int max;
-       char ifname[IF_NAMESIZE];
 
        /* FIXME: we should ask the device for these values. */
        info->min_rx_bufsize = 32;
@@ -534,14 +568,14 @@ mlx5_dev_infos_get(struct rte_eth_dev *dev, struct rte_eth_dev_info *info)
        info->rx_offload_capa = (mlx5_get_rx_port_offloads() |
                                 info->rx_queue_offload_capa);
        info->tx_offload_capa = mlx5_get_tx_port_offloads(dev);
-       if (mlx5_get_ifname(dev, &ifname) == 0)
-               info->if_index = if_nametoindex(ifname);
+       info->if_index = mlx5_ifindex(dev);
        info->reta_size = priv->reta_idx_n ?
                priv->reta_idx_n : config->ind_table_max_size;
        info->hash_key_size = MLX5_RSS_HASH_KEY_LEN;
        info->speed_capa = priv->link_speed_capa;
        info->flow_type_rss_offloads = ~MLX5_RSS_HF_MASK;
        mlx5_set_default_params(dev, info);
+       mlx5_set_txlimit_params(dev, info);
        info->switch_info.name = dev->data->name;
        info->switch_info.domain_id = priv->domain_id;
        info->switch_info.port_id = priv->representor_id;
@@ -874,7 +908,8 @@ mlx5_link_update_unlocked_gs(struct rte_eth_dev *dev,
                        dev->data->port_id, strerror(rte_errno));
                return ret;
        }
-       dev_link.link_speed = ecmd->speed;
+       dev_link.link_speed = (ecmd->speed == UINT32_MAX) ? ETH_SPEED_NUM_NONE :
+                                                           ecmd->speed;
        sc = ecmd->link_mode_masks[0] |
                ((uint64_t)ecmd->link_mode_masks[1] << 32);
        priv->link_speed_capa = 0;
@@ -946,7 +981,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 +1291,112 @@ 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);
+}
+
+/**
+ * Handle DEVX interrupts from the NIC.
+ * This function is probably called from the DPDK host thread.
+ *
+ * @param cb_arg
+ *   Callback argument.
+ */
+void
+mlx5_dev_interrupt_handler_devx(void *cb_arg)
+{
+#ifndef HAVE_IBV_DEVX_ASYNC
+       (void)cb_arg;
+       return;
+#else
+       struct mlx5_ibv_shared *sh = cb_arg;
+       union {
+               struct mlx5dv_devx_async_cmd_hdr cmd_resp;
+               uint8_t buf[MLX5_ST_SZ_BYTES(query_flow_counter_out) +
+                           MLX5_ST_SZ_BYTES(traffic_counter) +
+                           sizeof(struct mlx5dv_devx_async_cmd_hdr)];
+       } out;
+       uint8_t *buf = out.buf + sizeof(out.cmd_resp);
+
+       while (!mlx5_glue->devx_get_async_cmd_comp(sh->devx_comp,
+                                                  &out.cmd_resp,
+                                                  sizeof(out.buf)))
+               mlx5_flow_async_pool_query_handle
+                       (sh, (uint64_t)out.cmd_resp.wr_id,
+                        mlx5_devx_get_out_command_status(buf));
+#endif /* HAVE_IBV_DEVX_ASYNC */
+}
+
 /**
  * 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,17 +1422,28 @@ 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;
+       if (sh->intr_handle_devx.fd) {
+               rte_intr_callback_unregister(&sh->intr_handle_devx,
+                                            mlx5_dev_interrupt_handler_devx,
+                                            sh);
+               sh->intr_handle_devx.fd = 0;
+               sh->intr_handle_devx.type = RTE_INTR_HANDLE_UNKNOWN;
+       }
+       if (sh->devx_comp) {
+               mlx5_glue->devx_destroy_cmd_comp(sh->devx_comp);
+               sh->devx_comp = NULL;
+       }
 exit:
        pthread_mutex_unlock(&sh->intr_mutex);
 }
 
 /**
- * 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
@@ -1331,17 +1480,50 @@ mlx5_dev_shared_handler_install(struct rte_eth_dev *dev)
        if (ret) {
                DRV_LOG(INFO, "failed to change file descriptor"
                              " async event queue");
-               /* Indicate there will be no interrupts. */
-               dev->data->dev_conf.intr_conf.lsc = 0;
-               dev->data->dev_conf.intr_conf.rmv = 0;
-               sh->port[priv->ibv_port - 1].ih_port_id = RTE_MAX_ETHPORTS;
-               goto exit;
+               goto error;
        }
        sh->intr_handle.fd = sh->ctx->async_fd;
        sh->intr_handle.type = RTE_INTR_HANDLE_EXT;
        rte_intr_callback_register(&sh->intr_handle,
                                   mlx5_dev_interrupt_handler, sh);
+       if (priv->config.devx) {
+#ifndef HAVE_IBV_DEVX_ASYNC
+               goto error_unregister;
+#else
+               sh->devx_comp = mlx5_glue->devx_create_cmd_comp(sh->ctx);
+               if (sh->devx_comp) {
+                       flags = fcntl(sh->devx_comp->fd, F_GETFL);
+                       ret = fcntl(sh->devx_comp->fd, F_SETFL,
+                                   flags | O_NONBLOCK);
+                       if (ret) {
+                               DRV_LOG(INFO, "failed to change file descriptor"
+                                             " devx async event queue");
+                               goto error_unregister;
+                       }
+                       sh->intr_handle_devx.fd = sh->devx_comp->fd;
+                       sh->intr_handle_devx.type = RTE_INTR_HANDLE_EXT;
+                       rte_intr_callback_register
+                               (&sh->intr_handle_devx,
+                                mlx5_dev_interrupt_handler_devx, sh);
+               } else {
+                       DRV_LOG(INFO, "failed to create devx async command "
+                               "completion");
+                       goto error_unregister;
+               }
+#endif /* HAVE_IBV_DEVX_ASYNC */
+       }
        sh->intr_cnt++;
+       goto exit;
+error_unregister:
+       rte_intr_callback_unregister(&sh->intr_handle,
+                                    mlx5_dev_interrupt_handler, sh);
+error:
+       /* Indicate there will be no interrupts. */
+       dev->data->dev_conf.intr_conf.lsc = 0;
+       dev->data->dev_conf.intr_conf.rmv = 0;
+       sh->intr_handle.fd = 0;
+       sh->intr_handle.type = RTE_INTR_HANDLE_UNKNOWN;
+       sh->port[priv->ibv_port - 1].ih_port_id = RTE_MAX_ETHPORTS;
 exit:
        pthread_mutex_unlock(&sh->intr_mutex);
 }
@@ -1400,64 +1582,6 @@ mlx5_set_link_up(struct rte_eth_dev *dev)
        return mlx5_set_flags(dev, ~IFF_UP, IFF_UP);
 }
 
-/**
- * Configure the TX function to use.
- *
- * @param dev
- *   Pointer to private data structure.
- *
- * @return
- *   Pointer to selected Tx burst function.
- */
-eth_tx_burst_t
-mlx5_select_tx_function(struct rte_eth_dev *dev)
-{
-       struct mlx5_priv *priv = dev->data->dev_private;
-       eth_tx_burst_t tx_pkt_burst = mlx5_tx_burst;
-       struct mlx5_dev_config *config = &priv->config;
-       uint64_t tx_offloads = dev->data->dev_conf.txmode.offloads;
-       int tso = !!(tx_offloads & (DEV_TX_OFFLOAD_TCP_TSO |
-                                   DEV_TX_OFFLOAD_VXLAN_TNL_TSO |
-                                   DEV_TX_OFFLOAD_GRE_TNL_TSO |
-                                   DEV_TX_OFFLOAD_IP_TNL_TSO |
-                                   DEV_TX_OFFLOAD_UDP_TNL_TSO));
-       int swp = !!(tx_offloads & (DEV_TX_OFFLOAD_IP_TNL_TSO |
-                                   DEV_TX_OFFLOAD_UDP_TNL_TSO |
-                                   DEV_TX_OFFLOAD_OUTER_IPV4_CKSUM));
-       int vlan_insert = !!(tx_offloads & DEV_TX_OFFLOAD_VLAN_INSERT);
-
-       assert(priv != NULL);
-       /* Select appropriate TX function. */
-       if (vlan_insert || tso || swp)
-               return tx_pkt_burst;
-       if (config->mps == MLX5_MPW_ENHANCED) {
-               if (mlx5_check_vec_tx_support(dev) > 0) {
-                       if (mlx5_check_raw_vec_tx_support(dev) > 0)
-                               tx_pkt_burst = mlx5_tx_burst_raw_vec;
-                       else
-                               tx_pkt_burst = mlx5_tx_burst_vec;
-                       DRV_LOG(DEBUG,
-                               "port %u selected enhanced MPW Tx vectorized"
-                               " function",
-                               dev->data->port_id);
-               } else {
-                       tx_pkt_burst = mlx5_tx_burst_empw;
-                       DRV_LOG(DEBUG,
-                               "port %u selected enhanced MPW Tx function",
-                               dev->data->port_id);
-               }
-       } else if (config->mps && (config->txq_inline > 0)) {
-               tx_pkt_burst = mlx5_tx_burst_mpw_inline;
-               DRV_LOG(DEBUG, "port %u selected MPW inline Tx function",
-                       dev->data->port_id);
-       } else if (config->mps) {
-               tx_pkt_burst = mlx5_tx_burst_mpw;
-               DRV_LOG(DEBUG, "port %u selected MPW Tx function",
-                       dev->data->port_id);
-       }
-       return tx_pkt_burst;
-}
-
 /**
  * Configure the RX function to use.
  *
@@ -1804,3 +1928,89 @@ mlx5_translate_port_name(const char *port_name_in,
        port_info_out->name_type = MLX5_PHYS_PORT_NAME_TYPE_UNKNOWN;
        return;
 }
+
+/**
+ * DPDK callback to retrieve plug-in module EEPROM information (type and size).
+ *
+ * @param dev
+ *   Pointer to Ethernet device structure.
+ * @param[out] modinfo
+ *   Storage for plug-in module EEPROM information.
+ *
+ * @return
+ *   0 on success, a negative errno value otherwise and rte_errno is set.
+ */
+int
+mlx5_get_module_info(struct rte_eth_dev *dev,
+                    struct rte_eth_dev_module_info *modinfo)
+{
+       struct ethtool_modinfo info = {
+               .cmd = ETHTOOL_GMODULEINFO,
+       };
+       struct ifreq ifr = (struct ifreq) {
+               .ifr_data = (void *)&info,
+       };
+       int ret = 0;
+
+       if (!dev || !modinfo) {
+               DRV_LOG(WARNING, "missing argument, cannot get module info");
+               rte_errno = EINVAL;
+               return -rte_errno;
+       }
+       ret = mlx5_ifreq(dev, SIOCETHTOOL, &ifr);
+       if (ret) {
+               DRV_LOG(WARNING, "port %u ioctl(SIOCETHTOOL) failed: %s",
+                       dev->data->port_id, strerror(rte_errno));
+               return ret;
+       }
+       modinfo->type = info.type;
+       modinfo->eeprom_len = info.eeprom_len;
+       return ret;
+}
+
+/**
+ * DPDK callback to retrieve plug-in module EEPROM data.
+ *
+ * @param dev
+ *   Pointer to Ethernet device structure.
+ * @param[out] info
+ *   Storage for plug-in module EEPROM data.
+ *
+ * @return
+ *   0 on success, a negative errno value otherwise and rte_errno is set.
+ */
+int mlx5_get_module_eeprom(struct rte_eth_dev *dev,
+                          struct rte_dev_eeprom_info *info)
+{
+       struct ethtool_eeprom *eeprom;
+       struct ifreq ifr;
+       int ret = 0;
+
+       if (!dev || !info) {
+               DRV_LOG(WARNING, "missing argument, cannot get module eeprom");
+               rte_errno = EINVAL;
+               return -rte_errno;
+       }
+       eeprom = rte_calloc(__func__, 1,
+                           (sizeof(struct ethtool_eeprom) + info->length), 0);
+       if (!eeprom) {
+               DRV_LOG(WARNING, "port %u cannot allocate memory for "
+                       "eeprom data", dev->data->port_id);
+               rte_errno = ENOMEM;
+               return -rte_errno;
+       }
+       eeprom->cmd = ETHTOOL_GMODULEEEPROM;
+       eeprom->offset = info->offset;
+       eeprom->len = info->length;
+       ifr = (struct ifreq) {
+               .ifr_data = (void *)eeprom,
+       };
+       ret = mlx5_ifreq(dev, SIOCETHTOOL, &ifr);
+       if (ret)
+               DRV_LOG(WARNING, "port %u ioctl(SIOCETHTOOL) failed: %s",
+                       dev->data->port_id, strerror(rte_errno));
+       else
+               rte_memcpy(info->data, eeprom->data, info->length);
+       rte_free(eeprom);
+       return ret;
+}