Linux Audio

Check our new training course

Loading...
Note: File does not exist in v3.1.
  1/*
  2 * drivers/net/ethernet/mellanox/mlxsw/spectrum_dcb.c
  3 * Copyright (c) 2016 Mellanox Technologies. All rights reserved.
  4 * Copyright (c) 2016 Ido Schimmel <idosch@mellanox.com>
  5 *
  6 * Redistribution and use in source and binary forms, with or without
  7 * modification, are permitted provided that the following conditions are met:
  8 *
  9 * 1. Redistributions of source code must retain the above copyright
 10 *    notice, this list of conditions and the following disclaimer.
 11 * 2. Redistributions in binary form must reproduce the above copyright
 12 *    notice, this list of conditions and the following disclaimer in the
 13 *    documentation and/or other materials provided with the distribution.
 14 * 3. Neither the names of the copyright holders nor the names of its
 15 *    contributors may be used to endorse or promote products derived from
 16 *    this software without specific prior written permission.
 17 *
 18 * Alternatively, this software may be distributed under the terms of the
 19 * GNU General Public License ("GPL") version 2 as published by the Free
 20 * Software Foundation.
 21 *
 22 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 23 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 24 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 25 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
 26 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 27 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 28 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 29 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 30 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 31 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 32 * POSSIBILITY OF SUCH DAMAGE.
 33 */
 34
 35#include <linux/netdevice.h>
 36#include <linux/string.h>
 37#include <linux/bitops.h>
 38#include <net/dcbnl.h>
 39
 40#include "spectrum.h"
 41#include "reg.h"
 42
 43static u8 mlxsw_sp_dcbnl_getdcbx(struct net_device __always_unused *dev)
 44{
 45	return DCB_CAP_DCBX_HOST | DCB_CAP_DCBX_VER_IEEE;
 46}
 47
 48static u8 mlxsw_sp_dcbnl_setdcbx(struct net_device __always_unused *dev,
 49				 u8 mode)
 50{
 51	return (mode != (DCB_CAP_DCBX_HOST | DCB_CAP_DCBX_VER_IEEE)) ? 1 : 0;
 52}
 53
 54static int mlxsw_sp_dcbnl_ieee_getets(struct net_device *dev,
 55				      struct ieee_ets *ets)
 56{
 57	struct mlxsw_sp_port *mlxsw_sp_port = netdev_priv(dev);
 58
 59	memcpy(ets, mlxsw_sp_port->dcb.ets, sizeof(*ets));
 60
 61	return 0;
 62}
 63
 64static int mlxsw_sp_port_ets_validate(struct mlxsw_sp_port *mlxsw_sp_port,
 65				      struct ieee_ets *ets)
 66{
 67	struct net_device *dev = mlxsw_sp_port->dev;
 68	bool has_ets_tc = false;
 69	int i, tx_bw_sum = 0;
 70
 71	for (i = 0; i < IEEE_8021QAZ_MAX_TCS; i++) {
 72		switch (ets->tc_tsa[i]) {
 73		case IEEE_8021QAZ_TSA_STRICT:
 74			break;
 75		case IEEE_8021QAZ_TSA_ETS:
 76			has_ets_tc = true;
 77			tx_bw_sum += ets->tc_tx_bw[i];
 78			break;
 79		default:
 80			netdev_err(dev, "Only strict priority and ETS are supported\n");
 81			return -EINVAL;
 82		}
 83
 84		if (ets->prio_tc[i] >= IEEE_8021QAZ_MAX_TCS) {
 85			netdev_err(dev, "Invalid TC\n");
 86			return -EINVAL;
 87		}
 88	}
 89
 90	if (has_ets_tc && tx_bw_sum != 100) {
 91		netdev_err(dev, "Total ETS bandwidth should equal 100\n");
 92		return -EINVAL;
 93	}
 94
 95	return 0;
 96}
 97
 98static int mlxsw_sp_port_pg_prio_map(struct mlxsw_sp_port *mlxsw_sp_port,
 99				     u8 *prio_tc)
