/* // Copyright (c) 2010-2017 Intel Corporation // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. */ #include #include #include #include #include #include "prox_malloc.h" #include "version.h" #include "defines.h" #include "prox_args.h" #include "prox_assert.h" #include "prox_cfg.h" #include "cfgfile.h" #include "quit.h" #include "log.h" #include "parse_utils.h" #include "prox_port_cfg.h" #include "defaults.h" #include "prox_lua.h" #include "cqm.h" #include "prox_compat.h" #define MAX_RTE_ARGV 64 #define MAX_ARG_LEN 64 struct cfg_depr { const char *opt; const char *info; }; /* Helper macro */ #define STR_EQ(s1, s2) (!strcmp((s1), (s2))) /* configuration files support */ static int get_rte_cfg(unsigned sindex, char *str, void *data); static int get_global_cfg(unsigned sindex, char *str, void *data); static int get_port_cfg(unsigned sindex, char *str, void *data); static int get_defaults_cfg(unsigned sindex, char *str, void *data); static int get_cache_set_cfg(unsigned sindex, char *str, void *data); static int get_var_cfg(unsigned sindex, char *str, void *data); static int get_lua_cfg(unsigned sindex, char *str, void *data); static int get_core_cfg(unsigned sindex, char *str, void *data); static const char *cfg_file = DEFAULT_CONFIG_FILE; static struct rte_cfg rte_cfg; struct prox_cache_set_cfg prox_cache_set_cfg[PROX_MAX_CACHE_SET]; static char format_err_str[1024]; static const char *err_str = "Unknown error"; static struct cfg_section eal_default_cfg = { .name = "eal options", .parser = get_rte_cfg, .data = &rte_cfg, .indexp[0] = 0, .nbindex = 1, .error = 0 }; static struct cfg_section port_cfg = { .name = "port #", .parser = get_port_cfg, .data = &prox_port_cfg, .indexp[0] = 0, .nbindex = 1, .error = 0 }; static struct cfg_section var_cfg = { .name = "variables", .parser = get_var_cfg, .data = 0, .indexp[0] = 0, .nbindex = 1, .error = 0 }; static struct cfg_section cache_set_cfg = { .name = "cache set #", .parser = get_cache_set_cfg, .data = &prox_cache_set_cfg, .indexp[0] = 0, .nbindex = 1, .error = 0 }; static struct cfg_section defaults_cfg = { .name = "defaults", .parser = get_defaults_cfg, .data = 0, .indexp[0] = 0, .nbindex = 1, .error = 0 }; static struct cfg_section settings_cfg = { .name = "global", .parser = get_global_cfg, .data = &prox_cfg, .indexp[0] = 0, .nbindex = 1, .error = 0 }; static struct cfg_section lua_cfg = { .name = "lua", .parser = get_lua_cfg, .raw_lines = 1, .indexp[0] = 0, .nbindex = 1, .error = 0, }; static struct cfg_section core_cfg = { .name = "core #", .parser = get_core_cfg, .data = lcore_cfg_init, .indexp[0] = 0, .nbindex = 1, .error = 0 }; static void set_errf(const char *format, ...) { va_list ap; va_start(ap, format); vsnprintf(format_err_str, sizeof(format_err_str), format, ap); va_end(ap); err_str = format_err_str; } /* [eal options] parser */ static int get_rte_cfg(__attribute__((unused))unsigned sindex, char *str, void *data) { struct rte_cfg *pconfig = (struct rte_cfg *)data; if (str == NULL || pconfig == NULL) { return -1; } char *pkey = get_cfg_key(str); if (pkey == NULL) { set_errf("Missing key after option"); return -1; } if (STR_EQ(str, "-m")) { return parse_int(&pconfig->memory, pkey); } if (STR_EQ(str, "-n")) { if (parse_int(&pconfig->force_nchannel, pkey)) { return -1; } if (pconfig->force_nchannel == 0) { set_errf("Invalid number of memory channels"); return -1; } return 0; } if (STR_EQ(str, "-r")) { if (parse_int(&pconfig->force_nrank, pkey)) { return -1; } if (pconfig->force_nrank == 0 || pconfig->force_nrank > 16) { set_errf("Invalid number of memory ranks"); return -1; } return 0; } /* debug options */ if (STR_EQ(str, "no-pci")) { return parse_bool(&pconfig->no_pci, pkey); } if (STR_EQ(str, "no-hpet")) { return parse_bool(&pconfig->no_hpet, pkey); } if (STR_EQ(str, "no-shconf")) { return parse_bool(&pconfig->no_shconf, pkey); } if (STR_EQ(str, "no-huge")) { return parse_bool(&pconfig->no_hugetlbfs, pkey); } if (STR_EQ(str, "no-output")) { return parse_bool(&pconfig->no_output, pkey); } if (STR_EQ(str, "huge-dir")) { if (pconfig->hugedir) { free(pconfig->hugedir); } pconfig->hugedir = strdup(pkey); return 0; } if (STR_EQ(str, "eal")) { char eal[MAX_STR_LEN_PROC]; if (pconfig->eal) { free(pconfig->eal); pconfig->eal = NULL; } if (parse_str(eal, pkey, sizeof(eal))) return -1; pkey = eal; strip_spaces(&pkey, 1); if (*pkey) pconfig->eal = strdup(pkey); return 0; } set_errf("Option '%s' is not known", str); return -1; } struct cfg_depr global_cfg_depr[] = { {"virtualization", "This is now set automatically if needed"}, {"qinq_tag", "This option is deprecated"}, {"wait on quit", "This is now set automatically if needed"}, {"version", ""} }; const char *get_cfg_dir(void) { static char dir[PATH_MAX]; size_t end = strlen(cfg_file) - 1; while (end > 0 && cfg_file[end] != '/') end--; strncpy(dir, cfg_file, end); return dir; } static int get_lua_cfg(__attribute__((unused)) unsigned sindex, __attribute__((unused)) char *str, __attribute__((unused)) void *data) { int status; char cwd[1024]; if (NULL == getcwd(cwd, sizeof(cwd))) { set_errf("Failed to get current directory while loading Lua file\n"); return -1; } status = chdir(get_cfg_dir()); if (status) { set_errf("Failed to change directory to '%s' while loading Lua file\n", get_cfg_dir()); return -1; } struct lua_State *l = prox_lua(); char str_cpy[1024]; strncpy(str_cpy, str, sizeof(str_cpy)); uint32_t len = strlen(str_cpy); str_cpy[len++] = '\n'; str_cpy[len++] = 0; status = luaL_loadstring(l, str_cpy); if (status) { set_errf("Lua error: '%s'\n", lua_tostring(l, -1)); status = chdir(cwd); return -1; } status = lua_pcall(l, 0, LUA_MULTRET, 0); if (status) { set_errf("Lua error: '%s'\n", lua_tostring(l, -1)); status = chdir(cwd); return -1; } status = chdir(cwd); if (status) { set_errf("Failed to restore current directory to '%s' while loading Lua file\n", cwd); return -1; } return 0; } /* [global] parser */ static int get_global_cfg(__attribute__((unused))unsigned sindex, char *str, void *data) { struct prox_cfg *pset = (struct prox_cfg *)data; if (str == NULL || pset == NULL) { return -1; } char *pkey = get_cfg_key(str); if (pkey == NULL) { set_errf("Missing key after option"); return -1; } for (uint32_t i = 0; i < RTE_DIM(global_cfg_depr); ++i) { if (STR_EQ(str, global_cfg_depr[i].opt)) { set_errf("Option '%s' is deprecated%s%s", global_cfg_depr[i].opt, strlen(global_cfg_depr[i].info)? ": ": "", global_cfg_depr[i].info); return -1; } } if (STR_EQ(str, "name")) { return parse_str(pset->name, pkey, sizeof(pset->name)); } if (STR_EQ(str, "start time")) { return parse_int(&pset->start_time, pkey); } if (STR_EQ(str, "duration time")) { return parse_int(&pset->duration_time, pkey); } if (STR_EQ(str, "shuffle")) { return parse_flag(&pset->flags, DSF_SHUFFLE, pkey); } if (STR_EQ(str, "disable cmt")) { return parse_flag(&pset->flags, DSF_DISABLE_CMT, pkey); } if (STR_EQ(str, "mp rings")) { return parse_flag(&pset->flags, DSF_MP_RINGS, pkey); } if (STR_EQ(str, "enable bypass")) { return parse_flag(&pset->flags, DSF_ENABLE_BYPASS, pkey); } if (STR_EQ(str, "cpe table map")) { /* The config defined ports through 0, 1, 2 ... which need to be associated with ports. This is done through defining it using "cpe table map=" */ return parse_port_name_list((uint32_t*)pset->cpe_table_ports, NULL, PROX_MAX_PORTS, pkey); } if (STR_EQ(str, "pre cmd")) { return system(pkey); } if (STR_EQ(str, "unique mempool per socket")) { return parse_flag(&pset->flags, UNIQUE_MEMPOOL_PER_SOCKET, pkey); } if (STR_EQ(str, "log buffer size")) { if (parse_kmg(&pset->logbuf_size, pke
/*
 * Sample kobject implementation
 *
 * Copyright (C) 2004-2007 Greg Kroah-Hartman <greg@kroah.com>
 * Copyright (C) 2007 Novell Inc.
 *
 * Released under the GPL version 2 only.
 *
 */
