mlx5: Fix clock update EBUSY error flow

[ Upstream commit 0819419e791dd9cf9e145353fd97a09c43632f9f ]

mlx5dv_get_clock_info() reads clock data published by the kernel, and is
inherently racy with kernel clock updates. This race cannot be
eliminated; EBUSY will always be possible at some rate when wallclock
timestamping is used with extended CQ polling.

Add documentation for the EBUSY errno so callers know the expected
behavior: EBUSY indicates a CQE is available but the wallclock read
failed. Callers are expected to retry polling the CQ. Providers must not
consume the CQE on EBUSY and must leave the CQ in a valid state for the
next poll attempt.

Prior to this fix, the error was returned after the CQE was parsed, the
consumer index incremented, and WQ/SRQ modified. This left the CQ in an
inconsistent state: the CQE was silently lost and the CQ doorbell was
never updated, making recovery impossible regardless of retries.

Fix by moving the clock update before CQE parsing. At that point only
the consumer index has been advanced, so on failure it can be safely
reverted and EBUSY returned with the CQ intact.

Since EBUSY was previously undocumented and the error path was broken,
no existing user could have relied on this behavior. This fix is
therefore not a behavior change in practice.

Fixes: 4745c8079a86 ("mlx5: Implement read_completion_wallclock_ns")
Signed-off-by: Michael Gur <michaelgur@nvidia.com>
Signed-off-by: Yishai Hadas <yishaih@nvidia.com>
Signed-off-by: Nicolas Morey <nmorey@suse.com>
diff --git a/libibverbs/man/ibv_create_cq_ex.3 b/libibverbs/man/ibv_create_cq_ex.3
index 3949793..f0ea47b 100644
--- a/libibverbs/man/ibv_create_cq_ex.3
+++ b/libibverbs/man/ibv_create_cq_ex.3
@@ -74,6 +74,11 @@
 otherwise. When no completions are available on the CQ, ENOENT is returned, but the CQ remains
 in a valid state. On success, querying the completion's attribute could be done using the query
 functions described below. If an error code is given, end_poll shouldn't be called.
+When
+.B IBV_WC_EX_WITH_COMPLETION_TIMESTAMP_WALLCLOCK
+is requested, EBUSY may be returned transiently if the device clock information
+cannot be read due to a concurrent kernel update; the CQ remains in a valid state
+and the application should retry.
 
 .BI "int ibv_next_poll(struct ibv_cq_ex " "*cq")
 .br
diff --git a/providers/mlx5/cq.c b/providers/mlx5/cq.c
index 5b37f42..4085621 100644
--- a/providers/mlx5/cq.c
+++ b/providers/mlx5/cq.c
@@ -1137,6 +1137,17 @@
 		return ENOENT;
 	}
 
+	if (clock_update) {
+		err = mlx5dv_get_clock_info(ibcq->context, &cq->last_clock_info);
+		if (err) {
+			if (err == EBUSY)
+				--cq->cons_index;
+			if (lock)
+				mlx5_spin_unlock(&cq->lock);
+			return err;
+		}
+	}
+
 	if (stall)
 		cq->flags |= MLX5_CQ_FLAGS_FOUND_CQES;
 
@@ -1152,17 +1163,8 @@
 		}
 
 		cq->flags &= ~(MLX5_CQ_FLAGS_FOUND_CQES);
-
-		goto out;
 	}
 
-	if (clock_update && !err) {
-		err = mlx5dv_get_clock_info(ibcq->context, &cq->last_clock_info);
-		if (lock && err)
-			mlx5_spin_unlock(&cq->lock);
-	}
-
-out:
 	return err;
 }