Skip to content

Commit 1d5558b

Browse files
Eran Ben ElishaSaeed Mahameed
authored andcommitted
net/mlx5: poll cmd EQ in case of command timeout
Once driver detects a command interface command timeout, it warns the user and returns timeout error to the caller. In such case, the entry of the command is not evacuated (because only real event interrupt is allowed to clear command interface entry). If the HW event interrupt of this entry will never arrive, this entry will be left unused forever. Command interface entries are limited and eventually we can end up without the ability to post a new command. In addition, if driver will not consume the EQE of the lost interrupt and rearm the EQ, no new interrupts will arrive for other commands. Add a resiliency mechanism for manually polling the command EQ in case of a command timeout. In case resiliency mechanism will find non-handled EQE, it will consume it, and the command interface will be fully functional again. Once the resiliency flow finished, wait another 5 seconds for the command interface to complete for this command entry. Define mlx5_cmd_eq_recover() to manage the cmd EQ polling resiliency flow. Add an async EQ spinlock to avoid races between resiliency flows and real interrupts that might run simultaneously. Fixes: e126ba9 ("mlx5: Add driver for Mellanox Connect-IB adapters") Signed-off-by: Eran Ben Elisha <[email protected]> Signed-off-by: Saeed Mahameed <[email protected]>
1 parent 50b2412 commit 1d5558b

File tree

3 files changed

+86
-9
lines changed

3 files changed

+86
-9
lines changed

drivers/net/ethernet/mellanox/mlx5/core/cmd.c

Lines changed: 45 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -853,11 +853,21 @@ static void cb_timeout_handler(struct work_struct *work)
853853
struct mlx5_core_dev *dev = container_of(ent->cmd, struct mlx5_core_dev,
854854
cmd);
855855

856+
mlx5_cmd_eq_recover(dev);
857+
858+
/* Maybe got handled by eq recover ? */
859+
if (!test_bit(MLX5_CMD_ENT_STATE_PENDING_COMP, &ent->state)) {
860+
mlx5_core_warn(dev, "cmd[%d]: %s(0x%x) Async, recovered after timeout\n", ent->idx,
861+
mlx5_command_str(msg_to_opcode(ent->in)), msg_to_opcode(ent->in));
862+
goto out; /* phew, already handled */
863+
}
864+
856865
ent->ret = -ETIMEDOUT;
857-
mlx5_core_warn(dev, "%s(0x%x) timeout. Will cause a leak of a command resource\n",
858-
mlx5_command_str(msg_to_opcode(ent->in)),
859-
msg_to_opcode(ent->in));
866+
mlx5_core_warn(dev, "cmd[%d]: %s(0x%x) Async, timeout. Will cause a leak of a command resource\n",
867+
ent->idx, mlx5_command_str(msg_to_opcode(ent->in)), msg_to_opcode(ent->in));
860868
mlx5_cmd_comp_handler(dev, 1UL << ent->idx, true);
869+
870+
out:
861871
cmd_ent_put(ent); /* for the cmd_ent_get() took on schedule delayed work */
862872
}
863873

@@ -997,6 +1007,35 @@ static const char *deliv_status_to_str(u8 status)
9971007
}
9981008
}
9991009

1010+
enum {
1011+
MLX5_CMD_TIMEOUT_RECOVER_MSEC = 5 * 1000,
1012+
};
1013+
1014+
static void wait_func_handle_exec_timeout(struct mlx5_core_dev *dev,
1015+
struct mlx5_cmd_work_ent *ent)
1016+
{
1017+
unsigned long timeout = msecs_to_jiffies(MLX5_CMD_TIMEOUT_RECOVER_MSEC);
1018+
1019+
mlx5_cmd_eq_recover(dev);
1020+
1021+
/* Re-wait on the ent->done after executing the recovery flow. If the
1022+
* recovery flow (or any other recovery flow running simultaneously)
1023+
* has recovered an EQE, it should cause the entry to be completed by
1024+
* the command interface.
1025+
*/
1026+
if (wait_for_completion_timeout(&ent->done, timeout)) {
1027+
mlx5_core_warn(dev, "cmd[%d]: %s(0x%x) recovered after timeout\n", ent->idx,
1028+
mlx5_command_str(msg_to_opcode(ent->in)), msg_to_opcode(ent->in));
1029+
return;
1030+
}
1031+
1032+
mlx5_core_warn(dev, "cmd[%d]: %s(0x%x) No done completion\n", ent->idx,
1033+
mlx5_command_str(msg_to_opcode(ent->in)), msg_to_opcode(ent->in));
1034+
1035+
ent->ret = -ETIMEDOUT;
1036+
mlx5_cmd_comp_handler(dev, 1UL << ent->idx, true);
1037+
}
1038+
10001039
static int wait_func(struct mlx5_core_dev *dev, struct mlx5_cmd_work_ent *ent)
10011040
{
10021041
unsigned long timeout = msecs_to_jiffies(MLX5_CMD_TIMEOUT_MSEC);
@@ -1008,12 +1047,10 @@ static int wait_func(struct mlx5_core_dev *dev, struct mlx5_cmd_work_ent *ent)
10081047
ent->ret = -ECANCELED;
10091048
goto out_err;
10101049
}
1011-
if (cmd->mode == CMD_MODE_POLLING || ent->polling) {
1050+
if (cmd->mode == CMD_MODE_POLLING || ent->polling)
10121051
wait_for_completion(&ent->done);
1013-
} else if (!wait_for_completion_timeout(&ent->done, timeout)) {
1014-
ent->ret = -ETIMEDOUT;
1015-
mlx5_cmd_comp_handler(dev, 1UL << ent->idx, true);
1016-
}
1052+
else if (!wait_for_completion_timeout(&ent->done, timeout))
1053+
wait_func_handle_exec_timeout(dev, ent);
10171054

