1 /* SPDX-License-Identifier: BSD-3-Clause
2 * Copyright(c) 2018 Ericsson AB
10 #include <rte_atomic.h>
11 #include <rte_cycles.h>
12 #include <rte_memcpy.h>
13 #include <rte_random.h>
16 dsw_port_acquire_credits(struct dsw_evdev *dsw, struct dsw_port *port,
19 int32_t inflight_credits = port->inflight_credits;
20 int32_t missing_credits = credits - inflight_credits;
21 int32_t total_on_loan;
23 int32_t acquired_credits;
24 int32_t new_total_on_loan;
26 if (likely(missing_credits <= 0)) {
27 port->inflight_credits -= credits;
31 total_on_loan = rte_atomic32_read(&dsw->credits_on_loan);
32 available = dsw->max_inflight - total_on_loan;
33 acquired_credits = RTE_MAX(missing_credits, DSW_PORT_MIN_CREDITS);
35 if (available < acquired_credits)
38 /* This is a race, no locks are involved, and thus some other
39 * thread can allocate tokens in between the check and the
42 new_total_on_loan = rte_atomic32_add_return(&dsw->credits_on_loan,
45 if (unlikely(new_total_on_loan > dsw->max_inflight)) {
46 /* Some other port took the last credits */
47 rte_atomic32_sub(&dsw->credits_on_loan, acquired_credits);
51 DSW_LOG_DP_PORT(DEBUG, port->id, "Acquired %d tokens from pool.\n",
54 port->inflight_credits += acquired_credits;
55 port->inflight_credits -= credits;
61 dsw_port_return_credits(struct dsw_evdev *dsw, struct dsw_port *port,
64 port->inflight_credits += credits;
66 if (unlikely(port->inflight_credits > DSW_PORT_MAX_CREDITS)) {
67 int32_t leave_credits = DSW_PORT_MIN_CREDITS;
68 int32_t return_credits =
69 port->inflight_credits - leave_credits;
71 port->inflight_credits = leave_credits;
73 rte_atomic32_sub(&dsw->credits_on_loan, return_credits);
75 DSW_LOG_DP_PORT(DEBUG, port->id,
76 "Returned %d tokens to pool.\n",
82 dsw_port_load_record(struct dsw_port *port, unsigned int dequeued)
84 if (dequeued > 0 && port->busy_start == 0)
85 /* work period begins */
86 port->busy_start = rte_get_timer_cycles();
87 else if (dequeued == 0 && port->busy_start > 0) {
88 /* work period ends */
89 uint64_t work_period =
90 rte_get_timer_cycles() - port->busy_start;
91 port->busy_cycles += work_period;
97 dsw_port_load_close_period(struct dsw_port *port, uint64_t now)
99 uint64_t passed = now - port->measurement_start;
100 uint64_t busy_cycles = port->busy_cycles;
102 if (port->busy_start > 0) {
103 busy_cycles += (now - port->busy_start);
104 port->busy_start = now;
107 int16_t load = (DSW_MAX_LOAD * busy_cycles) / passed;
109 port->measurement_start = now;
110 port->busy_cycles = 0;
112 port->total_busy_cycles += busy_cycles;
118 dsw_port_load_update(struct dsw_port *port, uint64_t now)
124 old_load = rte_atomic16_read(&port->load);
126 period_load = dsw_port_load_close_period(port, now);
128 new_load = (period_load + old_load*DSW_OLD_LOAD_WEIGHT) /
129 (DSW_OLD_LOAD_WEIGHT+1);
131 rte_atomic16_set(&port->load, new_load);
135 dsw_port_consider_load_update(struct dsw_port *port, uint64_t now)
137 if (now < port->next_load_update)
140 port->next_load_update = now + port->load_update_interval;
142 dsw_port_load_update(port, now);
146 dsw_port_ctl_enqueue(struct dsw_port *port, struct dsw_ctl_msg *msg)
150 memcpy(&raw_msg, msg, sizeof(*msg));
152 /* there's always room on the ring */
153 while (rte_ring_enqueue(port->ctl_in_ring, raw_msg) != 0)
158 dsw_port_ctl_dequeue(struct dsw_port *port, struct dsw_ctl_msg *msg)
163 rc = rte_ring_dequeue(port->ctl_in_ring, &raw_msg);
166 memcpy(msg, &raw_msg, sizeof(*msg));
172 dsw_port_ctl_broadcast(struct dsw_evdev *dsw, struct dsw_port *source_port,
173 uint8_t type, uint8_t queue_id, uint16_t flow_hash)
176 struct dsw_ctl_msg msg = {
178 .originating_port_id = source_port->id,
179 .queue_id = queue_id,
180 .flow_hash = flow_hash
183 for (port_id = 0; port_id < dsw->num_ports; port_id++)
184 if (port_id != source_port->id)
185 dsw_port_ctl_enqueue(&dsw->ports[port_id], &msg);
189 dsw_port_is_flow_paused(struct dsw_port *port, uint8_t queue_id,
194 for (i = 0; i < port->paused_flows_len; i++) {
195 struct dsw_queue_flow *qf = &port->paused_flows[i];
196 if (qf->queue_id == queue_id &&
197 qf->flow_hash == flow_hash)
204 dsw_port_add_paused_flow(struct dsw_port *port, uint8_t queue_id,
205 uint16_t paused_flow_hash)
207 port->paused_flows[port->paused_flows_len] = (struct dsw_queue_flow) {
208 .queue_id = queue_id,
209 .flow_hash = paused_flow_hash
211 port->paused_flows_len++;
215 dsw_port_remove_paused_flow(struct dsw_port *port, uint8_t queue_id,
216 uint16_t paused_flow_hash)
220 for (i = 0; i < port->paused_flows_len; i++) {
221 struct dsw_queue_flow *qf = &port->paused_flows[i];
223 if (qf->queue_id == queue_id &&
224 qf->flow_hash == paused_flow_hash) {
225 uint16_t last_idx = port->paused_flows_len-1;
227 port->paused_flows[i] =
228 port->paused_flows[last_idx];
229 port->paused_flows_len--;
236 dsw_port_flush_out_buffers(struct dsw_evdev *dsw, struct dsw_port *source_port);
239 dsw_port_handle_pause_flow(struct dsw_evdev *dsw, struct dsw_port *port,
240 uint8_t originating_port_id, uint8_t queue_id,
241 uint16_t paused_flow_hash)
243 struct dsw_ctl_msg cfm = {
245 .originating_port_id = port->id,
246 .queue_id = queue_id,
247 .flow_hash = paused_flow_hash
250 DSW_LOG_DP_PORT(DEBUG, port->id, "Pausing queue_id %d flow_hash %d.\n",
251 queue_id, paused_flow_hash);
253 /* There might be already-scheduled events belonging to the
254 * paused flow in the output buffers.
256 dsw_port_flush_out_buffers(dsw, port);
258 dsw_port_add_paused_flow(port, queue_id, paused_flow_hash);
260 /* Make sure any stores to the original port's in_ring is seen
261 * before the ctl message.
265 dsw_port_ctl_enqueue(&dsw->ports[originating_port_id], &cfm);
269 dsw_find_lowest_load_port(uint8_t *port_ids, uint16_t num_port_ids,
270 uint8_t exclude_port_id, int16_t *port_loads,
271 uint8_t *target_port_id, int16_t *target_load)
273 int16_t candidate_port_id = -1;
274 int16_t candidate_load = DSW_MAX_LOAD;
277 for (i = 0; i < num_port_ids; i++) {
278 uint8_t port_id = port_ids[i];
279 if (port_id != exclude_port_id) {
280 int16_t load = port_loads[port_id];
281 if (candidate_port_id == -1 ||
282 load < candidate_load) {
283 candidate_port_id = port_id;
284 candidate_load = load;
288 *target_port_id = candidate_port_id;
289 *target_load = candidate_load;
292 struct dsw_queue_flow_burst {
293 struct dsw_queue_flow queue_flow;
298 dsw_cmp_burst(const void *v_burst_a, const void *v_burst_b)
300 const struct dsw_queue_flow_burst *burst_a = v_burst_a;
301 const struct dsw_queue_flow_burst *burst_b = v_burst_b;
303 int a_count = burst_a->count;
304 int b_count = burst_b->count;
306 return a_count - b_count;
309 #define DSW_QF_TO_INT(_qf) \
310 ((int)((((_qf)->queue_id)<<16)|((_qf)->flow_hash)))
313 dsw_cmp_qf(const void *v_qf_a, const void *v_qf_b)
315 const struct dsw_queue_flow *qf_a = v_qf_a;
316 const struct dsw_queue_flow *qf_b = v_qf_b;
318 return DSW_QF_TO_INT(qf_a) - DSW_QF_TO_INT(qf_b);
322 dsw_sort_qfs_to_bursts(struct dsw_queue_flow *qfs, uint16_t qfs_len,
323 struct dsw_queue_flow_burst *bursts)
326 struct dsw_queue_flow_burst *current_burst = NULL;
327 uint16_t num_bursts = 0;
329 /* We don't need the stable property, and the list is likely
330 * large enough for qsort() to outperform dsw_stable_sort(),
331 * so we use qsort() here.
333 qsort(qfs, qfs_len, sizeof(qfs[0]), dsw_cmp_qf);
335 /* arrange the (now-consecutive) events into bursts */
336 for (i = 0; i < qfs_len; i++) {
338 dsw_cmp_qf(&qfs[i], ¤t_burst->queue_flow) != 0) {
339 current_burst = &bursts[num_bursts];
340 current_burst->queue_flow = qfs[i];
341 current_burst->count = 0;
344 current_burst->count++;
347 qsort(bursts, num_bursts, sizeof(bursts[0]), dsw_cmp_burst);
353 dsw_retrieve_port_loads(struct dsw_evdev *dsw, int16_t *port_loads,
356 bool below_limit = false;
359 for (i = 0; i < dsw->num_ports; i++) {
360 int16_t load = rte_atomic16_read(&dsw->ports[i].load);
361 if (load < load_limit)
363 port_loads[i] = load;
369 dsw_select_migration_target(struct dsw_evdev *dsw,
370 struct dsw_port *source_port,
371 struct dsw_queue_flow_burst *bursts,
372 uint16_t num_bursts, int16_t *port_loads,
373 int16_t max_load, struct dsw_queue_flow *target_qf,
374 uint8_t *target_port_id)
376 uint16_t source_load = port_loads[source_port->id];
379 for (i = 0; i < num_bursts; i++) {
380 struct dsw_queue_flow *qf = &bursts[i].queue_flow;
382 if (dsw_port_is_flow_paused(source_port, qf->queue_id,
386 struct dsw_queue *queue = &dsw->queues[qf->queue_id];
389 dsw_find_lowest_load_port(queue->serving_ports,
390 queue->num_serving_ports,
391 source_port->id, port_loads,
392 target_port_id, &target_load);
394 if (target_load < source_load &&
395 target_load < max_load) {
401 DSW_LOG_DP_PORT(DEBUG, source_port->id, "For the %d flows considered, "
402 "no target port found with load less than %d.\n",
403 num_bursts, DSW_LOAD_TO_PERCENT(max_load));
409 dsw_schedule(struct dsw_evdev *dsw, uint8_t queue_id, uint16_t flow_hash)
411 struct dsw_queue *queue = &dsw->queues[queue_id];
414 if (queue->num_serving_ports > 1)
415 port_id = queue->flow_to_port_map[flow_hash];
417 /* A single-link queue, or atomic/ordered/parallel but
418 * with just a single serving port.
420 port_id = queue->serving_ports[0];
422 DSW_LOG_DP(DEBUG, "Event with queue_id %d flow_hash %d is scheduled "
423 "to port %d.\n", queue_id, flow_hash, port_id);
429 dsw_port_transmit_buffered(struct dsw_evdev *dsw, struct dsw_port *source_port,
430 uint8_t dest_port_id)
432 struct dsw_port *dest_port = &(dsw->ports[dest_port_id]);
433 uint16_t *buffer_len = &source_port->out_buffer_len[dest_port_id];
434 struct rte_event *buffer = source_port->out_buffer[dest_port_id];
435 uint16_t enqueued = 0;
437 if (*buffer_len == 0)
440 /* The rings are dimensioned to fit all in-flight events (even
441 * on a single ring), so looping will work.
445 rte_event_ring_enqueue_burst(dest_port->in_ring,
447 *buffer_len-enqueued,
449 } while (unlikely(enqueued != *buffer_len));
455 dsw_port_get_parallel_flow_id(struct dsw_port *port)
457 uint16_t flow_id = port->next_parallel_flow_id;
459 port->next_parallel_flow_id =
460 (port->next_parallel_flow_id + 1) % DSW_PARALLEL_FLOWS;
466 dsw_port_buffer_paused(struct dsw_port *port,
467 const struct rte_event *paused_event)
469 port->paused_events[port->paused_events_len] = *paused_event;
470 port->paused_events_len++;
474 dsw_port_buffer_non_paused(struct dsw_evdev *dsw, struct dsw_port *source_port,
475 uint8_t dest_port_id, const struct rte_event *event)
477 struct rte_event *buffer = source_port->out_buffer[dest_port_id];
478 uint16_t *buffer_len = &source_port->out_buffer_len[dest_port_id];
480 if (*buffer_len == DSW_MAX_PORT_OUT_BUFFER)
481 dsw_port_transmit_buffered(dsw, source_port, dest_port_id);
483 buffer[*buffer_len] = *event;
488 #define DSW_FLOW_ID_BITS (24)
490 dsw_flow_id_hash(uint32_t flow_id)
496 hash ^= ((flow_id >> offset) & DSW_MAX_FLOWS_MASK);
497 offset += DSW_MAX_FLOWS_BITS;
498 } while (offset < DSW_FLOW_ID_BITS);
504 dsw_port_buffer_parallel(struct dsw_evdev *dsw, struct dsw_port *source_port,
505 struct rte_event event)
507 uint8_t dest_port_id;
509 event.flow_id = dsw_port_get_parallel_flow_id(source_port);
511 dest_port_id = dsw_schedule(dsw, event.queue_id,
512 dsw_flow_id_hash(event.flow_id));
514 dsw_port_buffer_non_paused(dsw, source_port, dest_port_id, &event);
518 dsw_port_buffer_event(struct dsw_evdev *dsw, struct dsw_port *source_port,
519 const struct rte_event *event)
522 uint8_t dest_port_id;
524 if (unlikely(dsw->queues[event->queue_id].schedule_type ==
525 RTE_SCHED_TYPE_PARALLEL)) {
526 dsw_port_buffer_parallel(dsw, source_port, *event);
530 flow_hash = dsw_flow_id_hash(event->flow_id);
532 if (unlikely(dsw_port_is_flow_paused(source_port, event->queue_id,
534 dsw_port_buffer_paused(source_port, event);
538 dest_port_id = dsw_schedule(dsw, event->queue_id, flow_hash);
540 dsw_port_buffer_non_paused(dsw, source_port, dest_port_id, event);
544 dsw_port_flush_paused_events(struct dsw_evdev *dsw,
545 struct dsw_port *source_port,
546 uint8_t queue_id, uint16_t paused_flow_hash)
548 uint16_t paused_events_len = source_port->paused_events_len;
549 struct rte_event paused_events[paused_events_len];
550 uint8_t dest_port_id;
553 if (paused_events_len == 0)
556 if (dsw_port_is_flow_paused(source_port, queue_id, paused_flow_hash))
559 rte_memcpy(paused_events, source_port->paused_events,
560 paused_events_len * sizeof(struct rte_event));
562 source_port->paused_events_len = 0;
564 dest_port_id = dsw_schedule(dsw, queue_id, paused_flow_hash);
566 for (i = 0; i < paused_events_len; i++) {
567 struct rte_event *event = &paused_events[i];
570 flow_hash = dsw_flow_id_hash(event->flow_id);
572 if (event->queue_id == queue_id &&
573 flow_hash == paused_flow_hash)
574 dsw_port_buffer_non_paused(dsw, source_port,
575 dest_port_id, event);
577 dsw_port_buffer_paused(source_port, event);
582 dsw_port_migration_stats(struct dsw_port *port)
584 uint64_t migration_latency;
586 migration_latency = (rte_get_timer_cycles() - port->migration_start);
587 port->migration_latency += migration_latency;
592 dsw_port_end_migration(struct dsw_evdev *dsw, struct dsw_port *port)
594 uint8_t queue_id = port->migration_target_qf.queue_id;
595 uint16_t flow_hash = port->migration_target_qf.flow_hash;
597 port->migration_state = DSW_MIGRATION_STATE_IDLE;
598 port->seen_events_len = 0;
600 dsw_port_migration_stats(port);
602 if (dsw->queues[queue_id].schedule_type != RTE_SCHED_TYPE_PARALLEL) {
603 dsw_port_remove_paused_flow(port, queue_id, flow_hash);
604 dsw_port_flush_paused_events(dsw, port, queue_id, flow_hash);
607 DSW_LOG_DP_PORT(DEBUG, port->id, "Migration completed for queue_id "
608 "%d flow_hash %d.\n", queue_id, flow_hash);
612 dsw_port_consider_migration(struct dsw_evdev *dsw,
613 struct dsw_port *source_port,
616 bool any_port_below_limit;
617 struct dsw_queue_flow *seen_events = source_port->seen_events;
618 uint16_t seen_events_len = source_port->seen_events_len;
619 struct dsw_queue_flow_burst bursts[DSW_MAX_EVENTS_RECORDED];
621 int16_t source_port_load;
622 int16_t port_loads[dsw->num_ports];
624 if (now < source_port->next_migration)
627 if (dsw->num_ports == 1)
630 DSW_LOG_DP_PORT(DEBUG, source_port->id, "Considering migration.\n");
632 /* Randomize interval to avoid having all threads considering
633 * migration at the same in point in time, which might lead to
634 * all choosing the same target port.
636 source_port->next_migration = now +
637 source_port->migration_interval / 2 +
638 rte_rand() % source_port->migration_interval;
640 if (source_port->migration_state != DSW_MIGRATION_STATE_IDLE) {
641 DSW_LOG_DP_PORT(DEBUG, source_port->id,
642 "Migration already in progress.\n");
646 /* For simplicity, avoid migration in the unlikely case there
647 * is still events to consume in the in_buffer (from the last
650 if (source_port->in_buffer_len > 0) {
651 DSW_LOG_DP_PORT(DEBUG, source_port->id, "There are still "
652 "events in the input buffer.\n");
656 source_port_load = rte_atomic16_read(&source_port->load);
657 if (source_port_load < DSW_MIN_SOURCE_LOAD_FOR_MIGRATION) {
658 DSW_LOG_DP_PORT(DEBUG, source_port->id,
659 "Load %d is below threshold level %d.\n",
660 DSW_LOAD_TO_PERCENT(source_port_load),
661 DSW_LOAD_TO_PERCENT(DSW_MIN_SOURCE_LOAD_FOR_MIGRATION));
665 /* Avoid starting any expensive operations (sorting etc), in
666 * case of a scenario with all ports above the load limit.
668 any_port_below_limit =
669 dsw_retrieve_port_loads(dsw, port_loads,
670 DSW_MAX_TARGET_LOAD_FOR_MIGRATION);
671 if (!any_port_below_limit) {
672 DSW_LOG_DP_PORT(DEBUG, source_port->id,
673 "Candidate target ports are all too highly "
678 /* Sort flows into 'bursts' to allow attempting to migrating
679 * small (but still active) flows first - this it to avoid
680 * having large flows moving around the worker cores too much
681 * (to avoid cache misses, among other things). Of course, the
682 * number of recorded events (queue+flow ids) are limited, and
683 * provides only a snapshot, so only so many conclusions can
684 * be drawn from this data.
686 num_bursts = dsw_sort_qfs_to_bursts(seen_events, seen_events_len,
688 /* For non-big-little systems, there's no point in moving the
691 if (num_bursts < 2) {
692 DSW_LOG_DP_PORT(DEBUG, source_port->id, "Only a single flow "
693 "queue_id %d flow_hash %d has been seen.\n",
694 bursts[0].queue_flow.queue_id,
695 bursts[0].queue_flow.flow_hash);
699 /* The strategy is to first try to find a flow to move to a
700 * port with low load (below the migration-attempt
701 * threshold). If that fails, we try to find a port which is
702 * below the max threshold, and also less loaded than this
705 if (!dsw_select_migration_target(dsw, source_port, bursts, num_bursts,
707 DSW_MIN_SOURCE_LOAD_FOR_MIGRATION,
708 &source_port->migration_target_qf,
709 &source_port->migration_target_port_id)
711 !dsw_select_migration_target(dsw, source_port, bursts, num_bursts,
713 DSW_MAX_TARGET_LOAD_FOR_MIGRATION,
714 &source_port->migration_target_qf,
715 &source_port->migration_target_port_id))
718 DSW_LOG_DP_PORT(DEBUG, source_port->id, "Migrating queue_id %d "
719 "flow_hash %d from port %d to port %d.\n",
720 source_port->migration_target_qf.queue_id,
721 source_port->migration_target_qf.flow_hash,
722 source_port->id, source_port->migration_target_port_id);
724 /* We have a winner. */
726 source_port->migration_state = DSW_MIGRATION_STATE_PAUSING;
727 source_port->migration_start = rte_get_timer_cycles();
729 /* No need to go through the whole pause procedure for
730 * parallel queues, since atomic/ordered semantics need not to
734 if (dsw->queues[source_port->migration_target_qf.queue_id].schedule_type
735 == RTE_SCHED_TYPE_PARALLEL) {
736 uint8_t queue_id = source_port->migration_target_qf.queue_id;
737 uint16_t flow_hash = source_port->migration_target_qf.flow_hash;
738 uint8_t dest_port_id = source_port->migration_target_port_id;
740 /* Single byte-sized stores are always atomic. */
741 dsw->queues[queue_id].flow_to_port_map[flow_hash] =
745 dsw_port_end_migration(dsw, source_port);
750 /* There might be 'loopback' events already scheduled in the
753 dsw_port_flush_out_buffers(dsw, source_port);
755 dsw_port_add_paused_flow(source_port,
756 source_port->migration_target_qf.queue_id,
757 source_port->migration_target_qf.flow_hash);
759 dsw_port_ctl_broadcast(dsw, source_port, DSW_CTL_PAUS_REQ,
760 source_port->migration_target_qf.queue_id,
761 source_port->migration_target_qf.flow_hash);
762 source_port->cfm_cnt = 0;
766 dsw_port_flush_paused_events(struct dsw_evdev *dsw,
767 struct dsw_port *source_port,
768 uint8_t queue_id, uint16_t paused_flow_hash);
771 dsw_port_handle_unpause_flow(struct dsw_evdev *dsw, struct dsw_port *port,
772 uint8_t originating_port_id, uint8_t queue_id,
773 uint16_t paused_flow_hash)
775 struct dsw_ctl_msg cfm = {
777 .originating_port_id = port->id,
778 .queue_id = queue_id,
779 .flow_hash = paused_flow_hash
782 DSW_LOG_DP_PORT(DEBUG, port->id, "Un-pausing queue_id %d flow_hash %d.\n",
783 queue_id, paused_flow_hash);
785 dsw_port_remove_paused_flow(port, queue_id, paused_flow_hash);
789 dsw_port_ctl_enqueue(&dsw->ports[originating_port_id], &cfm);
791 dsw_port_flush_paused_events(dsw, port, queue_id, paused_flow_hash);
794 #define FORWARD_BURST_SIZE (32)
797 dsw_port_forward_migrated_flow(struct dsw_port *source_port,
798 struct rte_event_ring *dest_ring,
802 uint16_t events_left;
804 /* Control ring message should been seen before the ring count
805 * is read on the port's in_ring.
809 events_left = rte_event_ring_count(source_port->in_ring);
811 while (events_left > 0) {
812 uint16_t in_burst_size =
813 RTE_MIN(FORWARD_BURST_SIZE, events_left);
814 struct rte_event in_burst[in_burst_size];
818 in_len = rte_event_ring_dequeue_burst(source_port->in_ring,
820 in_burst_size, NULL);
821 /* No need to care about bursting forwarded events (to
822 * the destination port's in_ring), since migration
823 * doesn't happen very often, and also the majority of
824 * the dequeued events will likely *not* be forwarded.
826 for (i = 0; i < in_len; i++) {
827 struct rte_event *e = &in_burst[i];
828 if (e->queue_id == queue_id &&
829 dsw_flow_id_hash(e->flow_id) == flow_hash) {
830 while (rte_event_ring_enqueue_burst(dest_ring,
835 uint16_t last_idx = source_port->in_buffer_len;
836 source_port->in_buffer[last_idx] = *e;
837 source_port->in_buffer_len++;
841 events_left -= in_len;
846 dsw_port_move_migrating_flow(struct dsw_evdev *dsw,
847 struct dsw_port *source_port)
849 uint8_t queue_id = source_port->migration_target_qf.queue_id;
850 uint16_t flow_hash = source_port->migration_target_qf.flow_hash;
851 uint8_t dest_port_id = source_port->migration_target_port_id;
852 struct dsw_port *dest_port = &dsw->ports[dest_port_id];
854 dsw_port_flush_out_buffers(dsw, source_port);
858 dsw->queues[queue_id].flow_to_port_map[flow_hash] =
861 dsw_port_forward_migrated_flow(source_port, dest_port->in_ring,
862 queue_id, flow_hash);
864 /* Flow table update and migration destination port's enqueues
865 * must be seen before the control message.
869 dsw_port_ctl_broadcast(dsw, source_port, DSW_CTL_UNPAUS_REQ, queue_id,
871 source_port->cfm_cnt = 0;
872 source_port->migration_state = DSW_MIGRATION_STATE_UNPAUSING;
876 dsw_port_handle_confirm(struct dsw_evdev *dsw, struct dsw_port *port)
880 if (port->cfm_cnt == (dsw->num_ports-1)) {
881 switch (port->migration_state) {
882 case DSW_MIGRATION_STATE_PAUSING:
883 DSW_LOG_DP_PORT(DEBUG, port->id, "Going into forwarding "
884 "migration state.\n");
885 port->migration_state = DSW_MIGRATION_STATE_FORWARDING;
887 case DSW_MIGRATION_STATE_UNPAUSING:
888 dsw_port_end_migration(dsw, port);
898 dsw_port_ctl_process(struct dsw_evdev *dsw, struct dsw_port *port)
900 struct dsw_ctl_msg msg;
902 /* So any table loads happens before the ring dequeue, in the
903 * case of a 'paus' message.
907 if (dsw_port_ctl_dequeue(port, &msg) == 0) {
909 case DSW_CTL_PAUS_REQ:
910 dsw_port_handle_pause_flow(dsw, port,
911 msg.originating_port_id,
912 msg.queue_id, msg.flow_hash);
914 case DSW_CTL_UNPAUS_REQ:
915 dsw_port_handle_unpause_flow(dsw, port,
916 msg.originating_port_id,
921 dsw_port_handle_confirm(dsw, port);
928 dsw_port_note_op(struct dsw_port *port, uint16_t num_events)
930 /* To pull the control ring reasonbly often on busy ports,
931 * each dequeued/enqueued event is considered an 'op' too.
933 port->ops_since_bg_task += (num_events+1);
937 dsw_port_bg_process(struct dsw_evdev *dsw, struct dsw_port *port)
939 if (unlikely(port->migration_state == DSW_MIGRATION_STATE_FORWARDING &&
940 port->pending_releases == 0))
941 dsw_port_move_migrating_flow(dsw, port);
943 /* Polling the control ring is relatively inexpensive, and
944 * polling it often helps bringing down migration latency, so
945 * do this for every iteration.
947 dsw_port_ctl_process(dsw, port);
949 /* To avoid considering migration and flushing output buffers
950 * on every dequeue/enqueue call, the scheduler only performs
951 * such 'background' tasks every nth
952 * (i.e. DSW_MAX_PORT_OPS_PER_BG_TASK) operation.
954 if (unlikely(port->ops_since_bg_task >= DSW_MAX_PORT_OPS_PER_BG_TASK)) {
957 now = rte_get_timer_cycles();
961 /* Logic to avoid having events linger in the output
964 dsw_port_flush_out_buffers(dsw, port);
966 dsw_port_consider_load_update(port, now);
968 dsw_port_consider_migration(dsw, port, now);
970 port->ops_since_bg_task = 0;
975 dsw_port_flush_out_buffers(struct dsw_evdev *dsw, struct dsw_port *source_port)
977 uint16_t dest_port_id;
979 for (dest_port_id = 0; dest_port_id < dsw->num_ports; dest_port_id++)
980 dsw_port_transmit_buffered(dsw, source_port, dest_port_id);
984 dsw_event_enqueue(void *port, const struct rte_event *ev)
986 return dsw_event_enqueue_burst(port, ev, unlikely(ev == NULL) ? 0 : 1);
989 static __rte_always_inline uint16_t
990 dsw_event_enqueue_burst_generic(void *port, const struct rte_event events[],
991 uint16_t events_len, bool op_types_known,
992 uint16_t num_new, uint16_t num_release,
993 uint16_t num_non_release)
995 struct dsw_port *source_port = port;
996 struct dsw_evdev *dsw = source_port->dsw;
1000 DSW_LOG_DP_PORT(DEBUG, source_port->id, "Attempting to enqueue %d "
1001 "events to port %d.\n", events_len, source_port->id);
1003 dsw_port_bg_process(dsw, source_port);
1005 /* XXX: For performance (=ring efficiency) reasons, the
1006 * scheduler relies on internal non-ring buffers instead of
1007 * immediately sending the event to the destination ring. For
1008 * a producer that doesn't intend to produce or consume any
1009 * more events, the scheduler provides a way to flush the
1010 * buffer, by means of doing an enqueue of zero events. In
1011 * addition, a port cannot be left "unattended" (e.g. unused)
1012 * for long periods of time, since that would stall
1013 * migration. Eventdev API extensions to provide a cleaner way
1014 * to archieve both of these functions should be
1017 if (unlikely(events_len == 0)) {
1018 dsw_port_note_op(source_port, DSW_MAX_PORT_OPS_PER_BG_TASK);
1022 if (unlikely(events_len > source_port->enqueue_depth))
1023 events_len = source_port->enqueue_depth;
1025 dsw_port_note_op(source_port, events_len);
1027 if (!op_types_known)
1028 for (i = 0; i < events_len; i++) {
1029 switch (events[i].op) {
1030 case RTE_EVENT_OP_RELEASE:
1033 case RTE_EVENT_OP_NEW:
1035 /* Falls through. */
1042 /* Technically, we could allow the non-new events up to the
1043 * first new event in the array into the system, but for
1044 * simplicity reasons, we deny the whole burst if the port is
1045 * above the water mark.
1047 if (unlikely(num_new > 0 && rte_atomic32_read(&dsw->credits_on_loan) >
1048 source_port->new_event_threshold))
1051 enough_credits = dsw_port_acquire_credits(dsw, source_port,
1053 if (unlikely(!enough_credits))
1056 source_port->pending_releases -= num_release;
1058 for (i = 0; i < events_len; i++) {
1059 const struct rte_event *event = &events[i];
1061 if (likely(num_release == 0 ||
1062 event->op != RTE_EVENT_OP_RELEASE))
1063 dsw_port_buffer_event(dsw, source_port, event);
1066 DSW_LOG_DP_PORT(DEBUG, source_port->id, "%d non-release events "
1067 "accepted.\n", num_non_release);
1069 return num_non_release;
1073 dsw_event_enqueue_burst(void *port, const struct rte_event events[],
1074 uint16_t events_len)
1076 return dsw_event_enqueue_burst_generic(port, events, events_len, false,
1081 dsw_event_enqueue_new_burst(void *port, const struct rte_event events[],
1082 uint16_t events_len)
1084 return dsw_event_enqueue_burst_generic(port, events, events_len, true,
1085 events_len, 0, events_len);
1089 dsw_event_enqueue_forward_burst(void *port, const struct rte_event events[],
1090 uint16_t events_len)
1092 return dsw_event_enqueue_burst_generic(port, events, events_len, true,
1097 dsw_event_dequeue(void *port, struct rte_event *events, uint64_t wait)
1099 return dsw_event_dequeue_burst(port, events, 1, wait);
1103 dsw_port_record_seen_events(struct dsw_port *port, struct rte_event *events,
1108 for (i = 0; i < num; i++) {
1109 uint16_t l_idx = port->seen_events_idx;
1110 struct dsw_queue_flow *qf = &port->seen_events[l_idx];
1111 struct rte_event *event = &events[i];
1112 qf->queue_id = event->queue_id;
1113 qf->flow_hash = dsw_flow_id_hash(event->flow_id);
1115 port->seen_events_idx = (l_idx+1) % DSW_MAX_EVENTS_RECORDED;
1118 if (unlikely(port->seen_events_len != DSW_MAX_EVENTS_RECORDED))
1119 port->seen_events_len =
1120 RTE_MIN(port->seen_events_len + num,
1121 DSW_MAX_EVENTS_RECORDED);
1125 dsw_port_dequeue_burst(struct dsw_port *port, struct rte_event *events,
1128 struct dsw_port *source_port = port;
1129 struct dsw_evdev *dsw = source_port->dsw;
1131 dsw_port_ctl_process(dsw, source_port);
1133 if (unlikely(port->in_buffer_len > 0)) {
1134 uint16_t dequeued = RTE_MIN(num, port->in_buffer_len);
1136 rte_memcpy(events, &port->in_buffer[port->in_buffer_start],
1137 dequeued * sizeof(struct rte_event));
1139 port->in_buffer_start += dequeued;
1140 port->in_buffer_len -= dequeued;
1142 if (port->in_buffer_len == 0)
1143 port->in_buffer_start = 0;
1148 return rte_event_ring_dequeue_burst(port->in_ring, events, num, NULL);
1152 dsw_event_dequeue_burst(void *port, struct rte_event *events, uint16_t num,
1153 uint64_t wait __rte_unused)
1155 struct dsw_port *source_port = port;
1156 struct dsw_evdev *dsw = source_port->dsw;
1159 source_port->pending_releases = 0;
1161 dsw_port_bg_process(dsw, source_port);
1163 if (unlikely(num > source_port->dequeue_depth))
1164 num = source_port->dequeue_depth;
1166 dequeued = dsw_port_dequeue_burst(source_port, events, num);
1168 source_port->pending_releases = dequeued;
1170 dsw_port_load_record(source_port, dequeued);
1172 dsw_port_note_op(source_port, dequeued);
1175 DSW_LOG_DP_PORT(DEBUG, source_port->id, "Dequeued %d events.\n",
1178 dsw_port_return_credits(dsw, source_port, dequeued);
1180 /* One potential optimization one might think of is to
1181 * add a migration state (prior to 'pausing'), and
1182 * only record seen events when the port is in this
1183 * state (and transit to 'pausing' when enough events
1184 * have been gathered). However, that schema doesn't
1185 * seem to improve performance.
1187 dsw_port_record_seen_events(port, events, dequeued);
1189 /* XXX: Assuming the port can't produce any more work,
1190 * consider flushing the output buffer, on dequeued ==