git: 8d518f4b482f - stable/15 - mlx5_ib: do not consume CMD/PAGE_REQUEST events in the DEVX notifier
- Go to: [ bottom of page ] [ top of archives ] [ this month ]
Date: Mon, 20 Jul 2026 20:53:28 UTC
The branch stable/15 has been updated by kib:
URL: https://cgit.FreeBSD.org/src/commit/?id=8d518f4b482f64449b2bbb36a7c4627e80a3a93a
commit 8d518f4b482f64449b2bbb36a7c4627e80a3a93a
Author: Ariel Ehrenberg <aehrenberg@nvidia.com>
AuthorDate: 2026-06-09 11:20:08 +0000
Commit: Konstantin Belousov <kib@FreeBSD.org>
CommitDate: 2026-07-20 20:26:05 +0000
mlx5_ib: do not consume CMD/PAGE_REQUEST events in the DEVX notifier
(cherry picked from commit 2e3b3ce881490adcef17905450d0402030975051)
---
sys/dev/mlx5/mlx5_core/mlx5_eq.c | 33 ++++++++++++++++++++++++++++++---
sys/dev/mlx5/mlx5_ib/mlx5_ib_devx.c | 35 ++++++++++++++++++++++++++++-------
2 files changed, 58 insertions(+), 10 deletions(-)
diff --git a/sys/dev/mlx5/mlx5_core/mlx5_eq.c b/sys/dev/mlx5/mlx5_core/mlx5_eq.c
index 1090f8638171..f564b88c48b4 100644
--- a/sys/dev/mlx5/mlx5_core/mlx5_eq.c
+++ b/sys/dev/mlx5/mlx5_core/mlx5_eq.c
@@ -238,6 +238,10 @@ static int mlx5_eq_int(struct mlx5_core_dev *dev, struct mlx5_eq *eq)
u8 port;
while ((eqe = next_eqe_sw(eq))) {
+ bool (*devx_cb)(struct mlx5_core_dev *mdev,
+ uint8_t event_type, void *data);
+ bool devx_consumed = false;
+
/*
* Make sure we read EQ entry contents after we've
* checked the ownership bit.
@@ -247,9 +251,32 @@ static int mlx5_eq_int(struct mlx5_core_dev *dev, struct mlx5_eq *eq)
mlx5_core_dbg(eq->dev, "eqn %d, eqe type %s\n",
eq->eqn, eqe_type_str(eqe->type));
- if (dev->priv.eq_table.cb != NULL &&
- dev->priv.eq_table.cb(dev, eqe->type, &eqe->data)) {
- /* FALLTHROUGH */
+ /*
+ * The DEVX event notifier can be (un)registered concurrently.
+ * Fast path: when no notifier is registered (the common case
+ * with DEVX unused) skip RCU entirely - on FreeBSD LinuxKPI
+ * rcu_read_lock() pins the thread and starts an epoch section,
+ * which is not free per EQE. If a notifier appears registered,
+ * re-load the callback under rcu_read_lock() before calling it.
+ * The acquire load pairs with the store-release in
+ * mlx5_ib_devx_init_event_table() so eq_table.dev is visible
+ * once cb is; the re-check under RCU pairs with synchronize_rcu()
+ * in mlx5_ib_devx_cleanup_event_table() so the callback only
+ * ever runs inside an RCU section and none is in flight when the
+ * notifier (and its event table) is torn down.
+ */
+ devx_cb = smp_load_acquire(&dev->priv.eq_table.cb);
+ if (devx_cb != NULL) {
+ rcu_read_lock();
+ devx_cb = smp_load_acquire(&dev->priv.eq_table.cb);
+ if (devx_cb != NULL)
+ devx_consumed =
+ devx_cb(dev, eqe->type, &eqe->data);
+ rcu_read_unlock();
+ }
+
+ if (devx_consumed) {
+ /* event consumed by the DEVX notifier */
} else switch (eqe->type) {
case MLX5_EVENT_TYPE_COMP:
mlx5_cq_completion(dev, eqe);
diff --git a/sys/dev/mlx5/mlx5_ib/mlx5_ib_devx.c b/sys/dev/mlx5/mlx5_ib/mlx5_ib_devx.c
index 835a7fd8cfa4..6522a0aa6daf 100644
--- a/sys/dev/mlx5/mlx5_ib/mlx5_ib_devx.c
+++ b/sys/dev/mlx5/mlx5_ib/mlx5_ib_devx.c
@@ -2357,12 +2357,21 @@ static bool mlx5_devx_event_notifier(struct mlx5_core_dev *mdev,
bool is_unaffiliated;
u32 obj_id;
- /* Explicit filtering to kernel events which may occur frequently */
+ /*
+ * Command completions and page requests must be processed by the
+ * mlx5_core default EQ handler. Returning true here tells
+ * mlx5_eq_int() the event was consumed and skips core processing,
+ * which stalls the firmware command interface and page supply and
+ * wedges the device. Return false so the core handler runs for
+ * these frequent kernel events.
+ */
if (event_type == MLX5_EVENT_TYPE_CMD ||
event_type == MLX5_EVENT_TYPE_PAGE_REQUEST)
- return true;
+ return false;
- dev = mdev->priv.eq_table.dev;
+ dev = READ_ONCE(mdev->priv.eq_table.dev);
+ if (dev == NULL)
+ return false;
table = &dev->devx_event_table;
is_unaffiliated = is_unaffiliated_event(dev->mdev, event_type);
@@ -2401,8 +2410,14 @@ void mlx5_ib_devx_init_event_table(struct mlx5_ib_dev *dev)
xa_init_flags(&table->event_xa, 0);
mutex_init(&table->event_xa_lock);
- dev->mdev->priv.eq_table.dev = dev;
- dev->mdev->priv.eq_table.cb = mlx5_devx_event_notifier;
+ /*
+ * Publish dev before cb. The EQ interrupt handler loads cb with
+ * acquire semantics and the notifier then dereferences eq_table.dev,
+ * so dev must be visible to that handler once cb is observed.
+ */
+ WRITE_ONCE(dev->mdev->priv.eq_table.dev, dev);
+ smp_store_release(&dev->mdev->priv.eq_table.cb,
+ mlx5_devx_event_notifier);
}
void mlx5_ib_devx_cleanup_event_table(struct mlx5_ib_dev *dev)
@@ -2413,8 +2428,14 @@ void mlx5_ib_devx_cleanup_event_table(struct mlx5_ib_dev *dev)
void *entry;
unsigned long id;
- dev->mdev->priv.eq_table.cb = NULL;
- dev->mdev->priv.eq_table.dev = NULL;
+ /*
+ * Stop new dispatch by clearing cb, then wait for any in-flight EQ
+ * callback to finish its RCU read section before clearing dev and
+ * tearing down the event table.
+ */
+ WRITE_ONCE(dev->mdev->priv.eq_table.cb, NULL);
+ synchronize_rcu();
+ WRITE_ONCE(dev->mdev->priv.eq_table.dev, NULL);
mutex_lock(&dev->devx_event_table.event_xa_lock);
xa_for_each(&table->event_xa, id, entry) {
event = entry;