diff mbox

[API-NEXT,PATCHv12,3/3] example: tm: traffic manager example

Message ID 1447805799-31047-4-git-send-email-bill.fischofer@linaro.org
State Accepted
Commit ecb0ab65b6b4e8772405d7bc2c095ef229c18c26
Headers show

Commit Message

Bill Fischofer Nov. 18, 2015, 12:16 a.m. UTC
This commit includes all of the changes to build the traffic_mgr example
application.

Signed-off-by: Barry Spinney <spinney@ezchip.com>
Signed-off-by: Bill Fischofer <bill.fischofer@linaro.org>
---
 configure.ac                            |   1 +
 example/Makefile.am                     |   2 +-
 example/traffic_mgmt/.gitignore         |   1 +
 example/traffic_mgmt/Makefile.am        |   9 +
 example/traffic_mgmt/odp_traffic_mgmt.c | 781 ++++++++++++++++++++++++++++++++
 5 files changed, 793 insertions(+), 1 deletion(-)
 create mode 100644 example/traffic_mgmt/.gitignore
 create mode 100644 example/traffic_mgmt/Makefile.am
 create mode 100644 example/traffic_mgmt/odp_traffic_mgmt.c
diff mbox

Patch

diff --git a/configure.ac b/configure.ac
index 2c47358..ae5395a 100644
--- a/configure.ac
+++ b/configure.ac
@@ -325,6 +325,7 @@  AC_CONFIG_FILES([Makefile
 		 example/ipsec/Makefile
 		 example/packet/Makefile
 		 example/timer/Makefile
+		 example/traffic_mgmt/Makefile
 		 helper/Makefile
 		 helper/test/Makefile
 		 pkgconfig/libodp.pc
diff --git a/example/Makefile.am b/example/Makefile.am
index 353f397..6b67bf5 100644
--- a/example/Makefile.am
+++ b/example/Makefile.am
@@ -1 +1 @@ 
-SUBDIRS = classifier generator ipsec packet timer
+SUBDIRS = classifier generator ipsec packet timer traffic_mgmt
diff --git a/example/traffic_mgmt/.gitignore b/example/traffic_mgmt/.gitignore
new file mode 100644
index 0000000..9e742f0
--- /dev/null
+++ b/example/traffic_mgmt/.gitignore
@@ -0,0 +1 @@ 
+odp_traffic_mgmt
\ No newline at end of file
diff --git a/example/traffic_mgmt/Makefile.am b/example/traffic_mgmt/Makefile.am
new file mode 100644
index 0000000..c8ff797
--- /dev/null
+++ b/example/traffic_mgmt/Makefile.am
@@ -0,0 +1,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
+
+noinst_HEADERS = $(top_srcdir)/example/example_debug.h
+
+dist_odp_traffic_mgmt_SOURCES = odp_traffic_mgmt.c
diff --git a/example/traffic_mgmt/odp_traffic_mgmt.c b/example/traffic_mgmt/odp_traffic_mgmt.c
new file mode 100644
index 0000000..37a85c7
--- /dev/null
+++ b/example/traffic_mgmt/odp_traffic_mgmt.c
@@ -0,0 +1,781 @@ 
+/* Copyright 2015 EZchip Semiconductor Ltd. All Rights Reserved.
+ *
+ * Copyright (c) 2015, Linaro Limited
+ * All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#define _GNU_SOURCE
+
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <signal.h>
+#include <sys/resource.h>
+#include <execinfo.h>
+#include <odp.h>
+#include <odp/plat/packet_types.h>
+#include <example_debug.h>
+
+#define NUM_SVC_CLASSES     4
+#define USERS_PER_SVC_CLASS 2
+#define APPS_PER_USER       2
+#define TM_QUEUES_PER_APP   2
+#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 Kbps   1000
+#define Mbps   1000000
+#define PERCENT(percent)  (100 * percent)
+
+#define FALSE  0
+#define TRUE   1
+
+#define MAX(a, b) (((a) > (b)) ? (a) : (b))
+#define MIN(a, b) (((a) < (b)) ? (a) : (b))
+
+#define RANDOM_BUF_LEN  1024
+
+typedef struct {
+	odp_tm_shaper_params_t    shaper_params;
+	odp_tm_threshold_params_t threshold_params;
+	odp_tm_wred_params_t      wred_params[ODP_NUM_PACKET_COLORS];
+} profile_params_set_t;
+
+typedef struct {
+	odp_tm_shaper_t    shaper_profile;
+	odp_tm_threshold_t threshold_profile;
+	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 const odp_platform_init_t PLATFORM_PARAMS = {
+};
+
+static profile_params_set_t COMPANY_PROFILE_PARAMS = {
+	.shaper_params = {
+		.commit_bps = 50  * Mbps,  .commit_burst      = 1000000,
+		.peak_bps   = 0,           .peak_burst        = 0,
+		.dual_rate  = FALSE,       .shaper_len_adjust = 20
+	},
+
+	.threshold_params = {
+		.max_pkts  = 100000,    .enable_max_pkts  = TRUE,
+		.max_bytes = 10000000,  .enable_max_bytes = TRUE
+	},
+
+	.wred_params = {
+		[ODP_PACKET_GREEN ... ODP_PACKET_YELLOW] = {
+			.min_threshold     = PERCENT(70),
+			.med_threshold     = PERCENT(90),
+			.med_drop_prob     = PERCENT(80),
+			.max_drop_prob     = PERCENT(100),
+			.enable_wred       = TRUE,
+			.use_byte_fullness = FALSE,
+		},
+
+		[ODP_PACKET_RED] = {
+			.min_threshold     = PERCENT(40),
+			.med_threshold     = PERCENT(70),
+			.med_drop_prob     = PERCENT(70),
+			.max_drop_prob     = PERCENT(100),
+			.enable_wred       = TRUE,
+			.use_byte_fullness = FALSE,
+		},
+	}
+};
+
+static profile_params_set_t COS0_PROFILE_PARAMS = {
+	.shaper_params = {
+		.commit_bps = 1 * Mbps,  .commit_burst      = 100000,
+		.peak_bps   = 4 * Mbps,  .peak_burst        = 200000,
+		.dual_rate  = FALSE,     .shaper_len_adjust = 20
+	},
+
+	.threshold_params = {
+		.max_pkts  = 10000,    .enable_max_pkts  = TRUE,
+		.max_bytes = 1000000,  .enable_max_bytes = TRUE
+	},
+
+	.wred_params = {
+		[ODP_PACKET_GREEN ... ODP_PACKET_YELLOW] = {
+			.min_threshold     = PERCENT(80),
+			.med_threshold     = PERCENT(90),
+			.med_drop_prob     = PERCENT(50),
+			.max_drop_prob     = PERCENT(100),
+			.enable_wred       = TRUE,
+			.use_byte_fullness = FALSE,
+		},
+
+		[ODP_PACKET_RED] = {
+			.min_threshold     = PERCENT(60),
+			.med_threshold     = PERCENT(80),
+			.med_drop_prob     = PERCENT(70),
+			.max_drop_prob     = PERCENT(100),
+			.enable_wred       = TRUE,
+			.use_byte_fullness = FALSE,
+		},
+	}
+};
+
+static profile_params_set_t COS1_PROFILE_PARAMS = {
+	.shaper_params = {
+		.commit_bps = 500  * Kbps,  .commit_burst      = 50000,
+		.peak_bps   = 1500 * Kbps,  .peak_burst        = 150000,
+		.dual_rate  = FALSE,        .shaper_len_adjust = 20
+	},
+
+	.threshold_params = {
+		.max_pkts  = 5000,    .enable_max_pkts  = TRUE,
+		.max_bytes = 500000,  .enable_max_bytes = TRUE
+	},
+
+	.wred_params = {
+		[ODP_PACKET_GREEN ... ODP_PACKET_YELLOW] = {
+			.min_threshold     = PERCENT(40),
+			.med_threshold     = PERCENT(90),
+			.med_drop_prob     = PERCENT(70),
+			.max_drop_prob     = PERCENT(100),
+			.enable_wred       = TRUE,
+			.use_byte_fullness = FALSE,
+		},
+
+		[ODP_PACKET_RED] = {
+			.min_threshold     = PERCENT(50),
+			.med_threshold     = PERCENT(80),
+			.med_drop_prob     = PERCENT(80),
+			.max_drop_prob     = PERCENT(100),
+			.enable_wred       = TRUE,
+			.use_byte_fullness = FALSE,
+		},
+	}
+};
+
+static profile_params_set_t COS2_PROFILE_PARAMS = {
+	.shaper_params = {
+		.commit_bps = 200 * Kbps,  .commit_burst      = 20000,
+		.peak_bps   = 400 * Kbps,  .peak_burst        = 40000,
+		.dual_rate  = FALSE,       .shaper_len_adjust = 20
+	},
+
+	.threshold_params = {
+		.max_pkts  = 1000,    .enable_max_pkts  = TRUE,
+		.max_bytes = 100000,  .enable_max_bytes = TRUE
+	},
+
+	.wred_params = {
+		[ODP_PACKET_GREEN ... ODP_PACKET_YELLOW] = {
+			.min_threshold     = PERCENT(50),
+			.med_threshold     = PERCENT(80),
+			.med_drop_prob     = PERCENT(70),
+			.max_drop_prob     = PERCENT(100),
+			.enable_wred       = TRUE,
+			.use_byte_fullness = FALSE,
+		},
+
+		[ODP_PACKET_RED] = {
+			.min_threshold     = PERCENT(40),
+			.med_threshold     = PERCENT(70),
+			.med_drop_prob     = PERCENT(80),
+			.max_drop_prob     = PERCENT(100),
+			.enable_wred       = TRUE,
+			.use_byte_fullness = FALSE,
+		},
+	}
+};
+
+static profile_params_set_t COS3_PROFILE_PARAMS = {
+	.shaper_params = {
+		.commit_bps = 100 * Kbps,  .commit_burst      = 5000,
+		.peak_bps   = 0,           .peak_burst        = 0,
+		.dual_rate  = FALSE,       .shaper_len_adjust = 20
+	},
+
+	.threshold_params = {
+		.max_pkts  = 400,    .enable_max_pkts  = TRUE,
+		.max_bytes = 60000,  .enable_max_bytes = TRUE
+	},
+
+	.wred_params = {
+		[ODP_PACKET_GREEN ... ODP_PACKET_YELLOW] = {
+			.min_threshold     = PERCENT(40),
+			.med_threshold     = PERCENT(70),
+			.med_drop_prob     = PERCENT(80),
+			.max_drop_prob     = PERCENT(100),
+			.enable_wred       = TRUE,
+			.use_byte_fullness = FALSE,
+		},
+
+		[ODP_PACKET_RED] = {
+			.min_threshold     = PERCENT(30),
+			.med_threshold     = PERCENT(60),
+			.med_drop_prob     = PERCENT(80),
+			.max_drop_prob     = PERCENT(100),
+			.enable_wred       = TRUE,
+			.use_byte_fullness = FALSE,
+		},
+	}
+};
+
+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;
+
+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;
+
+static void tester_egress_fcn(odp_packet_t odp_pkt);
+
+/** @TODO can't call hidden apis in the implementation */
+void _odp_int_name_tbl_init(void);
+
+/* Returns the number of errors encountered. */
+
+static uint32_t create_profile_set(profile_params_set_t *profile_params_set,
+				   profile_set_t        *profile_set,
+				   const char           *base_name,
+				   uint32_t              name_idx,
+				   uint32_t              shaper_scale,
+				   uint32_t              threshold_scale)
+{
+	odp_tm_threshold_params_t threshold_params, *thresholds;
+	odp_tm_shaper_params_t    shaper_params, *shaper;
+	odp_tm_wred_params_t      wred_params, *wred;
+	uint32_t                  err_cnt, color;
+	char                      name[64], wred_name[64];
+
+	err_cnt = 0;
+	if (name_idx == 0)
+		snprintf(name, sizeof(name), "%s", base_name);
+	else
+		snprintf(name, sizeof(name), "%s-%u", base_name, name_idx);
+
+	odp_tm_shaper_params_init(&shaper_params);
+	shaper                          = &profile_params_set->shaper_params;
+	shaper_params.commit_bps        = shaper->commit_bps   * shaper_scale;
+	shaper_params.peak_bps          = shaper->peak_bps     * shaper_scale;
+	shaper_params.commit_burst      = shaper->commit_burst * shaper_scale;
+	shaper_params.peak_burst        = shaper->peak_burst   * shaper_scale;
+	shaper_params.dual_rate         = shaper->dual_rate;
+	shaper_params.shaper_len_adjust = shaper->shaper_len_adjust;
+	profile_set->shaper_profile     = odp_tm_shaper_create(name,
+							       &shaper_params);
+	if (profile_set->shaper_profile == ODP_TM_INVALID)
+		err_cnt++;
+
+	odp_tm_threshold_params_init(&threshold_params);
+	thresholds = &profile_params_set->threshold_params;
+	threshold_params.max_pkts = thresholds->max_pkts  * threshold_scale;
+	threshold_params.max_bytes = thresholds->max_bytes * threshold_scale;
+	threshold_params.enable_max_pkts  = thresholds->enable_max_pkts;
+	threshold_params.enable_max_bytes = thresholds->enable_max_bytes;
+	profile_set->threshold_profile =
+		odp_tm_threshold_create(name, &threshold_params);
+
+	if (profile_set->threshold_profile == ODP_TM_INVALID)
+		err_cnt++;
+
+	for (color = 0; color < ODP_NUM_PACKET_COLORS; color++) {
+		snprintf(wred_name, sizeof(wred_name), "%s-%u", name, color);
+
+		odp_tm_wred_params_init(&wred_params);
+		wred = &profile_params_set->wred_params[color];
+		wred_params.min_threshold     = wred->min_threshold;
+		wred_params.med_threshold     = wred->med_threshold;
+		wred_params.med_drop_prob     = wred->med_drop_prob;
+		wred_params.max_drop_prob     = wred->max_drop_prob;
+		wred_params.enable_wred       = wred->enable_wred;
+		wred_params.use_byte_fullness = wred->use_byte_fullness;
+		profile_set->wred_profiles[color] =
+			odp_tm_wred_create(wred_name, &wred_params);
+		if (profile_set->wred_profiles[color] == ODP_TM_INVALID)
+			err_cnt++;
+	}
+
+	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;
+	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,
+				      class_threshold_scale);
+	err_cnt += create_profile_set(&COS1_PROFILE_PARAMS,
+				      &COS_PROFILE_SETS[1], "ServiceClass1", 0,
+				      class_shaper_scale,
+				      class_threshold_scale);
+	err_cnt += create_profile_set(&COS2_PROFILE_PARAMS,
+				      &COS_PROFILE_SETS[2], "ServiceClass2", 0,
+				      class_shaper_scale,
+				      class_threshold_scale);
+	err_cnt += create_profile_set(&COS3_PROFILE_PARAMS,
+				      &COS_PROFILE_SETS[3], "ServiceClass3", 0,
+				      class_shaper_scale,
+				      class_threshold_scale);
+
+	err_cnt += create_profile_set(&COS0_PROFILE_PARAMS,
+				      &USER_PROFILE_SETS[0], "UserSvc0", 0,
+				      user_shaper_scale, user_threshold_scale);
+	err_cnt += create_profile_set(&COS1_PROFILE_PARAMS,
+				      &USER_PROFILE_SETS[1], "UserSvc1", 0,
+				      user_shaper_scale, user_threshold_scale);
+	err_cnt += create_profile_set(&COS2_PROFILE_PARAMS,
+				      &USER_PROFILE_SETS[2], "UserSvc2", 0,
+				      user_shaper_scale, user_threshold_scale);
+	err_cnt += create_profile_set(&COS3_PROFILE_PARAMS,
+				      &USER_PROFILE_SETS[3], "UserSvc3", 0,
+				      user_shaper_scale, user_threshold_scale);
+
+	for (app_idx = 0; app_idx < APPS_PER_USER; app_idx++) {
+		err_cnt += create_profile_set(&COS0_PROFILE_PARAMS,
+					      &APP_PROFILE_SETS[0][app_idx],
+					      "AppSvc0", app_idx + 1, 1, 1);
+		err_cnt += create_profile_set(&COS1_PROFILE_PARAMS,
+					      &APP_PROFILE_SETS[1][app_idx],
+					      "AppSvc1", app_idx + 1, 1, 1);
+		err_cnt += create_profile_set(&COS2_PROFILE_PARAMS,
+					      &APP_PROFILE_SETS[2][app_idx],
+					      "AppSvc2", app_idx + 1, 1, 1);
+		err_cnt += create_profile_set(&COS3_PROFILE_PARAMS,
+					      &APP_PROFILE_SETS[3][app_idx],
+					      "AppSvc3", app_idx + 1, 1, 1);
+	}
+
+	return err_cnt;
+}
+
+static int config_example_user(odp_tm_node_t cos_tm_node,
+			       uint8_t       svc_class,
+			       uint32_t      user_num)
+{
+	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;
+	char                  user_name[64];
+	int                   rc;
+
+	profile_set = &USER_PROFILE_SETS[svc_class];
+
+	odp_tm_node_params_init(&tm_node_params);
+	tm_node_params.max_fanin         = 64;
+	tm_node_params.shaper_profile    = profile_set->shaper_profile;
+	tm_node_params.threshold_profile = profile_set->threshold_profile;
+	tm_node_params.wred_profile[ODP_PACKET_GREEN] =
+		profile_set->wred_profiles[0];
+	tm_node_params.wred_profile[ODP_PACKET_YELLOW] =
+		profile_set->wred_profiles[1];
+	tm_node_params.wred_profile[ODP_PACKET_RED] =
+		profile_set->wred_profiles[2];
+	tm_node_params.level                    = 2;
+
+	snprintf(user_name, sizeof(user_name), "Subscriber-%u", 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);
+
+	for (app_idx = 0; app_idx < APPS_PER_USER; app_idx++) {
+		profile_set = &APP_PROFILE_SETS[svc_class][app_idx];
+		for (queue_idx = 0; queue_idx < TM_QUEUES_PER_APP;
+		     queue_idx++) {
+			odp_tm_queue_params_init(&tm_queue_params);
+			tm_queue_params.shaper_profile =
+				profile_set->shaper_profile;
+			tm_queue_params.threshold_profile =
+				profile_set->threshold_profile;
+			tm_queue_params.priority = svc_class;
+
+			tm_queue_params.wred_profile[ODP_PACKET_GREEN] =
+				profile_set->wred_profiles[ODP_PACKET_GREEN];
+			tm_queue_params.wred_profile[ODP_PACKET_YELLOW] =
+				profile_set->wred_profiles[ODP_PACKET_YELLOW];
+			tm_queue_params.wred_profile[ODP_PACKET_RED] =
+				profile_set->wred_profiles[ODP_PACKET_RED];
+
+			tm_queue = odp_tm_queue_create(odp_tm_test,
+						       &tm_queue_params);
+			rc = odp_tm_queue_connect(tm_queue, user_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;
+		}
+	}
+
+	return 0;
+}
+
+static int config_company_node(const char *company_name)
+{
+	odp_tm_node_params_t tm_node_params;
+	profile_set_t       *profile_set;
+	odp_tm_node_t        company_tm_node, cos_tm_node;
+	uint32_t             cos_idx, user_idx;
+	char                 cos_node_name[64];
+
+	profile_set = &COMPANY_PROFILE_SET;
+	odp_tm_node_params_init(&tm_node_params);
+	tm_node_params.max_fanin         = 64;
+	tm_node_params.shaper_profile    = profile_set->shaper_profile;
+	tm_node_params.threshold_profile = profile_set->threshold_profile;
+	tm_node_params.wred_profile[ODP_PACKET_GREEN] =
+		profile_set->wred_profiles[0];
+	tm_node_params.wred_profile[ODP_PACKET_YELLOW] =
+		profile_set->wred_profiles[1];
+	tm_node_params.wred_profile[ODP_PACKET_RED] =
+		profile_set->wred_profiles[2];
+	tm_node_params.level                    = 0;
+
+	company_tm_node = odp_tm_node_create(odp_tm_test, company_name,
+					     &tm_node_params);
+
+	for (cos_idx = 0; cos_idx < NUM_SVC_CLASSES; cos_idx++) {
+		odp_tm_node_params_init(&tm_node_params);
+		profile_set                      = &COS_PROFILE_SETS[cos_idx];
+		tm_node_params.max_fanin         = 64;
+		tm_node_params.shaper_profile    = profile_set->shaper_profile;
+		tm_node_params.threshold_profile =
+			profile_set->threshold_profile;
+		tm_node_params.level             = 1;
+
+		tm_node_params.wred_profile[ODP_PACKET_GREEN]  =
+			profile_set->wred_profiles[ODP_PACKET_GREEN];
+		tm_node_params.wred_profile[ODP_PACKET_YELLOW] =
+			profile_set->wred_profiles[ODP_PACKET_YELLOW];
+		tm_node_params.wred_profile[ODP_PACKET_RED]    =
+			profile_set->wred_profiles[ODP_PACKET_RED];
+
+		snprintf(cos_node_name, sizeof(cos_node_name), "%s-Class-%u",
+			 company_name, cos_idx);
+		cos_tm_node = odp_tm_node_create(odp_tm_test, cos_node_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,
+					    cos_idx * 256 + user_idx);
+	}
+
+	odp_tm_node_connect(company_tm_node, ODP_TM_ROOT);
+	return 0;
+}
+
+static int create_and_config_tm(void)
+{
+	odp_tm_params_t params;
+	uint32_t        err_cnt;
+
+	odp_tm_params_init(&params);
+	params.capability.max_tm_queues              = 10 * NUM_TM_QUEUES;
+	params.capability.max_priority               = 3;
+	params.capability.max_levels                 = 3;
+	params.capability.tm_queue_shaper_supported  = TRUE;
+	params.capability.tm_node_shaper_supported   = TRUE;
+	params.capability.red_supported              = TRUE;
+	params.capability.hierarchical_red_supported = TRUE;
+	params.capability.weights_supported          = TRUE;
+	params.capability.fair_queuing_supported     = TRUE;
+	params.egress.egress_kind                    = ODP_TM_EGRESS_FN;
+	params.egress.egress_fcn                     = tester_egress_fcn;
+
+	odp_tm_test = odp_tm_create("TM test", &params);
+	err_cnt = init_profile_sets();
+	if (err_cnt != 0)
+		printf("%s init_profile_sets encountered %u errors\n",
+		       __func__, err_cnt);
+
+	config_company_node("TestCompany");
+	return err_cnt;
+}
+
+static uint32_t random_8(void)
+{
+	uint32_t rand8;
+
+	if (RANDOM_BUF_LEN <= next_rand_byte) {
+		odp_random_data(random_buf, RANDOM_BUF_LEN, 1);
+		next_rand_byte = 0;
+	}
+
+	rand8 = random_buf[next_rand_byte++];
+	return rand8;
+}
+
+static uint32_t random_16(void)
+{
+	uint8_t byte1, byte2;
+
+	if ((RANDOM_BUF_LEN - 1) <= next_rand_byte) {
+		odp_random_data(random_buf, RANDOM_BUF_LEN, 1);
+		next_rand_byte = 0;
+	}
+
+	byte1 = random_buf[next_rand_byte++];
+	byte2 = random_buf[next_rand_byte++];
+	return (((uint16_t)byte1) << 8) | ((uint16_t)byte2);
+}
+
+static uint32_t pkt_service_class(void)
+{
+	uint32_t rand8;
+
+       /* 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;
+	}
+}
+
+static odp_packet_t make_odp_packet(uint16_t pkt_len)
+{
+	odp_packet_t odp_pkt;
+	uint8_t      rand8a, rand8b, pkt_color, drop_eligible;
+
+	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(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);
+}
+
+static int traffic_generator(uint32_t pkts_to_send)
+{
+	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;
+		}
+
+		odp_atomic_inc_u32(&atomic_pkts_into_tm);
+		pkts_into_tm = odp_atomic_load_u32(&atomic_pkts_into_tm);
+		pkts_from_tm = odp_atomic_load_u32(&atomic_pkts_from_tm);
+	}
+
+	printf("%s odp_tm_enq_errs=%u\n", __func__, odp_tm_enq_errs);
+
+       /* 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;
+	}
+
+	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;
+	}
+
+	return 0;
+}
+
+static int process_cmd_line_options(uint32_t argc, char *argv[])
+{
+	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;
+		}
+	}
+
+	return 0;
+}
+
+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[])
+{
+	struct sigaction signal_action;
+	struct rlimit    rlimit;
+	uint32_t pkts_into_tm, pkts_from_tm;
+
+	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);
+
+	odp_init_global(&ODP_INIT_PARAMS, &PLATFORM_PARAMS);
+	odp_init_local(ODP_THREAD_CONTROL);
+	_odp_int_name_tbl_init();
+
+	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;
+}