1c50de4afSAya Levin // SPDX-License-Identifier: GPL-2.0
2c50de4afSAya Levin // Copyright (c) 2019 Mellanox Technologies.
3c50de4afSAya Levin 
4c50de4afSAya Levin #include "health.h"
5c50de4afSAya Levin #include "lib/eq.h"
65f29458bSAya Levin #include "lib/mlx5.h"
7c50de4afSAya Levin 
82d708887SAya Levin int mlx5e_reporter_named_obj_nest_start(struct devlink_fmsg *fmsg, char *name)
92d708887SAya Levin {
102d708887SAya Levin 	int err;
112d708887SAya Levin 
122d708887SAya Levin 	err = devlink_fmsg_pair_nest_start(fmsg, name);
132d708887SAya Levin 	if (err)
142d708887SAya Levin 		return err;
152d708887SAya Levin 
162d708887SAya Levin 	err = devlink_fmsg_obj_nest_start(fmsg);
172d708887SAya Levin 	if (err)
182d708887SAya Levin 		return err;
192d708887SAya Levin 
202d708887SAya Levin 	return 0;
212d708887SAya Levin }
222d708887SAya Levin 
232d708887SAya Levin int mlx5e_reporter_named_obj_nest_end(struct devlink_fmsg *fmsg)
242d708887SAya Levin {
252d708887SAya Levin 	int err;
262d708887SAya Levin 
272d708887SAya Levin 	err = devlink_fmsg_obj_nest_end(fmsg);
282d708887SAya Levin 	if (err)
292d708887SAya Levin 		return err;
302d708887SAya Levin 
312d708887SAya Levin 	err = devlink_fmsg_pair_nest_end(fmsg);
322d708887SAya Levin 	if (err)
332d708887SAya Levin 		return err;
342d708887SAya Levin 
352d708887SAya Levin 	return 0;
362d708887SAya Levin }
372d708887SAya Levin 
382bf09e60SAya Levin int mlx5e_reporter_cq_diagnose(struct mlx5e_cq *cq, struct devlink_fmsg *fmsg)
392bf09e60SAya Levin {
402bf09e60SAya Levin 	struct mlx5e_priv *priv = cq->channel->priv;
412bf09e60SAya Levin 	u32 out[MLX5_ST_SZ_DW(query_cq_out)] = {};
422bf09e60SAya Levin 	u8 hw_status;
432bf09e60SAya Levin 	void *cqc;
442bf09e60SAya Levin 	int err;
452bf09e60SAya Levin 
462bf09e60SAya Levin 	err = mlx5_core_query_cq(priv->mdev, &cq->mcq, out, sizeof(out));
472bf09e60SAya Levin 	if (err)
482bf09e60SAya Levin 		return err;
492bf09e60SAya Levin 
502bf09e60SAya Levin 	cqc = MLX5_ADDR_OF(query_cq_out, out, cq_context);
512bf09e60SAya Levin 	hw_status = MLX5_GET(cqc, cqc, status);
522bf09e60SAya Levin 
532bf09e60SAya Levin 	err = mlx5e_reporter_named_obj_nest_start(fmsg, "CQ");
542bf09e60SAya Levin 	if (err)
552bf09e60SAya Levin 		return err;
562bf09e60SAya Levin 
572bf09e60SAya Levin 	err = devlink_fmsg_u32_pair_put(fmsg, "cqn", cq->mcq.cqn);
582bf09e60SAya Levin 	if (err)
592bf09e60SAya Levin 		return err;
602bf09e60SAya Levin 
612bf09e60SAya Levin 	err = devlink_fmsg_u8_pair_put(fmsg, "HW status", hw_status);
622bf09e60SAya Levin 	if (err)
632bf09e60SAya Levin 		return err;
642bf09e60SAya Levin 
652bf09e60SAya Levin 	err = mlx5e_reporter_named_obj_nest_end(fmsg);
662bf09e60SAya Levin 	if (err)
672bf09e60SAya Levin 		return err;
682bf09e60SAya Levin 
692bf09e60SAya Levin 	return 0;
702bf09e60SAya Levin }
712bf09e60SAya Levin 
722bf09e60SAya Levin int mlx5e_reporter_cq_common_diagnose(struct mlx5e_cq *cq, struct devlink_fmsg *fmsg)
732bf09e60SAya Levin {
742bf09e60SAya Levin 	u8 cq_log_stride;
752bf09e60SAya Levin 	u32 cq_sz;
762bf09e60SAya Levin 	int err;
772bf09e60SAya Levin 
782bf09e60SAya Levin 	cq_sz = mlx5_cqwq_get_size(&cq->wq);
792bf09e60SAya Levin 	cq_log_stride = mlx5_cqwq_get_log_stride_size(&cq->wq);
802bf09e60SAya Levin 
812bf09e60SAya Levin 	err = mlx5e_reporter_named_obj_nest_start(fmsg, "CQ");
822bf09e60SAya Levin 	if (err)
832bf09e60SAya Levin 		return err;
842bf09e60SAya Levin 
852bf09e60SAya Levin 	err = devlink_fmsg_u64_pair_put(fmsg, "stride size", BIT(cq_log_stride));
862bf09e60SAya Levin 	if (err)
872bf09e60SAya Levin 		return err;
882bf09e60SAya Levin 
892bf09e60SAya Levin 	err = devlink_fmsg_u32_pair_put(fmsg, "size", cq_sz);
902bf09e60SAya Levin 	if (err)
912bf09e60SAya Levin 		return err;
922bf09e60SAya Levin 
932bf09e60SAya Levin 	err = mlx5e_reporter_named_obj_nest_end(fmsg);
942bf09e60SAya Levin 	if (err)
952bf09e60SAya Levin 		return err;
962bf09e60SAya Levin 
972bf09e60SAya Levin 	return 0;
982bf09e60SAya Levin }
992bf09e60SAya Levin 
10011af6a6dSAya Levin int mlx5e_health_create_reporters(struct mlx5e_priv *priv)
10111af6a6dSAya Levin {
1029032e719SAya Levin 	int err;
1039032e719SAya Levin 
1049032e719SAya Levin 	err = mlx5e_reporter_tx_create(priv);
1059032e719SAya Levin 	if (err)
1069032e719SAya Levin 		return err;
1079032e719SAya Levin 
1089032e719SAya Levin 	err = mlx5e_reporter_rx_create(priv);
1099032e719SAya Levin 	if (err)
1109032e719SAya Levin 		return err;
1119032e719SAya Levin 
1129032e719SAya Levin 	return 0;
11311af6a6dSAya Levin }
11411af6a6dSAya Levin 
11511af6a6dSAya Levin void mlx5e_health_destroy_reporters(struct mlx5e_priv *priv)
11611af6a6dSAya Levin {
1179032e719SAya Levin 	mlx5e_reporter_rx_destroy(priv);
11811af6a6dSAya Levin 	mlx5e_reporter_tx_destroy(priv);
11911af6a6dSAya Levin }
12011af6a6dSAya Levin 
12111af6a6dSAya Levin void mlx5e_health_channels_update(struct mlx5e_priv *priv)
12211af6a6dSAya Levin {
12311af6a6dSAya Levin 	if (priv->tx_reporter)
12411af6a6dSAya Levin 		devlink_health_reporter_state_update(priv->tx_reporter,
12511af6a6dSAya Levin 						     DEVLINK_HEALTH_REPORTER_STATE_HEALTHY);
1269032e719SAya Levin 	if (priv->rx_reporter)
1279032e719SAya Levin 		devlink_health_reporter_state_update(priv->rx_reporter,
1289032e719SAya Levin 						     DEVLINK_HEALTH_REPORTER_STATE_HEALTHY);
12911af6a6dSAya Levin }
13011af6a6dSAya Levin 
131c50de4afSAya Levin int mlx5e_health_sq_to_ready(struct mlx5e_channel *channel, u32 sqn)
132c50de4afSAya Levin {
133c50de4afSAya Levin 	struct mlx5_core_dev *mdev = channel->mdev;
134c50de4afSAya Levin 	struct net_device *dev = channel->netdev;
135c50de4afSAya Levin 	struct mlx5e_modify_sq_param msp = {};
136c50de4afSAya Levin 	int err;
137c50de4afSAya Levin 
138c50de4afSAya Levin 	msp.curr_state = MLX5_SQC_STATE_ERR;
139c50de4afSAya Levin 	msp.next_state = MLX5_SQC_STATE_RST;
140c50de4afSAya Levin 
141c50de4afSAya Levin 	err = mlx5e_modify_sq(mdev, sqn, &msp);
142c50de4afSAya Levin 	if (err) {
143c50de4afSAya Levin 		netdev_err(dev, "Failed to move sq 0x%x to reset\n", sqn);
144c50de4afSAya Levin 		return err;
145c50de4afSAya Levin 	}
146c50de4afSAya Levin 
147c50de4afSAya Levin 	memset(&msp, 0, sizeof(msp));
148c50de4afSAya Levin 	msp.curr_state = MLX5_SQC_STATE_RST;
149c50de4afSAya Levin 	msp.next_state = MLX5_SQC_STATE_RDY;
150c50de4afSAya Levin 
151c50de4afSAya Levin 	err = mlx5e_modify_sq(mdev, sqn, &msp);
152c50de4afSAya Levin 	if (err) {
153c50de4afSAya Levin 		netdev_err(dev, "Failed to move sq 0x%x to ready\n", sqn);
154c50de4afSAya Levin 		return err;
155c50de4afSAya Levin 	}
156c50de4afSAya Levin 
157c50de4afSAya Levin 	return 0;
158c50de4afSAya Levin }
159c50de4afSAya Levin 
160c50de4afSAya Levin int mlx5e_health_recover_channels(struct mlx5e_priv *priv)
161c50de4afSAya Levin {
162c50de4afSAya Levin 	int err = 0;
163c50de4afSAya Levin 
164c50de4afSAya Levin 	rtnl_lock();
165c50de4afSAya Levin 	mutex_lock(&priv->state_lock);
166c50de4afSAya Levin 
167c50de4afSAya Levin 	if (!test_bit(MLX5E_STATE_OPENED, &priv->state))
168c50de4afSAya Levin 		goto out;
169c50de4afSAya Levin 
170c50de4afSAya Levin 	err = mlx5e_safe_reopen_channels(priv);
171c50de4afSAya Levin 
172c50de4afSAya Levin out:
173c50de4afSAya Levin 	mutex_unlock(&priv->state_lock);
174c50de4afSAya Levin 	rtnl_unlock();
175c50de4afSAya Levin 
176c50de4afSAya Levin 	return err;
177c50de4afSAya Levin }
178c50de4afSAya Levin 
179c50de4afSAya Levin int mlx5e_health_channel_eq_recover(struct mlx5_eq_comp *eq, struct mlx5e_channel *channel)
180c50de4afSAya Levin {
181c50de4afSAya Levin 	u32 eqe_count;
182c50de4afSAya Levin 
183c50de4afSAya Levin 	netdev_err(channel->netdev, "EQ 0x%x: Cons = 0x%x, irqn = 0x%x\n",
184c50de4afSAya Levin 		   eq->core.eqn, eq->core.cons_index, eq->core.irqn);
185c50de4afSAya Levin 
186c50de4afSAya Levin 	eqe_count = mlx5_eq_poll_irq_disabled(eq);
187c50de4afSAya Levin 	if (!eqe_count)
188c50de4afSAya Levin 		return -EIO;
189c50de4afSAya Levin 
190c50de4afSAya Levin 	netdev_err(channel->netdev, "Recovered %d eqes on EQ 0x%x\n",
191c50de4afSAya Levin 		   eqe_count, eq->core.eqn);
192c50de4afSAya Levin 
193c50de4afSAya Levin 	channel->stats->eq_rearm++;
194c50de4afSAya Levin 	return 0;
195c50de4afSAya Levin }
196c50de4afSAya Levin 
197c50de4afSAya Levin int mlx5e_health_report(struct mlx5e_priv *priv,
198c50de4afSAya Levin 			struct devlink_health_reporter *reporter, char *err_str,
199c50de4afSAya Levin 			struct mlx5e_err_ctx *err_ctx)
200c50de4afSAya Levin {
201c50de4afSAya Levin 	netdev_err(priv->netdev, err_str);
20299cda454SEran Ben Elisha 
20399cda454SEran Ben Elisha 	if (!reporter)
204c50de4afSAya Levin 		return err_ctx->recover(&err_ctx->ctx);
20599cda454SEran Ben Elisha 
206c50de4afSAya Levin 	return devlink_health_report(reporter, err_str, err_ctx);
207c50de4afSAya Levin }
2085f29458bSAya Levin 
2095f29458bSAya Levin #define MLX5_HEALTH_DEVLINK_MAX_SIZE 1024
2105f29458bSAya Levin static int mlx5e_health_rsc_fmsg_binary(struct devlink_fmsg *fmsg,
2115f29458bSAya Levin 					const void *value, u32 value_len)
2125f29458bSAya Levin 
2135f29458bSAya Levin {
2145f29458bSAya Levin 	u32 data_size;
2155f29458bSAya Levin 	u32 offset;
2165f29458bSAya Levin 	int err;
2175f29458bSAya Levin 
2185f29458bSAya Levin 	for (offset = 0; offset < value_len; offset += data_size) {
2195f29458bSAya Levin 		data_size = value_len - offset;
2205f29458bSAya Levin 		if (data_size > MLX5_HEALTH_DEVLINK_MAX_SIZE)
2215f29458bSAya Levin 			data_size = MLX5_HEALTH_DEVLINK_MAX_SIZE;
2225f29458bSAya Levin 		err = devlink_fmsg_binary_put(fmsg, value + offset, data_size);
2235f29458bSAya Levin 		if (err)
2245f29458bSAya Levin 			break;
2255f29458bSAya Levin 	}
2265f29458bSAya Levin 	return err;
2275f29458bSAya Levin }
2285f29458bSAya Levin 
2295f29458bSAya Levin int mlx5e_health_rsc_fmsg_dump(struct mlx5e_priv *priv, struct mlx5_rsc_key *key,
2305f29458bSAya Levin 			       struct devlink_fmsg *fmsg)
2315f29458bSAya Levin {
2325f29458bSAya Levin 	struct mlx5_core_dev *mdev = priv->mdev;
2335f29458bSAya Levin 	struct mlx5_rsc_dump_cmd *cmd;
2345f29458bSAya Levin 	struct page *page;
2355f29458bSAya Levin 	int cmd_err, err;
2365f29458bSAya Levin 	int end_err;
2375f29458bSAya Levin 	int size;
2385f29458bSAya Levin 
2395f29458bSAya Levin 	if (IS_ERR_OR_NULL(mdev->rsc_dump))
2405f29458bSAya Levin 		return -EOPNOTSUPP;
2415f29458bSAya Levin 
2425f29458bSAya Levin 	page = alloc_page(GFP_KERNEL);
2435f29458bSAya Levin 	if (!page)
2445f29458bSAya Levin 		return -ENOMEM;
2455f29458bSAya Levin 
2465f29458bSAya Levin 	err = devlink_fmsg_binary_pair_nest_start(fmsg, "data");
2475f29458bSAya Levin 	if (err)
2485f29458bSAya Levin 		return err;
2495f29458bSAya Levin 
2505f29458bSAya Levin 	cmd = mlx5_rsc_dump_cmd_create(mdev, key);
2515f29458bSAya Levin 	if (IS_ERR(cmd)) {
2525f29458bSAya Levin 		err = PTR_ERR(cmd);
2535f29458bSAya Levin 		goto free_page;
2545f29458bSAya Levin 	}
2555f29458bSAya Levin 
2565f29458bSAya Levin 	do {
2575f29458bSAya Levin 		cmd_err = mlx5_rsc_dump_next(mdev, cmd, page, &size);
2585f29458bSAya Levin 		if (cmd_err < 0) {
2595f29458bSAya Levin 			err = cmd_err;
2605f29458bSAya Levin 			goto destroy_cmd;
2615f29458bSAya Levin 		}
2625f29458bSAya Levin 
2635f29458bSAya Levin 		err = mlx5e_health_rsc_fmsg_binary(fmsg, page_address(page), size);
2645f29458bSAya Levin 		if (err)
2655f29458bSAya Levin 			goto destroy_cmd;
2665f29458bSAya Levin 
2675f29458bSAya Levin 	} while (cmd_err > 0);
2685f29458bSAya Levin 
2695f29458bSAya Levin destroy_cmd:
2705f29458bSAya Levin 	mlx5_rsc_dump_cmd_destroy(cmd);
2715f29458bSAya Levin 	end_err = devlink_fmsg_binary_pair_nest_end(fmsg);
2725f29458bSAya Levin 	if (end_err)
2735f29458bSAya Levin 		err = end_err;
2745f29458bSAya Levin free_page:
2755f29458bSAya Levin 	__free_page(page);
2765f29458bSAya Levin 	return err;
2775f29458bSAya Levin }
2785f29458bSAya Levin 
2795f29458bSAya Levin int mlx5e_health_queue_dump(struct mlx5e_priv *priv, struct devlink_fmsg *fmsg,
2805f29458bSAya Levin 			    int queue_idx, char *lbl)
2815f29458bSAya Levin {
2825f29458bSAya Levin 	struct mlx5_rsc_key key = {};
2835f29458bSAya Levin 	int err;
2845f29458bSAya Levin 
2855f29458bSAya Levin 	key.rsc = MLX5_SGMT_TYPE_FULL_QPC;
2865f29458bSAya Levin 	key.index1 = queue_idx;
2875f29458bSAya Levin 	key.size = PAGE_SIZE;
2885f29458bSAya Levin 	key.num_of_obj1 = 1;
2895f29458bSAya Levin 
2905f29458bSAya Levin 	err = devlink_fmsg_obj_nest_start(fmsg);
2915f29458bSAya Levin 	if (err)
2925f29458bSAya Levin 		return err;
2935f29458bSAya Levin 
2945f29458bSAya Levin 	err = mlx5e_reporter_named_obj_nest_start(fmsg, lbl);
2955f29458bSAya Levin 	if (err)
2965f29458bSAya Levin 		return err;
2975f29458bSAya Levin 
2985f29458bSAya Levin 	err = devlink_fmsg_u32_pair_put(fmsg, "index", queue_idx);
2995f29458bSAya Levin 	if (err)
3005f29458bSAya Levin 		return err;
3015f29458bSAya Levin 
3025f29458bSAya Levin 	err = mlx5e_health_rsc_fmsg_dump(priv, &key, fmsg);
3035f29458bSAya Levin 	if (err)
3045f29458bSAya Levin 		return err;
3055f29458bSAya Levin 
3065f29458bSAya Levin 	err = mlx5e_reporter_named_obj_nest_end(fmsg);
3075f29458bSAya Levin 	if (err)
3085f29458bSAya Levin 		return err;
3095f29458bSAya Levin 
3105f29458bSAya Levin 	return devlink_fmsg_obj_nest_end(fmsg);
3115f29458bSAya Levin }
312