DPDK-dev Archive on lore.kernel.org
 help / color / mirror / Atom feed
From: Dariusz Sosnowski <dsosnowski@nvidia.com>
To: Thomas Monjalon <thomas@monjalon.net>,
	Viacheslav Ovsiienko <viacheslavo@nvidia.com>,
	Bing Zhao <bingz@nvidia.com>, Ori Kam <orika@nvidia.com>,
	Suanming Mou <suanmingm@nvidia.com>,
	Matan Azrad <matan@nvidia.com>,
	Dmitry Kozlyuk <dmitry.kozliuk@gmail.com>
Cc: <dev@dpdk.org>, <stable@dpdk.org>, Cosmin Ratiu <cratiu@nvidia.com>
Subject: [PATCH] net/mlx5: move to polling for LSC interrupts
Date: Mon, 26 Oct 2026 17:04:00 +0100	[thread overview]
Message-ID: <20261026160400.1290443-1-dsosnowski@nvidia.com> (raw)

mlx5 networking driver on Linux implemented LSC interrupts
by listening on netlink events.
This method is unfortunately not reliable enough,
because of linkwatch mechanism. It can delay link up/down events
for up to ~1 second. This introduces unnecessary delay
when mlx5 ports are used with DPDK bonding driver for examples.

This patch addresses that by migrating LSC checks
to polling on link status using ETHTOOL_GLINK command.
This ethtool command, implemented by mlx5 since kernel 4.14,
will always get the current link status.
As a side effect it is also flushes the pending link watch events.

Fixes: 17f95513adca ("net/mlx5: fix link status change detection")
Cc: dmitry.kozliuk@gmail.com
Cc: stable@dpdk.org

Signed-off-by: Cosmin Ratiu <cratiu@nvidia.com>
Signed-off-by: Dariusz Sosnowski <dsosnowski@nvidia.com>
---
 .mailmap                                 |   1 +
 drivers/common/mlx5/linux/mlx5_nl.c      |  29 ---
 drivers/common/mlx5/linux/mlx5_nl.h      |   2 -
 drivers/net/mlx5/linux/meson.build       |   1 +
 drivers/net/mlx5/linux/mlx5_ethdev_os.c  | 147 ++++-------
 drivers/net/mlx5/linux/mlx5_lsc_intr.c   | 302 +++++++++++++++++++++++
 drivers/net/mlx5/linux/mlx5_os.c         |  22 --
 drivers/net/mlx5/mlx5.c                  |   7 -
 drivers/net/mlx5/mlx5.h                  |   4 +-
 drivers/net/mlx5/mlx5_lsc_intr.h         |  15 ++
 drivers/net/mlx5/mlx5_trigger.c          |  19 +-
 drivers/net/mlx5/windows/meson.build     |   1 +
 drivers/net/mlx5/windows/mlx5_lsc_intr.c |  24 ++
 13 files changed, 398 insertions(+), 176 deletions(-)
 create mode 100644 drivers/net/mlx5/linux/mlx5_lsc_intr.c
 create mode 100644 drivers/net/mlx5/mlx5_lsc_intr.h
 create mode 100644 drivers/net/mlx5/windows/mlx5_lsc_intr.c

diff --git a/.mailmap b/.mailmap
index 6d9e262cfb..ba580bdd1c 100644
--- a/.mailmap
+++ b/.mailmap
@@ -315,6 +315,7 @@ Congjie Zhou <zcjie0802@qq.com>
 Congwen Zhang <zhang.congwen@zte.com.cn>
 Conor Fogarty <conor.fogarty@intel.com>
 Conor Walsh <conor.walsh@intel.com>
+Cosmin Ratiu <cratiu@nvidia.com>
 Cristian Bidea <cristian.bidea@keysight.com>
 Cristian Dumitrescu <cristian.dumitrescu@intel.com>
 Cristian Sovaiala <cristian.sovaiala@nxp.com>
diff --git a/drivers/common/mlx5/linux/mlx5_nl.c b/drivers/common/mlx5/linux/mlx5_nl.c
index 3fabcfaee0..cc33a9231f 100644
--- a/drivers/common/mlx5/linux/mlx5_nl.c
+++ b/drivers/common/mlx5/linux/mlx5_nl.c
@@ -1994,35 +1994,6 @@ mlx5_nl_enable_roce_set(int nlsk_fd, int family_id, const char *pci_addr,
 	return mlx5_nl_driver_reload(nlsk_fd, family_id, pci_addr);
 }
 
