# define AOK 0
#endif
+/* Default value for max queued incoming updates */
+const uint32_t DPLANE_DEFAULT_MAX_QUEUED = 200;
+
+
/* Validation check macro for context blocks */
/* #define DPLANE_DEBUG 1 */
/* Counter used to assign internal ids to providers */
uint32_t dg_provider_id;
+ /* Limit number of pending, unprocessed updates */
+ _Atomic uint32_t dg_max_queued_updates;
+
_Atomic uint64_t dg_routes_in;
_Atomic uint32_t dg_routes_queued;
_Atomic uint32_t dg_routes_queued_max;
* End of dplane context accessors
*/
+/*
+ * Retrieve the limit on the number of pending, unprocessed updates.
+ */
+uint32_t dplane_get_in_queue_limit(void)
+{
+ return atomic_load_explicit(&zdplane_info.dg_max_queued_updates,
+ memory_order_relaxed);
+}
+
+/*
+ * Configure limit on the number of pending, queued updates.
+ */
+void dplane_set_in_queue_limit(uint32_t limit, bool set)
+{
+ /* Reset to default on 'unset' */
+ if (!set)
+ limit = DPLANE_DEFAULT_MAX_QUEUED;
+
+ atomic_store_explicit(&zdplane_info.dg_max_queued_updates, limit,
+ memory_order_relaxed);
+}
+
+/*
+ * Retrieve the current queue depth of incoming, unprocessed updates
+ */
+uint32_t dplane_get_in_queue_len(void)
+{
+ return atomic_load_explicit(&zdplane_info.dg_routes_queued,
+ memory_order_seq_cst);
+}
+
/*
* Initialize a context block for a route update from zebra data structs.
*/
static int dplane_route_enqueue(struct zebra_dplane_ctx *ctx)
{
int ret = EINVAL;
+ uint32_t high, curr;
/* Enqueue for processing by the dataplane thread */
DPLANE_LOCK();
}
DPLANE_UNLOCK();
+ curr = atomic_add_fetch_explicit(&zdplane_info.dg_routes_queued,
+ 1, memory_order_seq_cst);
+
+ /* Maybe update high-water counter also */
+ high = atomic_load_explicit(&zdplane_info.dg_routes_queued_max,
+ memory_order_seq_cst);
+ while (high < curr) {
+ if (atomic_compare_exchange_weak_explicit(
+ &zdplane_info.dg_routes_queued_max,
+ &high, curr,
+ memory_order_seq_cst,
+ memory_order_seq_cst))
+ break;
+ }
+
/* Ensure that an event for the dataplane thread is active */
thread_add_event(zdplane_info.dg_master, dplane_route_process, NULL, 0,
&zdplane_info.dg_t_update);
}
done:
- /* Update counters */
+ /* Update counter */
atomic_fetch_add_explicit(&zdplane_info.dg_routes_in, 1,
memory_order_relaxed);
- if (ret == AOK) {
- uint32_t high, curr;
-
- curr = atomic_fetch_add_explicit(&zdplane_info.dg_routes_queued,
- 1, memory_order_seq_cst);
-
- /* We don't have add_and_fetch - sigh */
- curr++;
-
- /* Maybe update high-water counter also */
- high = atomic_load_explicit(&zdplane_info.dg_routes_queued_max,
- memory_order_seq_cst);
- while (high < curr) {
- if (atomic_compare_exchange_weak_explicit(
- &zdplane_info.dg_routes_queued_max,
- &high, curr,
- memory_order_seq_cst,
- memory_order_seq_cst))
- break;
- }
-
+ if (ret == AOK)
result = ZEBRA_DPLANE_REQUEST_QUEUED;
- } else if (ctx) {
+ else if (ctx) {
atomic_fetch_add_explicit(&zdplane_info.dg_route_errors, 1,
memory_order_relaxed);
dplane_ctx_free(&ctx);
ctx->zd_status = res;
/* Enqueue result to zebra main context */
- (*zdplane_info.dg_results_cb)(ctx);
+ zdplane_info.dg_results_cb(ctx);
ctx = NULL;
}
*/
int dplane_show_helper(struct vty *vty, bool detailed)
{
- uint64_t queued, queue_max, errs, incoming;
+ uint64_t queued, limit, queue_max, errs, incoming;
/* Using atomics because counters are being changed in different
* contexts.
*/
incoming = atomic_load_explicit(&zdplane_info.dg_routes_in,
memory_order_relaxed);
+ limit = atomic_load_explicit(&zdplane_info.dg_max_queued_updates,
+ memory_order_relaxed);
queued = atomic_load_explicit(&zdplane_info.dg_routes_queued,
memory_order_relaxed);
queue_max = atomic_load_explicit(&zdplane_info.dg_routes_queued_max,
vty_out(vty, "Route updates: %"PRIu64"\n", incoming);
vty_out(vty, "Route update errors: %"PRIu64"\n", errs);
+ vty_out(vty, "Route update queue limit: %"PRIu64"\n", limit);
vty_out(vty, "Route update queue depth: %"PRIu64"\n", queued);
vty_out(vty, "Route update queue max: %"PRIu64"\n", queue_max);
TAILQ_INIT(&zdplane_info.dg_route_ctx_q);
TAILQ_INIT(&zdplane_info.dg_providers_q);
+ zdplane_info.dg_max_queued_updates = DPLANE_DEFAULT_MAX_QUEUED;
+
/* TODO -- register default kernel 'provider' during init */
zdplane_info.dg_run = true;
}
/*
- * TODO - WIP version of route-update processing after async dataplane
- * update.
+ * Route-update results processing after async dataplane update.
*/
static void rib_process_after(struct zebra_dplane_ctx *ctx)
{
{
struct meta_queue *mq = data;
unsigned i;
+ uint32_t queue_len, queue_limit;
+
+ /* Ensure there's room for more dataplane updates */
+ queue_limit = dplane_get_in_queue_limit();
+ queue_len = dplane_get_in_queue_len();
+ if (queue_len > queue_limit) {
+ if (IS_ZEBRA_DEBUG_RIB_DETAILED)
+ zlog_debug("meta_queue_process: dplane queue len %u, "
+ "limit %u, retrying", queue_len, queue_limit);
+
+ /* Ensure that the meta-queue is actually enqueued */
+ if (work_queue_empty(zebrad.ribq))
+ work_queue_add(zebrad.ribq, zebrad.mq);
+
+ return WQ_QUEUE_BLOCKED;
+ }
for (i = 0; i < MQ_SIZE; i++)
if (process_subq(mq->subq[i], i)) {
return dplane_show_provs_helper(vty, detailed);
}
+/* Configure dataplane incoming queue limit */
+DEFUN (zebra_dplane_queue_limit,
+ zebra_dplane_queue_limit_cmd,
+ "zebra dplane limit (0-10000)",
+ ZEBRA_STR
+ "Zebra dataplane\n"
+ "Limit incoming queued updates\n"
+ "Number of queued updates\n")
+{
+ uint32_t limit = 0;
+
+ limit = strtoul(argv[3]->arg, NULL, 10);
+
+ dplane_set_in_queue_limit(limit, true);
+
+ return CMD_SUCCESS;
+}
+
+/* Reset dataplane queue limit to default value */
+DEFUN (no_zebra_dplane_queue_limit,
+ no_zebra_dplane_queue_limit_cmd,
+ "no zebra dplane limit [(0-10000)]",
+ NO_STR
+ ZEBRA_STR
+ "Zebra dataplane\n"
+ "Limit incoming queued updates\n"
+ "Number of queued updates\n")
+{
+ dplane_set_in_queue_limit(0, false);
+
+ return CMD_SUCCESS;
+}
/* Table configuration write function. */
static int config_write_table(struct vty *vty)
install_element(VIEW_NODE, &show_dataplane_cmd);
install_element(VIEW_NODE, &show_dataplane_providers_cmd);
+ install_element(CONFIG_NODE, &zebra_dplane_queue_limit_cmd);
+ install_element(CONFIG_NODE, &no_zebra_dplane_queue_limit_cmd);
}