2022-01-01 18:18:33 +01:00
|
|
|
/*
|
|
|
|
* Zebra dataplane plugin for DPDK based hw offload
|
|
|
|
*
|
|
|
|
* Copyright (C) 2021 Nvidia
|
|
|
|
* Anuradha Karuppiah
|
|
|
|
*
|
|
|
|
* This program is free software; you can redistribute it and/or modify
|
|
|
|
* it under the terms of the GNU General Public License as published by
|
|
|
|
* the Free Software Foundation; either version 2 of the License, or
|
|
|
|
* (at your option) any later version.
|
|
|
|
*
|
|
|
|
* This program is distributed in the hope that it will be useful, but
|
|
|
|
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
|
|
* General Public License for more details.
|
|
|
|
*
|
|
|
|
* You should have received a copy of the GNU General Public License along
|
|
|
|
* with this program; see the file COPYING; if not, write to the Free Software
|
|
|
|
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
|
|
|
*/
|
|
|
|
|
|
|
|
#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";
|
|
|
|
|
2021-12-30 23:49:19 +01:00
|
|
|
extern struct zebra_privs_t zserv_privs;
|
|
|
|
|
2022-01-01 18:18:33 +01:00
|
|
|
static struct zd_dpdk_ctx dpdk_ctx_buf, *dpdk_ctx = &dpdk_ctx_buf;
|
|
|
|
#define dpdk_stat (&dpdk_ctx->stats)
|
|
|
|
|
2021-12-30 23:56:30 +01:00
|
|
|
static struct zd_dpdk_port *zd_dpdk_port_find_by_index(int ifindex);
|
|
|
|
|
|
|
|
DEFINE_MTYPE_STATIC(ZEBRA, DPDK_PORTS, "ZD DPDK port database");
|
2022-01-01 18:18:33 +01:00
|
|
|
|
|
|
|
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_rule_add(struct zebra_dplane_ctx *ctx)
|
|
|
|
{
|
|
|
|
/* XXX - place holder */
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static void zd_dpdk_rule_del(const char *ifname, int in_ifindex,
|
|
|
|
intptr_t dp_flow_ptr)
|
|
|
|
{
|
|
|
|
|
|
|
|
/* XXX - place holder */
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
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(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(dplane_ctx_rule_get_ifname(ctx), in_ifindex,
|
|
|
|
dp_flow_ptr);
|
|
|
|
break;
|
|
|
|
|
|
|
|
default:;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* 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;
|
|
|
|
|
|
|
|
default:
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2021-12-30 23:56:30 +01:00
|
|
|
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\n",
|
|
|
|
port_id, error.type);
|
|
|
|
} else {
|
|
|
|
if (IS_ZEBRA_DEBUG_DPLANE_DPDK)
|
|
|
|
zlog_debug("Flow isolate on port %u\n",
|
|
|
|
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");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2021-12-30 23:49:19 +01:00
|
|
|
static int zd_dpdk_init(void)
|
|
|
|
{
|
|
|
|
int rc;
|
|
|
|
char *argv[] = {(char *)"/usr/lib/frr/zebra", (char *)"--"};
|
|
|
|
|
|
|
|
zd_dpdk_vty_init();
|
|
|
|
|
|
|
|
frr_with_privs (&zserv_privs) {
|
|
|
|
rc = rte_eal_init(sizeof(argv) / sizeof(argv[0]), argv);
|
|
|
|
}
|
|
|
|
if (rc < 0) {
|
|
|
|
zlog_warn("EAL init failed %s", rte_strerror(rte_errno));
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2021-12-30 23:56:30 +01:00
|
|
|
frr_with_privs (&zserv_privs) {
|
|
|
|
zd_dpdk_port_init();
|
|
|
|
}
|
2021-12-30 23:49:19 +01:00
|
|
|
return 0;
|
|
|
|
}
|
2022-01-01 18:18:33 +01:00
|
|
|
|
2021-12-30 23:56:30 +01:00
|
|
|
|
2022-01-01 18:18:33 +01:00
|
|
|
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));
|
|
|
|
|
2021-12-30 23:49:19 +01:00
|
|
|
return zd_dpdk_init();
|
2022-01-01 18:18:33 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static int zd_dpdk_finish(struct zebra_dplane_provider *prov, bool early)
|
|
|
|
{
|
2021-12-30 23:49:19 +01:00
|
|
|
int rc;
|
|
|
|
|
2022-01-01 18:18:33 +01:00
|
|
|
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));
|
|
|
|
|
|
|
|
|
2021-12-30 23:49:19 +01:00
|
|
|
frr_with_privs (&zserv_privs) {
|
|
|
|
rc = rte_eal_cleanup();
|
|
|
|
}
|
|
|
|
if (rc < 0)
|
|
|
|
zlog_warn("EAL cleanup failed %s", rte_strerror(rte_errno));
|
|
|
|
|
2022-01-01 18:18:33 +01:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static int zd_dpdk_plugin_init(struct thread_master *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, );
|