net/mlx5: Avoid using pending command interface slots
authorMohamad Haj Yahia <mohamad@mellanox.com>
Thu, 23 Feb 2017 09:19:36 +0000 (11:19 +0200)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Wed, 7 Jun 2017 10:07:45 +0000 (12:07 +0200)
[ Upstream commit 73dd3a4839c1d27c36d4dcc92e1ff44225ecbeb7 ]

Currently when firmware command gets stuck or it takes long time to
complete, the driver command will get timeout and the command slot is
freed and can be used for new commands, and if the firmware receive new
command on the old busy slot its behavior is unexpected and this could
be harmful.
To fix this when the driver command gets timeout we return failure,
but we don't free the command slot and we wait for the firmware to
explicitly respond to that command.
Once all the entries are busy we will stop processing new firmware
commands.

Fixes: 9cba4ebcf374 ('net/mlx5: Fix potential deadlock in command mode change')
Signed-off-by: Mohamad Haj Yahia <mohamad@mellanox.com>
Cc: kernel-team@fb.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
drivers/net/ethernet/mellanox/mlx5/core/eq.c
drivers/net/ethernet/mellanox/mlx5/core/health.c
include/linux/mlx5/driver.h

index 3f51a44bde6b3fc710b185e9887f107f2ca41deb..cb45390c7623b430260ce92d5ff775e93261ee8b 100644 (file)
@@ -767,7 +767,7 @@ static void cb_timeout_handler(struct work_struct *work)
        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));
-       mlx5_cmd_comp_handler(dev, 1UL << ent->idx);
+       mlx5_cmd_comp_handler(dev, 1UL << ent->idx, true);
 }
 
 static void cmd_work_handler(struct work_struct *work)
@@ -797,6 +797,7 @@ static void cmd_work_handler(struct work_struct *work)
        }
 
        cmd->ent_arr[ent->idx] = ent;
+       set_bit(MLX5_CMD_ENT_STATE_PENDING_COMP, &ent->state);
        lay = get_inst(cmd, ent->idx);
        ent->lay = lay;
        memset(lay, 0, sizeof(*lay));
@@ -818,6 +819,20 @@ static void cmd_work_handler(struct work_struct *work)
        if (ent->callback)
                schedule_delayed_work(&ent->cb_timeout_work, cb_timeout);
 
+       /* Skip sending command to fw if internal error */
+       if (pci_channel_offline(dev->pdev) ||
+           dev->state == MLX5_DEVICE_STATE_INTERNAL_ERROR) {
+               u8 status = 0;
+               u32 drv_synd;
+
+               ent->ret = mlx5_internal_err_ret_value(dev, msg_to_opcode(ent->in), &drv_synd, &status);
+               MLX5_SET(mbox_out, ent->out, status, status);
+               MLX5_SET(mbox_out, ent->out, syndrome, drv_synd);
+
+               mlx5_cmd_comp_handler(dev, 1UL << ent->idx, true);
+               return;
+       }
+
        /* ring doorbell after the descriptor is valid */
        mlx5_core_dbg(dev, "writing 0x%x to command doorbell\n", 1 << ent->idx);
        wmb();
@@ -828,7 +843,7 @@ static void cmd_work_handler(struct work_struct *work)
                poll_timeout(ent);
                /* make sure we read the descriptor after ownership is SW */
                rmb();
-               mlx5_cmd_comp_handler(dev, 1UL << ent->idx);
+               mlx5_cmd_comp_handler(dev, 1UL << ent->idx, (ent->ret == -ETIMEDOUT));
        }
 }
 
@@ -872,7 +887,7 @@ static int wait_func(struct mlx5_core_dev *dev, struct mlx5_cmd_work_ent *ent)
                wait_for_completion(&ent->done);
        } else if (!wait_for_completion_timeout(&ent->done, timeout)) {
                ent->ret = -ETIMEDOUT;
-               mlx5_cmd_comp_handler(dev, 1UL << ent->idx);
+               mlx5_cmd_comp_handler(dev, 1UL << ent->idx, true);
        }
 
        err = ent->ret;
@@ -1369,7 +1384,7 @@ static void free_msg(struct mlx5_core_dev *dev, struct mlx5_cmd_msg *msg)
        }
 }
 
