xref: /dpdk/drivers/event/sw/sw_evdev_scheduler.c (revision 5078a8f37d82f3b54cf373389b6754e2bafd584f)
15566a3e3SBruce Richardson /* SPDX-License-Identifier: BSD-3-Clause
25566a3e3SBruce Richardson  * Copyright(c) 2016-2017 Intel Corporation
3617995dfSBruce Richardson  */
4617995dfSBruce Richardson 
5617995dfSBruce Richardson #include <rte_ring.h>
6617995dfSBruce Richardson #include <rte_hash_crc.h>
786aed50aSBruce Richardson #include <rte_event_ring.h>
8617995dfSBruce Richardson #include "sw_evdev.h"
9dca926caSGage Eads #include "iq_chunk.h"
1070207f35SRadu Nicolau #include "event_ring.h"
11617995dfSBruce Richardson 
12617995dfSBruce Richardson #define SW_IQS_MASK (SW_IQS_MAX-1)
13617995dfSBruce Richardson 
14617995dfSBruce Richardson /* Retrieve the highest priority IQ or -1 if no pkts available. Doing the
15617995dfSBruce Richardson  * CLZ twice is faster than caching the value due to data dependencies
16617995dfSBruce Richardson  */
17617995dfSBruce Richardson #define PKT_MASK_TO_IQ(pkts) \
183d4e27fdSDavid Marchand 	(rte_ctz32(pkts | (1 << SW_IQS_MAX)))
19617995dfSBruce Richardson 
20617995dfSBruce Richardson #if SW_IQS_MAX != 4
21617995dfSBruce Richardson #error Misconfigured PRIO_TO_IQ caused by SW_IQS_MAX value change
22617995dfSBruce Richardson #endif
23617995dfSBruce Richardson #define PRIO_TO_IQ(prio) (prio >> 6)
24617995dfSBruce Richardson 
25617995dfSBruce Richardson #define MAX_PER_IQ_DEQUEUE 48
26617995dfSBruce Richardson #define FLOWID_MASK (SW_QID_NUM_FIDS-1)
277904c82aSHarry van Haaren /* use cheap bit mixing, we only need to lose a few bits */
287904c82aSHarry van Haaren #define SW_HASH_FLOWID(f) (((f) ^ (f >> 10)) & FLOWID_MASK)
29617995dfSBruce Richardson 
3070207f35SRadu Nicolau 
31617995dfSBruce Richardson static inline uint32_t
sw_schedule_atomic_to_cq(struct sw_evdev * sw,struct sw_qid * const qid,uint32_t iq_num,unsigned int count)32617995dfSBruce Richardson sw_schedule_atomic_to_cq(struct sw_evdev *sw, struct sw_qid * const qid,
33617995dfSBruce Richardson 		uint32_t iq_num, unsigned int count)
34617995dfSBruce Richardson {
35617995dfSBruce Richardson 	struct rte_event qes[MAX_PER_IQ_DEQUEUE]; /* count <= MAX */
36617995dfSBruce Richardson 	struct rte_event blocked_qes[MAX_PER_IQ_DEQUEUE];
37617995dfSBruce Richardson 	uint32_t nb_blocked = 0;
38617995dfSBruce Richardson 	uint32_t i;
39617995dfSBruce Richardson 
40617995dfSBruce Richardson 	if (count > MAX_PER_IQ_DEQUEUE)
41617995dfSBruce Richardson 		count = MAX_PER_IQ_DEQUEUE;
42617995dfSBruce Richardson 
43617995dfSBruce Richardson 	/* This is the QID ID. The QID ID is static, hence it can be
44617995dfSBruce Richardson 	 * used to identify the stage of processing in history lists etc
45617995dfSBruce Richardson 	 */
46617995dfSBruce Richardson 	uint32_t qid_id = qid->id;
47617995dfSBruce Richardson 
48dca926caSGage Eads 	iq_dequeue_burst(sw, &qid->iq[iq_num], qes, count);
49617995dfSBruce Richardson 	for (i = 0; i < count; i++) {
50617995dfSBruce Richardson 		const struct rte_event *qe = &qes[i];
517904c82aSHarry van Haaren 		const uint16_t flow_id = SW_HASH_FLOWID(qes[i].flow_id);
52617995dfSBruce Richardson 		struct sw_fid_t *fid = &qid->fids[flow_id];
53617995dfSBruce Richardson 		int cq = fid->cq;
54617995dfSBruce Richardson 
55617995dfSBruce Richardson 		if (cq < 0) {
56733fc6caSHarry van Haaren 			uint32_t cq_idx;
57733fc6caSHarry van Haaren 			if (qid->cq_next_tx >= qid->cq_num_mapped_cqs)
58617995dfSBruce Richardson 				qid->cq_next_tx = 0;
59733fc6caSHarry van Haaren 			cq_idx = qid->cq_next_tx++;
60733fc6caSHarry van Haaren 
61617995dfSBruce Richardson 			cq = qid->cq_map[cq_idx];
62617995dfSBruce Richardson 
63617995dfSBruce Richardson 			/* find least used */
64617995dfSBruce Richardson 			int cq_free_cnt = sw->cq_ring_space[cq];
65617995dfSBruce Richardson 			for (cq_idx = 0; cq_idx < qid->cq_num_mapped_cqs;
66617995dfSBruce Richardson 					cq_idx++) {
67617995dfSBruce Richardson 				int test_cq = qid->cq_map[cq_idx];
68617995dfSBruce Richardson 				int test_cq_free = sw->cq_ring_space[test_cq];
69617995dfSBruce Richardson 				if (test_cq_free > cq_free_cnt) {
70617995dfSBruce Richardson 					cq = test_cq;
71617995dfSBruce Richardson 					cq_free_cnt = test_cq_free;
72617995dfSBruce Richardson 				}
73617995dfSBruce Richardson 			}
74617995dfSBruce Richardson 
75617995dfSBruce Richardson 			fid->cq = cq; /* this pins early */
76617995dfSBruce Richardson 		}
77617995dfSBruce Richardson 
78617995dfSBruce Richardson 		if (sw->cq_ring_space[cq] == 0 ||
79617995dfSBruce Richardson 				sw->ports[cq].inflights == SW_PORT_HIST_LIST) {
80617995dfSBruce Richardson 			blocked_qes[nb_blocked++] = *qe;
81617995dfSBruce Richardson 			continue;
82617995dfSBruce Richardson 		}
83617995dfSBruce Richardson 
84617995dfSBruce Richardson 		struct sw_port *p = &sw->ports[cq];
85617995dfSBruce Richardson 
86617995dfSBruce Richardson 		/* at this point we can queue up the packet on the cq_buf */
87617995dfSBruce Richardson 		fid->pcount++;
88617995dfSBruce Richardson 		p->cq_buf[p->cq_buf_count++] = *qe;
89617995dfSBruce Richardson 		p->inflights++;
90617995dfSBruce Richardson 		sw->cq_ring_space[cq]--;
91617995dfSBruce Richardson 
92617995dfSBruce Richardson 		int head = (p->hist_head++ & (SW_PORT_HIST_LIST-1));
93*5078a8f3SHarry van Haaren 		p->hist_list[head] = (struct sw_hist_list_entry) {
94*5078a8f3SHarry van Haaren 			.qid = qid_id,
95*5078a8f3SHarry van Haaren 			.fid = flow_id,
96*5078a8f3SHarry van Haaren 		};
97617995dfSBruce Richardson 
98617995dfSBruce Richardson 		p->stats.tx_pkts++;
99617995dfSBruce Richardson 		qid->stats.tx_pkts++;
1000e1eadd0SHarry van Haaren 		qid->to_port[cq]++;
101617995dfSBruce Richardson 
102617995dfSBruce Richardson 		/* if we just filled in the last slot, flush the buffer */
103617995dfSBruce Richardson 		if (sw->cq_ring_space[cq] == 0) {
10486aed50aSBruce Richardson 			struct rte_event_ring *worker = p->cq_worker_ring;
10586aed50aSBruce Richardson 			rte_event_ring_enqueue_burst(worker, p->cq_buf,
106617995dfSBruce Richardson 					p->cq_buf_count,
107617995dfSBruce Richardson 					&sw->cq_ring_space[cq]);
108617995dfSBruce Richardson 			p->cq_buf_count = 0;
109617995dfSBruce Richardson 		}
110617995dfSBruce Richardson 	}
111dca926caSGage Eads 	iq_put_back(sw, &qid->iq[iq_num], blocked_qes, nb_blocked);
112617995dfSBruce Richardson 
113617995dfSBruce Richardson 	return count - nb_blocked;
114617995dfSBruce Richardson }
115617995dfSBruce Richardson 
116617995dfSBruce Richardson static inline uint32_t
sw_schedule_parallel_to_cq(struct sw_evdev * sw,struct sw_qid * const qid,uint32_t iq_num,unsigned int count,int keep_order)117617995dfSBruce Richardson sw_schedule_parallel_to_cq(struct sw_evdev *sw, struct sw_qid * const qid,
118617995dfSBruce Richardson 		uint32_t iq_num, unsigned int count, int keep_order)
119617995dfSBruce Richardson {
120617995dfSBruce Richardson 	uint32_t i;
121617995dfSBruce Richardson 	uint32_t cq_idx = qid->cq_next_tx;
122617995dfSBruce Richardson 
123617995dfSBruce Richardson 	/* This is the QID ID. The QID ID is static, hence it can be
124617995dfSBruce Richardson 	 * used to identify the stage of processing in history lists etc
125617995dfSBruce Richardson 	 */
126617995dfSBruce Richardson 	uint32_t qid_id = qid->id;
127617995dfSBruce Richardson 
128617995dfSBruce Richardson 	if (count > MAX_PER_IQ_DEQUEUE)
129617995dfSBruce Richardson 		count = MAX_PER_IQ_DEQUEUE;
130617995dfSBruce Richardson 
131617995dfSBruce Richardson 	if (keep_order)
132617995dfSBruce Richardson 		/* only schedule as many as we have reorder buffer entries */
133617995dfSBruce Richardson 		count = RTE_MIN(count,
13470207f35SRadu Nicolau 				rob_ring_count(qid->reorder_buffer_freelist));
135617995dfSBruce Richardson 
136617995dfSBruce Richardson 	for (i = 0; i < count; i++) {
137dca926caSGage Eads 		const struct rte_event *qe = iq_peek(&qid->iq[iq_num]);
138617995dfSBruce Richardson 		uint32_t cq_check_count = 0;
139617995dfSBruce Richardson 		uint32_t cq;
140617995dfSBruce Richardson 
141617995dfSBruce Richardson 		/*
142617995dfSBruce Richardson 		 *  for parallel, just send to next available CQ in round-robin
143617995dfSBruce Richardson 		 * fashion. So scan for an available CQ. If all CQs are full
144617995dfSBruce Richardson 		 * just return and move on to next QID
145617995dfSBruce Richardson 		 */
146617995dfSBruce Richardson 		do {
147617995dfSBruce Richardson 			if (++cq_check_count > qid->cq_num_mapped_cqs)
148617995dfSBruce Richardson 				goto exit;
149733fc6caSHarry van Haaren 			if (cq_idx >= qid->cq_num_mapped_cqs)
150617995dfSBruce Richardson 				cq_idx = 0;
151733fc6caSHarry van Haaren 			cq = qid->cq_map[cq_idx++];
152733fc6caSHarry van Haaren 
15370207f35SRadu Nicolau 		} while (sw->ports[cq].inflights == SW_PORT_HIST_LIST ||
15470207f35SRadu Nicolau 				rte_event_ring_free_count(
15570207f35SRadu Nicolau 					sw->ports[cq].cq_worker_ring) == 0);
156617995dfSBruce Richardson 
157617995dfSBruce Richardson 		struct sw_port *p = &sw->ports[cq];
158617995dfSBruce Richardson 		if (sw->cq_ring_space[cq] == 0 ||
159617995dfSBruce Richardson 				p->inflights == SW_PORT_HIST_LIST)
160617995dfSBruce Richardson 			break;
161617995dfSBruce Richardson 
162617995dfSBruce Richardson 		sw->cq_ring_space[cq]--;
163617995dfSBruce Richardson 
164617995dfSBruce Richardson 		qid->stats.tx_pkts++;
165617995dfSBruce Richardson 
166617995dfSBruce Richardson 		const int head = (p->hist_head & (SW_PORT_HIST_LIST-1));
167*5078a8f3SHarry van Haaren 		p->hist_list[head] = (struct sw_hist_list_entry) {
168*5078a8f3SHarry van Haaren 			.qid = qid_id,
169*5078a8f3SHarry van Haaren 			.fid = SW_HASH_FLOWID(qe->flow_id),
170*5078a8f3SHarry van Haaren 		};
171617995dfSBruce Richardson 
172617995dfSBruce Richardson 		if (keep_order)
17370207f35SRadu Nicolau 			rob_ring_dequeue(qid->reorder_buffer_freelist,
174617995dfSBruce Richardson 					(void *)&p->hist_list[head].rob_entry);
175617995dfSBruce Richardson 
176617995dfSBruce Richardson 		sw->ports[cq].cq_buf[sw->ports[cq].cq_buf_count++] = *qe;
177dca926caSGage Eads 		iq_pop(sw, &qid->iq[iq_num]);
178617995dfSBruce Richardson 
179617995dfSBruce Richardson 		rte_compiler_barrier();
180617995dfSBruce Richardson 		p->inflights++;
181617995dfSBruce Richardson 		p->stats.tx_pkts++;
182617995dfSBruce Richardson 		p->hist_head++;
183617995dfSBruce Richardson 	}
184617995dfSBruce Richardson exit:
185617995dfSBruce Richardson 	qid->cq_next_tx = cq_idx;
186617995dfSBruce Richardson 	return i;
187617995dfSBruce Richardson }
188617995dfSBruce Richardson 
189617995dfSBruce Richardson static uint32_t
sw_schedule_dir_to_cq(struct sw_evdev * sw,struct sw_qid * const qid,uint32_t iq_num,unsigned int count __rte_unused)190617995dfSBruce Richardson sw_schedule_dir_to_cq(struct sw_evdev *sw, struct sw_qid * const qid,
191617995dfSBruce Richardson 		uint32_t iq_num, unsigned int count __rte_unused)
192617995dfSBruce Richardson {
193617995dfSBruce Richardson 	uint32_t cq_id = qid->cq_map[0];
194617995dfSBruce Richardson 	struct sw_port *port = &sw->ports[cq_id];
195617995dfSBruce Richardson 
196617995dfSBruce Richardson 	/* get max burst enq size for cq_ring */
197617995dfSBruce Richardson 	uint32_t count_free = sw->cq_ring_space[cq_id];
198617995dfSBruce Richardson 	if (count_free == 0)
199617995dfSBruce Richardson 		return 0;
200617995dfSBruce Richardson 
201617995dfSBruce Richardson 	/* burst dequeue from the QID IQ ring */
202dca926caSGage Eads 	struct sw_iq *iq = &qid->iq[iq_num];
203dca926caSGage Eads 	uint32_t ret = iq_dequeue_burst(sw, iq,
204617995dfSBruce Richardson 			&port->cq_buf[port->cq_buf_count], count_free);
205617995dfSBruce Richardson 	port->cq_buf_count += ret;
206617995dfSBruce Richardson 
207617995dfSBruce Richardson 	/* Update QID, Port and Total TX stats */
208617995dfSBruce Richardson 	qid->stats.tx_pkts += ret;
209617995dfSBruce Richardson 	port->stats.tx_pkts += ret;
210617995dfSBruce Richardson 
211617995dfSBruce Richardson 	/* Subtract credits from cached value */
212617995dfSBruce Richardson 	sw->cq_ring_space[cq_id] -= ret;
213617995dfSBruce Richardson 
214617995dfSBruce Richardson 	return ret;
215617995dfSBruce Richardson }
216617995dfSBruce Richardson 
217617995dfSBruce Richardson static uint32_t
sw_schedule_qid_to_cq(struct sw_evdev * sw)218617995dfSBruce Richardson sw_schedule_qid_to_cq(struct sw_evdev *sw)
219617995dfSBruce Richardson {
220617995dfSBruce Richardson 	uint32_t pkts = 0;
221617995dfSBruce Richardson 	uint32_t qid_idx;
222617995dfSBruce Richardson 
223617995dfSBruce Richardson 	sw->sched_cq_qid_called++;
224617995dfSBruce Richardson 
225617995dfSBruce Richardson 	for (qid_idx = 0; qid_idx < sw->qid_count; qid_idx++) {
226617995dfSBruce Richardson 		struct sw_qid *qid = sw->qids_prioritized[qid_idx];
227617995dfSBruce Richardson 
228617995dfSBruce Richardson 		int type = qid->type;
229617995dfSBruce Richardson 		int iq_num = PKT_MASK_TO_IQ(qid->iq_pkt_mask);
230617995dfSBruce Richardson 
231617995dfSBruce Richardson 		/* zero mapped CQs indicates directed */
232733fc6caSHarry van Haaren 		if (iq_num >= SW_IQS_MAX || qid->cq_num_mapped_cqs == 0)
233617995dfSBruce Richardson 			continue;
234617995dfSBruce Richardson 
235617995dfSBruce Richardson 		uint32_t pkts_done = 0;
236dca926caSGage Eads 		uint32_t count = iq_count(&qid->iq[iq_num]);
237617995dfSBruce Richardson 
23870207f35SRadu Nicolau 		if (count >= sw->sched_min_burst) {
239617995dfSBruce Richardson 			if (type == SW_SCHED_TYPE_DIRECT)
240617995dfSBruce Richardson 				pkts_done += sw_schedule_dir_to_cq(sw, qid,
241617995dfSBruce Richardson 						iq_num, count);
242617995dfSBruce Richardson 			else if (type == RTE_SCHED_TYPE_ATOMIC)
243617995dfSBruce Richardson 				pkts_done += sw_schedule_atomic_to_cq(sw, qid,
244617995dfSBruce Richardson 						iq_num, count);
245617995dfSBruce Richardson 			else
246617995dfSBruce Richardson 				pkts_done += sw_schedule_parallel_to_cq(sw, qid,
247617995dfSBruce Richardson 						iq_num, count,
248617995dfSBruce Richardson 						type == RTE_SCHED_TYPE_ORDERED);
249617995dfSBruce Richardson 		}
250617995dfSBruce Richardson 
251617995dfSBruce Richardson 		/* Check if the IQ that was polled is now empty, and unset it
252617995dfSBruce Richardson 		 * in the IQ mask if its empty.
253617995dfSBruce Richardson 		 */
254617995dfSBruce Richardson 		int all_done = (pkts_done == count);
255617995dfSBruce Richardson 
256617995dfSBruce Richardson 		qid->iq_pkt_mask &= ~(all_done << (iq_num));
257617995dfSBruce Richardson 		pkts += pkts_done;
258617995dfSBruce Richardson 	}
259617995dfSBruce Richardson 
260617995dfSBruce Richardson 	return pkts;
261617995dfSBruce Richardson }
262617995dfSBruce Richardson 
263617995dfSBruce Richardson /* This function will perform re-ordering of packets, and injecting into
264617995dfSBruce Richardson  * the appropriate QID IQ. As LB and DIR QIDs are in the same array, but *NOT*
265617995dfSBruce Richardson  * contiguous in that array, this function accepts a "range" of QIDs to scan.
266617995dfSBruce Richardson  */
267617995dfSBruce Richardson static uint16_t
sw_schedule_reorder(struct sw_evdev * sw,int qid_start,int qid_end)268617995dfSBruce Richardson sw_schedule_reorder(struct sw_evdev *sw, int qid_start, int qid_end)
269617995dfSBruce Richardson {
270617995dfSBruce Richardson 	/* Perform egress reordering */
271617995dfSBruce Richardson 	struct rte_event *qe;
272617995dfSBruce Richardson 	uint32_t pkts_iter = 0;
273617995dfSBruce Richardson 
274617995dfSBruce Richardson 	for (; qid_start < qid_end; qid_start++) {
275617995dfSBruce Richardson 		struct sw_qid *qid = &sw->qids[qid_start];
27670207f35SRadu Nicolau 		unsigned int i, num_entries_in_use;
277617995dfSBruce Richardson 
278617995dfSBruce Richardson 		if (qid->type != RTE_SCHED_TYPE_ORDERED)
279617995dfSBruce Richardson 			continue;
280617995dfSBruce Richardson 
28170207f35SRadu Nicolau 		num_entries_in_use = rob_ring_free_count(
282617995dfSBruce Richardson 					qid->reorder_buffer_freelist);
283617995dfSBruce Richardson 
28470207f35SRadu Nicolau 		if (num_entries_in_use < sw->sched_min_burst)
28570207f35SRadu Nicolau 			num_entries_in_use = 0;
28670207f35SRadu Nicolau 
287617995dfSBruce Richardson 		for (i = 0; i < num_entries_in_use; i++) {
288617995dfSBruce Richardson 			struct reorder_buffer_entry *entry;
289617995dfSBruce Richardson 			int j;
290617995dfSBruce Richardson 
291617995dfSBruce Richardson 			entry = &qid->reorder_buffer[qid->reorder_buffer_index];
292617995dfSBruce Richardson 
293617995dfSBruce Richardson 			if (!entry->ready)
294617995dfSBruce Richardson 				break;
295617995dfSBruce Richardson 
296617995dfSBruce Richardson 			for (j = 0; j < entry->num_fragments; j++) {
297617995dfSBruce Richardson 				uint16_t dest_qid;
298617995dfSBruce Richardson 				uint16_t dest_iq;
299617995dfSBruce Richardson 
300617995dfSBruce Richardson 				int idx = entry->fragment_index + j;
301617995dfSBruce Richardson 				qe = &entry->fragments[idx];
302617995dfSBruce Richardson 
303617995dfSBruce Richardson 				dest_qid = qe->queue_id;
304617995dfSBruce Richardson 				dest_iq  = PRIO_TO_IQ(qe->priority);
305617995dfSBruce Richardson 
306617995dfSBruce Richardson 				if (dest_qid >= sw->qid_count) {
307617995dfSBruce Richardson 					sw->stats.rx_dropped++;
308617995dfSBruce Richardson 					continue;
309617995dfSBruce Richardson 				}
310617995dfSBruce Richardson 
311617995dfSBruce Richardson 				pkts_iter++;
312617995dfSBruce Richardson 
313617995dfSBruce Richardson 				struct sw_qid *q = &sw->qids[dest_qid];
314dca926caSGage Eads 				struct sw_iq *iq = &q->iq[dest_iq];
315617995dfSBruce Richardson 
316617995dfSBruce Richardson 				/* we checked for space above, so enqueue must
317617995dfSBruce Richardson 				 * succeed
318617995dfSBruce Richardson 				 */
319dca926caSGage Eads 				iq_enqueue(sw, iq, qe);
320617995dfSBruce Richardson 				q->iq_pkt_mask |= (1 << (dest_iq));
321617995dfSBruce Richardson 				q->iq_pkt_count[dest_iq]++;
322617995dfSBruce Richardson 				q->stats.rx_pkts++;
323617995dfSBruce Richardson 			}
324617995dfSBruce Richardson 
325617995dfSBruce Richardson 			entry->ready = (j != entry->num_fragments);
326617995dfSBruce Richardson 			entry->num_fragments -= j;
327617995dfSBruce Richardson 			entry->fragment_index += j;
328617995dfSBruce Richardson 
329617995dfSBruce Richardson 			if (!entry->ready) {
330617995dfSBruce Richardson 				entry->fragment_index = 0;
331617995dfSBruce Richardson 
33270207f35SRadu Nicolau 				rob_ring_enqueue(
333617995dfSBruce Richardson 						qid->reorder_buffer_freelist,
334617995dfSBruce Richardson 						entry);
335617995dfSBruce Richardson 
336617995dfSBruce Richardson 				qid->reorder_buffer_index++;
337617995dfSBruce Richardson 				qid->reorder_buffer_index %= qid->window_size;
338617995dfSBruce Richardson 			}
339617995dfSBruce Richardson 		}
340617995dfSBruce Richardson 	}
341617995dfSBruce Richardson 	return pkts_iter;
342617995dfSBruce Richardson }
343617995dfSBruce Richardson 
344c0583d98SJerin Jacob static __rte_always_inline void
sw_refill_pp_buf(struct sw_evdev * sw,struct sw_port * port)345617995dfSBruce Richardson sw_refill_pp_buf(struct sw_evdev *sw, struct sw_port *port)
346617995dfSBruce Richardson {
347617995dfSBruce Richardson 	RTE_SET_USED(sw);
34886aed50aSBruce Richardson 	struct rte_event_ring *worker = port->rx_worker_ring;
349617995dfSBruce Richardson 	port->pp_buf_start = 0;
35086aed50aSBruce Richardson 	port->pp_buf_count = rte_event_ring_dequeue_burst(worker, port->pp_buf,
35170207f35SRadu Nicolau 			sw->sched_deq_burst_size, NULL);
352617995dfSBruce Richardson }
353617995dfSBruce Richardson 
354c0583d98SJerin Jacob static __rte_always_inline uint32_t
__pull_port_lb(struct sw_evdev * sw,uint32_t port_id,int allow_reorder)355617995dfSBruce Richardson __pull_port_lb(struct sw_evdev *sw, uint32_t port_id, int allow_reorder)
356617995dfSBruce Richardson {
357fa865c01SFerruh Yigit 	static struct reorder_buffer_entry dummy_rob;
358617995dfSBruce Richardson 	uint32_t pkts_iter = 0;
359617995dfSBruce Richardson 	struct sw_port *port = &sw->ports[port_id];
360617995dfSBruce Richardson 
361617995dfSBruce Richardson 	/* If shadow ring has 0 pkts, pull from worker ring */
36270207f35SRadu Nicolau 	if (!sw->refill_once_per_iter && port->pp_buf_count == 0)
363617995dfSBruce Richardson 		sw_refill_pp_buf(sw, port);
364617995dfSBruce Richardson 
365617995dfSBruce Richardson 	while (port->pp_buf_count) {
366617995dfSBruce Richardson 		const struct rte_event *qe = &port->pp_buf[port->pp_buf_start];
367617995dfSBruce Richardson 		struct sw_hist_list_entry *hist_entry = NULL;
368617995dfSBruce Richardson 		uint8_t flags = qe->op;
369617995dfSBruce Richardson 		const uint16_t eop = !(flags & QE_FLAG_NOT_EOP);
370617995dfSBruce Richardson 		int needs_reorder = 0;
371617995dfSBruce Richardson 		/* if no-reordering, having PARTIAL == NEW */
372617995dfSBruce Richardson 		if (!allow_reorder && !eop)
373617995dfSBruce Richardson 			flags = QE_FLAG_VALID;
374617995dfSBruce Richardson 
375617995dfSBruce Richardson 		uint32_t iq_num = PRIO_TO_IQ(qe->priority);
376617995dfSBruce Richardson 		struct sw_qid *qid = &sw->qids[qe->queue_id];
377617995dfSBruce Richardson 
378617995dfSBruce Richardson 		/* now process based on flags. Note that for directed
379617995dfSBruce Richardson 		 * queues, the enqueue_flush masks off all but the
380617995dfSBruce Richardson 		 * valid flag. This makes FWD and PARTIAL enqueues just
381617995dfSBruce Richardson 		 * NEW type, and makes DROPS no-op calls.
382617995dfSBruce Richardson 		 */
383617995dfSBruce Richardson 		if ((flags & QE_FLAG_COMPLETE) && port->inflights > 0) {
384617995dfSBruce Richardson 			const uint32_t hist_tail = port->hist_tail &
385617995dfSBruce Richardson 					(SW_PORT_HIST_LIST - 1);
386617995dfSBruce Richardson 
387617995dfSBruce Richardson 			hist_entry = &port->hist_list[hist_tail];
388617995dfSBruce Richardson 			const uint32_t hist_qid = hist_entry->qid;
389617995dfSBruce Richardson 			const uint32_t hist_fid = hist_entry->fid;
390617995dfSBruce Richardson 
391617995dfSBruce Richardson 			struct sw_fid_t *fid =
392617995dfSBruce Richardson 				&sw->qids[hist_qid].fids[hist_fid];
393617995dfSBruce Richardson 			fid->pcount -= eop;
394617995dfSBruce Richardson 			if (fid->pcount == 0)
395617995dfSBruce Richardson 				fid->cq = -1;
396617995dfSBruce Richardson 
397617995dfSBruce Richardson 			if (allow_reorder) {
398617995dfSBruce Richardson 				/* set reorder ready if an ordered QID */
399617995dfSBruce Richardson 				uintptr_t rob_ptr =
400617995dfSBruce Richardson 					(uintptr_t)hist_entry->rob_entry;
401617995dfSBruce Richardson 				const uintptr_t valid = (rob_ptr != 0);
402617995dfSBruce Richardson 				needs_reorder = valid;
403617995dfSBruce Richardson 				rob_ptr |=
404617995dfSBruce Richardson 					((valid - 1) & (uintptr_t)&dummy_rob);
405617995dfSBruce Richardson 				struct reorder_buffer_entry *tmp_rob_ptr =
406617995dfSBruce Richardson 					(struct reorder_buffer_entry *)rob_ptr;
407617995dfSBruce Richardson 				tmp_rob_ptr->ready = eop * needs_reorder;
408617995dfSBruce Richardson 			}
409617995dfSBruce Richardson 
410617995dfSBruce Richardson 			port->inflights -= eop;
411617995dfSBruce Richardson 			port->hist_tail += eop;
412617995dfSBruce Richardson 		}
413617995dfSBruce Richardson 		if (flags & QE_FLAG_VALID) {
414617995dfSBruce Richardson 			port->stats.rx_pkts++;
415617995dfSBruce Richardson 
416617995dfSBruce Richardson 			if (allow_reorder && needs_reorder) {
417617995dfSBruce Richardson 				struct reorder_buffer_entry *rob_entry =
418617995dfSBruce Richardson 						hist_entry->rob_entry;
419617995dfSBruce Richardson 
420617995dfSBruce Richardson 				/* Although fragmentation not currently
421617995dfSBruce Richardson 				 * supported by eventdev API, we support it
422617995dfSBruce Richardson 				 * here. Open: How do we alert the user that
423617995dfSBruce Richardson 				 * they've exceeded max frags?
424617995dfSBruce Richardson 				 */
425617995dfSBruce Richardson 				int num_frag = rob_entry->num_fragments;
426617995dfSBruce Richardson 				if (num_frag == SW_FRAGMENTS_MAX)
427617995dfSBruce Richardson 					sw->stats.rx_dropped++;
428617995dfSBruce Richardson 				else {
429617995dfSBruce Richardson 					int idx = rob_entry->num_fragments++;
430617995dfSBruce Richardson 					rob_entry->fragments[idx] = *qe;
431617995dfSBruce Richardson 				}
432617995dfSBruce Richardson 				goto end_qe;
433617995dfSBruce Richardson 			}
434617995dfSBruce Richardson 
435617995dfSBruce Richardson 			/* Use the iq_num from above to push the QE
436617995dfSBruce Richardson 			 * into the qid at the right priority
437617995dfSBruce Richardson 			 */
438617995dfSBruce Richardson 
439617995dfSBruce Richardson 			qid->iq_pkt_mask |= (1 << (iq_num));
440dca926caSGage Eads 			iq_enqueue(sw, &qid->iq[iq_num], qe);
441617995dfSBruce Richardson 			qid->iq_pkt_count[iq_num]++;
442617995dfSBruce Richardson 			qid->stats.rx_pkts++;
443617995dfSBruce Richardson 			pkts_iter++;
444617995dfSBruce Richardson 		}
445617995dfSBruce Richardson 
446617995dfSBruce Richardson end_qe:
447617995dfSBruce Richardson 		port->pp_buf_start++;
448617995dfSBruce Richardson 		port->pp_buf_count--;
449617995dfSBruce Richardson 	} /* while (avail_qes) */
450617995dfSBruce Richardson 
451617995dfSBruce Richardson 	return pkts_iter;
452617995dfSBruce Richardson }
453617995dfSBruce Richardson 
454617995dfSBruce Richardson static uint32_t
sw_schedule_pull_port_lb(struct sw_evdev * sw,uint32_t port_id)455617995dfSBruce Richardson sw_schedule_pull_port_lb(struct sw_evdev *sw, uint32_t port_id)
456617995dfSBruce Richardson {
457617995dfSBruce Richardson 	return __pull_port_lb(sw, port_id, 1);
458617995dfSBruce Richardson }
459617995dfSBruce Richardson 
460617995dfSBruce Richardson static uint32_t
sw_schedule_pull_port_no_reorder(struct sw_evdev * sw,uint32_t port_id)461617995dfSBruce Richardson sw_schedule_pull_port_no_reorder(struct sw_evdev *sw, uint32_t port_id)
462617995dfSBruce Richardson {
463617995dfSBruce Richardson 	return __pull_port_lb(sw, port_id, 0);
464617995dfSBruce Richardson }
465617995dfSBruce Richardson 
466617995dfSBruce Richardson static uint32_t
sw_schedule_pull_port_dir(struct sw_evdev * sw,uint32_t port_id)467617995dfSBruce Richardson sw_schedule_pull_port_dir(struct sw_evdev *sw, uint32_t port_id)
468617995dfSBruce Richardson {
469617995dfSBruce Richardson 	uint32_t pkts_iter = 0;
470617995dfSBruce Richardson 	struct sw_port *port = &sw->ports[port_id];
471617995dfSBruce Richardson 
472617995dfSBruce Richardson 	/* If shadow ring has 0 pkts, pull from worker ring */
47370207f35SRadu Nicolau 	if (!sw->refill_once_per_iter && port->pp_buf_count == 0)
474617995dfSBruce Richardson 		sw_refill_pp_buf(sw, port);
475617995dfSBruce Richardson 
476617995dfSBruce Richardson 	while (port->pp_buf_count) {
477617995dfSBruce Richardson 		const struct rte_event *qe = &port->pp_buf[port->pp_buf_start];
478617995dfSBruce Richardson 		uint8_t flags = qe->op;
479617995dfSBruce Richardson 
480617995dfSBruce Richardson 		if ((flags & QE_FLAG_VALID) == 0)
481617995dfSBruce Richardson 			goto end_qe;
482617995dfSBruce Richardson 
483617995dfSBruce Richardson 		uint32_t iq_num = PRIO_TO_IQ(qe->priority);
484617995dfSBruce Richardson 		struct sw_qid *qid = &sw->qids[qe->queue_id];
485dca926caSGage Eads 		struct sw_iq *iq = &qid->iq[iq_num];
486617995dfSBruce Richardson 
487617995dfSBruce Richardson 		port->stats.rx_pkts++;
488617995dfSBruce Richardson 
489617995dfSBruce Richardson 		/* Use the iq_num from above to push the QE
490617995dfSBruce Richardson 		 * into the qid at the right priority
491617995dfSBruce Richardson 		 */
492617995dfSBruce Richardson 		qid->iq_pkt_mask |= (1 << (iq_num));
493dca926caSGage Eads 		iq_enqueue(sw, iq, qe);
494617995dfSBruce Richardson 		qid->iq_pkt_count[iq_num]++;
495617995dfSBruce Richardson 		qid->stats.rx_pkts++;
496617995dfSBruce Richardson 		pkts_iter++;
497617995dfSBruce Richardson 
498617995dfSBruce Richardson end_qe:
499617995dfSBruce Richardson 		port->pp_buf_start++;
500617995dfSBruce Richardson 		port->pp_buf_count--;
501617995dfSBruce Richardson 	} /* while port->pp_buf_count */
502617995dfSBruce Richardson 
503617995dfSBruce Richardson 	return pkts_iter;
504617995dfSBruce Richardson }
505617995dfSBruce Richardson 
5064689c579SMattias Rönnblom int32_t
sw_event_schedule(struct rte_eventdev * dev)507617995dfSBruce Richardson sw_event_schedule(struct rte_eventdev *dev)
508617995dfSBruce Richardson {
509617995dfSBruce Richardson 	struct sw_evdev *sw = sw_pmd_priv(dev);
510617995dfSBruce Richardson 	uint32_t in_pkts, out_pkts;
511617995dfSBruce Richardson 	uint32_t out_pkts_total = 0, in_pkts_total = 0;
512617995dfSBruce Richardson 	int32_t sched_quanta = sw->sched_quanta;
513617995dfSBruce Richardson 	uint32_t i;
514617995dfSBruce Richardson 
515617995dfSBruce Richardson 	sw->sched_called++;
51663ddc002SVipin Varghese 	if (unlikely(!sw->started))
5174689c579SMattias Rönnblom 		return -EAGAIN;
518617995dfSBruce Richardson 
519617995dfSBruce Richardson 	do {
520617995dfSBruce Richardson 		uint32_t in_pkts_this_iteration = 0;
521617995dfSBruce Richardson 
522617995dfSBruce Richardson 		/* Pull from rx_ring for ports */
523617995dfSBruce Richardson 		do {
524617995dfSBruce Richardson 			in_pkts = 0;
525bd5ac24fSHarry van Haaren 			for (i = 0; i < sw->port_count; i++) {
526bd5ac24fSHarry van Haaren 				/* ack the unlinks in progress as done */
527bd5ac24fSHarry van Haaren 				if (sw->ports[i].unlinks_in_progress)
528bd5ac24fSHarry van Haaren 					sw->ports[i].unlinks_in_progress = 0;
529bd5ac24fSHarry van Haaren 
530617995dfSBruce Richardson 				if (sw->ports[i].is_directed)
531617995dfSBruce Richardson 					in_pkts += sw_schedule_pull_port_dir(sw, i);
532617995dfSBruce Richardson 				else if (sw->ports[i].num_ordered_qids > 0)
533617995dfSBruce Richardson 					in_pkts += sw_schedule_pull_port_lb(sw, i);
534617995dfSBruce Richardson 				else
535617995dfSBruce Richardson 					in_pkts += sw_schedule_pull_port_no_reorder(sw, i);
536bd5ac24fSHarry van Haaren 			}
537617995dfSBruce Richardson 
538617995dfSBruce Richardson 			/* QID scan for re-ordered */
539617995dfSBruce Richardson 			in_pkts += sw_schedule_reorder(sw, 0,
540617995dfSBruce Richardson 					sw->qid_count);
541617995dfSBruce Richardson 			in_pkts_this_iteration += in_pkts;
542617995dfSBruce Richardson 		} while (in_pkts > 4 &&
543617995dfSBruce Richardson 				(int)in_pkts_this_iteration < sched_quanta);
544617995dfSBruce Richardson 
545e4dff550SVipin Varghese 		out_pkts = sw_schedule_qid_to_cq(sw);
546617995dfSBruce Richardson 		out_pkts_total += out_pkts;
547617995dfSBruce Richardson 		in_pkts_total += in_pkts_this_iteration;
548617995dfSBruce Richardson 
549617995dfSBruce Richardson 		if (in_pkts == 0 && out_pkts == 0)
550617995dfSBruce Richardson 			break;
551617995dfSBruce Richardson 	} while ((int)out_pkts_total < sched_quanta);
552617995dfSBruce Richardson 
553642bc2a3SVipin Varghese 	sw->stats.tx_pkts += out_pkts_total;
554642bc2a3SVipin Varghese 	sw->stats.rx_pkts += in_pkts_total;
555642bc2a3SVipin Varghese 
556642bc2a3SVipin Varghese 	sw->sched_no_iq_enqueues += (in_pkts_total == 0);
557642bc2a3SVipin Varghese 	sw->sched_no_cq_enqueues += (out_pkts_total == 0);
558642bc2a3SVipin Varghese 
559324b37e6SHarry van Haaren 	uint64_t work_done = (in_pkts_total + out_pkts_total) != 0;
560324b37e6SHarry van Haaren 	sw->sched_progress_last_iter = work_done;
561324b37e6SHarry van Haaren 
562324b37e6SHarry van Haaren 	uint64_t cqs_scheds_last_iter = 0;
563324b37e6SHarry van Haaren 
564617995dfSBruce Richardson 	/* push all the internal buffered QEs in port->cq_ring to the
565617995dfSBruce Richardson 	 * worker cores: aka, do the ring transfers batched.
566617995dfSBruce Richardson 	 */
56770207f35SRadu Nicolau 	int no_enq = 1;
568617995dfSBruce Richardson 	for (i = 0; i < sw->port_count; i++) {
56970207f35SRadu Nicolau 		struct sw_port *port = &sw->ports[i];
57070207f35SRadu Nicolau 		struct rte_event_ring *worker = port->cq_worker_ring;
57170207f35SRadu Nicolau 
57270207f35SRadu Nicolau 		/* If shadow ring has 0 pkts, pull from worker ring */
57370207f35SRadu Nicolau 		if (sw->refill_once_per_iter && port->pp_buf_count == 0)
57470207f35SRadu Nicolau 			sw_refill_pp_buf(sw, port);
57570207f35SRadu Nicolau 
57670207f35SRadu Nicolau 		if (port->cq_buf_count >= sw->sched_min_burst) {
57770207f35SRadu Nicolau 			rte_event_ring_enqueue_burst(worker,
57870207f35SRadu Nicolau 					port->cq_buf,
57970207f35SRadu Nicolau 					port->cq_buf_count,
580617995dfSBruce Richardson 					&sw->cq_ring_space[i]);
58170207f35SRadu Nicolau 			port->cq_buf_count = 0;
58270207f35SRadu Nicolau 			no_enq = 0;
583324b37e6SHarry van Haaren 			cqs_scheds_last_iter |= (1ULL << i);
58470207f35SRadu Nicolau 		} else {
58570207f35SRadu Nicolau 			sw->cq_ring_space[i] =
58670207f35SRadu Nicolau 					rte_event_ring_free_count(worker) -
58770207f35SRadu Nicolau 					port->cq_buf_count;
58870207f35SRadu Nicolau 		}
58970207f35SRadu Nicolau 	}
59070207f35SRadu Nicolau 
59170207f35SRadu Nicolau 	if (no_enq) {
59270207f35SRadu Nicolau 		if (unlikely(sw->sched_flush_count > SCHED_NO_ENQ_CYCLE_FLUSH))
59370207f35SRadu Nicolau 			sw->sched_min_burst = 1;
59470207f35SRadu Nicolau 		else
59570207f35SRadu Nicolau 			sw->sched_flush_count++;
59670207f35SRadu Nicolau 	} else {
59770207f35SRadu Nicolau 		if (sw->sched_flush_count)
59870207f35SRadu Nicolau 			sw->sched_flush_count--;
59970207f35SRadu Nicolau 		else
60070207f35SRadu Nicolau 			sw->sched_min_burst = sw->sched_min_burst_size;
601617995dfSBruce Richardson 	}
602617995dfSBruce Richardson 
603324b37e6SHarry van Haaren 	/* Provide stats on what eventdev ports were scheduled to this
604324b37e6SHarry van Haaren 	 * iteration. If more than 64 ports are active, always report that
605324b37e6SHarry van Haaren 	 * all Eventdev ports have been scheduled events.
606324b37e6SHarry van Haaren 	 */
607324b37e6SHarry van Haaren 	sw->sched_last_iter_bitmask = cqs_scheds_last_iter;
608324b37e6SHarry van Haaren 	if (unlikely(sw->port_count >= 64))
609324b37e6SHarry van Haaren 		sw->sched_last_iter_bitmask = UINT64_MAX;
6104689c579SMattias Rönnblom 
6114689c579SMattias Rönnblom 	return work_done ? 0 : -EAGAIN;
612617995dfSBruce Richardson }
613