-/**
- * Try to parse a Netlink message as a link status update.
- *
- * @param hdr
- *  Netlink message header.
- * @param[out] ifindex
- *  Index of the updated interface.
- *
- * @return
- *  0 on success, negative on failure.
- */
-RTE_EXPORT_INTERNAL_SYMBOL(mlx5_nl_parse_link_status_update)
-int
-mlx5_nl_parse_link_status_update(struct nlmsghdr *hdr, uint32_t *ifindex)
-{
-	struct ifinfomsg *info;
-
-	switch (hdr->nlmsg_type) {
-	case RTM_NEWLINK:
-	case RTM_DELLINK:
-	case RTM_GETLINK:
-	case RTM_SETLINK:
-		info = NLMSG_DATA(hdr);
-		*ifindex = info->ifi_index;
-		return 0;
-	}
-	return -1;
-}
-
 /**
  * Read pending events from a Netlink socket.
  *
diff --git a/drivers/common/mlx5/linux/mlx5_nl.h b/drivers/common/mlx5/linux/mlx5_nl.h
index 07a3b531b5..d1c0c92e1e 100644
--- a/drivers/common/mlx5/linux/mlx5_nl.h
+++ b/drivers/common/mlx5/linux/mlx5_nl.h
@@ -102,8 +102,6 @@ int mlx5_nl_enable_roce_set(int nlsk_fd, int family_id, const char *pci_addr,
 
 __rte_internal
 int mlx5_nl_read_events(int nlsk_fd, mlx5_nl_event_cb *cb, void *cb_arg);
-__rte_internal
-int mlx5_nl_parse_link_status_update(struct nlmsghdr *hdr, uint32_t *ifindex);
 
 __rte_internal
 int mlx5_nl_devlink_esw_multiport_get(int nlsk_fd, int family_id,
diff --git a/drivers/net/mlx5/linux/meson.build b/drivers/net/mlx5/linux/meson.build
index e45ea988db..9fdb1555a6 100644
--- a/drivers/net/mlx5/linux/meson.build
+++ b/drivers/net/mlx5/linux/meson.build
@@ -10,6 +10,7 @@ sources += files(
         'mlx5_mp_os.c',
         'mlx5_vlan_os.c',
         'mlx5_flow_os.c',
+        'mlx5_lsc_intr.c',
 )
 
 cflags += no_wvla_cflag
diff --git a/drivers/net/mlx5/linux/mlx5_ethdev_os.c b/drivers/net/mlx5/linux/mlx5_ethdev_os.c
index f6394ebd19..d6103896f5 100644
--- a/drivers/net/mlx5/linux/mlx5_ethdev_os.c
+++ b/drivers/net/mlx5/linux/mlx5_ethdev_os.c
@@ -317,6 +317,40 @@ mlx5_find_master_dev(struct rte_eth_dev *dev)
 	return NULL;
 }
 
+/**
+ * Retrieve the carrier state of a device.
+ *
+ * The IFF_RUNNING flag reported by SIOCGIFFLAGS reflects the RFC 2863
+ * operational state, which the kernel only refreshes from its link watch
+ * work queue and which may therefore lag the actual carrier by up to a
+ * second. ETHTOOL_GLINK reports the carrier directly, and additionally
+ * flushes any link watch event still pending for the device.
+ *
+ * @param dev
+ *   Pointer to Ethernet device structure.
+ *
+ * @return
+ *   1 if the carrier is up, 0 otherwise.
+ *   Negative errno value is returned on error.
+ */
+int
+mlx5_link_carrier_get(struct rte_eth_dev *dev)
+{
+	struct ethtool_value ecmd = {
+		.cmd = ETHTOOL_GLINK,
+	};
+	struct ifreq ifr;
+	int ret;
+
+	memset(&ifr, 0, sizeof(ifr));
+	ifr.ifr_data = (void *)&ecmd;
+	ret = mlx5_ifreq(dev, SIOCETHTOOL, &ifr);
+	if (ret != 0)
+		return ret;
+
+	return !!ecmd.data;
+}
+
 /**
  * DPDK callback to retrieve physical link information.
  *
@@ -347,9 +381,11 @@ mlx5_link_update_unlocked_gset(struct rte_eth_dev *dev,
 			dev->data->port_id, strerror(rte_errno));
 		return ret;
 	}
+	ret = mlx5_link_carrier_get(dev);
+	if (ret < 0)
+		return ret;
 	dev_link = (struct rte_eth_link) {
-		.link_status = ((ifr.ifr_flags & IFF_UP) &&
-				(ifr.ifr_flags & IFF_RUNNING)),
+		.link_status = ((ifr.ifr_flags & IFF_UP) && ret),
 	};
 	ifr = (struct ifreq) {
 		.ifr_data = (void *)&edata,
@@ -428,9 +464,11 @@ mlx5_link_update_unlocked_gs(struct rte_eth_dev *dev,
 			dev->data->port_id, strerror(rte_errno));
 		return ret;
 	}
+	ret = mlx5_link_carrier_get(dev);
+	if (ret < 0)
+		return ret;
 	dev_link = (struct rte_eth_link) {
-		.link_status = ((ifr.ifr_flags & IFF_UP) &&
-				(ifr.ifr_flags & IFF_RUNNING)),
+		.link_status = ((ifr.ifr_flags & IFF_UP) && ret),
 	};
 	ifr = (struct ifreq) {
 		.ifr_data = (void *)&gcmd,
@@ -537,10 +575,7 @@ mlx5_link_update(struct rte_eth_dev *dev, int wait_to_complete)
 			return ret;
 		}
 	} while (wait_to_complete || retry-- > 0);
-	ret = !!memcmp(&dev->data->dev_link, &dev_link,
-		       sizeof(struct rte_eth_link));
-	dev->data->dev_link = dev_link;
-	return ret;
+	return rte_eth_linkstatus_set(dev, &dev_link) == 0;
 }
 
 /**
@@ -663,102 +698,6 @@ mlx5_dev_interrupt_device_fatal(struct mlx5_dev_ctx_shared *sh)
 	}
 }
 
-static bool
-mlx5_dev_nl_ifindex_verify(uint32_t if_index, struct mlx5_priv *priv)
-{
-	struct mlx5_bond_info *bond = &priv->sh->bond;
-	int i;
-
-	if (bond->n_port == 0)
-		return (if_index == priv->if_index);
-
-	if (if_index == bond->ifindex)
-		return true;
-	for (i = 0; i < MLX5_BOND_MAX_PORTS; i++) {
-		if (!strlen(priv->sh->bond.ports[i].ifname))
-			continue;
-		if (if_index == bond->ports[i].ifindex)
-			return true;
-	}
-
-	return false;
-}
-
-static void
-mlx5_link_update_bond(struct rte_eth_dev *dev)
-{
-	struct mlx5_priv *priv = dev->data->dev_private;
-	struct mlx5_bond_info *bond = &priv->sh->bond;
-	struct ifreq ifr = (struct ifreq) {
-		.ifr_flags = 0,
-	};
-	int ret;
-
-	ret = mlx5_ifreq_by_ifname(bond->ifname, SIOCGIFFLAGS, &ifr);
-	if (ret) {
-		DRV_LOG(WARNING, "ifname %s ioctl(SIOCGIFFLAGS) failed: %s",
-			bond->ifname, strerror(rte_errno));
-		return;
-	}
-	dev->data->dev_link.link_status =
-		((ifr.ifr_flags & IFF_UP) && (ifr.ifr_flags & IFF_RUNNING));
-}
-
-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 (mlx5_dev_nl_ifindex_verify(if_index, priv)) {
-			/* 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 (priv->sh->bond.n_port)
-					mlx5_link_update_bond(dev);
-				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.
diff --git a/drivers/net/mlx5/linux/mlx5_lsc_intr.c b/drivers/net/mlx5/linux/mlx5_lsc_intr.c
new file mode 100644
index 0000000000..2fb3b9adfc
--- /dev/null
+++ b/drivers/net/mlx5/linux/mlx5_lsc_intr.c
@@ -0,0 +1,302 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2026 NVIDIA Corporation & Affiliates
+ */
+
+#include <stdint.h>
+#include <time.h>
+
+#include <ethdev_driver.h>
+#include <rte_alarm.h>
+#include <rte_bitset.h>
+#include <rte_interrupts.h>
+#include <rte_pause.h>
+#include <rte_spinlock.h>
+#include <rte_stdatomic.h>
+
+#include "mlx5.h"
+#include "mlx5_lsc_intr.h"
+#include "mlx5_utils.h"
+
+#define LINK_POLL_DEFAULT_PERIOD_US (UINT64_C(100) * 1000)
+#define LINK_POLL_MINIMUM_PERIOD_US LINK_POLL_DEFAULT_PERIOD_US
+#define LINK_POLL_MAXIMUM_PERIOD_US (UINT64_C(800) * 1000)
+
+#define POLLED_PORTS_MAX (RTE_MAX_ETHPORTS)
+
+struct mlx5_lsc_intr_ctx {
+	RTE_BITSET_DECLARE(polled_ports, POLLED_PORTS_MAX);
+	uint64_t period_us;
+	bool armed;
+	rte_spinlock_t lock;
+	RTE_ATOMIC(bool) cb_in_progress;
+};
+
+static struct mlx5_lsc_intr_ctx lsc_intr_ctx = {
+	.period_us = LINK_POLL_DEFAULT_PERIOD_US,
+	.lock = RTE_SPINLOCK_INITIALIZER,
+};
+
+static inline bool
+lsc_intr_cb_in_progress(struct mlx5_lsc_intr_ctx *ctx)
+{
+	return rte_atomic_load_explicit(&ctx->cb_in_progress, rte_memory_order_seq_cst);
+}
+
+static inline void
+lsc_intr_cb_set_in_progress(struct mlx5_lsc_intr_ctx *ctx, bool val)
+{
+	rte_atomic_store_explicit(&ctx->cb_in_progress, val, rte_memory_order_seq_cst);
+}
+
+static struct timespec
+timespec_sub(const struct timespec *t0, const struct timespec *t1)
+{
+	struct timespec ts;
+
+	ts.tv_sec = t0->tv_sec - t1->tv_sec;
+	ts.tv_nsec = t0->tv_nsec - t1->tv_nsec;
+	if (ts.tv_nsec < 0) {
+		ts.tv_sec--;
+		ts.tv_nsec += 1000000000L;
+	}
+	return ts;
+}
+
+static uint64_t
+adjust_period_and_calc_delay(struct mlx5_lsc_intr_ctx *ctx,
+			     struct timespec *after,
+			     struct timespec *before)
+{
+	struct timespec delta = timespec_sub(after, before);
+	uint64_t new_period;
+	uint64_t delta_us;
+
+	delta_us = delta.tv_sec * 1000000;
+	delta_us += delta.tv_nsec / 1000;
+
+	new_period = ctx->period_us;
+
+	if (delta_us > new_period / 2) {
+		/* If takes too long, adjust period up. */
+		while (new_period < LINK_POLL_MAXIMUM_PERIOD_US && delta_us > new_period / 2)
+			new_period = RTE_MIN(new_period * 2, LINK_POLL_MAXIMUM_PERIOD_US);
+	} else if (delta_us < new_period / 4) {
+		/* If polling is fast, adjust period down. */
+		while (new_period > LINK_POLL_MINIMUM_PERIOD_US && delta_us < new_period / 4)
+			new_period = RTE_MAX(new_period / 2, LINK_POLL_MINIMUM_PERIOD_US);
+	}
+
+	if (new_period != ctx->period_us) {
+		DRV_LOG(DEBUG, "adjusting link polling period: %" PRIu64 " us to %" PRIu64 " us",
+			ctx->period_us, new_period);
+		ctx->period_us = new_period;
+	}
+
+	if (ctx->period_us == LINK_POLL_MAXIMUM_PERIOD_US) {
+		/*
+		 * If at maximum period and querying link still takes too long, then default to
+		 * half of the maximum period so other interrupt thread users are not starved.
+		 */
+		if (delta_us > ctx->period_us / 2)
+			return LINK_POLL_MAXIMUM_PERIOD_US / 2;
+	}
+
+	MLX5_ASSERT(delta_us <= ctx->period_us / 2);
+	return ctx->period_us - delta_us;
+}
+
+static void
+lsc_intr_disarm(struct mlx5_lsc_intr_ctx *ctx)
+{
+	rte_spinlock_lock(&ctx->lock);
+	ctx->armed = false;
+	rte_spinlock_unlock(&ctx->lock);
+}
+
+static bool
+lsc_intr_dev_add(struct mlx5_lsc_intr_ctx *ctx, struct rte_eth_dev *dev)
+{
+	uint16_t port_id = dev->data->port_id;
+	bool schedule = false;
+
+	rte_spinlock_lock(&ctx->lock);
+	rte_bitset_set(ctx->polled_ports, port_id);
+	if (!ctx->armed) {
+		ctx->armed = true;
+		schedule = true;
+	}
+	rte_spinlock_unlock(&ctx->lock);
+
+	return schedule;
+}
+
+static void
+lsc_intr_dev_remove(struct mlx5_lsc_intr_ctx *ctx, struct rte_eth_dev *dev, bool disarm)
+{
+	rte_spinlock_lock(&ctx->lock);
+	rte_bitset_clear(ctx->polled_ports, dev->data->port_id);
+	if (disarm)
+		ctx->armed = false;
+	rte_spinlock_unlock(&ctx->lock);
+}
+
+static int
+lsc_intr_gather_devs(struct mlx5_lsc_intr_ctx *ctx,
+		     struct rte_eth_dev *out[POLLED_PORTS_MAX])
+{
+	ssize_t port_id;
+	int ret = 0;
+
+	rte_spinlock_lock(&ctx->lock);
+	RTE_BITSET_FOREACH_SET(port_id, ctx->polled_ports, POLLED_PORTS_MAX) {
+		out[ret++] = &rte_eth_devices[port_id];
+	}
+	rte_spinlock_unlock(&ctx->lock);
+
+	return ret;
+}
+
+static void
+lsc_intr_check_links(struct rte_eth_dev **devs, int devs_nb)
+{
+	for (int i = 0; i < devs_nb; ++i) {
+		struct rte_eth_dev *dev = devs[i];
+		struct rte_eth_link dev_link;
+		int ret;
+
+		ret = mlx5_link_carrier_get(dev);
+		if (ret < 0) {
+			devs[i] = NULL;
+			continue;
+		}
+
+		dev_link = dev->data->dev_link;
+		if (dev_link.link_status == ret) {
+			devs[i] = NULL;
+			continue;
+		}
+		dev_link.link_status = ret;
+		dev->data->dev_link = dev_link;
+	}
+}
+
+static void
+lsc_intr_notify(struct rte_eth_dev **devs, int devs_nb)
+{
+	for (int i = 0; i < devs_nb; ++i) {
+		if (devs[i] == NULL)
+			continue;
+
+		rte_eth_dev_callback_process(devs[i], RTE_ETH_EVENT_INTR_LSC, NULL);
+	}
+}
+
+static bool
+lsc_intr_should_reschedule(struct mlx5_lsc_intr_ctx *ctx)
+{
+	bool should;
+
+	rte_spinlock_lock(&ctx->lock);
+	should = rte_bitset_count_set(ctx->polled_ports, POLLED_PORTS_MAX) > 0;
+	if (!should)
+		ctx->armed = false;
+	rte_spinlock_unlock(&ctx->lock);
+
+	return should;
+}
+
+static bool
+lsc_intr_cb(struct mlx5_lsc_intr_ctx *ctx, uint64_t *delay_us)
+{
+	static struct rte_eth_dev *to_process[POLLED_PORTS_MAX];
+	struct timespec before;
+	struct timespec after;
+	int to_process_nb = 0;
+	bool have_time = true;
+
+	if (clock_gettime(CLOCK_MONOTONIC, &before) < 0)
+		have_time = false;
+
+	to_process_nb = lsc_intr_gather_devs(ctx, to_process);
+	if (to_process_nb == 0)
+		return lsc_intr_should_reschedule(ctx);
+
+	lsc_intr_check_links(to_process, to_process_nb);
+	lsc_intr_notify(to_process, to_process_nb);
+
+	if (clock_gettime(CLOCK_MONOTONIC, &after) < 0)
+		have_time = false;
+
+	if (have_time) {
+		*delay_us = adjust_period_and_calc_delay(ctx, &after, &before);
+	} else {
+		DRV_LOG(WARNING, "link polling callback failed to calculate polling time");
+		*delay_us = ctx->period_us;
+	}
+
+	return lsc_intr_should_reschedule(ctx);
+}
+
+static void
+mlx5_lsc_intr_cb(void *arg)
+{
+	struct mlx5_lsc_intr_ctx *ctx = arg;
+	uint64_t delay_us = ctx->period_us;
+	bool reschedule;
+	int ret;
+
+	lsc_intr_cb_set_in_progress(ctx, true);
+	reschedule = lsc_intr_cb(ctx, &delay_us);
+	lsc_intr_cb_set_in_progress(ctx, false);
+
+	/* Reschedule the polling */
+	if (reschedule) {
+		ret = rte_eal_alarm_set(delay_us, mlx5_lsc_intr_cb, ctx);
+		if (ret < 0) {
+			DRV_LOG(ERR, "rescheduling link polling callback failed (%s)",
+				rte_strerror(-ret));
+			lsc_intr_disarm(ctx);
+		}
+	}
+}
+
+int
+mlx5_lsc_intr_setup(struct rte_eth_dev *dev)
+{
+	struct mlx5_lsc_intr_ctx *ctx = &lsc_intr_ctx;
+	bool schedule;
+	int ret;
+
+	schedule = lsc_intr_dev_add(ctx, dev);
+	if (!schedule)
+		return 0;
+
+	ret = rte_eal_alarm_set(ctx->period_us, mlx5_lsc_intr_cb, ctx);
+	if (ret < 0) {
+		DRV_LOG(ERR, "port %u registering link polling callback failed (%s)",
+			dev->data->port_id, rte_strerror(-ret));
+		lsc_intr_dev_remove(ctx, dev, true);
+		return ret;
+	}
+	DRV_LOG(DEBUG, "registered link polling callback");
+
+	return 0;
+}
+
+void
+mlx5_lsc_intr_cleanup(struct rte_eth_dev *dev)
+{
+	struct mlx5_lsc_intr_ctx *ctx = &lsc_intr_ctx;
+
+	lsc_intr_dev_remove(ctx, dev, false);
+
+	/*
+	 * If callback is running, then wait until it finishes to prevent
+	 * freeing dev while it is running.
+	 *
+	 * mlx5_lsc_intr_cb will be called only from interrupt thread.
+	 * If cleanup was called from the callback, then no need to wait.
+	 */
+	while (lsc_intr_cb_in_progress(ctx) && !rte_thread_is_intr())
+		rte_pause();
+}
diff --git a/drivers/net/mlx5/linux/mlx5_os.c b/drivers/net/mlx5/linux/mlx5_os.c
index 7c2c35c945..08dd2b46d9 100644
--- a/drivers/net/mlx5/linux/mlx5_os.c
+++ b/drivers/net/mlx5/linux/mlx5_os.c
@@ -1803,8 +1803,6 @@ mlx5_dev_spawn(struct rte_device *dpdk_dev,
 		eth_dev->data->port_id);
 	/* Read link status in case it is up and there will be no event. */
 	mlx5_link_update(eth_dev, 0);
