]> asedeno.scripts.mit.edu Git - linux.git/blob - drivers/net/ethernet/mellanox/mlx5/core/en/reporter_tx.c
Merge git://git.kernel.org/pub/scm/linux/kernel/git/netdev/net
[linux.git] / drivers / net / ethernet / mellanox / mlx5 / core / en / reporter_tx.c
1 /* SPDX-License-Identifier: GPL-2.0 */
2 /* Copyright (c) 2019 Mellanox Technologies. */
3
4 #include <net/devlink.h>
5 #include "reporter.h"
6 #include "lib/eq.h"
7
8 #define MLX5E_TX_REPORTER_PER_SQ_MAX_LEN 256
9
10 struct mlx5e_tx_err_ctx {
11         int (*recover)(struct mlx5e_txqsq *sq);
12         struct mlx5e_txqsq *sq;
13 };
14
15 static int mlx5e_wait_for_sq_flush(struct mlx5e_txqsq *sq)
16 {
17         unsigned long exp_time = jiffies + msecs_to_jiffies(2000);
18
19         while (time_before(jiffies, exp_time)) {
20                 if (sq->cc == sq->pc)
21                         return 0;
22
23                 msleep(20);
24         }
25
26         netdev_err(sq->channel->netdev,
27                    "Wait for SQ 0x%x flush timeout (sq cc = 0x%x, sq pc = 0x%x)\n",
28                    sq->sqn, sq->cc, sq->pc);
29
30         return -ETIMEDOUT;
31 }
32
33 static void mlx5e_reset_txqsq_cc_pc(struct mlx5e_txqsq *sq)
34 {
35         WARN_ONCE(sq->cc != sq->pc,
36                   "SQ 0x%x: cc (0x%x) != pc (0x%x)\n",
37                   sq->sqn, sq->cc, sq->pc);
38         sq->cc = 0;
39         sq->dma_fifo_cc = 0;
40         sq->pc = 0;
41 }
42
43 static int mlx5e_sq_to_ready(struct mlx5e_txqsq *sq, int curr_state)
44 {
45         struct mlx5_core_dev *mdev = sq->channel->mdev;
46         struct net_device *dev = sq->channel->netdev;
47         struct mlx5e_modify_sq_param msp = {0};
48         int err;
49
50         msp.curr_state = curr_state;
51         msp.next_state = MLX5_SQC_STATE_RST;
52
53         err = mlx5e_modify_sq(mdev, sq->sqn, &msp);
54         if (err) {
55                 netdev_err(dev, "Failed to move sq 0x%x to reset\n", sq->sqn);
56                 return err;
57         }
58
59         memset(&msp, 0, sizeof(msp));
60         msp.curr_state = MLX5_SQC_STATE_RST;
61         msp.next_state = MLX5_SQC_STATE_RDY;
62
63         err = mlx5e_modify_sq(mdev, sq->sqn, &msp);
64         if (err) {
65                 netdev_err(dev, "Failed to move sq 0x%x to ready\n", sq->sqn);
66                 return err;
67         }
68
69         return 0;
70 }
71
72 static int mlx5e_tx_reporter_err_cqe_recover(struct mlx5e_txqsq *sq)
73 {
74         struct mlx5_core_dev *mdev = sq->channel->mdev;
75         struct net_device *dev = sq->channel->netdev;
76         u8 state;
77         int err;
78
79         err = mlx5_core_query_sq_state(mdev, sq->sqn, &state);
80         if (err) {
81                 netdev_err(dev, "Failed to query SQ 0x%x state. err = %d\n",
82                            sq->sqn, err);
83                 goto out;
84         }
85
86         if (state != MLX5_SQC_STATE_ERR)
87                 goto out;
88
89         mlx5e_tx_disable_queue(sq->txq);
90
91         err = mlx5e_wait_for_sq_flush(sq);
92         if (err)
93                 goto out;
94
95         /* At this point, no new packets will arrive from the stack as TXQ is
96          * marked with QUEUE_STATE_DRV_XOFF. In addition, NAPI cleared all
97          * pending WQEs. SQ can safely reset the SQ.
98          */
99
100         err = mlx5e_sq_to_ready(sq, state);
101         if (err)
102                 goto out;
103
104         mlx5e_reset_txqsq_cc_pc(sq);
105         sq->stats->recover++;
106         clear_bit(MLX5E_SQ_STATE_RECOVERING, &sq->state);
107         mlx5e_activate_txqsq(sq);
108
109         return 0;
110 out:
111         clear_bit(MLX5E_SQ_STATE_RECOVERING, &sq->state);
112         return err;
113 }
114
115 static int mlx5_tx_health_report(struct devlink_health_reporter *tx_reporter,
116                                  char *err_str,
117                                  struct mlx5e_tx_err_ctx *err_ctx)
118 {
119         if (IS_ERR_OR_NULL(tx_reporter)) {
120                 netdev_err(err_ctx->sq->channel->netdev, err_str);
121                 return err_ctx->recover(err_ctx->sq);
122         }
123
124         return devlink_health_report(tx_reporter, err_str, err_ctx);
125 }
126
127 void mlx5e_tx_reporter_err_cqe(struct mlx5e_txqsq *sq)
128 {
129         char err_str[MLX5E_TX_REPORTER_PER_SQ_MAX_LEN];
130         struct mlx5e_tx_err_ctx err_ctx = {0};
131
132         err_ctx.sq       = sq;
133         err_ctx.recover  = mlx5e_tx_reporter_err_cqe_recover;
134         sprintf(err_str, "ERR CQE on SQ: 0x%x", sq->sqn);
135
136         mlx5_tx_health_report(sq->channel->priv->tx_reporter, err_str,
137                               &err_ctx);
138 }
139
140 static int mlx5e_tx_reporter_timeout_recover(struct mlx5e_txqsq *sq)
141 {
142         struct mlx5_eq_comp *eq = sq->cq.mcq.eq;
143         u32 eqe_count;
144
145         netdev_err(sq->channel->netdev, "EQ 0x%x: Cons = 0x%x, irqn = 0x%x\n",
146                    eq->core.eqn, eq->core.cons_index, eq->core.irqn);
147
148         eqe_count = mlx5_eq_poll_irq_disabled(eq);
149         if (!eqe_count) {
150                 clear_bit(MLX5E_SQ_STATE_ENABLED, &sq->state);
151                 return -EIO;
152         }
153
154         netdev_err(sq->channel->netdev, "Recover %d eqes on EQ 0x%x\n",
155                    eqe_count, eq->core.eqn);
156         sq->channel->stats->eq_rearm++;
157         return 0;
158 }
159
160 int mlx5e_tx_reporter_timeout(struct mlx5e_txqsq *sq)
161 {
162         char err_str[MLX5E_TX_REPORTER_PER_SQ_MAX_LEN];
163         struct mlx5e_tx_err_ctx err_ctx;
164
165         err_ctx.sq       = sq;
166         err_ctx.recover  = mlx5e_tx_reporter_timeout_recover;
167         sprintf(err_str,
168                 "TX timeout on queue: %d, SQ: 0x%x, CQ: 0x%x, SQ Cons: 0x%x SQ Prod: 0x%x, usecs since last trans: %u\n",
169                 sq->channel->ix, sq->sqn, sq->cq.mcq.cqn, sq->cc, sq->pc,
170                 jiffies_to_usecs(jiffies - sq->txq->trans_start));
171
172         return mlx5_tx_health_report(sq->channel->priv->tx_reporter, err_str,
173                                      &err_ctx);
174 }
175
176 /* state lock cannot be grabbed within this function.
177  * It can cause a dead lock or a read-after-free.
178  */
179 static int mlx5e_tx_reporter_recover_from_ctx(struct mlx5e_tx_err_ctx *err_ctx)
180 {
181         return err_ctx->recover(err_ctx->sq);
182 }
183
184 static int mlx5e_tx_reporter_recover_all(struct mlx5e_priv *priv)
185 {
186         int err = 0;
187
188         rtnl_lock();
189         mutex_lock(&priv->state_lock);
190
191         if (!test_bit(MLX5E_STATE_OPENED, &priv->state))
192                 goto out;
193
194         err = mlx5e_safe_reopen_channels(priv);
195
196 out:
197         mutex_unlock(&priv->state_lock);
198         rtnl_unlock();
199
200         return err;
201 }
202
203 static int mlx5e_tx_reporter_recover(struct devlink_health_reporter *reporter,
204                                      void *context)
205 {
206         struct mlx5e_priv *priv = devlink_health_reporter_priv(reporter);
207         struct mlx5e_tx_err_ctx *err_ctx = context;
208
209         return err_ctx ? mlx5e_tx_reporter_recover_from_ctx(err_ctx) :
210                          mlx5e_tx_reporter_recover_all(priv);
211 }
212
213 static int
214 mlx5e_tx_reporter_build_diagnose_output(struct devlink_fmsg *fmsg,
215                                         u32 sqn, u8 state, bool stopped)
216 {
217         int err;
218
219         err = devlink_fmsg_obj_nest_start(fmsg);
220         if (err)
221                 return err;
222
223         err = devlink_fmsg_u32_pair_put(fmsg, "sqn", sqn);
224         if (err)
225                 return err;
226
227         err = devlink_fmsg_u8_pair_put(fmsg, "HW state", state);
228         if (err)
229                 return err;
230
231         err = devlink_fmsg_bool_pair_put(fmsg, "stopped", stopped);
232         if (err)
233                 return err;
234
235         err = devlink_fmsg_obj_nest_end(fmsg);
236         if (err)
237                 return err;
238
239         return 0;
240 }
241
242 static int mlx5e_tx_reporter_diagnose(struct devlink_health_reporter *reporter,
243                                       struct devlink_fmsg *fmsg)
244 {
245         struct mlx5e_priv *priv = devlink_health_reporter_priv(reporter);
246         int i, err = 0;
247
248         mutex_lock(&priv->state_lock);
249
250         if (!test_bit(MLX5E_STATE_OPENED, &priv->state))
251                 goto unlock;
252
253         err = devlink_fmsg_arr_pair_nest_start(fmsg, "SQs");
254         if (err)
255                 goto unlock;
256
257         for (i = 0; i < priv->channels.num * priv->channels.params.num_tc;
258              i++) {
259                 struct mlx5e_txqsq *sq = priv->txq2sq[i];
260                 u8 state;
261
262                 err = mlx5_core_query_sq_state(priv->mdev, sq->sqn, &state);
263                 if (err)
264                         goto unlock;
265
266                 err = mlx5e_tx_reporter_build_diagnose_output(fmsg, sq->sqn,
267                                                               state,
268                                                               netif_xmit_stopped(sq->txq));
269                 if (err)
270                         goto unlock;
271         }
272         err = devlink_fmsg_arr_pair_nest_end(fmsg);
273         if (err)
274                 goto unlock;
275
276 unlock:
277         mutex_unlock(&priv->state_lock);
278         return err;
279 }
280
281 static const struct devlink_health_reporter_ops mlx5_tx_reporter_ops = {
282                 .name = "tx",
283                 .recover = mlx5e_tx_reporter_recover,
284                 .diagnose = mlx5e_tx_reporter_diagnose,
285 };
286
287 #define MLX5_REPORTER_TX_GRACEFUL_PERIOD 500
288
289 int mlx5e_tx_reporter_create(struct mlx5e_priv *priv)
290 {
291         struct mlx5_core_dev *mdev = priv->mdev;
292         struct devlink *devlink = priv_to_devlink(mdev);
293
294         priv->tx_reporter =
295                 devlink_health_reporter_create(devlink, &mlx5_tx_reporter_ops,
296                                                MLX5_REPORTER_TX_GRACEFUL_PERIOD,
297                                                true, priv);
298         if (IS_ERR(priv->tx_reporter))
299                 netdev_warn(priv->netdev,
300                             "Failed to create tx reporter, err = %ld\n",
301                             PTR_ERR(priv->tx_reporter));
302         return IS_ERR_OR_NULL(priv->tx_reporter);
303 }
304
305 void mlx5e_tx_reporter_destroy(struct mlx5e_priv *priv)
306 {
307         if (IS_ERR_OR_NULL(priv->tx_reporter))
308                 return;
309
310         devlink_health_reporter_destroy(priv->tx_reporter);
311 }