#include <linux/kobject.h>
#include <linux/string.h>
#include <linux/sysfs.h>
#include <linux/module.h>
#include <linux/init.h>

/*
 * This module shows how to create a simple subdirectory in sysfs called
 * /sys/kernel/kobject-example  In that directory, 3 files are created:
 * "foo", "baz", and "bar".  If an integer is written to these files, it can be
 * later read out of it.
 */

static int foo;
static int baz;
static int bar;

/*
 * The "foo" file where a static variable is read from and written to.
 */
static ssize_t foo_show(struct kobject *kobj, struct kobj_attribute *attr,
			char *buf)
{
	return sprintf(buf, "%d\n", foo);
}

static ssize_t foo_store(struct kobject *kobj, struct kobj_attribute *attr,
			 const char *buf, size_t count)
{
	int ret;

	ret = kstrtoint(buf, 10, &foo);
	if (ret < 0)
		return ret;

	return count;
}

/* Sysfs attributes cannot be world-writable. */
static struct kobj_attribute foo_attribute =
	__ATTR(foo, 0664, foo_show, foo_store);

/*
 * More complex function where we determine which variable is being accessed by
 * looking at the attribute for the "baz" and "bar" files.
 */
static ssize_t b_show(struct kobject *kobj, struct kobj_attribute *attr,
		      char *buf)
{
	int var;

	if (strcmp(attr->attr.name, "baz") == 0)
		var = baz;
	else
		var = bar;
	return sprintf(buf, "%d\n", var);
}