-	/* Watch LSC interrupts between port probe and port start. */
-	priv->sh->port[priv->dev_port - 1].nl_ih_port_id = eth_dev->data->port_id;
 	mlx5_set_link_up(eth_dev);
 	for (i = 0; i < MLX5_FLOW_TYPE_MAXI; i++) {
 		icfg[i].release_mem_en = !!sh->config.reclaim_mode;
@@ -1956,8 +1954,6 @@ mlx5_dev_spawn(struct rte_device *dpdk_dev,
 	return eth_dev;
 error:
 	if (priv) {
-		priv->sh->port[priv->dev_port - 1].nl_ih_port_id =
-							       RTE_MAX_ETHPORTS;
 		rte_io_wmb();
 #ifdef HAVE_MLX5_HWS_SUPPORT
 		if (eth_dev &&
@@ -3262,19 +3258,6 @@ mlx5_os_dev_shared_handler_install(struct mlx5_dev_ctx_shared *sh)
 			}
 		}
 	}
-	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;
-	}
-	sh->intr_handle_nl = mlx5_os_interrupt_handler_create
-		(RTE_INTR_INSTANCE_F_SHARED, true,
-		 nlsk_fd, mlx5_dev_interrupt_handler_nl, sh);
-	if (sh->intr_handle_nl == NULL) {
-		DRV_LOG(ERR, "Fail to allocate intr_handle");
-		return;
-	}
 	if (sh->cdev->config.devx) {
 #ifdef HAVE_IBV_DEVX_ASYNC
 		struct mlx5dv_devx_cmd_comp *devx_comp;
@@ -3312,11 +3295,6 @@ mlx5_os_dev_shared_handler_uninstall(struct mlx5_dev_ctx_shared *sh)
 
 	mlx5_os_interrupt_handler_destroy(sh->intr_handle,
 					  mlx5_dev_interrupt_handler, sh);
-	fd = rte_intr_fd_get(sh->intr_handle_nl);
-	mlx5_os_interrupt_handler_destroy(sh->intr_handle_nl,
-					  mlx5_dev_interrupt_handler_nl, sh);
-	if (fd >= 0)
-		close(fd);
 #ifdef HAVE_IBV_DEVX_ASYNC
 	mlx5_os_interrupt_handler_destroy(sh->intr_handle_devx,
 					  mlx5_dev_interrupt_handler_devx, sh);
diff --git a/drivers/net/mlx5/mlx5.c b/drivers/net/mlx5/mlx5.c
index a04884fb4b..2fdd17f08e 100644
--- a/drivers/net/mlx5/mlx5.c
+++ b/drivers/net/mlx5/mlx5.c
@@ -1930,7 +1930,6 @@ mlx5_alloc_shared_dev_ctx(const struct mlx5_dev_spawn_data *spawn,
 	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);
@@ -2559,12 +2558,6 @@ mlx5_dev_close(struct rte_eth_dev *dev)
 	mlx5_free(priv->mac);
 	dev->data->mac_addrs = NULL;
 	mlx5_free(priv->mac_own);
-	sh->port[priv->dev_port - 1].nl_ih_port_id = RTE_MAX_ETHPORTS;
-	/*
-	 * The interrupt handler port id must be reset before priv is reset
-	 * since 'mlx5_dev_interrupt_nl_cb' uses priv.
-	 */
-	rte_io_wmb();
 	/*
 	 * Free the shared context in last turn, because the cleanup
 	 * routines above may use some shared fields, like
diff --git a/drivers/net/mlx5/mlx5.h b/drivers/net/mlx5/mlx5.h
index e3df458915..51c789bf45 100644
--- a/drivers/net/mlx5/mlx5.h
+++ b/drivers/net/mlx5/mlx5.h
@@ -838,7 +838,6 @@ struct mlx5_age_info {
 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
@@ -1649,7 +1648,6 @@ struct mlx5_dev_ctx_shared {
 	/* 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. */
 	struct rte_intr_handle *intr_handle_ib; /* Interrupt handler for IB device. */
 	void *devx_comp; /* DEVX async comp obj. */
 	struct mlx5_devx_obj *tis[16]; /* TIS object. */
@@ -2382,13 +2380,13 @@ int mlx5_get_mtu(struct rte_eth_dev *dev, uint16_t *mtu);
 int mlx5_set_mtu(struct rte_eth_dev *dev, uint16_t mtu);
 int mlx5_read_clock(struct rte_eth_dev *dev, uint64_t *clock);
 int mlx5_link_update(struct rte_eth_dev *dev, int wait_to_complete);
+int mlx5_link_carrier_get(struct rte_eth_dev *dev);
 int mlx5_dev_get_flow_ctrl(struct rte_eth_dev *dev,
 			   struct rte_eth_fc_conf *fc_conf);
 int mlx5_dev_set_flow_ctrl(struct rte_eth_dev *dev,
 			   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);
 void mlx5_dev_interrupt_handler_ib(void *arg);
 int mlx5_set_link_down(struct rte_eth_dev *dev);
 int mlx5_set_link_up(struct rte_eth_dev *dev);
diff --git a/drivers/net/mlx5/mlx5_lsc_intr.h b/drivers/net/mlx5/mlx5_lsc_intr.h
new file mode 100644
index 0000000000..4e4a1eef5b
--- /dev/null
+++ b/drivers/net/mlx5/mlx5_lsc_intr.h
@@ -0,0 +1,15 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2026 NVIDIA Corporation & Affiliates
+ */
+
+#ifndef RTE_PMD_MLX5_LSC_INTR_H_
+#define RTE_PMD_MLX5_LSC_INTR_H_
+
+#include <stdbool.h>
+
+struct rte_eth_dev;
+
+int mlx5_lsc_intr_setup(struct rte_eth_dev *dev);
+void mlx5_lsc_intr_cleanup(struct rte_eth_dev *dev);
+
+#endif /* RTE_PMD_MLX5_LSC_INTR_H_ */
diff --git a/drivers/net/mlx5/mlx5_trigger.c b/drivers/net/mlx5/mlx5_trigger.c
index c5f493117b..7c166fa687 100644
--- a/drivers/net/mlx5/mlx5_trigger.c
+++ b/drivers/net/mlx5/mlx5_trigger.c
@@ -18,6 +18,7 @@
 #include "mlx5_rx.h"
 #include "mlx5_tx.h"
 #include "mlx5_utils.h"
+#include "mlx5_lsc_intr.h"
 #include "rte_pmd_mlx5.h"
 
 static void mlx5_traffic_disable_legacy(struct rte_eth_dev *dev);
@@ -1507,6 +1508,14 @@ mlx5_dev_start(struct rte_eth_dev *dev)
 	dev->rx_pkt_burst = mlx5_select_rx_function(dev);
 	/* Enable datapath on secondary process. */
 	mlx5_mp_os_req_start_rxtx(dev);
+	if (dev->data->dev_conf.intr_conf.lsc) {
+		ret = mlx5_lsc_intr_setup(dev);
+		if (ret < 0) {
+			DRV_LOG(INFO, "port %u failed configure LSC interrupts, starting without",
+				dev->data->port_id);
+			dev->data->dev_conf.intr_conf.lsc = 0;
+		}
+	}
 	if (rte_intr_fd_get(priv->sh->intr_handle) >= 0) {
 		priv->sh->port[priv->dev_port - 1].ih_port_id =
 					(uint32_t)dev->data->port_id;
@@ -1515,14 +1524,6 @@ mlx5_dev_start(struct rte_eth_dev *dev)
 			dev->data->port_id);
 		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;
@@ -1664,7 +1665,7 @@ mlx5_dev_stop(struct rte_eth_dev *dev)
 	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_lsc_intr_cleanup(dev);
 	mlx5_txq_stop(dev);
 	mlx5_rxq_stop(dev);
 	mlx5_dev_free_consec_tx_mem(dev, true);
diff --git a/drivers/net/mlx5/windows/meson.build b/drivers/net/mlx5/windows/meson.build
index c7d6617275..73cdcc47ae 100644
--- a/drivers/net/mlx5/windows/meson.build
+++ b/drivers/net/mlx5/windows/meson.build
@@ -8,4 +8,5 @@ sources += files(
         'mlx5_ethdev_os.c',
         'mlx5_vlan_os.c',
         'mlx5_flow_os.c',
+        'mlx5_lsc_intr.c',
 )
diff --git a/drivers/net/mlx5/windows/mlx5_lsc_intr.c b/drivers/net/mlx5/windows/mlx5_lsc_intr.c
new file mode 100644
index 0000000000..eac5bfdac2
--- /dev/null
+++ b/drivers/net/mlx5/windows/mlx5_lsc_intr.c
@@ -0,0 +1,24 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (c) 2026 NVIDIA Corporation & Affiliates
+ */
+
+#include <errno.h>
+#include <stdbool.h>
+
+#include <ethdev_driver.h>
+#include <rte_common.h>
+
+#include "mlx5_utils.h"
+#include "mlx5_lsc_intr.h"
+
+int
+mlx5_lsc_intr_setup(struct rte_eth_dev *dev)
+{
+	DRV_LOG(ERR, "port %u does not support LSC interrupts", dev->data->port_id);
+	return -ENOTSUP;
+}
+
+void
+mlx5_lsc_intr_cleanup(struct rte_eth_dev *dev __rte_unused)
+{
+}
-- 
2.43.0


             reply	other threads:[~2026-10-26 16:05 UTC|newest]

Thread overview: 3+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-10-26 16:04 Dariusz Sosnowski [this message]
2026-10-26 17:03 ` [PATCH] net/mlx5: move to polling for LSC interrupts Stephen Hemminger
2026-10-26 17:13 ` Stephen Hemminger

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20261026160400.1290443-1-dsosnowski@nvidia.com \
    --to=dsosnowski@nvidia.com \
    --cc=bingz@nvidia.com \
    --cc=cratiu@nvidia.com \
    --cc=dev@dpdk.org \
    --cc=dmitry.kozliuk@gmail.com \
    --cc=matan@nvidia.com \
    --cc=orika@nvidia.com \
    --cc=stable@dpdk.org \
    --cc=suanmingm@nvidia.com \
    --cc=thomas@monjalon.net \
    --cc=viacheslavo@nvidia.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox