diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-09 13:16:35 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-09 13:16:35 +0000 |
commit | e2bbf175a2184bd76f6c54ccf8456babeb1a46fc (patch) | |
tree | f0b76550d6e6f500ada964a3a4ee933a45e5a6f1 /zebra/dpdk | |
parent | Initial commit. (diff) | |
download | frr-e2bbf175a2184bd76f6c54ccf8456babeb1a46fc.tar.xz frr-e2bbf175a2184bd76f6c54ccf8456babeb1a46fc.zip |
Adding upstream version 9.1.upstream/9.1
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'zebra/dpdk')
-rw-r--r-- | zebra/dpdk/zebra_dplane_dpdk.c | 720 | ||||
-rw-r--r-- | zebra/dpdk/zebra_dplane_dpdk.h | 23 | ||||
-rw-r--r-- | zebra/dpdk/zebra_dplane_dpdk_private.h | 48 | ||||
-rw-r--r-- | zebra/dpdk/zebra_dplane_dpdk_vty.c | 70 |
4 files changed, 861 insertions, 0 deletions
diff --git a/zebra/dpdk/zebra_dplane_dpdk.c b/zebra/dpdk/zebra_dplane_dpdk.c new file mode 100644 index 0000000..4c32044 --- /dev/null +++ b/zebra/dpdk/zebra_dplane_dpdk.c @@ -0,0 +1,720 @@ +// 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); diff --git a/zebra/dpdk/zebra_dplane_dpdk.h b/zebra/dpdk/zebra_dplane_dpdk.h new file mode 100644 index 0000000..e5a3dbe --- /dev/null +++ b/zebra/dpdk/zebra_dplane_dpdk.h @@ -0,0 +1,23 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Zebra dataplane plugin for DPDK based hw offload + * + * Copyright (C) 2021 Nvidia + * Anuradha Karuppiah + */ + +#ifndef _ZEBRA_DPLANE_DPDK_H +#define _ZEBRA_DPLANE_DPDK_H + +#include <zebra.h> + + +#define ZD_DPDK_INVALID_PORT 0xffff + +extern void zd_dpdk_pbr_flows_show(struct vty *vty); +extern void zd_dpdk_port_show(struct vty *vty, uint16_t port_id, bool uj, + int detail); +extern void zd_dpdk_stat_show(struct vty *vty); +extern void zd_dpdk_vty_init(void); + +#endif diff --git a/zebra/dpdk/zebra_dplane_dpdk_private.h b/zebra/dpdk/zebra_dplane_dpdk_private.h new file mode 100644 index 0000000..e10f525 --- /dev/null +++ b/zebra/dpdk/zebra_dplane_dpdk_private.h @@ -0,0 +1,48 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Zebra dataplane plugin for DPDK based hw offload + * + * Copyright (C) 2021 Nvidia + * Anuradha Karuppiah + */ + +#ifndef _ZEBRA_DPLANE_DPDK_PRIVATE_H +#define _ZEBRA_DPLANE_DPDK_PRIVATE_H + +#include <zebra.h> + +#include <rte_ethdev.h> + +#include "zebra_dplane_dpdk.h" + +/* match on eth, sip, dip, udp */ +#define ZD_PBR_PATTERN_MAX 6 +/* dec_ttl, set_smac, set_dmac, * phy_port, count + */ +#define ZD_PBR_ACTION_MAX 6 + +#define ZD_ETH_TYPE_IP 0x800 + +struct zd_dpdk_port { + uint16_t port_id; /* dpdk port_id */ + struct rte_eth_dev_info dev_info; /* PCI info + driver name */ + uint32_t flags; +#define ZD_DPDK_PORT_FLAG_PROBED (1 << 0) +#define ZD_DPDK_PORT_FLAG_INITED (1 << 1) +}; + +struct zd_dpdk_stat { + _Atomic uint32_t ignored_updates; + + _Atomic uint32_t rule_adds; + _Atomic uint32_t rule_dels; +}; + +struct zd_dpdk_ctx { + /* Stats */ + struct zd_dpdk_stat stats; + struct zd_dpdk_port *dpdk_ports; + int dpdk_logtype; +}; + +#endif diff --git a/zebra/dpdk/zebra_dplane_dpdk_vty.c b/zebra/dpdk/zebra_dplane_dpdk_vty.c new file mode 100644 index 0000000..45334a7 --- /dev/null +++ b/zebra/dpdk/zebra_dplane_dpdk_vty.c @@ -0,0 +1,70 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Zebra dataplane plugin for DPDK based hw offload + * + * Copyright (C) 2021 Nvidia + * Donald Sharp + */ +#include <zebra.h> + +#include "lib/json.h" +#include "zebra/dpdk/zebra_dplane_dpdk.h" + +#include "zebra/dpdk/zebra_dplane_dpdk_vty_clippy.c" + +#define ZD_STR "Zebra dataplane information\n" +#define ZD_DPDK_STR "DPDK offload information\n" + +DEFPY(zd_dpdk_show_counters, zd_dpdk_show_counters_cmd, + "show dplane dpdk counters", + SHOW_STR ZD_STR ZD_DPDK_STR "show counters\n") +{ + zd_dpdk_stat_show(vty); + + return CMD_SUCCESS; +} + + +DEFPY (zd_dpdk_show_ports, + zd_dpdk_show_ports_cmd, + "show dplane dpdk port [(1-32)$port_id] [detail$detail] [json$json]", + SHOW_STR + ZD_STR + ZD_DPDK_STR + "show port info\n" + "DPDK port identifier\n" + "Detailed information\n" + JSON_STR) +{ + bool uj = !!json; + bool ud = !!detail; + + if (!port_id) + port_id = ZD_DPDK_INVALID_PORT; + zd_dpdk_port_show(vty, port_id, uj, ud); + + return CMD_SUCCESS; +} + + +DEFPY (zd_dpdk_show_pbr_flows, + zd_dpdk_show_pbr_flows_cmd, + "show dplane dpdk pbr flows", + SHOW_STR + ZD_STR + ZD_DPDK_STR + "show pbr info\n" + "DPDK flows\n") +{ + zd_dpdk_pbr_flows_show(vty); + + return CMD_SUCCESS; +} + + +void zd_dpdk_vty_init(void) +{ + install_element(VIEW_NODE, &zd_dpdk_show_counters_cmd); + install_element(VIEW_NODE, &zd_dpdk_show_ports_cmd); + install_element(VIEW_NODE, &zd_dpdk_show_pbr_flows_cmd); +} |