Skip to content

Commit 3fa9daa

Browse files
Eran Ben Elishagregkh
authored andcommitted
net/mlx5: poll cmd EQ in case of command timeout
commit 1d5558b upstream. 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 <eranbe@mellanox.com> Signed-off-by: Saeed Mahameed <saeedm@nvidia.com> Cc: Timo Rothenpieler <timo@rothenpieler.org> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
1 parent 42bb7b7 commit 3fa9daa

3 files changed

Lines changed: 86 additions & 9 deletions

File tree

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

@@ -987,6 +997,35 @@ static const char *deliv_status_to_str(u8 status)
987997
}
988998
}
989999

1000+
enum {
1001+
MLX5_CMD_TIMEOUT_RECOVER_MSEC = 5 * 1000,
1002+
};
1003+
1004+
static void wait_func_handle_exec_timeout(struct mlx5_core_dev *dev,
1005+
struct mlx5_cmd_work_ent *ent)
1006+
{
1007+
unsigned long timeout = msecs_to_jiffies(MLX5_CMD_TIMEOUT_RECOVER_MSEC);
1008+
1009+
mlx5_cmd_eq_recover(dev);
1010+
1011+
/* Re-wait on the ent->done after executing the recovery flow. If the
1012+
* recovery flow (or any other recovery flow running simultaneously)
1013+
* has recovered an EQE, it should cause the entry to be completed by
1014+
* the command interface.
1015+
*/
1016+
if (wait_for_completion_timeout(&ent->done, timeout)) {
1017+
mlx5_core_warn(dev, "cmd[%d]: %s(0x%x) recovered after timeout\n", ent->idx,
1018+
mlx5_command_str(msg_to_opcode(ent->in)), msg_to_opcode(ent->in));
1019+
return;
1020+
}
1021+
1022+
mlx5_core_warn(dev, "cmd[%d]: %s(0x%x) No done completion\n", ent->idx,
1023+
mlx5_command_str(msg_to_opcode(ent->in)), msg_to_opcode(ent->in));
1024+
1025+
ent->ret = -ETIMEDOUT;
1026+
mlx5_cmd_comp_handler(dev, 1UL << ent->idx, true);
1027+
}
1028+
9901029
static int wait_func(struct mlx5_core_dev *dev, struct mlx5_cmd_work_ent *ent)
9911030
{
9921031
unsigned long timeout = msecs_to_jiffies(MLX5_CMD_TIMEOUT_MSEC);
@@ -998,12 +1037,10 @@ static int wait_func(struct mlx5_core_dev *dev, struct mlx5_cmd_work_ent *ent)
9981037
ent->ret = -ECANCELED;
9991038
goto out_err;
10001039
}
1001-
if (cmd->mode == CMD_MODE_POLLING || ent->polling) {
1040+
if (cmd->mode == CMD_MODE_POLLING || ent->polling)
10021041
wait_for_completion(&ent->done);
1003-
} else if (!wait_for_completion_timeout(&ent->done, timeout)) {
1004-
ent->ret = -ETIMEDOUT;
1005-
mlx5_cmd_comp_handler(dev, 1UL << ent->idx, true);
1006-
}
1042+
else if (!wait_for_completion_timeout(&ent->done, timeout))
1043+
wait_func_handle_exec_timeout(dev, ent);
10071044

10081045
out_err:
10091046
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
@@ -190,6 +190,29 @@ u32 mlx5_eq_poll_irq_disabled(struct mlx5_eq_comp *eq)
190190
return count_eqe;
191191
}
192192

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

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

231+
mlx5_eq_async_int_lock(eq_async, &flags);
232+
207233
eqe = next_eqe_sw(eq);
208234
if (!eqe)
209235
goto out;
@@ -224,8 +250,19 @@ static int mlx5_eq_async_int(struct notifier_block *nb,
224250

225251
out:
226252
eq_update_ci(eq, 1);
253+
mlx5_eq_async_int_unlock(eq_async, &flags);
227254

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

231268
static void init_eq_buf(struct mlx5_eq *eq)
@@ -570,6 +607,7 @@ setup_async_eq(struct mlx5_core_dev *dev, struct mlx5_eq_async *eq,
570607
int err;
571608

572609
eq->irq_nb.notifier_call = mlx5_eq_async_int;
610+
spin_lock_init(&eq->lock);
573611

574612
err = create_async_eq(dev, &eq->core, param);
575613
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
@@ -38,6 +38,7 @@ struct mlx5_eq {
3838
struct mlx5_eq_async {
3939
struct mlx5_eq core;
4040
struct notifier_block irq_nb;
41+
spinlock_t lock; /* To avoid irq EQ handle races with resiliency flows */
4142
};
4243

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

8485
u32 mlx5_eq_poll_irq_disabled(struct mlx5_eq_comp *eq);
86+
void mlx5_cmd_eq_recover(struct mlx5_core_dev *dev);
8587
void mlx5_eq_synchronize_async_irq(struct mlx5_core_dev *dev);
8688
void mlx5_eq_synchronize_cmd_irq(struct mlx5_core_dev *dev);
8789

0 commit comments

Comments
 (0)