xref: /linux/drivers/net/ethernet/chelsio/cxgb4/cxgb4_tc_matchall.c (revision 4ec4762d8ec6edcfe59fd806472d2b7518debe52)
1*4ec4762dSRahul Lakkireddy // SPDX-License-Identifier: GPL-2.0-only
2*4ec4762dSRahul Lakkireddy /* Copyright (C) 2019 Chelsio Communications.  All rights reserved. */
3*4ec4762dSRahul Lakkireddy 
4*4ec4762dSRahul Lakkireddy #include "cxgb4.h"
5*4ec4762dSRahul Lakkireddy #include "cxgb4_tc_matchall.h"
6*4ec4762dSRahul Lakkireddy #include "sched.h"
7*4ec4762dSRahul Lakkireddy 
8*4ec4762dSRahul Lakkireddy static int cxgb4_matchall_egress_validate(struct net_device *dev,
9*4ec4762dSRahul Lakkireddy 					  struct tc_cls_matchall_offload *cls)
10*4ec4762dSRahul Lakkireddy {
11*4ec4762dSRahul Lakkireddy 	struct netlink_ext_ack *extack = cls->common.extack;
12*4ec4762dSRahul Lakkireddy 	struct flow_action *actions = &cls->rule->action;
13*4ec4762dSRahul Lakkireddy 	struct port_info *pi = netdev2pinfo(dev);
14*4ec4762dSRahul Lakkireddy 	struct flow_action_entry *entry;
15*4ec4762dSRahul Lakkireddy 	u64 max_link_rate;
16*4ec4762dSRahul Lakkireddy 	u32 i, speed;
17*4ec4762dSRahul Lakkireddy 	int ret;
18*4ec4762dSRahul Lakkireddy 
19*4ec4762dSRahul Lakkireddy 	if (!flow_action_has_entries(actions)) {
20*4ec4762dSRahul Lakkireddy 		NL_SET_ERR_MSG_MOD(extack,
21*4ec4762dSRahul Lakkireddy 				   "Egress MATCHALL offload needs at least 1 policing action");
22*4ec4762dSRahul Lakkireddy 		return -EINVAL;
23*4ec4762dSRahul Lakkireddy 	} else if (!flow_offload_has_one_action(actions)) {
24*4ec4762dSRahul Lakkireddy 		NL_SET_ERR_MSG_MOD(extack,
25*4ec4762dSRahul Lakkireddy 				   "Egress MATCHALL offload only supports 1 policing action");
26*4ec4762dSRahul Lakkireddy 		return -EINVAL;
27*4ec4762dSRahul Lakkireddy 	} else if (pi->tc_block_shared) {
28*4ec4762dSRahul Lakkireddy 		NL_SET_ERR_MSG_MOD(extack,
29*4ec4762dSRahul Lakkireddy 				   "Egress MATCHALL offload not supported with shared blocks");
30*4ec4762dSRahul Lakkireddy 		return -EINVAL;
31*4ec4762dSRahul Lakkireddy 	}
32*4ec4762dSRahul Lakkireddy 
33*4ec4762dSRahul Lakkireddy 	ret = t4_get_link_params(pi, NULL, &speed, NULL);
34*4ec4762dSRahul Lakkireddy 	if (ret) {
35*4ec4762dSRahul Lakkireddy 		NL_SET_ERR_MSG_MOD(extack,
36*4ec4762dSRahul Lakkireddy 				   "Failed to get max speed supported by the link");
37*4ec4762dSRahul Lakkireddy 		return -EINVAL;
38*4ec4762dSRahul Lakkireddy 	}
39*4ec4762dSRahul Lakkireddy 
40*4ec4762dSRahul Lakkireddy 	/* Convert from Mbps to bps */
41*4ec4762dSRahul Lakkireddy 	max_link_rate = (u64)speed * 1000 * 1000;
42*4ec4762dSRahul Lakkireddy 
43*4ec4762dSRahul Lakkireddy 	flow_action_for_each(i, entry, actions) {
44*4ec4762dSRahul Lakkireddy 		switch (entry->id) {
45*4ec4762dSRahul Lakkireddy 		case FLOW_ACTION_POLICE:
46*4ec4762dSRahul Lakkireddy 			/* Convert bytes per second to bits per second */
47*4ec4762dSRahul Lakkireddy 			if (entry->police.rate_bytes_ps * 8 > max_link_rate) {
48*4ec4762dSRahul Lakkireddy 				NL_SET_ERR_MSG_MOD(extack,
49*4ec4762dSRahul Lakkireddy 						   "Specified policing max rate is larger than underlying link speed");
50*4ec4762dSRahul Lakkireddy 				return -ERANGE;
51*4ec4762dSRahul Lakkireddy 			}
52*4ec4762dSRahul Lakkireddy 			break;
53*4ec4762dSRahul Lakkireddy 		default:
54*4ec4762dSRahul Lakkireddy 			NL_SET_ERR_MSG_MOD(extack,
55*4ec4762dSRahul Lakkireddy 					   "Only policing action supported with Egress MATCHALL offload");
56*4ec4762dSRahul Lakkireddy 			return -EOPNOTSUPP;
57*4ec4762dSRahul Lakkireddy 		}
58*4ec4762dSRahul Lakkireddy 	}
59*4ec4762dSRahul Lakkireddy 
60*4ec4762dSRahul Lakkireddy 	return 0;
61*4ec4762dSRahul Lakkireddy }
62*4ec4762dSRahul Lakkireddy 
63*4ec4762dSRahul Lakkireddy static int cxgb4_matchall_alloc_tc(struct net_device *dev,
64*4ec4762dSRahul Lakkireddy 				   struct tc_cls_matchall_offload *cls)
65*4ec4762dSRahul Lakkireddy {
66*4ec4762dSRahul Lakkireddy 	struct ch_sched_params p = {
67*4ec4762dSRahul Lakkireddy 		.type = SCHED_CLASS_TYPE_PACKET,
68*4ec4762dSRahul Lakkireddy 		.u.params.level = SCHED_CLASS_LEVEL_CH_RL,
69*4ec4762dSRahul Lakkireddy 		.u.params.mode = SCHED_CLASS_MODE_CLASS,
70*4ec4762dSRahul Lakkireddy 		.u.params.rateunit = SCHED_CLASS_RATEUNIT_BITS,
71*4ec4762dSRahul Lakkireddy 		.u.params.ratemode = SCHED_CLASS_RATEMODE_ABS,
72*4ec4762dSRahul Lakkireddy 		.u.params.class = SCHED_CLS_NONE,
73*4ec4762dSRahul Lakkireddy 		.u.params.minrate = 0,
74*4ec4762dSRahul Lakkireddy 		.u.params.weight = 0,
75*4ec4762dSRahul Lakkireddy 		.u.params.pktsize = dev->mtu,
76*4ec4762dSRahul Lakkireddy 	};
77*4ec4762dSRahul Lakkireddy 	struct netlink_ext_ack *extack = cls->common.extack;
78*4ec4762dSRahul Lakkireddy 	struct cxgb4_tc_port_matchall *tc_port_matchall;
79*4ec4762dSRahul Lakkireddy 	struct port_info *pi = netdev2pinfo(dev);
80*4ec4762dSRahul Lakkireddy 	struct adapter *adap = netdev2adap(dev);
81*4ec4762dSRahul Lakkireddy 	struct flow_action_entry *entry;
82*4ec4762dSRahul Lakkireddy 	struct sched_class *e;
83*4ec4762dSRahul Lakkireddy 	u32 i;
84*4ec4762dSRahul Lakkireddy 
85*4ec4762dSRahul Lakkireddy 	tc_port_matchall = &adap->tc_matchall->port_matchall[pi->port_id];
86*4ec4762dSRahul Lakkireddy 
87*4ec4762dSRahul Lakkireddy 	flow_action_for_each(i, entry, &cls->rule->action)
88*4ec4762dSRahul Lakkireddy 		if (entry->id == FLOW_ACTION_POLICE)
89*4ec4762dSRahul Lakkireddy 			break;
90*4ec4762dSRahul Lakkireddy 
91*4ec4762dSRahul Lakkireddy 	/* Convert from bytes per second to Kbps */
92*4ec4762dSRahul Lakkireddy 	p.u.params.maxrate = div_u64(entry->police.rate_bytes_ps * 8, 1000);
93*4ec4762dSRahul Lakkireddy 	p.u.params.channel = pi->tx_chan;
94*4ec4762dSRahul Lakkireddy 	e = cxgb4_sched_class_alloc(dev, &p);
95*4ec4762dSRahul Lakkireddy 	if (!e) {
96*4ec4762dSRahul Lakkireddy 		NL_SET_ERR_MSG_MOD(extack,
97*4ec4762dSRahul Lakkireddy 				   "No free traffic class available for policing action");
98*4ec4762dSRahul Lakkireddy 		return -ENOMEM;
99*4ec4762dSRahul Lakkireddy 	}
100*4ec4762dSRahul Lakkireddy 
101*4ec4762dSRahul Lakkireddy 	tc_port_matchall->egress.hwtc = e->idx;
102*4ec4762dSRahul Lakkireddy 	tc_port_matchall->egress.cookie = cls->cookie;
103*4ec4762dSRahul Lakkireddy 	tc_port_matchall->egress.state = CXGB4_MATCHALL_STATE_ENABLED;
104*4ec4762dSRahul Lakkireddy 	return 0;
105*4ec4762dSRahul Lakkireddy }
106*4ec4762dSRahul Lakkireddy 
107*4ec4762dSRahul Lakkireddy static void cxgb4_matchall_free_tc(struct net_device *dev)
108*4ec4762dSRahul Lakkireddy {
109*4ec4762dSRahul Lakkireddy 	struct cxgb4_tc_port_matchall *tc_port_matchall;
110*4ec4762dSRahul Lakkireddy 	struct port_info *pi = netdev2pinfo(dev);
111*4ec4762dSRahul Lakkireddy 	struct adapter *adap = netdev2adap(dev);
112*4ec4762dSRahul Lakkireddy 
113*4ec4762dSRahul Lakkireddy 	tc_port_matchall = &adap->tc_matchall->port_matchall[pi->port_id];
114*4ec4762dSRahul Lakkireddy 	cxgb4_sched_class_free(dev, tc_port_matchall->egress.hwtc);
115*4ec4762dSRahul Lakkireddy 
116*4ec4762dSRahul Lakkireddy 	tc_port_matchall->egress.hwtc = SCHED_CLS_NONE;
117*4ec4762dSRahul Lakkireddy 	tc_port_matchall->egress.cookie = 0;
118*4ec4762dSRahul Lakkireddy 	tc_port_matchall->egress.state = CXGB4_MATCHALL_STATE_DISABLED;
119*4ec4762dSRahul Lakkireddy }
120*4ec4762dSRahul Lakkireddy 
121*4ec4762dSRahul Lakkireddy int cxgb4_tc_matchall_replace(struct net_device *dev,
122*4ec4762dSRahul Lakkireddy 			      struct tc_cls_matchall_offload *cls_matchall)
123*4ec4762dSRahul Lakkireddy {
124*4ec4762dSRahul Lakkireddy 	struct netlink_ext_ack *extack = cls_matchall->common.extack;
125*4ec4762dSRahul Lakkireddy 	struct cxgb4_tc_port_matchall *tc_port_matchall;
126*4ec4762dSRahul Lakkireddy 	struct port_info *pi = netdev2pinfo(dev);
127*4ec4762dSRahul Lakkireddy 	struct adapter *adap = netdev2adap(dev);
128*4ec4762dSRahul Lakkireddy 	int ret;
129*4ec4762dSRahul Lakkireddy 
130*4ec4762dSRahul Lakkireddy 	tc_port_matchall = &adap->tc_matchall->port_matchall[pi->port_id];
131*4ec4762dSRahul Lakkireddy 	if (tc_port_matchall->egress.state == CXGB4_MATCHALL_STATE_ENABLED) {
132*4ec4762dSRahul Lakkireddy 		NL_SET_ERR_MSG_MOD(extack,
133*4ec4762dSRahul Lakkireddy 				   "Only 1 Egress MATCHALL can be offloaded");
134*4ec4762dSRahul Lakkireddy 		return -ENOMEM;
135*4ec4762dSRahul Lakkireddy 	}
136*4ec4762dSRahul Lakkireddy 
137*4ec4762dSRahul Lakkireddy 	ret = cxgb4_matchall_egress_validate(dev, cls_matchall);
138*4ec4762dSRahul Lakkireddy 	if (ret)
139*4ec4762dSRahul Lakkireddy 		return ret;
140*4ec4762dSRahul Lakkireddy 
141*4ec4762dSRahul Lakkireddy 	return cxgb4_matchall_alloc_tc(dev, cls_matchall);
142*4ec4762dSRahul Lakkireddy }
143*4ec4762dSRahul Lakkireddy 
144*4ec4762dSRahul Lakkireddy int cxgb4_tc_matchall_destroy(struct net_device *dev,
145*4ec4762dSRahul Lakkireddy 			      struct tc_cls_matchall_offload *cls_matchall)
146*4ec4762dSRahul Lakkireddy {
147*4ec4762dSRahul Lakkireddy 	struct cxgb4_tc_port_matchall *tc_port_matchall;
148*4ec4762dSRahul Lakkireddy 	struct port_info *pi = netdev2pinfo(dev);
149*4ec4762dSRahul Lakkireddy 	struct adapter *adap = netdev2adap(dev);
150*4ec4762dSRahul Lakkireddy 
151*4ec4762dSRahul Lakkireddy 	tc_port_matchall = &adap->tc_matchall->port_matchall[pi->port_id];
152*4ec4762dSRahul Lakkireddy 	if (cls_matchall->cookie != tc_port_matchall->egress.cookie)
153*4ec4762dSRahul Lakkireddy 		return -ENOENT;
154*4ec4762dSRahul Lakkireddy 
155*4ec4762dSRahul Lakkireddy 	cxgb4_matchall_free_tc(dev);
156*4ec4762dSRahul Lakkireddy 	return 0;
157*4ec4762dSRahul Lakkireddy }
158*4ec4762dSRahul Lakkireddy 
159*4ec4762dSRahul Lakkireddy static void cxgb4_matchall_disable_offload(struct net_device *dev)
160*4ec4762dSRahul Lakkireddy {
161*4ec4762dSRahul Lakkireddy 	struct cxgb4_tc_port_matchall *tc_port_matchall;
162*4ec4762dSRahul Lakkireddy 	struct port_info *pi = netdev2pinfo(dev);
163*4ec4762dSRahul Lakkireddy 	struct adapter *adap = netdev2adap(dev);
164*4ec4762dSRahul Lakkireddy 
165*4ec4762dSRahul Lakkireddy 	tc_port_matchall = &adap->tc_matchall->port_matchall[pi->port_id];
166*4ec4762dSRahul Lakkireddy 	if (tc_port_matchall->egress.state == CXGB4_MATCHALL_STATE_ENABLED)
167*4ec4762dSRahul Lakkireddy 		cxgb4_matchall_free_tc(dev);
168*4ec4762dSRahul Lakkireddy }
169*4ec4762dSRahul Lakkireddy 
170*4ec4762dSRahul Lakkireddy int cxgb4_init_tc_matchall(struct adapter *adap)
171*4ec4762dSRahul Lakkireddy {
172*4ec4762dSRahul Lakkireddy 	struct cxgb4_tc_port_matchall *tc_port_matchall;
173*4ec4762dSRahul Lakkireddy 	struct cxgb4_tc_matchall *tc_matchall;
174*4ec4762dSRahul Lakkireddy 	int ret;
175*4ec4762dSRahul Lakkireddy 
176*4ec4762dSRahul Lakkireddy 	tc_matchall = kzalloc(sizeof(*tc_matchall), GFP_KERNEL);
177*4ec4762dSRahul Lakkireddy 	if (!tc_matchall)
178*4ec4762dSRahul Lakkireddy 		return -ENOMEM;
179*4ec4762dSRahul Lakkireddy 
180*4ec4762dSRahul Lakkireddy 	tc_port_matchall = kcalloc(adap->params.nports,
181*4ec4762dSRahul Lakkireddy 				   sizeof(*tc_port_matchall),
182*4ec4762dSRahul Lakkireddy 				   GFP_KERNEL);
183*4ec4762dSRahul Lakkireddy 	if (!tc_port_matchall) {
184*4ec4762dSRahul Lakkireddy 		ret = -ENOMEM;
185*4ec4762dSRahul Lakkireddy 		goto out_free_matchall;
186*4ec4762dSRahul Lakkireddy 	}
187*4ec4762dSRahul Lakkireddy 
188*4ec4762dSRahul Lakkireddy 	tc_matchall->port_matchall = tc_port_matchall;
189*4ec4762dSRahul Lakkireddy 	adap->tc_matchall = tc_matchall;
190*4ec4762dSRahul Lakkireddy 	return 0;
191*4ec4762dSRahul Lakkireddy 
192*4ec4762dSRahul Lakkireddy out_free_matchall:
193*4ec4762dSRahul Lakkireddy 	kfree(tc_matchall);
194*4ec4762dSRahul Lakkireddy 	return ret;
195*4ec4762dSRahul Lakkireddy }
196*4ec4762dSRahul Lakkireddy 
197*4ec4762dSRahul Lakkireddy void cxgb4_cleanup_tc_matchall(struct adapter *adap)
198*4ec4762dSRahul Lakkireddy {
199*4ec4762dSRahul Lakkireddy 	u8 i;
200*4ec4762dSRahul Lakkireddy 
201*4ec4762dSRahul Lakkireddy 	if (adap->tc_matchall) {
202*4ec4762dSRahul Lakkireddy 		if (adap->tc_matchall->port_matchall) {
203*4ec4762dSRahul Lakkireddy 			for (i = 0; i < adap->params.nports; i++) {
204*4ec4762dSRahul Lakkireddy 				struct net_device *dev = adap->port[i];
205*4ec4762dSRahul Lakkireddy 
206*4ec4762dSRahul Lakkireddy 				if (dev)
207*4ec4762dSRahul Lakkireddy 					cxgb4_matchall_disable_offload(dev);
208*4ec4762dSRahul Lakkireddy 			}
209*4ec4762dSRahul Lakkireddy 			kfree(adap->tc_matchall->port_matchall);
210*4ec4762dSRahul Lakkireddy 		}
211*4ec4762dSRahul Lakkireddy 		kfree(adap->tc_matchall);
212*4ec4762dSRahul Lakkireddy 	}
213*4ec4762dSRahul Lakkireddy }
214