treewide: remove redundant IS_ERR() before error code check
[linux/fpc-iii.git] / drivers / net / ethernet / mscc / ocelot_tc.c
bloba4f7fbd765075eb59b32d212e61bdf0e6ca4c0e9
1 // SPDX-License-Identifier: (GPL-2.0 OR MIT)
2 /* Microsemi Ocelot Switch TC driver
4 * Copyright (c) 2019 Microsemi Corporation
5 */
7 #include "ocelot_tc.h"
8 #include "ocelot_police.h"
9 #include "ocelot_ace.h"
10 #include <net/pkt_cls.h>
12 static int ocelot_setup_tc_cls_matchall(struct ocelot_port_private *priv,
13 struct tc_cls_matchall_offload *f,
14 bool ingress)
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;
23 netdev_dbg(priv->dev, "%s: port %u command %d cookie %lu\n",
24 __func__, port, f->command, f->cookie);
26 if (!ingress) {
27 NL_SET_ERR_MSG_MOD(extack, "Only ingress is supported");
28 return -EOPNOTSUPP;
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;
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;
45 action = &f->rule->action.entries[0];
47 if (action->id != FLOW_ACTION_POLICE) {
48 NL_SET_ERR_MSG_MOD(extack, "Unsupported action");
49 return -EOPNOTSUPP;
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;
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);
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;
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;
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;
82 priv->tc.police_id = 0;
83 priv->tc.offload_cnt--;
84 return 0;
85 case TC_CLSMATCHALL_STATS: /* fall through */
86 default:
87 return -EOPNOTSUPP;
91 static int ocelot_setup_tc_block_cb(enum tc_setup_type type,
92 void *type_data,
93 void *cb_priv, bool ingress)
95 struct ocelot_port_private *priv = cb_priv;
97 if (!tc_cls_can_offload_and_chain0(priv->dev, type_data))
98 return -EOPNOTSUPP;
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");
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");
113 return -EOPNOTSUPP;
117 static int ocelot_setup_tc_block_cb_ig(enum tc_setup_type type,
118 void *type_data,
119 void *cb_priv)
121 return ocelot_setup_tc_block_cb(type, type_data,
122 cb_priv, true);
125 static int ocelot_setup_tc_block_cb_eg(enum tc_setup_type type,
126 void *type_data,
127 void *cb_priv)
129 return ocelot_setup_tc_block_cb(type, type_data,
130 cb_priv, false);
133 static LIST_HEAD(ocelot_block_cb_list);
135 static int ocelot_setup_tc_block(struct ocelot_port_private *priv,
136 struct flow_block_offload *f)
138 struct flow_block_cb *block_cb;
139 flow_setup_cb_t *cb;
140 int err;
142 netdev_dbg(priv->dev, "tc_block command %d, binder_type %d\n",
143 f->command, f->binder_type);
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;
154 f->driver_block_list = &ocelot_block_cb_list;
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;
161 block_cb = flow_block_cb_alloc(cb, priv, priv, NULL);
162 if (IS_ERR(block_cb))
163 return PTR_ERR(block_cb);
165 err = ocelot_setup_tc_block_flower_bind(priv, f);
166 if (err < 0) {
167 flow_block_cb_free(block_cb);
168 return err;
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;
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;
187 int ocelot_setup_tc(struct net_device *dev, enum tc_setup_type type,
188 void *type_data)
190 struct ocelot_port_private *priv = netdev_priv(dev);
192 switch (type) {
193 case TC_SETUP_BLOCK:
194 return ocelot_setup_tc_block(priv, type_data);
195 default:
196 return -EOPNOTSUPP;
198 return 0;