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