Linux Audio

Check our new training course

Loading...
v6.8
  1// SPDX-License-Identifier: GPL-2.0+
  2/* Microchip Sparx5 Switch driver
  3 *
  4 * Copyright (c) 2022 Microchip Technology Inc. and its subsidiaries.
  5 */
  6
  7#include <net/pkt_cls.h>
  8#include <net/pkt_sched.h>
  9
 10#include "sparx5_tc.h"
 11#include "sparx5_main.h"
 12#include "sparx5_qos.h"
 13
 14/* tc block handling */
 15static LIST_HEAD(sparx5_block_cb_list);
 16
 17static int sparx5_tc_block_cb(enum tc_setup_type type,
 18			      void *type_data,
 19			      void *cb_priv, bool ingress)
 20{
 21	struct net_device *ndev = cb_priv;
 22
 23	switch (type) {
 24	case TC_SETUP_CLSMATCHALL:
 25		return sparx5_tc_matchall(ndev, type_data, ingress);
 26	case TC_SETUP_CLSFLOWER:
 27		return sparx5_tc_flower(ndev, type_data, ingress);
 28	default:
 29		return -EOPNOTSUPP;
 30	}
 31}
 32
 33static int sparx5_tc_block_cb_ingress(enum tc_setup_type type,
 34				      void *type_data,
 35				      void *cb_priv)
 36{
 37	return sparx5_tc_block_cb(type, type_data, cb_priv, true);
 38}
 39
 40static int sparx5_tc_block_cb_egress(enum tc_setup_type type,
 41				     void *type_data,
 42				     void *cb_priv)
 43{
 44	return sparx5_tc_block_cb(type, type_data, cb_priv, false);
 45}
 46
 47static int sparx5_tc_setup_block(struct net_device *ndev,
 48				 struct flow_block_offload *fbo)
 49{
 50	flow_setup_cb_t *cb;
 51
 52	if (fbo->binder_type == FLOW_BLOCK_BINDER_TYPE_CLSACT_INGRESS)
 53		cb = sparx5_tc_block_cb_ingress;
 54	else if (fbo->binder_type == FLOW_BLOCK_BINDER_TYPE_CLSACT_EGRESS)
 55		cb = sparx5_tc_block_cb_egress;
 56	else
 57		return -EOPNOTSUPP;
 58
 59	return flow_block_cb_setup_simple(fbo, &sparx5_block_cb_list,
 60					  cb, ndev, ndev, false);
 61}
 62
 63static void sparx5_tc_get_layer_and_idx(u32 parent, u32 portno, u32 *layer,
 64					u32 *idx)
 65{
 66	if (parent == TC_H_ROOT) {
 67		*layer = 2;
 68		*idx = portno;
 69	} else {
 70		u32 queue = TC_H_MIN(parent) - 1;
 71		*layer = 0;
 72		*idx = SPX5_HSCH_L0_GET_IDX(portno, queue);
 73	}
 74}
 75
 76static int sparx5_tc_setup_qdisc_mqprio(struct net_device *ndev,
 77					struct tc_mqprio_qopt_offload *m)
 78{
 79	m->qopt.hw = TC_MQPRIO_HW_OFFLOAD_TCS;
 80
 81	if (m->qopt.num_tc == 0)
 82		return sparx5_tc_mqprio_del(ndev);
 83	else
 84		return sparx5_tc_mqprio_add(ndev, m->qopt.num_tc);
 85}
 86
 87static int sparx5_tc_setup_qdisc_tbf(struct net_device *ndev,
 88				     struct tc_tbf_qopt_offload *qopt)
 89{
 90	struct sparx5_port *port = netdev_priv(ndev);
 91	u32 layer, se_idx;
 92
 93	sparx5_tc_get_layer_and_idx(qopt->parent, port->portno, &layer,
 94				    &se_idx);
 95
 96	switch (qopt->command) {
 97	case TC_TBF_REPLACE:
 98		return sparx5_tc_tbf_add(port, &qopt->replace_params, layer,
 99					 se_idx);
100	case TC_TBF_DESTROY:
101		return sparx5_tc_tbf_del(port, layer, se_idx);
102	case TC_TBF_STATS:
103		return -EOPNOTSUPP;
104	default:
105		return -EOPNOTSUPP;
106	}
107
108	return -EOPNOTSUPP;
109}
110
111static int sparx5_tc_setup_qdisc_ets(struct net_device *ndev,
112				     struct tc_ets_qopt_offload *qopt)
113{
114	struct tc_ets_qopt_offload_replace_params *params =
115		&qopt->replace_params;
116	struct sparx5_port *port = netdev_priv(ndev);
117	int i;
118
119	/* Only allow ets on ports  */
120	if (qopt->parent != TC_H_ROOT)
121		return -EOPNOTSUPP;
122
123	switch (qopt->command) {
124	case TC_ETS_REPLACE:
125
126		/* We support eight priorities */
127		if (params->bands != SPX5_PRIOS)
128			return -EOPNOTSUPP;
129
130		/* Sanity checks */
131		for (i = 0; i < SPX5_PRIOS; ++i) {
132			/* Priority map is *always* reverse e.g: 7 6 5 .. 0 */
133			if (params->priomap[i] != (7 - i))
134				return -EOPNOTSUPP;
135			/* Throw an error if we receive zero weights by tc */
136			if (params->quanta[i] && params->weights[i] == 0) {
137				pr_err("Invalid ets configuration; band %d has weight zero",
138				       i);
139				return -EINVAL;
140			}
141		}
142
143		return sparx5_tc_ets_add(port, params);
144	case TC_ETS_DESTROY:
145
146		return sparx5_tc_ets_del(port);
147	case TC_ETS_GRAFT:
148		return -EOPNOTSUPP;
149
150	default:
151		return -EOPNOTSUPP;
152	}
153
154	return -EOPNOTSUPP;
155}
156
157int sparx5_port_setup_tc(struct net_device *ndev, enum tc_setup_type type,
158			 void *type_data)
159{
160	switch (type) {
161	case TC_SETUP_BLOCK:
162		return sparx5_tc_setup_block(ndev, type_data);
163	case TC_SETUP_QDISC_MQPRIO:
164		return sparx5_tc_setup_qdisc_mqprio(ndev, type_data);
165	case TC_SETUP_QDISC_TBF:
166		return sparx5_tc_setup_qdisc_tbf(ndev, type_data);
167	case TC_SETUP_QDISC_ETS:
168		return sparx5_tc_setup_qdisc_ets(ndev, type_data);
169	default:
170		return -EOPNOTSUPP;
171	}
172
173	return 0;
174}
v6.2
  1// SPDX-License-Identifier: GPL-2.0+
  2/* Microchip Sparx5 Switch driver
  3 *
  4 * Copyright (c) 2022 Microchip Technology Inc. and its subsidiaries.
  5 */
  6
  7#include <net/pkt_cls.h>
 
  8
  9#include "sparx5_tc.h"
 10#include "sparx5_main.h"
 11#include "sparx5_qos.h"
 12
 13/* tc block handling */
 14static LIST_HEAD(sparx5_block_cb_list);
 15
 16static int sparx5_tc_block_cb(enum tc_setup_type type,
 17			      void *type_data,
 18			      void *cb_priv, bool ingress)
 19{
 20	struct net_device *ndev = cb_priv;
 21
 22	switch (type) {
 23	case TC_SETUP_CLSMATCHALL:
 24		return sparx5_tc_matchall(ndev, type_data, ingress);
 25	case TC_SETUP_CLSFLOWER:
 26		return sparx5_tc_flower(ndev, type_data, ingress);
 27	default:
 28		return -EOPNOTSUPP;
 29	}
 30}
 31
 32static int sparx5_tc_block_cb_ingress(enum tc_setup_type type,
 33				      void *type_data,
 34				      void *cb_priv)
 35{
 36	return sparx5_tc_block_cb(type, type_data, cb_priv, true);
 37}
 38
 39static int sparx5_tc_block_cb_egress(enum tc_setup_type type,
 40				     void *type_data,
 41				     void *cb_priv)
 42{
 43	return sparx5_tc_block_cb(type, type_data, cb_priv, false);
 44}
 45
 46static int sparx5_tc_setup_block(struct net_device *ndev,
 47				 struct flow_block_offload *fbo)
 48{
 49	flow_setup_cb_t *cb;
 50
 51	if (fbo->binder_type == FLOW_BLOCK_BINDER_TYPE_CLSACT_INGRESS)
 52		cb = sparx5_tc_block_cb_ingress;
 53	else if (fbo->binder_type == FLOW_BLOCK_BINDER_TYPE_CLSACT_EGRESS)
 54		cb = sparx5_tc_block_cb_egress;
 55	else
 56		return -EOPNOTSUPP;
 57
 58	return flow_block_cb_setup_simple(fbo, &sparx5_block_cb_list,
 59					  cb, ndev, ndev, false);
 60}
 61
 62static void sparx5_tc_get_layer_and_idx(u32 parent, u32 portno, u32 *layer,
 63					u32 *idx)
 64{
 65	if (parent == TC_H_ROOT) {
 66		*layer = 2;
 67		*idx = portno;
 68	} else {
 69		u32 queue = TC_H_MIN(parent) - 1;
 70		*layer = 0;
 71		*idx = SPX5_HSCH_L0_GET_IDX(portno, queue);
 72	}
 73}
 74
 75static int sparx5_tc_setup_qdisc_mqprio(struct net_device *ndev,
 76					struct tc_mqprio_qopt_offload *m)
 77{
 78	m->qopt.hw = TC_MQPRIO_HW_OFFLOAD_TCS;
 79
 80	if (m->qopt.num_tc == 0)
 81		return sparx5_tc_mqprio_del(ndev);
 82	else
 83		return sparx5_tc_mqprio_add(ndev, m->qopt.num_tc);
 84}
 85
 86static int sparx5_tc_setup_qdisc_tbf(struct net_device *ndev,
 87				     struct tc_tbf_qopt_offload *qopt)
 88{
 89	struct sparx5_port *port = netdev_priv(ndev);
 90	u32 layer, se_idx;
 91
 92	sparx5_tc_get_layer_and_idx(qopt->parent, port->portno, &layer,
 93				    &se_idx);
 94
 95	switch (qopt->command) {
 96	case TC_TBF_REPLACE:
 97		return sparx5_tc_tbf_add(port, &qopt->replace_params, layer,
 98					 se_idx);
 99	case TC_TBF_DESTROY:
100		return sparx5_tc_tbf_del(port, layer, se_idx);
101	case TC_TBF_STATS:
102		return -EOPNOTSUPP;
103	default:
104		return -EOPNOTSUPP;
105	}
106
107	return -EOPNOTSUPP;
108}
109
110static int sparx5_tc_setup_qdisc_ets(struct net_device *ndev,
111				     struct tc_ets_qopt_offload *qopt)
112{
113	struct tc_ets_qopt_offload_replace_params *params =
114		&qopt->replace_params;
115	struct sparx5_port *port = netdev_priv(ndev);
116	int i;
117
118	/* Only allow ets on ports  */
119	if (qopt->parent != TC_H_ROOT)
120		return -EOPNOTSUPP;
121
122	switch (qopt->command) {
123	case TC_ETS_REPLACE:
124
125		/* We support eight priorities */
126		if (params->bands != SPX5_PRIOS)
127			return -EOPNOTSUPP;
128
129		/* Sanity checks */
130		for (i = 0; i < SPX5_PRIOS; ++i) {
131			/* Priority map is *always* reverse e.g: 7 6 5 .. 0 */
132			if (params->priomap[i] != (7 - i))
133				return -EOPNOTSUPP;
134			/* Throw an error if we receive zero weights by tc */
135			if (params->quanta[i] && params->weights[i] == 0) {
136				pr_err("Invalid ets configuration; band %d has weight zero",
137				       i);
138				return -EINVAL;
139			}
140		}
141
142		return sparx5_tc_ets_add(port, params);
143	case TC_ETS_DESTROY:
144
145		return sparx5_tc_ets_del(port);
146	case TC_ETS_GRAFT:
147		return -EOPNOTSUPP;
148
149	default:
150		return -EOPNOTSUPP;
151	}
152
153	return -EOPNOTSUPP;
154}
155
156int sparx5_port_setup_tc(struct net_device *ndev, enum tc_setup_type type,
157			 void *type_data)
158{
159	switch (type) {
160	case TC_SETUP_BLOCK:
161		return sparx5_tc_setup_block(ndev, type_data);
162	case TC_SETUP_QDISC_MQPRIO:
163		return sparx5_tc_setup_qdisc_mqprio(ndev, type_data);
164	case TC_SETUP_QDISC_TBF:
165		return sparx5_tc_setup_qdisc_tbf(ndev, type_data);
166	case TC_SETUP_QDISC_ETS:
167		return sparx5_tc_setup_qdisc_ets(ndev, type_data);
168	default:
169		return -EOPNOTSUPP;
170	}
171
172	return 0;
173}