// SPDX-License-Identifier: GPL-2.0-or-later /* * Zebra dataplane plugin for DPDK based hw offload * * Copyright (C) 2021 Nvidia * Anuradha Karuppiah */ #ifdef HAVE_CONFIG_H #include "config.h" /* Include this explicitly */ #endif #include "lib/libfrr.h" #include "zebra/debug.h" #include "zebra/interface.h" #include "zebra/zebra_dplane.h" #include "zebra/debug.h" #include "zebra/zebra_pbr.h" #include "zebra/dpdk/zebra_dplane_dpdk_private.h" static const char *plugin_name = "zebra_dplane_dpdk"; static struct zd_dpdk_ctx dpdk_ctx_buf, *dpdk_ctx = &dpdk_ctx_buf; #define dpdk_stat (&dpdk_ctx->stats) static struct zd_dpdk_port *zd_dpdk_port_find_by_index(int ifindex); DEFINE_MTYPE_STATIC(ZEBRA, DPDK_PORTS, "ZD DPDK port database"); void zd_dpdk_stat_show(struct vty *vty) { uint32_t tmp_cnt; vty_out(vty, "%30s\n%30s\n", "Dataplane DPDK counters", "======================="); #define ZD_DPDK_SHOW_COUNTER(label, counter) \ do { \ tmp_cnt = \ atomic_load_explicit(&counter, memory_order_relaxed); \ vty_out(vty, "%28s: %u\n", (label), (tmp_cnt)); \ } while (0) ZD_DPDK_SHOW_COUNTER("PBR rule adds", dpdk_stat->rule_adds); ZD_DPDK_SHOW_COUNTER("PBR rule dels", dpdk_stat->rule_dels); ZD_DPDK_SHOW_COUNTER("Ignored updates", dpdk_stat->ignored_updates); } static void zd_dpdk_flow_stat_show(struct vty *vty, int in_ifindex, intptr_t dp_flow_ptr) { struct rte_flow_action_count count = {.shared = 0, .id = 0}; const struct rte_flow_action actions[] = { { .type = RTE_FLOW_ACTION_TYPE_COUNT, .conf = &count, }, { .type = RTE_FLOW_ACTION_TYPE_END, }, }; int rc; struct zd_dpdk_port *in_dport; struct rte_flow_query_count query; struct rte_flow_error error; uint64_t hits, bytes; in_dport = zd_dpdk_port_find_by_index(in_ifindex); if (!in_dport) { vty_out(vty, "PBR dpdk flow query failed; in_port %d missing\n", in_ifindex); return; } memset(&query, 0, sizeof(query)); rc = rte_flow_query(in_dport->port_id, (struct rte_flow *)dp_flow_ptr, actions, &query, &error); if (rc) { vty_out(vty, "PBR dpdk flow query failed; in_ifindex %d rc %d\n", in_ifindex, error.type); return; } hits = (query.hits_set) ? query.hits : 0; bytes = (query.bytes_set) ? query.bytes : 0; vty_out(vty, " DPDK stats: packets %" PRIu64 " bytes %" PRIu64 "\n", hits, bytes); } static int zd_dpdk_pbr_show_rules_walkcb(struct hash_bucket *bucket, void *arg) { struct zebra_pbr_rule *rule = (struct zebra_pbr_rule *)bucket->data; struct vty *vty = (struct vty *)arg; struct vrf *vrf; struct interface *ifp = NULL; struct zebra_pbr_action *zaction = &rule->action; zebra_pbr_show_rule_unit(rule, vty); if (zaction->dp_flow_ptr) { vrf = vrf_lookup_by_id(rule->vrf_id); if (vrf) ifp = if_lookup_by_name_vrf(rule->ifname, vrf); if (ifp) zd_dpdk_flow_stat_show(vty, ifp->ifindex, zaction->dp_flow_ptr); } return HASHWALK_CONTINUE; } void zd_dpdk_pbr_flows_show(struct vty *vty) { hash_walk(zrouter.rules_hash, zd_dpdk_pbr_show_rules_walkcb, vty); } static void zd_dpdk_rule_add(struct zebra_dplane_ctx *ctx) { static struct rte_flow_attr attrs = {.ingress = 1, .transfer = 1}; uint32_t filter_bm = dplane_ctx_rule_get_filter_bm(ctx); int in_ifindex = dplane_ctx_get_ifindex(ctx); int out_ifindex = dplane_ctx_rule_get_out_ifindex(ctx); struct rte_flow_item_eth eth, eth_mask; struct rte_flow_item_ipv4 ip, ip_mask; struct rte_flow_item_udp udp, udp_mask; struct rte_flow_action_count conf_count; struct rte_flow_action_set_mac conf_smac, conf_dmac; struct rte_flow_action_port_id conf_port; struct rte_flow_item items[ZD_PBR_PATTERN_MAX]; struct rte_flow_action actions[ZD_PBR_ACTION_MAX]; int item_cnt = 0; int act_cnt = 0; struct in_addr tmp_mask; const struct ethaddr *mac; struct rte_flow *flow; struct rte_flow_error error; struct zd_dpdk_port *in_dport; struct zd_dpdk_port *out_dport; uint32_t pri = dplane_ctx_rule_get_priority(ctx); int seq = dplane_ctx_rule_get_seq(ctx); int unique = dplane_ctx_rule_get_unique(ctx); if (IS_ZEBRA_DEBUG_DPLANE_DPDK_DETAIL) zlog_debug( "PBR dpdk flow create ifname %s seq %d pri %u unique %d\n", dplane_ctx_rule_get_ifname(ctx), seq, pri, unique); in_dport = zd_dpdk_port_find_by_index(in_ifindex); if (!in_dport) { if (IS_ZEBRA_DEBUG_DPLANE_DPDK_DETAIL) zlog_debug( "PBR dpdk flow create ifname %s seq %d pri %u unique %d failed; in_port %d missing\n", dplane_ctx_rule_get_ifname(ctx), seq, pri, unique, in_ifindex); return; } out_dport = zd_dpdk_port_find_by_index(out_ifindex); if (!out_dport) { if (IS_ZEBRA_DEBUG_DPLANE_DPDK_DETAIL) zlog_debug( "PBR dpdk flow create ifname %s seq %d pri %u unique %d failed; out_port %d missing\n", dplane_ctx_rule_get_ifname(ctx), seq, pri, unique, out_ifindex); return; } /*********************** match items **************************/ memset(ð, 0, sizeof(eth)); memset(ð_mask, 0, sizeof(eth_mask)); eth.type = eth_mask.type = htons(RTE_ETHER_TYPE_IPV4); items[item_cnt].type = RTE_FLOW_ITEM_TYPE_ETH; items[item_cnt].spec = ð items[item_cnt].mask = ð_mask; items[item_cnt].last = NULL; ++item_cnt; memset(&ip, 0, sizeof(ip)); memset(&ip_mask, 0, sizeof(ip_mask)); if (filter_bm & PBR_FILTER_SRC_IP) { const struct prefix *src_ip; src_ip = dplane_ctx_rule_get_src_ip(ctx); ip.hdr.src_addr = src_ip->u.prefix4.s_addr; masklen2ip(src_ip->prefixlen, &tmp_mask); ip_mask.hdr.src_addr = tmp_mask.s_addr; } if (filter_bm & PBR_FILTER_DST_IP) { const struct prefix *dst_ip; dst_ip = dplane_ctx_rule_get_dst_ip(ctx); ip.hdr.dst_addr = dst_ip->u.prefix4.s_addr; masklen2ip(dst_ip->prefixlen, &tmp_mask); ip_mask.hdr.dst_addr = tmp_mask.s_addr; } if (filter_bm & PBR_FILTER_IP_PROTOCOL) { ip.hdr.next_proto_id = dplane_ctx_rule_get_ipproto(ctx); ip_mask.hdr.next_proto_id = UINT8_MAX; } items[item_cnt].type = RTE_FLOW_ITEM_TYPE_IPV4; items[item_cnt].spec = &ip; items[item_cnt].mask = &ip_mask; items[item_cnt].last = NULL; ++item_cnt; if ((filter_bm & (PBR_FILTER_SRC_PORT | PBR_FILTER_DST_PORT))) { memset(&udp, 0, sizeof(udp)); memset(&udp_mask, 0, sizeof(udp_mask)); if (filter_bm & PBR_FILTER_SRC_PORT) { udp.hdr.src_port = RTE_BE16(dplane_ctx_rule_get_src_port(ctx)); udp_mask.hdr.src_port = UINT16_MAX; } if (filter_bm & PBR_FILTER_DST_PORT) { udp.hdr.dst_port = RTE_BE16(dplane_ctx_rule_get_dst_port(ctx)); udp_mask.hdr.dst_port = UINT16_MAX; } items[item_cnt].type = RTE_FLOW_ITEM_TYPE_UDP; items[item_cnt].spec = &udp; items[item_cnt].mask = &udp_mask; items[item_cnt].last = NULL; ++item_cnt; } items[item_cnt].type = RTE_FLOW_ITEM_TYPE_END; /*************************** actions *****************************/ actions[act_cnt].type = RTE_FLOW_ACTION_TYPE_COUNT; memset(&conf_count, 0, sizeof(conf_count)); actions[act_cnt].conf = &conf_count; ++act_cnt; actions[act_cnt].type = RTE_FLOW_ACTION_TYPE_DEC_TTL; ++act_cnt; mac = dplane_ctx_rule_get_smac(ctx); memcpy(conf_smac.mac_addr, mac, RTE_ETHER_ADDR_LEN); actions[act_cnt].type = RTE_FLOW_ACTION_TYPE_SET_MAC_SRC; actions[act_cnt].conf = &conf_smac; ++act_cnt; mac = dplane_ctx_rule_get_dmac(ctx); memcpy(conf_dmac.mac_addr, mac, RTE_ETHER_ADDR_LEN); actions[act_cnt].type = RTE_FLOW_ACTION_TYPE_SET_MAC_DST; actions[act_cnt].conf = &conf_dmac; ++act_cnt; memset(&conf_port, 0, sizeof(conf_port)); conf_port.id = out_dport->port_id; actions[act_cnt].type = RTE_FLOW_ACTION_TYPE_PORT_ID; actions[act_cnt].conf = &conf_port; ++act_cnt; actions[act_cnt].type = RTE_FLOW_ACTION_TYPE_END; frr_with_privs (&zserv_privs) { flow = rte_flow_create(in_dport->port_id, &attrs, items, actions, &error); } if (flow) { dplane_ctx_rule_set_dp_flow_ptr(ctx, (intptr_t)flow); if (IS_ZEBRA_DEBUG_DPLANE_DPDK_DETAIL) zlog_debug( "PBR dpdk flow 0x%" PRIxPTR " created ifname %s seq %d pri %u unique %d\n", (intptr_t)flow, dplane_ctx_rule_get_ifname(ctx), seq, pri, unique); } else { zlog_warn( "PBR dpdk flow create failed ifname %s seq %d pri %u unique %d; rc %d\n", dplane_ctx_rule_get_ifname(ctx), seq, pri, unique, error.type); } } static void zd_dpdk_rule_del(struct zebra_dplane_ctx *ctx, const char *ifname, int in_ifindex, intptr_t dp_flow_ptr) { struct zd_dpdk_port *in_dport; struct rte_flow_error error; int rc; if (IS_ZEBRA_DEBUG_DPLANE_DPDK_DETAIL) zlog_debug( "PBR dpdk flow delete ifname %s ifindex %d dp_flow 0x%" PRIxPTR "\n", ifname, in_ifindex, dp_flow_ptr); if (!dp_flow_ptr) { if (IS_ZEBRA_DEBUG_DPLANE_DPDK_DETAIL) zlog_debug( "PBR dpdk flow delete failed; ifname %s ifindex %d dp_flow 0x%" PRIxPTR "; empty dp\n", ifname, in_ifindex, dp_flow_ptr); return; } dplane_ctx_rule_set_dp_flow_ptr(ctx, (intptr_t)NULL); in_dport = zd_dpdk_port_find_by_index(in_ifindex); if (!in_dport) { if (IS_ZEBRA_DEBUG_DPLANE_DPDK_DETAIL) zlog_debug( "PBR dpdk flow delete failed; ifname %s ifindex %d dp_flow 0x%" PRIxPTR " in port missing\n", ifname, in_ifindex, dp_flow_ptr); return; } frr_with_privs (&zserv_privs) { rc = rte_flow_destroy(in_dport->port_id, (struct rte_flow *)dp_flow_ptr, &error); } if (rc) zlog_warn( "PBR dpdk flow delete failed; ifname %s ifindex %d dp_flow 0x%" PRIxPTR "\n", ifname, in_ifindex, dp_flow_ptr); } static void zd_dpdk_rule_update(struct zebra_dplane_ctx *ctx) { enum dplane_op_e op; int in_ifindex; intptr_t dp_flow_ptr; if (IS_ZEBRA_DEBUG_DPLANE_DPDK_DETAIL) zlog_debug("Dplane %s", dplane_op2str(dplane_ctx_get_op(ctx))); op = dplane_ctx_get_op(ctx); switch (op) { case DPLANE_OP_RULE_ADD: atomic_fetch_add_explicit(&dpdk_stat->rule_adds, 1, memory_order_relaxed); zd_dpdk_rule_add(ctx); break; case DPLANE_OP_RULE_UPDATE: /* delete old rule and install new one */ atomic_fetch_add_explicit(&dpdk_stat->rule_adds, 1, memory_order_relaxed); in_ifindex = dplane_ctx_get_ifindex(ctx); dp_flow_ptr = dplane_ctx_rule_get_old_dp_flow_ptr(ctx); zd_dpdk_rule_del(ctx, dplane_ctx_rule_get_ifname(ctx), in_ifindex, dp_flow_ptr); zd_dpdk_rule_add(ctx); break; case DPLANE_OP_RULE_DELETE: atomic_fetch_add_explicit(&dpdk_stat->rule_dels, 1, memory_order_relaxed); in_ifindex = dplane_ctx_get_ifindex(ctx); dp_flow_ptr = dplane_ctx_rule_get_dp_flow_ptr(ctx); zd_dpdk_rule_del(ctx, dplane_ctx_rule_get_ifname(ctx), in_ifindex, dp_flow_ptr); break; case DPLANE_OP_NONE: case DPLANE_OP_ROUTE_INSTALL: case DPLANE_OP_ROUTE_UPDATE: case DPLANE_OP_ROUTE_DELETE: case DPLANE_OP_ROUTE_NOTIFY: case DPLANE_OP_NH_INSTALL: case DPLANE_OP_NH_UPDATE: case DPLANE_OP_NH_DELETE: case DPLANE_OP_LSP_INSTALL: case DPLANE_OP_LSP_UPDATE: case DPLANE_OP_LSP_DELETE: case DPLANE_OP_LSP_NOTIFY: case DPLANE_OP_PW_INSTALL: case DPLANE_OP_PW_UNINSTALL: case DPLANE_OP_SYS_ROUTE_ADD: case DPLANE_OP_SYS_ROUTE_DELETE: case DPLANE_OP_ADDR_INSTALL: case DPLANE_OP_ADDR_UNINSTALL: case DPLANE_OP_MAC_INSTALL: case DPLANE_OP_MAC_DELETE: case DPLANE_OP_NEIGH_INSTALL: case DPLANE_OP_NEIGH_UPDATE: case DPLANE_OP_NEIGH_DELETE: case DPLANE_OP_VTEP_ADD: case DPLANE_OP_VTEP_DELETE: case DPLANE_OP_NEIGH_DISCOVER: case DPLANE_OP_BR_PORT_UPDATE: case DPLANE_OP_IPTABLE_ADD: case DPLANE_OP_IPTABLE_DELETE: case DPLANE_OP_IPSET_ADD: case DPLANE_OP_IPSET_DELETE: case DPLANE_OP_IPSET_ENTRY_ADD: case DPLANE_OP_IPSET_ENTRY_DELETE: case DPLANE_OP_NEIGH_IP_INSTALL: case DPLANE_OP_NEIGH_IP_DELETE: case DPLANE_OP_NEIGH_TABLE_UPDATE: case DPLANE_OP_GRE_SET: case DPLANE_OP_INTF_ADDR_ADD: case DPLANE_OP_INTF_ADDR_DEL: case DPLANE_OP_INTF_NETCONFIG: case DPLANE_OP_INTF_INSTALL: case DPLANE_OP_INTF_UPDATE: case DPLANE_OP_INTF_DELETE: break; } } /* DPDK provider callback. */ static void zd_dpdk_process_update(struct zebra_dplane_ctx *ctx) { switch (dplane_ctx_get_op(ctx)) { case DPLANE_OP_RULE_ADD: case DPLANE_OP_RULE_UPDATE: case DPLANE_OP_RULE_DELETE: zd_dpdk_rule_update(ctx); break; case DPLANE_OP_NONE: case DPLANE_OP_ROUTE_INSTALL: case DPLANE_OP_ROUTE_UPDATE: case DPLANE_OP_ROUTE_DELETE: case DPLANE_OP_ROUTE_NOTIFY: case DPLANE_OP_NH_INSTALL: case DPLANE_OP_NH_UPDATE: case DPLANE_OP_NH_DELETE: case DPLANE_OP_LSP_INSTALL: case DPLANE_OP_LSP_UPDATE: case DPLANE_OP_LSP_DELETE: case DPLANE_OP_LSP_NOTIFY: case DPLANE_OP_PW_INSTALL: case DPLANE_OP_PW_UNINSTALL: case DPLANE_OP_SYS_ROUTE_ADD: case DPLANE_OP_SYS_ROUTE_DELETE: case DPLANE_OP_ADDR_INSTALL: case DPLANE_OP_ADDR_UNINSTALL: case DPLANE_OP_MAC_INSTALL: case DPLANE_OP_MAC_DELETE: case DPLANE_OP_NEIGH_INSTALL: case DPLANE_OP_NEIGH_UPDATE: case DPLANE_OP_NEIGH_DELETE: case DPLANE_OP_VTEP_ADD: case DPLANE_OP_VTEP_DELETE: case DPLANE_OP_NEIGH_DISCOVER: case DPLANE_OP_BR_PORT_UPDATE: case DPLANE_OP_IPTABLE_ADD: case DPLANE_OP_IPTABLE_DELETE: case DPLANE_OP_IPSET_ADD: case DPLANE_OP_IPSET_DELETE: case DPLANE_OP_IPSET_ENTRY_ADD: case DPLANE_OP_IPSET_ENTRY_DELETE: case DPLANE_OP_NEIGH_IP_INSTALL: case DPLANE_OP_NEIGH_IP_DELETE: case DPLANE_OP_NEIGH_TABLE_UPDATE: case DPLANE_OP_GRE_SET: case DPLANE_OP_INTF_ADDR_ADD: case DPLANE_OP_INTF_ADDR_DEL: case DPLANE_OP_INTF_NETCONFIG: case DPLANE_OP_INTF_INSTALL: case DPLANE_OP_INTF_UPDATE: case DPLANE_OP_INTF_DELETE: atomic_fetch_add_explicit(&dpdk_stat->ignored_updates, 1, memory_order_relaxed); break; } } static int zd_dpdk_process(struct zebra_dplane_provider *prov) { struct zebra_dplane_ctx *ctx; int counter, limit; if (IS_ZEBRA_DEBUG_DPLANE_DPDK_DETAIL) zlog_debug("processing %s", dplane_provider_get_name(prov)); limit = dplane_provider_get_work_limit(prov); for (counter = 0; counter < limit; counter++) { ctx = dplane_provider_dequeue_in_ctx(prov); if (!ctx) break; zd_dpdk_process_update(ctx); dplane_ctx_set_status(ctx, ZEBRA_DPLANE_REQUEST_SUCCESS); dplane_provider_enqueue_out_ctx(prov, ctx); } return 0; } static void zd_dpdk_port_show_entry(struct zd_dpdk_port *dport, struct vty *vty, int detail) { struct rte_eth_dev_info *dev_info; dev_info = &dport->dev_info; if (detail) { vty_out(vty, "DPDK port: %u\n", dport->port_id); vty_out(vty, " Device: %s\n", dev_info->device ? dev_info->device->name : "-"); vty_out(vty, " Driver: %s\n", dev_info->driver_name ? dev_info->driver_name : "-"); vty_out(vty, " Interface: %s (%d)\n", ifindex2ifname(dev_info->if_index, VRF_DEFAULT), dev_info->if_index); vty_out(vty, " Switch: %s Domain: %u Port: %u\n", dev_info->switch_info.name, dev_info->switch_info.domain_id, dev_info->switch_info.port_id); vty_out(vty, "\n"); } else { vty_out(vty, "%-4u %-16s %-16s %-16d %s,%u,%u\n", dport->port_id, dev_info->device ? dev_info->device->name : "-", ifindex2ifname(dev_info->if_index, VRF_DEFAULT), dev_info->if_index, dev_info->switch_info.name, dev_info->switch_info.domain_id, dev_info->switch_info.port_id); } } static struct zd_dpdk_port *zd_dpdk_port_find_by_index(int ifindex) { int count; struct zd_dpdk_port *dport; struct rte_eth_dev_info *dev_info; for (count = 0; count < RTE_MAX_ETHPORTS; ++count) { dport = &dpdk_ctx->dpdk_ports[count]; if (!(dport->flags & ZD_DPDK_PORT_FLAG_INITED)) continue; dev_info = &dport->dev_info; if (dev_info->if_index == (uint32_t)ifindex) return dport; } return NULL; } void zd_dpdk_port_show(struct vty *vty, uint16_t port_id, bool uj, int detail) { int count; struct zd_dpdk_port *dport; /* XXX - support for json is yet to be added */ if (uj) return; if (!detail) { vty_out(vty, "%-4s %-16s %-16s %-16s %s\n", "Port", "Device", "IfName", "IfIndex", "sw,domain,port"); } for (count = 0; count < RTE_MAX_ETHPORTS; ++count) { dport = &dpdk_ctx->dpdk_ports[count]; if (dport->flags & ZD_DPDK_PORT_FLAG_INITED) zd_dpdk_port_show_entry(dport, vty, detail); } } static void zd_dpdk_port_init(void) { struct zd_dpdk_port *dport; uint16_t port_id; struct rte_eth_dev_info *dev_info; int count; int rc; struct rte_flow_error error; /* allocate a list of ports */ dpdk_ctx->dpdk_ports = XCALLOC(MTYPE_DPDK_PORTS, sizeof(struct zd_dpdk_port) * RTE_MAX_ETHPORTS); if (IS_ZEBRA_DEBUG_DPLANE_DPDK) zlog_debug("dpdk port init"); count = 0; RTE_ETH_FOREACH_DEV(port_id) { if (IS_ZEBRA_DEBUG_DPLANE_DPDK) zlog_debug("dpdk port init %d", port_id); dport = &dpdk_ctx->dpdk_ports[count]; count++; dport->port_id = port_id; dport->flags |= ZD_DPDK_PORT_FLAG_PROBED; dev_info = &dport->dev_info; if (rte_eth_dev_info_get(port_id, dev_info) < 0) { zlog_warn("failed to get dev info for %u, %s", port_id, rte_strerror(rte_errno)); continue; } dport->flags |= ZD_DPDK_PORT_FLAG_INITED; if (IS_ZEBRA_DEBUG_DPLANE_DPDK) zlog_debug( "port %u, dev %s, ifI %d, sw_name %s, sw_domain %u, sw_port %u", port_id, dev_info->device ? dev_info->device->name : "-", dev_info->if_index, dev_info->switch_info.name, dev_info->switch_info.domain_id, dev_info->switch_info.port_id); if (rte_flow_isolate(port_id, 1, &error)) { if (IS_ZEBRA_DEBUG_DPLANE_DPDK) zlog_debug( "Flow isolate on port %u failed %d", port_id, error.type); } else { if (IS_ZEBRA_DEBUG_DPLANE_DPDK) zlog_debug("Flow isolate on port %u", port_id); } rc = rte_eth_dev_start(port_id); if (rc) { zlog_warn("DPDK port %d start error: %s", port_id, rte_strerror(-rc)); continue; } if (IS_ZEBRA_DEBUG_DPLANE_DPDK) zlog_debug("DPDK port %d started in promiscuous mode ", port_id); } if (!count) { if (IS_ZEBRA_DEBUG_DPLANE_DPDK) zlog_debug("no probed ethernet devices"); } } static int zd_dpdk_init(void) { int rc; static const char *argv[] = {(char *)"/usr/lib/frr/zebra", (char *)"--"}; zd_dpdk_vty_init(); frr_with_privs (&zserv_privs) { rc = rte_eal_init(array_size(argv), argv); } if (rc < 0) { zlog_warn("EAL init failed %s", rte_strerror(rte_errno)); return -1; } frr_with_privs (&zserv_privs) { zd_dpdk_port_init(); } return 0; } static int zd_dpdk_start(struct zebra_dplane_provider *prov) { if (IS_ZEBRA_DEBUG_DPLANE_DPDK) zlog_debug("%s start", dplane_provider_get_name(prov)); return zd_dpdk_init(); } static int zd_dpdk_finish(struct zebra_dplane_provider *prov, bool early) { int rc; if (early) { if (IS_ZEBRA_DEBUG_DPLANE_DPDK) zlog_debug("%s early finish", dplane_provider_get_name(prov)); return 0; } if (IS_ZEBRA_DEBUG_DPLANE_DPDK) zlog_debug("%s finish", dplane_provider_get_name(prov)); frr_with_privs (&zserv_privs) { rc = rte_eal_cleanup(); } if (rc < 0) zlog_warn("EAL cleanup failed %s", rte_strerror(rte_errno)); return 0; } static int zd_dpdk_plugin_init(struct event_loop *tm) { int ret; ret = dplane_provider_register( plugin_name, DPLANE_PRIO_KERNEL, DPLANE_PROV_FLAGS_DEFAULT, zd_dpdk_start, zd_dpdk_process, zd_dpdk_finish, dpdk_ctx, NULL); if (IS_ZEBRA_DEBUG_DPLANE_DPDK) zlog_debug("%s register status %d", plugin_name, ret); return 0; } static int zd_dpdk_module_init(void) { hook_register(frr_late_init, zd_dpdk_plugin_init); return 0; } FRR_MODULE_SETUP(.name = "dplane_dpdk", .version = "0.0.1", .description = "Data plane plugin using dpdk for hw offload", .init = zd_dpdk_module_init);