]> git.proxmox.com Git - mirror_ubuntu-jammy-kernel.git/blame - drivers/net/ethernet/mscc/ocelot_tc.c
Merge git://git.kernel.org/pub/scm/linux/kernel/git/netdev/net
[mirror_ubuntu-jammy-kernel.git] / drivers / net / ethernet / mscc / ocelot_tc.c
CommitLineData
2c1d029a
JA
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"
fe3490e6 9#include "ocelot_ace.h"
2c1d029a
JA
10#include <net/pkt_cls.h>
11
004d44f6 12static int ocelot_setup_tc_cls_matchall(struct ocelot_port_private *priv,
2c1d029a
JA
13 struct tc_cls_matchall_offload *f,
14 bool ingress)
15{
16 struct netlink_ext_ack *extack = f->common.extack;
004d44f6 17 struct ocelot *ocelot = priv->port.ocelot;
2c1d029a
JA
18 struct ocelot_policer pol = { 0 };
19 struct flow_action_entry *action;
004d44f6 20 int port = priv->chip_port;
2c1d029a
JA
21 int err;
22
004d44f6
VO
23 netdev_dbg(priv->dev, "%s: port %u command %d cookie %lu\n",
24 __func__, port, f->command, f->cookie);
2c1d029a
JA
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
004d44f6 39 if (priv->tc.block_shared) {
2c1d029a
JA
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
004d44f6 52 if (priv->tc.police_id && priv->tc.police_id != f->cookie) {
2c1d029a
JA
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
004d44f6 63 err = ocelot_port_policer_add(ocelot, port, &pol);
2c1d029a
JA
64 if (err) {
65 NL_SET_ERR_MSG_MOD(extack, "Could not add policer\n");
66 return err;
67 }
68
004d44f6
VO
69 priv->tc.police_id = f->cookie;
70 priv->tc.offload_cnt++;
2c1d029a
JA
71 return 0;
72 case TC_CLSMATCHALL_DESTROY:
004d44f6 73 if (priv->tc.police_id != f->cookie)
2c1d029a
JA
74 return -ENOENT;
75
004d44f6 76 err = ocelot_port_policer_del(ocelot, port);
2c1d029a
JA
77 if (err) {
78 NL_SET_ERR_MSG_MOD(extack,
79 "Could not delete policer\n");
80 return err;
81 }
004d44f6
VO
82 priv->tc.police_id = 0;
83 priv->tc.offload_cnt--;
2c1d029a
JA
84 return 0;
85 case TC_CLSMATCHALL_STATS: /* fall through */
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{
004d44f6 95 struct ocelot_port_private *priv = cb_priv;
2c1d029a 96
004d44f6 97 if (!tc_cls_can_offload_and_chain0(priv->dev, type_data))
2c1d029a
JA
98 return -EOPNOTSUPP;
99
100 switch (type) {
101 case TC_SETUP_CLSMATCHALL:
004d44f6 102 netdev_dbg(priv->dev, "tc_block_cb: TC_SETUP_CLSMATCHALL %s\n",
2c1d029a
JA
103 ingress ? "ingress" : "egress");
104
004d44f6 105 return ocelot_setup_tc_cls_matchall(priv, type_data, ingress);
2c1d029a 106 case TC_SETUP_CLSFLOWER:
fe3490e6 107 return 0;
2c1d029a 108 default:
004d44f6 109 netdev_dbg(priv->dev, "tc_block_cb: type %d %s\n",
2c1d029a
JA
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
955bcb6e
PNA
133static LIST_HEAD(ocelot_block_cb_list);
134
004d44f6 135static int ocelot_setup_tc_block(struct ocelot_port_private *priv,
955bcb6e 136 struct flow_block_offload *f)
2c1d029a 137{
955bcb6e 138 struct flow_block_cb *block_cb;
a7323311 139 flow_setup_cb_t *cb;
955bcb6e 140 int err;
2c1d029a 141
004d44f6 142 netdev_dbg(priv->dev, "tc_block command %d, binder_type %d\n",
2c1d029a
JA
143 f->command, f->binder_type);
144
32f8c409 145 if (f->binder_type == FLOW_BLOCK_BINDER_TYPE_CLSACT_INGRESS) {
2c1d029a 146 cb = ocelot_setup_tc_block_cb_ig;
004d44f6 147 priv->tc.block_shared = f->block_shared;
32f8c409 148 } else if (f->binder_type == FLOW_BLOCK_BINDER_TYPE_CLSACT_EGRESS) {
2c1d029a
JA
149 cb = ocelot_setup_tc_block_cb_eg;
150 } else {
151 return -EOPNOTSUPP;
152 }
153
955bcb6e
PNA
154 f->driver_block_list = &ocelot_block_cb_list;
155
2c1d029a 156 switch (f->command) {
9c0e189e 157 case FLOW_BLOCK_BIND:
004d44f6 158 if (flow_block_cb_is_busy(cb, priv, &ocelot_block_cb_list))
0d4fd02e
PNA
159 return -EBUSY;
160
004d44f6 161 block_cb = flow_block_cb_alloc(cb, priv, priv, NULL);
955bcb6e
PNA
162 if (IS_ERR(block_cb))
163 return PTR_ERR(block_cb);
fe3490e6 164
004d44f6 165 err = ocelot_setup_tc_block_flower_bind(priv, f);
955bcb6e
PNA
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;
9c0e189e 173 case FLOW_BLOCK_UNBIND:
004d44f6 174 block_cb = flow_block_cb_lookup(f->block, cb, priv);
955bcb6e
PNA
175 if (!block_cb)
176 return -ENOENT;
177
004d44f6 178 ocelot_setup_tc_block_flower_unbind(priv, f);
955bcb6e
PNA
179 flow_block_cb_remove(block_cb, f);
180 list_del(&block_cb->driver_list);
2c1d029a
JA
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{
004d44f6 190 struct ocelot_port_private *priv = netdev_priv(dev);
2c1d029a
JA
191
192 switch (type) {
193 case TC_SETUP_BLOCK:
004d44f6 194 return ocelot_setup_tc_block(priv, type_data);
2c1d029a
JA
195 default:
196 return -EOPNOTSUPP;
197 }
198 return 0;
199}