File Index
Files and directories
Sign in
FILE

pcie_cong_event.c

File preview with syntax highlighting.

linux-6.19.14/drivers/net/ethernet/mellanox/mlx5/core/en/pcie_cong_event.c C
  1// SPDX-License-Identifier: GPL-2.0 OR Linux-OpenIB
  2// Copyright (c) 2025, NVIDIA CORPORATION & AFFILIATES.
  3
  4#include "../devlink.h"
  5#include "en.h"
  6#include "pcie_cong_event.h"
  7
  8#define MLX5E_CONG_HIGH_STATE 0x7
  9
 10enum {
 11	MLX5E_INBOUND_CONG  = BIT(0),
 12	MLX5E_OUTBOUND_CONG = BIT(1),
 13};
 14
 15struct mlx5e_pcie_cong_thresh {
 16	u16 inbound_high;
 17	u16 inbound_low;
 18	u16 outbound_high;
 19	u16 outbound_low;
 20};
 21
 22struct mlx5e_pcie_cong_stats {
 23	u32 pci_bw_inbound_high;
 24	u32 pci_bw_inbound_low;
 25	u32 pci_bw_outbound_high;
 26	u32 pci_bw_outbound_low;
 27	u32 pci_bw_stale_event;
 28};
 29
 30struct mlx5e_pcie_cong_event {
 31	u64 obj_id;
 32
 33	struct mlx5e_priv *priv;
 34
 35	/* For event notifier and workqueue. */
 36	struct work_struct work;
 37	struct mlx5_nb nb;
 38
 39	/* Stores last read state. */
 40	u8 state;
 41
 42	/* For ethtool stats group. */
 43	struct mlx5e_pcie_cong_stats stats;
 44};
 45
 46
 47static const struct counter_desc mlx5e_pcie_cong_stats_desc[] = {
 48	{ MLX5E_DECLARE_STAT(struct mlx5e_pcie_cong_stats,
 49			     pci_bw_inbound_high) },
 50	{ MLX5E_DECLARE_STAT(struct mlx5e_pcie_cong_stats,
 51			     pci_bw_inbound_low) },
 52	{ MLX5E_DECLARE_STAT(struct mlx5e_pcie_cong_stats,
 53			     pci_bw_outbound_high) },
 54	{ MLX5E_DECLARE_STAT(struct mlx5e_pcie_cong_stats,
 55			     pci_bw_outbound_low) },
 56	{ MLX5E_DECLARE_STAT(struct mlx5e_pcie_cong_stats,
 57			     pci_bw_stale_event) },
 58};
 59
 60#define NUM_PCIE_CONG_COUNTERS ARRAY_SIZE(mlx5e_pcie_cong_stats_desc)
 61
 62static MLX5E_DECLARE_STATS_GRP_OP_NUM_STATS(pcie_cong)
 63{
 64	return priv->cong_event ? NUM_PCIE_CONG_COUNTERS : 0;
 65}
 66
 67static MLX5E_DECLARE_STATS_GRP_OP_UPDATE_STATS(pcie_cong) {}
 68
 69static MLX5E_DECLARE_STATS_GRP_OP_FILL_STRS(pcie_cong)
 70{
 71	if (!priv->cong_event)
 72		return;
 73
 74	for (int i = 0; i < NUM_PCIE_CONG_COUNTERS; i++)
 75		ethtool_puts(data, mlx5e_pcie_cong_stats_desc[i].format);
 76}
 77
 78static MLX5E_DECLARE_STATS_GRP_OP_FILL_STATS(pcie_cong)
 79{
 80	if (!priv->cong_event)
 81		return;
 82
 83	for (int i = 0; i < NUM_PCIE_CONG_COUNTERS; i++) {
 84		u32 ctr = MLX5E_READ_CTR32_CPU(&priv->cong_event->stats,
 85					       mlx5e_pcie_cong_stats_desc,
 86					       i);
 87
 88		mlx5e_ethtool_put_stat(data, ctr);
 89	}
 90}
 91
 92MLX5E_DEFINE_STATS_GRP(pcie_cong, 0);
 93
 94static int
 95mlx5_cmd_pcie_cong_event_set(struct mlx5_core_dev *dev,
 96			     const struct mlx5e_pcie_cong_thresh *config,
 97			     u64 *obj_id)
 98{
 99	u32 in[MLX5_ST_SZ_DW(pcie_cong_event_cmd_in)] = {};
100	u32 out[MLX5_ST_SZ_DW(general_obj_out_cmd_hdr)];
101	void *cong_obj;
102	void *hdr;
103	int err;
104
105	hdr = MLX5_ADDR_OF(pcie_cong_event_cmd_in, in, hdr);
106	cong_obj = MLX5_ADDR_OF(pcie_cong_event_cmd_in, in, cong_obj);
107
108	MLX5_SET(general_obj_in_cmd_hdr, hdr, opcode,
109		 MLX5_CMD_OP_CREATE_GENERAL_OBJECT);
110
111	MLX5_SET(general_obj_in_cmd_hdr, hdr, obj_type,
112		 MLX5_GENERAL_OBJECT_TYPES_PCIE_CONG_EVENT);
113
114	MLX5_SET(pcie_cong_event_obj, cong_obj, inbound_event_en, 1);
115	MLX5_SET(pcie_cong_event_obj, cong_obj, outbound_event_en, 1);
116
117	MLX5_SET(pcie_cong_event_obj, cong_obj,
118		 inbound_cong_high_threshold, config->inbound_high);
119	MLX5_SET(pcie_cong_event_obj, cong_obj,
120		 inbound_cong_low_threshold, config->inbound_low);
121
122	MLX5_SET(pcie_cong_event_obj, cong_obj,
123		 outbound_cong_high_threshold, config->outbound_high);
124	MLX5_SET(pcie_cong_event_obj, cong_obj,
125		 outbound_cong_low_threshold, config->outbound_low);
126
127	err = mlx5_cmd_exec(dev, in, sizeof(in), out, sizeof(out));
128	if (err)
129		return err;
130
131	*obj_id = MLX5_GET(general_obj_out_cmd_hdr, out, obj_id);
132
133	mlx5_core_dbg(dev, "PCIe congestion event (obj_id=%llu) created. Config: in: [%u, %u], out: [%u, %u]\n",
134		      *obj_id,
135		      config->inbound_high, config->inbound_low,
136		      config->outbound_high, config->outbound_low);
137
138	return 0;
139}
140
141static int mlx5_cmd_pcie_cong_event_destroy(struct mlx5_core_dev *dev,
142					    u64 obj_id)
143{
144	u32 in[MLX5_ST_SZ_DW(pcie_cong_event_cmd_in)] = {};
145	u32 out[MLX5_ST_SZ_DW(general_obj_out_cmd_hdr)];
146	void *hdr;
147
148	hdr = MLX5_ADDR_OF(pcie_cong_event_cmd_in, in, hdr);
149	MLX5_SET(general_obj_in_cmd_hdr, hdr, opcode,
150		 MLX5_CMD_OP_DESTROY_GENERAL_OBJECT);
151	MLX5_SET(general_obj_in_cmd_hdr, hdr, obj_type,
152		 MLX5_GENERAL_OBJECT_TYPES_PCIE_CONG_EVENT);
153	MLX5_SET(general_obj_in_cmd_hdr, hdr, obj_id, obj_id);
154
155	return mlx5_cmd_exec(dev, in, sizeof(in), out, sizeof(out));
156}
157
158static int mlx5_cmd_pcie_cong_event_query(struct mlx5_core_dev *dev,
159					  u64 obj_id,
160					  u32 *state)
161{
162	u32 in[MLX5_ST_SZ_DW(pcie_cong_event_cmd_in)] = {};
163	u32 out[MLX5_ST_SZ_DW(pcie_cong_event_cmd_out)];
164	void *obj;
165	void *hdr;
166	u8 cong;
167	int err;
168
169	hdr = MLX5_ADDR_OF(pcie_cong_event_cmd_in, in, hdr);
170
171	MLX5_SET(general_obj_in_cmd_hdr, hdr, opcode,
172		 MLX5_CMD_OP_QUERY_GENERAL_OBJECT);
173	MLX5_SET(general_obj_in_cmd_hdr, hdr, obj_type,
174		 MLX5_GENERAL_OBJECT_TYPES_PCIE_CONG_EVENT);
175	MLX5_SET(general_obj_in_cmd_hdr, hdr, obj_id, obj_id);
176
177	err = mlx5_cmd_exec(dev, in, sizeof(in), out, sizeof(out));
178	if (err)
179		return err;
180
181	obj = MLX5_ADDR_OF(pcie_cong_event_cmd_out, out, cong_obj);
182
183	if (state) {
184		cong = MLX5_GET(pcie_cong_event_obj, obj, inbound_cong_state);
185		if (cong == MLX5E_CONG_HIGH_STATE)
186			*state |= MLX5E_INBOUND_CONG;
187
188		cong = MLX5_GET(pcie_cong_event_obj, obj, outbound_cong_state);
189		if (cong == MLX5E_CONG_HIGH_STATE)
190			*state |= MLX5E_OUTBOUND_CONG;
191	}
192
193	return 0;
194}
195
196static void mlx5e_pcie_cong_event_work(struct work_struct *work)
197{
198	struct mlx5e_pcie_cong_event *cong_event;
199	struct mlx5_core_dev *dev;
200	struct mlx5e_priv *priv;
201	u32 new_cong_state = 0;
202	u32 changes;
203	int err;
204
205	cong_event = container_of(work, struct mlx5e_pcie_cong_event, work);
206	priv = cong_event->priv;
207	dev = priv->mdev;
208
209	err = mlx5_cmd_pcie_cong_event_query(dev, cong_event->obj_id,
210					     &new_cong_state);
211	if (err) {
212		mlx5_core_warn(dev, "Error %d when querying PCIe cong event object (obj_id=%llu).\n",
213			       err, cong_event->obj_id);
214		return;
215	}
216
217	changes = cong_event->state ^ new_cong_state;
218	if (!changes) {
219		cong_event->stats.pci_bw_stale_event++;
220		return;
221	}
222
223	cong_event->state = new_cong_state;
224
225	if (changes & MLX5E_INBOUND_CONG) {
226		if (new_cong_state & MLX5E_INBOUND_CONG)
227			cong_event->stats.pci_bw_inbound_high++;
228		else
229			cong_event->stats.pci_bw_inbound_low++;
230	}
231
232	if (changes & MLX5E_OUTBOUND_CONG) {
233		if (new_cong_state & MLX5E_OUTBOUND_CONG)
234			cong_event->stats.pci_bw_outbound_high++;
235		else
236			cong_event->stats.pci_bw_outbound_low++;
237	}
238}
239
240static int mlx5e_pcie_cong_event_handler(struct notifier_block *nb,
241					 unsigned long event, void *eqe)
242{
243	struct mlx5e_pcie_cong_event *cong_event;
244
245	cong_event = mlx5_nb_cof(nb, struct mlx5e_pcie_cong_event, nb);
246	queue_work(cong_event->priv->wq, &cong_event->work);
247
248	return NOTIFY_OK;
249}
250
251static int
252mlx5e_pcie_cong_get_thresh_config(struct mlx5_core_dev *dev,
253				  struct mlx5e_pcie_cong_thresh *config)
254{
255	u32 ids[4] = {
256		MLX5_DEVLINK_PARAM_ID_PCIE_CONG_IN_LOW,
257		MLX5_DEVLINK_PARAM_ID_PCIE_CONG_IN_HIGH,
258		MLX5_DEVLINK_PARAM_ID_PCIE_CONG_OUT_LOW,
259		MLX5_DEVLINK_PARAM_ID_PCIE_CONG_OUT_HIGH,
260	};
261	struct devlink *devlink = priv_to_devlink(dev);
262	union devlink_param_value val[4];
263
264	for (int i = 0; i < 4; i++) {
265		u32 id = ids[i];
266		int err;
267
268		err = devl_param_driverinit_value_get(devlink, id, &val[i]);
269		if (err)
270			return err;
271	}
272
273	config->inbound_low = val[0].vu16;
274	config->inbound_high = val[1].vu16;
275	config->outbound_low = val[2].vu16;
276	config->outbound_high = val[3].vu16;
277
278	return 0;
279}
280
281static int
282mlx5e_thresh_config_validate(struct mlx5_core_dev *mdev,
283			     const struct mlx5e_pcie_cong_thresh *config)
284{
285	int err = 0;
286
287	if (config->inbound_low >= config->inbound_high) {
288		err = -EINVAL;
289		mlx5_core_err(mdev, "PCIe inbound congestion threshold configuration invalid: low (%u) >= high (%u).\n",
290			      config->inbound_low, config->inbound_high);
291	}
292
293	if (config->outbound_low >= config->outbound_high) {
294		err = -EINVAL;
295		mlx5_core_err(mdev, "PCIe outbound congestion threshold configuration invalid: low (%u) >= high (%u).\n",
296			      config->outbound_low, config->outbound_high);
297	}
298
299	return err;
300}
301
302int mlx5e_pcie_cong_event_init(struct mlx5e_priv *priv)
303{
304	struct mlx5e_pcie_cong_thresh thresh_config = {};
305	struct mlx5e_pcie_cong_event *cong_event;
306	struct mlx5_core_dev *mdev = priv->mdev;
307	int err;
308
309	if (!mlx5_pcie_cong_event_supported(mdev))
310		return 0;
311
312	err = mlx5e_pcie_cong_get_thresh_config(mdev, &thresh_config);
313	if (WARN_ON(err))
314		return err;
315
316	err = mlx5e_thresh_config_validate(mdev, &thresh_config);
317	if (err) {
318		mlx5_core_err(mdev, "PCIe congestion event feature disabled\n");
319		return err;
320	}
321
322	cong_event = kvzalloc_node(sizeof(*cong_event), GFP_KERNEL,
323				   mdev->priv.numa_node);
324	if (!cong_event)
325		return -ENOMEM;
326
327	INIT_WORK(&cong_event->work, mlx5e_pcie_cong_event_work);
328	MLX5_NB_INIT(&cong_event->nb, mlx5e_pcie_cong_event_handler,
329		     OBJECT_CHANGE);
330
331	cong_event->priv = priv;
332
333	err = mlx5_cmd_pcie_cong_event_set(mdev, &thresh_config,
334					   &cong_event->obj_id);
335	if (err) {
336		mlx5_core_warn(mdev, "Error creating a PCIe congestion event object\n");
337		goto err_free;
338	}
339
340	err = mlx5_eq_notifier_register(mdev, &cong_event->nb);
341	if (err) {
342		mlx5_core_warn(mdev, "Error registering notifier for the PCIe congestion event\n");
343		goto err_obj_destroy;
344	}
345
346	priv->cong_event = cong_event;
347
348	return 0;
349
350err_obj_destroy:
351	mlx5_cmd_pcie_cong_event_destroy(mdev, cong_event->obj_id);
352err_free:
353	kvfree(cong_event);
354
355	return err;
356}
357
358void mlx5e_pcie_cong_event_cleanup(struct mlx5e_priv *priv)
359{
360	struct mlx5e_pcie_cong_event *cong_event = priv->cong_event;
361	struct mlx5_core_dev *mdev = priv->mdev;
362
363	if (!cong_event)
364		return;
365
366	priv->cong_event = NULL;
367
368	mlx5_eq_notifier_unregister(mdev, &cong_event->nb);
369	cancel_work_sync(&cong_event->work);
370
371	if (mlx5_cmd_pcie_cong_event_destroy(mdev, cong_event->obj_id))
372		mlx5_core_warn(mdev, "Error destroying PCIe congestion event (obj_id=%llu)\n",
373			       cong_event->obj_id);
374
375	kvfree(cong_event);
376}
~/ Read-only file preview.