| From foo@baz Thu 28 May 2020 01:21:48 PM CEST |
| From: Moshe Shemesh <moshe@mellanox.com> |
| Date: Fri, 27 Dec 2019 07:01:53 +0200 |
| Subject: net/mlx5: Add command entry handling completion |
| |
| From: Moshe Shemesh <moshe@mellanox.com> |
| |
| [ Upstream commit 17d00e839d3b592da9659c1977d45f85b77f986a ] |
| |
| When FW response to commands is very slow and all command entries in |
| use are waiting for completion we can have a race where commands can get |
| timeout before they get out of the queue and handled. Timeout |
| completion on uninitialized command will cause releasing command's |
| buffers before accessing it for initialization and then we will get NULL |
| pointer exception while trying access it. It may also cause releasing |
| buffers of another command since we may have timeout completion before |
| even allocating entry index for this command. |
| Add entry handling completion to avoid this race. |
| |
| Fixes: e126ba97dba9 ("mlx5: Add driver for Mellanox Connect-IB adapters") |
| Signed-off-by: Moshe Shemesh <moshe@mellanox.com> |
| Signed-off-by: Eran Ben Elisha <eranbe@mellanox.com> |
| Signed-off-by: Saeed Mahameed <saeedm@mellanox.com> |
| Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> |
| --- |
| drivers/net/ethernet/mellanox/mlx5/core/cmd.c | 14 ++++++++++++++ |
| include/linux/mlx5/driver.h | 1 + |
| 2 files changed, 15 insertions(+) |
| |
| --- a/drivers/net/ethernet/mellanox/mlx5/core/cmd.c |
| +++ b/drivers/net/ethernet/mellanox/mlx5/core/cmd.c |
| @@ -835,6 +835,7 @@ static void cmd_work_handler(struct work |
| int alloc_ret; |
| int cmd_mode; |
| |
| + complete(&ent->handling); |
| sem = ent->page_queue ? &cmd->pages_sem : &cmd->sem; |
| down(sem); |
| if (!ent->page_queue) { |
| @@ -953,6 +954,11 @@ static int wait_func(struct mlx5_core_de |
| struct mlx5_cmd *cmd = &dev->cmd; |
| int err; |
| |
| + if (!wait_for_completion_timeout(&ent->handling, timeout) && |
| + cancel_work_sync(&ent->work)) { |
| + ent->ret = -ECANCELED; |
| + goto out_err; |
| + } |
| if (cmd->mode == CMD_MODE_POLLING || ent->polling) { |
| wait_for_completion(&ent->done); |
| } else if (!wait_for_completion_timeout(&ent->done, timeout)) { |
| @@ -960,12 +966,17 @@ static int wait_func(struct mlx5_core_de |
| mlx5_cmd_comp_handler(dev, 1UL << ent->idx, true); |
| } |
| |
| +out_err: |
| err = ent->ret; |
| |
| if (err == -ETIMEDOUT) { |
| mlx5_core_warn(dev, "%s(0x%x) timeout. Will cause a leak of a command resource\n", |
| mlx5_command_str(msg_to_opcode(ent->in)), |
| msg_to_opcode(ent->in)); |
| + } else if (err == -ECANCELED) { |
| + mlx5_core_warn(dev, "%s(0x%x) canceled on out of queue timeout.\n", |
| + mlx5_command_str(msg_to_opcode(ent->in)), |
| + msg_to_opcode(ent->in)); |
| } |
| mlx5_core_dbg(dev, "err %d, delivery status %s(%d)\n", |
| err, deliv_status_to_str(ent->status), ent->status); |
| @@ -1001,6 +1012,7 @@ static int mlx5_cmd_invoke(struct mlx5_c |
| ent->token = token; |
| ent->polling = force_polling; |
| |
| + init_completion(&ent->handling); |
| if (!callback) |
| init_completion(&ent->done); |
| |
| @@ -1020,6 +1032,8 @@ static int mlx5_cmd_invoke(struct mlx5_c |
| err = wait_func(dev, ent); |
| if (err == -ETIMEDOUT) |
| goto out; |
| + if (err == -ECANCELED) |
| + goto out_free; |
| |
| ds = ent->ts2 - ent->ts1; |
| op = MLX5_GET(mbox_in, in->first.data, opcode); |
| --- a/include/linux/mlx5/driver.h |
| +++ b/include/linux/mlx5/driver.h |
| @@ -902,6 +902,7 @@ struct mlx5_cmd_work_ent { |
| struct delayed_work cb_timeout_work; |
| void *context; |
| int idx; |
| + struct completion handling; |
| struct completion done; |
| struct mlx5_cmd *cmd; |
| struct work_struct work; |