Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
830 changes: 442 additions & 388 deletions docs/graph.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
7 changes: 4 additions & 3 deletions docs/meson.build
Original file line number Diff line number Diff line change
Expand Up @@ -74,9 +74,10 @@ custom_target(
# Individual command man pages
# The list is hardcoded since we can't run grcli during meson configuration.
grcli_commands = [
'address', 'affinity', 'conntrack', 'dnat44', 'events', 'graph', 'interface',
'logging', 'nexthop', 'ping', 'ping6', 'route', 'router-advert', 'snat44',
'stats', 'trace', 'traceroute', 'traceroute6', 'tunsrc',
'address', 'affinity', 'conntrack', 'dnat44', 'events', 'fdb', 'graph',
'interface', 'logging', 'nexthop', 'ping', 'ping6', 'route',
'router-advert', 'snat44', 'stats', 'trace', 'traceroute', 'traceroute6',
'tunsrc',
]

foreach cmd : grcli_commands
Expand Down
19 changes: 2 additions & 17 deletions modules/infra/control/control_queue.c → main/control_queue.c
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,12 @@
// Copyright (c) 2024 Christophe Fontaine

#include <gr_control_queue.h>
#include <gr_event.h>
#include <gr_iface.h>
#include <gr_log.h>
#include <gr_macro.h>
#include <gr_module.h>
#include <gr_nexthop.h>

#include <event2/event.h>
#include <rte_lcore.h>
#include <rte_ring.h>

#include <pthread.h>
Expand Down Expand Up @@ -88,23 +86,11 @@ static void *sem_wait_to_event(void *) {
return NULL;
}

// When interfaces or nexthops are deleted, drain the control queue
// to free any packets that reference the deleted object. This prevents
// callbacks from being invoked with dangling pointers.
static void event_handler(uint32_t event, const void *obj) {
void control_queue_drain(uint32_t event, const void *obj) {
struct control_queue_drain drain = {event, obj};
control_queue_poll(0, 0, &drain);
}
Comment thread
rjarry marked this conversation as resolved.

static struct gr_event_subscription event_sub = {
.callback = event_handler,
.ev_count = 2,
.ev_types = {
GR_EVENT_IFACE_REMOVE,
GR_EVENT_NEXTHOP_DELETE,
},
};

static void control_queue_init(struct event_base *ev_base) {
atomic_init(&thread_shutdown, false);

Expand Down Expand Up @@ -146,5 +132,4 @@ static struct gr_module module = {

RTE_INIT(control_queue_module_init) {
gr_register_module(&module);
gr_event_subscribe(&event_sub);
}
20 changes: 19 additions & 1 deletion main/event.c
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,13 @@
#include "api.h"

#include <gr_api.h>
#include <gr_control_queue.h>
#include <gr_event.h>
#include <gr_log.h>
#include <gr_queue.h>

#include <rte_lcore.h>

#include <string.h>

STAILQ_HEAD(subscribers, gr_event_subscription);
Expand All @@ -17,7 +20,7 @@ void gr_event_subscribe(struct gr_event_subscription *sub) {
STAILQ_INSERT_TAIL(&subscribers, sub, next);
}

void gr_event_push(uint32_t ev_type, const void *obj) {
static void notify_subscribers(void *obj, uintptr_t ev_type, const struct control_queue_drain *) {
const struct gr_event_subscription *sub;

STAILQ_FOREACH (sub, &subscribers, next) {
Expand All @@ -28,9 +31,24 @@ void gr_event_push(uint32_t ev_type, const void *obj) {
}
}
}

api_send_notifications(ev_type, obj);
}

void gr_event_push(uint32_t ev_type, const void *obj) {
if (rte_lcore_has_role(rte_lcore_id(), ROLE_NON_EAL)) {
// Called from a dataplane worker thread.
// Defer the notification to the control plane thread.
if (control_queue_push(notify_subscribers, (void *)obj, ev_type) < 0) {
// XXX: add error stat if push fails?
}
Comment thread
rjarry marked this conversation as resolved.
} else {
// Called from the control plane thread.
// Notify subscribers immediately.
notify_subscribers((void *)obj, ev_type, NULL);
}
}

STAILQ_HEAD(serializers, gr_event_serializer);
static struct serializers serializers = STAILQ_HEAD_INITIALIZER(serializers);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,10 @@ struct control_queue_drain {
const void *obj; // Object being deleted
};

// Force drain the control queue from all items.
// Pass ev_type and deleted_obj to item callbacks so that they can ignore/free references.
void control_queue_drain(uint32_t ev_type, const void *deleted_obj);

// Callback definition to pass arbitrary data to be processed by the control plane event loop.
// It is up to the function to free any data referenced by the pointer if necessary.
//
Expand Down
1 change: 1 addition & 0 deletions main/meson.build
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@

src += files(
'api.c',
'control_queue.c',
'dpdk.c',
'event.c',
'main.c',
Expand Down
9 changes: 7 additions & 2 deletions modules/infra/api/gr_infra.h
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ typedef enum : uint8_t {
GR_IFACE_TYPE_VLAN,
GR_IFACE_TYPE_IPIP,
GR_IFACE_TYPE_BOND,
GR_IFACE_TYPE_BRIDGE,
GR_IFACE_TYPE_COUNT
} gr_iface_type_t;

Expand Down Expand Up @@ -56,6 +57,7 @@ typedef enum : uint8_t {
GR_IFACE_MODE_VRF = 0,
GR_IFACE_MODE_XC,
GR_IFACE_MODE_BOND,
GR_IFACE_MODE_BRIDGE,
GR_IFACE_MODE_COUNT
} gr_iface_mode_t;

Expand Down Expand Up @@ -433,8 +435,6 @@ struct gr_infra_cpu_affinity_set_req {
// Helper function to convert iface type enum to string
static inline const char *gr_iface_type_name(gr_iface_type_t type) {
switch (type) {
case GR_IFACE_TYPE_UNDEF:
return "undef";
case GR_IFACE_TYPE_VRF:
return "vrf";
case GR_IFACE_TYPE_PORT:
Expand All @@ -445,6 +445,9 @@ static inline const char *gr_iface_type_name(gr_iface_type_t type) {
return "ipip";
case GR_IFACE_TYPE_BOND:
return "bond";
case GR_IFACE_TYPE_BRIDGE:
return "bridge";
case GR_IFACE_TYPE_UNDEF:
case GR_IFACE_TYPE_COUNT:
break;
}
Expand All @@ -460,6 +463,8 @@ static inline const char *gr_iface_mode_name(gr_iface_mode_t mode) {
return "XC";
case GR_IFACE_MODE_BOND:
return "bond";
case GR_IFACE_MODE_BRIDGE:
return "bridge";
case GR_IFACE_MODE_COUNT:
break;
}
Expand Down
4 changes: 3 additions & 1 deletion modules/infra/control/ctlplane.c
Original file line number Diff line number Diff line change
Expand Up @@ -206,7 +206,8 @@ static void iface_cp_poll(evutil_socket_t, short reason, void *ev_iface) {
}
}

mbuf_data(mbuf)->iface = iface;
iface_mbuf_data(mbuf)->iface = iface;
iface_mbuf_data(mbuf)->vlan_id = 0;

if (post_to_stack(iface_output, mbuf) < 0) {
LOG(ERR, "post_to_stack: %s", strerror(errno));
Expand Down Expand Up @@ -396,6 +397,7 @@ static void iface_event(uint32_t event, const void *obj) {
case GR_IFACE_TYPE_PORT:
case GR_IFACE_TYPE_VLAN:
case GR_IFACE_TYPE_BOND:
case GR_IFACE_TYPE_BRIDGE:
break;
default:
return;
Expand Down
9 changes: 6 additions & 3 deletions modules/infra/control/iface.c
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
// Copyright (c) 2024 Robin Jarry

#include <gr_config.h>
#include <gr_control_queue.h>
#include <gr_event.h>
#include <gr_iface.h>
#include <gr_log.h>
Expand Down Expand Up @@ -33,6 +34,7 @@ static bool iface_type_valid(gr_iface_type_t type) {
case GR_IFACE_TYPE_VLAN:
case GR_IFACE_TYPE_IPIP:
case GR_IFACE_TYPE_BOND:
case GR_IFACE_TYPE_BRIDGE:
return true;
case GR_IFACE_TYPE_UNDEF:
case GR_IFACE_TYPE_COUNT:
Expand Down Expand Up @@ -586,10 +588,11 @@ int iface_destroy(struct iface *iface) {

rte_rcu_qsbr_synchronize(gr_datapath_rcu(), RTE_QSBR_THRID_INVALID);

// Push IFACE_REMOVE event after RCU sync to ensure all datapath threads
// Drain the control queue after RCU sync to ensure all datapath threads
// have seen that this iface is gone. At this point, only packets already
// in the control queue may still reference it. The event triggers
// a drain that frees those packets before type->fini() frees the iface.
// in the control queue may still reference it.
control_queue_drain(GR_EVENT_IFACE_REMOVE, iface);

gr_event_push(GR_EVENT_IFACE_REMOVE, iface);

type = iface_type_get(iface->type);
Expand Down
1 change: 0 additions & 1 deletion modules/infra/control/meson.build
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@

src += files(
'bond.c',
'control_queue.c',
'ctlplane.c',
'graph.c',
'group_nexthop.c',
Expand Down
7 changes: 4 additions & 3 deletions modules/infra/control/nexthop.c
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
// SPDX-License-Identifier: BSD-3-Clause
// Copyright (c) 2024 Robin Jarry

#include <gr_control_queue.h>
#include <gr_event.h>
#include <gr_id_pool.h>
#include <gr_iface.h>
Expand Down Expand Up @@ -478,11 +479,11 @@ void nexthop_destroy(struct nexthop *nh) {

rte_rcu_qsbr_synchronize(gr_datapath_rcu(), RTE_QSBR_THRID_INVALID);

// Push NEXTHOP_DELETE event after RCU sync to ensure all datapath
// Drain the control queue after RCU sync to ensure all datapath
// threads have seen that this nexthop is gone. At this point, only
// packets already in the control queue may still reference it.
// The event triggers a drain that frees those packets before we free
// the nexthop memory.
control_queue_drain(GR_EVENT_NEXTHOP_DELETE, nh);

if (nh->origin != GR_NH_ORIGIN_INTERNAL)
gr_event_push(GR_EVENT_NEXTHOP_DELETE, nh);

Expand Down
2 changes: 2 additions & 0 deletions modules/infra/datapath/eth_output.c
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,8 @@ eth_output_process(struct rte_graph *graph, struct rte_node *node, void **objs,
eth->src_addr = src_mac;
eth->ether_type = priv->ether_type;

iface_mbuf_data(mbuf)->vlan_id = 0;

edge = OUTPUT;
next:
if (gr_mbuf_is_traced(mbuf)) {
Expand Down
21 changes: 9 additions & 12 deletions modules/infra/datapath/iface_output.c
Original file line number Diff line number Diff line change
Expand Up @@ -56,45 +56,42 @@ static uint16_t iface_output_process(
void **objs,
uint16_t nb_objs
) {
uint16_t iface_id, vlan_id;
const struct iface *iface;
struct iface_mbuf_data *d;
struct rte_mbuf *m;
rte_edge_t edge;

IFACE_STATS_VARS(tx);

for (uint16_t i = 0; i < nb_objs; i++) {
m = objs[i];
iface = mbuf_data(m)->iface;
iface_id = iface->id;
d = iface_mbuf_data(m);
iface = d->iface;

if (iface->type == GR_IFACE_TYPE_VLAN) {
const struct iface_info_vlan *vlan = iface_info_vlan(iface);
vlan_id = vlan->vlan_id;
d->vlan_id = vlan->vlan_id;
iface = iface_from_id(vlan->parent_id);
} else {
vlan_id = 0;
}

if (gr_mbuf_is_traced(m)) {
struct iface_output_trace_data *t = gr_mbuf_trace_add(m, node, sizeof(*t));
t->iface_id = iface_id;
t->vlan_id = vlan_id;
t->iface_id = d->iface->id;
t->vlan_id = d->vlan_id;
}

if (iface == NULL) {
edge = NO_PARENT;
goto next;
}
if (!(iface->flags & GR_IFACE_F_UP)) {
if (!(d->iface->flags & GR_IFACE_F_UP)) {
edge = IFACE_DOWN;
goto next;
}

IFACE_STATS_INC(tx, m, iface);
IFACE_STATS_INC(tx, m, d->iface);

iface_mbuf_data(m)->iface = iface;
iface_mbuf_data(m)->vlan_id = vlan_id;
d->iface = iface;
edge = iface_type_edges[iface->type];
next:
rte_node_enqueue_x1(graph, node, edge, m);
Expand Down
Loading