static ssize_t b_store(struct kobject *kobj, struct kobj_attribute *attr,
		       const char *buf, size_t count)
{
	int var, ret;

	ret = kstrtoint(buf, 10, &var);
	if (ret < 0)
		return ret;

	if (strcmp(attr->attr.name, "baz") == 0)
		baz = var;
	else
		bar = var;
	return count;
}

static struct kobj_attribute baz_attribute =
	__ATTR(baz, 0664, b_show, b_store);
static struct kobj_attribute bar_attribute =
	__ATTR(bar, 0664, b_show, b_store);


/*
 * Create a group of attributes so that we can create and destroy them all
 * at once.
 */
static struct attribute *attrs[] = {
	&foo_attribute.attr,
	&baz_attribute.attr,
	&bar_attribute.attr,
	NULL,	/* need to NULL terminate the list of attributes */
};

/*
 * An unnamed attribute group will put all of the attributes directly in
 * the kobject directory.  If we specify a name, a subdirectory will be
 * created for the attributes with the directory being the name of the
 * attribute group.
 */
static struct attribute_group attr_group = {
	.attrs = attrs,
};

static struct kobject *example_kobj;

static int __init example_init(void)
{
	int retval;

	/*
	 * Create a simple kobject with the name of "kobject_example",
	 * located under /sys/kernel/
	 *
	 * As this is a simple directory, no uevent will be sent to
	 * userspace.  That is why this function should not be used for
	 * any type of dynamic kobjects, where the name and number are
	 * not known ahead of time.
	 */
	example_kobj = kobject_create_and_add("kobject_example", kernel_kobj);
	if (!example_kobj)
		return -ENOMEM;

	/* Create the files associated with this kobject */
	retval = sysfs_create_group(example_kobj, &attr_group);
	if (retval)
		kobject_put(example_kobj);

	return retval;
}

static void __exit example_exit(void)
{
	kobject_put(example_kobj);
}

