From: Pravin Pathak <[email protected]> - Validate duplicate/conflicting queue-port links and slot capacity at the start of slb_port_link_one(), and fix priority levels to 2. - Validate nb_event_ports and nb_event_queues limits in slb_configure(), avoid rounding max_inflight to a power of 2, and precompute migration_cooldown_cycles once during configuration. - Verify all queues are configured and linked to at least one port in slb_start(). - Use relaxed stores when clearing drained reorder buffer slots and avoid duplicate acquire load on the first burst element in slb_reorder_drain(). - Ensure hold buffer capacity before dequeuing in slb_atomic_refill() and continue refilling uncongested ports while hold buffer space remains. - Enforce 1-to-1 single-link port and single-link queue pairing in slb_port_link_one(), disallowing linking a single-link queue to a regular port or a regular queue to a single-linked port. - Preserve event sched_type on single-link dequeue and only clear impl_opaque to 0.
Signed-off-by: Pravin Pathak <[email protected]> --- drivers/event/slb/slb_evdev.c | 200 +++++++++++++-------- drivers/event/slb/slb_evdev.h | 41 +---- drivers/event/slb/slb_event.c | 290 ++++++++++++++++++------------- drivers/event/slb/slb_selftest.c | 12 -- 4 files changed, 306 insertions(+), 237 deletions(-) diff --git a/drivers/event/slb/slb_evdev.c b/drivers/event/slb/slb_evdev.c index 16bfefaa68..346b4b38ed 100644 --- a/drivers/event/slb/slb_evdev.c +++ b/drivers/event/slb/slb_evdev.c @@ -84,10 +84,6 @@ slb_port_setup(struct rte_eventdev *dev, uint8_t port_id, .last_migrated_time = 0, }; - port->remap_input_q = false; - rte_atomic_store_explicit(&port->active_queues, 0, - rte_memory_order_relaxed); - dev->data->ports[port_id] = port; /* Reset all queues mapped to this port */ @@ -199,9 +195,7 @@ slb_queue_setup(struct rte_eventdev *dev, uint8_t queue_id, return -ENOMEM; queue->q_ring = ring; - /* Single-link queue uses atomic schedule type */ - queue->schedule_type = queue->is_single_link ? - RTE_SCHED_TYPE_ATOMIC : conf->schedule_type; + queue->schedule_type = conf->schedule_type; queue->serving_ports_mask = 0; queue->num_serving_ports = 0; @@ -270,16 +264,20 @@ slb_queue_def_conf(struct rte_eventdev *dev __rte_unused, static void slb_port_unlink_queue_from_prio(struct slb_port *port, int prio, int slot) { + /* Shift the queue entries to overwriting the unlinked queue slot */ for (int k = slot; k < port->num_q[prio] - 1; k++) port->q_entries[prio][k] = port->q_entries[prio][k + 1]; + /* Decrement mapped queue count and Clear the last slot after shifting */ port->num_q[prio]--; memset(&port->q_entries[prio][port->num_q[prio]], 0, sizeof(struct slb_port_q_entry)); + /* Adjust the next queue index if it points beyond the new count */ if (port->next_q[prio] >= port->num_q[prio]) port->next_q[prio] = 0; } +/* Find the slot of a queue in a specific priority level, returning -1 if not found */ static int slb_port_find_queue_slot(struct slb_port *port, int prio, uint8_t qid) { @@ -290,24 +288,67 @@ slb_port_find_queue_slot(struct slb_port *port, int prio, uint8_t qid) return -1; } +/* Link a queue to a port at a specific priority level */ static int slb_port_link_one(struct slb_port *port, struct slb_queue *queue, uint8_t qid, uint8_t app_prio, bool has_prio) { + struct rte_event_ring *dq_ring; + uint16_t slot; int prio; - bool found = false; if (port->is_single_linked) { - if (port->single_linked_q != NULL && - port->single_linked_q != queue) { + /* Only a single-link queue can be linked to a single-linked port */ + if (!queue->is_single_link) { + rte_errno = EINVAL; + return -EINVAL; + } + /* Only one queue can be linked to a single-linked port */ + if (port->single_linked_q != NULL) { + if (port->single_linked_q == queue) + return 0; + rte_errno = EINVAL; + return -EINVAL; + } + /* A single-link queue can only be linked to one port */ + if (queue->serving_ports_mask != 0) { rte_errno = EINVAL; return -EINVAL; } port->single_linked_q = queue; port->single_linked_ring = queue->q_ring; } else { - if (!queue->is_single_link && - queue->schedule_type == RTE_SCHED_TYPE_ATOMIC && + /* A single-link queue cannot be linked to a regular port */ + if (queue->is_single_link) { + rte_errno = EINVAL; + return -EINVAL; + } + + /* Convert application priority to internal priority level (0 or 1) */ + if (has_prio) + prio = (app_prio < RTE_EVENT_DEV_PRIORITY_NORMAL) ? 0 : 1; + else + prio = SLB_MAX_PRIORITIES - 1; + + /* Check if queue is already mapped to this port at requested priority */ + for (int pr = 0; pr < SLB_MAX_PRIORITIES; pr++) { + if (slb_port_find_queue_slot(port, pr, qid) >= 0) { + if (pr == prio) + return 0; + rte_errno = EINVAL; + return -EINVAL; + } + } + + /* Ensure a free mapping slot is available at the requested priority */ + slot = port->num_q[prio]; + if (slot >= SLB_PORT_MAX_Q_MAP_PER_PRIO) { + rte_errno = ENOSPC; + return -ENOSPC; + } + + /* Allocate per-port ring when linking an atomic queue */ + if (queue->schedule_type == RTE_SCHED_TYPE_ATOMIC && queue->per_port_rings[port->id] == NULL) { char ring_name[RTE_RING_NAMESIZE]; struct rte_event_ring *ring; @@ -327,53 +368,23 @@ slb_port_link_one(struct slb_port *port, struct slb_queue *queue, queue->per_port_rings[port->id] = ring; } - if (has_prio) - prio = (app_prio < RTE_EVENT_DEV_PRIORITY_NORMAL) ? 0 : 1; - else - prio = SLB_MAX_PRIORITIES - 1; - - for (int pr = 0; pr < SLB_MAX_PRIORITIES; pr++) { - int s = slb_port_find_queue_slot(port, pr, qid); - - if (s >= 0) { - if (pr == prio) - found = true; - else - slb_port_unlink_queue_from_prio(port, pr, s); - break; - } - } - if (!found) { - uint16_t slot = port->num_q[prio]; - struct rte_event_ring *dq_ring = - (!queue->is_single_link && - queue->schedule_type == RTE_SCHED_TYPE_ATOMIC) ? - queue->per_port_rings[port->id] : queue->q_ring; - - if (slot >= SLB_PORT_MAX_Q_MAP_PER_PRIO) { - rte_errno = ENOSPC; - return -ENOSPC; - } - port->q_entries[prio][slot].dq_ring = dq_ring; - port->q_entries[prio][slot].queue = queue; - port->q_entries[prio][slot].queue_id = qid; - port->q_entries[prio][slot].schedule_type = - queue->schedule_type; - port->q_entries[prio][slot].is_single_link = - queue->is_single_link; - port->num_q[prio]++; - } + dq_ring = (queue->schedule_type == RTE_SCHED_TYPE_ATOMIC) ? + queue->per_port_rings[port->id] : queue->q_ring; - rte_atomic_fetch_or_explicit(&port->active_queues, - (1ULL << qid), - rte_memory_order_release); + port->q_entries[prio][slot].dq_ring = dq_ring; + port->q_entries[prio][slot].queue = queue; + port->q_entries[prio][slot].queue_id = qid; + port->q_entries[prio][slot].schedule_type = queue->schedule_type; + port->num_q[prio]++; } + /* Add newly linked port to the queue's serving ports mask */ queue->serving_ports_mask |= (1ULL << port->id); queue->num_serving_ports = rte_popcount64(queue->serving_ports_mask); return 0; } +/* Unlink a queue from a port */ static int slb_port_unlink_one(struct slb_port *port, struct slb_queue *queue, uint8_t qid) { @@ -386,7 +397,7 @@ slb_port_unlink_one(struct slb_port *port, struct slb_queue *queue, uint8_t qid) } port->single_linked_q = NULL; port->single_linked_ring = NULL; - } else { + } else { /* Unlink queue by finding its slot */ for (int prio = 0; prio < SLB_MAX_PRIORITIES; prio++) { int s = slb_port_find_queue_slot(port, prio, qid); @@ -396,21 +407,17 @@ slb_port_unlink_one(struct slb_port *port, struct slb_queue *queue, uint8_t qid) break; } } - if (!found) { + if (!found) { /* No link found */ rte_errno = EINVAL; return -EINVAL; } + /* For atomic queues, free the per-port ring if it exists */ if (queue->schedule_type == RTE_SCHED_TYPE_ATOMIC && queue->per_port_rings[port->id] != NULL) { rte_event_ring_free(queue->per_port_rings[port->id]); queue->per_port_rings[port->id] = NULL; } - - rte_atomic_fetch_and_explicit(&port->active_queues, - ~(1ULL << qid), - rte_memory_order_release); - port->remap_input_q = true; } queue->serving_ports_mask &= ~(1ULL << port->id); @@ -418,6 +425,7 @@ slb_port_unlink_one(struct slb_port *port, struct slb_queue *queue, uint8_t qid) return 0; } +/* Link all queues to a port at specific priorities */ static int slb_port_link(struct rte_eventdev *dev, void *p, const uint8_t queues[], const uint8_t priorities[], uint16_t num) @@ -439,6 +447,7 @@ slb_port_link(struct rte_eventdev *dev, void *p, const uint8_t queues[], return num; } +/* Unlink all queues from a port */ static int slb_port_unlink(struct rte_eventdev *dev, void *p, uint8_t queues[], uint16_t num) @@ -458,6 +467,7 @@ slb_port_unlink(struct rte_eventdev *dev, void *p, uint8_t queues[], return num; } +/* Get information about the event device */ static void slb_info_get(struct rte_eventdev *dev __rte_unused, struct rte_event_dev_info *info) @@ -467,7 +477,7 @@ slb_info_get(struct rte_eventdev *dev __rte_unused, .max_event_queues = SLB_MAX_QUEUES, .max_event_queue_flows = SLB_MAX_FLOWS, .max_event_queue_priority_levels = SLB_MAX_PRIORITIES, - .max_event_priority_levels = SLB_MAX_PRIORITIES, + .max_event_priority_levels = 1, .max_event_ports = SLB_MAX_PORTS, .max_event_port_dequeue_depth = SLB_MAX_PORT_DEQUEUE_DEPTH, .max_event_port_enqueue_depth = SLB_MAX_PORT_ENQUEUE_DEPTH, @@ -487,13 +497,13 @@ slb_info_get(struct rte_eventdev *dev __rte_unused, }; } +/* Configure the event device */ static int slb_configure(const struct rte_eventdev *dev) { struct slb_evdev *slb = slb_pmd_priv(dev); const struct rte_event_dev_config *conf = &dev->data->dev_conf; int32_t min_max_in_flight; - uint32_t q_size; if (conf->nb_events_limit <= 0 || conf->nb_events_limit > SLB_MAX_EVENTS) { @@ -503,36 +513,60 @@ slb_configure(const struct rte_eventdev *dev) return -EINVAL; } + /* Validate max event ports and queues that SLB can support */ + if (conf->nb_event_ports == 0 || + conf->nb_event_ports > SLB_MAX_PORTS) { + SLB_LOG_DP_LINE(INFO, + "nb_event_ports (%u) invalid, max is %d", + conf->nb_event_ports, SLB_MAX_PORTS); + return -EINVAL; + } + + if (conf->nb_event_queues == 0 || + conf->nb_event_queues > SLB_MAX_QUEUES) { + SLB_LOG_DP_LINE(INFO, + "nb_event_queues (%u) invalid, max is %d", + conf->nb_event_queues, SLB_MAX_QUEUES); + return -EINVAL; + } + slb->num_ports = conf->nb_event_ports; slb->num_queues = conf->nb_event_queues; + /* Compute minimum credits required to avoid deadlock */ min_max_in_flight = conf->nb_event_ports * SLB_PORT_MAX_CREDITS; if (min_max_in_flight > SLB_MAX_EVENTS) min_max_in_flight = SLB_MAX_EVENTS; - q_size = rte_align32pow2((uint32_t)RTE_MAX(conf->nb_events_limit, - min_max_in_flight)); - if (q_size > SLB_MAX_EVENTS) - q_size = SLB_MAX_EVENTS; - - slb->max_inflight = (int32_t)q_size; + /* Set credit pool size to max of nb_events_limit and minimum required to avoid deadlock */ + slb->max_inflight = RTE_MAX(conf->nb_events_limit, min_max_in_flight); + slb->migration_cooldown_cycles = + (rte_get_timer_hz() * SLB_MIGRATION_COOLDOWN_MS) / 1000; return 0; } +/* Start the event device */ static int slb_start(struct rte_eventdev *dev) { struct slb_evdev *slb = slb_pmd_priv(dev); + /* Make sure all queues are set up and have at least one linked port */ for (int i = 0; i < slb->num_queues; i++) { - if (slb->queues[i].serving_ports_mask == 0) - SLB_LOG_DP_LINE(INFO, - "Queue %d has no linked ports on start", i); + if (slb->queues[i].q_ring == NULL || + slb->queues[i].serving_ports_mask == 0) { + SLB_LOG_DP_LINE(ERR, + "Queue %d is not configured or has no linked ports", + i); + return -ENOLINK; + } } + /* No events in flight */ rte_atomic_store_explicit(&slb->credits_on_loan, 0, rte_memory_order_relaxed); + /* Reset port states */ for (int i = 0; i < slb->num_ports; i++) { struct slb_port *port = &slb->ports[i]; port->inflight_credits = 0; @@ -544,6 +578,7 @@ slb_start(struct rte_eventdev *dev) return 0; } +/* Empty the event queue */ static void slb_drain_ring(uint8_t dev_id, struct rte_event_ring *ring, eventdev_stop_flush_t flush, void *flush_arg) @@ -553,6 +588,7 @@ slb_drain_ring(uint8_t dev_id, struct rte_event_ring *ring, if (ring == NULL) return; + /* Dequeue all events from the ring and call user callback if provided */ while (rte_event_ring_dequeue_burst(ring, &ev, 1, NULL)) { if (flush != NULL) flush(dev_id, ev, flush_arg); @@ -560,6 +596,7 @@ slb_drain_ring(uint8_t dev_id, struct rte_event_ring *ring, rte_ring_reset(&ring->r); } +/* Stop the event device */ static void slb_stop(struct rte_eventdev *dev) { @@ -575,6 +612,7 @@ slb_stop(struct rte_eventdev *dev) q->atomic_state != NULL) { struct slb_atomic_state *astate = q->atomic_state; + /* Call user callback for each held event */ for (uint16_t h = 0; h < astate->hold_count; h++) { if (flush != NULL) flush(dev_id, astate->hold_buffer[h], @@ -584,6 +622,7 @@ slb_stop(struct rte_eventdev *dev) astate->next_port_rr = 0; astate->migration.in_progress = false; + /* Reset flow to port mapping */ for (int f = 0; f < SLB_ATOMIC_FLOWS; f++) { rte_atomic_store_explicit( &astate->flow_map.flows[f].port_id, @@ -594,6 +633,7 @@ slb_stop(struct rte_eventdev *dev) memset(astate->port_stats, 0, sizeof(astate->port_stats)); + /* Drain all events from per-port rings */ for (uint16_t pid = 0; pid < slb->num_ports; pid++) { if (q->per_port_rings[pid] != NULL) slb_drain_ring(dev_id, @@ -606,11 +646,12 @@ slb_stop(struct rte_eventdev *dev) uint8_t st = rte_atomic_load_explicit( &q->reorder_buffer[s].state, rte_memory_order_relaxed); - + /* Flush all ready events from the reorder buffer. */ if (st == SLB_REORDER_READY && flush != NULL) flush(dev_id, q->reorder_buffer[s].event, flush_arg); + /* Mark freed slot as empty */ rte_atomic_store_explicit( &q->reorder_buffer[s].state, SLB_REORDER_EMPTY, @@ -623,6 +664,7 @@ slb_stop(struct rte_eventdev *dev) slb_drain_ring(dev_id, q->q_ring, flush, flush_arg); } + /* Reset each port's state, history lists and queue mappings */ for (int i = 0; i < slb->num_ports; i++) { struct slb_port *port = &slb->ports[i]; port->inflight_credits = 0; @@ -635,21 +677,25 @@ slb_stop(struct rte_eventdev *dev) port->next_q[p] = 0; } + /* Reset the credit pool to all available */ rte_atomic_store_explicit(&slb->credits_on_loan, 0, rte_memory_order_relaxed); } +/* Close the event device */ static int slb_close(struct rte_eventdev *dev) { struct slb_evdev *slb = slb_pmd_priv(dev); + /* Release all resources acquired by the queues */ for (int i = 0; i < slb->num_queues; i++) slb_queue_release(dev, i); return 0; } +/* Dump the event device current state and statistics */ void slb_dump(struct rte_eventdev *dev, FILE *f) { @@ -689,14 +735,12 @@ slb_dump(struct rte_eventdev *dev, FILE *f) fprintf(f, "\n PORT STATISTICS & QUEUE LINKAGES:\n"); for (i = 0; i < slb->num_ports; i++) { struct slb_port *p = &slb->ports[i]; - uint64_t aq = rte_atomic_load_explicit(&p->active_queues, - rte_memory_order_acquire); int linked_count = 0; fprintf(f, - " Port %2u (credits=%3d, active=0x%08" PRIx64 - ", loaded=%d, mig_in=%" PRIu64 ", mig_out=%" PRIu64 "): ", - i, p->inflight_credits, aq, p->is_loaded, + " Port %2u (credits=%3d, loaded=%d" + ", mig_in=%" PRIu64 ", mig_out=%" PRIu64 "): ", + i, p->inflight_credits, p->is_loaded, p->migrations_in, p->migrations_out); if (p->is_single_linked) { @@ -743,7 +787,8 @@ slb_dump(struct rte_eventdev *dev, FILE *f) i, count, ring_size, q->serving_ports_mask, q->num_serving_ports, q->schedule_type, q->is_single_link); - if (q->schedule_type == RTE_SCHED_TYPE_ATOMIC) { + if (!q->is_single_link && + q->schedule_type == RTE_SCHED_TYPE_ATOMIC) { if (q->atomic_state) { fprintf(f, " Atomic State: total_migrations=%" PRIu64 @@ -774,7 +819,8 @@ slb_dump(struct rte_eventdev *dev, FILE *f) } } } - } else if (q->schedule_type == RTE_SCHED_TYPE_ORDERED) { + } else if (!q->is_single_link && + q->schedule_type == RTE_SCHED_TYPE_ORDERED) { fprintf(f, " Ordered reorder_drain_idx=%u\n", q->reorder_drain_idx); } diff --git a/drivers/event/slb/slb_evdev.h b/drivers/event/slb/slb_evdev.h index bf262903cd..c01290e539 100644 --- a/drivers/event/slb/slb_evdev.h +++ b/drivers/event/slb/slb_evdev.h @@ -16,8 +16,8 @@ #define SLB_PMD_NAME RTE_STR(event_slb) #define SLB_MAX_PORTS (64) -#define SLB_MAX_PORT_DEQUEUE_DEPTH (128) -#define SLB_MAX_PORT_ENQUEUE_DEPTH (128) +#define SLB_MAX_PORT_DEQUEUE_DEPTH (128u) +#define SLB_MAX_PORT_ENQUEUE_DEPTH (128u) #define SLB_MAX_QUEUES (64) @@ -36,13 +36,13 @@ /* Maximum number of queues that can be linked to a port per priority */ #define SLB_PORT_MAX_Q_MAP_PER_PRIO (SLB_MAX_QUEUES) -/* Number of priority levels supported by SLB. Configure 1..4. */ +/* Number of priority levels supported by SLB. Currently fixed to 2 */ #ifndef SLB_MAX_PRIORITIES #define SLB_MAX_PRIORITIES 2 #endif -#if (SLB_MAX_PRIORITIES < 1) || (SLB_MAX_PRIORITIES > 4) -#error "SLB_MAX_PRIORITIES must be between 1 and 4" +#if (SLB_MAX_PRIORITIES != 2) +#error "SLB_MAX_PRIORITIES must be set to 2 for now" #endif /* Total max mapping slots (per-prio * priorities) */ @@ -56,8 +56,7 @@ struct slb_port_q_entry { struct slb_queue *queue; uint8_t queue_id; uint8_t schedule_type; - bool is_single_link; - uint8_t pad[5]; + uint8_t pad[6]; }; #define SLB_PORT_HISTORY_SIZE 256 @@ -139,15 +138,13 @@ struct __rte_cache_aligned slb_port { uint16_t pending_releases; int32_t inflight_credits; int32_t new_event_threshold; - RTE_ATOMIC(uint64_t) active_queues; uint16_t num_q[SLB_MAX_PRIORITIES]; uint16_t next_q[SLB_MAX_PRIORITIES]; bool is_single_linked; bool implicit_release; bool credits_drained; - bool remap_input_q; bool is_loaded; /* Port marked loaded for atomic flow migration */ - uint8_t pad_port[3]; + uint8_t pad_port[4]; struct rte_event_ring *single_linked_ring; struct slb_evdev *slb; @@ -196,7 +193,7 @@ struct slb_queue { rte_spinlock_t reorder_lock; }; -/* Limited by the size of 'serving_ports_mask' and 'active_queues' bitmasks */ +/* Limited by the size of 64-bit port and queue bitmasks */ static_assert(SLB_MAX_PORTS <= 64, "Max compile-time port count exceeded"); static_assert(SLB_MAX_QUEUES <= 64, "Max compile-time queue count exceeded"); @@ -208,6 +205,7 @@ struct slb_evdev { struct slb_queue queues[SLB_MAX_QUEUES]; uint8_t num_queues; int32_t max_inflight; + uint64_t migration_cooldown_cycles; alignas(RTE_CACHE_LINE_SIZE) RTE_ATOMIC(int32_t) credits_on_loan; }; @@ -262,25 +260,4 @@ extern int event_slb_logtype; #define SLB_LOG_DP_PORT_LINE(level, port_id, fmt, ...) \ SLB_LOG_DP_LINE(level, "<Port %d> " fmt, port_id, ## __VA_ARGS__) -/* Obtain serving port masks for the specified queue and set corresponding - * active queue bits for all served ports. - */ -static inline void -slb_port_wakeup_queues(struct slb_evdev *slb, struct slb_queue *queue) -{ - uint8_t queue_id = queue - slb->queues; - uint64_t port_mask = queue->serving_ports_mask; - struct slb_port *port; - uint8_t port_id; - - while (port_mask) { - port_id = rte_ctz64(port_mask); - port = &slb->ports[port_id]; - rte_atomic_fetch_or_explicit(&port->active_queues, - (1ULL << queue_id), - rte_memory_order_release); - port_mask &= ~(1ULL << port_id); - } -} - #endif /* _SLB_EVDEV_H_ */ diff --git a/drivers/event/slb/slb_event.c b/drivers/event/slb/slb_event.c index c44deba905..3d0436e4a0 100644 --- a/drivers/event/slb/slb_event.c +++ b/drivers/event/slb/slb_event.c @@ -11,7 +11,7 @@ #include "slb_evdev.h" -/* Acquire credits for new events */ +/* Port Acquiring credits for new events. Serve from local cache if possible. */ static bool slb_port_acquire_credits(struct slb_evdev *slb, struct slb_port *port, int32_t credits) @@ -34,34 +34,38 @@ slb_port_acquire_credits(struct slb_evdev *slb, struct slb_port *port, available = slb->max_inflight - total_inflight; required_credits = RTE_MAX(abs(remaining_credits), SLB_PORT_MIN_CREDITS); + /* Pool does not have enough credits */ if (available < required_credits) return false; + /* Take credits from the pool */ new_total_inflight = rte_atomic_fetch_add_explicit(&slb->credits_on_loan, required_credits, rte_memory_order_relaxed) + required_credits; - + /* Check if the new total exceeds the maximum allowed */ if (unlikely(new_total_inflight > slb->max_inflight)) { rte_atomic_fetch_sub_explicit(&slb->credits_on_loan, required_credits, rte_memory_order_relaxed); return false; } - + /* Update the port's credit counts*/ port->inflight_credits += required_credits; port->inflight_credits -= credits; return true; } -/* Return accumulated credits (Terminated events) */ +/* Port returning credits (Terminated events). Keep local cache. + * If local cache exceeds the threshold, return excess credits to the pool + */ void slb_port_return_credits(struct slb_evdev *slb, struct slb_port *port, int32_t credits) { - port->inflight_credits += credits; + port->inflight_credits += credits; /* Local credit cache */ if (unlikely(port->inflight_credits >= SLB_PORT_MAX_CREDITS)) { int32_t return_credits = port->inflight_credits - @@ -74,6 +78,7 @@ slb_port_return_credits(struct slb_evdev *slb, struct slb_port *port, } } +/* Port enqueue statistics */ static void slb_port_enqueue_stats(struct slb_port *port, uint16_t num_new, uint16_t num_forward, uint16_t num_release) @@ -98,7 +103,7 @@ slb_port_queue_enqueue_burst_stats(struct slb_port *source_port, #define REORDER_BURST_SZ 64 -/* Drain in-order contiguous ready events from ordered queue's reorder buffer */ +/* Drain contiguous ready events from ordered queue's reorder buffer */ void slb_reorder_drain(struct slb_evdev *slb, struct slb_queue *queue) { @@ -114,8 +119,8 @@ slb_reorder_drain(struct slb_evdev *slb, struct slb_queue *queue) struct slb_reorder_entry *buf = queue->reorder_buffer; uint8_t state; - /* Drain from current drain index till hitting an empty slot. - * Released slots need no processing except marking them empty. + /* Drain events from the current drain index till an empty slot. + * Released event slots need no processing except marking them as empty. */ while ((state = rte_atomic_load_explicit(&buf[drain_idx].state, rte_memory_order_acquire)) != @@ -123,30 +128,27 @@ slb_reorder_drain(struct slb_evdev *slb, struct slb_queue *queue) if (state == SLB_REORDER_RELEASED) { rte_atomic_store_explicit(&buf[drain_idx].state, SLB_REORDER_EMPTY, - rte_memory_order_release); + rte_memory_order_relaxed); drain_idx = (drain_idx + 1) & mask; continue; } - /* Gather contiguous READY events destined for same target_qid */ + /* Gather contiguous READY events destined to same target_qid */ struct rte_event burst[REORDER_BURST_SZ]; uint16_t burst_size = 0; uint8_t target_qid = buf[drain_idx].event.queue_id; - while (burst_size < REORDER_BURST_SZ && - rte_atomic_load_explicit(&buf[drain_idx].state, - rte_memory_order_acquire) == - SLB_REORDER_READY && - buf[drain_idx].event.queue_id == target_qid) { + do { burst[burst_size++] = buf[drain_idx].event; rte_atomic_store_explicit(&buf[drain_idx].state, SLB_REORDER_EMPTY, - rte_memory_order_release); + rte_memory_order_relaxed); drain_idx = (drain_idx + 1) & mask; - } - - if (burst_size == 0) - break; + } while (burst_size < REORDER_BURST_SZ && + rte_atomic_load_explicit(&buf[drain_idx].state, + rte_memory_order_acquire) == + SLB_REORDER_READY && + buf[drain_idx].event.queue_id == target_qid); /* Target queue is sized to max_inflight and can never be full */ rte_event_ring_enqueue_burst(slb->queues[target_qid].q_ring, @@ -157,13 +159,14 @@ slb_reorder_drain(struct slb_evdev *slb, struct slb_queue *queue) rte_spinlock_unlock(&queue->reorder_lock); } -/* Hash flow ID to atomic flow slot index */ +/* Hash 20-bit flow ID into atomic flow slot index (SLB_ATOMIC_FLOWS) */ static inline uint32_t slb_flow_hash(uint32_t flow_id) { return (flow_id * 2654435761U) & SLB_ATOMIC_FLOWS_MASK; } +/* Find the lightest flow on a given port to migrate */ static int32_t slb_find_lightest_flow_on_port(struct slb_queue *queue, uint8_t port_id, uint64_t now, uint64_t cooldown_cycles) @@ -187,12 +190,14 @@ slb_find_lightest_flow_on_port(struct slb_queue *queue, uint8_t port_id, r = &ering->r; cons = rte_atomic_load_explicit(&r->cons.tail, rte_memory_order_relaxed); prod = rte_atomic_load_explicit(&r->prod.tail, rte_memory_order_relaxed); - count = prod - cons; + count = prod - cons; /* Number of atomic events in the per port ring */ memset(counts, 0, sizeof(counts)); if (count > 0) { + /* Skip over the ring header and point to events */ struct rte_event *ring_events = (struct rte_event *)(r + 1); + /* Count events for each flow in per port queue */ for (uint32_t i = 0; i < count; i++) { uint32_t slot = (cons + i) & r->mask; uint32_t f_idx = slb_flow_hash(ring_events[slot].flow_id); @@ -201,9 +206,11 @@ slb_find_lightest_flow_on_port(struct slb_queue *queue, uint8_t port_id, } } + /* Find the flow with the minimum number of events */ for (uint32_t f = 0; f < SLB_ATOMIC_FLOWS; f++) { if (rte_atomic_load_explicit(&astate->flow_map.flows[f].port_id, rte_memory_order_relaxed) == (int32_t)port_id) { + /* Skip flows that have been migrated recently */ if (astate->flow_last_migrated[f] != 0 && (now - astate->flow_last_migrated[f]) < cooldown_cycles) continue; @@ -218,16 +225,19 @@ slb_find_lightest_flow_on_port(struct slb_queue *queue, uint8_t port_id, return best_flow_idx; } +/* Check if port is loaded and migration is necessary. Start the flow + * migration processes if required. + */ static void slb_check_and_trigger_migration(struct slb_evdev *slb, struct slb_queue *queue, struct slb_atomic_state *astate, uint8_t full_port_id) { struct slb_atomic_port_stats *pstats = &astate->port_stats[full_port_id]; + uint64_t cooldown_cycles = slb->migration_cooldown_cycles; uint64_t now; - uint64_t hz; - uint64_t cooldown_cycles; int32_t flow_to_migrate; + /* Check if the port has crossed load threshold */ pstats->load_counter++; if (pstats->load_counter < SLB_MIGRATION_LOAD_THRESHOLD) return; @@ -235,20 +245,21 @@ slb_check_and_trigger_migration(struct slb_evdev *slb, struct slb_queue *queue, if (astate->migration.in_progress) return; + /* Already marked for migration */ if (slb->ports[full_port_id].is_loaded) return; - if (pstats->flow_count <= 1) + if (pstats->flow_count <= 1) /* Not enough flows to migrate */ return; now = rte_get_timer_cycles(); - hz = rte_get_timer_hz(); - cooldown_cycles = (hz * SLB_MIGRATION_COOLDOWN_MS) / 1000; + /* Port is still in cooldown period */ if (slb->ports[full_port_id].last_migrated_time != 0 && (now - slb->ports[full_port_id].last_migrated_time) < cooldown_cycles) return; + /* Find flow to migrate*/ flow_to_migrate = slb_find_lightest_flow_on_port( queue, full_port_id, now, cooldown_cycles); if (flow_to_migrate < 0) @@ -261,6 +272,7 @@ slb_check_and_trigger_migration(struct slb_evdev *slb, struct slb_queue *queue, astate->migration.flow_idx = (uint32_t)flow_to_migrate; } +/* Find the target port (one with minimum flows) for flow migration */ static inline int slb_find_migration_target_port(struct slb_evdev *slb, struct slb_queue *queue, struct slb_atomic_state *astate, uint8_t mig_port, @@ -291,15 +303,18 @@ slb_find_migration_target_port(struct slb_evdev *slb, struct slb_queue *queue, return target_port; } +/* Complete the flow migration process from original port to target port */ static inline void slb_complete_flow_migration(struct slb_evdev *slb, struct slb_atomic_state *astate, uint8_t mig_port, int target_port, uint64_t now) { uint32_t f_idx = astate->migration.flow_idx; + /* Switch the flow mapping to the target port */ rte_atomic_store_explicit(&astate->flow_map.flows[f_idx].port_id, target_port, rte_memory_order_release); + /* Switch all events in hold buffer to the target port */ for (uint16_t h = 0; h < astate->hold_count; h++) { if (astate->hold_ports[h] == mig_port && slb_flow_hash(astate->hold_buffer[h].flow_id) == f_idx) { @@ -307,38 +322,45 @@ slb_complete_flow_migration(struct slb_evdev *slb, struct slb_atomic_state *asta } } + /* Update migration timestamps */ astate->flow_last_migrated[f_idx] = now; slb->ports[mig_port].last_migrated_time = now; slb->ports[target_port].last_migrated_time = now; + /* Update flow counts */ if (astate->port_stats[mig_port].flow_count > 0) astate->port_stats[mig_port].flow_count--; astate->port_stats[target_port].flow_count++; astate->migrations_count++; + /* Update migration counters */ slb->ports[mig_port].flow_migrations++; slb->ports[mig_port].migrations_out++; slb->ports[target_port].migrations_in++; + /* Mark migration as complete */ astate->port_stats[mig_port].load_counter = 0; slb->ports[mig_port].is_loaded = false; astate->migration.in_progress = false; } +/* Assign a port to unmapped flow */ static inline uint8_t slb_assign_unmapped_flow_port(struct slb_evdev *slb, struct slb_atomic_state *astate, uint64_t serving_mask, uint16_t num_serving_ports) { uint32_t rr_counter = astate->next_port_rr++; - int port_offset = rr_counter % num_serving_ports; + int port_offset = rr_counter % num_serving_ports; /* Pick next port */ uint64_t temp_mask = serving_mask; uint8_t assigned_port = 0; + /* Convert offset to port ID */ for (int j = 0; j <= port_offset; j++) { assigned_port = rte_ctz64(temp_mask); temp_mask &= (temp_mask - 1); } + /* If next port in RR selection is loaded then find an unloaded port */ if (slb->ports[assigned_port].is_loaded) { uint64_t cand_mask = serving_mask; @@ -355,14 +377,14 @@ slb_assign_unmapped_flow_port(struct slb_evdev *slb, struct slb_atomic_state *as return assigned_port; } -/* If port queue is empty for an atomic queue, pull events from main atomic queue to fill - * up port queues. +/* If per port atomic queue is empty, pull events from the main atomic queue to fill + * up all port queues. Handle migration and hold buffer processing. */ static void slb_atomic_refill(struct slb_port *port, struct slb_queue *queue) { struct slb_evdev *slb = port->slb; - struct rte_event master_events[SLB_MAX_PORT_DEQUEUE_DEPTH]; + struct rte_event rcv_events[SLB_MAX_PORT_DEQUEUE_DEPTH]; uint16_t n_deq; uint16_t i; uint64_t full_ports_mask = 0; @@ -373,28 +395,27 @@ slb_atomic_refill(struct slb_port *port, struct slb_queue *queue) if (unlikely(astate == NULL)) return; + /* Only one thread can refill at a time */ if (!rte_spinlock_trylock(&astate->refill_lock)) return; - /* 0. Detect migration in progress before hold buffer processing */ + /* 0. Handle migration in progress before hold buffer processing */ if (astate->migration.in_progress) { uint8_t mig_port = astate->migration.port_id; uint64_t enq_cnt = astate->port_stats[mig_port].q_enqueued; uint64_t ret_cnt = astate->port_stats[mig_port].q_returned; + /* Check if it is safe to migrate. i.e. no pending events */ if (enq_cnt == ret_cnt) { uint64_t now = rte_get_timer_cycles(); - uint64_t hz = rte_get_timer_hz(); - uint64_t cooldown_cycles = - (hz * SLB_MIGRATION_COOLDOWN_MS) / 1000; int target_port = slb_find_migration_target_port( slb, queue, astate, mig_port, now, - cooldown_cycles); + slb->migration_cooldown_cycles); if (target_port != -1) slb_complete_flow_migration( slb, astate, mig_port, target_port, now); - else { + else { /* If no suitable target port found, cancel migration */ astate->port_stats[mig_port].load_counter = 0; slb->ports[mig_port].is_loaded = false; astate->migration.in_progress = false; @@ -402,10 +423,11 @@ slb_atomic_refill(struct slb_port *port, struct slb_queue *queue) } } + /* Add the migration port to the full ports mask to avoid scheduling events to it */ if (astate->migration.in_progress) full_ports_mask |= (1ULL << astate->migration.port_id); - /* 1. Process Hold Buffer first */ + /* 1. Process Hold Buffer events */ if (astate->hold_count > 0) { uint16_t retained = 0; uint16_t count = astate->hold_count; @@ -415,10 +437,12 @@ slb_atomic_refill(struct slb_port *port, struct slb_queue *queue) uint8_t dest_port_id = astate->hold_ports[i]; uint16_t burst_len = 1; + /* Collect consecutive events for the same destination port */ while ((i + burst_len) < count && astate->hold_ports[i + burst_len] == dest_port_id) burst_len++; + /* If destination port is full, retain the event in hold buffer*/ if (full_ports_mask & (1ULL << dest_port_id)) { if (retained != i) { memmove(&astate->hold_buffer[retained], @@ -433,15 +457,18 @@ slb_atomic_refill(struct slb_port *port, struct slb_queue *queue) continue; } + /* Enqueue events to the destination port */ uint16_t enqueued = rte_event_ring_enqueue_burst( queue->per_port_rings[dest_port_id], &astate->hold_buffer[i], burst_len, NULL); astate->port_stats[dest_port_id].q_enqueued += enqueued; + /* If enqueue was successful then decrement the load counter */ if (enqueued > 0 && astate->port_stats[dest_port_id].load_counter > 0) astate->port_stats[dest_port_id].load_counter--; + /* If enqueue was not successful, retain the event in hold buffer */ if (enqueued < burst_len) { uint16_t rem = burst_len - enqueued; uint16_t start_idx = i + enqueued; @@ -456,6 +483,7 @@ slb_atomic_refill(struct slb_port *port, struct slb_queue *queue) rem); } retained += rem; + /* If no events were enqueued, trigger migration */ if (enqueued == 0) slb_check_and_trigger_migration( slb, queue, astate, dest_port_id); @@ -465,7 +493,11 @@ slb_atomic_refill(struct slb_port *port, struct slb_queue *queue) } astate->hold_count = retained; - if (astate->hold_count > 0) { + /* Return without pulling events from main atomic queue + * if hold buffer does not have enough free space. + */ + if (SLB_HOLD_BUFFER_SIZE - astate->hold_count < + 3 * RTE_DIM(rcv_events)) { rte_spinlock_unlock(&astate->refill_lock); return; } @@ -475,14 +507,13 @@ slb_atomic_refill(struct slb_port *port, struct slb_queue *queue) serving_mask = queue->serving_ports_mask; num_serving_ports = queue->num_serving_ports; - if (num_serving_ports == 0) { - rte_spinlock_unlock(&astate->refill_lock); - return; - } - for (int iter = 0; iter < 2; iter++) { + if (SLB_HOLD_BUFFER_SIZE - astate->hold_count < + RTE_DIM(rcv_events)) + break; + n_deq = rte_event_ring_dequeue_burst( - queue->q_ring, master_events, RTE_DIM(master_events), NULL); + queue->q_ring, rcv_events, RTE_DIM(rcv_events), NULL); if (n_deq == 0) break; @@ -494,14 +525,19 @@ slb_atomic_refill(struct slb_port *port, struct slb_queue *queue) struct rte_event sorted_events[SLB_MAX_PORT_DEQUEUE_DEPTH]; uint64_t batch_ports_mask = 0; + /* Dequeue events from main atomic queue, sort events, + * enqueue events to destination port queues. If destination port queue + * is full, retain events in hold buffer + */ for (i = 0; i < n_deq; i++) { - struct rte_event *ev = &master_events[i]; + struct rte_event *ev = &rcv_events[i]; uint32_t flow_idx = slb_flow_hash(ev->flow_id); int32_t dest_port_id = rte_atomic_load_explicit( &astate->flow_map.flows[flow_idx].port_id, rte_memory_order_acquire); + /* If flow is unmapped, map it to a port */ if (unlikely(dest_port_id == -1)) { dest_port_id = slb_assign_unmapped_flow_port( slb, astate, serving_mask, num_serving_ports); @@ -514,6 +550,7 @@ slb_atomic_refill(struct slb_port *port, struct slb_queue *queue) dest_ports[i] = (uint8_t)dest_port_id; uint64_t p_bit = (1ULL << dest_port_id); + /* Count events for each destination port */ if (!(batch_ports_mask & p_bit)) { batch_ports_mask |= p_bit; port_counts[dest_port_id] = 1; @@ -525,6 +562,7 @@ slb_atomic_refill(struct slb_port *port, struct slb_queue *queue) uint16_t current_offset = 0; uint64_t pmask = batch_ports_mask; + /* Calculate offsets for each destination port */ while (pmask != 0) { uint8_t p = rte_ctz64(pmask); @@ -534,10 +572,11 @@ slb_atomic_refill(struct slb_port *port, struct slb_queue *queue) current_offset += port_counts[p]; } + /* Sort events by destination port */ for (i = 0; i < n_deq; i++) { uint8_t p = dest_ports[i]; - sorted_events[insert_pos[p]++] = master_events[i]; + sorted_events[insert_pos[p]++] = rcv_events[i]; } pmask = batch_ports_mask; @@ -548,40 +587,35 @@ slb_atomic_refill(struct slb_port *port, struct slb_queue *queue) pmask &= (pmask - 1); + /* If destination port is full, retain events in hold buffer */ if (full_ports_mask & (1ULL << p)) { - uint16_t space = SLB_HOLD_BUFFER_SIZE - astate->hold_count; - uint16_t to_copy = RTE_MIN(p_count, space); - - if (to_copy > 0) { - rte_memcpy(&astate->hold_buffer[astate->hold_count], - &sorted_events[p_start], - to_copy * sizeof(struct rte_event)); - memset(&astate->hold_ports[astate->hold_count], p, to_copy); - astate->hold_count += to_copy; - } + rte_memcpy(&astate->hold_buffer[astate->hold_count], + &sorted_events[p_start], + p_count * sizeof(struct rte_event)); + memset(&astate->hold_ports[astate->hold_count], p, p_count); + astate->hold_count += p_count; continue; } + /* Enqueue events to the destination port */ uint16_t enqueued = rte_event_ring_enqueue_burst( queue->per_port_rings[p], &sorted_events[p_start], p_count, NULL); astate->port_stats[p].q_enqueued += enqueued; + /* If all events were enqueued successfully, decrement load counter */ if (enqueued == p_count) { if (unlikely(astate->port_stats[p].load_counter > 0)) astate->port_stats[p].load_counter--; } else { uint16_t rem = p_count - enqueued; - uint16_t space = SLB_HOLD_BUFFER_SIZE - astate->hold_count; - uint16_t to_copy = RTE_MIN(rem, space); - + /* Mark the destination port as full and retain remaining events */ full_ports_mask |= (1ULL << p); - if (to_copy > 0) { - rte_memcpy(&astate->hold_buffer[astate->hold_count], - &sorted_events[p_start + enqueued], - to_copy * sizeof(struct rte_event)); - memset(&astate->hold_ports[astate->hold_count], p, to_copy); - astate->hold_count += to_copy; - } + rte_memcpy(&astate->hold_buffer[astate->hold_count], + &sorted_events[p_start + enqueued], + rem * sizeof(struct rte_event)); + memset(&astate->hold_ports[astate->hold_count], p, rem); + astate->hold_count += rem; + /* If not a single event was enqueued, check for migration */ if (enqueued == 0) slb_check_and_trigger_migration( slb, queue, astate, p); @@ -605,6 +639,7 @@ slb_event_enqueue_burst_generic(struct slb_port *source_port, if (unlikely(events_len == 0)) return 0; + /* If operation types are not known, count them */ if (!op_types_known) { for (uint16_t i = 0; i < events_len; i++) { switch (events[i].op) { @@ -621,7 +656,7 @@ slb_event_enqueue_burst_generic(struct slb_port *source_port, } } - /* Check credit threshold for NEW events */ + /* Check credit threshold if there are NEW events */ if (num_new > 0) { if (unlikely(rte_atomic_load_explicit(&slb->credits_on_loan, rte_memory_order_relaxed) > @@ -633,32 +668,38 @@ slb_event_enqueue_burst_generic(struct slb_port *source_port, return 0; } + /* Return credits for RELEASE events */ if (num_release > 0) slb_port_return_credits(slb, source_port, num_release); + /* Check for release underflow and balance pending releases */ cur_pending = source_port->pending_releases; RTE_VERIFY(num_forward + num_release <= cur_pending); + source_port->pending_releases = cur_pending - (num_forward + num_release); slb_port_enqueue_stats(source_port, num_new, num_forward, num_release); - /* Unified zero-copy fast path: when port has no in-flight ordered/atomic events - * and no release ops. + /* Unified zero-copy fast path: when burst does not have past ordered/atomic + * events being forwarded and there are no RELEASE events. This means no + * history list entries are present. */ if (likely(source_port->history_avail == SLB_PORT_HISTORY_MAX_TOKENS && num_release == 0)) { uint8_t q0 = events[0].queue_id; uint16_t burst_size = 1; + /* Collect events going to the same queue in a burst */ while (burst_size < events_len && events[burst_size].queue_id == q0) burst_size++; + /* Enqueue the entire burst if going to the same queue */ if (likely(burst_size == events_len)) { rte_event_ring_enqueue_burst(slb->queues[q0].q_ring, events, events_len, NULL); slb_port_queue_enqueue_burst_stats(source_port, q0, events_len); - } else { + } else { /* Sort events by destination queue */ uint8_t dest_queues[SLB_MAX_PORT_ENQUEUE_DEPTH]; uint16_t queue_counts[SLB_MAX_QUEUES]; uint16_t insert_pos[SLB_MAX_QUEUES]; @@ -701,6 +742,7 @@ slb_event_enqueue_burst_generic(struct slb_port *source_port, sorted_events[insert_pos[q]++] = events[k]; } + /* Enqueue events to their respective queues */ qmask = batch_queues_mask; while (qmask != 0) { uint8_t q = rte_ctz64(qmask); @@ -716,15 +758,12 @@ slb_event_enqueue_burst_generic(struct slb_port *source_port, } } - if (num_forward > 0) - source_port->pending_releases -= num_forward; - SLB_LOG_DP_PORT_LINE(DEBUG, source_port->id, "%d events accepted.", events_len); return events_len; } - /* General path for bursts with in-flight ordered/atomic events or releases */ + /* General path for bursts with any mix of events */ uint64_t ordered_drain_mask = 0; uint16_t freed_count = 0; uint16_t head = source_port->history_head; @@ -733,14 +772,19 @@ slb_event_enqueue_burst_generic(struct slb_port *source_port, const struct rte_event *enq_events[SLB_MAX_PORT_ENQUEUE_DEPTH]; uint16_t queue_counts[SLB_MAX_QUEUES]; uint64_t batch_queues_mask = 0; + bool has_history = (source_port->history_avail < + SLB_PORT_HISTORY_MAX_TOKENS); for (uint16_t idx = 0; idx < events_len; idx++) { const struct rte_event *ev = &events[idx]; - uint8_t h_idx = (ev->op != RTE_EVENT_OP_NEW) ? ev->impl_opaque : 0; + /* History list index 0 is used by NEW, parallel, and single-link events */ + uint8_t h_idx = (has_history && ev->op != RTE_EVENT_OP_NEW) ? + ev->impl_opaque : 0; struct slb_history_entry *hist = (h_idx != 0 && source_port->history[h_idx].active) ? &source_port->history[h_idx] : NULL; + /* Ordered event is being forwarded/released. */ if (hist != NULL && hist->sched_type == RTE_SCHED_TYPE_ORDERED) { struct slb_queue *orig_q = &slb->queues[hist->orig_queue_id]; @@ -748,26 +792,32 @@ slb_event_enqueue_burst_generic(struct slb_port *source_port, uint16_t slot = hist->seq_slot; if (ev->op == RTE_EVENT_OP_RELEASE) { + /* Release the reorder buffer entry */ rte_atomic_store_explicit( &orig_q->reorder_buffer[slot].state, SLB_REORDER_RELEASED, rte_memory_order_release); } else { + /* Put event in reorder buffer */ orig_q->reorder_buffer[slot].event = *ev; rte_atomic_store_explicit( &orig_q->reorder_buffer[slot].state, SLB_REORDER_READY, rte_memory_order_release); } + /* Mark the ordered queue for draining */ ordered_drain_mask |= (1ULL << hist->orig_queue_id); } + /* Free the history entry */ hist->active = false; hist->next_free = head; head = h_idx; freed_count++; slb_port_queue_enqueue_stats(source_port, ev->queue_id); } else if (ev->op == RTE_EVENT_OP_RELEASE) { + /* Atomic or parallel event being released */ if (hist != NULL) { + /* Count atomic events being returned */ if (hist->sched_type == RTE_SCHED_TYPE_ATOMIC) { struct slb_queue *orig_q = &slb->queues[hist->orig_queue_id]; @@ -776,6 +826,7 @@ slb_event_enqueue_burst_generic(struct slb_port *source_port, orig_q->atomic_state->port_stats[ep].q_returned++; } } + /* Free the history entry */ hist->active = false; hist->next_free = head; head = h_idx; @@ -783,7 +834,9 @@ slb_event_enqueue_burst_generic(struct slb_port *source_port, } slb_port_queue_enqueue_stats(source_port, ev->queue_id); } else { + /* Atomic/Parallel event being forwarded*/ if (hist != NULL) { + /* Count atomic events being returned */ if (hist->sched_type == RTE_SCHED_TYPE_ATOMIC) { struct slb_queue *orig_q = &slb->queues[hist->orig_queue_id]; @@ -792,6 +845,7 @@ slb_event_enqueue_burst_generic(struct slb_port *source_port, orig_q->atomic_state->port_stats[ep].q_returned++; } } + /* Free the history entry */ hist->active = false; hist->next_free = head; head = h_idx; @@ -805,6 +859,7 @@ slb_event_enqueue_burst_generic(struct slb_port *source_port, enq_events[n_enq] = ev; n_enq++; + /* Count the number of events going to each queue */ if (!(batch_queues_mask & q_bit)) { batch_queues_mask |= q_bit; queue_counts[qid] = 1; @@ -814,9 +869,10 @@ slb_event_enqueue_burst_generic(struct slb_port *source_port, } } - if (n_enq > 0) { + if (n_enq > 0) { /* There are events to enqueue */ if (likely((batch_queues_mask & (batch_queues_mask - 1)) == 0 && n_enq == events_len)) { + /* Entire batch is going to a single queue */ uint8_t q = rte_ctz64(batch_queues_mask); rte_event_ring_enqueue_burst(slb->queues[q].q_ring, @@ -830,6 +886,7 @@ slb_event_enqueue_burst_generic(struct slb_port *source_port, uint16_t current_offset = 0; uint64_t qmask = batch_queues_mask; + /* Calculate the offset for each queue */ while (qmask != 0) { uint8_t q = rte_ctz64(qmask); @@ -839,12 +896,14 @@ slb_event_enqueue_burst_generic(struct slb_port *source_port, current_offset += queue_counts[q]; } + /* Sort events by queue */ for (uint16_t k = 0; k < n_enq; k++) { uint8_t q = dest_queues[k]; sorted_events[insert_pos[q]++] = *enq_events[k]; } + /* Enqueue events for each queue */ qmask = batch_queues_mask; while (qmask != 0) { uint8_t q = rte_ctz64(qmask); @@ -861,11 +920,13 @@ slb_event_enqueue_burst_generic(struct slb_port *source_port, } } + /* Update the history list head once after all frees*/ if (freed_count > 0) { source_port->history_head = head; source_port->history_avail += freed_count; } + /* If events are added to reorder buffers, call the drain function */ while (ordered_drain_mask != 0) { uint8_t qid = rte_ctz64(ordered_drain_mask); @@ -873,14 +934,12 @@ slb_event_enqueue_burst_generic(struct slb_port *source_port, slb_reorder_drain(slb, &slb->queues[qid]); } - if (num_forward + num_release > 0) - source_port->pending_releases -= (num_forward + num_release); - SLB_LOG_DP_PORT_LINE(DEBUG, source_port->id, "%d events accepted.", events_len); return events_len; } +/* Enqueue burst of mix event type. (NEW, FORWARDED, RELEASED) */ uint16_t slb_event_enqueue_burst(void *port, const struct rte_event events[], uint16_t events_len) @@ -891,6 +950,7 @@ slb_event_enqueue_burst(void *port, const struct rte_event events[], false, 0, 0, 0); } +/* Enqueue burst of all new events */ uint16_t slb_event_enqueue_new_burst(void *port, const struct rte_event events[], uint16_t events_len) @@ -901,6 +961,7 @@ slb_event_enqueue_new_burst(void *port, const struct rte_event events[], true, events_len, 0, 0); } +/* Enqueue burst of all forwarded events */ uint16_t slb_event_enqueue_forward_burst(void *port, const struct rte_event events[], uint16_t events_len) @@ -911,6 +972,9 @@ slb_event_enqueue_forward_burst(void *port, const struct rte_event events[], true, 0, events_len, 0); } +/* Dequeue events from an ordered queue. Event's slot in the ring is + * used as its sequence number + */ static inline uint16_t slb_ordered_ring_dequeue_burst(struct rte_event_ring *r, struct rte_event *events, @@ -936,6 +1000,7 @@ slb_ordered_ring_dequeue_burst(struct rte_event_ring *r, return n; } +/* Create history entries for events dequeued from a queue_id of type sched_type */ static inline void slb_port_record_dequeued_history(struct slb_port *port, struct rte_event events[], uint16_t dequeued, uint8_t queue_id, @@ -965,35 +1030,31 @@ slb_port_record_dequeued_history(struct slb_port *port, struct rte_event events[ port->history_avail -= dequeued; } +/* Dequeue events from the port */ static inline uint16_t slb_port_dequeue(struct slb_port *port, struct rte_event events[], uint16_t num) { uint16_t dequeued = 0; + /* If port is single-linked, it is simple dequeue from the linked queue */ if (port->is_single_linked) { if (unlikely(port->single_linked_ring == NULL)) return 0; dequeued = rte_event_ring_dequeue_burst(port->single_linked_ring, events, num, NULL); - for (uint16_t j = 0; j < dequeued; j++) { - events[j].sched_type = RTE_SCHED_TYPE_ATOMIC; + for (uint16_t j = 0; j < dequeued; j++) events[j].impl_opaque = 0; - } return dequeued; } - uint64_t active_queues = rte_atomic_load_explicit( - &port->active_queues, rte_memory_order_acquire); - if (!active_queues) - return 0; - for (int pr = 0; pr < SLB_MAX_PRIORITIES; pr++) { int slots = port->num_q[pr]; if (slots == 0) - continue; + continue; /* No queues mapped at this priority */ + /* Start from where we left off */ int start = port->next_q[pr]; if (unlikely(start >= slots)) @@ -1007,25 +1068,18 @@ slb_port_dequeue(struct slb_port *port, struct rte_event events[], struct slb_port_q_entry *qent = &port->q_entries[pr][idx]; uint8_t queue_id = qent->queue_id; - - if (!((active_queues >> queue_id) & 1)) - continue; - struct rte_event_ring *ring_to_dq = qent->dq_ring; if (unlikely(ring_to_dq == NULL)) continue; uint8_t sched_type = qent->schedule_type; - bool is_single_link = qent->is_single_link; - bool is_ordered = !is_single_link && - (sched_type == RTE_SCHED_TYPE_ORDERED); - bool is_atomic = !is_single_link && - (sched_type == RTE_SCHED_TYPE_ATOMIC); + bool is_ordered = (sched_type == RTE_SCHED_TYPE_ORDERED); + bool is_atomic = (sched_type == RTE_SCHED_TYPE_ATOMIC); if (is_ordered || is_atomic) { if (unlikely(port->history_avail == 0)) - continue; + continue; /* No available history entries */ uint16_t dequeue_num = RTE_MIN(num, port->history_avail); @@ -1035,11 +1089,12 @@ slb_port_dequeue(struct slb_port *port, struct rte_event events[], dequeued = slb_ordered_ring_dequeue_burst( ring_to_dq, events, dequeue_num, &start_slot); - } else { + } else { /* Atomic queue */ dequeued = rte_event_ring_dequeue_burst( ring_to_dq, events, dequeue_num, NULL); if (dequeued == 0) { + /* Per port queue is empty, call for refill */ slb_atomic_refill(port, qent->queue); dequeued = rte_event_ring_dequeue_burst( ring_to_dq, events, dequeue_num, @@ -1047,26 +1102,22 @@ slb_port_dequeue(struct slb_port *port, struct rte_event events[], } } + /* Create history entries for dequeued events */ if (dequeued > 0) slb_port_record_dequeued_history( port, events, dequeued, queue_id, sched_type, start_slot, ring_to_dq->r.mask); - } else { + } else { /* Parallel queue */ dequeued = rte_event_ring_dequeue_burst( ring_to_dq, events, num, NULL); - if (is_single_link) { - for (uint16_t j = 0; j < dequeued; j++) { - events[j].sched_type = - RTE_SCHED_TYPE_ATOMIC; - events[j].impl_opaque = 0; - } - } else { - for (uint16_t j = 0; j < dequeued; j++) - events[j].impl_opaque = 0; - } + for (uint16_t j = 0; j < dequeued; j++) + events[j].impl_opaque = 0; } + /* If dequeued non-zero events successfully, move to the next queue + * for the next dequeue call and return + */ if (dequeued > 0) { idx++; port->next_q[pr] = (idx >= slots) ? 0 : idx; @@ -1077,6 +1128,7 @@ slb_port_dequeue(struct slb_port *port, struct rte_event events[], return 0; } +/* Process pending releases for the port */ static inline void slb_port_process_implicit_release(struct slb_port *port, struct slb_evdev *slb, uint16_t pending) @@ -1084,10 +1136,14 @@ slb_port_process_implicit_release(struct slb_port *port, struct slb_evdev *slb, uint16_t head; uint64_t ordered_drain_mask = 0; + slb_port_return_credits(slb, port, pending); + port->pending_releases = 0; + if (port->is_single_linked || port->history_avail >= SLB_PORT_HISTORY_MAX_TOKENS) - goto done; + return; + /* Process all history entries */ head = port->history_head; for (int i = 1; i < SLB_PORT_HISTORY_SIZE; i++) { struct slb_history_entry *hist = &port->history[i]; @@ -1095,6 +1151,7 @@ slb_port_process_implicit_release(struct slb_port *port, struct slb_evdev *slb, if (!hist->active) continue; + /* Ordered releases will free the reorder buffer slot*/ if (hist->sched_type == RTE_SCHED_TYPE_ORDERED) { struct slb_queue *oq = &slb->queues[hist->orig_queue_id]; @@ -1106,6 +1163,7 @@ slb_port_process_implicit_release(struct slb_port *port, struct slb_evdev *slb, ordered_drain_mask |= (1ULL << hist->orig_queue_id); } } else if (hist->sched_type == RTE_SCHED_TYPE_ATOMIC) { + /* Atomic releases are counted as returned events */ struct slb_queue *oq = &slb->queues[hist->orig_queue_id]; if (oq->atomic_state) { @@ -1114,6 +1172,7 @@ slb_port_process_implicit_release(struct slb_port *port, struct slb_evdev *slb, oq->atomic_state->port_stats[ep].q_returned++; } } + /* Free the history entries */ hist->active = false; hist->next_free = head; head = (uint16_t)i; @@ -1121,18 +1180,16 @@ slb_port_process_implicit_release(struct slb_port *port, struct slb_evdev *slb, } port->history_head = head; + /* If there are ordered releases, call the reorder drain function */ while (ordered_drain_mask != 0) { uint8_t qid = rte_ctz64(ordered_drain_mask); ordered_drain_mask &= ~(1ULL << qid); slb_reorder_drain(slb, &slb->queues[qid]); } - -done: - slb_port_return_credits(slb, port, pending); - port->pending_releases = 0; } +/* Eventdev dequeue burst function */ uint16_t slb_event_dequeue_burst(void *p, struct rte_event events[], uint16_t num, uint64_t wait __rte_unused) @@ -1142,6 +1199,7 @@ slb_event_dequeue_burst(void *p, struct rte_event events[], uint16_t num, uint16_t pending = port->pending_releases; uint16_t n; + /* Handle pending releases */ if (port->implicit_release && pending > 0) slb_port_process_implicit_release(port, slb, pending); diff --git a/drivers/event/slb/slb_selftest.c b/drivers/event/slb/slb_selftest.c index 776b6d6ab4..4a072b811b 100644 --- a/drivers/event/slb/slb_selftest.c +++ b/drivers/event/slb/slb_selftest.c @@ -362,12 +362,6 @@ test_ordered_forward(void) cons_rx, rx[i].u64); return -1; } - if (rx[i].sched_type != RTE_SCHED_TYPE_ATOMIC) { - SLB_LOG_LINE(ERR, - "Single-link dequeue sched_type expected %u, got %u", - RTE_SCHED_TYPE_ATOMIC, rx[i].sched_type); - return -1; - } cons_rx++; rx[i].op = RTE_EVENT_OP_RELEASE; } @@ -422,12 +416,6 @@ test_single_link(void) for (i = 0; i < 16; i++) { if (rx[i].u64 != (uint64_t)(5000 + i)) return -1; - if (rx[i].sched_type != RTE_SCHED_TYPE_ATOMIC) { - SLB_LOG_LINE(ERR, - "Single-link dequeue sched_type expected %u, got %u", - RTE_SCHED_TYPE_ATOMIC, rx[i].sched_type); - return -1; - } rx[i].op = RTE_EVENT_OP_RELEASE; } rte_event_enqueue_burst(g_dev_id, PORT_CONS, rx, 16); -- 2.53.0

