1 // SPDX-License-Identifier: (GPL-2.0 OR MIT)
2 /* Microsemi Ocelot Switch TC driver
3  *
4  * Copyright (c) 2019 Microsemi Corporation
5  */
6 
7 #include "ocelot_tc.h"
8 #include "ocelot_police.h"
9 #include "ocelot_ace.h"
10 #include <net/pkt_cls.h>
11 
ocelot_setup_tc_cls_matchall(struct ocelot_port * port,struct tc_cls_matchall_offload * f,bool ingress)12 static int ocelot_setup_tc_cls_matchall(struct ocelot_port *port,
13 					struct tc_cls_matchall_offload *f,
14 					bool ingress)
15 {
16 	struct netlink_ext_ack *extack = f->common.extack;
17 	struct ocelot_policer pol = { 0 };
18 	struct flow_action_entry *action;
19 	int err;
20 
21 	netdev_dbg(port->dev, "%s: port %u command %d cookie %lu\n",
22 		   __func__, port->chip_port, f->command, f->cookie);
23 
24 	if (!ingress) {
25 		NL_SET_ERR_MSG_MOD(extack, "Only ingress is supported");
26 		return -EOPNOTSUPP;
27 	}
28 
29 	switch (f->command) {
30 	case TC_CLSMATCHALL_REPLACE:
31 		if (!flow_offload_has_one_action(&f->rule->action)) {
32 			NL_SET_ERR_MSG_MOD(extack,
33 					   "Only one action is supported");
34 			return -EOPNOTSUPP;
35 		}
36 
37 		if (port->tc.block_shared) {
38 			NL_SET_ERR_MSG_MOD(extack,
39 					   "Rate limit is not supported on shared blocks");
40 			return -EOPNOTSUPP;
41 		}
42 
43 		action = &f->rule->action.entries[0];
44 
45 		if (action->id != FLOW_ACTION_POLICE) {
46 			NL_SET_ERR_MSG_MOD(extack, "Unsupported action");
47 			return -EOPNOTSUPP;
48 		}
49 
50 		if (port->tc.police_id && port->tc.police_id != f->cookie) {
51 			NL_SET_ERR_MSG_MOD(extack,
52 					   "Only one policer per port is supported\n");
53 			return -EEXIST;
54 		}
55 
56 		pol.rate = (u32)div_u64(action->police.rate_bytes_ps, 1000) * 8;
57 		pol.burst = (u32)div_u64(action->police.rate_bytes_ps *
58 					 PSCHED_NS2TICKS(action->police.burst),
59 					 PSCHED_TICKS_PER_SEC);
60 
61 		err = ocelot_port_policer_add(port, &pol);
62 		if (err) {
63 			NL_SET_ERR_MSG_MOD(extack, "Could not add policer\n");
64 			return err;
65 		}
66 
67 		port->tc.police_id = f->cookie;
68 		port->tc.offload_cnt++;
69 		return 0;
70 	case TC_CLSMATCHALL_DESTROY:
71 		if (port->tc.police_id != f->cookie)
72 			return -ENOENT;
73 
74 		err = ocelot_port_policer_del(port);
75 		if (err) {
76 			NL_SET_ERR_MSG_MOD(extack,
77 					   "Could not delete policer\n");
78 			return err;
79 		}
80 		port->tc.police_id = 0;
81 		port->tc.offload_cnt--;
82 		return 0;
83 	case TC_CLSMATCHALL_STATS: /* fall through */
84 	default:
85 		return -EOPNOTSUPP;
86 	}
87 }
88 
ocelot_setup_tc_block_cb(enum tc_setup_type type,void * type_data,void * cb_priv,bool ingress)89 static int ocelot_setup_tc_block_cb(enum tc_setup_type type,
90 				    void *type_data,
91 				    void *cb_priv, bool ingress)
92 {
93 	struct ocelot_port *port = cb_priv;
94 
95 	if (!tc_cls_can_offload_and_chain0(port->dev, type_data))
96 		return -EOPNOTSUPP;
97 
98 	switch (type) {
99 	case TC_SETUP_CLSMATCHALL:
100 		netdev_dbg(port->dev, "tc_block_cb: TC_SETUP_CLSMATCHALL %s\n",
101 			   ingress ? "ingress" : "egress");
102 
103 		return ocelot_setup_tc_cls_matchall(port, type_data, ingress);
104 	case TC_SETUP_CLSFLOWER:
105 		return 0;
106 	default:
107 		netdev_dbg(port->dev, "tc_block_cb: type %d %s\n",
108 			   type,
109 			   ingress ? "ingress" : "egress");
110 
111 		return -EOPNOTSUPP;
112 	}
113 }
114 
ocelot_setup_tc_block_cb_ig(enum tc_setup_type type,void * type_data,void * cb_priv)115 static int ocelot_setup_tc_block_cb_ig(enum tc_setup_type type,
116 				       void *type_data,
117 				       void *cb_priv)
118 {
119 	return ocelot_setup_tc_block_cb(type, type_data,
120 					cb_priv, true);
121 }
122 
ocelot_setup_tc_block_cb_eg(enum tc_setup_type type,void * type_data,void * cb_priv)123 static int ocelot_setup_tc_block_cb_eg(enum tc_setup_type type,
124 				       void *type_data,
125 				       void *cb_priv)
126 {
127 	return ocelot_setup_tc_block_cb(type, type_data,
128 					cb_priv, false);
129 }
130 
131 static LIST_HEAD(ocelot_block_cb_list);
132 
ocelot_setup_tc_block(struct ocelot_port * port,struct flow_block_offload * f)133 static int ocelot_setup_tc_block(struct ocelot_port *port,
134 				 struct flow_block_offload *f)
135 {
136 	struct flow_block_cb *block_cb;
137 	flow_setup_cb_t *cb;
138 	int err;
139 
140 	netdev_dbg(port->dev, "tc_block command %d, binder_type %d\n",
141 		   f->command, f->binder_type);
142 
143 	if (f->binder_type == FLOW_BLOCK_BINDER_TYPE_CLSACT_INGRESS) {
144 		cb = ocelot_setup_tc_block_cb_ig;
145 		port->tc.block_shared = f->block_shared;
146 	} else if (f->binder_type == FLOW_BLOCK_BINDER_TYPE_CLSACT_EGRESS) {
147 		cb = ocelot_setup_tc_block_cb_eg;
148 	} else {
149 		return -EOPNOTSUPP;
150 	}
151 
152 	f->driver_block_list = &ocelot_block_cb_list;
153 
154 	switch (f->command) {
155 	case FLOW_BLOCK_BIND:
156 		if (flow_block_cb_is_busy(cb, port, &ocelot_block_cb_list))
157 			return -EBUSY;
158 
159 		block_cb = flow_block_cb_alloc(cb, port, port, NULL);
160 		if (IS_ERR(block_cb))
161 			return PTR_ERR(block_cb);
162 
163 		err = ocelot_setup_tc_block_flower_bind(port, f);
164 		if (err < 0) {
165 			flow_block_cb_free(block_cb);
166 			return err;
167 		}
168 		flow_block_cb_add(block_cb, f);
169 		list_add_tail(&block_cb->driver_list, f->driver_block_list);
170 		return 0;
171 	case FLOW_BLOCK_UNBIND:
172 		block_cb = flow_block_cb_lookup(f->block, cb, port);
173 		if (!block_cb)
174 			return -ENOENT;
175 
176 		ocelot_setup_tc_block_flower_unbind(port, f);
177 		flow_block_cb_remove(block_cb, f);
178 		list_del(&block_cb->driver_list);
179 		return 0;
180 	default:
181 		return -EOPNOTSUPP;
182 	}
183 }
184 
ocelot_setup_tc(struct net_device * dev,enum tc_setup_type type,void * type_data)185 int ocelot_setup_tc(struct net_device *dev, enum tc_setup_type type,
186 		    void *type_data)
187 {
188 	struct ocelot_port *port = netdev_priv(dev);
189 
190 	switch (type) {
191 	case TC_SETUP_BLOCK:
192 		return ocelot_setup_tc_block(port, type_data);
193 	default:
194 		return -EOPNOTSUPP;
195 	}
196 	return 0;
197 }
198