#include <mlx5_devx_cmds.h>
#include <mlx5_common.h>
#include <mlx5_malloc.h>
+#include <mlx5_nl.h>
#include "mlx5.h"
#include "mlx5_rxtx.h"
}
}
+static void
+mlx5_dev_interrupt_nl_cb(struct nlmsghdr *hdr, void *cb_arg)
+{
+ struct mlx5_dev_ctx_shared *sh = cb_arg;
+ uint32_t i;
+ uint32_t if_index;
+
+ if (mlx5_nl_parse_link_status_update(hdr, &if_index) < 0)
+ return;
+ for (i = 0; i < sh->max_port; i++) {
+ struct mlx5_dev_shared_port *port = &sh->port[i];
+ struct rte_eth_dev *dev;
+ struct mlx5_priv *priv;
+
+ if (port->nl_ih_port_id >= RTE_MAX_ETHPORTS)
+ continue;
+ dev = &rte_eth_devices[port->nl_ih_port_id];
+ /* Probing may initiate an LSC before configuration is done. */
+ if (dev->data->dev_configured &&
+ !dev->data->dev_conf.intr_conf.lsc)
+ break;
+ priv = dev->data->dev_private;
+ if (priv->if_index == if_index) {
+ /* Block logical LSC events. */
+ uint16_t prev_status = dev->data->dev_link.link_status;
+
+ if (mlx5_link_update(dev, 0) < 0)
+ DRV_LOG(ERR, "Failed to update link status: %s",
+ rte_strerror(rte_errno));
+ else if (prev_status != dev->data->dev_link.link_status)
+ rte_eth_dev_callback_process
+ (dev, RTE_ETH_EVENT_INTR_LSC, NULL);
+ break;
+ }
+ }
+}
+
+void
+mlx5_dev_interrupt_handler_nl(void *arg)
+{
+ struct mlx5_dev_ctx_shared *sh = arg;
+ int nlsk_fd = rte_intr_fd_get(sh->intr_handle_nl);
+
+ if (nlsk_fd < 0)
+ return;
+ if (mlx5_nl_read_events(nlsk_fd, mlx5_dev_interrupt_nl_cb, sh) < 0)
+ DRV_LOG(ERR, "Failed to process Netlink events: %s",
+ rte_strerror(rte_errno));
+}
+
/**
* Handle shared asynchronous events the NIC (removal event
* and link status change). Supports multiport IB device.
tmp = sh->port[tmp - 1].ih_port_id;
dev = &rte_eth_devices[tmp];
MLX5_ASSERT(dev);
- if ((event.event_type == IBV_EVENT_PORT_ACTIVE ||
- event.event_type == IBV_EVENT_PORT_ERR) &&
- dev->data->dev_conf.intr_conf.lsc) {
- mlx5_glue->ack_async_event(&event);
- if (mlx5_link_update(dev, 0) == -EAGAIN) {
- usleep(0);
- continue;
- }
- rte_eth_dev_callback_process
- (dev, RTE_ETH_EVENT_INTR_LSC, NULL);
- continue;
- }
DRV_LOG(DEBUG,
"port %u cannot handle an unknown event (type %d)",
dev->data->port_id, event.event_type);
mlx5_pmd_socket_uninit();
}
+static int
+mlx5_os_dev_shared_handler_install_lsc(struct mlx5_dev_ctx_shared *sh)
+{
+ int nlsk_fd, flags, ret;
+
+ nlsk_fd = mlx5_nl_init(NETLINK_ROUTE, RTMGRP_LINK);
+ if (nlsk_fd < 0) {
+ DRV_LOG(ERR, "Failed to create a socket for Netlink events: %s",
+ rte_strerror(rte_errno));
+ return -1;
+ }
+ flags = fcntl(nlsk_fd, F_GETFL);
+ ret = fcntl(nlsk_fd, F_SETFL, flags | O_NONBLOCK);
+ if (ret != 0) {
+ DRV_LOG(ERR, "Failed to make Netlink event socket non-blocking: %s",
+ strerror(errno));
+ rte_errno = errno;
+ goto error;
+ }
+ rte_intr_type_set(sh->intr_handle_nl, RTE_INTR_HANDLE_EXT);
+ rte_intr_fd_set(sh->intr_handle_nl, nlsk_fd);
+ if (rte_intr_callback_register(sh->intr_handle_nl,
+ mlx5_dev_interrupt_handler_nl,
+ sh) != 0) {
+ DRV_LOG(ERR, "Failed to register Netlink events interrupt");
+ rte_intr_fd_set(sh->intr_handle_nl, -1);
+ goto error;
+ }
+ return 0;
+error:
+ close(nlsk_fd);
+ return -1;
+}
+
/**
* Install shared asynchronous device events handler.
* This function is implemented to support event sharing
rte_intr_fd_set(sh->intr_handle, -1);
}
}
+ sh->intr_handle_nl = rte_intr_instance_alloc
+ (RTE_INTR_INSTANCE_F_SHARED);
+ if (sh->intr_handle_nl == NULL) {
+ DRV_LOG(ERR, "Fail to allocate intr_handle");
+ rte_errno = ENOMEM;
+ return;
+ }
+ rte_intr_fd_set(sh->intr_handle_nl, -1);
+ if (mlx5_os_dev_shared_handler_install_lsc(sh) < 0) {
+ DRV_LOG(INFO, "Fail to install the shared Netlink event handler.");
+ rte_intr_fd_set(sh->intr_handle_nl, -1);
+ }
if (sh->cdev->config.devx) {
#ifdef HAVE_IBV_DEVX_ASYNC
sh->intr_handle_devx =
void
mlx5_os_dev_shared_handler_uninstall(struct mlx5_dev_ctx_shared *sh)
{
+ int nlsk_fd;
+
if (rte_intr_fd_get(sh->intr_handle) >= 0)
mlx5_intr_callback_unregister(sh->intr_handle,
mlx5_dev_interrupt_handler, sh);
rte_intr_instance_free(sh->intr_handle);
+ nlsk_fd = rte_intr_fd_get(sh->intr_handle_nl);
+ if (nlsk_fd >= 0) {
+ mlx5_intr_callback_unregister
+ (sh->intr_handle_nl, mlx5_dev_interrupt_handler_nl, sh);
+ close(nlsk_fd);
+ }
+ rte_intr_instance_free(sh->intr_handle_nl);
#ifdef HAVE_IBV_DEVX_ASYNC
if (rte_intr_fd_get(sh->intr_handle_devx) >= 0)
rte_intr_callback_unregister(sh->intr_handle_devx,
for (i = 0; i < sh->max_port; i++) {
sh->port[i].ih_port_id = RTE_MAX_ETHPORTS;
sh->port[i].devx_ih_port_id = RTE_MAX_ETHPORTS;
+ sh->port[i].nl_ih_port_id = RTE_MAX_ETHPORTS;
}
if (sh->cdev->config.devx) {
sh->td = mlx5_devx_cmd_create_td(sh->cdev->ctx);
struct mlx5_dev_shared_port {
uint32_t ih_port_id;
uint32_t devx_ih_port_id;
+ uint32_t nl_ih_port_id;
/*
* Interrupt handler port_id. Used by shared interrupt
* handler to find the corresponding rte_eth device
/* Shared interrupt handler section. */
struct rte_intr_handle *intr_handle; /* Interrupt handler for device. */
struct rte_intr_handle *intr_handle_devx; /* DEVX interrupt handler. */
+ struct rte_intr_handle *intr_handle_nl; /* Netlink interrupt handler. */
void *devx_comp; /* DEVX async comp obj. */
struct mlx5_devx_obj *tis[16]; /* TIS object. */
struct mlx5_devx_obj *td; /* Transport domain. */
struct rte_eth_fc_conf *fc_conf);
void mlx5_dev_interrupt_handler(void *arg);
void mlx5_dev_interrupt_handler_devx(void *arg);
+void mlx5_dev_interrupt_handler_nl(void *arg);
int mlx5_set_link_down(struct rte_eth_dev *dev);
int mlx5_set_link_up(struct rte_eth_dev *dev);
int mlx5_is_removed(struct rte_eth_dev *dev);
priv->sh->port[priv->dev_port - 1].ih_port_id =
(uint32_t)dev->data->port_id;
} else {
- DRV_LOG(INFO, "port %u starts without LSC and RMV interrupts.",
+ DRV_LOG(INFO, "port %u starts without RMV interrupts.",
dev->data->port_id);
- dev->data->dev_conf.intr_conf.lsc = 0;
dev->data->dev_conf.intr_conf.rmv = 0;
}
+ if (rte_intr_fd_get(priv->sh->intr_handle_nl) >= 0) {
+ priv->sh->port[priv->dev_port - 1].nl_ih_port_id =
+ (uint32_t)dev->data->port_id;
+ } else {
+ DRV_LOG(INFO, "port %u starts without LSC interrupts.",
+ dev->data->port_id);
+ dev->data->dev_conf.intr_conf.lsc = 0;
+ }
if (rte_intr_fd_get(priv->sh->intr_handle_devx) >= 0)
priv->sh->port[priv->dev_port - 1].devx_ih_port_id =
(uint32_t)dev->data->port_id;
mlx5_rx_intr_vec_disable(dev);
priv->sh->port[priv->dev_port - 1].ih_port_id = RTE_MAX_ETHPORTS;
priv->sh->port[priv->dev_port - 1].devx_ih_port_id = RTE_MAX_ETHPORTS;
+ priv->sh->port[priv->dev_port - 1].nl_ih_port_id = RTE_MAX_ETHPORTS;
mlx5_txq_stop(dev);
mlx5_rxq_stop(dev);
if (priv->obj_ops.lb_dummy_queue_release)