Commit | Line | Data |
---|---|---|
c4c8f39a JK |
1 | // SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause) |
2 | /* | |
3 | * Copyright (C) 2018 Netronome Systems, Inc. | |
4 | * | |
5 | * This software is dual licensed under the GNU General License Version 2, | |
6 | * June 1991 as shown in the file COPYING in the top-level directory of this | |
7 | * source tree or the BSD 2-Clause License provided below. You have the | |
8 | * option to license this software under the complete terms of either license. | |
9 | * | |
10 | * The BSD 2-Clause License: | |
11 | * | |
12 | * Redistribution and use in source and binary forms, with or | |
13 | * without modification, are permitted provided that the following | |
14 | * conditions are met: | |
15 | * | |
16 | * 1. Redistributions of source code must retain the above | |
17 | * copyright notice, this list of conditions and the following | |
18 | * disclaimer. | |
19 | * | |
20 | * 2. Redistributions in binary form must reproduce the above | |
21 | * copyright notice, this list of conditions and the following | |
22 | * disclaimer in the documentation and/or other materials | |
23 | * provided with the distribution. | |
24 | * | |
25 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, | |
26 | * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF | |
27 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND | |
28 | * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS | |
29 | * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN | |
30 | * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN | |
31 | * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE | |
32 | * SOFTWARE. | |
33 | */ | |
34 | ||
d05d902e | 35 | #include <linux/bitfield.h> |
cc54dc28 | 36 | #include <linux/etherdevice.h> |
d05d902e JK |
37 | #include <linux/lockdep.h> |
38 | #include <linux/netdevice.h> | |
39 | #include <linux/rcupdate.h> | |
40 | #include <linux/slab.h> | |
8c8e6406 JK |
41 | #include <net/pkt_cls.h> |
42 | #include <net/pkt_sched.h> | |
cb89cac8 | 43 | #include <net/red.h> |
cc54dc28 JK |
44 | |
45 | #include "../nfpcore/nfp.h" | |
c4c8f39a JK |
46 | #include "../nfpcore/nfp_cpp.h" |
47 | #include "../nfpcore/nfp_nsp.h" | |
48 | #include "../nfp_app.h" | |
49 | #include "../nfp_main.h" | |
cc54dc28 | 50 | #include "../nfp_net.h" |
d05d902e | 51 | #include "../nfp_net_repr.h" |
cc54dc28 | 52 | #include "../nfp_port.h" |
c4c8f39a JK |
53 | #include "main.h" |
54 | ||
d05d902e JK |
55 | static u32 nfp_abm_portid(enum nfp_repr_type rtype, unsigned int id) |
56 | { | |
57 | return FIELD_PREP(NFP_ABM_PORTID_TYPE, rtype) | | |
58 | FIELD_PREP(NFP_ABM_PORTID_ID, id); | |
59 | } | |
60 | ||
674cb229 JK |
61 | static int |
62 | __nfp_abm_reset_root(struct net_device *netdev, struct nfp_abm_link *alink, | |
63 | u32 handle, unsigned int qs, u32 init_val) | |
cb89cac8 | 64 | { |
674cb229 JK |
65 | struct nfp_port *port = nfp_port_from_netdev(netdev); |
66 | int ret; | |
cb89cac8 | 67 | |
674cb229 JK |
68 | ret = nfp_abm_ctrl_set_all_q_lvls(alink, init_val); |
69 | memset(alink->qdiscs, 0, sizeof(*alink->qdiscs) * alink->num_qdiscs); | |
cb89cac8 | 70 | |
674cb229 JK |
71 | alink->parent = handle; |
72 | alink->num_qdiscs = qs; | |
73 | port->tc_offload_cnt = qs; | |
cb89cac8 | 74 | |
674cb229 JK |
75 | return ret; |
76 | } | |
77 | ||
78 | static void | |
79 | nfp_abm_reset_root(struct net_device *netdev, struct nfp_abm_link *alink, | |
80 | u32 handle, unsigned int qs) | |
81 | { | |
82 | __nfp_abm_reset_root(netdev, alink, handle, qs, ~0); | |
83 | } | |
84 | ||
85 | static int | |
86 | nfp_abm_red_find(struct nfp_abm_link *alink, struct tc_red_qopt_offload *opt) | |
87 | { | |
88 | unsigned int i = TC_H_MIN(opt->parent) - 1; | |
89 | ||
90 | if (opt->parent == TC_H_ROOT) | |
91 | i = 0; | |
92 | else if (TC_H_MAJ(alink->parent) == TC_H_MAJ(opt->parent)) | |
93 | i = TC_H_MIN(opt->parent) - 1; | |
94 | else | |
95 | return -EOPNOTSUPP; | |
96 | ||
97 | if (i >= alink->num_qdiscs || opt->handle != alink->qdiscs[i].handle) | |
98 | return -EOPNOTSUPP; | |
99 | ||
100 | return i; | |
cb89cac8 JK |
101 | } |
102 | ||
8c8e6406 JK |
103 | static void |
104 | nfp_abm_red_destroy(struct net_device *netdev, struct nfp_abm_link *alink, | |
105 | u32 handle) | |
106 | { | |
674cb229 | 107 | unsigned int i; |
8c8e6406 | 108 | |
674cb229 JK |
109 | for (i = 0; i < alink->num_qdiscs; i++) |
110 | if (handle == alink->qdiscs[i].handle) | |
111 | break; | |
112 | if (i == alink->num_qdiscs) | |
8c8e6406 JK |
113 | return; |
114 | ||
674cb229 JK |
115 | if (alink->parent == TC_H_ROOT) { |
116 | nfp_abm_reset_root(netdev, alink, TC_H_ROOT, 0); | |
117 | } else { | |
118 | nfp_abm_ctrl_set_q_lvl(alink, i, ~0); | |
119 | memset(&alink->qdiscs[i], 0, sizeof(*alink->qdiscs)); | |
120 | } | |
8c8e6406 JK |
121 | } |
122 | ||
123 | static int | |
124 | nfp_abm_red_replace(struct net_device *netdev, struct nfp_abm_link *alink, | |
125 | struct tc_red_qopt_offload *opt) | |
126 | { | |
674cb229 JK |
127 | bool existing; |
128 | int i, err; | |
129 | ||
130 | i = nfp_abm_red_find(alink, opt); | |
131 | existing = i >= 0; | |
8c8e6406 JK |
132 | |
133 | if (opt->set.min != opt->set.max || !opt->set.is_ecn) { | |
134 | nfp_warn(alink->abm->app->cpp, | |
135 | "RED offload failed - unsupported parameters\n"); | |
136 | err = -EINVAL; | |
137 | goto err_destroy; | |
138 | } | |
8c8e6406 | 139 | |
674cb229 JK |
140 | if (existing) { |
141 | if (alink->parent == TC_H_ROOT) | |
142 | err = nfp_abm_ctrl_set_all_q_lvls(alink, opt->set.min); | |
143 | else | |
144 | err = nfp_abm_ctrl_set_q_lvl(alink, i, opt->set.min); | |
cb89cac8 JK |
145 | if (err) |
146 | goto err_destroy; | |
674cb229 | 147 | return 0; |
cb89cac8 JK |
148 | } |
149 | ||
674cb229 JK |
150 | if (opt->parent == TC_H_ROOT) { |
151 | i = 0; | |
152 | err = __nfp_abm_reset_root(netdev, alink, TC_H_ROOT, 1, | |
153 | opt->set.min); | |
154 | } else if (TC_H_MAJ(alink->parent) == TC_H_MAJ(opt->parent)) { | |
155 | i = TC_H_MIN(opt->parent) - 1; | |
156 | err = nfp_abm_ctrl_set_q_lvl(alink, i, opt->set.min); | |
157 | } else { | |
158 | return -EINVAL; | |
159 | } | |
160 | /* Set the handle to try full clean up, in case IO failed */ | |
161 | alink->qdiscs[i].handle = opt->handle; | |
162 | if (err) | |
163 | goto err_destroy; | |
164 | ||
165 | if (opt->parent == TC_H_ROOT) | |
166 | err = nfp_abm_ctrl_read_stats(alink, &alink->qdiscs[i].stats); | |
167 | else | |
168 | err = nfp_abm_ctrl_read_q_stats(alink, i, | |
169 | &alink->qdiscs[i].stats); | |
170 | if (err) | |
171 | goto err_destroy; | |
172 | ||
173 | if (opt->parent == TC_H_ROOT) | |
174 | err = nfp_abm_ctrl_read_xstats(alink, | |
175 | &alink->qdiscs[i].xstats); | |
176 | else | |
177 | err = nfp_abm_ctrl_read_q_xstats(alink, i, | |
178 | &alink->qdiscs[i].xstats); | |
179 | if (err) | |
180 | goto err_destroy; | |
181 | ||
182 | alink->qdiscs[i].stats.backlog_pkts = 0; | |
183 | alink->qdiscs[i].stats.backlog_bytes = 0; | |
8c8e6406 JK |
184 | |
185 | return 0; | |
186 | err_destroy: | |
cb89cac8 | 187 | /* If the qdisc keeps on living, but we can't offload undo changes */ |
674cb229 JK |
188 | if (existing) { |
189 | opt->set.qstats->qlen -= alink->qdiscs[i].stats.backlog_pkts; | |
cb89cac8 | 190 | opt->set.qstats->backlog -= |
674cb229 | 191 | alink->qdiscs[i].stats.backlog_bytes; |
cb89cac8 | 192 | } |
674cb229 JK |
193 | nfp_abm_red_destroy(netdev, alink, opt->handle); |
194 | ||
8c8e6406 JK |
195 | return err; |
196 | } | |
197 | ||
cb89cac8 JK |
198 | static void |
199 | nfp_abm_update_stats(struct nfp_alink_stats *new, struct nfp_alink_stats *old, | |
200 | struct tc_qopt_offload_stats *stats) | |
201 | { | |
202 | _bstats_update(stats->bstats, new->tx_bytes - old->tx_bytes, | |
203 | new->tx_pkts - old->tx_pkts); | |
204 | stats->qstats->qlen += new->backlog_pkts - old->backlog_pkts; | |
205 | stats->qstats->backlog += new->backlog_bytes - old->backlog_bytes; | |
206 | stats->qstats->overlimits += new->overlimits - old->overlimits; | |
207 | stats->qstats->drops += new->drops - old->drops; | |
208 | } | |
209 | ||
210 | static int | |
211 | nfp_abm_red_stats(struct nfp_abm_link *alink, struct tc_red_qopt_offload *opt) | |
212 | { | |
213 | struct nfp_alink_stats *prev_stats; | |
214 | struct nfp_alink_stats stats; | |
674cb229 | 215 | int i, err; |
cb89cac8 | 216 | |
674cb229 JK |
217 | i = nfp_abm_red_find(alink, opt); |
218 | if (i < 0) | |
219 | return i; | |
220 | prev_stats = &alink->qdiscs[i].stats; | |
cb89cac8 | 221 | |
674cb229 JK |
222 | if (alink->parent == TC_H_ROOT) |
223 | err = nfp_abm_ctrl_read_stats(alink, &stats); | |
224 | else | |
225 | err = nfp_abm_ctrl_read_q_stats(alink, i, &stats); | |
cb89cac8 JK |
226 | if (err) |
227 | return err; | |
228 | ||
229 | nfp_abm_update_stats(&stats, prev_stats, &opt->stats); | |
230 | ||
231 | *prev_stats = stats; | |
232 | ||
233 | return 0; | |
234 | } | |
235 | ||
236 | static int | |
237 | nfp_abm_red_xstats(struct nfp_abm_link *alink, struct tc_red_qopt_offload *opt) | |
238 | { | |
239 | struct nfp_alink_xstats *prev_xstats; | |
240 | struct nfp_alink_xstats xstats; | |
674cb229 | 241 | int i, err; |
cb89cac8 | 242 | |
674cb229 JK |
243 | i = nfp_abm_red_find(alink, opt); |
244 | if (i < 0) | |
245 | return i; | |
246 | prev_xstats = &alink->qdiscs[i].xstats; | |
cb89cac8 | 247 | |
674cb229 JK |
248 | if (alink->parent == TC_H_ROOT) |
249 | err = nfp_abm_ctrl_read_xstats(alink, &xstats); | |
250 | else | |
251 | err = nfp_abm_ctrl_read_q_xstats(alink, i, &xstats); | |
cb89cac8 JK |
252 | if (err) |
253 | return err; | |
254 | ||
255 | opt->xstats->forced_mark += xstats.ecn_marked - prev_xstats->ecn_marked; | |
256 | opt->xstats->pdrop += xstats.pdrop - prev_xstats->pdrop; | |
257 | ||
258 | *prev_xstats = xstats; | |
259 | ||
260 | return 0; | |
261 | } | |
262 | ||
8c8e6406 JK |
263 | static int |
264 | nfp_abm_setup_tc_red(struct net_device *netdev, struct nfp_abm_link *alink, | |
265 | struct tc_red_qopt_offload *opt) | |
266 | { | |
8c8e6406 JK |
267 | switch (opt->command) { |
268 | case TC_RED_REPLACE: | |
269 | return nfp_abm_red_replace(netdev, alink, opt); | |
270 | case TC_RED_DESTROY: | |
271 | nfp_abm_red_destroy(netdev, alink, opt->handle); | |
272 | return 0; | |
cb89cac8 JK |
273 | case TC_RED_STATS: |
274 | return nfp_abm_red_stats(alink, opt); | |
275 | case TC_RED_XSTATS: | |
276 | return nfp_abm_red_xstats(alink, opt); | |
8c8e6406 JK |
277 | default: |
278 | return -EOPNOTSUPP; | |
279 | } | |
280 | } | |
281 | ||
2440711e JK |
282 | static int |
283 | nfp_abm_mq_stats(struct nfp_abm_link *alink, struct tc_mq_qopt_offload *opt) | |
284 | { | |
285 | struct nfp_alink_stats stats; | |
286 | unsigned int i; | |
287 | int err; | |
288 | ||
289 | for (i = 0; i < alink->num_qdiscs; i++) { | |
290 | if (alink->qdiscs[i].handle == TC_H_UNSPEC) | |
291 | continue; | |
292 | ||
293 | err = nfp_abm_ctrl_read_q_stats(alink, i, &stats); | |
294 | if (err) | |
295 | return err; | |
296 | ||
297 | nfp_abm_update_stats(&stats, &alink->qdiscs[i].stats, | |
298 | &opt->stats); | |
299 | } | |
300 | ||
301 | return 0; | |
302 | } | |
303 | ||
674cb229 JK |
304 | static int |
305 | nfp_abm_setup_tc_mq(struct net_device *netdev, struct nfp_abm_link *alink, | |
306 | struct tc_mq_qopt_offload *opt) | |
307 | { | |
308 | switch (opt->command) { | |
309 | case TC_MQ_CREATE: | |
310 | nfp_abm_reset_root(netdev, alink, opt->handle, | |
311 | alink->total_queues); | |
312 | return 0; | |
313 | case TC_MQ_DESTROY: | |
314 | if (opt->handle == alink->parent) | |
315 | nfp_abm_reset_root(netdev, alink, TC_H_ROOT, 0); | |
316 | return 0; | |
2440711e JK |
317 | case TC_MQ_STATS: |
318 | return nfp_abm_mq_stats(alink, opt); | |
674cb229 JK |
319 | default: |
320 | return -EOPNOTSUPP; | |
321 | } | |
322 | } | |
323 | ||
8c8e6406 JK |
324 | static int |
325 | nfp_abm_setup_tc(struct nfp_app *app, struct net_device *netdev, | |
326 | enum tc_setup_type type, void *type_data) | |
327 | { | |
328 | struct nfp_repr *repr = netdev_priv(netdev); | |
329 | struct nfp_port *port; | |
330 | ||
331 | port = nfp_port_from_netdev(netdev); | |
332 | if (!port || port->type != NFP_PORT_PF_PORT) | |
333 | return -EOPNOTSUPP; | |
334 | ||
335 | switch (type) { | |
674cb229 JK |
336 | case TC_SETUP_QDISC_MQ: |
337 | return nfp_abm_setup_tc_mq(netdev, repr->app_priv, type_data); | |
8c8e6406 JK |
338 | case TC_SETUP_QDISC_RED: |
339 | return nfp_abm_setup_tc_red(netdev, repr->app_priv, type_data); | |
340 | default: | |
341 | return -EOPNOTSUPP; | |
342 | } | |
343 | } | |
344 | ||
d05d902e JK |
345 | static struct net_device *nfp_abm_repr_get(struct nfp_app *app, u32 port_id) |
346 | { | |
347 | enum nfp_repr_type rtype; | |
348 | struct nfp_reprs *reprs; | |
349 | u8 port; | |
350 | ||
351 | rtype = FIELD_GET(NFP_ABM_PORTID_TYPE, port_id); | |
352 | port = FIELD_GET(NFP_ABM_PORTID_ID, port_id); | |
353 | ||
354 | reprs = rcu_dereference(app->reprs[rtype]); | |
355 | if (!reprs) | |
356 | return NULL; | |
357 | ||
358 | if (port >= reprs->num_reprs) | |
359 | return NULL; | |
360 | ||
361 | return rcu_dereference(reprs->reprs[port]); | |
362 | } | |
363 | ||
364 | static int | |
365 | nfp_abm_spawn_repr(struct nfp_app *app, struct nfp_abm_link *alink, | |
366 | enum nfp_port_type ptype) | |
367 | { | |
368 | struct net_device *netdev; | |
369 | enum nfp_repr_type rtype; | |
370 | struct nfp_reprs *reprs; | |
371 | struct nfp_repr *repr; | |
372 | struct nfp_port *port; | |
2ef3c253 | 373 | unsigned int txqs; |
d05d902e JK |
374 | int err; |
375 | ||
2ef3c253 | 376 | if (ptype == NFP_PORT_PHYS_PORT) { |
d05d902e | 377 | rtype = NFP_REPR_TYPE_PHYS_PORT; |
2ef3c253 JK |
378 | txqs = 1; |
379 | } else { | |
d05d902e | 380 | rtype = NFP_REPR_TYPE_PF; |
2ef3c253 JK |
381 | txqs = alink->vnic->max_rx_rings; |
382 | } | |
d05d902e | 383 | |
2ef3c253 | 384 | netdev = nfp_repr_alloc_mqs(app, txqs, 1); |
d05d902e JK |
385 | if (!netdev) |
386 | return -ENOMEM; | |
387 | repr = netdev_priv(netdev); | |
388 | repr->app_priv = alink; | |
389 | ||
390 | port = nfp_port_alloc(app, ptype, netdev); | |
391 | if (IS_ERR(port)) { | |
392 | err = PTR_ERR(port); | |
393 | goto err_free_repr; | |
394 | } | |
395 | ||
396 | if (ptype == NFP_PORT_PHYS_PORT) { | |
1f700367 | 397 | port->eth_forced = true; |
d05d902e JK |
398 | err = nfp_port_init_phy_port(app->pf, app, port, alink->id); |
399 | if (err) | |
400 | goto err_free_port; | |
401 | } else { | |
402 | port->pf_id = alink->abm->pf_id; | |
290f54db JK |
403 | port->pf_split = app->pf->max_data_vnics > 1; |
404 | port->pf_split_id = alink->id; | |
d05d902e JK |
405 | port->vnic = alink->vnic->dp.ctrl_bar; |
406 | } | |
407 | ||
408 | SET_NETDEV_DEV(netdev, &alink->vnic->pdev->dev); | |
409 | eth_hw_addr_random(netdev); | |
410 | ||
411 | err = nfp_repr_init(app, netdev, nfp_abm_portid(rtype, alink->id), | |
412 | port, alink->vnic->dp.netdev); | |
413 | if (err) | |
414 | goto err_free_port; | |
415 | ||
416 | reprs = nfp_reprs_get_locked(app, rtype); | |
417 | WARN(nfp_repr_get_locked(app, reprs, alink->id), "duplicate repr"); | |
418 | rcu_assign_pointer(reprs->reprs[alink->id], netdev); | |
419 | ||
420 | nfp_info(app->cpp, "%s Port %d Representor(%s) created\n", | |
421 | ptype == NFP_PORT_PF_PORT ? "PCIe" : "Phys", | |
422 | alink->id, netdev->name); | |
423 | ||
424 | return 0; | |
425 | ||
426 | err_free_port: | |
427 | nfp_port_free(port); | |
428 | err_free_repr: | |
429 | nfp_repr_free(netdev); | |
430 | return err; | |
431 | } | |
432 | ||
433 | static void | |
434 | nfp_abm_kill_repr(struct nfp_app *app, struct nfp_abm_link *alink, | |
435 | enum nfp_repr_type rtype) | |
436 | { | |
437 | struct net_device *netdev; | |
438 | struct nfp_reprs *reprs; | |
439 | ||
440 | reprs = nfp_reprs_get_locked(app, rtype); | |
441 | netdev = nfp_repr_get_locked(app, reprs, alink->id); | |
442 | if (!netdev) | |
443 | return; | |
444 | rcu_assign_pointer(reprs->reprs[alink->id], NULL); | |
445 | synchronize_rcu(); | |
446 | /* Cast to make sure nfp_repr_clean_and_free() takes a nfp_repr */ | |
447 | nfp_repr_clean_and_free((struct nfp_repr *)netdev_priv(netdev)); | |
448 | } | |
449 | ||
450 | static void | |
451 | nfp_abm_kill_reprs(struct nfp_abm *abm, struct nfp_abm_link *alink) | |
452 | { | |
453 | nfp_abm_kill_repr(abm->app, alink, NFP_REPR_TYPE_PF); | |
454 | nfp_abm_kill_repr(abm->app, alink, NFP_REPR_TYPE_PHYS_PORT); | |
455 | } | |
456 | ||
457 | static void nfp_abm_kill_reprs_all(struct nfp_abm *abm) | |
458 | { | |
459 | struct nfp_pf *pf = abm->app->pf; | |
460 | struct nfp_net *nn; | |
461 | ||
462 | list_for_each_entry(nn, &pf->vnics, vnic_list) | |
463 | nfp_abm_kill_reprs(abm, (struct nfp_abm_link *)nn->app_priv); | |
464 | } | |
465 | ||
466 | static enum devlink_eswitch_mode nfp_abm_eswitch_mode_get(struct nfp_app *app) | |
467 | { | |
468 | struct nfp_abm *abm = app->priv; | |
469 | ||
470 | return abm->eswitch_mode; | |
471 | } | |
472 | ||
473 | static int nfp_abm_eswitch_set_legacy(struct nfp_abm *abm) | |
474 | { | |
475 | nfp_abm_kill_reprs_all(abm); | |
055ee0d6 | 476 | nfp_abm_ctrl_qm_disable(abm); |
d05d902e JK |
477 | |
478 | abm->eswitch_mode = DEVLINK_ESWITCH_MODE_LEGACY; | |
479 | return 0; | |
480 | } | |
481 | ||
482 | static void nfp_abm_eswitch_clean_up(struct nfp_abm *abm) | |
483 | { | |
484 | if (abm->eswitch_mode != DEVLINK_ESWITCH_MODE_LEGACY) | |
485 | WARN_ON(nfp_abm_eswitch_set_legacy(abm)); | |
486 | } | |
487 | ||
488 | static int nfp_abm_eswitch_set_switchdev(struct nfp_abm *abm) | |
489 | { | |
490 | struct nfp_app *app = abm->app; | |
491 | struct nfp_pf *pf = app->pf; | |
492 | struct nfp_net *nn; | |
493 | int err; | |
494 | ||
055ee0d6 JK |
495 | err = nfp_abm_ctrl_qm_enable(abm); |
496 | if (err) | |
497 | return err; | |
498 | ||
d05d902e JK |
499 | list_for_each_entry(nn, &pf->vnics, vnic_list) { |
500 | struct nfp_abm_link *alink = nn->app_priv; | |
501 | ||
502 | err = nfp_abm_spawn_repr(app, alink, NFP_PORT_PHYS_PORT); | |
503 | if (err) | |
504 | goto err_kill_all_reprs; | |
505 | ||
506 | err = nfp_abm_spawn_repr(app, alink, NFP_PORT_PF_PORT); | |
507 | if (err) | |
508 | goto err_kill_all_reprs; | |
509 | } | |
510 | ||
511 | abm->eswitch_mode = DEVLINK_ESWITCH_MODE_SWITCHDEV; | |
512 | return 0; | |
513 | ||
514 | err_kill_all_reprs: | |
515 | nfp_abm_kill_reprs_all(abm); | |
055ee0d6 | 516 | nfp_abm_ctrl_qm_disable(abm); |
d05d902e JK |
517 | return err; |
518 | } | |
519 | ||
520 | static int nfp_abm_eswitch_mode_set(struct nfp_app *app, u16 mode) | |
521 | { | |
522 | struct nfp_abm *abm = app->priv; | |
523 | ||
524 | if (abm->eswitch_mode == mode) | |
525 | return 0; | |
526 | ||
527 | switch (mode) { | |
528 | case DEVLINK_ESWITCH_MODE_LEGACY: | |
529 | return nfp_abm_eswitch_set_legacy(abm); | |
530 | case DEVLINK_ESWITCH_MODE_SWITCHDEV: | |
531 | return nfp_abm_eswitch_set_switchdev(abm); | |
532 | default: | |
533 | return -EINVAL; | |
534 | } | |
535 | } | |
536 | ||
cc54dc28 JK |
537 | static void |
538 | nfp_abm_vnic_set_mac(struct nfp_pf *pf, struct nfp_abm *abm, struct nfp_net *nn, | |
539 | unsigned int id) | |
540 | { | |
541 | struct nfp_eth_table_port *eth_port = &pf->eth_tbl->ports[id]; | |
542 | u8 mac_addr[ETH_ALEN]; | |
543 | const char *mac_str; | |
544 | char name[32]; | |
545 | ||
546 | if (id > pf->eth_tbl->count) { | |
547 | nfp_warn(pf->cpp, "No entry for persistent MAC address\n"); | |
548 | eth_hw_addr_random(nn->dp.netdev); | |
549 | return; | |
550 | } | |
551 | ||
552 | snprintf(name, sizeof(name), "eth%u.mac.pf%u", | |
553 | eth_port->eth_index, abm->pf_id); | |
554 | ||
555 | mac_str = nfp_hwinfo_lookup(pf->hwinfo, name); | |
556 | if (!mac_str) { | |
557 | nfp_warn(pf->cpp, "Can't lookup persistent MAC address (%s)\n", | |
558 | name); | |
559 | eth_hw_addr_random(nn->dp.netdev); | |
560 | return; | |
561 | } | |
562 | ||
563 | if (sscanf(mac_str, "%02hhx:%02hhx:%02hhx:%02hhx:%02hhx:%02hhx", | |
564 | &mac_addr[0], &mac_addr[1], &mac_addr[2], | |
565 | &mac_addr[3], &mac_addr[4], &mac_addr[5]) != 6) { | |
566 | nfp_warn(pf->cpp, "Can't parse persistent MAC address (%s)\n", | |
567 | mac_str); | |
568 | eth_hw_addr_random(nn->dp.netdev); | |
569 | return; | |
570 | } | |
571 | ||
572 | ether_addr_copy(nn->dp.netdev->dev_addr, mac_addr); | |
573 | ether_addr_copy(nn->dp.netdev->perm_addr, mac_addr); | |
574 | } | |
575 | ||
576 | static int | |
577 | nfp_abm_vnic_alloc(struct nfp_app *app, struct nfp_net *nn, unsigned int id) | |
578 | { | |
1f700367 | 579 | struct nfp_eth_table_port *eth_port = &app->pf->eth_tbl->ports[id]; |
cc54dc28 JK |
580 | struct nfp_abm *abm = app->priv; |
581 | struct nfp_abm_link *alink; | |
1f700367 | 582 | int err; |
cc54dc28 JK |
583 | |
584 | alink = kzalloc(sizeof(*alink), GFP_KERNEL); | |
585 | if (!alink) | |
586 | return -ENOMEM; | |
587 | nn->app_priv = alink; | |
588 | alink->abm = abm; | |
589 | alink->vnic = nn; | |
590 | alink->id = id; | |
674cb229 JK |
591 | alink->parent = TC_H_ROOT; |
592 | alink->total_queues = alink->vnic->max_rx_rings; | |
778e1cdd | 593 | alink->qdiscs = kvcalloc(alink->total_queues, sizeof(*alink->qdiscs), |
674cb229 JK |
594 | GFP_KERNEL); |
595 | if (!alink->qdiscs) { | |
596 | err = -ENOMEM; | |
597 | goto err_free_alink; | |
598 | } | |
cc54dc28 | 599 | |
1f700367 JK |
600 | /* This is a multi-host app, make sure MAC/PHY is up, but don't |
601 | * make the MAC/PHY state follow the state of any of the ports. | |
602 | */ | |
603 | err = nfp_eth_set_configured(app->cpp, eth_port->index, true); | |
604 | if (err < 0) | |
674cb229 | 605 | goto err_free_qdiscs; |
1f700367 | 606 | |
d05d902e | 607 | netif_keep_dst(nn->dp.netdev); |
cc54dc28 JK |
608 | |
609 | nfp_abm_vnic_set_mac(app->pf, abm, nn, id); | |
610 | nfp_abm_ctrl_read_params(alink); | |
611 | ||
612 | return 0; | |
1f700367 | 613 | |
674cb229 JK |
614 | err_free_qdiscs: |
615 | kvfree(alink->qdiscs); | |
1f700367 JK |
616 | err_free_alink: |
617 | kfree(alink); | |
618 | return err; | |
cc54dc28 JK |
619 | } |
620 | ||
621 | static void nfp_abm_vnic_free(struct nfp_app *app, struct nfp_net *nn) | |
622 | { | |
623 | struct nfp_abm_link *alink = nn->app_priv; | |
624 | ||
d05d902e | 625 | nfp_abm_kill_reprs(alink->abm, alink); |
674cb229 | 626 | kvfree(alink->qdiscs); |
cc54dc28 JK |
627 | kfree(alink); |
628 | } | |
629 | ||
0a8b7019 JK |
630 | static u64 * |
631 | nfp_abm_port_get_stats(struct nfp_app *app, struct nfp_port *port, u64 *data) | |
632 | { | |
633 | struct nfp_repr *repr = netdev_priv(port->netdev); | |
634 | struct nfp_abm_link *alink; | |
635 | unsigned int i; | |
636 | ||
637 | if (port->type != NFP_PORT_PF_PORT) | |
638 | return data; | |
639 | alink = repr->app_priv; | |
640 | for (i = 0; i < alink->vnic->dp.num_r_vecs; i++) { | |
641 | *data++ = nfp_abm_ctrl_stat_non_sto(alink, i); | |
642 | *data++ = nfp_abm_ctrl_stat_sto(alink, i); | |
643 | } | |
644 | return data; | |
645 | } | |
646 | ||
647 | static int | |
648 | nfp_abm_port_get_stats_count(struct nfp_app *app, struct nfp_port *port) | |
649 | { | |
650 | struct nfp_repr *repr = netdev_priv(port->netdev); | |
651 | struct nfp_abm_link *alink; | |
652 | ||
653 | if (port->type != NFP_PORT_PF_PORT) | |
654 | return 0; | |
655 | alink = repr->app_priv; | |
656 | return alink->vnic->dp.num_r_vecs * 2; | |
657 | } | |
658 | ||
659 | static u8 * | |
660 | nfp_abm_port_get_stats_strings(struct nfp_app *app, struct nfp_port *port, | |
661 | u8 *data) | |
662 | { | |
663 | struct nfp_repr *repr = netdev_priv(port->netdev); | |
664 | struct nfp_abm_link *alink; | |
665 | unsigned int i; | |
666 | ||
667 | if (port->type != NFP_PORT_PF_PORT) | |
668 | return data; | |
669 | alink = repr->app_priv; | |
670 | for (i = 0; i < alink->vnic->dp.num_r_vecs; i++) { | |
671 | data = nfp_pr_et(data, "q%u_no_wait", i); | |
672 | data = nfp_pr_et(data, "q%u_delayed", i); | |
673 | } | |
674 | return data; | |
675 | } | |
676 | ||
c4c8f39a JK |
677 | static int nfp_abm_init(struct nfp_app *app) |
678 | { | |
679 | struct nfp_pf *pf = app->pf; | |
d05d902e | 680 | struct nfp_reprs *reprs; |
c4c8f39a | 681 | struct nfp_abm *abm; |
cc54dc28 | 682 | int err; |
c4c8f39a JK |
683 | |
684 | if (!pf->eth_tbl) { | |
685 | nfp_err(pf->cpp, "ABM NIC requires ETH table\n"); | |
686 | return -EINVAL; | |
687 | } | |
688 | if (pf->max_data_vnics != pf->eth_tbl->count) { | |
689 | nfp_err(pf->cpp, "ETH entries don't match vNICs (%d vs %d)\n", | |
690 | pf->max_data_vnics, pf->eth_tbl->count); | |
691 | return -EINVAL; | |
692 | } | |
693 | if (!pf->mac_stats_bar) { | |
694 | nfp_warn(app->cpp, "ABM NIC requires mac_stats symbol\n"); | |
695 | return -EINVAL; | |
696 | } | |
697 | ||
698 | abm = kzalloc(sizeof(*abm), GFP_KERNEL); | |
699 | if (!abm) | |
700 | return -ENOMEM; | |
701 | app->priv = abm; | |
702 | abm->app = app; | |
703 | ||
cc54dc28 JK |
704 | err = nfp_abm_ctrl_find_addrs(abm); |
705 | if (err) | |
706 | goto err_free_abm; | |
707 | ||
055ee0d6 JK |
708 | /* We start in legacy mode, make sure advanced queuing is disabled */ |
709 | err = nfp_abm_ctrl_qm_disable(abm); | |
710 | if (err) | |
711 | goto err_free_abm; | |
712 | ||
d05d902e JK |
713 | err = -ENOMEM; |
714 | reprs = nfp_reprs_alloc(pf->max_data_vnics); | |
715 | if (!reprs) | |
716 | goto err_free_abm; | |
717 | RCU_INIT_POINTER(app->reprs[NFP_REPR_TYPE_PHYS_PORT], reprs); | |
718 | ||
719 | reprs = nfp_reprs_alloc(pf->max_data_vnics); | |
720 | if (!reprs) | |
721 | goto err_free_phys; | |
722 | RCU_INIT_POINTER(app->reprs[NFP_REPR_TYPE_PF], reprs); | |
723 | ||
c4c8f39a | 724 | return 0; |
cc54dc28 | 725 | |
d05d902e JK |
726 | err_free_phys: |
727 | nfp_reprs_clean_and_free_by_type(app, NFP_REPR_TYPE_PHYS_PORT); | |
cc54dc28 JK |
728 | err_free_abm: |
729 | kfree(abm); | |
730 | app->priv = NULL; | |
731 | return err; | |
c4c8f39a JK |
732 | } |
733 | ||
734 | static void nfp_abm_clean(struct nfp_app *app) | |
735 | { | |
736 | struct nfp_abm *abm = app->priv; | |
737 | ||
d05d902e JK |
738 | nfp_abm_eswitch_clean_up(abm); |
739 | nfp_reprs_clean_and_free_by_type(app, NFP_REPR_TYPE_PF); | |
740 | nfp_reprs_clean_and_free_by_type(app, NFP_REPR_TYPE_PHYS_PORT); | |
c4c8f39a JK |
741 | kfree(abm); |
742 | app->priv = NULL; | |
743 | } | |
744 | ||
745 | const struct nfp_app_type app_abm = { | |
746 | .id = NFP_APP_ACTIVE_BUFFER_MGMT_NIC, | |
747 | .name = "abm", | |
748 | ||
749 | .init = nfp_abm_init, | |
750 | .clean = nfp_abm_clean, | |
751 | ||
cc54dc28 JK |
752 | .vnic_alloc = nfp_abm_vnic_alloc, |
753 | .vnic_free = nfp_abm_vnic_free, | |
d05d902e | 754 | |
0a8b7019 JK |
755 | .port_get_stats = nfp_abm_port_get_stats, |
756 | .port_get_stats_count = nfp_abm_port_get_stats_count, | |
757 | .port_get_stats_strings = nfp_abm_port_get_stats_strings, | |
758 | ||
8c8e6406 JK |
759 | .setup_tc = nfp_abm_setup_tc, |
760 | ||
d05d902e JK |
761 | .eswitch_mode_get = nfp_abm_eswitch_mode_get, |
762 | .eswitch_mode_set = nfp_abm_eswitch_mode_set, | |
763 | ||
764 | .repr_get = nfp_abm_repr_get, | |
c4c8f39a | 765 | }; |