Hi, would you please review this RFC? it addresses the JIRA ticket odp-488. The due date will be the end of this month.
thanks, Forrest On 8 December 2016 at 09:59, Forrest Shi <forrest....@linaro.org> wrote: > ping > > On 2 December 2016 at 16:29, <forrest....@linaro.org> wrote: > >> From: Xuelin Shi <forrest....@linaro.org> >> >> This patch made the following changes: >> - try to create one tm background thread for each pktio >> - receiving packets from multiple pktios other than generating packets >> - identifying TM user by ip with individual class of service >> - print the user service while starting the program >> - pirnt the packets counts every 10 seconds for each user >> >> Currently only allow to create multiple tm threads for system with >> more than 24 cores. This patch also reduce the constraint to 4 cores. >> >> Signed-off-by: Xuelin Shi <forrest....@linaro.org> >> --- >> example/traffic_mgmt/Makefile.am | 7 +- >> example/traffic_mgmt/odp_traffic_mgmt.c | 559 ++++++++++++--------- >> example/traffic_mgmt/odp_traffic_mgmt.h | 48 ++ >> example/traffic_mgmt/odp_traffic_pktio.c | 794 >> ++++++++++++++++++++++++++++++ >> platform/linux-generic/odp_traffic_mngr.c | 2 +- >> 5 files changed, 1162 insertions(+), 248 deletions(-) >> create mode 100644 example/traffic_mgmt/odp_traffic_mgmt.h >> create mode 100644 example/traffic_mgmt/odp_traffic_pktio.c >> >> diff --git a/example/traffic_mgmt/Makefile.am >> b/example/traffic_mgmt/Makefile.am >> index c8ff797..d2c7929 100644 >> --- a/example/traffic_mgmt/Makefile.am >> +++ b/example/traffic_mgmt/Makefile.am >> @@ -2,8 +2,9 @@ include $(top_srcdir)/example/Makefile.inc >> >> bin_PROGRAMS = odp_traffic_mgmt$(EXEEXT) >> odp_traffic_mgmt_LDFLAGS = $(AM_LDFLAGS) -static >> -odp_traffic_mgmt_CFLAGS = $(AM_CFLAGS) -I${top_srcdir}/example >> +odp_traffic_mgmt_CFLAGS = $(AM_CFLAGS) -I${top_srcdir}/example >> -I${top_srcdir}/test >> >> -noinst_HEADERS = $(top_srcdir)/example/example_debug.h >> +noinst_HEADERS = $(top_srcdir)/example/example_debug.h \ >> + $(top_srcdir)/example/traffic_mgmt/odp_traffic_mgmt.h >> >> -dist_odp_traffic_mgmt_SOURCES = odp_traffic_mgmt.c >> +dist_odp_traffic_mgmt_SOURCES = odp_traffic_mgmt.c odp_traffic_pktio.c >> diff --git a/example/traffic_mgmt/odp_traffic_mgmt.c >> b/example/traffic_mgmt/odp_traffic_mgmt.c >> index c4f5356..3879f8e 100644 >> --- a/example/traffic_mgmt/odp_traffic_mgmt.c >> +++ b/example/traffic_mgmt/odp_traffic_mgmt.c >> @@ -9,22 +9,31 @@ >> #define _GNU_SOURCE >> >> #include <unistd.h> >> +#include <stdio.h> >> #include <signal.h> >> +#include <inttypes.h> >> #include <sys/resource.h> >> #include <execinfo.h> >> #include <odp_api.h> >> #include <example_debug.h> >> +#include <odp/helper/ip.h> >> >> +#include "odp_traffic_mgmt.h" >> + >> +#define TM_USER_IP_START 0x01010101 >> #define NUM_SVC_CLASSES 4 >> #define USERS_PER_SVC_CLASS 2 >> -#define APPS_PER_USER 2 >> -#define TM_QUEUES_PER_APP 2 >> +#define APPS_PER_USER 1 >> +#define TM_QUEUES_PER_APP 1 >> #define NUM_USERS (USERS_PER_SVC_CLASS * NUM_SVC_CLASSES) >> #define NUM_TM_QUEUES (NUM_USERS * APPS_PER_USER * >> TM_QUEUES_PER_APP) >> #define TM_QUEUES_PER_USER (TM_QUEUES_PER_APP * APPS_PER_USER) >> #define TM_QUEUES_PER_CLASS (USERS_PER_SVC_CLASS * TM_QUEUES_PER_USER) >> #define MAX_NODES_PER_LEVEL (NUM_USERS * APPS_PER_USER) >> >> +#define MAX_NB_PKTIO 32 >> +#define MAX_NB_USERS (NUM_USERS * APPS_PER_USER * MAX_NB_PKTIO) >> + >> #define KBPS 1000 >> #define MBPS 1000000 >> #define PERCENT(percent) (100 * percent) >> @@ -49,11 +58,6 @@ typedef struct { >> odp_tm_wred_t wred_profiles[ODP_NUM_PACKET_COLORS]; >> } profile_set_t; >> >> -static const odp_init_t ODP_INIT_PARAMS = { >> - .log_fn = odp_override_log, >> - .abort_fn = odp_override_abort >> -}; >> - >> static profile_params_set_t COMPANY_PROFILE_PARAMS = { >> .shaper_params = { >> .commit_bps = 50 * MBPS, .commit_burst = 1000000, >> @@ -161,8 +165,8 @@ static profile_params_set_t COS2_PROFILE_PARAMS = { >> }, >> >> .threshold_params = { >> - .max_pkts = 1000, .enable_max_pkts = TRUE, >> - .max_bytes = 100000, .enable_max_bytes = TRUE >> + .max_pkts = 1000, .enable_max_pkts = FALSE, >> + .max_bytes = 100000, .enable_max_bytes = FALSE >> }, >> >> .wred_params = { >> @@ -194,8 +198,8 @@ static profile_params_set_t COS3_PROFILE_PARAMS = { >> }, >> >> .threshold_params = { >> - .max_pkts = 400, .enable_max_pkts = TRUE, >> - .max_bytes = 60000, .enable_max_bytes = TRUE >> + .max_pkts = 400, .enable_max_pkts = FALSE, >> + .max_bytes = 60000, .enable_max_bytes = FALSE >> }, >> >> .wred_params = { >> @@ -219,26 +223,59 @@ static profile_params_set_t COS3_PROFILE_PARAMS = { >> } >> }; >> >> -static profile_set_t COMPANY_PROFILE_SET; >> static profile_set_t COS_PROFILE_SETS[NUM_SVC_CLASSES]; >> static profile_set_t USER_PROFILE_SETS[NUM_SVC_CLASSES]; >> static profile_set_t APP_PROFILE_SETS[NUM_SVC_CLASSES][APPS_PER_USER]; >> >> -static odp_tm_t odp_tm_test; >> - >> -static odp_pool_t odp_pool; >> - >> -static odp_tm_queue_t queue_num_tbls[NUM_SVC_CLASSES][TM_QUEUES_PER_CLASS >> + 1]; >> -static uint32_t next_queue_nums[NUM_SVC_CLASSES]; >> - >> static uint8_t random_buf[RANDOM_BUF_LEN]; >> static uint32_t next_rand_byte; >> +odp_atomic_u64_t atomic_pkts_into_tm; >> +odp_atomic_u64_t atomic_pkts_from_tm; >> >> -static odp_atomic_u32_t atomic_pkts_into_tm; >> -static odp_atomic_u32_t atomic_pkts_from_tm; >> - >> -static uint32_t g_num_pkts_to_send = 1000; >> -static uint8_t g_print_tm_stats = TRUE; >> +typedef struct { >> + struct { >> + odp_atomic_u64_t tm_pkts_in; >> + odp_atomic_u64_t tm_bytes_in; >> + odp_atomic_u64_t tm_pkts_out; >> + odp_atomic_u64_t tm_bytes_out; >> + } s; /* statistics info for each user of tm */ >> + uint64_t max_pkts_limit; /* max packets allowed for this user */ >> + uint64_t max_bytes_limit; /* max bytes allowed for this user */ >> + odp_bool_t enable_pkts_limit; /* max packets limit is valid */ >> + odp_bool_t enable_bytes_limit; /* max bytes limit is valid */ >> + uint32_t ipaddr; /* user ipaddr */ >> + int svc; /* service class */ >> + struct tm_cfg_port_s *tmp; /* coming from which port */ >> +} tm_user_t; >> + >> +/* tm config and profile for each interface */ >> +typedef struct tm_cfg_port_s { >> + int port_id; >> + profile_set_t port_profile; >> + odp_tm_t odp_tm; >> + uint32_t next_queue_nums[NUM_SVC_CLASSES]; >> + odp_tm_queue_t queue_num_tbls[NUM_SVC_CLASSES][TM_QUEUES_PER_CLASS >> + 1]; >> +} tm_port_config_t; >> + >> +/* statistics value info, used for param pass */ >> +typedef struct { >> + uint64_t pkts_in; >> + uint64_t pkts_out; >> + uint64_t bytes_in; >> + uint64_t bytes_out; >> +} stat_info_t; >> + >> +static tm_user_t tm_users[MAX_NB_USERS]; >> +static int nb_tm_users; >> +static tm_port_config_t tm_port_profiles[MAX_NB_PKTIO]; >> +static int nb_tm_odp; >> + >> +static profile_params_set_t *cos_profile_params[NUM_SVC_CLASSES] = { >> + &COS0_PROFILE_PARAMS, >> + &COS1_PROFILE_PARAMS, >> + &COS2_PROFILE_PARAMS, >> + &COS3_PROFILE_PARAMS >> +}; >> >> static void tester_egress_fcn(odp_packet_t odp_pkt); >> >> @@ -308,23 +345,17 @@ static uint32_t create_profile_set(profile_params_set_t >> *profile_params_set, >> return err_cnt; >> } >> >> -/* Returns the number of errors encountered. */ >> - >> static uint32_t init_profile_sets(void) >> { >> uint32_t class_shaper_scale, class_threshold_scale, >> user_shaper_scale; >> uint32_t user_threshold_scale, err_cnt, app_idx; >> >> - class_shaper_scale = TM_QUEUES_PER_CLASS / 2; >> - class_threshold_scale = TM_QUEUES_PER_CLASS; >> - user_shaper_scale = TM_QUEUES_PER_USER / 2; >> - user_threshold_scale = TM_QUEUES_PER_USER; >> + class_shaper_scale = 1; >> + class_threshold_scale = 1; >> + user_shaper_scale = 1; >> + user_threshold_scale = 1; >> err_cnt = 0; >> >> - err_cnt += create_profile_set(&COMPANY_PROFILE_PARAMS, >> - &COMPANY_PROFILE_SET, >> - "CompanyProfiles", 0, 1, 1); >> - >> err_cnt += create_profile_set(&COS0_PROFILE_PARAMS, >> &COS_PROFILE_SETS[0], >> "ServiceClass0", 0, >> class_shaper_scale, >> @@ -373,19 +404,22 @@ static uint32_t init_profile_sets(void) >> return err_cnt; >> } >> >> -static int config_example_user(odp_tm_node_t cos_tm_node, >> +static int config_example_user(tm_port_config_t *tmp, >> + odp_tm_node_t cos_tm_node, >> uint8_t svc_class, >> uint32_t user_num) >> { >> + odp_tm_t odp_tm_test; >> odp_tm_queue_params_t tm_queue_params; >> odp_tm_node_params_t tm_node_params; >> odp_tm_queue_t tm_queue; >> odp_tm_node_t user_tm_node; >> profile_set_t *profile_set; >> - uint32_t app_idx, queue_idx, svc_class_queue_num; >> + uint32_t app_idx, queue_idx, qnum; >> char user_name[64]; >> - int rc; >> + int rc, numq = 0; >> >> + odp_tm_test = tmp->odp_tm; >> profile_set = &USER_PROFILE_SETS[svc_class]; >> >> odp_tm_node_params_init(&tm_node_params); >> @@ -400,7 +434,8 @@ static int config_example_user(odp_tm_node_t >> cos_tm_node, >> profile_set->wred_profiles[2]; >> tm_node_params.level = 2; >> >> - snprintf(user_name, sizeof(user_name), "Subscriber-%u", user_num); >> + snprintf(user_name, sizeof(user_name), "port%d-sub%u", >> + tmp->port_id, user_num); >> user_tm_node = odp_tm_node_create(odp_tm_test, user_name, >> &tm_node_params); >> odp_tm_node_connect(user_tm_node, cos_tm_node); >> @@ -429,24 +464,27 @@ static int config_example_user(odp_tm_node_t >> cos_tm_node, >> if (rc < 0) >> return rc; >> >> - svc_class_queue_num = >> next_queue_nums[svc_class]++; >> - queue_num_tbls[svc_class][svc_class_queue_num + >> 1] = >> - tm_queue; >> + numq++; >> + qnum = tmp->next_queue_nums[svc_class]; >> + tmp->next_queue_nums[svc_class]++; >> + tmp->queue_num_tbls[svc_class][qnum + 1] = >> tm_queue; >> } >> } >> >> return 0; >> } >> >> -static int config_company_node(const char *company_name) >> +static int config_company_node(const char *company_name, >> tm_port_config_t *tmp) >> { >> + profile_params_set_t *param; >> odp_tm_node_params_t tm_node_params; >> profile_set_t *profile_set; >> odp_tm_node_t company_tm_node, cos_tm_node; >> + odp_tm_t odp_tm_test; >> uint32_t cos_idx, user_idx; >> char cos_node_name[64]; >> >> - profile_set = &COMPANY_PROFILE_SET; >> + profile_set = &tmp->port_profile; >> odp_tm_node_params_init(&tm_node_params); >> tm_node_params.max_fanin = 64; >> tm_node_params.shaper_profile = profile_set->shaper_profile; >> @@ -459,6 +497,7 @@ static int config_company_node(const char >> *company_name) >> profile_set->wred_profiles[2]; >> tm_node_params.level = 0; >> >> + odp_tm_test = tmp->odp_tm; >> company_tm_node = odp_tm_node_create(odp_tm_test, company_name, >> &tm_node_params); >> >> @@ -484,21 +523,50 @@ static int config_company_node(const char >> *company_name) >> &tm_node_params); >> odp_tm_node_connect(cos_tm_node, company_tm_node); >> >> - for (user_idx = 0; user_idx < USERS_PER_SVC_CLASS; >> user_idx++) >> - config_example_user(cos_tm_node, cos_idx, >> + param = cos_profile_params[cos_idx]; >> + for (user_idx = 0; user_idx < USERS_PER_SVC_CLASS; >> user_idx++) { >> + tm_user_t *u; >> + >> + config_example_user(tmp, cos_tm_node, cos_idx, >> cos_idx * 256 + user_idx); >> + u = &tm_users[nb_tm_users]; >> + u->svc = cos_idx; >> + u->ipaddr = TM_USER_IP_START + nb_tm_users; >> + >> + u->max_bytes_limit = >> param->threshold_params.max_bytes; >> + u->max_pkts_limit = param->threshold_params.max_pk >> ts; >> + u->tmp = tmp; >> + >> + if (param->threshold_params.enable_max_pkts) >> + u->enable_pkts_limit = TRUE; >> + >> + if (param->threshold_params.enable_max_bytes) >> + u->enable_bytes_limit = TRUE; >> + nb_tm_users++; >> + } >> } >> >> odp_tm_node_connect(company_tm_node, ODP_TM_ROOT); >> + >> return 0; >> } >> >> -static int create_and_config_tm(void) >> +static int create_and_config_tm(int nb_pktio) >> { >> odp_tm_level_requirements_t *per_level; >> odp_tm_requirements_t requirements; >> odp_tm_egress_t egress; >> uint32_t level, err_cnt; >> + int i; >> + char tm_name[64]; >> + >> + err_cnt = init_profile_sets(); >> + if (err_cnt != 0) { >> + printf("%s init_profile_sets encountered %u errors\n", >> + __func__, err_cnt); >> + >> + return 0; >> + } >> >> odp_tm_requirements_init(&requirements); >> odp_tm_egress_init(&egress); >> @@ -525,14 +593,27 @@ static int create_and_config_tm(void) >> egress.egress_kind = ODP_TM_EGRESS_FN; >> egress.egress_fcn = tester_egress_fcn; >> >> - odp_tm_test = odp_tm_create("TM test", &requirements, &egress); >> - err_cnt = init_profile_sets(); >> - if (err_cnt != 0) >> - printf("%s init_profile_sets encountered %u errors\n", >> - __func__, err_cnt); >> + for (i = 0; i < nb_pktio; i++) { >> + tm_port_config_t *tmp; >> >> - config_company_node("TestCompany"); >> - return err_cnt; >> + tmp = &tm_port_profiles[i]; >> + tmp->port_id = i; >> + snprintf(tm_name, 64, "TM-test-%d", i); >> + tmp->odp_tm = odp_tm_create(tm_name, &requirements, >> &egress); >> + if (tmp->odp_tm == ODP_TM_INVALID) >> + break; >> + >> + snprintf(tm_name, 64, "PortPorfiles-%d", i); >> + if (create_profile_set(&COMPANY_PROFILE_PARAMS, >> + &tmp->port_profile, tm_name, 0, 1, >> 1)) >> + break; >> + >> + snprintf(tm_name, 64, "TestPort-%d", i); >> + config_company_node(tm_name, tmp); >> + } >> + nb_tm_odp = i; >> + >> + return i; >> } >> >> static uint32_t random_8(void) >> @@ -545,6 +626,7 @@ static uint32_t random_8(void) >> } >> >> rand8 = random_buf[next_rand_byte++]; >> + >> return rand8; >> } >> >> @@ -562,232 +644,221 @@ static uint32_t random_16(void) >> return (((uint16_t)byte1) << 8) | ((uint16_t)byte2); >> } >> >> -static uint32_t pkt_service_class(void) >> +static inline uint32_t pkt_to_user(odp_packet_t pkt) >> { >> - uint32_t rand8; >> + odph_ipv4hdr_t *ip; >> + uint32_t idx; >> >> - /* Make most of the traffic use service class 3 to increase the >> amount >> - * of delayed traffic so as to stimulate more interesting >> behaviors. >> - */ >> - rand8 = random_8(); >> - switch (rand8) { >> - case 0 ... 24: return 0; >> - case 25 ... 49: return 1; >> - case 50 ... 150: return 2; >> - case 151 ... 255: return 3; >> - default: return 3; >> - } >> + ip = odp_packet_l3_ptr(pkt, NULL); >> + idx = odp_be_to_cpu_32(ip->src_addr) - TM_USER_IP_START; >> + >> + return idx & (nb_tm_users - 1); >> } >> >> -static odp_packet_t make_odp_packet(uint16_t pkt_len) >> +/** >> + * find the packet from which user and eligible for which service. >> + * this should be a lookup table implementation. Here for simplicity, >> + * only check last byte of ip src addr to classify the users. >> + */ >> +static inline uint32_t pkt_service_class(odp_packet_t pkt) >> { >> - odp_packet_t odp_pkt; >> - uint8_t rand8a, rand8b, pkt_color, drop_eligible; >> + uint32_t idx; >> + tm_user_t *u; >> + >> + idx = pkt_to_user(pkt); >> + u = &tm_users[idx]; >> + >> + return u->svc; >> +} >> + >> +void tester_egress_fcn(odp_packet_t odp_pkt) >> +{ >> + tm_user_t *u; >> + uint32_t idx = pkt_to_user(odp_pkt); >> + >> + odp_atomic_inc_u64(&atomic_pkts_from_tm); >> + u = &tm_users[idx]; >> + odp_atomic_inc_u64(&u->s.tm_pkts_out); >> + odp_atomic_add_u64(&u->s.tm_bytes_out, odp_packet_len(odp_pkt)); >> + >> + /* not forwarding, need to free, otherwise packet pool will be >> full */ >> + odp_packet_free(odp_pkt); >> +} >> + >> +int tm_send_packet(odp_packet_t pkt) >> +{ >> + uint8_t rand8a, rand8b, pkt_color, drop_eligible; >> + uint32_t svc_class, queue_num, idx; >> + uint64_t bytes_in; >> + int rc; >> + tm_user_t *u; >> + odp_tm_queue_t tm_queue; >> + >> + idx = pkt_to_user(pkt); >> + u = &tm_users[idx]; >> + if (u->enable_pkts_limit && >> + u->max_pkts_limit <= odp_atomic_load_u64(&u->s.tm_pkts_in)) >> + return -1; >> + >> + bytes_in = odp_atomic_load_u64(&u->s.tm_bytes_in); >> + if (u->enable_bytes_limit && >> + u->max_bytes_limit <= bytes_in + odp_packet_len(pkt)) >> + return -1; >> >> rand8a = random_8(); >> rand8b = random_8(); >> pkt_color = (rand8a < 224) ? 0 : ((rand8a < 248) ? 1 : 2); >> drop_eligible = (rand8b < 240) ? 1 : 0; >> - odp_pkt = odp_packet_alloc(odp_pool, pkt_len); >> - if (odp_pkt == ODP_PACKET_INVALID) { >> - printf("%s odp_packet_alloc failure *******\n", __func__); >> - return 0; >> + odp_packet_color_set(pkt, pkt_color); >> + odp_packet_drop_eligible_set(pkt, drop_eligible); >> + odp_packet_shaper_len_adjust_set(pkt, 24); >> + >> + svc_class = pkt_service_class(pkt); >> + queue_num = random_16() & (TM_QUEUES_PER_CLASS - 1); >> + tm_queue = u->tmp->queue_num_tbls[svc_class][queue_num + 1]; >> + rc = odp_tm_enq(tm_queue, pkt); >> + if (rc > 0) { >> + odp_atomic_inc_u64(&atomic_pkts_into_tm); >> + odp_atomic_inc_u64(&u->s.tm_pkts_in); >> + odp_atomic_add_u64(&u->s.tm_bytes_in, >> odp_packet_len(pkt)); >> } >> >> - odp_packet_color_set(odp_pkt, pkt_color); >> - odp_packet_drop_eligible_set(odp_pkt, drop_eligible); >> - odp_packet_shaper_len_adjust_set(odp_pkt, 24); >> - return odp_pkt; >> -} >> - >> -void tester_egress_fcn(odp_packet_t odp_pkt ODP_UNUSED) >> -{ >> - odp_atomic_inc_u32(&atomic_pkts_from_tm); >> + return rc; >> } >> >> -static int traffic_generator(uint32_t pkts_to_send) >> +int tm_config_and_init(int nb_pktio) >> { >> - odp_pool_param_t pool_params; >> - odp_tm_queue_t tm_queue; >> - odp_packet_t pkt; >> - odp_bool_t tm_is_idle; >> - uint32_t svc_class, queue_num, pkt_len, pkts_into_tm; >> - uint32_t pkts_from_tm, pkt_cnt, millisecs, >> odp_tm_enq_errs; >> - int rc; >> - >> - memset(&pool_params, 0, sizeof(odp_pool_param_t)); >> - pool_params.type = ODP_POOL_PACKET; >> - pool_params.pkt.num = pkts_to_send + 10; >> - pool_params.pkt.len = 1600; >> - pool_params.pkt.seg_len = 0; >> - pool_params.pkt.uarea_size = 0; >> - >> - odp_pool = odp_pool_create("MyPktPool", &pool_params); >> - odp_tm_enq_errs = 0; >> - >> - pkt_cnt = 0; >> - while (pkt_cnt < pkts_to_send) { >> - svc_class = pkt_service_class(); >> - queue_num = random_16() & (TM_QUEUES_PER_CLASS - 1); >> - tm_queue = queue_num_tbls[svc_class][queue_num + 1]; >> - pkt_len = ((uint32_t)((random_8() & 0x7F) + 2)) * 32; >> - pkt_len = MIN(pkt_len, 1500); >> - pkt = make_odp_packet(pkt_len); >> - >> - pkt_cnt++; >> - rc = odp_tm_enq(tm_queue, pkt); >> - if (rc < 0) { >> - odp_tm_enq_errs++; >> - continue; >> - } >> + int i, rc; >> + tm_user_t *u; >> >> - odp_atomic_inc_u32(&atomic_pkts_into_tm); >> - } >> + if (NUM_USERS < NUM_SVC_CLASSES * USERS_PER_SVC_CLASS || >> + MAX_NB_PKTIO < nb_pktio) >> + return 0; >> >> - printf("%s odp_tm_enq_errs=%u\n", __func__, odp_tm_enq_errs); >> + odp_atomic_init_u64(&atomic_pkts_into_tm, 0); >> + odp_atomic_init_u64(&atomic_pkts_from_tm, 0); >> >> - /* Wait until the main traffic mgmt worker thread is idle and has >> no >> - * outstanding events (i.e. no timers, empty work queue, etc), but >> - * not longer than 60 seconds. >> - */ >> - for (millisecs = 0; millisecs < 600000; millisecs++) { >> - usleep(100); >> - tm_is_idle = odp_tm_is_idle(odp_tm_test); >> - if (tm_is_idle) >> - break; >> + for (i = 0; i < MAX_NB_USERS; i++) { >> + u = &tm_users[i]; >> + odp_atomic_init_u64(&u->s.tm_pkts_in, 0); >> + odp_atomic_init_u64(&u->s.tm_pkts_out, 0); >> + odp_atomic_init_u64(&u->s.tm_bytes_in, 0); >> + odp_atomic_init_u64(&u->s.tm_bytes_out, 0); >> } >> >> - if (!tm_is_idle) >> - printf("%s WARNING stopped waiting for the TM system " >> - "to be IDLE!\n", __func__); >> - >> - /* Wait for up to 2 seconds for pkts_from_tm to match >> pkts_into_tm. */ >> - for (millisecs = 0; millisecs < 2000; millisecs++) { >> - usleep(1000); >> - pkts_into_tm = odp_atomic_load_u32(&atomic_pkts_into_tm); >> - pkts_from_tm = odp_atomic_load_u32(&atomic_pkts_from_tm); >> - if (pkts_into_tm <= pkts_from_tm) >> - break; >> + rc = create_and_config_tm(nb_pktio); >> + if (!rc) { >> + odp_random_data(random_buf, RANDOM_BUF_LEN, 1); >> + next_rand_byte = 0; >> } >> >> - return 0; >> + return rc; >> } >> >> -static int process_cmd_line_options(uint32_t argc, char *argv[]) >> +static void tm_print_stat_impl(int interval, stat_info_t *prev) >> { >> - uint32_t arg_idx; >> - char *arg; >> - >> - arg_idx = 1; >> - while (arg_idx < argc) { >> - arg = argv[arg_idx++]; >> - if (!arg) { >> - return -1; >> - } else if (arg[0] == '-') { >> - switch (arg[1]) { >> - case 'n': >> - if (argc <= arg_idx) >> - return -1; >> - g_num_pkts_to_send = >> - atoi(argv[arg_idx++]); >> - break; >> - >> - case 'q': >> - g_print_tm_stats = FALSE; >> - break; >> - >> - default: >> - printf("Unrecognized cmd line option >> '%s'\n", >> - arg); >> - return -1; >> - } >> - } else { >> - /* Currently all cmd line options are '-' flag >> based. */ >> - return -1; >> - } >> + int i; >> + >> + printf("\nTM toal pkts_in=%" PRIu64 ", pkts_out=%" PRIu64 "\n", >> + odp_atomic_load_u64(&atomic_pkts_into_tm), >> + odp_atomic_load_u64(&atomic_pkts_from_tm)); >> + >> + printf("----------------------\n"); >> + for (i = 0; i < nb_tm_users; i++) { >> + uint64_t bps; >> + tm_user_t *u; >> + stat_info_t si, *prev_si; >> + >> + u = &tm_users[i]; >> + prev_si = &prev[i]; >> + si.pkts_in = odp_atomic_load_u64(&u->s.tm_pkts_in); >> + si.pkts_out = odp_atomic_load_u64(&u->s.tm_pkts_out); >> + si.bytes_in = odp_atomic_load_u64(&u->s.tm_bytes_in); >> + si.bytes_out = odp_atomic_load_u64(&u->s.tm_bytes_out); >> + bps = (si.bytes_out - prev_si->bytes_out) * 8 / interval; >> + *prev_si = si; >> + >> + printf("user %d: pkts_in=%" PRIu64 ", pkts_out=%" PRIu64 >> + ", bytes_in=%" PRIu64 ", bytes_out=%" PRIu64 >> + ", bps=%" PRIu64 "\n", i, si.pkts_in, si.pkts_out, >> + si.bytes_in, si.bytes_out, bps); >> } >> >> - return 0; >> + printf("\n"); >> } >> >> -static void signal_handler(int signal) >> +void tm_print_stat(int duration, int interval) >> { >> - size_t num_stack_frames; >> - const char *signal_name; >> - void *bt_array[128]; >> - >> - switch (signal) { >> - case SIGILL: >> - signal_name = "SIGILL"; break; >> - case SIGFPE: >> - signal_name = "SIGFPE"; break; >> - case SIGSEGV: >> - signal_name = "SIGSEGV"; break; >> - case SIGTERM: >> - signal_name = "SIGTERM"; break; >> - case SIGBUS: >> - signal_name = "SIGBUS"; break; >> - default: >> - signal_name = "UNKNOWN"; break; >> + int i; >> + int elapsed = 0; >> + int loop_forever = (duration == 0); >> + stat_info_t prev[MAX_NB_USERS]; >> + >> + for (i = 0; i < nb_tm_users; i++) { >> + tm_user_t *u; >> + stat_info_t *si; >> + >> + u = &tm_users[i]; >> + si = &prev[i]; >> + si->pkts_in = odp_atomic_load_u64(&u->s.tm_pkts_in); >> + si->pkts_out = odp_atomic_load_u64(&u->s.tm_pkts_out); >> + si->bytes_in = odp_atomic_load_u64(&u->s.tm_bytes_in); >> + si->bytes_out = odp_atomic_load_u64(&u->s.tm_bytes_out); >> } >> >> - num_stack_frames = backtrace(bt_array, 100); >> - printf("Received signal=%u (%s) exiting.", signal, signal_name); >> - backtrace_symbols_fd(bt_array, num_stack_frames, fileno(stderr)); >> - fflush(NULL); >> - sync(); >> - abort(); >> + do { >> + sleep(interval); >> + tm_print_stat_impl(interval, &prev[0]); >> + elapsed += interval; >> + } while (loop_forever || (elapsed < duration)); >> } >> >> -int main(int argc, char *argv[]) >> +void tm_print_user_cos(void) >> { >> - struct sigaction signal_action; >> - struct rlimit rlimit; >> - uint32_t pkts_into_tm, pkts_from_tm; >> - odp_instance_t instance; >> - int rc; >> - >> - memset(&signal_action, 0, sizeof(signal_action)); >> - signal_action.sa_handler = signal_handler; >> - sigfillset(&signal_action.sa_mask); >> - sigaction(SIGILL, &signal_action, NULL); >> - sigaction(SIGFPE, &signal_action, NULL); >> - sigaction(SIGSEGV, &signal_action, NULL); >> - sigaction(SIGTERM, &signal_action, NULL); >> - sigaction(SIGBUS, &signal_action, NULL); >> - >> - getrlimit(RLIMIT_CORE, &rlimit); >> - rlimit.rlim_cur = rlimit.rlim_max; >> - setrlimit(RLIMIT_CORE, &rlimit); >> - >> - rc = odp_init_global(&instance, &ODP_INIT_PARAMS, NULL); >> - if (rc != 0) { >> - printf("Error: odp_init_global() failed, rc = %d\n", rc); >> - abort(); >> + int i; >> + char buf[80]; >> + >> + printf("\nClass Of Service\n" >> + "----------------------\n"); >> + >> + for (i = 0; i < NUM_SVC_CLASSES; i++) { >> + profile_params_set_t *p; >> + char *b = buf; >> + int n; >> + >> + p = cos_profile_params[i]; >> + snprintf(buf, 32, "COS%d:", i); >> + printf("%-12s%-16scommit bps=%" PRIu64 ", burst=%d\n", >> + buf, "shaper: ", p->shaper_params.commit_bps, >> + p->shaper_params.commit_burst); >> + printf("%-28speak bps=%" PRIu64 ", burst=%d\n", "", >> + p->shaper_params.peak_bps, >> + p->shaper_params.peak_burst); >> + >> + n = snprintf(buf, 80, "%-12s%-16s", "", "threshold: "); >> + b = buf + n; >> + if (p->threshold_params.enable_max_pkts) { >> + n = snprintf(b, 80, "max pkts=%" PRIu64, >> + p->threshold_params.max_pkts); >> + b += n; >> + } >> + if (p->threshold_params.enable_max_bytes) { >> + n = snprintf(b, 80, ", bytes=%" PRIu64, >> + p->threshold_params.max_bytes); >> + b += n; >> + } >> + printf("%s\n", buf); >> } >> - rc = odp_init_local(instance, ODP_THREAD_CONTROL); >> - if (rc != 0) { >> - printf("Error: odp_init_local() failed, rc = %d\n", rc); >> - abort(); >> + printf("\nTM Users\n" >> + "--%6s----%6s--------%3s--\n", "userid", "ipaddr", "cos"); >> + for (i = 0; i < nb_tm_users; i++) { >> + uint8_t *p; >> + tm_user_t *u; >> + >> + u = &tm_users[i]; >> + p = (uint8_t *)&u->ipaddr; >> + snprintf(buf, 16, "%d.%d.%d.%d", p[3], p[2], p[1], p[0]); >> + printf("%6d %10s %8d\n", i, buf, u->svc); >> } >> - >> - if (process_cmd_line_options(argc, argv) < 0) >> - return -1; >> - >> - create_and_config_tm(); >> - >> - odp_random_data(random_buf, RANDOM_BUF_LEN, 1); >> - next_rand_byte = 0; >> - >> - odp_atomic_init_u32(&atomic_pkts_into_tm, 0); >> - odp_atomic_init_u32(&atomic_pkts_from_tm, 0); >> - >> - traffic_generator(g_num_pkts_to_send); >> - >> - pkts_into_tm = odp_atomic_load_u32(&atomic_pkts_into_tm); >> - pkts_from_tm = odp_atomic_load_u32(&atomic_pkts_from_tm); >> - printf("pkts_into_tm=%u pkts_from_tm=%u\n", pkts_into_tm, >> pkts_from_tm); >> - >> - odp_tm_stats_print(odp_tm_test); >> - return 0; >> + printf("\n"); >> } >> diff --git a/example/traffic_mgmt/odp_traffic_mgmt.h >> b/example/traffic_mgmt/odp_traffic_mgmt.h >> new file mode 100644 >> index 0000000..4bbc137 >> --- /dev/null >> +++ b/example/traffic_mgmt/odp_traffic_mgmt.h >> @@ -0,0 +1,48 @@ >> +/* Copyright (c) 2016, Linaro Limited >> + * All rights reserved. >> + * >> + * SPDX-License-Identifier: BSD-3-Clause >> + */ >> + >> +#ifndef _ODP_TRAFFIC_MODULE_H_ >> +#define _ODP_TRAFFIC_MODULE_H_ >> + >> +#ifdef __cplusplus >> +extern "C" { >> +#endif >> + >> +/** >> + * Config and initialize the tm system. >> + * >> + * @param nb_pktio number of pktios for the tm >> + * @return 0 if success else -1 >> + */ >> +int tm_config_and_init(int nb_pktio); >> + >> +/** >> + * Print tm user stastics for each interval seconds >> + * >> + * @param duration how many seconds this function will run >> + * @param interval how many seconds for each print >> + */ >> +void tm_print_stat(int duration, int interval); >> + >> +/** >> + * Print tm service information for a user >> + */ >> +void tm_print_user_cos(void); >> + >> +/** >> + * Send packets to traffic management system >> + * >> + * @param pkt the packet will be sent >> + * >> + * @return 1 if success else <= 0 >> + */ >> +int tm_send_packet(odp_packet_t pkt); >> + >> +#ifdef __cplusplus >> +} >> +#endif >> + >> +#endif >> diff --git a/example/traffic_mgmt/odp_traffic_pktio.c >> b/example/traffic_mgmt/odp_traffic_pktio.c >> new file mode 100644 >> index 0000000..9f408fd >> --- /dev/null >> +++ b/example/traffic_mgmt/odp_traffic_pktio.c >> @@ -0,0 +1,794 @@ >> +/* Copyright (c) 2016, Linaro Limited >> + * All rights reserved. >> + * >> + * SPDX-License-Identifier: BSD-3-Clause >> + */ >> + >> +/** >> + * @file >> + * >> + * @example odp_traffic_mgmt.c >> + */ >> + >> +/** enable strtok */ >> +#ifndef _GNU_SOURCE >> +#define _GNU_SOURCE >> +#endif >> + >> +#include <stdio.h> >> +#include <stdlib.h> >> +#include <signal.h> >> +#include <getopt.h> >> +#include <unistd.h> >> +#include <errno.h> >> +#include <inttypes.h> >> +#include <sys/resource.h> >> +#include <execinfo.h> >> +#include <test_debug.h> >> + >> +#include <odp_api.h> >> +#include <odp/helper/linux.h> >> +#include <odp/helper/eth.h> >> +#include <odp/helper/ip.h> >> + >> +#include "odp_traffic_mgmt.h" >> + >> +/** @def MAX_WORKERS >> + * @brief Maximum number of worker threads >> + */ >> +#define MAX_WORKERS 32 >> + >> +/** @def SHM_PKT_POOL_SIZE >> + * @brief Size of the shared memory block >> + */ >> +#define SHM_PKT_POOL_SIZE 8192 >> + >> +/** @def SHM_PKT_POOL_BUF_SIZE >> + * @brief Buffer size of the packet pool buffer >> + */ >> +#define SHM_PKT_POOL_BUF_SIZE 1856 >> + >> +/** @def MAX_PKT_BURST >> + * @brief Maximum number of packet in a burst >> + */ >> +#define MAX_PKT_BURST 32 >> + >> +/** Maximum number of pktio queues per interface */ >> +#define MAX_QUEUES 32 >> + >> +/** Maximum number of pktio interfaces */ >> +#define MAX_PKTIOS 8 >> + >> +/** Default seconds to run */ >> +#define DEFAULT_RUN_SECONDS 60 >> + >> +/** Get rid of path in filename - only for unix-type paths using '/' */ >> +#define NO_PATH(file_name) (strrchr((file_name), '/') ? \ >> + strrchr((file_name), '/') + 1 : (file_name)) >> +/** >> + * Parsed command line application arguments >> + */ >> +typedef struct { >> + int cpu_count; >> + int duration; /**< Number of seconds to run */ >> + int if_count; /**< Number of interfaces to be used */ >> + int num_workers; /**< Number of worker threads */ >> + char **if_names; /**< Array of pointers to interface names >> */ >> + char *if_str; /**< Storage for interface names */ >> + int error_check; /**< Check packet errors */ >> +} appl_args_t; >> + >> +static int exit_threads; /**< Break workers loop if set to 1 */ >> + >> +static const odp_init_t ODP_INIT_PARAMS = { >> + .log_fn = odp_override_log, >> + .abort_fn = odp_override_abort >> +}; >> + >> +/** >> + * Statistics >> + */ >> +typedef union { >> + struct { >> + /** Number of packets received */ >> + odp_atomic_u64_t packets; >> + odp_atomic_u64_t bytes; >> + /** Packets dropped due to receive error */ >> + odp_atomic_u64_t rx_drops; >> + odp_atomic_u64_t rx_drop_bytes; >> + /** Packets dropped due to enqueue traffic management >> error */ >> + odp_atomic_u64_t tm_drops; >> + odp_atomic_u64_t tm_drop_bytes; >> + } s; >> + >> + uint8_t padding[ODP_CACHE_LINE_SIZE]; >> +} stats_t ODP_ALIGNED_CACHE; >> + >> +/** >> + * Thread specific arguments >> + */ >> +typedef struct thread_args_t { >> + uint64_t pkts; >> + >> + int thr_idx; >> + int num_pktio; >> + >> + struct { >> + odp_pktio_t rx_pktio; >> + odp_pktin_queue_t pktin; >> + odp_queue_t rx_queue; >> + int rx_idx; >> + int rx_queue_idx; >> + } pktio[MAX_PKTIOS]; >> +} thread_args_t; >> + >> +/** >> + * Grouping of all global data >> + */ >> +typedef struct { >> + /** Application (parsed) arguments */ >> + appl_args_t appl; >> + /** Thread specific arguments */ >> + thread_args_t thread[MAX_WORKERS]; >> + >> + /** Table of pktio handles */ >> + struct { >> + odp_pktio_t pktio; >> + odp_pktin_queue_t pktin[MAX_QUEUES]; >> + odp_queue_t rx_q[MAX_QUEUES]; >> + int num_rx_thr; >> + int num_rx_queue; >> + int next_rx_queue; >> + } pktios[MAX_PKTIOS]; >> +} args_t; >> + >> +/** Global pointer to args */ >> +static args_t *gbl_args; >> +/** Global barrier to synchronize main and workers */ >> +static odp_barrier_t barrier; >> + >> +/** >> + * Drop packets which input parsing marked as containing errors. >> + * >> + * Frees packets with error and modifies pkt_tbl[] to only contain >> packets with >> + * no detected errors. >> + * >> + * @param pkt_tbl Array of packets >> + * @param num Number of packets in pkt_tbl[] >> + * >> + * @return Number of packets dropped >> + */ >> +static inline int drop_err_pkts(odp_packet_t pkt_tbl[], unsigned num) >> +{ >> + odp_packet_t pkt; >> + unsigned dropped = 0; >> + unsigned i, j; >> + >> + for (i = 0, j = 0; i < num; ++i) { >> + pkt = pkt_tbl[i]; >> + >> + if (odp_unlikely(odp_packet_has_error(pkt))) { >> + odp_packet_free(pkt); /* Drop */ >> + dropped++; >> + } else if (odp_unlikely(i != j++)) { >> + pkt_tbl[j - 1] = pkt; >> + } >> + } >> + >> + return dropped; >> +} >> + >> +/** >> + * Packet IO worker thread accessing IO resources directly >> + * >> + * @param arg thread arguments of type 'thread_args_t *' >> + */ >> +static int run_worker_direct_mode(void *arg) >> +{ >> + int thr; >> + int pkts, i; >> + odp_packet_t pkt_tbl[MAX_PKT_BURST]; >> + int num_pktio; >> + odp_pktin_queue_t pktin; >> + int pktio = 0; >> + thread_args_t *thr_args = arg; >> + >> + thr = odp_thread_id(); >> + >> + num_pktio = thr_args->num_pktio; >> + pktin = thr_args->pktio[pktio].pktin; >> + >> + printf("[%02i] num pktios %i, PKTIN_DIRECT\n", thr, num_pktio); >> + >> + /* Loop packets */ >> + while (!exit_threads) { >> + if (num_pktio > 1) { >> + pktin = thr_args->pktio[pktio].pktin; >> + pktio++; >> + if (pktio == num_pktio) >> + pktio = 0; >> + } >> + >> + pkts = odp_pktin_recv(pktin, pkt_tbl, MAX_PKT_BURST); >> + if (odp_unlikely(pkts <= 0)) >> + continue; >> + >> + if (gbl_args->appl.error_check) { >> + int rx_drops; >> + >> + /* Drop packets with errors */ >> + rx_drops = drop_err_pkts(pkt_tbl, pkts); >> + >> + if (odp_unlikely(rx_drops)) { >> + if (pkts == rx_drops) >> + continue; >> + >> + pkts -= rx_drops; >> + } >> + } >> + >> + for (i = 0; i < pkts; i++) { >> + /* try to send packets to Traffic Management >> System */ >> + if (tm_send_packet(pkt_tbl[i]) <= 0) >> + odp_packet_free(pkt_tbl[i]); >> + } >> + } >> + >> + /* Make sure that latest stat writes are visible to other threads >> */ >> + odp_mb_full(); >> + >> + return 0; >> +} >> + >> +/** >> + * Create a pktio handle, optionally associating a default input queue. >> + * >> + * @param dev Name of device to open >> + * @param index Pktio index >> + * @param pool Pool to associate with device for packet RX/TX >> + * >> + * @retval 0 on success >> + * @retval -1 on failure >> + */ >> +static int create_pktio(const char *dev, int idx, int num_rx, odp_pool_t >> pool) >> +{ >> + odp_pktio_t pktio; >> + odp_pktio_param_t pktio_param; >> + odp_pktio_capability_t capa; >> + odp_pktin_queue_param_t pktin_param; >> + odp_pktio_op_mode_t mode_rx; >> + >> + odp_pktio_param_init(&pktio_param); >> + >> + pktio = odp_pktio_open(dev, pool, &pktio_param); >> + if (pktio == ODP_PKTIO_INVALID) { >> + LOG_ERR("Error: failed to open %s\n", dev); >> + return -1; >> + } >> + >> + printf("created pktio %" PRIu64 " (%s)\n", >> + odp_pktio_to_u64(pktio), dev); >> + >> + if (odp_pktio_capability(pktio, &capa)) { >> + LOG_ERR("Error: capability query failed %s\n", dev); >> + return -1; >> + } >> + >> + odp_pktin_queue_param_init(&pktin_param); >> + >> + mode_rx = ODP_PKTIO_OP_MT_UNSAFE; >> + >> + if (num_rx > (int)capa.max_input_queues) { >> + printf("Sharing %i input queues between %i workers\n", >> + capa.max_input_queues, num_rx); >> + num_rx = capa.max_input_queues; >> + mode_rx = ODP_PKTIO_OP_MT; >> + } >> + >> + pktin_param.hash_enable = 1; >> + pktin_param.hash_proto.proto.ipv4_udp = 1; >> + pktin_param.num_queues = num_rx; >> + pktin_param.op_mode = mode_rx; >> + >> + if (odp_pktin_queue_config(pktio, &pktin_param)) { >> + LOG_ERR("Error: input queue config failed %s\n", dev); >> + return -1; >> + } >> + >> + if (odp_pktin_queue(pktio, gbl_args->pktios[idx].pktin, >> + num_rx) != num_rx) { >> + LOG_ERR("Error: pktin queue query failed %s\n", dev); >> + return -1; >> + } >> + >> + printf("created %i input on (%s)\n", num_rx, dev); >> + >> + gbl_args->pktios[idx].num_rx_queue = num_rx; >> + gbl_args->pktios[idx].pktio = pktio; >> + >> + return 0; >> +} >> + >> +/* >> + * Bind worker threads to interfaces and calculate number of queues >> needed >> + * >> + * less workers (N) than interfaces (M) >> + * - assign each worker to process every Nth interface >> + * - workers process inequal number of interfaces, when M is not >> divisible by N >> + * - needs only single queue per interface >> + * otherwise >> + * - assign an interface to every Mth worker >> + * - interfaces are processed by inequal number of workers, when N is >> not >> + * divisible by M >> + * - tries to configure a queue per worker per interface >> + * - shares queues, if interface capability does not allows a queue per >> worker >> + */ >> +static void bind_workers(void) >> +{ >> + int if_count, num_workers; >> + int rx_idx, thr, pktio; >> + thread_args_t *thr_args; >> + >> + if_count = gbl_args->appl.if_count; >> + num_workers = gbl_args->appl.num_workers; >> + >> + if (if_count > num_workers) { >> + thr = 0; >> + >> + for (rx_idx = 0; rx_idx < if_count; rx_idx++) { >> + thr_args = &gbl_args->thread[thr]; >> + pktio = thr_args->num_pktio; >> + thr_args->pktio[pktio].rx_idx = rx_idx; >> + thr_args->num_pktio++; >> + >> + gbl_args->pktios[rx_idx].num_rx_thr++; >> + >> + thr++; >> + if (thr >= num_workers) >> + thr = 0; >> + } >> + } else { >> + rx_idx = 0; >> + >> + for (thr = 0; thr < num_workers; thr++) { >> + thr_args = &gbl_args->thread[thr]; >> + pktio = thr_args->num_pktio; >> + thr_args->pktio[pktio].rx_idx = rx_idx; >> + thr_args->num_pktio++; >> + >> + gbl_args->pktios[rx_idx].num_rx_thr++; >> + >> + rx_idx++; >> + if (rx_idx >= if_count) >> + rx_idx = 0; >> + } >> + } >> +} >> + >> +/* >> + * Bind queues to threads and fill in missing thread arguments (handles) >> + */ >> +static void bind_queues(void) >> +{ >> + int num_workers; >> + int thr, pktio; >> + >> + num_workers = gbl_args->appl.num_workers; >> + >> + for (thr = 0; thr < num_workers; thr++) { >> + int rx_idx; >> + thread_args_t *thr_args = &gbl_args->thread[thr]; >> + int num = thr_args->num_pktio; >> + >> + for (pktio = 0; pktio < num; pktio++) { >> + int rx_queue; >> + >> + rx_idx = thr_args->pktio[pktio].rx_idx; >> + rx_queue = gbl_args->pktios[rx_idx].next_ >> rx_queue; >> + >> + thr_args->pktio[pktio].rx_queue_idx = rx_queue; >> + thr_args->pktio[pktio].pktin = >> + gbl_args->pktios[rx_idx].pktin[rx_queue]; >> + thr_args->pktio[pktio].rx_queue = >> + gbl_args->pktios[rx_idx].rx_q[rx_queue]; >> + thr_args->pktio[pktio].rx_pktio = >> + gbl_args->pktios[rx_idx].pktio; >> + >> + rx_queue++; >> + >> + if (rx_queue >= gbl_args->pktios[rx_idx].num_r >> x_queue) >> + rx_queue = 0; >> + >> + gbl_args->pktios[rx_idx].next_rx_queue = >> rx_queue; >> + } >> + } >> +} >> + >> +/** >> + * Prinf usage information >> + */ >> +static void usage(char *progname) >> +{ >> + printf("\n" >> + "OpenDataPlane traffic management application.\n" >> + "\n" >> + "Usage: %s OPTIONS\n" >> + " E.g. %s -i eth0,eth1 -c 2\n" >> + " In the above example,\n" >> + " two threads will be used for receiving pkts from eth0 >> and eth1\n" >> + "\n" >> + "Mandatory OPTIONS:\n" >> + " -i, --interface Eth interfaces (comma-separated, no >> spaces)\n" >> + " Interface count min 1, max %i\n" >> + "\n" >> + "Optional OPTIONS:\n" >> + " -c, --count <number> CPU count.\n" >> + " -t, --time <number> seconds to run.\n" >> + " -e, --error_check 0: Don't check packet errors >> (default)\n" >> + " 1: Check packet errors\n" >> + " -h, --help Display help and exit.\n\n" >> + "\n", NO_PATH(progname), NO_PATH(progname), MAX_PKTIOS >> + ); >> +} >> + >> +/** >> + * Parse and store the command line arguments >> + * >> + * @param argc argument count >> + * @param argv[] argument vector >> + * @param appl_args Store application arguments here >> + */ >> +static void parse_args(int argc, char *argv[], appl_args_t *appl_args) >> +{ >> + int opt; >> + int long_index; >> + char *token; >> + size_t len; >> + int i; >> + static const struct option longopts[] = { >> + {"count", required_argument, NULL, 'c'}, >> + {"time", required_argument, NULL, 't'}, >> + {"interface", required_argument, NULL, 'i'}, >> + {"error_check", required_argument, NULL, 'e'}, >> + {"help", no_argument, NULL, 'h'}, >> + {NULL, 0, NULL, 0} >> + }; >> + >> + static const char *shortopts = "+c:t:i:e:h"; >> + >> + /* let helper collect its own arguments (e.g. --odph_proc) */ >> + odph_parse_options(argc, argv, shortopts, longopts); >> + >> + appl_args->error_check = 0; /* don't check packet errors by >> default */ >> + >> + opterr = 0; /* do not issue errors on helper options */ >> + >> + while (1) { >> + opt = getopt_long(argc, argv, shortopts, longopts, >> &long_index); >> + >> + if (opt == -1) >> + break; /* No more options */ >> + >> + switch (opt) { >> + case 'c': >> + appl_args->cpu_count = atoi(optarg); >> + break; >> + case 't': >> + appl_args->duration = atoi(optarg); >> + break; >> + case 'i': >> + len = strlen(optarg); >> + if (len == 0) { >> + usage(argv[0]); >> + exit(EXIT_FAILURE); >> + } >> + len += 1; /* add room for '\0' */ >> + >> + appl_args->if_str = malloc(len); >> + if (appl_args->if_str == NULL) { >> + usage(argv[0]); >> + exit(EXIT_FAILURE); >> + } >> + >> + /* count the number of tokens separated by ',' */ >> + strcpy(appl_args->if_str, optarg); >> + for (token = strtok(appl_args->if_str, ","), i = >> 0; >> + token != NULL; >> + token = strtok(NULL, ","), i++) >> + ; >> + >> + appl_args->if_count = i; >> + >> + if (appl_args->if_count < 1 || >> + appl_args->if_count > MAX_PKTIOS) { >> + usage(argv[0]); >> + exit(EXIT_FAILURE); >> + } >> + >> + /* allocate storage for the if names */ >> + appl_args->if_names = >> + calloc(appl_args->if_count, sizeof(char *)); >> + >> + /* store the if names (reset names string) */ >> + strcpy(appl_args->if_str, optarg); >> + for (token = strtok(appl_args->if_str, ","), i = >> 0; >> + token != NULL; token = strtok(NULL, ","), >> i++) { >> + appl_args->if_names[i] = token; >> + } >> + break; >> + case 'e': >> + appl_args->error_check = atoi(optarg); >> + break; >> + case 'h': >> + usage(argv[0]); >> + exit(EXIT_SUCCESS); >> + break; >> + default: >> + break; >> + } >> + } >> + >> + if (appl_args->if_count == 0) { >> + usage(argv[0]); >> + exit(EXIT_FAILURE); >> + } >> + >> + optind = 1; /* reset 'extern optind' from the getopt >> lib */ >> +} >> + >> +/** >> + * Print system and application info >> + */ >> +static void print_info(char *progname, appl_args_t *appl_args) >> +{ >> + int i; >> + >> + printf("\n" >> + "ODP system info\n" >> + "---------------\n" >> + "ODP API version: %s\n" >> + "ODP impl name: %s\n" >> + "CPU model: %s\n" >> + "CPU freq (hz): %" PRIu64 "\n" >> + "Cache line size: %i\n" >> + "CPU count: %i\n" >> + "\n", >> + odp_version_api_str(), odp_version_impl_name(), >> + odp_cpu_model_str(), odp_cpu_hz_max(), >> + odp_sys_cache_line_size(), odp_cpu_count()); >> + >> + printf("Running ODP appl: \"%s\"\n" >> + "-----------------\n" >> + "IF-count: %i\n" >> + "Using IFs: ", >> + progname, appl_args->if_count); >> + for (i = 0; i < appl_args->if_count; ++i) >> + printf(" %s", appl_args->if_names[i]); >> + printf("\n" >> + "Mode: PKTIN_DIRECT"); >> + >> + printf("\n\n"); >> + fflush(NULL); >> +} >> + >> +static void gbl_args_init(args_t *args) >> +{ >> + int pktio, queue; >> + >> + memset(args, 0, sizeof(args_t)); >> + >> + for (pktio = 0; pktio < MAX_PKTIOS; pktio++) { >> + args->pktios[pktio].pktio = ODP_PKTIO_INVALID; >> + >> + for (queue = 0; queue < MAX_QUEUES; queue++) >> + args->pktios[pktio].rx_q[queue] = >> ODP_QUEUE_INVALID; >> + } >> +} >> + >> +static void signal_handler(int signal) >> +{ >> + size_t num_stack_frames; >> + const char *signal_name; >> + void *bt_array[128]; >> + >> + switch (signal) { >> + case SIGILL: >> + signal_name = "SIGILL"; break; >> + case SIGFPE: >> + signal_name = "SIGFPE"; break; >> + case SIGSEGV: >> + signal_name = "SIGSEGV"; break; >> + case SIGTERM: >> + signal_name = "SIGTERM"; break; >> + case SIGBUS: >> + signal_name = "SIGBUS"; break; >> + default: >> + signal_name = "UNKNOWN"; break; >> + } >> + >> + num_stack_frames = backtrace(bt_array, 100); >> + printf("Received signal=%u (%s) exiting.", signal, signal_name); >> + backtrace_symbols_fd(bt_array, num_stack_frames, fileno(stderr)); >> + fflush(NULL); >> + sync(); >> + abort(); >> +} >> + >> +int main(int argc, char *argv[]) >> +{ >> + odph_odpthread_t thread_tbl[MAX_WORKERS]; >> + odp_pool_t pool; >> + int i; >> + int cpu; >> + int num_workers; >> + odp_shm_t shm; >> + odp_cpumask_t cpumask; >> + char cpumaskstr[ODP_CPUMASK_STR_SIZE]; >> + odp_pool_param_t params; >> + int ret; >> + int if_count; >> + int duration; >> + int (*thr_run_func)(void *); >> + odp_instance_t instance; >> + struct sigaction signal_action; >> + >> + memset(&signal_action, 0, sizeof(signal_action)); >> + signal_action.sa_handler = signal_handler; >> + sigfillset(&signal_action.sa_mask); >> + sigaction(SIGILL, &signal_action, NULL); >> + sigaction(SIGFPE, &signal_action, NULL); >> + sigaction(SIGSEGV, &signal_action, NULL); >> + sigaction(SIGTERM, &signal_action, NULL); >> + sigaction(SIGBUS, &signal_action, NULL); >> + >> + /* Init ODP before calling anything else */ >> + if (odp_init_global(&instance, NULL, NULL)) { >> + LOG_ERR("Error: ODP global init failed.\n"); >> + exit(EXIT_FAILURE); >> + } >> + >> + /* Init this thread */ >> + if (odp_init_local(instance, ODP_THREAD_CONTROL)) { >> + LOG_ERR("Error: ODP local init failed.\n"); >> + exit(EXIT_FAILURE); >> + } >> + >> + /* Reserve memory for args from shared mem */ >> + shm = odp_shm_reserve("shm_args", sizeof(args_t), >> + ODP_CACHE_LINE_SIZE, 0); >> + gbl_args = odp_shm_addr(shm); >> + >> + if (gbl_args == NULL) { >> + LOG_ERR("Error: shared mem alloc failed.\n"); >> + exit(EXIT_FAILURE); >> + } >> + gbl_args_init(gbl_args); >> + >> + /* Parse and store the application arguments */ >> + parse_args(argc, argv, &gbl_args->appl); >> + >> + /* Print both system and application information */ >> + print_info(NO_PATH(argv[0]), &gbl_args->appl); >> + >> + /* Default to system CPU count unless user specified */ >> + num_workers = MAX_WORKERS; >> + if (gbl_args->appl.cpu_count) >> + num_workers = gbl_args->appl.cpu_count; >> + >> + /* Get default worker cpumask */ >> + num_workers = odp_cpumask_default_worker(&cpumask, num_workers); >> + (void)odp_cpumask_to_str(&cpumask, cpumaskstr, >> sizeof(cpumaskstr)); >> + >> + gbl_args->appl.num_workers = num_workers; >> + >> + for (i = 0; i < num_workers; i++) >> + gbl_args->thread[i].thr_idx = i; >> + >> + if_count = gbl_args->appl.if_count; >> + >> + printf("num worker threads: %i\n", num_workers); >> + printf("first CPU: %i\n", odp_cpumask_first(&cpumask)); >> + printf("cpu mask: %s\n", cpumaskstr); >> + >> + /* Create packet pool */ >> + odp_pool_param_init(¶ms); >> + params.pkt.seg_len = SHM_PKT_POOL_BUF_SIZE; >> + params.pkt.len = SHM_PKT_POOL_BUF_SIZE; >> + params.pkt.num = SHM_PKT_POOL_SIZE; >> + params.type = ODP_POOL_PACKET; >> + >> + pool = odp_pool_create("packet pool", ¶ms); >> + >> + if (pool == ODP_POOL_INVALID) { >> + LOG_ERR("Error: packet pool create failed.\n"); >> + exit(EXIT_FAILURE); >> + } >> + odp_pool_print(pool); >> + >> + bind_workers(); >> + >> + for (i = 0; i < if_count; ++i) { >> + const char *dev = gbl_args->appl.if_names[i]; >> + int num_rx; >> + >> + /* A queue per assigned worker */ >> + num_rx = gbl_args->pktios[i].num_rx_thr; >> + >> + if (create_pktio(dev, i, num_rx, pool)) >> + exit(EXIT_FAILURE); >> + } >> + >> + gbl_args->pktios[i].pktio = ODP_PKTIO_INVALID; >> + >> + bind_queues(); >> + >> + if (!tm_config_and_init(if_count)) { >> + LOG_ERR("Error: tm system initialization failed.\n"); >> + exit(EXIT_FAILURE); >> + } >> + tm_print_user_cos(); >> + >> + memset(thread_tbl, 0, sizeof(thread_tbl)); >> + >> + odp_barrier_init(&barrier, num_workers + 1); >> + >> + thr_run_func = run_worker_direct_mode; >> + >> + /* Create worker threads */ >> + cpu = odp_cpumask_first(&cpumask); >> + for (i = 0; i < num_workers; ++i) { >> + odp_cpumask_t thd_mask; >> + odph_odpthread_params_t thr_params; >> + >> + memset(&thr_params, 0, sizeof(thr_params)); >> + thr_params.start = thr_run_func; >> + thr_params.arg = &gbl_args->thread[i]; >> + thr_params.thr_type = ODP_THREAD_WORKER; >> + thr_params.instance = instance; >> + >> + odp_cpumask_zero(&thd_mask); >> + odp_cpumask_set(&thd_mask, cpu); >> + odph_odpthreads_create(&thread_tbl[i], &thd_mask, >> + &thr_params); >> + cpu = odp_cpumask_next(&cpumask, cpu); >> + } >> + >> + /* Start packet receive and transmit */ >> + for (i = 0; i < if_count; ++i) { >> + odp_pktio_t pktio; >> + uint8_t mac[ODPH_ETHADDR_LEN]; >> + char buf[32]; >> + const char *dev; >> + >> + pktio = gbl_args->pktios[i].pktio; >> + ret = odp_pktio_start(pktio); >> + if (ret) { >> + LOG_ERR("Error: unable to start %s\n", >> + gbl_args->appl.if_names[i]); >> + exit(EXIT_FAILURE); >> + } else { >> + dev = gbl_args->appl.if_names[i]; >> + odp_pktio_mac_addr(pktio, mac, ODPH_ETHADDR_LEN); >> + sprintf(buf, "%02x:%02x:%02x:%02x:%02x:%02x", >> + mac[0], mac[1], mac[2], mac[3], mac[4], >> mac[5]); >> + printf("start pktio: %s, mac %s\n", dev, buf); >> + } >> + } >> + >> + /* Print packets count every 10 seconds */ >> + duration = gbl_args->appl.duration; >> + if (duration < 10) >> + duration = DEFAULT_RUN_SECONDS; >> + tm_print_stat(duration, 10); >> + exit_threads = 1; >> + >> + /* Master thread waits for other threads to exit */ >> + for (i = 0; i < num_workers; ++i) >> + odph_odpthreads_join(&thread_tbl[i]); >> + >> + free(gbl_args->appl.if_names); >> + free(gbl_args->appl.if_str); >> + >> + printf("Exit %d\n\n", ret); >> + return ret; >> +} >> diff --git a/platform/linux-generic/odp_traffic_mngr.c >> b/platform/linux-generic/odp_traffic_mngr.c >> index 62e5c63..354f240 100644 >> --- a/platform/linux-generic/odp_traffic_mngr.c >> +++ b/platform/linux-generic/odp_traffic_mngr.c >> @@ -2767,7 +2767,7 @@ static int tm_group_attach(odp_tm_t odp_tm) >> total_cpus = odp_cpumask_count(&all_cpus); >> avail_cpus = odp_cpumask_count(&worker_cpus); >> >> - if (total_cpus < 24) { >> + if (total_cpus < 4) { >> tm_group = tm_group_list; >> odp_tm_group = MAKE_ODP_TM_SYSTEM_GROUP(tm_group); >> if (tm_group == NULL) >> -- >> 1.8.3.1 >> >> >