KVM: x86: Manually calculate reserved bits when loading PDPTRS
[linux-2.6-microblaze.git] / drivers / net / ethernet / mscc / ocelot_tc.c
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
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
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
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
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
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         tc_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(f->net, 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, 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
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 }