10181055
out_err:
10191056
err = ent->ret;

drivers/net/ethernet/mellanox/mlx5/core/eq.c

Lines changed: 39 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -189,6 +189,29 @@ u32 mlx5_eq_poll_irq_disabled(struct mlx5_eq_comp *eq)
189189
return count_eqe;
190190
}
191191

192+
static void mlx5_eq_async_int_lock(struct mlx5_eq_async *eq, unsigned long *flags)
193+
__acquires(&eq->lock)
194+
{
195+
if (in_irq())
196+
spin_lock(&eq->lock);
197+
else
198+
spin_lock_irqsave(&eq->lock, *flags);
199+
}
200+
201+
static void mlx5_eq_async_int_unlock(struct mlx5_eq_async *eq, unsigned long *flags)
202+
__releases(&eq->lock)
203+
{
204+
if (in_irq())
205+
spin_unlock(&eq->lock);
206+
else
207+
spin_unlock_irqrestore(&eq->lock, *flags);
208+
}
209+
210+
enum async_eq_nb_action {
211+
ASYNC_EQ_IRQ_HANDLER = 0,
212+
ASYNC_EQ_RECOVER = 1,
213+
};
214+
192215
static int mlx5_eq_async_int(struct notifier_block *nb,
193216
unsigned long action, void *data)
194217
{
@@ -198,11 +221,14 @@ static int mlx5_eq_async_int(struct notifier_block *nb,
198221
struct mlx5_eq_table *eqt;
199222
struct mlx5_core_dev *dev;
200223
struct mlx5_eqe *eqe;
224+
unsigned long flags;
201225
int num_eqes = 0;
202226

203227
dev = eq->dev;
204228
eqt = dev->priv.eq_table;
205229

230+
mlx5_eq_async_int_lock(eq_async, &flags);
231+
206232
eqe = next_eqe_sw(eq);
207233
if (!eqe)
208234
goto out;
@@ -223,8 +249,19 @@ static int mlx5_eq_async_int(struct notifier_block *nb,
223249

224250
out:
225251
eq_update_ci(eq, 1);
252+
mlx5_eq_async_int_unlock(eq_async, &flags);
226253

227-
return 0;
254+
return unlikely(action == ASYNC_EQ_RECOVER) ? num_eqes : 0;
255+
}
256+
257+
void mlx5_cmd_eq_recover(struct mlx5_core_dev *dev)
258+
{
259+
struct mlx5_eq_async *eq = &dev->priv.eq_table->cmd_eq;
260+
int eqes;
261+
262+
eqes = mlx5_eq_async_int(&eq->irq_nb, ASYNC_EQ_RECOVER, NULL);
263+
if (eqes)
264+
mlx5_core_warn(dev, "Recovered %d EQEs on cmd_eq\n", eqes);
228265
}
229266

230267
static void init_eq_buf(struct mlx5_eq *eq)
@@ -569,6 +606,7 @@ setup_async_eq(struct mlx5_core_dev *dev, struct mlx5_eq_async *eq,
569606
int err;
570607

571608
eq->irq_nb.notifier_call = mlx5_eq_async_int;
609+
spin_lock_init(&eq->lock);
572610

573611
err = create_async_eq(dev, &eq->core, param);
574612
if (err) {

drivers/net/ethernet/mellanox/mlx5/core/lib/eq.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ struct mlx5_eq {
3737
struct mlx5_eq_async {
3838
struct mlx5_eq core;
3939
struct notifier_block irq_nb;
40+
spinlock_t lock; /* To avoid irq EQ handle races with resiliency flows */
4041
};
4142

4243
struct mlx5_eq_comp {
@@ -81,6 +82,7 @@ void mlx5_cq_tasklet_cb(unsigned long data);
8182
struct cpumask *mlx5_eq_comp_cpumask(struct mlx5_core_dev *dev, int ix);
8283

8384
u32 mlx5_eq_poll_irq_disabled(struct mlx5_eq_comp *eq);
85+
void mlx5_cmd_eq_recover(struct mlx5_core_dev *dev);
8486
void mlx5_eq_synchronize_async_irq(struct mlx5_core_dev *dev);
8587
void mlx5_eq_synchronize_cmd_irq(struct mlx5_core_dev *dev);
8688

0 commit comments

Comments
 (0)