100{
101	char pptb_pl[MLXSW_REG_PPTB_LEN];
102	int i;
103
104	mlxsw_reg_pptb_pack(pptb_pl, mlxsw_sp_port->local_port);
105	for (i = 0; i < IEEE_8021QAZ_MAX_TCS; i++)
106		mlxsw_reg_pptb_prio_to_buff_pack(pptb_pl, i, prio_tc[i]);
107
108	return mlxsw_reg_write(mlxsw_sp_port->mlxsw_sp->core, MLXSW_REG(pptb),
109			       pptb_pl);
110}
111
112static bool mlxsw_sp_ets_has_pg(u8 *prio_tc, u8 pg)
113{
114	int i;
115
116	for (i = 0; i < IEEE_8021QAZ_MAX_TCS; i++)
117		if (prio_tc[i] == pg)
118			return true;
119	return false;
120}
121
122static int mlxsw_sp_port_pg_destroy(struct mlxsw_sp_port *mlxsw_sp_port,
123				    u8 *old_prio_tc, u8 *new_prio_tc)
124{
125	struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp;
126	char pbmc_pl[MLXSW_REG_PBMC_LEN];
127	int err, i;
128
129	mlxsw_reg_pbmc_pack(pbmc_pl, mlxsw_sp_port->local_port, 0, 0);
130	err = mlxsw_reg_query(mlxsw_sp->core, MLXSW_REG(pbmc), pbmc_pl);
131	if (err)
132		return err;
133
134	for (i = 0; i < IEEE_8021QAZ_MAX_TCS; i++) {
135		u8 pg = old_prio_tc[i];
136
137		if (!mlxsw_sp_ets_has_pg(new_prio_tc, pg))
138			mlxsw_reg_pbmc_lossy_buffer_pack(pbmc_pl, pg, 0);
139	}
140
141	return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(pbmc), pbmc_pl);
142}
143
144static int mlxsw_sp_port_headroom_set(struct mlxsw_sp_port *mlxsw_sp_port,
145				      struct ieee_ets *ets)
146{
147	bool pause_en = mlxsw_sp_port_is_pause_en(mlxsw_sp_port);
148	struct ieee_ets *my_ets = mlxsw_sp_port->dcb.ets;
149	struct net_device *dev = mlxsw_sp_port->dev;
150	int err;
151
152	/* Create the required PGs, but don't destroy existing ones, as
153	 * traffic is still directed to them.
154	 */
155	err = __mlxsw_sp_port_headroom_set(mlxsw_sp_port, dev->mtu,
156					   ets->prio_tc, pause_en,
157					   mlxsw_sp_port->dcb.pfc);
158	if (err) {
159		netdev_err(dev, "Failed to configure port's headroom\n");
160		return err;
161	}
162
163	err = mlxsw_sp_port_pg_prio_map(mlxsw_sp_port, ets->prio_tc);
164	if (err) {
165		netdev_err(dev, "Failed to set PG-priority mapping\n");
166		goto err_port_prio_pg_map;
167	}
168
169	err = mlxsw_sp_port_pg_destroy(mlxsw_sp_port, my_ets->prio_tc,
170				       ets->prio_tc);
171	if (err)
172		netdev_warn(dev, "Failed to remove ununsed PGs\n");
173
174	return 0;
175
176err_port_prio_pg_map:
177	mlxsw_sp_port_pg_destroy(mlxsw_sp_port, ets->prio_tc, my_ets->prio_tc);
178	return err;
179}
180
181static int __mlxsw_sp_dcbnl_ieee_setets(struct mlxsw_sp_port *mlxsw_sp_port,
182					struct ieee_ets *ets)
183{
184	struct ieee_ets *my_ets = mlxsw_sp_port->dcb.ets;
185	struct net_device *dev = mlxsw_sp_port->dev;
186	int i, err;
187
188	/* Egress configuration. */
189	for (i = 0; i < IEEE_8021QAZ_MAX_TCS; i++) {
190		bool dwrr = ets->tc_tsa[i] == IEEE_8021QAZ_TSA_ETS;
191		u8 weight = ets->tc_tx_bw[i];
192
193		err = mlxsw_sp_port_ets_set(mlxsw_sp_port,
194					    MLXSW_REG_QEEC_HIERARCY_SUBGROUP, i,
195					    0, dwrr, weight);
196		if (err) {
197			netdev_err(dev, "Failed to link subgroup ETS element %d to group\n",
198				   i);
199			goto err_port_ets_set;
200		}
201	}
202
203	for (i = 0; i < IEEE_8021QAZ_MAX_TCS; i++) {
204		err = mlxsw_sp_port_prio_tc_set(mlxsw_sp_port, i,
205						ets->prio_tc[i]);
206		if (err) {
207			netdev_err(dev, "Failed to map prio %d to TC %d\n", i,
208				   ets->prio_tc[i]);
209			goto err_port_prio_tc_set;
210		}
211	}
212
213	/* Ingress configuration. */
214	err = mlxsw_sp_port_headroom_set(mlxsw_sp_port, ets);
215	if (err)
216		goto err_port_headroom_set;
217
218	return 0;
219
220err_port_headroom_set:
221	i = IEEE_8021QAZ_MAX_TCS;
222err_port_prio_tc_set:
223	for (i--; i >= 0; i--)
224		mlxsw_sp_port_prio_tc_set(mlxsw_sp_port, i, my_ets->prio_tc[i]);
225	i = IEEE_8021QAZ_MAX_TCS;
226err_port_ets_set:
227	for (i--; i >= 0; i--) {
228		bool dwrr = my_ets->tc_tsa[i] == IEEE_8021QAZ_TSA_ETS;
229		u8 weight = my_ets->tc_tx_bw[i];
230
231		err = mlxsw_sp_port_ets_set(mlxsw_sp_port,
232					    MLXSW_REG_QEEC_HIERARCY_SUBGROUP, i,
233					    0, dwrr, weight);
234	}
235	return err;
236}
237
238static int mlxsw_sp_dcbnl_ieee_setets(struct net_device *dev,
239				      struct ieee_ets *ets)
240{
241	struct mlxsw_sp_port *mlxsw_sp_port = netdev_priv(dev);
242	int err;
243
244	err = mlxsw_sp_port_ets_validate(mlxsw_sp_port, ets);
245	if (err)
246		return err;
247
248	err = __mlxsw_sp_dcbnl_ieee_setets(mlxsw_sp_port, ets);
249	if (err)
250		return err;
251
252	memcpy(mlxsw_sp_port->dcb.ets, ets, sizeof(*ets));
253	mlxsw_sp_port->dcb.ets->ets_cap = IEEE_8021QAZ_MAX_TCS;
254
255	return 0;
256}
257
258static int mlxsw_sp_dcbnl_ieee_getmaxrate(struct net_device *dev,
259					  struct ieee_maxrate *maxrate)
260{
261	struct mlxsw_sp_port *mlxsw_sp_port = netdev_priv(dev);
262
263	memcpy(maxrate, mlxsw_sp_port->dcb.maxrate, sizeof(*maxrate));
264
265	return 0;
266}
267
268static int mlxsw_sp_dcbnl_ieee_setmaxrate(struct net_device *dev,
269					  struct ieee_maxrate *maxrate)
270{
271	struct mlxsw_sp_port *mlxsw_sp_port = netdev_priv(dev);
272	struct ieee_maxrate *my_maxrate = mlxsw_sp_port->dcb.maxrate;
273	int err, i;
274
275	for (i = 0; i < IEEE_8021QAZ_MAX_TCS; i++) {
276		err = mlxsw_sp_port_ets_maxrate_set(mlxsw_sp_port,
277						    MLXSW_REG_QEEC_HIERARCY_SUBGROUP,
278						    i, 0,
279						    maxrate->tc_maxrate[i]);
280		if (err) {
281			netdev_err(dev, "Failed to set maxrate for TC %d\n", i);
282			goto err_port_ets_maxrate_set;
283		}
284	}
285
286	memcpy(mlxsw_sp_port->dcb.maxrate, maxrate, sizeof(*maxrate));
287
288	return 0;
289
290err_port_ets_maxrate_set:
291	for (i--; i >= 0; i--)
292		mlxsw_sp_port_ets_maxrate_set(mlxsw_sp_port,
293					      MLXSW_REG_QEEC_HIERARCY_SUBGROUP,
294					      i, 0, my_maxrate->tc_maxrate[i]);
295	return err;
296}
297
298static int mlxsw_sp_port_pfc_cnt_get(struct mlxsw_sp_port *mlxsw_sp_port,
299				     u8 prio)
300{
301	struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp;
302	struct ieee_pfc *my_pfc = mlxsw_sp_port->dcb.pfc;
303	char ppcnt_pl[MLXSW_REG_PPCNT_LEN];
304	int err;
305
306	mlxsw_reg_ppcnt_pack(ppcnt_pl, mlxsw_sp_port->local_port,
307			     MLXSW_REG_PPCNT_PRIO_CNT, prio);
308	err = mlxsw_reg_query(mlxsw_sp->core, MLXSW_REG(ppcnt), ppcnt_pl);
309	if (err)
310		return err;
311
312	my_pfc->requests[prio] = mlxsw_reg_ppcnt_tx_pause_get(ppcnt_pl);
313	my_pfc->indications[prio] = mlxsw_reg_ppcnt_rx_pause_get(ppcnt_pl);
314
315	return 0;
316}
317
318static int mlxsw_sp_dcbnl_ieee_getpfc(struct net_device *dev,
319				      struct ieee_pfc *pfc)
320{
321	struct mlxsw_sp_port *mlxsw_sp_port = netdev_priv(dev);
322	int err, i;
323
324	for (i = 0; i < IEEE_8021QAZ_MAX_TCS; i++) {
325		err = mlxsw_sp_port_pfc_cnt_get(mlxsw_sp_port, i);
326		if (err) {
327			netdev_err(dev, "Failed to get PFC count for priority %d\n",
328				   i);
329			return err;
330		}
331	}
332
333	memcpy(pfc, mlxsw_sp_port->dcb.pfc, sizeof(*pfc));
334
335	return 0;
336}
337
338static int mlxsw_sp_port_pfc_set(struct mlxsw_sp_port *mlxsw_sp_port,
339				 struct ieee_pfc *pfc)
340{
341	char pfcc_pl[MLXSW_REG_PFCC_LEN];
342
343	mlxsw_reg_pfcc_pack(pfcc_pl, mlxsw_sp_port->local_port);
344	mlxsw_reg_pfcc_pprx_set(pfcc_pl, mlxsw_sp_port->link.rx_pause);
345	mlxsw_reg_pfcc_pptx_set(pfcc_pl, mlxsw_sp_port->link.tx_pause);
346	mlxsw_reg_pfcc_prio_pack(pfcc_pl, pfc->pfc_en);
347
348	return mlxsw_reg_write(mlxsw_sp_port->mlxsw_sp->core, MLXSW_REG(pfcc),
349			       pfcc_pl);
350}
351
352static int mlxsw_sp_dcbnl_ieee_setpfc(struct net_device *dev,
353				      struct ieee_pfc *pfc)
354{
355	struct mlxsw_sp_port *mlxsw_sp_port = netdev_priv(dev);
356	bool pause_en = mlxsw_sp_port_is_pause_en(mlxsw_sp_port);
357	int err;
358
359	if (pause_en && pfc->pfc_en) {
360		netdev_err(dev, "PAUSE frames already enabled on port\n");
361		return -EINVAL;
362	}
363
364	err = __mlxsw_sp_port_headroom_set(mlxsw_sp_port, dev->mtu,
365					   mlxsw_sp_port->dcb.ets->prio_tc,
366					   pause_en, pfc);
367	if (err) {
368		netdev_err(dev, "Failed to configure port's headroom for PFC\n");
369		return err;
370	}
371
372	err = mlxsw_sp_port_pfc_set(mlxsw_sp_port, pfc);
373	if (err) {
374		netdev_err(dev, "Failed to configure PFC\n");
375		goto err_port_pfc_set;
376	}
377
378	memcpy(mlxsw_sp_port->dcb.pfc, pfc, sizeof(*pfc));
379	mlxsw_sp_port->dcb.pfc->pfc_cap = IEEE_8021QAZ_MAX_TCS;
380
381	return 0;
382
383err_port_pfc_set:
384	__mlxsw_sp_port_headroom_set(mlxsw_sp_port, dev->mtu,
385				     mlxsw_sp_port->dcb.ets->prio_tc, pause_en,
386				     mlxsw_sp_port->dcb.pfc);
387	return err;
388}
389
390static const struct dcbnl_rtnl_ops mlxsw_sp_dcbnl_ops = {
391	.ieee_getets		= mlxsw_sp_dcbnl_ieee_getets,
392	.ieee_setets		= mlxsw_sp_dcbnl_ieee_setets,
393	.ieee_getmaxrate	= mlxsw_sp_dcbnl_ieee_getmaxrate,
394	.ieee_setmaxrate	= mlxsw_sp_dcbnl_ieee_setmaxrate,
395	.ieee_getpfc		= mlxsw_sp_dcbnl_ieee_getpfc,
396	.ieee_setpfc		= mlxsw_sp_dcbnl_ieee_setpfc,
397
398	.getdcbx		= mlxsw_sp_dcbnl_getdcbx,
399	.setdcbx		= mlxsw_sp_dcbnl_setdcbx,
400};
401
402static int mlxsw_sp_port_ets_init(struct mlxsw_sp_port *mlxsw_sp_port)
403{
404	mlxsw_sp_port->dcb.ets = kzalloc(sizeof(*mlxsw_sp_port->dcb.ets),
405					 GFP_KERNEL);
406	if (!mlxsw_sp_port->dcb.ets)
407		return -ENOMEM;
408
409	mlxsw_sp_port->dcb.ets->ets_cap = IEEE_8021QAZ_MAX_TCS;
410
411	return 0;
412}
413
414static void mlxsw_sp_port_ets_fini(struct mlxsw_sp_port *mlxsw_sp_port)
415{
416	kfree(mlxsw_sp_port->dcb.ets);
417}
418
419static int mlxsw_sp_port_maxrate_init(struct mlxsw_sp_port *mlxsw_sp_port)
420{
421	int i;
422
423	mlxsw_sp_port->dcb.maxrate = kmalloc(sizeof(*mlxsw_sp_port->dcb.maxrate),
424					     GFP_KERNEL);
425	if (!mlxsw_sp_port->dcb.maxrate)
426		return -ENOMEM;
427
428	for (i = 0; i < IEEE_8021QAZ_MAX_TCS; i++)
429		mlxsw_sp_port->dcb.maxrate->tc_maxrate[i] = MLXSW_REG_QEEC_MAS_DIS;
430
431	return 0;
432}
433
434static void mlxsw_sp_port_maxrate_fini(struct mlxsw_sp_port *mlxsw_sp_port)
435{
436	kfree(mlxsw_sp_port->dcb.maxrate);
437}
438
439static int mlxsw_sp_port_pfc_init(struct mlxsw_sp_port *mlxsw_sp_port)
440{
441	mlxsw_sp_port->dcb.pfc = kzalloc(sizeof(*mlxsw_sp_port->dcb.pfc),
442					 GFP_KERNEL);
443	if (!mlxsw_sp_port->dcb.pfc)
444		return -ENOMEM;
445
446	mlxsw_sp_port->dcb.pfc->pfc_cap = IEEE_8021QAZ_MAX_TCS;
447
448	return 0;
449}
450
451static void mlxsw_sp_port_pfc_fini(struct mlxsw_sp_port *mlxsw_sp_port)
452{
453	kfree(mlxsw_sp_port->dcb.pfc);
454}
455
456int mlxsw_sp_port_dcb_init(struct mlxsw_sp_port *mlxsw_sp_port)
457{
458	int err;
459
460	err = mlxsw_sp_port_ets_init(mlxsw_sp_port);
461	if (err)
462		return err;
463	err = mlxsw_sp_port_maxrate_init(mlxsw_sp_port);
464	if (err)
465		goto err_port_maxrate_init;
466	err = mlxsw_sp_port_pfc_init(mlxsw_sp_port);
467	if (err)
468		goto err_port_pfc_init;
469
470	mlxsw_sp_port->dev->dcbnl_ops = &mlxsw_sp_dcbnl_ops;
471
472	return 0;
473
474err_port_pfc_init:
475	mlxsw_sp_port_maxrate_fini(mlxsw_sp_port);
476err_port_maxrate_init:
477	mlxsw_sp_port_ets_fini(mlxsw_sp_port);
478	return err;
479}
480
481void mlxsw_sp_port_dcb_fini(struct mlxsw_sp_port *mlxsw_sp_port)
482{
483	mlxsw_sp_port_pfc_fini(mlxsw_sp_port);
484	mlxsw_sp_port_maxrate_fini(mlxsw_sp_port);
485	mlxsw_sp_port_ets_fini(mlxsw_sp_port);
486}