-void mlx5_cmd_comp_handler(struct mlx5_core_dev *dev, u64 vec)
+void mlx5_cmd_comp_handler(struct mlx5_core_dev *dev, u64 vec, bool forced)
 {
        struct mlx5_cmd *cmd = &dev->cmd;
        struct mlx5_cmd_work_ent *ent;
@@ -1389,6 +1404,19 @@ void mlx5_cmd_comp_handler(struct mlx5_core_dev *dev, u64 vec)
                        struct semaphore *sem;
 
                        ent = cmd->ent_arr[i];
+
+                       /* if we already completed the command, ignore it */
+                       if (!test_and_clear_bit(MLX5_CMD_ENT_STATE_PENDING_COMP,
+                                               &ent->state)) {
+                               /* only real completion can free the cmd slot */
+                               if (!forced) {
+                                       mlx5_core_err(dev, "Command completion arrived after timeout (entry idx = %d).\n",
+                                                     ent->idx);
+                                       free_ent(cmd, ent->idx);
+                               }
+                               continue;
+                       }
+
                        if (ent->callback)
                                cancel_delayed_work(&ent->cb_timeout_work);
                        if (ent->page_queue)
@@ -1411,7 +1439,10 @@ void mlx5_cmd_comp_handler(struct mlx5_core_dev *dev, u64 vec)
                                mlx5_core_dbg(dev, "command completed. ret 0x%x, delivery status %s(0x%x)\n",
                                              ent->ret, deliv_status_to_str(ent->status), ent->status);
                        }
-                       free_ent(cmd, ent->idx);
+
+                       /* only real completion will free the entry slot */
+                       if (!forced)
+                               free_ent(cmd, ent->idx);
 
                        if (ent->callback) {
                                ds = ent->ts2 - ent->ts1;
index aaca09002ca678c9794cb01ba2443f51578ecc29..f86e9ff995be0294bc4c8389fa4dcb4d67839d21 100644 (file)
@@ -234,7 +234,7 @@ static int mlx5_eq_int(struct mlx5_core_dev *dev, struct mlx5_eq *eq)
                        break;
 
                case MLX5_EVENT_TYPE_CMD:
-                       mlx5_cmd_comp_handler(dev, be32_to_cpu(eqe->data.cmd.vector));
+                       mlx5_cmd_comp_handler(dev, be32_to_cpu(eqe->data.cmd.vector), false);
                        break;
 
                case MLX5_EVENT_TYPE_PORT_CHANGE:
index 5bcf93422ee0b28337040138d026c2ab443642a9..2115c8aacc5b3a6a16f4e3431be1482e7bbe154f 100644 (file)
@@ -90,7 +90,7 @@ static void trigger_cmd_completions(struct mlx5_core_dev *dev)
        spin_unlock_irqrestore(&dev->cmd.alloc_lock, flags);
 
        mlx5_core_dbg(dev, "vector 0x%llx\n", vector);
-       mlx5_cmd_comp_handler(dev, vector);
+       mlx5_cmd_comp_handler(dev, vector, true);
        return;
 
 no_trig:
index ecc451d89ccd8c68edb8ab4051224d44b9568668..e1a903a5bb3ecfab8caeff3fe71b493a30a4fec6 100644 (file)
@@ -640,7 +640,12 @@ enum {
 
 typedef void (*mlx5_cmd_cbk_t)(int status, void *context);
 
+enum {
+       MLX5_CMD_ENT_STATE_PENDING_COMP,
+};
+
 struct mlx5_cmd_work_ent {
+       unsigned long           state;
        struct mlx5_cmd_msg    *in;
        struct mlx5_cmd_msg    *out;
        void                   *uout;
@@ -838,7 +843,7 @@ void mlx5_eq_pagefault(struct mlx5_core_dev *dev, struct mlx5_eqe *eqe);
 #endif
 void mlx5_srq_event(struct mlx5_core_dev *dev, u32 srqn, int event_type);
 struct mlx5_core_srq *mlx5_core_get_srq(struct mlx5_core_dev *dev, u32 srqn);
-void mlx5_cmd_comp_handler(struct mlx5_core_dev *dev, u64 vec);
+void mlx5_cmd_comp_handler(struct mlx5_core_dev *dev, u64 vec, bool forced);
 void mlx5_cq_event(struct mlx5_core_dev *dev, u32 cqn, int event_type);
 int mlx5_create_map_eq(struct mlx5_core_dev *dev, struct mlx5_eq *eq, u8 vecidx,
                       int nent, u64 mask, const char *name, struct mlx5_uar *uar);