Flow Scaling (10M Flows)

This commit is contained in:
Christian Giese
2024-02-06 12:54:50 +00:00
committed by Hannes Gredler
parent 3c3cd25bc9
commit 0365835fbf
28 changed files with 982 additions and 947 deletions
+6 -3
View File
@@ -590,6 +590,12 @@ main(int argc, char *argv[])
}
LOG(INFO, "Total PPS of all streams: %.2f\n", g_ctx->total_pps);
if(!bbl_stream_index_init()) {
fprintf(stderr, "Error: Failed to init stream index\n");
goto CLEANUP;
}
/* Setup control job. */
timer_add_periodic(&g_ctx->timer_root, &g_ctx->control_timer, "Control Timer",
1, 0, g_ctx, &bbl_ctrl_job);
@@ -601,9 +607,6 @@ main(int argc, char *argv[])
}
}
/* Init IO stream token buckets. */
io_init_stream_token_bucket();
/* Start smear job. Use a crazy nsec bucket '12345678',
* such that we do not accidentally smear ourselves. */
timer_add_periodic(&g_ctx->timer_root, &g_ctx->smear_timer, "Timer Smearing",
+1 -1
View File
@@ -92,7 +92,7 @@ bbl_a10nsp_interfaces_add()
/* Timer to compute periodic rates */
timer_add_periodic(&g_ctx->timer_root, &a10nsp_interface->rate_job, "Rate Computation", 1, 0, a10nsp_interface,
&bbl_a10nsp_interface_rate_job);
a10nsp_interface->rate_job->reset = false;
LOG(DEBUG, "Added a10nsp interface %s\n", a10nsp_config->interface);
a10nsp_config = a10nsp_config->next;
}
+1 -1
View File
@@ -78,7 +78,7 @@ bbl_access_interfaces_add()
/* Timer to compute periodic rates */
timer_add_periodic(&g_ctx->timer_root, &access_interface->rate_job, "Rate Computation", 1, 0, access_interface,
&bbl_access_interface_rate_job);
access_interface->rate_job->reset = false;
LOG(DEBUG, "Added access interface %s\n", access_config->interface);
}
access_config->access_interface = interface->access;
+50 -30
View File
@@ -459,6 +459,7 @@ link_add(char *interface_name)
link_config = calloc(1, sizeof(bbl_link_config_s));
link_config->interface = strdup(interface_name);
link_config->io_mode = g_ctx->config.io_mode;
link_config->io_burst = g_ctx->config.io_burst;
link_config->io_slots_rx = g_ctx->config.io_slots;
link_config->io_slots_tx = g_ctx->config.io_slots;
link_config->qdisc_bypass = g_ctx->config.qdisc_bypass;
@@ -479,11 +480,12 @@ json_parse_link(json_t *link, bbl_link_config_s *link_config)
const char *schema[] = {
"interface", "description", "mac",
"io-mode", "io-slots", "io-slots-tx",
"io-slots-rx", "qdisc-bypass", "tx-interval",
"rx-interval", "tx-threads", "rx-threads",
"rx-cpuset", "tx-cpuset", "lag-interface",
"lacp-priority"
"io-mode", "io-slots" "io-burst",
"io-slots-tx", "io-slots-rx", "qdisc-bypass",
"tx-interval","rx-interval",
"tx-threads", "rx-threads",
"rx-cpuset", "tx-cpuset",
"lag-interface", "lacp-priority"
};
if(!schema_validate(link, "links", schema,
sizeof(schema)/sizeof(schema[0]))) {
@@ -553,7 +555,12 @@ json_parse_link(json_t *link, bbl_link_config_s *link_config)
if(value) {
link_config->io_slots_rx = json_number_value(value);
}
JSON_OBJ_GET_NUMBER(link, value, "links", "io-burst", 1, 65535);
if(value) {
link_config->io_burst = json_number_value(value);
} else {
link_config->io_burst = g_ctx->config.io_burst;
}
JSON_OBJ_GET_BOOL(link, value, "links", "qdisc-bypass");
if(value) {
link_config->qdisc_bypass = json_boolean_value(value);
@@ -2538,31 +2545,38 @@ json_parse_config_streams(json_t *root)
bbl_stream_config_s *stream_config = g_ctx->config.stream_config;
if(json_typeof(root) != JSON_OBJECT) {
fprintf(stderr, "JSON config error: Configuration root element must object\n");
fprintf(stderr, "JSON config error: Configuration root element must be of type object\n");
return false;
}
section = json_object_get(root, "streams");
if(json_is_array(section)) {
/* Get tail end of stream-config list. */
if(stream_config) {
while(stream_config->next) {
stream_config = stream_config->next;
}
if(section) {
if(!json_is_array(section)) {
fprintf(stderr, "JSON config error: Configuration streams element must contain list of objects\n");
return false;
}
/* Config is provided as array (multiple streams) */
size = json_array_size(section);
for(i = 0; i < size; i++) {
if(!stream_config) {
g_ctx->config.stream_config = calloc(1, sizeof(bbl_stream_config_s));
stream_config = g_ctx->config.stream_config;
} else {
stream_config->next = calloc(1, sizeof(bbl_stream_config_s));
stream_config = stream_config->next;
}
if(!json_parse_stream(json_array_get(section, i), stream_config)) {
return false;
}
} else {
return true;
}
/* Get tail end of stream-config list. */
if(stream_config) {
while(stream_config->next) {
stream_config = stream_config->next;
}
}
/* Config is provided as array (multiple streams) */
size = json_array_size(section);
for(i = 0; i < size; i++) {
if(!stream_config) {
g_ctx->config.stream_config = calloc(1, sizeof(bbl_stream_config_s));
stream_config = g_ctx->config.stream_config;
} else {
stream_config->next = calloc(1, sizeof(bbl_stream_config_s));
stream_config = stream_config->next;
}
if(!json_parse_stream(json_array_get(section, i), stream_config)) {
return false;
}
}
return true;
@@ -3571,7 +3585,7 @@ json_parse_config(json_t *root)
if(json_is_object(section)) {
const char *schema[] = {
"io-mode", "io-slots", "qdisc-bypass",
"io-mode", "io-slots", "io-burst", "qdisc-bypass",
"tx-interval", "rx-interval", "tx-threads",
"rx-threads", "capture-include-streams", "mac-modifier",
"lag", "network", "access", "a10nsp", "links"
@@ -3608,6 +3622,11 @@ json_parse_config(json_t *root)
if(value) {
g_ctx->config.io_slots = json_number_value(value);
}
value = json_object_get(section, "io-burst");
JSON_OBJ_GET_NUMBER(section, value, "interfaces", "io-burst", 1, 65535);
if(value) {
g_ctx->config.io_burst = json_number_value(value);
}
JSON_OBJ_GET_BOOL(section, value, "interfaces", "qdisc-bypass");
if(value) {
g_ctx->config.qdisc_bypass = json_boolean_value(value);
@@ -4041,9 +4060,10 @@ bbl_config_init_defaults()
g_ctx->pcap.include_streams = false;
g_ctx->config.username = g_default_user;
g_ctx->config.password = g_default_pass;
g_ctx->config.tx_interval = 1 * MSEC;
g_ctx->config.rx_interval = 1 * MSEC;
g_ctx->config.tx_interval = 0.1 * MSEC;
g_ctx->config.rx_interval = 0.1 * MSEC;
g_ctx->config.io_slots = 4096;
g_ctx->config.io_burst = 256;
g_ctx->config.io_max_stream_len = 9000;
g_ctx->config.qdisc_bypass = true;
g_ctx->config.sessions = 1;
@@ -4099,7 +4119,7 @@ bbl_config_init_defaults()
g_ctx->config.multicast_traffic_pps = 1000;
g_ctx->config.traffic_autostart = true;
g_ctx->config.stream_rate_calc = true;
g_ctx->config.stream_max_burst = 32;
g_ctx->config.stream_max_burst = 16;
g_ctx->config.multicast_traffic_autostart = true;
g_ctx->config.session_traffic_autostart = true;
}
+1
View File
@@ -149,6 +149,7 @@ typedef struct bbl_link_config_
uint16_t io_slots_tx;
uint16_t io_slots_rx;
uint16_t io_burst;
bool qdisc_bypass;
+4 -4
View File
@@ -80,7 +80,6 @@ bbl_ctx_add()
g_ctx->vlan_session_dict = hashtable_dict_new((dict_compare_func)bbl_compare_key64, bbl_key64_hash, BBL_SESSION_HASHTABLE_SIZE);
g_ctx->l2tp_session_dict = hashtable_dict_new((dict_compare_func)bbl_compare_key32, bbl_key32_hash, BBL_SESSION_HASHTABLE_SIZE);
g_ctx->li_flow_dict = hashtable_dict_new((dict_compare_func)bbl_compare_key32, bbl_key32_hash, BBL_LI_HASHTABLE_SIZE);
g_ctx->stream_flow_dict = hashtable_dict_new((dict_compare_func)bbl_compare_key64, bbl_key64_hash, BBL_STREAM_FLOW_HASHTABLE_SIZE);
return true;
}
@@ -119,13 +118,14 @@ bbl_ctx_del() {
bbl_session_free(p);
}
}
free(g_ctx->session_list);
if(g_ctx->session_list) free(g_ctx->session_list);
if(g_ctx->stream_index) free(g_ctx->stream_index);
/* Free hash table dictionaries. */
dict_free(g_ctx->vlan_session_dict, NULL);
dict_free(g_ctx->l2tp_session_dict, NULL);
dict_free(g_ctx->li_flow_dict, NULL);
dict_free(g_ctx->stream_flow_dict, NULL);
pcapng_free();
free(g_ctx);
+7 -1
View File
@@ -78,7 +78,11 @@ typedef struct bbl_ctx_
dict *vlan_session_dict; /* hashtable for 1:1 vlan sessions */
dict *l2tp_session_dict; /* hashtable for L2TP sessions */
dict *li_flow_dict; /* hashtable for LI flows */
dict *stream_flow_dict; /* hashtable for traffic stream flows */
bbl_stream_s **stream_index;
bbl_stream_s *stream_head;
bbl_stream_s *stream_tail;
uint64_t streams;
bbl_stream_group_s *stream_groups;
@@ -89,6 +93,7 @@ typedef struct bbl_ctx_
char *ctrl_socket_path;
bbl_ctrl_thread_s *ctrl_thread;
io_thread_s *io_threads; /* single linked list of threads */
io_bucket_s *io_bucket;
bool tcp;
bool dpdk;
@@ -146,6 +151,7 @@ typedef struct bbl_ctx_
io_mode_t io_mode;
uint16_t io_slots;
uint16_t io_burst;
uint16_t io_max_stream_len;
bool qdisc_bypass;
-1
View File
@@ -53,7 +53,6 @@
#define BBL_SESSION_HASHTABLE_SIZE 128993 /* is a prime number */
#define BBL_LI_HASHTABLE_SIZE 32771 /* is a prime number */
#define BBL_STREAM_FLOW_HASHTABLE_SIZE 128993 /* is a prime number */
/* Mock Addresses */
#define MOCK_IP_LOCAL 167772170 /* 10.0.0.10 */
+1 -1
View File
@@ -20,7 +20,7 @@ typedef struct bbl_lag_
uint8_t active_count;
bbl_lag_member_s *active_list[LAG_MEMBER_ACTIVE_MAX];
uint64_t select;
uint32_t select;
CIRCLEQ_ENTRY(bbl_lag_) lag_qnode;
CIRCLEQ_HEAD(lag_member_, bbl_lag_member_ ) lag_member_qhead; /* list of member interfaces */
+1 -1
View File
@@ -207,7 +207,7 @@ bbl_network_interfaces_add()
/* Timer to compute periodic rates */
timer_add_periodic(&g_ctx->timer_root, &network_interface->rate_job, "Rate Computation", 1, 0, network_interface,
&bbl_network_interface_rate_job);
network_interface->rate_job->reset = false;
LOG(DEBUG, "Added network interface %s\n", ifname);
network_config = network_config->next;
}
+2 -2
View File
@@ -3536,8 +3536,8 @@ decode_ipv4(uint8_t *buf, uint16_t len,
return DECODE_ERROR;
}
ipv4->id = be16toh(header->ip_id);
ipv4->offset = be16toh(header->ip_off);
if(header->ip_id) ipv4->id = be16toh(header->ip_id);
if(header->ip_off) ipv4->offset = be16toh(header->ip_off);
ipv4->ttl = header->ip_ttl;
ipv4->protocol = header->ip_p;
+1
View File
@@ -309,6 +309,7 @@ typedef enum protocol_error_ {
IGNORED,
EMPTY,
FULL,
STREAM_WAIT
} protocol_error_t;
typedef enum icmpv6_message_ {
+16 -41
View File
@@ -14,10 +14,8 @@ bbl_rx_stream_network(bbl_network_interface_s *interface,
bbl_ethernet_header_s *eth)
{
bbl_stream_s *stream;
if(!eth->bbl || memcmp(interface->mac, eth->dst, ETH_ADDR_LEN) != 0) {
return false;
}
stream = bbl_stream_rx(eth, NULL);
if(!eth->bbl) return false;
stream = bbl_stream_rx(eth, interface->mac);
if(stream) {
if(stream->rx_network_interface == NULL) {
stream->rx_network_interface = interface;
@@ -32,29 +30,13 @@ bbl_rx_stream_access(bbl_access_interface_s *interface,
bbl_ethernet_header_s *eth)
{
bbl_stream_s *stream;
bbl_session_s *session;
uint32_t session_id = 0;
if(!(eth->bbl && eth->bbl->type == BBL_TYPE_UNICAST)) {
return false;
}
session_id |= eth->dst[5];
session_id |= eth->dst[4] << 8;
session_id |= eth->dst[3] << 16;
session = bbl_session_get(session_id);
if(session) {
if(session->session_state != BBL_TERMINATED &&
session->session_state != BBL_IDLE) {
stream = bbl_stream_rx(eth, session);
if(stream) {
if(stream->rx_access_interface == NULL) {
stream->rx_access_interface = interface;
}
return true;
}
if(!eth->bbl) return false;
stream = bbl_stream_rx(eth, NULL);
if(stream) {
if(stream->rx_access_interface == NULL) {
stream->rx_access_interface = interface;
}
return true;
}
return false;
}
@@ -64,10 +46,8 @@ bbl_rx_stream_a10nsp(bbl_a10nsp_interface_s *interface,
bbl_ethernet_header_s *eth)
{
bbl_stream_s *stream;
if(!eth->bbl || memcmp(interface->mac, eth->dst, ETH_ADDR_LEN) != 0) {
return false;
}
stream = bbl_stream_rx(eth, NULL);
if(!eth->bbl) return false;
stream = bbl_stream_rx(eth, interface->mac);
if(stream) {
if(stream->rx_a10nsp_interface == NULL) {
stream->rx_a10nsp_interface = interface;
@@ -81,19 +61,14 @@ bool
bbl_rx_thread(bbl_interface_s *interface,
bbl_ethernet_header_s *eth)
{
bbl_network_interface_s *network_interface = interface->network;
bbl_network_interface_s *network_interface;
if(interface->state == INTERFACE_DISABLED) {
return true;
}
while(network_interface) {
if(network_interface->vlan == eth->vlan_outer) {
return bbl_rx_stream_network(network_interface, eth);
}
network_interface = network_interface->next;
}
if(interface->access) {
network_interface = interface->network_vlan[eth->vlan_outer];
if(network_interface) {
return bbl_rx_stream_network(network_interface, eth);
} else if(interface->access) {
return bbl_rx_stream_access(interface->access, eth);
} else if(interface->a10nsp) {
return bbl_rx_stream_a10nsp(interface->a10nsp, eth);
@@ -130,7 +105,7 @@ bbl_rx_handler(bbl_interface_s *interface,
if(!bbl_rx_stream_network(network_interface, eth)) {
bbl_network_rx_handler(network_interface, eth);
}
} else if(interface->access) {
} else if(interface->access) {
if(!bbl_rx_stream_access(interface->access, eth)) {
bbl_access_rx_handler(interface->access, eth);
}
+3 -14
View File
@@ -280,10 +280,6 @@ bbl_session_monkey_job(timer_s *timer) {
/**
* bbl_session_get
*
* This function allows to change the state of a session including
* the required action caused by state changes.
*
* @param ctx global context
* @param session_id session-id
* @return session or NULL if session not found
*/
@@ -1749,25 +1745,18 @@ bbl_session_ctrl_traffic_stop(int fd, uint32_t session_id, json_t *arguments __a
int
bbl_session_ctrl_traffic_reset(int fd, uint32_t session_id __attribute__((unused)), json_t *arguments __attribute__((unused)))
{
bbl_stream_s *stream;
struct dict_itor *itor;
bbl_stream_s *stream = g_ctx->stream_head;
g_ctx->stats.session_traffic_flows_verified = 0;
/* Iterate over all traffic streams */
itor = dict_itor_new(g_ctx->stream_flow_dict);
dict_itor_first(itor);
for (; dict_itor_valid(itor); dict_itor_next(itor)) {
stream = (bbl_stream_s*)*dict_itor_datum(itor);
if(!stream) {
continue;
}
while(stream) {
if(stream->session && stream->session_traffic) {
stream->session->session_traffic.flows_verified = 0;
bbl_stream_reset(stream);
}
stream = stream->next;
}
dict_itor_free(itor);
return bbl_ctrl_status(fd, "ok", 200, NULL);
}
+32 -53
View File
@@ -165,8 +165,6 @@ bbl_stats_generate(bbl_stats_s * stats)
bbl_stats_update_cps();
bbl_stats_generate_multicast(stats, false);
struct dict_itor *itor;
/* Iterate over all sessions */
for(i = 0; i < g_ctx->sessions; i++) {
session = &g_ctx->session_list[i];
@@ -370,36 +368,32 @@ bbl_stats_generate(bbl_stats_s * stats)
}
/* Iterate over all traffic streams */
itor = dict_itor_new(g_ctx->stream_flow_dict);
dict_itor_first(itor);
for (; dict_itor_valid(itor); dict_itor_next(itor)) {
stream = (bbl_stream_s*)*dict_itor_datum(itor);
if(stream) {
if(stats->min_stream_loss) {
if(stream->rx_loss < stats->min_stream_loss) stats->min_stream_loss = stream->rx_loss;
} else {
stats->min_stream_loss = stream->rx_loss;
}
if(stream->rx_loss > stats->max_stream_loss) stats->max_stream_loss = stream->rx_loss;
if(stream->rx_first_seq) {
if(stats->min_stream_rx_first_seq) {
if(stream->rx_first_seq < stats->min_stream_rx_first_seq) stats->min_stream_rx_first_seq = stream->rx_first_seq;
} else {
stats->min_stream_rx_first_seq = stream->rx_first_seq;
}
if(stream->rx_first_seq > stats->max_stream_rx_first_seq) stats->max_stream_rx_first_seq = stream->rx_first_seq;
if(stats->min_stream_delay_us) {
if(stream->rx_min_delay_us < stats->min_stream_delay_us) stats->min_stream_delay_us = stream->rx_min_delay_us;
} else {
stats->min_stream_delay_us = stream->rx_min_delay_us;
}
if(stream->rx_max_delay_us > stats->max_stream_delay_us) stats->max_stream_delay_us = stream->rx_max_delay_us;
}
stream = g_ctx->stream_head;
while(stream) {
if(stats->min_stream_loss) {
if(stream->rx_loss < stats->min_stream_loss) stats->min_stream_loss = stream->rx_loss;
} else {
stats->min_stream_loss = stream->rx_loss;
}
if(stream->rx_loss > stats->max_stream_loss) stats->max_stream_loss = stream->rx_loss;
if(stream->rx_first_seq) {
if(stats->min_stream_rx_first_seq) {
if(stream->rx_first_seq < stats->min_stream_rx_first_seq) stats->min_stream_rx_first_seq = stream->rx_first_seq;
} else {
stats->min_stream_rx_first_seq = stream->rx_first_seq;
}
if(stream->rx_first_seq > stats->max_stream_rx_first_seq) stats->max_stream_rx_first_seq = stream->rx_first_seq;
if(stats->min_stream_delay_us) {
if(stream->rx_min_delay_us < stats->min_stream_delay_us) stats->min_stream_delay_us = stream->rx_min_delay_us;
} else {
stats->min_stream_delay_us = stream->rx_min_delay_us;
}
if(stream->rx_max_delay_us > stats->max_stream_delay_us) stats->max_stream_delay_us = stream->rx_max_delay_us;
}
stream = stream->next;
}
dict_itor_free(itor);
}
void
@@ -805,8 +799,6 @@ bbl_stats_json(bbl_stats_s * stats)
bbl_session_s *session;
bbl_stream_s *stream;
struct dict_itor *itor;
json_t *root = NULL;
json_t *jobj = NULL;
json_t *jobj_array = NULL;
@@ -1204,18 +1196,14 @@ bbl_stats_json(bbl_stats_s * stats)
if(g_ctx->config.json_report_streams) {
jobj_array = json_array();
itor = dict_itor_new(g_ctx->stream_flow_dict);
dict_itor_first(itor);
for (; dict_itor_valid(itor); dict_itor_next(itor)) {
stream = (bbl_stream_s*)*dict_itor_datum(itor);
if(stream) {
jobj_sub = bbl_stream_json(stream);
if(jobj_sub) {
json_array_append(jobj_array, jobj_sub);
}
stream = g_ctx->stream_head;
while(stream) {
jobj_sub = bbl_stream_json(stream);
if(jobj_sub) {
json_array_append(jobj_array, jobj_sub);
}
stream = stream->next;
}
dict_itor_free(itor);
json_object_set(jobj, "streams", jobj_array);
}
@@ -1233,7 +1221,6 @@ void
bbl_compute_avg_rate(bbl_rate_s *rate, uint64_t current_value)
{
uint8_t idx;
uint64_t div;
uint64_t sum;
if(current_value == 0) return;
@@ -1241,18 +1228,10 @@ bbl_compute_avg_rate(bbl_rate_s *rate, uint64_t current_value)
rate->diff_value[rate->cursor] = current_value - rate->last_value;
sum = 0;
div = 0;
for(idx = 0; idx < BBL_AVG_SAMPLES; idx++) {
if(rate->diff_value[idx]) {
sum += rate->diff_value[idx];
div++;
}
}
if(div) {
rate->avg = sum / div;
} else {
rate->avg = 0;
sum += rate->diff_value[idx];
}
rate->avg = sum / BBL_AVG_SAMPLES;
if(rate->avg > rate->avg_max) {
rate->avg_max = rate->avg;
}
File diff suppressed because it is too large Load Diff
+53 -44
View File
@@ -67,6 +67,7 @@ typedef struct bbl_stream_config_
typedef struct bbl_stream_group_
{
double pps;
uint32_t count;
bbl_stream_s *head;
struct timer_ *timer;
@@ -82,38 +83,9 @@ typedef struct bbl_stream_
uint8_t sub_type;
uint8_t direction;
bbl_stream_config_s *config;
bbl_stream_group_s *group;
bbl_stream_s *group_next; /* Next stream of same group */
bbl_stream_s *reverse; /* Reverse stream direction */
uint32_t session_version;
bbl_session_s *session;
bbl_stream_s *session_next; /* Next stream of same session */
endpoint_state_t *endpoint;
io_handle_s *io;
bbl_access_interface_s *access_interface;
bbl_network_interface_s *network_interface;
bbl_a10nsp_interface_s *a10nsp_interface;
struct timer_ *tx_timer;
bbl_interface_s *tx_interface; /* TX interface */
uint8_t *tx_buf; /* TX buffer */
uint16_t tx_len; /* TX length */
uint16_t tx_bbl_hdr_len; /* TX BBL HDR length */
uint64_t tx_interval; /* TX interval in nsec */
__time_t tx_first_epoch;
uint32_t ipv4_src;
uint32_t ipv4_dst;
uint8_t *ipv6_src;
uint8_t *ipv6_dst;
bool threaded;
bool session_traffic;
bool active;
bool setup;
bool verified;
bool wait;
@@ -123,22 +95,50 @@ typedef struct bbl_stream_
bool tcp;
bool lag;
bool ldp_lookup;
uint32_t session_version;
uint32_t ldp_entry_version;
uint32_t ipv4_src;
uint32_t ipv4_dst;
uint16_t tx_len; /* TX length */
uint16_t tx_bbl_hdr_len; /* TX BBL HDR length */
uint8_t *tx_buf; /* TX buffer */
uint8_t *ipv6_src;
uint8_t *ipv6_dst;
bbl_stream_config_s *config;
bbl_stream_s *next; /* Next stream (global) */
bbl_stream_s *io_next; /* Next stream of same IO handle */
bbl_stream_s *group_next; /* Next stream of same group */
bbl_stream_s *session_next; /* Next stream of same session */
bbl_stream_s *reverse; /* Reverse stream direction */
bbl_stream_group_s *group;
bbl_session_s *session;
endpoint_state_t *endpoint;
io_handle_s *io;
io_bucket_s *io_bucket;
bbl_access_interface_s *access_interface;
bbl_network_interface_s *network_interface;
bbl_a10nsp_interface_s *a10nsp_interface;
bbl_interface_s *tx_interface; /* TX interface */
ldp_db_entry_s *ldp_entry;
bool send_window_active;
uint64_t send_window_start_packets;
struct timespec send_window_start;
struct timespec wait_start;
CIRCLEQ_ENTRY(bbl_stream_) tx_qnode;
uint32_t token_bucket;
uint32_t token_burst;
uint64_t lag_select;
uint64_t tx_packets;
uint64_t tokens;
uint64_t tokens_burst;
uint32_t lag_select;
__time_t tx_first_epoch;
struct timespec wait_start;
char _pad0 __attribute__((__aligned__(CACHE_LINE_SIZE))); /* empty cache line */
@@ -194,6 +194,12 @@ typedef struct bbl_stream_
} bbl_stream_s;
bbl_stream_s *
bbl_stream_index_get(uint64_t flow_id);
bool
bbl_stream_index_init();
bool
bbl_stream_session_init(bbl_session_s *session);
@@ -204,10 +210,13 @@ void
bbl_stream_final();
protocol_error_t
bbl_stream_tx(io_handle_s *io, uint8_t *buf, uint16_t *len);
bbl_stream_io_send(io_handle_s *io, bbl_stream_s *stream);
bbl_stream_s*
bbl_stream_io_send_iter(io_handle_s *io);
bbl_stream_s *
bbl_stream_rx(bbl_ethernet_header_s *eth, bbl_session_s *session);
bbl_stream_rx(bbl_ethernet_header_s *eth, uint8_t *mac);
void
bbl_stream_reset(bbl_stream_s *stream);
+1 -33
View File
@@ -7,36 +7,4 @@
* SPDX-License-Identifier: BSD-3-Clause
*/
#include "io.h"
void
io_update_stream_token_bucket(io_handle_s *io)
{
io->stream_tokens += io->stream_rate;
if(io->stream_tokens > io->stream_burst) {
io->stream_tokens = io->stream_burst;
}
}
void
io_init_stream_token_bucket()
{
bbl_interface_s *interface;
io_handle_s *io;
double rate;
CIRCLEQ_FOREACH(interface, &g_ctx->interface_qhead, interface_qnode) {
io = interface->io.tx;
while(io) {
rate = (io->stream_pps / (io->interface->config->tx_interval / 1000)) * 1.3;
io->stream_rate = rate;
if(rate - (double)io->stream_rate) {
/* Roundup. */
io->stream_rate++;
}
io->stream_tokens = 0;
io->stream_burst = io->stream_rate * 3;
io = io->next;
}
}
}
#include "io.h"
+1 -6
View File
@@ -19,6 +19,7 @@
#include "../bbl_txq.h"
#include "io_def.h"
#include "io_bucket.h"
#include "io_socket.h"
#include "io_interface.h"
#include "io_thread.h"
@@ -30,10 +31,4 @@
#include "io_dpdk.h"
#endif
void
io_update_stream_token_bucket(io_handle_s *io);
void
io_init_stream_token_bucket();
#endif
+56
View File
@@ -0,0 +1,56 @@
/*
* BNG Blaster (BBL) - IO Bucket
*
* Christian Giese, January 2024
*
* Copyright (C) 2020-2023, RtBrick, Inc.
* SPDX-License-Identifier: BSD-3-Clause
*/
#include "io.h"
void
io_tocken_job(timer_s *timer)
{
io_bucket_s *io_bucket = timer->data;
struct timespec time_elapsed;
uint64_t tokens;
if(unlikely(!io_bucket->timestamp_start.tv_sec)) {
io_bucket->timestamp_start.tv_sec = timer->timestamp->tv_sec;
io_bucket->timestamp_start.tv_nsec = timer->timestamp->tv_nsec;
return;
}
timespec_sub(&time_elapsed, timer->timestamp, &io_bucket->timestamp_start);
tokens = io_bucket->tokens_per_sec * time_elapsed.tv_sec;
tokens += (io_bucket->tokens_per_sec * time_elapsed.tv_nsec) / SEC;
io_bucket->tokens = tokens;
}
static io_bucket_s *
io_bucket_add(double pps)
{
io_bucket_s *io_bucket = calloc(1, sizeof(io_bucket_s));
io_bucket->next = g_ctx->io_bucket;
g_ctx->io_bucket = io_bucket;
io_bucket->pps = pps;
io_bucket->tokens_per_sec = pps * IO_TOKENS_PER_PACKET;
timer_add_periodic(&g_ctx->timer_root, &io_bucket->timer,
"TB", 0, 1*MSEC+1, io_bucket, &io_tocken_job);
return io_bucket;
}
void
io_bucket_stream(bbl_stream_s *stream)
{
io_bucket_s *io_bucket = g_ctx->io_bucket;
while(io_bucket) {
if(io_bucket->pps == stream->config->pps) {
break;
}
io_bucket = io_bucket->next;
}
if(!io_bucket) {
io_bucket = io_bucket_add(stream->config->pps);
}
stream->io_bucket = io_bucket;
}
+15
View File
@@ -0,0 +1,15 @@
/*
* BNG Blaster (BBL) - IO Bucket
*
* Christian Giese, January 2024
*
* Copyright (C) 2020-2023, RtBrick, Inc.
* SPDX-License-Identifier: BSD-3-Clause
*/
#ifndef __BBL_IO_BUCKET_H__
#define __BBL_IO_BUCKET_H__
void
io_bucket_stream(bbl_stream_s *stream);
#endif
+14 -12
View File
@@ -9,6 +9,8 @@
#ifndef __BBL_IO_DEF_H__
#define __BBL_IO_DEF_H__
#define IO_TOKENS_PER_PACKET 1000
typedef struct io_handle_ io_handle_s;
typedef struct io_thread_ io_thread_s;
@@ -38,6 +40,15 @@ typedef enum {
IO_MODE_AF_XDP /* AF_XDP */
} __attribute__ ((__packed__)) io_mode_t;
typedef struct io_bucket_ {
double pps;
uint64_t tokens_per_sec;
uint64_t tokens;
struct timer_ *timer;
struct timespec timestamp_start;
struct io_bucket_ *next;
} io_bucket_s;
typedef struct io_handle_ {
io_mode_t mode;
io_direction_t direction;
@@ -70,10 +81,9 @@ typedef struct io_handle_ {
uint16_t vlan_tpid;
double stream_pps;
uint32_t stream_tokens;
uint32_t stream_rate;
uint32_t stream_burst;
CIRCLEQ_HEAD(stream_tx_, bbl_stream_) stream_tx_qhead;
uint32_t stream_count;
bbl_stream_s *stream_head;
bbl_stream_s *stream_cur;
struct timespec timestamp; /* user space timestamps */
@@ -93,7 +103,6 @@ typedef struct io_handle_ {
} io_handle_s;
typedef void (*io_thread_cb_fn)(io_thread_s *thread);
typedef bool (*io_thread_stream_cb_fn)(bbl_stream_s *stream);
typedef struct io_thread_ {
pthread_t thread;
@@ -108,18 +117,11 @@ typedef struct io_thread_ {
io_thread_cb_fn run_fn;
io_thread_cb_fn teardown_fn;
io_thread_stream_cb_fn stream_tx_fn;
uint8_t *sp;
io_handle_s *io;
bbl_txq_s *txq;
struct {
struct timer_root_ root;
struct timer_ *io;
} timer;
struct io_thread_ *next;
} io_thread_s;
+158 -128
View File
@@ -16,9 +16,18 @@
#ifdef BNGBLASTER_DPDK
#include <dev_driver.h>
#include <rte_memory.h>
#include <rte_launch.h>
#include <rte_eal.h>
#include <rte_common.h>
#include <rte_dev.h>
#include <rte_bbdev.h>
#include <rte_cycles.h>
#include <rte_random.h>
#include <rte_hexdump.h>
#include <rte_interrupts.h>
#include <rte_per_lcore.h>
#include <rte_lcore.h>
#include <rte_debug.h>
@@ -26,14 +35,14 @@
#include <rte_ethdev.h>
#include <rte_malloc.h>
#define NUM_MBUFS 4096
#define MBUF_CACHE_SIZE 250
#define BURST_SIZE_RX 64
#define NUM_MBUFS 8192
#define MBUF_CACHE_SIZE 256
#define BURST_SIZE_RX 256
#define BURST_SIZE_TX 32
static struct rte_eth_conf port_conf = {
.rxmode = {
.split_hdr_size = 0,
.mq_mode = 0,
},
.txmode = {
.mq_mode = RTE_ETH_MQ_TX_NONE,
@@ -66,7 +75,7 @@ io_dpdk_link_status(uint16_t port_id)
interface->state = INTERFACE_UP;
LOG(DPDK, "DPDK: interface %s (%u) link up (speed %u Mbps %s)\n",
interface->name, port_id, (unsigned)link.link_speed,
(link.link_duplex == ETH_LINK_FULL_DUPLEX) ? ("full-duplex") : ("half-duplex"));
(link.link_duplex == RTE_ETH_LINK_FULL_DUPLEX) ? ("full-duplex") : ("half-duplex"));
} else {
interface->state = INTERFACE_DOWN;
LOG(DPDK, "DPDK: interface %s (%u) link down\n",
@@ -194,17 +203,25 @@ io_dpdk_rx_job(timer_s *timer)
/* Copy RX timestamp */
eth->timestamp.tv_sec = io->timestamp.tv_sec;
eth->timestamp.tv_nsec = io->timestamp.tv_nsec;
/* Dump the packet into pcap file */
if(g_ctx->pcap.write_buf && (!eth->bbl || g_ctx->pcap.include_streams)) {
pcap = true;
pcapng_push_packet_header(&io->timestamp, io->buf, io->buf_len,
interface->ifindex, PCAPNG_EPB_FLAGS_INBOUND);
}
bbl_rx_handler(interface, eth);
} else if(decode_result == UNKNOWN_PROTOCOL) {
io->stats.unknown++;
} else {
io->stats.protocol_errors++;
}
/* Dump the packet into pcap file */
if(g_ctx->pcap.write_buf && (!eth->bbl || g_ctx->pcap.include_streams)) {
pcap = true;
pcapng_push_packet_header(&io->timestamp, io->buf, io->buf_len,
interface->ifindex, PCAPNG_EPB_FLAGS_INBOUND);
/* Dump the packet into pcap file */
if(g_ctx->pcap.write_buf) {
pcap = true;
pcapng_push_packet_header(&io->timestamp, io->buf, io->buf_len,
interface->ifindex, PCAPNG_EPB_FLAGS_INBOUND);
}
if(decode_result == UNKNOWN_PROTOCOL) {
io->stats.unknown++;
} else {
io->stats.protocol_errors++;
}
}
rte_pktmbuf_free(packet);
}
@@ -245,73 +262,87 @@ io_dpdk_tx_job(timer_s *timer)
io_handle_s *io = timer->data;
bbl_interface_s *interface = io->interface;
uint32_t stream_packets = 0;
bool ctrl = true;
bbl_stream_s *stream = NULL;
uint16_t burst = interface->config->io_burst;
bool pcap = false;
assert(io->mode == IO_MODE_DPDK);
assert(io->direction == IO_EGRESS);
assert(io->thread == NULL);
io_update_stream_token_bucket(io);
/* Get TX timestamp */
//clock_gettime(CLOCK_MONOTONIC, &io->timestamp);
io->timestamp.tv_sec = timer->timestamp->tv_sec;
io->timestamp.tv_nsec = timer->timestamp->tv_nsec;
while(true) {
/* If sendto fails, the failed packet remains in TX buffer to be retried
* in the next interval. */
if(io->buf_len) {
if(packet_is_bbl(io->buf, io->buf_len)) {
/* Update timestamp if BBL traffic is retried. */
*(uint32_t*)(io->buf + (io->buf_len - 8)) = io->timestamp.tv_sec;
*(uint32_t*)(io->buf + (io->buf_len - 4)) = io->timestamp.tv_nsec;
}
} else {
if(!io->mbuf) {
if(!io_dpdk_mbuf_alloc(io)) {
break;
}
}
if(ctrl) {
/* First send all control traffic which has higher priority. */
if(bbl_tx(interface, io->buf, &io->buf_len) != PROTOCOL_SUCCESS) {
io->buf_len = 0;
ctrl = false;
continue;
}
} else {
/* Send traffic streams up to allowed burst. */
if(++stream_packets > io->stream_burst) {
break;
}
if(bbl_stream_tx(io, io->buf, &io->buf_len) != PROTOCOL_SUCCESS) {
break;
}
while(burst) {
if(!io->mbuf) {
if(!io_dpdk_mbuf_alloc(io)) {
break;
}
}
io->mbuf->data_len = io->buf_len;
if(likely(io->buf_len == 0)) {
if(bbl_tx(interface, io->buf, &io->buf_len) != PROTOCOL_SUCCESS) {
io->buf_len = 0;
break;
}
}
/* Transmit the packet. */
if(rte_eth_tx_burst(interface->port_id, io->queue, &io->mbuf, 1) == 0) {
io->mbuf->data_len = io->buf_len;
if(rte_eth_tx_burst(interface->port_id, io->queue, &io->mbuf, 1) != 0) {
/* Dump the packet into pcap file. */
if(unlikely(g_ctx->pcap.write_buf != NULL)) {
pcap = true;
pcapng_push_packet_header(&io->timestamp, io->buf, io->buf_len,
interface->ifindex, PCAPNG_EPB_FLAGS_OUTBOUND);
}
io->stats.packets++;
io->stats.bytes += io->buf_len;
io->mbuf = NULL;
io->buf_len = 0;
burst--;
} else {
/* This packet will be retried next interval
* because io->buf_len is not reset to zero. */
if(pcap) {
pcapng_fflush();
io->stats.io_errors++;
burst = 0;
}
}
while(burst) {
/* Send traffic streams up to allowed burst. */
if(!io->mbuf) {
if(!io_dpdk_mbuf_alloc(io)) {
break;
}
return;
}
/* Dump the packet into pcap file. */
if(g_ctx->pcap.write_buf && (ctrl || g_ctx->pcap.include_streams)) {
pcap = true;
pcapng_push_packet_header(&io->timestamp, io->buf, io->buf_len,
interface->ifindex, PCAPNG_EPB_FLAGS_OUTBOUND);
stream = bbl_stream_io_send_iter(io);
if(unlikely(stream == NULL)) {
break;
}
/* Transmit the packet. */
io->mbuf->data_len = io->buf_len;
if(rte_eth_tx_burst(interface->port_id, io->queue, &io->mbuf, 1) != 0) {
/* Dump the packet into pcap file. */
if(unlikely(g_ctx->pcap.write_buf && g_ctx->pcap.include_streams)) {
pcap = true;
pcapng_push_packet_header(&io->timestamp, io->buf, io->buf_len,
interface->ifindex, PCAPNG_EPB_FLAGS_OUTBOUND);
}
stream->tx_packets++;
stream->flow_seq++;
io->stats.packets++;
io->stats.bytes += io->buf_len;
io->mbuf = NULL;
io->buf_len = 0;
burst--;
} else {
/* This packet will be retried next interval
* because io->buf_len is not reset to zero. */
io->stats.io_errors++;
burst = 0;
}
io->stats.packets++;
io->stats.bytes += io->buf_len;
io->mbuf = NULL;
io->buf = 0;
io->buf_len = 0;
}
if(pcap) {
pcapng_fflush();
@@ -337,12 +368,11 @@ io_dpdk_thread_rx_run_fn(io_thread_s *thread)
struct timespec sleep, rem;
sleep.tv_sec = 0;
sleep.tv_nsec = 0;
sleep.tv_nsec = 10;
while(thread->active) {
nb_rx = rte_eth_rx_burst(port_id, io->queue, pkts_burst, BURST_SIZE_RX);
if(nb_rx == 0) {
sleep.tv_nsec = 10;
nanosleep(&sleep, &rem);
continue;
}
@@ -360,84 +390,89 @@ io_dpdk_thread_rx_run_fn(io_thread_s *thread)
}
}
/**
* This job is for DPDK TX in worker thread!
*/
void
io_dpdk_thread_tx_job(timer_s *timer)
io_dpdk_thread_tx_run_fn(io_thread_s *thread)
{
io_thread_s *thread = timer->data;
io_handle_s *io = thread->io;
bbl_interface_s *interface = io->interface;
bbl_txq_s *txq = thread->txq;
bbl_txq_slot_t *slot;
uint32_t stream_packets = 0;
bool ctrl = true;
bbl_stream_s *stream = NULL;
uint16_t io_burst = interface->config->io_burst;
uint16_t burst = 0;
struct timespec sleep, rem;
sleep.tv_sec = 0;
sleep.tv_nsec = 1000;
assert(io->mode == IO_MODE_DPDK);
assert(io->direction == IO_EGRESS);
assert(io->thread);
io_update_stream_token_bucket(io);
while(thread->active) {
nanosleep(&sleep, &rem);
burst = io_burst;
/* Get TX timestamp */
//clock_gettime(CLOCK_MONOTONIC, &io->timestamp);
io->timestamp.tv_sec = timer->timestamp->tv_sec;
io->timestamp.tv_nsec = timer->timestamp->tv_nsec;
while(true) {
/* If sendto fails, the failed packet remains in TX buffer to be retried
* in the next interval. */
if(io->buf_len) {
if(packet_is_bbl(io->buf, io->buf_len)) {
/* Update timestamp if BBL traffic is retried. */
*(uint32_t*)(io->buf + (io->buf_len - 8)) = io->timestamp.tv_sec;
*(uint32_t*)(io->buf + (io->buf_len - 4)) = io->timestamp.tv_nsec;
}
} else {
/* First send all control traffic which has higher priority. */
while((slot = bbl_txq_read_slot(txq))) {
/* This packet will be retried next interval
* because slot is not marked as read. */
if(!io->mbuf) {
if(!io_dpdk_mbuf_alloc(io)) {
break;
}
}
if(ctrl) {
/* First send all control traffic which has higher priority. */
slot = bbl_txq_read_slot(txq);
if(slot) {
io->buf_len = slot->packet_len;
memcpy(io->buf, slot->packet, slot->packet_len);
bbl_txq_read_next(txq);
} else {
ctrl = false;
continue;
}
/* Transmit the packet. */
io->mbuf->data_len = slot->packet_len;
memcpy(io->buf, slot->packet, slot->packet_len);
if(rte_eth_tx_burst(interface->port_id, io->queue, &io->mbuf, 1) != 0) {
io->stats.packets++;
io->stats.bytes += slot->packet_len;
io->mbuf = NULL;
bbl_txq_read_next(txq);
if(burst) burst--;
} else {
/* Send traffic streams up to allowed burst. */
if(++stream_packets > io->stream_burst) {
break;
}
if(bbl_stream_tx(io, io->buf, &io->buf_len) != PROTOCOL_SUCCESS) {
io->stats.io_errors++;
burst = 0;
break;
}
}
/* Get TX timestamp */
clock_gettime(CLOCK_MONOTONIC, &io->timestamp);
while(burst) {
/* Send traffic streams up to allowed burst. */
if(!io->mbuf) {
if(!io_dpdk_mbuf_alloc(io)) {
break;
}
}
stream = bbl_stream_io_send_iter(io);
if(unlikely(stream == NULL)) {
break;
}
/* Transmit the packet. */
io->mbuf->data_len = stream->tx_len;
memcpy(io->buf, stream->tx_buf, stream->tx_len);
if(rte_eth_tx_burst(interface->port_id, io->queue, &io->mbuf, 1) != 0) {
stream->tx_packets++;
stream->flow_seq++;
io->stats.packets++;
io->stats.bytes += stream->tx_len;
io->mbuf = NULL;
burst--;
} else {
io->stats.io_errors++;
burst = 0;
}
}
io->mbuf->data_len = io->buf_len;
/* Transmit the packet. */
if(rte_eth_tx_burst(interface->port_id, io->queue, &io->mbuf, 1) == 0) {
/* This packet will be retried next interval
* because io->buf_len is not reset to zero. */
return;
}
io->stats.packets++;
io->stats.bytes += io->buf_len;
io->mbuf = NULL;
io->buf = 0;
io->buf_len = 0;
}
}
bool
io_dpdk_add_mbuf_pool(io_handle_s *io)
{
@@ -475,11 +510,11 @@ io_dpdk_interface_init(bbl_interface_s *interface)
uint16_t id;
uint16_t nb_rx_queue = 1;
uint16_t nb_tx_queue = 1;
struct rte_eth_dev_info dev_info;
struct rte_eth_dev_info dev_info = {0};
struct rte_eth_conf local_port_conf = port_conf;
struct rte_eth_rxconf rx_conf;
struct rte_eth_txconf tx_conf;
struct rte_ether_addr mac;
struct rte_eth_rxconf rx_conf = {0};
struct rte_eth_txconf tx_conf = {0};
struct rte_ether_addr mac = {0};
io_handle_s *io;
RTE_ETH_FOREACH_DEV(port_id) {
@@ -523,7 +558,7 @@ io_dpdk_interface_init(bbl_interface_s *interface)
local_port_conf.rxmode.mq_mode = RTE_ETH_MQ_RX_RSS;
local_port_conf.rx_adv_conf.rss_conf.rss_hf =
(RTE_ETH_RSS_IP | RTE_ETH_RSS_TCP | RTE_ETH_RSS_UDP) &
(RTE_ETH_RSS_VLAN | RTE_ETH_RSS_IP | RTE_ETH_RSS_TCP | RTE_ETH_RSS_UDP | RTE_ETH_RSS_PPPOE | RTE_ETH_RSS_L2TPV2| RTE_ETH_RSS_MPLS) &
dev_info.flow_type_rss_offloads;
ret = rte_eth_dev_configure(port_id, nb_rx_queue, nb_tx_queue, &local_port_conf);
@@ -557,7 +592,6 @@ io_dpdk_interface_init(bbl_interface_s *interface)
io->next = interface->io.rx;
interface->io.rx = io;
io->interface = interface;
CIRCLEQ_INIT(&io->stream_tx_qhead);
if(config->rx_threads) {
if(!io_thread_init(io)) {
return false;
@@ -565,7 +599,7 @@ io_dpdk_interface_init(bbl_interface_s *interface)
io->thread->run_fn = io_dpdk_thread_rx_run_fn;
} else {
timer_add_periodic(&g_ctx->timer_root, &interface->io.rx_job, "RX", 0,
config->rx_interval, io, &io_dpdk_rx_job);
config->rx_interval, io, &io_dpdk_rx_job);
}
io->queue = queue;
if(!io_dpdk_add_mbuf_pool(io)) {
@@ -597,15 +631,11 @@ io_dpdk_interface_init(bbl_interface_s *interface)
interface->io.tx = io;
io->interface = interface;
io->buf = malloc(IO_BUFFER_LEN);
CIRCLEQ_INIT(&io->stream_tx_qhead);
if(config->tx_threads) {
if(!io_thread_init(io)) {
return false;
}
timer_add_periodic(&io->thread->timer.root, &io->thread->timer.io, "TX (threaded)", 0,
config->tx_interval, io->thread, &io_dpdk_thread_tx_job);
io->thread->timer.io->reset = false;
io->thread->set_cpu_affinity = true;
io->thread->run_fn = io_dpdk_thread_tx_run_fn;
} else {
timer_add_periodic(&g_ctx->timer_root, &interface->io.tx_job, "TX", 0,
config->tx_interval, io, &io_dpdk_tx_job);
-2
View File
@@ -89,7 +89,6 @@ io_interface_init_rx(bbl_interface_s *interface)
io->next = interface->io.rx;
interface->io.rx = io;
io->interface = interface;
CIRCLEQ_INIT(&io->stream_tx_qhead);
if(config->rx_threads) {
if(!io_thread_init(io)) {
return false;
@@ -136,7 +135,6 @@ io_interface_init_tx(bbl_interface_s *interface)
io->next = interface->io.tx;
interface->io.tx = io;
io->interface = interface;
CIRCLEQ_INIT(&io->stream_tx_qhead);
if(config->tx_threads) {
if(!io_thread_init(io)) {
return false;
+70 -59
View File
@@ -88,17 +88,25 @@ io_packet_mmap_rx_job(timer_s *timer)
//eth->timestamp.tv_nsec = tphdr->tp_nsec; /* ktime/hw timestamp */
eth->timestamp.tv_sec = io->timestamp.tv_sec;
eth->timestamp.tv_nsec = io->timestamp.tv_nsec;
/* Dump the packet into pcap file */
if(g_ctx->pcap.write_buf && (!eth->bbl || g_ctx->pcap.include_streams)) {
pcap = true;
pcapng_push_packet_header(&io->timestamp, io->buf, io->buf_len,
interface->ifindex, PCAPNG_EPB_FLAGS_INBOUND);
}
bbl_rx_handler(interface, eth);
} else if(decode_result == UNKNOWN_PROTOCOL) {
io->stats.unknown++;
} else {
io->stats.protocol_errors++;
}
/* Dump the packet into pcap file */
if(g_ctx->pcap.write_buf && (!eth->bbl || g_ctx->pcap.include_streams)) {
pcap = true;
pcapng_push_packet_header(&io->timestamp, io->buf, io->buf_len,
interface->ifindex, PCAPNG_EPB_FLAGS_INBOUND);
/* Dump the packet into pcap file */
if(g_ctx->pcap.write_buf) {
pcap = true;
pcapng_push_packet_header(&io->timestamp, io->buf, io->buf_len,
interface->ifindex, PCAPNG_EPB_FLAGS_INBOUND);
}
if(decode_result == UNKNOWN_PROTOCOL) {
io->stats.unknown++;
} else {
io->stats.protocol_errors++;
}
}
/* Return ownership back to kernel */
tphdr->tp_status = TP_STATUS_KERNEL;
@@ -124,7 +132,9 @@ io_packet_mmap_tx_job(timer_s *timer)
struct tpacket2_hdr* tphdr;
uint8_t *frame_ptr;
uint32_t stream_packets = 0;
bbl_stream_s *stream = NULL;
uint16_t burst = interface->config->io_burst;
bool ctrl = true;
bool pcap = false;
@@ -132,8 +142,6 @@ io_packet_mmap_tx_job(timer_s *timer)
assert(io->direction == IO_EGRESS);
assert(io->thread == NULL);
io_update_stream_token_bucket(io);
frame_ptr = io->ring + (io->cursor * io->req.tp_frame_size);
tphdr = (struct tpacket2_hdr*)frame_ptr;
if(tphdr->tp_status != TP_STATUS_AVAILABLE) {
@@ -145,7 +153,7 @@ io_packet_mmap_tx_job(timer_s *timer)
//clock_gettime(CLOCK_MONOTONIC, &io->timestamp);
io->timestamp.tv_sec = timer->timestamp->tv_sec;
io->timestamp.tv_nsec = timer->timestamp->tv_nsec;
while(true) {
while(burst) {
/* Check if this slot available for writing. */
if(tphdr->tp_status != TP_STATUS_AVAILABLE) {
io->stats.no_buffer++;
@@ -160,21 +168,20 @@ io_packet_mmap_tx_job(timer_s *timer)
continue;
}
} else {
/* Send traffic streams up to allowed burst. */
if(++stream_packets > io->stream_burst) {
stream = bbl_stream_io_send_iter(io);
if(!stream) {
break;
}
if(bbl_stream_tx(io, io->buf, &io->buf_len) != PROTOCOL_SUCCESS) {
break;
}
}
memcpy(io->buf, stream->tx_buf, stream->tx_len);
io->buf_len = stream->tx_len;
}
tphdr->tp_len = io->buf_len;
tphdr->tp_status = TP_STATUS_SEND_REQUEST;
io->queued++;
io->stats.packets++;
io->stats.bytes += io->buf_len;
burst--;
/* Dump the packet into pcap file. */
if(g_ctx->pcap.write_buf && (ctrl || g_ctx->pcap.include_streams)) {
@@ -225,15 +232,14 @@ io_packet_mmap_thread_rx_run_fn(io_thread_s *thread)
struct timespec sleep, rem;
sleep.tv_sec = 0;
sleep.tv_nsec = 0;
sleep.tv_nsec = 10000; /* 0.01ms */
while(thread->active) {
frame_ptr = ring + (cursor * frame_size);
tphdr = (struct tpacket2_hdr*)frame_ptr;
if(!(tphdr->tp_status & TP_STATUS_USER)) {
/* If no buffer is available poll kernel */
poll_kernel(io, POLLIN);
sleep.tv_nsec = 100000; /* 0.1ms */
//poll_kernel(io, POLLIN);
nanosleep(&sleep, &rem);
continue;
}
@@ -254,18 +260,13 @@ io_packet_mmap_thread_rx_run_fn(io_thread_s *thread)
frame_ptr = ring + (cursor * frame_size);
tphdr = (struct tpacket2_hdr*)frame_ptr;
}
sleep.tv_nsec = 1000; /* 0.001ms */
nanosleep(&sleep, &rem);
}
}
/**
* This job is for PACKET_MMAP TX in worker thread!
*/
void
io_packet_mmap_thread_tx_job(timer_s *timer)
io_packet_mmap_thread_tx_run_fn(io_thread_s *thread)
{
io_thread_s *thread = timer->data;
io_handle_s *io = thread->io;
bbl_interface_s *interface = io->interface;
@@ -275,30 +276,41 @@ io_packet_mmap_thread_tx_job(timer_s *timer)
struct tpacket2_hdr* tphdr;
uint8_t *frame_ptr;
uint32_t stream_packets = 0;
bbl_stream_s *stream = NULL;
uint16_t io_burst = interface->config->io_burst;
uint16_t burst = 0;
bool ctrl = true;
struct timespec sleep, rem;
sleep.tv_sec = 0;
sleep.tv_nsec = 10;
assert(io->mode == IO_MODE_PACKET_MMAP);
assert(io->direction == IO_EGRESS);
assert(io->thread);
io_update_stream_token_bucket(io);
while(thread->active) {
nanosleep(&sleep, &rem);
frame_ptr = io->ring + (io->cursor * io->req.tp_frame_size);
tphdr = (struct tpacket2_hdr *)frame_ptr;
if(tphdr->tp_status != TP_STATUS_AVAILABLE) {
/* If no buffer is available poll kernel. */
io->stats.no_buffer++;
poll_kernel(io, POLLOUT);
continue;
}
frame_ptr = io->ring + (io->cursor * io->req.tp_frame_size);
tphdr = (struct tpacket2_hdr *)frame_ptr;
if(tphdr->tp_status != TP_STATUS_AVAILABLE) {
/* If no buffer is available poll kernel. */
poll_kernel(io, POLLOUT);
io->stats.no_buffer++;
} else {
/* Get TX timestamp */
//clock_gettime(CLOCK_MONOTONIC, &io->timestamp);
io->timestamp.tv_sec = timer->timestamp->tv_sec;
io->timestamp.tv_nsec = timer->timestamp->tv_nsec;
while(true) {
/* Check if this slot available for writing. */
clock_gettime(CLOCK_MONOTONIC, &io->timestamp);
burst = io_burst;
ctrl = true;
while(burst) {
if(tphdr->tp_status != TP_STATUS_AVAILABLE) {
io->stats.no_buffer++;
poll_kernel(io, POLLOUT);
break;
}
io->buf = frame_ptr + TPACKET2_HDRLEN - sizeof(struct sockaddr_ll);
@@ -316,12 +328,12 @@ io_packet_mmap_thread_tx_job(timer_s *timer)
}
} else {
/* Send traffic streams up to allowed burst. */
if(++stream_packets > io->stream_burst) {
break;
}
if(bbl_stream_tx(io, io->buf, &io->buf_len) != PROTOCOL_SUCCESS) {
stream = bbl_stream_io_send_iter(io);
if(!stream) {
break;
}
memcpy(io->buf, stream->tx_buf, stream->tx_len);
io->buf_len = stream->tx_len;
}
tphdr->tp_len = io->buf_len;
@@ -330,22 +342,23 @@ io_packet_mmap_thread_tx_job(timer_s *timer)
io->queued++;
io->stats.packets++;
io->stats.bytes += io->buf_len;
burst--;
/* Get next slot. */
io->cursor = (io->cursor + 1) % io->req.tp_frame_nr;
frame_ptr = io->ring + (io->cursor * io->req.tp_frame_size);
tphdr = (struct tpacket2_hdr *)frame_ptr;
}
}
if(io->queued) {
/* Notify kernel. */
if(sendto(io->fd, NULL, 0, 0, NULL, 0) < 0) {
LOG(IO, "PACKET_MMAP sendto on interface %s failed with error %s (%d)\n",
interface->name, strerror(errno), errno);
io->stats.io_errors++;
} else {
io->queued = 0;
if(io->queued) {
/* Notify kernel. */
if(sendto(io->fd, NULL, 0, 0, NULL, 0) < 0) {
LOG(IO, "PACKET_MMAP sendto on interface %s failed with error %s (%d)\n",
interface->name, strerror(errno), errno);
io->stats.io_errors++;
} else {
io->queued = 0;
}
}
}
}
@@ -366,9 +379,7 @@ io_packet_mmap_init(io_handle_s *io)
if(io->direction == IO_INGRESS) {
thread->run_fn = io_packet_mmap_thread_rx_run_fn;
} else {
timer_add_periodic(&thread->timer.root, &thread->timer.io, "TX (threaded)", 0,
config->tx_interval, thread, &io_packet_mmap_thread_tx_job);
thread->timer.io->reset = false;
thread->run_fn = io_packet_mmap_thread_tx_run_fn;
}
} else {
if(io->direction == IO_INGRESS) {
+124 -108
View File
@@ -45,17 +45,25 @@ io_raw_rx_job(timer_s *timer)
/* Copy RX timestamp */
eth->timestamp.tv_sec = io->timestamp.tv_sec;
eth->timestamp.tv_nsec = io->timestamp.tv_nsec;
/* Dump the packet into pcap file */
if(g_ctx->pcap.write_buf && (!eth->bbl || g_ctx->pcap.include_streams)) {
pcap = true;
pcapng_push_packet_header(&io->timestamp, io->buf, io->buf_len,
interface->ifindex, PCAPNG_EPB_FLAGS_INBOUND);
}
bbl_rx_handler(interface, eth);
} else if(decode_result == UNKNOWN_PROTOCOL) {
io->stats.unknown++;
} else {
io->stats.protocol_errors++;
}
/* Dump the packet into pcap file */
if(g_ctx->pcap.write_buf && (!eth->bbl || g_ctx->pcap.include_streams)) {
pcap = true;
pcapng_push_packet_header(&io->timestamp, io->buf, io->buf_len,
interface->ifindex, PCAPNG_EPB_FLAGS_INBOUND);
/* Dump the packet into pcap file */
if(g_ctx->pcap.write_buf) {
pcap = true;
pcapng_push_packet_header(&io->timestamp, io->buf, io->buf_len,
interface->ifindex, PCAPNG_EPB_FLAGS_INBOUND);
}
if(decode_result == UNKNOWN_PROTOCOL) {
io->stats.unknown++;
} else {
io->stats.protocol_errors++;
}
}
}
if(pcap) {
@@ -89,71 +97,85 @@ io_raw_tx_job(timer_s *timer)
io_handle_s *io = timer->data;
bbl_interface_s *interface = io->interface;
uint32_t stream_packets = 0;
bool ctrl = true;
bbl_stream_s *stream = NULL;
uint16_t burst = interface->config->io_burst;
bool pcap = false;
assert(io->mode == IO_MODE_RAW);
assert(io->direction == IO_EGRESS);
assert(io->thread == NULL);
io_update_stream_token_bucket(io);
/* Get TX timestamp */
//clock_gettime(CLOCK_MONOTONIC, &io->timestamp);
io->timestamp.tv_sec = timer->timestamp->tv_sec;
io->timestamp.tv_nsec = timer->timestamp->tv_nsec;
while(true) {
/* If sendto fails, the failed packet remains in TX buffer to be retried
* in the next interval. */
if(io->buf_len) {
if(packet_is_bbl(io->buf, io->buf_len)) {
/* Update timestamp if BBL traffic is retried. */
*(uint32_t*)(io->buf + (io->buf_len - 8)) = io->timestamp.tv_sec;
*(uint32_t*)(io->buf + (io->buf_len - 4)) = io->timestamp.tv_nsec;
}
} else if(ctrl) {
/* First send all control traffic which has higher priority. */
while(burst) {
if(likely(io->buf_len == 0)) {
if(bbl_tx(interface, io->buf, &io->buf_len) != PROTOCOL_SUCCESS) {
io->buf_len = 0;
ctrl = false;
continue;
}
} else {
/* Send traffic streams up to allowed burst. */
if(++stream_packets > io->stream_burst) {
break;
}
if(bbl_stream_tx(io, io->buf, &io->buf_len) != PROTOCOL_SUCCESS) {
break;
}
}
if(sendto(io->fd, io->buf, io->buf_len, 0, (struct sockaddr*)&io->addr, sizeof(struct sockaddr_ll)) <0 ) {
if(errno == EMSGSIZE) {
io_raw_tx_lo_long(io);
} else {
/* This packet will be retried next interval
* because io->buf_len is not reset to zero. */
LOG(IO, "RAW sendto on interface %s failed with error %s (%d)\n",
interface->name, strerror(errno), errno);
io->stats.io_errors++;
break;
}
} else {
if(sendto(io->fd, io->buf, io->buf_len, 0, (struct sockaddr*)&io->addr, sizeof(struct sockaddr_ll)) > 0) {
/* Dump the packet into pcap file. */
if(g_ctx->pcap.write_buf && (ctrl || g_ctx->pcap.include_streams)) {
if(unlikely(g_ctx->pcap.write_buf != NULL)) {
pcap = true;
pcapng_push_packet_header(&io->timestamp, io->buf, io->buf_len,
interface->ifindex, PCAPNG_EPB_FLAGS_OUTBOUND);
}
io->stats.packets++;
io->stats.bytes += io->buf_len;
io->buf_len = 0;
burst--;
} else {
if(errno == EMSGSIZE) {
io_raw_tx_lo_long(io);
io->buf_len = 0;
} else {
/* This packet will be retried next interval
* because io->buf_len is not reset to zero. */
LOG(IO, "RAW sendto on interface %s failed with error %s (%d)\n",
interface->name, strerror(errno), errno);
io->stats.io_errors++;
burst = 0;
}
}
io->buf_len = 0;
}
if(pcap) {
while(burst) {
/* Send traffic streams up to allowed burst. */
stream = bbl_stream_io_send_iter(io);
if(unlikely(stream == NULL)) {
break;
}
if(sendto(io->fd, stream->tx_buf, stream->tx_len, 0, (struct sockaddr*)&io->addr, sizeof(struct sockaddr_ll)) > 0) {
/* Dump the packet into pcap file. */
if(unlikely(g_ctx->pcap.write_buf && g_ctx->pcap.include_streams)) {
pcap = true;
pcapng_push_packet_header(&io->timestamp, stream->tx_buf, stream->tx_len,
interface->ifindex, PCAPNG_EPB_FLAGS_OUTBOUND);
}
stream->tx_packets++;
stream->flow_seq++;
io->stats.packets++;
io->stats.bytes += stream->tx_len;
burst--;
} else {
if(errno == EMSGSIZE) {
io->buf_len = stream->tx_len;
io_raw_tx_lo_long(io);
io->buf_len = 0;
} else {
LOG(IO, "RAW sendto on interface %s failed with error %s (%d)\n",
interface->name, strerror(errno), errno);
io->stats.io_errors++;
burst = 0;
}
}
}
if(unlikely(pcap)) {
pcapng_fflush();
}
}
@@ -166,12 +188,11 @@ io_raw_thread_rx_run_fn(io_thread_s *thread)
struct sockaddr saddr;
int saddr_size = sizeof(saddr);
struct timespec sleep, rem;
assert(io->direction == IO_INGRESS);
struct timespec sleep, rem;
sleep.tv_sec = 0;
sleep.tv_nsec = 0;
sleep.tv_nsec = 1000; /* 0.001ms */
while(thread->active) {
/* Get RX timestamp */
@@ -179,7 +200,6 @@ io_raw_thread_rx_run_fn(io_thread_s *thread)
/* Receive from socket */
io->buf_len = recvfrom(io->fd, io->buf, IO_BUFFER_LEN, 0, &saddr , (socklen_t*)&saddr_size);
if(io->buf_len < 14 || io->buf_len > IO_BUFFER_LEN) {
sleep.tv_nsec = 1000; /* 0.001ms */
nanosleep(&sleep, &rem);
continue;
}
@@ -188,77 +208,75 @@ io_raw_thread_rx_run_fn(io_thread_s *thread)
}
}
/**
* This job is for RAW TX in worker thread!
*/
void
io_raw_thread_tx_job(timer_s *timer)
io_raw_thread_tx_run_fn(io_thread_s *thread)
{
io_thread_s *thread = timer->data;
io_handle_s *io = thread->io;
bbl_interface_s *interface = io->interface;
bbl_txq_s *txq = thread->txq;
bbl_txq_slot_t *slot;
uint32_t stream_packets = 0;
bbl_stream_s *stream = NULL;
uint16_t io_burst = interface->config->io_burst;
uint16_t burst = 0;
struct timespec sleep, rem;
sleep.tv_sec = 0;
sleep.tv_nsec = 1000 * io_burst;
assert(io->mode == IO_MODE_RAW);
assert(io->direction == IO_EGRESS);
assert(io->thread);
io_update_stream_token_bucket(io);
while(thread->active) {
nanosleep(&sleep, &rem);
burst = io_burst;
/* First send all control traffic which has higher priority. */
while((slot = bbl_txq_read_slot(txq))) {
/* This packet will be retried next interval
* because slot is not marked as read. */
if(sendto(io->fd, slot->packet, slot->packet_len, 0, (struct sockaddr*)&io->addr, sizeof(struct sockaddr_ll)) <0 ) {
LOG(IO, "RAW sendto on interface %s failed with error %s (%d)\n",
io->interface->name, strerror(errno), errno);
io->stats.io_errors++;
return;
}
io->stats.packets++;
io->stats.bytes += slot->packet_len;
bbl_txq_read_next(txq);
}
/* Get TX timestamp */
//clock_gettime(CLOCK_MONOTONIC, &io->timestamp);
io->timestamp.tv_sec = timer->timestamp->tv_sec;
io->timestamp.tv_nsec = timer->timestamp->tv_nsec;
/* Send traffic streams up to allowed burst. */
while(stream_packets++ < io->stream_burst) {
/* If sendto fails, the failed packet remains in TX buffer
* to be retried in the next interval. */
if(io->buf_len) {
if(packet_is_bbl(io->buf, io->buf_len)) {
/* Update timestamp if BBL traffic is retried. */
*(uint32_t*)(io->buf + (io->buf_len - 8)) = io->timestamp.tv_sec;
*(uint32_t*)(io->buf + (io->buf_len - 4)) = io->timestamp.tv_nsec;
}
} else {
if(bbl_stream_tx(io, io->buf, &io->buf_len) != PROTOCOL_SUCCESS) {
break;
}
}
if(sendto(io->fd, io->buf, io->buf_len, 0, (struct sockaddr*)&io->addr, sizeof(struct sockaddr_ll)) <0 ) {
if(errno == EMSGSIZE) {
io_raw_tx_lo_long(io);
/* First send all control traffic which has higher priority. */
while((slot = bbl_txq_read_slot(txq))) {
if(sendto(io->fd, slot->packet, slot->packet_len, 0, (struct sockaddr*)&io->addr, sizeof(struct sockaddr_ll)) >= 0 ) {
io->stats.packets++;
io->stats.bytes += slot->packet_len;
bbl_txq_read_next(txq);
if(burst) burst--;
} else {
/* This packet will be retried next interval
* because io->buf_len is not reset to zero. */
LOG(IO, "RAW sendto on interface %s failed with error %s (%d)\n",
io->interface->name, strerror(errno), errno);
io->stats.io_errors++;
burst = 0;
break;
}
} else {
io->stats.packets++;
io->stats.bytes += io->buf_len;
}
io->buf_len = 0;
/* Get TX timestamp */
clock_gettime(CLOCK_MONOTONIC, &io->timestamp);
while(burst) {
/* Send traffic streams up to allowed burst. */
stream = bbl_stream_io_send_iter(io);
if(unlikely(stream == NULL)) {
break;
}
if(unlikely(sendto(io->fd, stream->tx_buf, stream->tx_len, 0, (struct sockaddr*)&io->addr, sizeof(struct sockaddr_ll)) >=0)) {
stream->tx_packets++;
stream->flow_seq++;
io->stats.packets++;
io->stats.bytes += stream->tx_len;
burst--;
} else {
if(errno == EMSGSIZE) {
io->buf_len = stream->tx_len;
io_raw_tx_lo_long(io);
io->buf_len = 0;
} else {
LOG(IO, "RAW sendto on interface %s failed with error %s (%d)\n",
io->interface->name, strerror(errno), errno);
io->stats.io_errors++;
burst = 0;
}
}
}
}
}
@@ -280,9 +298,7 @@ io_raw_init(io_handle_s *io)
if(io->direction == IO_INGRESS) {
thread->run_fn = io_raw_thread_rx_run_fn;
} else {
timer_add_periodic(&thread->timer.root, &thread->timer.io, "TX (threaded)", 0,
config->tx_interval, thread, &io_raw_thread_tx_job);
thread->timer.io->reset = false;
thread->run_fn = io_raw_thread_tx_run_fn;
}
} else {
if(io->direction == IO_INGRESS) {
+19 -26
View File
@@ -62,7 +62,7 @@ io_thread_rx_handler(io_thread_s *thread, io_handle_s *io)
io->stats.packets++;
io->stats.bytes += io->buf_len;
if(likely(packet_is_bbl(io->buf, io->buf_len))) {
if(packet_is_bbl(io->buf, io->buf_len)) {
/** Process */
decode_result = decode_ethernet(io->buf, io->buf_len, thread->sp, SCRATCHPAD_LEN, &eth);
if(decode_result == PROTOCOL_SUCCESS) {
@@ -132,17 +132,25 @@ io_thread_main_rx_job(timer_s *timer)
/* Copy RX timestamp */
eth->timestamp.tv_sec = slot->timestamp.tv_sec;
eth->timestamp.tv_nsec = slot->timestamp.tv_nsec;
/* Dump the packet into pcap file. */
if(g_ctx->pcap.write_buf && (!eth->bbl || g_ctx->pcap.include_streams)) {
pcap = true;
pcapng_push_packet_header(&slot->timestamp, slot->packet, slot->packet_len,
interface->ifindex, PCAPNG_EPB_FLAGS_INBOUND);
}
bbl_rx_handler(interface, eth);
} else if(decode_result == UNKNOWN_PROTOCOL) {
io->stats.unknown++;
} else {
io->stats.protocol_errors++;
}
/* Dump the packet into pcap file. */
if(g_ctx->pcap.write_buf && (!eth->bbl || g_ctx->pcap.include_streams)) {
pcap = true;
pcapng_push_packet_header(&slot->timestamp, slot->packet, slot->packet_len,
interface->ifindex, PCAPNG_EPB_FLAGS_INBOUND);
/* Dump the packet into pcap file. */
if(g_ctx->pcap.write_buf) {
pcap = true;
pcapng_push_packet_header(&slot->timestamp, slot->packet, slot->packet_len,
interface->ifindex, PCAPNG_EPB_FLAGS_INBOUND);
}
if(decode_result == UNKNOWN_PROTOCOL) {
io->stats.unknown++;
} else {
io->stats.protocol_errors++;
}
}
bbl_txq_read_next(thread->txq);
}
@@ -211,17 +219,6 @@ io_thread_main(void *thread_data)
return NULL;
}
void
io_thread_timer_loop(io_thread_s *thread)
{
pthread_mutex_lock(&thread->mutex);
timer_smear_all_buckets(&thread->timer.root);
pthread_mutex_unlock(&thread->mutex);
while(thread->active) {
timer_walk(&thread->timer.root);
}
}
bool
io_thread_init(io_handle_s *io)
{
@@ -262,11 +259,8 @@ io_thread_init(io_handle_s *io)
return false;
}
/* Init thread timer root */
timer_init_root(&thread->timer.root);
/* Default run function which might be overwritten */
thread->run_fn = io_thread_timer_loop;
thread->run_fn = NULL;
/* Add thread main loop timers/jobs */
if(io->direction == IO_INGRESS && !interface->io.rx_job) {
@@ -313,7 +307,6 @@ io_thread_start_all()
sleep.tv_nsec = 7800179; /* prime number between 7 and 8ms */
while(thread) {
thread->active = true;
timer_smear_all_buckets(&thread->timer.root);
pthread_create(&thread->thread, NULL, io_thread_main, (void *)thread);
if(thread->set_cpu_affinity) {
if(pthread_setaffinity_np(thread->thread, sizeof(cpu_set_t), &thread->cpuset)) {
+9 -15
View File
@@ -157,16 +157,10 @@ The following steps are required to build the BNG Blaster with experimental
.. note::
Tested with DPDK version 21.11.2 (LTS) and Ubuntu 22.04 (LTS)!
Tested with DPDK version 23.11 (LTS) and Ubuntu 22.04 (LTS)!
It is recommended to install the DPDK development package if possible:
.. code-block:: none
sudo apt install dpdk libdpdk-dev
This package does not support all NIC types (e.g. Mellanox, ...),
which requires downloading and installing DPDK manually:
Download and install DPDK:
https://doc.dpdk.org/guides/linux_gsg/build_dpdk.html
.. code-block:: none
@@ -175,12 +169,12 @@ https://doc.dpdk.org/guides/linux_gsg/build_dpdk.html
sudo apt install meson ninja-build
# download DPDK
wget https://fast.dpdk.org/rel/dpdk-21.11.2.tar.xz
tar xJf dpdk-21.11.2.tar.xz
cd dpdk-stable-21.11.2
wget https://fast.dpdk.org/rel/dpdk-22.11.4.tar.xz
tar xJf dpdk-22.11.4.tar.xz
cd dpdk-stable-22.11.4
# build
meson build
# build with driver SDK
meson -Denable_driver_sdk=true build
cd build
ninja
@@ -200,9 +194,9 @@ If DPDK is installed correctly, cmake should show the following output:
.. code-block:: none
-- Build bngblaster with DPDK support
-- Found PkgConfig: /usr/bin/pkg-config (found version "0.29.2")
-- Found PkgConfig: /usr/bin/pkg-config (found version "1.8.0")
-- Checking for module 'libdpdk'
-- Found libdpdk, version 21.11.2
-- Found libdpdk, version 22.11.4
-- Found DPDK via pkg-config
The installed version should now show `dpdk` as new IO mode.