module_init(example_init);
module_exit(example_exit);
MODULE_LICENSE("GPL v2");
MODULE_AUTHOR("Greg Kroah-Hartman <greg@kroah.com>");
packet")) return 0; else if (STR_EQ(pkey, "hw")) { targ->flags |= TASK_ARG_HW_SRC_MAC; return 0; } else { return -1; } } targ->flags |= TASK_ARG_SRC_MAC_SET; return 0; } if (STR_EQ(str, "gateway ipv4")) { /* Gateway IP address used when generating */ return parse_ip(&targ->gateway_ipv4, pkey); } if (STR_EQ(str, "local ipv4")) { /* source IP address to be used for packets */ return parse_ip(&targ->local_ipv4, pkey); } if (STR_EQ(str, "remote ipv4")) { /* source IP address to be used for packets */ return parse_ip(&targ->remote_ipv4, pkey); } if (STR_EQ(str, "local ipv6")) { /* source IPv6 address to be used for packets */ return parse_ip6(&targ->local_ipv6, pkey); } if (STR_EQ(str, "number of packets")) return parse_int(&targ->n_pkts, pkey); if (STR_EQ(str, "pipes")) { uint32_t val; int err = parse_int(&val, pkey); if (err) return -1; if (!val || !rte_is_power_of_2(val)) { set_errf("Number of pipes has to be power of 2 and not zero"); return -1; } targ->qos_conf.port_params.n_pipes_per_subport = val; return 0; } if (STR_EQ(str, "queue size")) { uint32_t val; int err = parse_int(&val, pkey); if (err) { return -1; } targ->qos_conf.port_params.qsize[0] = val; targ->qos_conf.port_params.qsize[1] = val; targ->qos_conf.port_params.qsize[2] = val; targ->qos_conf.port_params.qsize[3] = val; return 0; } if (STR_EQ(str, "subport tb rate")) { return parse_int(&targ->qos_conf.subport_params[0].tb_rate, pkey); } if (STR_EQ(str, "subport tb size")) { return parse_int(&targ->qos_conf.subport_params[0].tb_size, pkey); } if (STR_EQ(str, "subport tc 0 rate")) { return parse_int(&targ->qos_conf.subport_params[0].tc_rate[0], pkey); } if (STR_EQ(str, "subport tc 1 rate")) { return parse_int(&targ->qos_conf.subport_params[0].tc_rate[1], pkey); } if (STR_EQ(str, "subport tc 2 rate")) { return parse_int(&targ->qos_conf.subport_params[0].tc_rate[2], pkey); } if (STR_EQ(str, "subport tc 3 rate")) { return parse_int(&targ->qos_conf.subport_params[0].tc_rate[3], pkey); } if (STR_EQ(str, "subport tc rate")) { uint32_t val; int err = parse_int(&val, pkey); if (err) { return -1; } targ->qos_conf.subport_params[0].tc_rate[0] = val; targ->qos_conf.subport_params[0].tc_rate[1] = val; targ->qos_conf.subport_params[0].tc_rate[2] = val; targ->qos_conf.subport_params[0].tc_rate[3] = val; return 0; } if (STR_EQ(str, "subport tc period")) { return parse_int(&targ->qos_conf.subport_params[0].tc_period, pkey); } if (STR_EQ(str, "pipe tb rate")) { return parse_int(&targ->qos_conf.pipe_params[0].tb_rate, pkey); } if (STR_EQ(str, "pipe tb size")) { return parse_int(&targ->qos_conf.pipe_params[0].tb_size, pkey); } if (STR_EQ(str, "pipe tc rate")) { uint32_t val; int err = parse_int(&val, pkey); if (err) { return -1; } targ->qos_conf.pipe_params[0].tc_rate[0] = val; targ->qos_conf.pipe_params[0].tc_rate[1] = val; targ->qos_conf.pipe_params[0].tc_rate[2] = val; targ->qos_conf.pipe_params[0].tc_rate[3] = val; return 0; } if (STR_EQ(str, "pipe tc 0 rate")) { return parse_int(&targ->qos_conf.pipe_params[0].tc_rate[0], pkey); } if (STR_EQ(str, "pipe tc 1 rate")) { return parse_int(&targ->qos_conf.pipe_params[0].tc_rate[1], pkey); } if (STR_EQ(str, "pipe tc 2 rate")) { return parse_int(&targ->qos_conf.pipe_params[0].tc_rate[2], pkey); } if (STR_EQ(str, "pipe tc 3 rate")) { return parse_int(&targ->qos_conf.pipe_params[0].tc_rate[3], pkey); } if (STR_EQ(str, "pipe tc period")) { return parse_int(&targ->qos_conf.pipe_params[0].tc_period, pkey); } if (STR_EQ(str, "police action")) { char *in = strstr(pkey, " io="); if (in == NULL) { set_errf("Need to specify io colors using io=in_color,out_color\n"); return -1; } *in = 0; in += strlen(" io="); char *out = strstr(in, ","); if (out == NULL) { set_errf("Output color not specified\n"); } *out = 0; out++; enum police_action in_color = str_to_color(in); enum police_action out_color = str_to_color(out); if (in_color == ACT_INVALID) { set_errf("Invalid input color %s. Expected green, yellow or red", in); return -1; } if (out_color == ACT_INVALID) { set_errf("Invalid output color %s. Expected green, yellow or red", out); return -1; } enum police_action action = str_to_color(pkey); if (action == ACT_INVALID) { set_errf("Error action %s. Expected green, yellow, red or drop", pkey); return -1; } targ->police_act[in_color][out_color] = action; return 0; } if (STR_EQ(str, "qinq tag")) { return parse_int(&targ->qinq_tag, pkey); } if (STR_EQ(str, "cir")) { return parse_int(&targ->cir, pkey); } if (STR_EQ(str, "cbs")) { return parse_int(&targ->cbs, pkey); } if (STR_EQ(str, "pir")) { return parse_int(&targ->pir, pkey); } if (STR_EQ(str, "pbs")) { return parse_int(&targ->pbs, pkey); } if (STR_EQ(str, "ebs")) { return parse_int(&targ->ebs, pkey); } uint32_t queue_id = 0; if (sscanf(str, "queue %d weight", &queue_id) == 1) { uint32_t val; int err = parse_int(&val, pkey); if (err) { return -1; } targ->qos_conf.pipe_params[0].wrr_weights[queue_id] = val; return 0; } if (STR_EQ(str, "classify")) { if (!(targ->task_init->flag_features & TASK_FEATURE_CLASSIFY)) { set_errf("Classify is not supported in '%s' mode", targ->task_init->mode_str); return -1; } return parse_flag(&targ->runtime_flags, TASK_CLASSIFY, pkey); } if (STR_EQ(str, "flow table size")) { return parse_int(&targ->flow_table_size, pkey); } #ifdef GRE_TP if (STR_EQ(str, "tbf rate")) { return parse_int(&targ->tb_rate, pkey); } if (STR_EQ(str, "tbf size")) { return parse_int(&targ->tb_size, pkey); } #endif if (STR_EQ(str, "max rules")) { return parse_int(&targ->n_max_rules, pkey); } if (STR_EQ(str, "tunnel hop limit")) { uint32_t val; int err = parse_int(&val, pkey); if (err) { return -1; } targ->tunnel_hop_limit = val; return 0; } if (STR_EQ(str, "lookup port mask")) { uint32_t val; int err = parse_int(&val, pkey); if (err) { return -1; } targ->lookup_port_mask = val; return 0; } if (STR_EQ(str, "irq debug")) { parse_int(&targ->irq_debug, pkey); return 0; } set_errf("Option '%s' is not known", str); /* fail on unknown keys */ return -1; } static int str_is_number(const char *in) { int dot_once = 0; for (size_t i = 0; i < strlen(in); ++i) { if (!dot_once && in[i] == '.') { dot_once = 1; continue; } if (in[i] < '0' || in[i] > '9') return 0; } return 1; } /* command line parameters parsing procedure */ int prox_parse_args(int argc, char **argv) { int i, opt, ret; char *tmp, *tmp2; char tmp3[64]; /* Default settings */ prox_cfg.flags |= DSF_AUTOSTART | DSF_WAIT_ON_QUIT; prox_cfg.ui = PROX_UI_CURSES; plog_info("\tCommand line:"); for (i = 0; i < argc; ++i) { plog_info(" %s", argv[i]); } plog_info("\n"); while ((opt = getopt(argc, argv, "f:dnzpo:tkuar:emsiw:l:v:q:")) != EOF) { switch (opt) { case 'f': /* path to config file */ cfg_file = optarg; size_t offset = 0; for (size_t i = 0; i < strlen(cfg_file); ++i) { if (cfg_file[i] == '/') { offset = i + 1; } } strncpy(prox_cfg.name, cfg_file + offset, MAX_NAME_SIZE); break; case 'v': plog_set_lvl(atoi(optarg)); break; case 'l': prox_cfg.log_name_pid = 0; strncpy(prox_cfg.log_name, optarg, MAX_NAME_SIZE); break; case 'p': prox_cfg.log_name_pid = 1; break; case 'k': prox_cfg.use_stats_logger = 1; break; case 'd': prox_cfg.flags |= DSF_DAEMON; prox_cfg.ui = PROX_UI_NONE; break; case 'z': prox_cfg.flags |= DSF_USE_DUMMY_CPU_TOPO; prox_cfg.flags |= DSF_CHECK_INIT; break; case 'n': prox_cfg.flags |= DSF_USE_DUMMY_DEVICES; break; case 'r': if (!str_is_number(optarg) || strlen(optarg) > 11) return -1; strncpy(prox_cfg.update_interval_str, optarg, sizeof(prox_cfg.update_interval_str)); break; case 'o': if (prox_cfg.flags & DSF_DAEMON) break; if (!strcmp(optarg, "curses")) { prox_cfg.ui = PROX_UI_CURSES; } else if (!strcmp(optarg, "cli")) { prox_cfg.ui = PROX_UI_CLI; } else if (!strcmp(optarg, "none")) { prox_cfg.ui = PROX_UI_NONE; } else { plog_err("Invalid local UI '%s', local UI can be 'curses', 'cli' or 'none'.", optarg); return -1; } break; case 'q': if (luaL_loadstring(prox_lua(), optarg)) { set_errf("Lua error: '%s'\n", lua_tostring(prox_lua(), -1)); return -1; } if (lua_pcall(prox_lua(), 0, LUA_MULTRET, 0)) { set_errf("Lua error: '%s'\n", lua_tostring(prox_lua(), -1)); return -1; } break; case 'a': /* autostart all cores */ prox_cfg.flags |= DSF_AUTOSTART; break; case 'e': /* don't autostart */ prox_cfg.flags &= ~DSF_AUTOSTART; break; case 't': prox_cfg.flags |= DSF_LISTEN_TCP; break; case 'u': prox_cfg.flags |= DSF_LISTEN_UDS; break; case 'm': /* list supported task modes and exit */ prox_cfg.flags |= DSF_LIST_TASK_MODES; break; case 's': /* check configuration file syntax and exit */ prox_cfg.flags |= DSF_CHECK_SYNTAX; break; case 'i': /* check initialization sequence and exit */ prox_cfg.flags |= DSF_CHECK_INIT; break; case 'w': tmp = optarg; tmp2 = 0; if (strlen(tmp) >= 3 && (tmp2 = strchr(tmp, '='))) { *tmp2 = 0; tmp3[0] = '$'; strncpy(tmp3 + 1, tmp, 63); plog_info("\tAdding variable: %s = %s\n", tmp3, tmp2 + 1); ret = add_var(tmp3, tmp2 + 1, 1); if (ret == -2) { plog_err("\tFailed to add variable, too many variables defines\n"); return -1; } else if(ret == -3) { plog_err("\tFailed to add variable, already defined\n"); return -1; } break; } /* fall-through */ default: plog_err("\tUnknown option\n"); return -1; } } /* reset getopt lib for DPDK */ optind = 0; return 0; } static int check_cfg(void) { /* Sanity check */ #define RETURN_IF(cond, err) \ if (cond) { \ plog_err(err); \ return -1; \ }; RETURN_IF(rte_cfg.force_nchannel == 0, "\tError: number of memory channels not specified in [eal options] section\n"); RETURN_IF(prox_cfg.master >= RTE_MAX_LCORE, "\tError: No master core specified (one core needs to have mode=master)\n"); #undef RETURN_IF return 0; } static int calc_tot_rxrings(void) { struct lcore_cfg *slconf, *dlconf; struct task_args *starg, *dtarg; uint32_t dlcore_id; uint8_t dtask_id; struct core_task ct; dlconf = NULL; while (core_targ_next_early(&dlconf, &dtarg, 1) == 0) { dtarg->tot_rxrings = 0; } slconf = NULL; while (core_targ_next_early(&slconf, &starg, 1) == 0) { for (uint8_t idx = 0; idx < MAX_PROTOCOLS; ++idx) { for (uint8_t ring_idx = 0; ring_idx < starg->core_task_set[idx].n_elems; ++ring_idx) { ct = starg->core_task_set[idx].core_task[ring_idx]; if (!prox_core_active(ct.core, 0)) { set_errf("Core %u is disabled but Core %u task %u is sending to it\n", ct.core, slconf->id, starg->id); return -1; } dlconf = &lcore_cfg_init[ct.core]; if (ct.task >= dlconf->n_tasks_all) { set_errf("Core %u task %u not enabled\n", ct.core, ct.task); return -1; } dtarg = &dlconf->targs[ct.task]; /* Control rings are not relevant at this point. */ if (ct.type) continue; if (!(dtarg->flags & TASK_ARG_RX_RING)) { set_errf("Core %u task %u is not expecting to receive through a ring\n", ct.core, ct.task); return -1; } dtarg->tot_rxrings++; if (dtarg->tot_rxrings > MAX_RINGS_PER_TASK) { set_errf("Core %u task %u is receiving from too many tasks", ct.core, ct.task); return -1; } } } } return 0; } static void prox_set_core_mask(void) { struct lcore_cfg *lconf; prox_core_clr(); for (uint8_t lcore_id = 0; lcore_id < RTE_MAX_LCORE; ++lcore_id) { lconf = &lcore_cfg_init[lcore_id]; if (lconf->n_tasks_all > 0 && lconf->targs[0].mode != MASTER) { prox_core_set_active(lcore_id); } } } static int is_using_no_drop(void) { uint32_t lcore_id; struct lcore_cfg *lconf; struct task_args *targs; lcore_id = -1; while(prox_core_next(&lcore_id, 1) == 0) { lconf = &lcore_cfg_init[lcore_id]; for (uint8_t task_id = 0; task_id < lconf->n_tasks_all; ++task_id) { targs = &lconf->targs[task_id]; if (!(targs->flags & TASK_ARG_DROP)) return 1; } } return 0; } int prox_read_config_file(void) { set_global_defaults(&prox_cfg); set_task_defaults(&prox_cfg, lcore_cfg_init); set_port_defaults(); plog_info("=== Parsing configuration file '%s' ===\n", cfg_file); struct cfg_file *pcfg = cfg_open(cfg_file); if (pcfg == NULL) { return -1; } struct cfg_section* config_sections[] = { &lua_cfg , &var_cfg , &eal_default_cfg , &cache_set_cfg , &port_cfg , &defaults_cfg , &settings_cfg , &core_cfg , NULL }; for (struct cfg_section** section = config_sections; *section != NULL; ++section) { const char* name = (*section)->name; size_t len = strlen(name); plog_info("\t*** Reading [%s] section%s ***\n", name, name[len - 1] == '#'? "s": ""); cfg_parse(pcfg, *section); if ((*section)->error) { plog_err("At line %u, section [%s], entry %u: '%s'\n\t%s\n" , pcfg->err_line, pcfg->err_section, pcfg->err_entry + 1, pcfg->cur_line, strlen(get_parse_err())? get_parse_err() : err_str); cfg_close(pcfg); /* cannot close before printing error, print uses internal buffer */ return -1; } } cfg_close(pcfg); prox_set_core_mask(); if (is_using_no_drop()) { prox_cfg.flags &= ~DSF_WAIT_ON_QUIT; } if (calc_tot_rxrings()) { plog_err("Error in configuration: %s\n", err_str); return -1; } return check_cfg(); } static void failed_rte_eal_init(__attribute__((unused))const char *prog_name) { plog_err("\tError in rte_eal_init()\n"); } int prox_setup_rte(const char *prog_name) { char *rte_argv[MAX_RTE_ARGV]; char rte_arg[MAX_RTE_ARGV][MAX_ARG_LEN]; char tmp[PROX_CM_STR_LEN]; /* create mask of used cores */ plog_info("=== Setting up RTE EAL ===\n"); if (prox_cfg.flags & DSF_USE_DUMMY_CPU_TOPO) { plog_info("Using dummy cpu topology\n"); snprintf(tmp, sizeof(tmp), "0x1"); } else { prox_core_to_hex(tmp, sizeof(tmp), 0); plog_info("\tWorker threads core mask is %s\n", tmp); prox_core_to_hex(tmp, sizeof(tmp), 1); plog_info("\tWith master core index %u, full core mask is %s\n", prox_cfg.master, tmp); } /* fake command line parameters for rte_eal_init() */ int argc = 0; rte_argv[argc] = strdup(prog_name); sprintf(rte_arg[++argc], "-c%s", tmp); rte_argv[argc] = rte_arg[argc]; #if RTE_VERSION >= RTE_VERSION_NUM(1,8,0,0) if (prox_cfg.flags & DSF_USE_DUMMY_CPU_TOPO) sprintf(rte_arg[++argc], "--master-lcore=%u", 0); else sprintf(rte_arg[++argc], "--master-lcore=%u", prox_cfg.master); rte_argv[argc] = rte_arg[argc]; #else /* For old DPDK versions, the master core had to be the first core. */ uint32_t first_core = -1; if (prox_core_next(&first_core, 1) == -1) { plog_err("Can't core ID of first core in use\n"); return -1; } if (first_core != prox_cfg.master) { plog_err("The master core needs to be the first core (master core = %u, first core = %u).\n", first_core, prox_cfg.master); return -1; } #endif if (rte_cfg.memory) { sprintf(rte_arg[++argc], "-m%u", rte_cfg.memory); rte_argv[argc] = rte_arg[argc]; } if (rte_cfg.force_nchannel) { sprintf(rte_arg[++argc], "-n%u", rte_cfg.force_nchannel); rte_argv[argc] = rte_arg[argc]; } if (rte_cfg.force_nrank) { sprintf(rte_arg[++argc], "-r%u", rte_cfg.force_nrank); rte_argv[argc] = rte_arg[argc]; } if (rte_cfg.no_hugetlbfs) { strcpy(rte_arg[++argc], "--no-huge"); rte_argv[argc] = rte_arg[argc]; } if (rte_cfg.no_pci) { strcpy(rte_arg[++argc], "--no-pci"); rte_argv[argc] = rte_arg[argc]; } if (rte_cfg.no_hpet) { strcpy(rte_arg[++argc], "--no-hpet"); rte_argv[argc] = rte_arg[argc]; } if (rte_cfg.no_shconf) { strcpy(rte_arg[++argc], "--no-shconf"); rte_argv[argc] = rte_arg[argc]; } if (rte_cfg.eal != NULL) { char *ptr = rte_cfg.eal; char *ptr2; while (ptr != NULL) { while (isspace(*ptr)) ptr++; ptr2 = ptr; ptr = strchr(ptr, ' '); if (ptr) { *ptr++ = '\0'; } strcpy(rte_arg[++argc], ptr2); rte_argv[argc] = rte_arg[argc]; } } if (rte_cfg.hugedir != NULL) { strcpy(rte_arg[++argc], "--huge-dir"); rte_argv[argc] = rte_arg[argc]; rte_argv[++argc] = rte_cfg.hugedir; } if (rte_cfg.no_output) { rte_log_set_global_level(0); } /* init EAL */ plog_info("\tEAL command line:"); if (argc >= MAX_RTE_ARGV) { plog_err("too many arguments for EAL\n"); return -1; } for (int h = 0; h <= argc; ++h) { plog_info(" %s", rte_argv[h]); } plog_info("\n"); rte_set_application_usage_hook(failed_rte_eal_init); if (rte_eal_init(++argc, rte_argv) < 0) { plog_err("\tError in rte_eal_init()\n"); return -1; } plog_info("\tEAL Initialized\n"); if (prox_cfg.flags & DSF_USE_DUMMY_CPU_TOPO) return 0; /* check if all active cores are in enabled in DPDK */ for (uint32_t lcore_id = 0; lcore_id < RTE_MAX_LCORE; ++lcore_id) { if (lcore_id == prox_cfg.master) { if (!rte_lcore_is_enabled(lcore_id)) return -1; } else if (rte_lcore_is_enabled(lcore_id) != prox_core_active(lcore_id, 0)) { plog_err("\tFailed to enable lcore %u\n", lcore_id); return -1; } else if (lcore_cfg_init[lcore_id].n_tasks_all != 0 && !rte_lcore_is_enabled(lcore_id)) { plog_err("\tFailed to enable lcore %u\n", lcore_id); return -1; } } return 0; }