]>
Commit | Line | Data |
---|---|---|
acddc0ed | 1 | // SPDX-License-Identifier: GPL-2.0-or-later |
36c3b296 AK |
2 | /* |
3 | * Zebra dataplane plugin for DPDK based hw offload | |
4 | * | |
5 | * Copyright (C) 2021 Nvidia | |
6 | * Anuradha Karuppiah | |
36c3b296 AK |
7 | */ |
8 | ||
9 | #ifdef HAVE_CONFIG_H | |
10 | #include "config.h" /* Include this explicitly */ | |
11 | #endif | |
12 | ||
13 | #include "lib/libfrr.h" | |
14 | ||
15 | #include "zebra/debug.h" | |
16 | #include "zebra/interface.h" | |
17 | #include "zebra/zebra_dplane.h" | |
18 | #include "zebra/debug.h" | |
19 | #include "zebra/zebra_pbr.h" | |
20 | ||
21 | #include "zebra/dpdk/zebra_dplane_dpdk_private.h" | |
22 | ||
23 | static const char *plugin_name = "zebra_dplane_dpdk"; | |
24 | ||
25 | static struct zd_dpdk_ctx dpdk_ctx_buf, *dpdk_ctx = &dpdk_ctx_buf; | |
26 | #define dpdk_stat (&dpdk_ctx->stats) | |
27 | ||
a66d6246 AK |
28 | static struct zd_dpdk_port *zd_dpdk_port_find_by_index(int ifindex); |
29 | ||
30 | DEFINE_MTYPE_STATIC(ZEBRA, DPDK_PORTS, "ZD DPDK port database"); | |
36c3b296 AK |
31 | |
32 | void zd_dpdk_stat_show(struct vty *vty) | |
33 | { | |
34 | uint32_t tmp_cnt; | |
35 | ||
36 | vty_out(vty, "%30s\n%30s\n", "Dataplane DPDK counters", | |
37 | "======================="); | |
38 | ||
39 | #define ZD_DPDK_SHOW_COUNTER(label, counter) \ | |
40 | do { \ | |
41 | tmp_cnt = \ | |
42 | atomic_load_explicit(&counter, memory_order_relaxed); \ | |
43 | vty_out(vty, "%28s: %u\n", (label), (tmp_cnt)); \ | |
7f0416b3 | 44 | } while (0) |
36c3b296 AK |
45 | |
46 | ZD_DPDK_SHOW_COUNTER("PBR rule adds", dpdk_stat->rule_adds); | |
47 | ZD_DPDK_SHOW_COUNTER("PBR rule dels", dpdk_stat->rule_dels); | |
48 | ZD_DPDK_SHOW_COUNTER("Ignored updates", dpdk_stat->ignored_updates); | |
49 | } | |
50 | ||
51 | ||
7f0416b3 AK |
52 | static void zd_dpdk_flow_stat_show(struct vty *vty, int in_ifindex, |
53 | intptr_t dp_flow_ptr) | |
54 | { | |
55 | struct rte_flow_action_count count = {.shared = 0, .id = 0}; | |
56 | const struct rte_flow_action actions[] = { | |
57 | { | |
58 | .type = RTE_FLOW_ACTION_TYPE_COUNT, | |
59 | .conf = &count, | |
60 | }, | |
61 | { | |
62 | .type = RTE_FLOW_ACTION_TYPE_END, | |
63 | }, | |
64 | }; | |
65 | int rc; | |
66 | struct zd_dpdk_port *in_dport; | |
67 | struct rte_flow_query_count query; | |
68 | struct rte_flow_error error; | |
69 | uint64_t hits, bytes; | |
70 | ||
71 | in_dport = zd_dpdk_port_find_by_index(in_ifindex); | |
72 | if (!in_dport) { | |
73 | vty_out(vty, "PBR dpdk flow query failed; in_port %d missing\n", | |
74 | in_ifindex); | |
75 | return; | |
76 | } | |
77 | memset(&query, 0, sizeof(query)); | |
78 | rc = rte_flow_query(in_dport->port_id, (struct rte_flow *)dp_flow_ptr, | |
79 | actions, &query, &error); | |
80 | if (rc) { | |
81 | vty_out(vty, | |
82 | "PBR dpdk flow query failed; in_ifindex %d rc %d\n", | |
83 | in_ifindex, error.type); | |
84 | return; | |
85 | } | |
86 | hits = (query.hits_set) ? query.hits : 0; | |
87 | bytes = (query.bytes_set) ? query.bytes : 0; | |
88 | vty_out(vty, " DPDK stats: packets %" PRIu64 " bytes %" PRIu64 "\n", | |
89 | hits, bytes); | |
90 | } | |
91 | ||
92 | ||
93 | static int zd_dpdk_pbr_show_rules_walkcb(struct hash_bucket *bucket, void *arg) | |
94 | { | |
95 | struct zebra_pbr_rule *rule = (struct zebra_pbr_rule *)bucket->data; | |
96 | struct vty *vty = (struct vty *)arg; | |
97 | struct vrf *vrf; | |
98 | struct interface *ifp = NULL; | |
99 | struct zebra_pbr_action *zaction = &rule->action; | |
100 | ||
101 | zebra_pbr_show_rule_unit(rule, vty); | |
102 | if (zaction->dp_flow_ptr) { | |
103 | vrf = vrf_lookup_by_id(rule->vrf_id); | |
104 | if (vrf) | |
105 | ifp = if_lookup_by_name_vrf(rule->ifname, vrf); | |
106 | ||
107 | if (ifp) | |
108 | zd_dpdk_flow_stat_show(vty, ifp->ifindex, | |
109 | zaction->dp_flow_ptr); | |
110 | } | |
111 | return HASHWALK_CONTINUE; | |
112 | } | |
113 | ||
114 | ||
115 | void zd_dpdk_pbr_flows_show(struct vty *vty) | |
116 | { | |
117 | hash_walk(zrouter.rules_hash, zd_dpdk_pbr_show_rules_walkcb, vty); | |
118 | } | |
119 | ||
120 | ||
36c3b296 AK |
121 | static void zd_dpdk_rule_add(struct zebra_dplane_ctx *ctx) |
122 | { | |
7f0416b3 AK |
123 | static struct rte_flow_attr attrs = {.ingress = 1, .transfer = 1}; |
124 | uint32_t filter_bm = dplane_ctx_rule_get_filter_bm(ctx); | |
125 | int in_ifindex = dplane_ctx_get_ifindex(ctx); | |
126 | int out_ifindex = dplane_ctx_rule_get_out_ifindex(ctx); | |
127 | struct rte_flow_item_eth eth, eth_mask; | |
128 | struct rte_flow_item_ipv4 ip, ip_mask; | |
129 | struct rte_flow_item_udp udp, udp_mask; | |
130 | struct rte_flow_action_count conf_count; | |
131 | struct rte_flow_action_set_mac conf_smac, conf_dmac; | |
132 | struct rte_flow_action_port_id conf_port; | |
133 | struct rte_flow_item items[ZD_PBR_PATTERN_MAX]; | |
134 | struct rte_flow_action actions[ZD_PBR_ACTION_MAX]; | |
135 | int item_cnt = 0; | |
136 | int act_cnt = 0; | |
137 | struct in_addr tmp_mask; | |
138 | const struct ethaddr *mac; | |
139 | struct rte_flow *flow; | |
140 | struct rte_flow_error error; | |
141 | struct zd_dpdk_port *in_dport; | |
142 | struct zd_dpdk_port *out_dport; | |
143 | uint32_t pri = dplane_ctx_rule_get_priority(ctx); | |
144 | int seq = dplane_ctx_rule_get_seq(ctx); | |
145 | int unique = dplane_ctx_rule_get_unique(ctx); | |
146 | ||
147 | if (IS_ZEBRA_DEBUG_DPLANE_DPDK_DETAIL) | |
148 | zlog_debug( | |
149 | "PBR dpdk flow create ifname %s seq %d pri %u unique %d\n", | |
150 | dplane_ctx_rule_get_ifname(ctx), seq, pri, unique); | |
151 | in_dport = zd_dpdk_port_find_by_index(in_ifindex); | |
152 | if (!in_dport) { | |
153 | if (IS_ZEBRA_DEBUG_DPLANE_DPDK_DETAIL) | |
154 | zlog_debug( | |
155 | "PBR dpdk flow create ifname %s seq %d pri %u unique %d failed; in_port %d missing\n", | |
156 | dplane_ctx_rule_get_ifname(ctx), seq, pri, | |
157 | unique, in_ifindex); | |
158 | return; | |
159 | } | |
160 | ||
161 | out_dport = zd_dpdk_port_find_by_index(out_ifindex); | |
162 | if (!out_dport) { | |
163 | if (IS_ZEBRA_DEBUG_DPLANE_DPDK_DETAIL) | |
164 | zlog_debug( | |
165 | "PBR dpdk flow create ifname %s seq %d pri %u unique %d failed; out_port %d missing\n", | |
166 | dplane_ctx_rule_get_ifname(ctx), seq, pri, | |
167 | unique, out_ifindex); | |
168 | return; | |
169 | } | |
170 | ||
171 | /*********************** match items **************************/ | |
172 | memset(ð, 0, sizeof(eth)); | |
173 | memset(ð_mask, 0, sizeof(eth_mask)); | |
174 | eth.type = eth_mask.type = htons(RTE_ETHER_TYPE_IPV4); | |
175 | items[item_cnt].type = RTE_FLOW_ITEM_TYPE_ETH; | |
176 | items[item_cnt].spec = ð | |
177 | items[item_cnt].mask = ð_mask; | |
178 | items[item_cnt].last = NULL; | |
179 | ++item_cnt; | |
180 | ||
181 | memset(&ip, 0, sizeof(ip)); | |
182 | memset(&ip_mask, 0, sizeof(ip_mask)); | |
183 | if (filter_bm & PBR_FILTER_SRC_IP) { | |
184 | const struct prefix *src_ip; | |
185 | ||
186 | src_ip = dplane_ctx_rule_get_src_ip(ctx); | |
187 | ip.hdr.src_addr = src_ip->u.prefix4.s_addr; | |
188 | masklen2ip(src_ip->prefixlen, &tmp_mask); | |
189 | ip_mask.hdr.src_addr = tmp_mask.s_addr; | |
190 | } | |
191 | if (filter_bm & PBR_FILTER_DST_IP) { | |
192 | const struct prefix *dst_ip; | |
193 | ||
194 | dst_ip = dplane_ctx_rule_get_dst_ip(ctx); | |
195 | ip.hdr.dst_addr = dst_ip->u.prefix4.s_addr; | |
196 | masklen2ip(dst_ip->prefixlen, &tmp_mask); | |
197 | ip_mask.hdr.dst_addr = tmp_mask.s_addr; | |
198 | } | |
199 | if (filter_bm & PBR_FILTER_IP_PROTOCOL) { | |
200 | ip.hdr.next_proto_id = dplane_ctx_rule_get_ipproto(ctx); | |
201 | ip_mask.hdr.next_proto_id = UINT8_MAX; | |
202 | } | |
203 | items[item_cnt].type = RTE_FLOW_ITEM_TYPE_IPV4; | |
204 | items[item_cnt].spec = &ip; | |
205 | items[item_cnt].mask = &ip_mask; | |
206 | items[item_cnt].last = NULL; | |
207 | ++item_cnt; | |
208 | ||
209 | if ((filter_bm & (PBR_FILTER_SRC_PORT | PBR_FILTER_DST_PORT))) { | |
210 | memset(&udp, 0, sizeof(udp)); | |
211 | memset(&udp_mask, 0, sizeof(udp_mask)); | |
212 | if (filter_bm & PBR_FILTER_SRC_PORT) { | |
213 | udp.hdr.src_port = | |
214 | RTE_BE16(dplane_ctx_rule_get_src_port(ctx)); | |
215 | udp_mask.hdr.src_port = UINT16_MAX; | |
216 | } | |
217 | if (filter_bm & PBR_FILTER_DST_PORT) { | |
218 | udp.hdr.dst_port = | |
219 | RTE_BE16(dplane_ctx_rule_get_dst_port(ctx)); | |
220 | udp_mask.hdr.dst_port = UINT16_MAX; | |
221 | } | |
222 | items[item_cnt].type = RTE_FLOW_ITEM_TYPE_UDP; | |
223 | items[item_cnt].spec = &udp; | |
224 | items[item_cnt].mask = &udp_mask; | |
225 | items[item_cnt].last = NULL; | |
226 | ++item_cnt; | |
227 | } | |
228 | ||
229 | items[item_cnt].type = RTE_FLOW_ITEM_TYPE_END; | |
230 | ||
231 | /*************************** actions *****************************/ | |
232 | actions[act_cnt].type = RTE_FLOW_ACTION_TYPE_COUNT; | |
233 | memset(&conf_count, 0, sizeof(conf_count)); | |
234 | actions[act_cnt].conf = &conf_count; | |
235 | ++act_cnt; | |
236 | ||
237 | actions[act_cnt].type = RTE_FLOW_ACTION_TYPE_DEC_TTL; | |
238 | ++act_cnt; | |
239 | ||
240 | mac = dplane_ctx_rule_get_smac(ctx); | |
241 | memcpy(conf_smac.mac_addr, mac, RTE_ETHER_ADDR_LEN); | |
242 | actions[act_cnt].type = RTE_FLOW_ACTION_TYPE_SET_MAC_SRC; | |
243 | actions[act_cnt].conf = &conf_smac; | |
244 | ++act_cnt; | |
245 | ||
246 | mac = dplane_ctx_rule_get_dmac(ctx); | |
247 | memcpy(conf_dmac.mac_addr, mac, RTE_ETHER_ADDR_LEN); | |
248 | actions[act_cnt].type = RTE_FLOW_ACTION_TYPE_SET_MAC_DST; | |
249 | actions[act_cnt].conf = &conf_dmac; | |
250 | ++act_cnt; | |
251 | ||
252 | memset(&conf_port, 0, sizeof(conf_port)); | |
253 | conf_port.id = out_dport->port_id; | |
254 | actions[act_cnt].type = RTE_FLOW_ACTION_TYPE_PORT_ID; | |
255 | actions[act_cnt].conf = &conf_port; | |
256 | ++act_cnt; | |
257 | ||
258 | actions[act_cnt].type = RTE_FLOW_ACTION_TYPE_END; | |
259 | ||
260 | frr_with_privs (&zserv_privs) { | |
261 | flow = rte_flow_create(in_dport->port_id, &attrs, items, | |
262 | actions, &error); | |
263 | } | |
264 | ||
265 | if (flow) { | |
266 | dplane_ctx_rule_set_dp_flow_ptr(ctx, (intptr_t)flow); | |
267 | if (IS_ZEBRA_DEBUG_DPLANE_DPDK_DETAIL) | |
268 | zlog_debug( | |
269 | "PBR dpdk flow 0x%" PRIxPTR | |
270 | " created ifname %s seq %d pri %u unique %d\n", | |
271 | (intptr_t)flow, dplane_ctx_rule_get_ifname(ctx), | |
272 | seq, pri, unique); | |
273 | } else { | |
274 | zlog_warn( | |
275 | "PBR dpdk flow create failed ifname %s seq %d pri %u unique %d; rc %d\n", | |
276 | dplane_ctx_rule_get_ifname(ctx), seq, pri, unique, | |
277 | error.type); | |
278 | } | |
36c3b296 AK |
279 | } |
280 | ||
281 | ||
7f0416b3 AK |
282 | static void zd_dpdk_rule_del(struct zebra_dplane_ctx *ctx, const char *ifname, |
283 | int in_ifindex, intptr_t dp_flow_ptr) | |
36c3b296 | 284 | { |
7f0416b3 AK |
285 | struct zd_dpdk_port *in_dport; |
286 | struct rte_flow_error error; | |
287 | int rc; | |
36c3b296 | 288 | |
7f0416b3 AK |
289 | if (IS_ZEBRA_DEBUG_DPLANE_DPDK_DETAIL) |
290 | zlog_debug( | |
291 | "PBR dpdk flow delete ifname %s ifindex %d dp_flow 0x%" PRIxPTR | |
292 | "\n", | |
293 | ifname, in_ifindex, dp_flow_ptr); | |
294 | ||
295 | if (!dp_flow_ptr) { | |
296 | if (IS_ZEBRA_DEBUG_DPLANE_DPDK_DETAIL) | |
297 | zlog_debug( | |
298 | "PBR dpdk flow delete failed; ifname %s ifindex %d dp_flow 0x%" PRIxPTR | |
299 | "; empty dp\n", | |
300 | ifname, in_ifindex, dp_flow_ptr); | |
301 | return; | |
302 | } | |
303 | ||
304 | dplane_ctx_rule_set_dp_flow_ptr(ctx, (intptr_t)NULL); | |
305 | in_dport = zd_dpdk_port_find_by_index(in_ifindex); | |
306 | if (!in_dport) { | |
307 | if (IS_ZEBRA_DEBUG_DPLANE_DPDK_DETAIL) | |
308 | zlog_debug( | |
309 | "PBR dpdk flow delete failed; ifname %s ifindex %d dp_flow 0x%" PRIxPTR | |
310 | " in port missing\n", | |
311 | ifname, in_ifindex, dp_flow_ptr); | |
312 | return; | |
313 | } | |
314 | ||
315 | frr_with_privs (&zserv_privs) { | |
316 | rc = rte_flow_destroy(in_dport->port_id, | |
317 | (struct rte_flow *)dp_flow_ptr, &error); | |
318 | } | |
319 | ||
320 | if (rc) | |
321 | zlog_warn( | |
322 | "PBR dpdk flow delete failed; ifname %s ifindex %d dp_flow 0x%" PRIxPTR | |
323 | "\n", | |
324 | ifname, in_ifindex, dp_flow_ptr); | |
36c3b296 AK |
325 | } |
326 | ||
327 | ||
328 | static void zd_dpdk_rule_update(struct zebra_dplane_ctx *ctx) | |
329 | { | |
330 | enum dplane_op_e op; | |
331 | int in_ifindex; | |
332 | intptr_t dp_flow_ptr; | |
333 | ||
7f0416b3 | 334 | if (IS_ZEBRA_DEBUG_DPLANE_DPDK_DETAIL) |
36c3b296 | 335 | zlog_debug("Dplane %s", dplane_op2str(dplane_ctx_get_op(ctx))); |
7f0416b3 | 336 | |
36c3b296 AK |
337 | |
338 | op = dplane_ctx_get_op(ctx); | |
339 | switch (op) { | |
340 | case DPLANE_OP_RULE_ADD: | |
341 | atomic_fetch_add_explicit(&dpdk_stat->rule_adds, 1, | |
342 | memory_order_relaxed); | |
343 | zd_dpdk_rule_add(ctx); | |
344 | break; | |
345 | ||
346 | case DPLANE_OP_RULE_UPDATE: | |
347 | /* delete old rule and install new one */ | |
348 | atomic_fetch_add_explicit(&dpdk_stat->rule_adds, 1, | |
349 | memory_order_relaxed); | |
350 | in_ifindex = dplane_ctx_get_ifindex(ctx); | |
351 | dp_flow_ptr = dplane_ctx_rule_get_old_dp_flow_ptr(ctx); | |
7f0416b3 AK |
352 | zd_dpdk_rule_del(ctx, dplane_ctx_rule_get_ifname(ctx), |
353 | in_ifindex, dp_flow_ptr); | |
36c3b296 AK |
354 | zd_dpdk_rule_add(ctx); |
355 | break; | |
356 | ||
357 | case DPLANE_OP_RULE_DELETE: | |
358 | atomic_fetch_add_explicit(&dpdk_stat->rule_dels, 1, | |
359 | memory_order_relaxed); | |
360 | in_ifindex = dplane_ctx_get_ifindex(ctx); | |
361 | dp_flow_ptr = dplane_ctx_rule_get_dp_flow_ptr(ctx); | |
7f0416b3 AK |
362 | zd_dpdk_rule_del(ctx, dplane_ctx_rule_get_ifname(ctx), |
363 | in_ifindex, dp_flow_ptr); | |
36c3b296 AK |
364 | break; |
365 | ||
7f0416b3 AK |
366 | case DPLANE_OP_NONE: |
367 | case DPLANE_OP_ROUTE_INSTALL: | |
368 | case DPLANE_OP_ROUTE_UPDATE: | |
369 | case DPLANE_OP_ROUTE_DELETE: | |
370 | case DPLANE_OP_ROUTE_NOTIFY: | |
371 | case DPLANE_OP_NH_INSTALL: | |
372 | case DPLANE_OP_NH_UPDATE: | |
373 | case DPLANE_OP_NH_DELETE: | |
374 | case DPLANE_OP_LSP_INSTALL: | |
375 | case DPLANE_OP_LSP_UPDATE: | |
376 | case DPLANE_OP_LSP_DELETE: | |
377 | case DPLANE_OP_LSP_NOTIFY: | |
378 | case DPLANE_OP_PW_INSTALL: | |
379 | case DPLANE_OP_PW_UNINSTALL: | |
380 | case DPLANE_OP_SYS_ROUTE_ADD: | |
381 | case DPLANE_OP_SYS_ROUTE_DELETE: | |
382 | case DPLANE_OP_ADDR_INSTALL: | |
383 | case DPLANE_OP_ADDR_UNINSTALL: | |
384 | case DPLANE_OP_MAC_INSTALL: | |
385 | case DPLANE_OP_MAC_DELETE: | |
386 | case DPLANE_OP_NEIGH_INSTALL: | |
387 | case DPLANE_OP_NEIGH_UPDATE: | |
388 | case DPLANE_OP_NEIGH_DELETE: | |
389 | case DPLANE_OP_VTEP_ADD: | |
390 | case DPLANE_OP_VTEP_DELETE: | |
391 | case DPLANE_OP_NEIGH_DISCOVER: | |
392 | case DPLANE_OP_BR_PORT_UPDATE: | |
393 | case DPLANE_OP_IPTABLE_ADD: | |
394 | case DPLANE_OP_IPTABLE_DELETE: | |
395 | case DPLANE_OP_IPSET_ADD: | |
396 | case DPLANE_OP_IPSET_DELETE: | |
397 | case DPLANE_OP_IPSET_ENTRY_ADD: | |
398 | case DPLANE_OP_IPSET_ENTRY_DELETE: | |
399 | case DPLANE_OP_NEIGH_IP_INSTALL: | |
400 | case DPLANE_OP_NEIGH_IP_DELETE: | |
401 | case DPLANE_OP_NEIGH_TABLE_UPDATE: | |
402 | case DPLANE_OP_GRE_SET: | |
403 | case DPLANE_OP_INTF_ADDR_ADD: | |
404 | case DPLANE_OP_INTF_ADDR_DEL: | |
405 | case DPLANE_OP_INTF_NETCONFIG: | |
406 | case DPLANE_OP_INTF_INSTALL: | |
407 | case DPLANE_OP_INTF_UPDATE: | |
408 | case DPLANE_OP_INTF_DELETE: | |
409 | break; | |
36c3b296 AK |
410 | } |
411 | } | |
412 | ||
413 | ||
414 | /* DPDK provider callback. | |
415 | */ | |
416 | static void zd_dpdk_process_update(struct zebra_dplane_ctx *ctx) | |
417 | { | |
418 | switch (dplane_ctx_get_op(ctx)) { | |
419 | ||
420 | case DPLANE_OP_RULE_ADD: | |
421 | case DPLANE_OP_RULE_UPDATE: | |
422 | case DPLANE_OP_RULE_DELETE: | |
423 | zd_dpdk_rule_update(ctx); | |
424 | break; | |
7f0416b3 AK |
425 | case DPLANE_OP_NONE: |
426 | case DPLANE_OP_ROUTE_INSTALL: | |
427 | case DPLANE_OP_ROUTE_UPDATE: | |
428 | case DPLANE_OP_ROUTE_DELETE: | |
429 | case DPLANE_OP_ROUTE_NOTIFY: | |
430 | case DPLANE_OP_NH_INSTALL: | |
431 | case DPLANE_OP_NH_UPDATE: | |
432 | case DPLANE_OP_NH_DELETE: | |
433 | case DPLANE_OP_LSP_INSTALL: | |
434 | case DPLANE_OP_LSP_UPDATE: | |
435 | case DPLANE_OP_LSP_DELETE: | |
436 | case DPLANE_OP_LSP_NOTIFY: | |
437 | case DPLANE_OP_PW_INSTALL: | |
438 | case DPLANE_OP_PW_UNINSTALL: | |
439 | case DPLANE_OP_SYS_ROUTE_ADD: | |
440 | case DPLANE_OP_SYS_ROUTE_DELETE: | |
441 | case DPLANE_OP_ADDR_INSTALL: | |
442 | case DPLANE_OP_ADDR_UNINSTALL: | |
443 | case DPLANE_OP_MAC_INSTALL: | |
444 | case DPLANE_OP_MAC_DELETE: | |
445 | case DPLANE_OP_NEIGH_INSTALL: | |
446 | case DPLANE_OP_NEIGH_UPDATE: | |
447 | case DPLANE_OP_NEIGH_DELETE: | |
448 | case DPLANE_OP_VTEP_ADD: | |
449 | case DPLANE_OP_VTEP_DELETE: | |
450 | case DPLANE_OP_NEIGH_DISCOVER: | |
451 | case DPLANE_OP_BR_PORT_UPDATE: | |
452 | case DPLANE_OP_IPTABLE_ADD: | |
453 | case DPLANE_OP_IPTABLE_DELETE: | |
454 | case DPLANE_OP_IPSET_ADD: | |
455 | case DPLANE_OP_IPSET_DELETE: | |
456 | case DPLANE_OP_IPSET_ENTRY_ADD: | |
457 | case DPLANE_OP_IPSET_ENTRY_DELETE: | |
458 | case DPLANE_OP_NEIGH_IP_INSTALL: | |
459 | case DPLANE_OP_NEIGH_IP_DELETE: | |
460 | case DPLANE_OP_NEIGH_TABLE_UPDATE: | |
461 | case DPLANE_OP_GRE_SET: | |
462 | case DPLANE_OP_INTF_ADDR_ADD: | |
463 | case DPLANE_OP_INTF_ADDR_DEL: | |
464 | case DPLANE_OP_INTF_NETCONFIG: | |
465 | case DPLANE_OP_INTF_INSTALL: | |
466 | case DPLANE_OP_INTF_UPDATE: | |
467 | case DPLANE_OP_INTF_DELETE: | |
36c3b296 AK |
468 | atomic_fetch_add_explicit(&dpdk_stat->ignored_updates, 1, |
469 | memory_order_relaxed); | |
470 | ||
471 | break; | |
472 | } | |
473 | } | |
474 | ||
475 | ||
476 | static int zd_dpdk_process(struct zebra_dplane_provider *prov) | |
477 | { | |
478 | struct zebra_dplane_ctx *ctx; | |
479 | int counter, limit; | |
480 | ||
481 | if (IS_ZEBRA_DEBUG_DPLANE_DPDK_DETAIL) | |
482 | zlog_debug("processing %s", dplane_provider_get_name(prov)); | |
483 | ||
484 | limit = dplane_provider_get_work_limit(prov); | |
485 | for (counter = 0; counter < limit; counter++) { | |
486 | ctx = dplane_provider_dequeue_in_ctx(prov); | |
487 | if (!ctx) | |
488 | break; | |
489 | ||
490 | zd_dpdk_process_update(ctx); | |
491 | dplane_ctx_set_status(ctx, ZEBRA_DPLANE_REQUEST_SUCCESS); | |
492 | dplane_provider_enqueue_out_ctx(prov, ctx); | |
493 | } | |
494 | ||
495 | return 0; | |
496 | } | |
497 | ||
a66d6246 AK |
498 | static void zd_dpdk_port_show_entry(struct zd_dpdk_port *dport, struct vty *vty, |
499 | int detail) | |
500 | { | |
501 | struct rte_eth_dev_info *dev_info; | |
502 | ||
503 | dev_info = &dport->dev_info; | |
504 | if (detail) { | |
505 | vty_out(vty, "DPDK port: %u\n", dport->port_id); | |
506 | vty_out(vty, " Device: %s\n", | |
507 | dev_info->device ? dev_info->device->name : "-"); | |
508 | vty_out(vty, " Driver: %s\n", | |
509 | dev_info->driver_name ? dev_info->driver_name : "-"); | |
510 | vty_out(vty, " Interface: %s (%d)\n", | |
511 | ifindex2ifname(dev_info->if_index, VRF_DEFAULT), | |
512 | dev_info->if_index); | |
513 | vty_out(vty, " Switch: %s Domain: %u Port: %u\n", | |
514 | dev_info->switch_info.name, | |
515 | dev_info->switch_info.domain_id, | |
516 | dev_info->switch_info.port_id); | |
517 | vty_out(vty, "\n"); | |
518 | } else { | |
519 | vty_out(vty, "%-4u %-16s %-16s %-16d %s,%u,%u\n", | |
520 | dport->port_id, | |
521 | dev_info->device ? dev_info->device->name : "-", | |
522 | ifindex2ifname(dev_info->if_index, VRF_DEFAULT), | |
523 | dev_info->if_index, dev_info->switch_info.name, | |
524 | dev_info->switch_info.domain_id, | |
525 | dev_info->switch_info.port_id); | |
526 | } | |
527 | } | |
528 | ||
529 | ||
530 | static struct zd_dpdk_port *zd_dpdk_port_find_by_index(int ifindex) | |
531 | { | |
532 | int count; | |
533 | struct zd_dpdk_port *dport; | |
534 | struct rte_eth_dev_info *dev_info; | |
535 | ||
536 | for (count = 0; count < RTE_MAX_ETHPORTS; ++count) { | |
537 | dport = &dpdk_ctx->dpdk_ports[count]; | |
538 | if (!(dport->flags & ZD_DPDK_PORT_FLAG_INITED)) | |
539 | continue; | |
540 | dev_info = &dport->dev_info; | |
541 | if (dev_info->if_index == (uint32_t)ifindex) | |
542 | return dport; | |
543 | } | |
544 | ||
545 | return NULL; | |
546 | } | |
547 | ||
548 | ||
549 | void zd_dpdk_port_show(struct vty *vty, uint16_t port_id, bool uj, int detail) | |
550 | { | |
551 | int count; | |
552 | struct zd_dpdk_port *dport; | |
553 | ||
554 | /* XXX - support for json is yet to be added */ | |
555 | if (uj) | |
556 | return; | |
557 | ||
558 | if (!detail) { | |
559 | vty_out(vty, "%-4s %-16s %-16s %-16s %s\n", "Port", "Device", | |
560 | "IfName", "IfIndex", "sw,domain,port"); | |
561 | } | |
562 | ||
563 | for (count = 0; count < RTE_MAX_ETHPORTS; ++count) { | |
564 | dport = &dpdk_ctx->dpdk_ports[count]; | |
565 | if (dport->flags & ZD_DPDK_PORT_FLAG_INITED) | |
566 | zd_dpdk_port_show_entry(dport, vty, detail); | |
567 | } | |
568 | } | |
569 | ||
570 | ||
571 | static void zd_dpdk_port_init(void) | |
572 | { | |
573 | struct zd_dpdk_port *dport; | |
574 | uint16_t port_id; | |
575 | struct rte_eth_dev_info *dev_info; | |
576 | int count; | |
577 | int rc; | |
578 | struct rte_flow_error error; | |
579 | ||
580 | /* allocate a list of ports */ | |
581 | dpdk_ctx->dpdk_ports = | |
582 | XCALLOC(MTYPE_DPDK_PORTS, | |
583 | sizeof(struct zd_dpdk_port) * RTE_MAX_ETHPORTS); | |
584 | ||
585 | if (IS_ZEBRA_DEBUG_DPLANE_DPDK) | |
586 | zlog_debug("dpdk port init"); | |
587 | count = 0; | |
588 | RTE_ETH_FOREACH_DEV(port_id) | |
589 | { | |
590 | if (IS_ZEBRA_DEBUG_DPLANE_DPDK) | |
591 | zlog_debug("dpdk port init %d", port_id); | |
592 | dport = &dpdk_ctx->dpdk_ports[count]; | |
593 | count++; | |
594 | dport->port_id = port_id; | |
595 | dport->flags |= ZD_DPDK_PORT_FLAG_PROBED; | |
596 | dev_info = &dport->dev_info; | |
597 | if (rte_eth_dev_info_get(port_id, dev_info) < 0) { | |
598 | zlog_warn("failed to get dev info for %u, %s", port_id, | |
599 | rte_strerror(rte_errno)); | |
600 | continue; | |
601 | } | |
602 | dport->flags |= ZD_DPDK_PORT_FLAG_INITED; | |
603 | if (IS_ZEBRA_DEBUG_DPLANE_DPDK) | |
604 | zlog_debug( | |
605 | "port %u, dev %s, ifI %d, sw_name %s, sw_domain %u, sw_port %u", | |
606 | port_id, | |
607 | dev_info->device ? dev_info->device->name : "-", | |
608 | dev_info->if_index, dev_info->switch_info.name, | |
609 | dev_info->switch_info.domain_id, | |
610 | dev_info->switch_info.port_id); | |
611 | if (rte_flow_isolate(port_id, 1, &error)) { | |
612 | if (IS_ZEBRA_DEBUG_DPLANE_DPDK) | |
613 | zlog_debug( | |
614 | "Flow isolate on port %u failed %d\n", | |
615 | port_id, error.type); | |
616 | } else { | |
617 | if (IS_ZEBRA_DEBUG_DPLANE_DPDK) | |
618 | zlog_debug("Flow isolate on port %u\n", | |
619 | port_id); | |
620 | } | |
621 | rc = rte_eth_dev_start(port_id); | |
622 | if (rc) { | |
623 | zlog_warn("DPDK port %d start error: %s", port_id, | |
624 | rte_strerror(-rc)); | |
625 | continue; | |
626 | } | |
627 | if (IS_ZEBRA_DEBUG_DPLANE_DPDK) | |
628 | zlog_debug("DPDK port %d started in promiscuous mode ", | |
629 | port_id); | |
630 | } | |
631 | ||
632 | if (!count) { | |
633 | if (IS_ZEBRA_DEBUG_DPLANE_DPDK) | |
634 | zlog_debug("no probed ethernet devices"); | |
635 | } | |
636 | } | |
637 | ||
638 | ||
67f5a232 AK |
639 | static int zd_dpdk_init(void) |
640 | { | |
641 | int rc; | |
7f0416b3 AK |
642 | static const char *argv[] = {(char *)"/usr/lib/frr/zebra", |
643 | (char *)"--"}; | |
67f5a232 AK |
644 | |
645 | zd_dpdk_vty_init(); | |
646 | ||
647 | frr_with_privs (&zserv_privs) { | |
7f0416b3 | 648 | rc = rte_eal_init(ARRAY_SIZE(argv), argv); |
67f5a232 AK |
649 | } |
650 | if (rc < 0) { | |
651 | zlog_warn("EAL init failed %s", rte_strerror(rte_errno)); | |
652 | return -1; | |
653 | } | |
654 | ||
a66d6246 AK |
655 | frr_with_privs (&zserv_privs) { |
656 | zd_dpdk_port_init(); | |
657 | } | |
67f5a232 AK |
658 | return 0; |
659 | } | |
36c3b296 | 660 | |
a66d6246 | 661 | |
36c3b296 AK |
662 | static int zd_dpdk_start(struct zebra_dplane_provider *prov) |
663 | { | |
664 | if (IS_ZEBRA_DEBUG_DPLANE_DPDK) | |
665 | zlog_debug("%s start", dplane_provider_get_name(prov)); | |
666 | ||
67f5a232 | 667 | return zd_dpdk_init(); |
36c3b296 AK |
668 | } |
669 | ||
670 | ||
671 | static int zd_dpdk_finish(struct zebra_dplane_provider *prov, bool early) | |
672 | { | |
67f5a232 AK |
673 | int rc; |
674 | ||
36c3b296 AK |
675 | if (early) { |
676 | if (IS_ZEBRA_DEBUG_DPLANE_DPDK) | |
677 | zlog_debug("%s early finish", | |
678 | dplane_provider_get_name(prov)); | |
679 | ||
680 | return 0; | |
681 | } | |
682 | ||
683 | if (IS_ZEBRA_DEBUG_DPLANE_DPDK) | |
684 | zlog_debug("%s finish", dplane_provider_get_name(prov)); | |
685 | ||
686 | ||
67f5a232 AK |
687 | frr_with_privs (&zserv_privs) { |
688 | rc = rte_eal_cleanup(); | |
689 | } | |
690 | if (rc < 0) | |
691 | zlog_warn("EAL cleanup failed %s", rte_strerror(rte_errno)); | |
692 | ||
36c3b296 AK |
693 | return 0; |
694 | } | |
695 | ||
696 | ||
697 | static int zd_dpdk_plugin_init(struct thread_master *tm) | |
698 | { | |
699 | int ret; | |
700 | ||
701 | ret = dplane_provider_register( | |
702 | plugin_name, DPLANE_PRIO_KERNEL, DPLANE_PROV_FLAGS_DEFAULT, | |
703 | zd_dpdk_start, zd_dpdk_process, zd_dpdk_finish, dpdk_ctx, NULL); | |
704 | ||
705 | if (IS_ZEBRA_DEBUG_DPLANE_DPDK) | |
706 | zlog_debug("%s register status %d", plugin_name, ret); | |
707 | ||
708 | return 0; | |
709 | } | |
710 | ||
711 | ||
712 | static int zd_dpdk_module_init(void) | |
713 | { | |
714 | hook_register(frr_late_init, zd_dpdk_plugin_init); | |
715 | return 0; | |
716 | } | |
717 | ||
718 | FRR_MODULE_SETUP(.name = "dplane_dpdk", .version = "0.0.1", | |
719 | .description = "Data plane plugin using dpdk for hw offload", | |
7f0416b3 | 720 | .init = zd_dpdk_module_init); |