* [dpdk-dev] [PATCH v2] examples/ip_pipeline: add link identification feature
@ 2016-03-01 10:35 Fan Zhang
2016-03-07 11:41 ` Thomas Monjalon
0 siblings, 1 reply; 2+ messages in thread
From: Fan Zhang @ 2016-03-01 10:35 UTC (permalink / raw)
To: dev
This patch adds link identification feature to packet framework. To
identify a link, user can use both existing port-mask option, or specify
PCI device in every LINK section in the configuration file.
Signed-off-by: Fan Zhang <roy.fan.zhang@intel.com>
Acked-by: Cristian Dumitrescu <cristian.dumitrescu@intel.com>
---
*v2
To be applied on top of:
[dpdk-dev,v2] examples/ip_pipeline: config parser clean-up
(http://dpdk.org/dev/patchwork/patch/10569/)
examples/ip_pipeline/app.h | 19 +--
examples/ip_pipeline/config_check.c | 16 ++-
examples/ip_pipeline/config_parse.c | 155 +++++++++++++++++----
examples/ip_pipeline/init.c | 95 ++++++++++---
examples/ip_pipeline/pipeline/pipeline_common_fe.c | 12 +-
5 files changed, 231 insertions(+), 66 deletions(-)
diff --git a/examples/ip_pipeline/app.h b/examples/ip_pipeline/app.h
index 6510d6d..f749a5f 100644
--- a/examples/ip_pipeline/app.h
+++ b/examples/ip_pipeline/app.h
@@ -49,7 +49,7 @@
#include "pipeline.h"
#define APP_PARAM_NAME_SIZE PIPELINE_NAME_SIZE
-
+#define APP_LINK_PCI_BDF_SIZE 16
struct app_mempool_params {
char *name;
uint32_t parsed;
@@ -64,7 +64,7 @@ struct app_link_params {
uint32_t parsed;
uint32_t pmd_id; /* Generated based on port mask */
uint32_t arp_q; /* 0 = Disabled (packets go to default queue 0) */
- uint32_t tcp_syn_local_q; /* 0 = Disabled (pkts go to default queue) */
+ uint32_t tcp_syn_q; /* 0 = Disabled (pkts go to default queue) */
uint32_t ip_local_q; /* 0 = Disabled (pkts go to default queue 0) */
uint32_t tcp_local_q; /* 0 = Disabled (pkts go to default queue 0) */
uint32_t udp_local_q; /* 0 = Disabled (pkts go to default queue 0) */
@@ -73,6 +73,7 @@ struct app_link_params {
uint32_t ip; /* 0 = Invalid */
uint32_t depth; /* Valid only when IP is valid */
uint64_t mac_addr; /* Read from HW */
+ char pci_bdf[APP_LINK_PCI_BDF_SIZE];
struct rte_eth_conf conf;
uint8_t promisc;
@@ -265,6 +266,10 @@ struct app_thread_data {
struct rte_ring *msgq_out;
};
+#ifndef APP_MAX_LINKS
+#define APP_MAX_LINKS 16
+#endif
+
struct app_eal_params {
/* Map lcore set to physical cpu set */
char *coremap;
@@ -286,13 +291,13 @@ struct app_eal_params {
uint32_t ranks;
/* Add a PCI device in black list. */
- char *pci_blacklist;
+ char *pci_blacklist[APP_MAX_LINKS];
/* Add a PCI device in white list. */
- char *pci_whitelist;
+ char *pci_whitelist[APP_MAX_LINKS];
/* Add a virtual device. */
- char *vdev;
+ char *vdev[APP_MAX_LINKS];
/* Use VMware TSC map instead of native RDTSC */
uint32_t vmware_tsc_map_present;
@@ -367,10 +372,6 @@ struct app_eal_params {
#define APP_MAX_MEMPOOLS 8
#endif
-#ifndef APP_MAX_LINKS
-#define APP_MAX_LINKS 16
-#endif
-
#ifndef APP_LINK_MAX_HWQ_IN
#define APP_LINK_MAX_HWQ_IN 64
#endif
diff --git a/examples/ip_pipeline/config_check.c b/examples/ip_pipeline/config_check.c
index 1ff5763..fd9ff49 100644
--- a/examples/ip_pipeline/config_check.c
+++ b/examples/ip_pipeline/config_check.c
@@ -59,12 +59,16 @@ check_mempools(struct app_params *app)
static void
check_links(struct app_params *app)
{
- uint32_t n_links_port_mask = __builtin_popcountll(app->port_mask);
uint32_t i;
/* Check that number of links matches the port mask */
- APP_CHECK((app->n_links == n_links_port_mask),
- "Not enough links provided in the PORT_MASK\n");
+ if (app->port_mask) {
+ uint32_t n_links_port_mask =
+ __builtin_popcountll(app->port_mask);
+
+ APP_CHECK((app->n_links == n_links_port_mask),
+ "Not enough links provided in the PORT_MASK\n");
+ }
for (i = 0; i < app->n_links; i++) {
struct app_link_params *link = &app->link_params[i];
@@ -76,8 +80,8 @@ check_links(struct app_params *app)
rxq_max = 0;
if (link->arp_q > rxq_max)
rxq_max = link->arp_q;
- if (link->tcp_syn_local_q > rxq_max)
- rxq_max = link->tcp_syn_local_q;
+ if (link->tcp_syn_q > rxq_max)
+ rxq_max = link->tcp_syn_q;
if (link->ip_local_q > rxq_max)
rxq_max = link->ip_local_q;
if (link->tcp_local_q > rxq_max)
@@ -89,7 +93,7 @@ check_links(struct app_params *app)
for (i = 1; i <= rxq_max; i++)
APP_CHECK(((link->arp_q == i) ||
- (link->tcp_syn_local_q == i) ||
+ (link->tcp_syn_q == i) ||
(link->ip_local_q == i) ||
(link->tcp_local_q == i) ||
(link->udp_local_q == i) ||
diff --git a/examples/ip_pipeline/config_parse.c b/examples/ip_pipeline/config_parse.c
index 5f72af9..4695ac1 100644
--- a/examples/ip_pipeline/config_parse.c
+++ b/examples/ip_pipeline/config_parse.c
@@ -56,6 +56,7 @@
static struct app_params app_params_default = {
.config_file = "./config/ip_pipeline.cfg",
.log_level = APP_LOG_LEVEL_HIGH,
+ .port_mask = 0,
.eal_params = {
.channels = 4,
@@ -74,7 +75,7 @@ static const struct app_link_params link_params_default = {
.parsed = 0,
.pmd_id = 0,
.arp_q = 0,
- .tcp_syn_local_q = 0,
+ .tcp_syn_q = 0,
.ip_local_q = 0,
.tcp_local_q = 0,
.udp_local_q = 0,
@@ -83,6 +84,7 @@ static const struct app_link_params link_params_default = {
.ip = 0,
.depth = 0,
.mac_addr = 0,
+ .pci_bdf = {0},
.conf = {
.link_speed = 0,
@@ -205,12 +207,13 @@ struct app_pipeline_params default_pipeline_params = {
};
static const char app_usage[] =
- "Usage: %s [-f CONFIG_FILE] [-s SCRIPT_FILE] -p PORT_MASK "
+ "Usage: %s [-f CONFIG_FILE] [-s SCRIPT_FILE] [-p PORT_MASK] "
"[-l LOG_LEVEL] [--preproc PREPROCESSOR] [--preproc-args ARGS]\n"
"\n"
"Arguments:\n"
"\t-f CONFIG_FILE: Default config file is %s\n"
- "\t-p PORT_MASK: Mask of NIC port IDs in hexadecimal format\n"
+ "\t-p PORT_MASK: Mask of NIC port IDs in hex format (generated from "
+ "config file when not provided)\n"
"\t-s SCRIPT_FILE: No CLI script file is run when not specified\n"
"\t-l LOG_LEVEL: 0 = NONE, 1 = HIGH PRIO (default), 2 = LOW PRIO\n"
"\t--preproc PREPROCESSOR: Configuration file pre-processor\n"
@@ -283,6 +286,11 @@ parser_read_arg_bool(const char *p)
#define PARSE_ERROR(exp, section, entry) \
APP_CHECK(exp, "Parse error in section \"%s\": entry \"%s\"\n", section, entry)
+#define PARSE_ERROR_MESSAGE(exp, section, entry, message) \
+APP_CHECK(exp, "Parse error in section \"%s\", entry \"%s\": %s\n", \
+ section, entry, message)
+
+
#define PARSE_ERROR_MALLOC(exp) \
APP_CHECK(exp, "Parse error: no free memory\n")
@@ -657,29 +665,62 @@ parse_eal(struct app_params *app,
/* pci_blacklist */
if ((strcmp(entry->name, "pci_blacklist") == 0) ||
(strcmp(entry->name, "b") == 0)) {
- PARSE_ERROR_DUPLICATE((p->pci_blacklist == NULL),
- section_name,
- entry->name);
- p->pci_blacklist = strdup(entry->value);
+ uint32_t i;
+
+ for (i = 0; i < APP_MAX_LINKS; i++) {
+ if (p->pci_blacklist[i])
+ continue;
+
+ p->pci_blacklist[i] =
+ strdup(entry->value);
+ PARSE_ERROR_MALLOC(p->pci_blacklist[i]);
+ }
+
+ PARSE_ERROR_MESSAGE((i < APP_MAX_LINKS),
+ section_name, entry->name,
+ "too many elements");
continue;
}
/* pci_whitelist */
if ((strcmp(entry->name, "pci_whitelist") == 0) ||
(strcmp(entry->name, "w") == 0)) {
- PARSE_ERROR_DUPLICATE((p->pci_whitelist == NULL),
- section_name,
- entry->name);
- p->pci_whitelist = strdup(entry->value);
+ uint32_t i;
+
+ PARSE_ERROR_MESSAGE((app->port_mask != 0),
+ section_name, entry->name, "entry to be "
+ "generated by the application (port_mask "
+ "not provided)");
+
+ for (i = 0; i < APP_MAX_LINKS; i++) {
+ if (p->pci_whitelist[i])
+ continue;
+
+ p->pci_whitelist[i] = strdup(entry->value);
+ PARSE_ERROR_MALLOC(p->pci_whitelist[i]);
+ }
+
+ PARSE_ERROR_MESSAGE((i < APP_MAX_LINKS),
+ section_name, entry->name,
+ "too many elements");
continue;
}
/* vdev */
if (strcmp(entry->name, "vdev") == 0) {
- PARSE_ERROR_DUPLICATE((p->vdev == NULL),
- section_name,
- entry->name);
- p->vdev = strdup(entry->value);
+ uint32_t i;
+
+ for (i = 0; i < APP_MAX_LINKS; i++) {
+ if (p->vdev[i])
+ continue;
+
+ p->vdev[i] = strdup(entry->value);
+ PARSE_ERROR_MALLOC(p->vdev[i]);
+ }
+
+ PARSE_ERROR_MESSAGE((i < APP_MAX_LINKS),
+ section_name, entry->name,
+ "too many elements");
continue;
}
@@ -1395,6 +1436,7 @@ parse_link(struct app_params *app,
struct app_link_params *param;
struct rte_cfgfile_entry *entries;
int n_entries, i;
+ int pci_bdf_present = 0;
ssize_t param_idx;
n_entries = rte_cfgfile_section_num_entries(cfg, section_name);
@@ -1433,7 +1475,7 @@ parse_link(struct app_params *app,
if (strcmp(ent->name, "tcp_syn_q") == 0) {
int status = parser_read_uint32(
- ¶m->tcp_syn_local_q, ent->value);
+ ¶m->tcp_syn_q, ent->value);
PARSE_ERROR((status == 0), section_name, ent->name);
continue;
@@ -1476,10 +1518,31 @@ parse_link(struct app_params *app,
continue;
}
+ if (strcmp(ent->name, "pci_bdf") == 0) {
+ PARSE_ERROR_DUPLICATE((pci_bdf_present == 0),
+ section_name, ent->name);
+
+ snprintf(param->pci_bdf, APP_LINK_PCI_BDF_SIZE,
+ "%s", ent->value);
+ pci_bdf_present = 1;
+ continue;
+ }
+
/* unrecognized */
PARSE_ERROR_INVALID(0, section_name, ent->name);
}
+ /* Check for mandatory fields */
+ if (app->port_mask)
+ PARSE_ERROR_MESSAGE((pci_bdf_present == 0),
+ section_name, "pci_bdf",
+ "entry not allowed (port_mask is provided)");
+ else
+ PARSE_ERROR_MESSAGE((pci_bdf_present),
+ section_name, "pci_bdf",
+ "this entry is mandatory (port_mask is not "
+ "provided)");
+
param->parsed = 1;
free(entries);
@@ -2121,7 +2184,8 @@ create_implicit_mempools(struct app_params *app)
}
static void
-parse_port_mask(struct app_params *app, uint64_t port_mask)
+create_implicit_links_from_port_mask(struct app_params *app,
+ uint64_t port_mask)
{
uint32_t pmd_id, link_id;
@@ -2142,6 +2206,18 @@ parse_port_mask(struct app_params *app, uint64_t port_mask)
}
}
+static void
+assign_link_pmd_id_from_pci_bdf(struct app_params *app)
+{
+ uint32_t i;
+
+ for (i = 0; i < app->n_links; i++) {
+ struct app_link_params *link = &app->link_params[i];
+
+ link->pmd_id = i;
+ }
+}
+
int
app_config_parse(struct app_params *app, const char *file_name)
{
@@ -2153,7 +2229,8 @@ app_config_parse(struct app_params *app, const char *file_name)
create_implicit_mempools(app);
/* Port mask */
- parse_port_mask(app, app->port_mask);
+ if (app->port_mask)
+ create_implicit_links_from_port_mask(app, app->port_mask);
/* Load application configuration file */
cfg = rte_cfgfile_load(file_name, 0);
@@ -2232,6 +2309,9 @@ app_config_parse(struct app_params *app, const char *file_name)
APP_PARAM_COUNT(app->msgq_params, app->n_msgq);
APP_PARAM_COUNT(app->pipeline_params, app->n_pipelines);
+ if (app->port_mask == 0)
+ assign_link_pmd_id_from_pci_bdf(app);
+
/* Save configuration to output file */
app_config_save(app, app->output_file);
@@ -2245,6 +2325,7 @@ static void
save_eal_params(struct app_params *app, FILE *f)
{
struct app_eal_params *p = &app->eal_params;
+ uint32_t i;
fprintf(f, "[EAL]\n");
@@ -2263,14 +2344,29 @@ save_eal_params(struct app_params *app, FILE *f)
if (p->ranks_present)
fprintf(f, "%s = %" PRIu32 "\n", "r", p->ranks);
- if (p->pci_blacklist)
- fprintf(f, "%s = %s\n", "pci_blacklist", p->pci_blacklist);
+ for (i = 0; i < APP_MAX_LINKS; i++) {
+ if (p->pci_blacklist[i] == NULL)
+ break;
+
+ fprintf(f, "%s = %s\n", "pci_blacklist",
+ p->pci_blacklist[i]);
+ }
- if (p->pci_whitelist)
- fprintf(f, "%s = %s\n", "pci_whitelist", p->pci_whitelist);
+ for (i = 0; i < APP_MAX_LINKS; i++) {
+ if (p->pci_whitelist[i] == NULL)
+ break;
- if (p->vdev)
- fprintf(f, "%s = %s\n", "vdev", p->vdev);
+ fprintf(f, "%s = %s\n", "pci_whitelist",
+ p->pci_whitelist[i]);
+ }
+
+ for (i = 0; i < APP_MAX_LINKS; i++) {
+ if (p->vdev[i] == NULL)
+ break;
+
+ fprintf(f, "%s = %s\n", "vdev",
+ p->vdev[i]);
+ }
if (p->vmware_tsc_map_present)
fprintf(f, "%s = %s\n", "vmware_tsc_map",
@@ -2371,14 +2467,17 @@ save_links_params(struct app_params *app, FILE *f)
fprintf(f, "; %s = %" PRIu32 "\n", "pmd_id", p->pmd_id);
fprintf(f, "%s = %s\n", "promisc", p->promisc ? "yes" : "no");
fprintf(f, "%s = %" PRIu32 "\n", "arp_q", p->arp_q);
- fprintf(f, "%s = %" PRIu32 "\n", "tcp_syn_local_q",
- p->tcp_syn_local_q);
+ fprintf(f, "%s = %" PRIu32 "\n", "tcp_syn_q",
+ p->tcp_syn_q);
fprintf(f, "%s = %" PRIu32 "\n", "ip_local_q", p->ip_local_q);
fprintf(f, "%s = %" PRIu32 "\n", "tcp_local_q", p->tcp_local_q);
fprintf(f, "%s = %" PRIu32 "\n", "udp_local_q", p->udp_local_q);
fprintf(f, "%s = %" PRIu32 "\n", "sctp_local_q",
p->sctp_local_q);
+ if (strlen(p->pci_bdf))
+ fprintf(f, "%s = %s\n", "pci_bdf", p->pci_bdf);
+
fputc('\n', f);
}
}
@@ -2891,10 +2990,6 @@ app_config_args(struct app_params *app, int argc, char **argv)
optind = 0; /* reset getopt lib */
- /* Check that mandatory args have been provided */
- if (!p_present)
- rte_panic("Error: PORT_MASK is not provided\n");
-
/* Check dependencies between args */
if (preproc_params_present && (preproc_present == 0))
rte_panic("Error: Preprocessor args specified while "
diff --git a/examples/ip_pipeline/init.c b/examples/ip_pipeline/init.c
index 186ca03..0bbef05 100644
--- a/examples/ip_pipeline/init.c
+++ b/examples/ip_pipeline/init.c
@@ -96,9 +96,10 @@ app_init_core_mask(struct app_params *app)
static void
app_init_eal(struct app_params *app)
{
- char buffer[32];
+ char buffer[256];
struct app_eal_params *p = &app->eal_params;
uint32_t n_args = 0;
+ uint32_t i;
int status;
app->eal_argv[n_args++] = strdup(app->app_name);
@@ -132,24 +133,47 @@ app_init_eal(struct app_params *app)
app->eal_argv[n_args++] = strdup(buffer);
}
- if (p->pci_blacklist) {
+ for (i = 0; i < APP_MAX_LINKS; i++) {
+ if (p->pci_blacklist[i] == NULL)
+ break;
+
snprintf(buffer,
sizeof(buffer),
"--pci-blacklist=%s",
- p->pci_blacklist);
+ p->pci_blacklist[i]);
app->eal_argv[n_args++] = strdup(buffer);
}
- if (p->pci_whitelist) {
+ if (app->port_mask != 0)
+ for (i = 0; i < APP_MAX_LINKS; i++) {
+ if (p->pci_whitelist[i] == NULL)
+ break;
+
+ snprintf(buffer,
+ sizeof(buffer),
+ "--pci-whitelist=%s",
+ p->pci_whitelist[i]);
+ app->eal_argv[n_args++] = strdup(buffer);
+ }
+ else
+ for (i = 0; i < app->n_links; i++) {
+ char *pci_bdf = app->link_params[i].pci_bdf;
+
+ snprintf(buffer,
+ sizeof(buffer),
+ "--pci-whitelist=%s",
+ pci_bdf);
+ app->eal_argv[n_args++] = strdup(buffer);
+ }
+
+ for (i = 0; i < APP_MAX_LINKS; i++) {
+ if (p->vdev[i] == NULL)
+ break;
+
snprintf(buffer,
sizeof(buffer),
- "--pci-whitelist=%s",
- p->pci_whitelist);
- app->eal_argv[n_args++] = strdup(buffer);
- }
-
- if (p->vdev) {
- snprintf(buffer, sizeof(buffer), "--vdev=%s", p->vdev);
+ "--vdev=%s",
+ p->vdev[i]);
app->eal_argv[n_args++] = strdup(buffer);
}
@@ -267,6 +291,15 @@ app_init_eal(struct app_params *app)
app->eal_argc = n_args;
APP_LOG(app, HIGH, "Initializing EAL ...");
+ if (app->log_level >= APP_LOG_LEVEL_LOW) {
+ int i;
+
+ fprintf(stdout, "[APP] EAL arguments: \"");
+ for (i = 1; i < app->eal_argc; i++)
+ fprintf(stdout, "%s ", app->eal_argv[i]);
+ fprintf(stdout, "\"\n");
+ }
+
status = rte_eal_init(app->eal_argc, app->eal_argv);
if (status < 0)
rte_panic("EAL init error\n");
@@ -317,7 +350,7 @@ app_link_filter_tcp_syn_add(struct app_link_params *link)
{
struct rte_eth_syn_filter filter = {
.hig_pri = 1,
- .queue = link->tcp_syn_local_q,
+ .queue = link->tcp_syn_q,
};
return rte_eth_dev_filter_ctrl(link->pmd_id,
@@ -555,28 +588,45 @@ app_link_set_arp_filter(struct app_params *app, struct app_link_params *cp)
static void
app_link_set_tcp_syn_filter(struct app_params *app, struct app_link_params *cp)
{
- if (cp->tcp_syn_local_q != 0) {
+ if (cp->tcp_syn_q != 0) {
int status = app_link_filter_tcp_syn_add(cp);
APP_LOG(app, LOW, "%s (%" PRIu32 "): "
"Adding TCP SYN filter (queue = %" PRIu32 ")",
- cp->name, cp->pmd_id, cp->tcp_syn_local_q);
+ cp->name, cp->pmd_id, cp->tcp_syn_q);
if (status)
rte_panic("%s (%" PRIu32 "): "
"Error adding TCP SYN filter "
"(queue = %" PRIu32 ") (%" PRId32 ")\n",
- cp->name, cp->pmd_id, cp->tcp_syn_local_q,
+ cp->name, cp->pmd_id, cp->tcp_syn_q,
status);
}
}
+static int
+app_link_is_virtual(struct app_link_params *p)
+{
+ uint32_t pmd_id = p->pmd_id;
+ struct rte_eth_dev *dev = &rte_eth_devices[pmd_id];
+
+ if (dev->dev_type == RTE_ETH_DEV_VIRTUAL)
+ return 1;
+
+ return 0;
+}
+
void
app_link_up_internal(struct app_params *app, struct app_link_params *cp)
{
uint32_t i;
int status;
+ if (app_link_is_virtual(cp)) {
+ cp->state = 1;
+ return;
+ }
+
/* For each link, add filters for IP of current link */
if (cp->ip != 0) {
for (i = 0; i < app->n_links; i++) {
@@ -671,8 +721,8 @@ app_link_up_internal(struct app_params *app, struct app_link_params *cp)
/* PMD link up */
status = rte_eth_dev_set_link_up(cp->pmd_id);
if (status < 0)
- rte_panic("%s (%" PRIu32 "): PMD set up error %" PRId32 "\n",
- cp->name, cp->pmd_id, status);
+ rte_panic("%s (%" PRIu32 "): PMD set link up error %"
+ PRId32 "\n", cp->name, cp->pmd_id, status);
/* Mark link as UP */
cp->state = 1;
@@ -682,9 +732,18 @@ void
app_link_down_internal(struct app_params *app, struct app_link_params *cp)
{
uint32_t i;
+ int status;
+
+ if (app_link_is_virtual(cp)) {
+ cp->state = 0;
+ return;
+ }
/* PMD link down */
- rte_eth_dev_set_link_down(cp->pmd_id);
+ status = rte_eth_dev_set_link_down(cp->pmd_id);
+ if (status < 0)
+ rte_panic("%s (%" PRIu32 "): PMD set link down error %"
+ PRId32 "\n", cp->name, cp->pmd_id, status);
/* Mark link as DOWN */
cp->state = 0;
diff --git a/examples/ip_pipeline/pipeline/pipeline_common_fe.c b/examples/ip_pipeline/pipeline/pipeline_common_fe.c
index 1e16ad6..bffc9a4 100644
--- a/examples/ip_pipeline/pipeline/pipeline_common_fe.c
+++ b/examples/ip_pipeline/pipeline/pipeline_common_fe.c
@@ -898,9 +898,15 @@ print_link_info(struct app_link_params *p)
mac_addr = (struct ether_addr *) &p->mac_addr;
- printf("%s: flags=<%s>\n",
- p->name,
- (p->state) ? "UP" : "DOWN");
+ if (strlen(p->pci_bdf))
+ printf("%s(%s): flags=<%s>\n",
+ p->name,
+ p->pci_bdf,
+ (p->state) ? "UP" : "DOWN");
+ else
+ printf("%s: flags=<%s>\n",
+ p->name,
+ (p->state) ? "UP" : "DOWN");
if (p->ip)
printf("\tinet %" PRIu32 ".%" PRIu32
--
2.5.0
^ permalink raw reply [flat|nested] 2+ messages in thread
end of thread, other threads:[~2016-03-07 11:43 UTC | newest]
Thread overview: 2+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2016-03-01 10:35 [dpdk-dev] [PATCH v2] examples/ip_pipeline: add link identification feature Fan Zhang
2016-03-07 11:41 ` Thomas Monjalon
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).