| From: Alex Vesker <valex@mellanox.com> |
| Date: Tue, 12 Jun 2018 16:14:31 +0300 |
| Subject: net/mlx5: Fix command interface race in polling mode |
| |
| commit d412c31dae053bf30a1bc15582a9990df297a660 upstream. |
| |
| The command interface can work in two modes: Events and Polling. |
| In the general case, each time we invoke a command, a work is |
| queued to handle it. |
| |
| When working in events, the interrupt handler completes the |
| command execution. On the other hand, when working in polling |
| mode, the work itself completes it. |
| |
| Due to a bug in the work handler, a command could have been |
| completed by the interrupt handler, while the work handler |
| hasn't finished yet, causing the it to complete once again |
| if the command interface mode was changed from Events to |
| polling after the interrupt handler was called. |
| |
| mlx5_unload_one() |
| mlx5_stop_eqs() |
| // Destroy the EQ before cmd EQ |
| ...cmd_work_handler() |
| write_doorbell() |
| --> EVENT_TYPE_CMD |
| mlx5_cmd_comp_handler() // First free |
| free_ent(cmd, ent->idx) |
| complete(&ent->done) |
| |
| <-- mlx5_stop_eqs //cmd was complete |
| // move to polling before destroying the last cmd EQ |
| mlx5_cmd_use_polling() |
| cmd->mode = POLL; |
| |
| --> cmd_work_handler (continues) |
| if (cmd->mode == POLL) |
| mlx5_cmd_comp_handler() // Double free |
| |
| The solution is to store the cmd->mode before writing the doorbell. |
| |
| Fixes: e126ba97dba9 ("mlx5: Add driver for Mellanox Connect-IB adapters") |
| Signed-off-by: Alex Vesker <valex@mellanox.com> |
| Signed-off-by: Saeed Mahameed <saeedm@mellanox.com> |
| [bwh: Backported to 3.16: adjust context] |
| Signed-off-by: Ben Hutchings <ben@decadent.org.uk> |
| --- |
| drivers/net/ethernet/mellanox/mlx5/core/cmd.c | 4 +++- |
| 1 file changed, 3 insertions(+), 1 deletion(-) |
| |
| --- a/drivers/net/ethernet/mellanox/mlx5/core/cmd.c |
| +++ b/drivers/net/ethernet/mellanox/mlx5/core/cmd.c |
| @@ -560,6 +560,7 @@ static void cmd_work_handler(struct work |
| struct mlx5_cmd_layout *lay; |
| struct semaphore *sem; |
| int alloc_ret; |
| + int cmd_mode; |
| |
| sem = ent->page_queue ? &cmd->pages_sem : &cmd->sem; |
| down(sem); |
| @@ -602,6 +603,7 @@ static void cmd_work_handler(struct work |
| set_signature(ent, !cmd->checksum_disabled); |
| dump_command(dev, ent, 1); |
| ktime_get_ts(&ent->ts1); |
| + cmd_mode = cmd->mode; |
| |
| if (ent->callback) |
| schedule_delayed_work(&ent->cb_timeout_work, cb_timeout); |
| @@ -611,7 +613,7 @@ static void cmd_work_handler(struct work |
| iowrite32be(1 << ent->idx, &dev->iseg->cmd_dbell); |
| mlx5_core_dbg(dev, "write 0x%x to command doorbell\n", 1 << ent->idx); |
| mmiowb(); |
| - if (cmd->mode == CMD_MODE_POLLING) { |
| + if (cmd_mode == CMD_MODE_POLLING) { |
| poll_timeout(ent); |
| /* make sure we read the descriptor after ownership is SW */ |
| rmb(); |