blob: 16a6db71ca5e7eeabb53bd7aed58d7d130ffe890 [file] [log] [blame]
Joergen Andreasen2c1d0292019-05-28 14:49:17 +02001// 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"
Horatiu Vulturfe3490e2019-05-31 09:16:57 +02009#include "ocelot_ace.h"
Joergen Andreasen2c1d0292019-05-28 14:49:17 +020010#include <net/pkt_cls.h>
11
12static 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
89static 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:
Horatiu Vulturfe3490e2019-05-31 09:16:57 +0200105 return 0;
Joergen Andreasen2c1d0292019-05-28 14:49:17 +0200106 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
115static 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
123static 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
Pablo Neira Ayuso955bcb62019-07-09 22:55:46 +0200131static LIST_HEAD(ocelot_block_cb_list);
132
Joergen Andreasen2c1d0292019-05-28 14:49:17 +0200133static int ocelot_setup_tc_block(struct ocelot_port *port,
Pablo Neira Ayuso955bcb62019-07-09 22:55:46 +0200134 struct flow_block_offload *f)
Joergen Andreasen2c1d0292019-05-28 14:49:17 +0200135{
Pablo Neira Ayuso955bcb62019-07-09 22:55:46 +0200136 struct flow_block_cb *block_cb;
Pablo Neira Ayusoa7323312019-07-19 18:20:15 +0200137 flow_setup_cb_t *cb;
Pablo Neira Ayuso955bcb62019-07-09 22:55:46 +0200138 int err;
Joergen Andreasen2c1d0292019-05-28 14:49:17 +0200139
140 netdev_dbg(port->dev, "tc_block command %d, binder_type %d\n",
141 f->command, f->binder_type);
142
Pablo Neira Ayuso32f8c402019-07-09 22:55:41 +0200143 if (f->binder_type == FLOW_BLOCK_BINDER_TYPE_CLSACT_INGRESS) {
Joergen Andreasen2c1d0292019-05-28 14:49:17 +0200144 cb = ocelot_setup_tc_block_cb_ig;
Pablo Neira Ayuso955bcb62019-07-09 22:55:46 +0200145 port->tc.block_shared = f->block_shared;
Pablo Neira Ayuso32f8c402019-07-09 22:55:41 +0200146 } else if (f->binder_type == FLOW_BLOCK_BINDER_TYPE_CLSACT_EGRESS) {
Joergen Andreasen2c1d0292019-05-28 14:49:17 +0200147 cb = ocelot_setup_tc_block_cb_eg;
148 } else {
149 return -EOPNOTSUPP;
150 }
151
Pablo Neira Ayuso955bcb62019-07-09 22:55:46 +0200152 f->driver_block_list = &ocelot_block_cb_list;
153
Joergen Andreasen2c1d0292019-05-28 14:49:17 +0200154 switch (f->command) {
Pablo Neira Ayuso9c0e1892019-07-09 22:55:40 +0200155 case FLOW_BLOCK_BIND:
Pablo Neira Ayuso0d4fd022019-07-09 22:55:48 +0200156 if (flow_block_cb_is_busy(cb, port, &ocelot_block_cb_list))
157 return -EBUSY;
158
Pablo Neira Ayuso0c7294d2019-07-19 18:20:14 +0200159 block_cb = flow_block_cb_alloc(cb, port, port, NULL);
Pablo Neira Ayuso955bcb62019-07-09 22:55:46 +0200160 if (IS_ERR(block_cb))
161 return PTR_ERR(block_cb);
Horatiu Vulturfe3490e2019-05-31 09:16:57 +0200162
Pablo Neira Ayuso955bcb62019-07-09 22:55:46 +0200163 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;
Pablo Neira Ayuso9c0e1892019-07-09 22:55:40 +0200171 case FLOW_BLOCK_UNBIND:
Pablo Neira Ayuso14bfb132019-07-19 18:20:16 +0200172 block_cb = flow_block_cb_lookup(f->block, cb, port);
Pablo Neira Ayuso955bcb62019-07-09 22:55:46 +0200173 if (!block_cb)
174 return -ENOENT;
175
Horatiu Vulturfe3490e2019-05-31 09:16:57 +0200176 ocelot_setup_tc_block_flower_unbind(port, f);
Pablo Neira Ayuso955bcb62019-07-09 22:55:46 +0200177 flow_block_cb_remove(block_cb, f);
178 list_del(&block_cb->driver_list);
Joergen Andreasen2c1d0292019-05-28 14:49:17 +0200179 return 0;
180 default:
181 return -EOPNOTSUPP;
182 }
183}
184
185int 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}