1*2bfe3f2eSlogwang /*
2*2bfe3f2eSlogwang  *   BSD LICENSE
3*2bfe3f2eSlogwang  *
4*2bfe3f2eSlogwang  *   Copyright (C) Cavium, Inc 2017.
5*2bfe3f2eSlogwang  *
6*2bfe3f2eSlogwang  *   Redistribution and use in source and binary forms, with or without
7*2bfe3f2eSlogwang  *   modification, are permitted provided that the following conditions
8*2bfe3f2eSlogwang  *   are met:
9*2bfe3f2eSlogwang  *
10*2bfe3f2eSlogwang  *     * Redistributions of source code must retain the above copyright
11*2bfe3f2eSlogwang  *       notice, this list of conditions and the following disclaimer.
12*2bfe3f2eSlogwang  *     * Redistributions in binary form must reproduce the above copyright
13*2bfe3f2eSlogwang  *       notice, this list of conditions and the following disclaimer in
14*2bfe3f2eSlogwang  *       the documentation and/or other materials provided with the
15*2bfe3f2eSlogwang  *       distribution.
16*2bfe3f2eSlogwang  *     * Neither the name of Cavium, Inc nor the names of its
17*2bfe3f2eSlogwang  *       contributors may be used to endorse or promote products derived
18*2bfe3f2eSlogwang  *       from this software without specific prior written permission.
19*2bfe3f2eSlogwang  *
20*2bfe3f2eSlogwang  *   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
21*2bfe3f2eSlogwang  *   "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
22*2bfe3f2eSlogwang  *   LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
23*2bfe3f2eSlogwang  *   A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
24*2bfe3f2eSlogwang  *   OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
25*2bfe3f2eSlogwang  *   SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
26*2bfe3f2eSlogwang  *   LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
27*2bfe3f2eSlogwang  *   DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
28*2bfe3f2eSlogwang  *   THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
29*2bfe3f2eSlogwang  *   (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
30*2bfe3f2eSlogwang  *   OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31*2bfe3f2eSlogwang  */
32*2bfe3f2eSlogwang 
33*2bfe3f2eSlogwang #include "test_perf_common.h"
34*2bfe3f2eSlogwang 
35*2bfe3f2eSlogwang /* See http://dpdk.org/doc/guides/tools/testeventdev.html for test details */
36*2bfe3f2eSlogwang 
37*2bfe3f2eSlogwang static inline int
38*2bfe3f2eSlogwang atq_nb_event_queues(struct evt_options *opt)
39*2bfe3f2eSlogwang {
40*2bfe3f2eSlogwang 	/* nb_queues = number of producers */
41*2bfe3f2eSlogwang 	return evt_nr_active_lcores(opt->plcores);
42*2bfe3f2eSlogwang }
43*2bfe3f2eSlogwang 
44*2bfe3f2eSlogwang static inline __attribute__((always_inline)) void
45*2bfe3f2eSlogwang atq_mark_fwd_latency(struct rte_event *const ev)
46*2bfe3f2eSlogwang {
47*2bfe3f2eSlogwang 	if (unlikely(ev->sub_event_type == 0)) {
48*2bfe3f2eSlogwang 		struct perf_elt *const m = ev->event_ptr;
49*2bfe3f2eSlogwang 
50*2bfe3f2eSlogwang 		m->timestamp = rte_get_timer_cycles();
51*2bfe3f2eSlogwang 	}
52*2bfe3f2eSlogwang }
53*2bfe3f2eSlogwang 
54*2bfe3f2eSlogwang static inline __attribute__((always_inline)) void
55*2bfe3f2eSlogwang atq_fwd_event(struct rte_event *const ev, uint8_t *const sched_type_list,
56*2bfe3f2eSlogwang 		const uint8_t nb_stages)
57*2bfe3f2eSlogwang {
58*2bfe3f2eSlogwang 	ev->sub_event_type++;
59*2bfe3f2eSlogwang 	ev->sched_type = sched_type_list[ev->sub_event_type % nb_stages];
60*2bfe3f2eSlogwang 	ev->op = RTE_EVENT_OP_FORWARD;
61*2bfe3f2eSlogwang 	ev->event_type = RTE_EVENT_TYPE_CPU;
62*2bfe3f2eSlogwang }
63*2bfe3f2eSlogwang 
64*2bfe3f2eSlogwang static int
65*2bfe3f2eSlogwang perf_atq_worker(void *arg, const int enable_fwd_latency)
66*2bfe3f2eSlogwang {
67*2bfe3f2eSlogwang 	PERF_WORKER_INIT;
68*2bfe3f2eSlogwang 	struct rte_event ev;
69*2bfe3f2eSlogwang 
70*2bfe3f2eSlogwang 	while (t->done == false) {
71*2bfe3f2eSlogwang 		uint16_t event = rte_event_dequeue_burst(dev, port, &ev, 1, 0);
72*2bfe3f2eSlogwang 
73*2bfe3f2eSlogwang 		if (enable_fwd_latency)
74*2bfe3f2eSlogwang 			rte_prefetch0(ev.event_ptr);
75*2bfe3f2eSlogwang 
76*2bfe3f2eSlogwang 		if (!event) {
77*2bfe3f2eSlogwang 			rte_pause();
78*2bfe3f2eSlogwang 			continue;
79*2bfe3f2eSlogwang 		}
80*2bfe3f2eSlogwang 
81*2bfe3f2eSlogwang 		if (enable_fwd_latency)
82*2bfe3f2eSlogwang 		/* first stage in pipeline, mark ts to compute fwd latency */
83*2bfe3f2eSlogwang 			atq_mark_fwd_latency(&ev);
84*2bfe3f2eSlogwang 
85*2bfe3f2eSlogwang 		/* last stage in pipeline */
86*2bfe3f2eSlogwang 		if (unlikely((ev.sub_event_type % nb_stages) == laststage)) {
87*2bfe3f2eSlogwang 			if (enable_fwd_latency)
88*2bfe3f2eSlogwang 				cnt = perf_process_last_stage_latency(pool,
89*2bfe3f2eSlogwang 					&ev, w, bufs, sz, cnt);
90*2bfe3f2eSlogwang 			else
91*2bfe3f2eSlogwang 				cnt = perf_process_last_stage(pool, &ev, w,
92*2bfe3f2eSlogwang 					 bufs, sz, cnt);
93*2bfe3f2eSlogwang 		} else {
94*2bfe3f2eSlogwang 			atq_fwd_event(&ev, sched_type_list, nb_stages);
95*2bfe3f2eSlogwang 			while (rte_event_enqueue_burst(dev, port, &ev, 1) != 1)
96*2bfe3f2eSlogwang 				rte_pause();
97*2bfe3f2eSlogwang 		}
98*2bfe3f2eSlogwang 	}
99*2bfe3f2eSlogwang 	return 0;
100*2bfe3f2eSlogwang }
101*2bfe3f2eSlogwang 
102*2bfe3f2eSlogwang static int
103*2bfe3f2eSlogwang perf_atq_worker_burst(void *arg, const int enable_fwd_latency)
104*2bfe3f2eSlogwang {
105*2bfe3f2eSlogwang 	PERF_WORKER_INIT;
106*2bfe3f2eSlogwang 	uint16_t i;
107*2bfe3f2eSlogwang 	/* +1 to avoid prefetch out of array check */
108*2bfe3f2eSlogwang 	struct rte_event ev[BURST_SIZE + 1];
109*2bfe3f2eSlogwang 
110*2bfe3f2eSlogwang 	while (t->done == false) {
111*2bfe3f2eSlogwang 		uint16_t const nb_rx = rte_event_dequeue_burst(dev, port, ev,
112*2bfe3f2eSlogwang 				BURST_SIZE, 0);
113*2bfe3f2eSlogwang 
114*2bfe3f2eSlogwang 		if (!nb_rx) {
115*2bfe3f2eSlogwang 			rte_pause();
116*2bfe3f2eSlogwang 			continue;
117*2bfe3f2eSlogwang 		}
118*2bfe3f2eSlogwang 
119*2bfe3f2eSlogwang 		for (i = 0; i < nb_rx; i++) {
120*2bfe3f2eSlogwang 			if (enable_fwd_latency) {
121*2bfe3f2eSlogwang 				rte_prefetch0(ev[i+1].event_ptr);
122*2bfe3f2eSlogwang 				/* first stage in pipeline.
123*2bfe3f2eSlogwang 				 * mark time stamp to compute fwd latency
124*2bfe3f2eSlogwang 				 */
125*2bfe3f2eSlogwang 				atq_mark_fwd_latency(&ev[i]);
126*2bfe3f2eSlogwang 			}
127*2bfe3f2eSlogwang 			/* last stage in pipeline */
128*2bfe3f2eSlogwang 			if (unlikely((ev[i].sub_event_type % nb_stages)
129*2bfe3f2eSlogwang 						== laststage)) {
130*2bfe3f2eSlogwang 				if (enable_fwd_latency)
131*2bfe3f2eSlogwang 					cnt = perf_process_last_stage_latency(
132*2bfe3f2eSlogwang 						pool, &ev[i], w, bufs, sz, cnt);
133*2bfe3f2eSlogwang 				else
134*2bfe3f2eSlogwang 					cnt = perf_process_last_stage(pool,
135*2bfe3f2eSlogwang 						&ev[i], w, bufs, sz, cnt);
136*2bfe3f2eSlogwang 
137*2bfe3f2eSlogwang 				ev[i].op = RTE_EVENT_OP_RELEASE;
138*2bfe3f2eSlogwang 			} else {
139*2bfe3f2eSlogwang 				atq_fwd_event(&ev[i], sched_type_list,
140*2bfe3f2eSlogwang 						nb_stages);
141*2bfe3f2eSlogwang 			}
142*2bfe3f2eSlogwang 		}
143*2bfe3f2eSlogwang 
144*2bfe3f2eSlogwang 		uint16_t enq;
145*2bfe3f2eSlogwang 
146*2bfe3f2eSlogwang 		enq = rte_event_enqueue_burst(dev, port, ev, nb_rx);
147*2bfe3f2eSlogwang 		while (enq < nb_rx) {
148*2bfe3f2eSlogwang 			enq += rte_event_enqueue_burst(dev, port,
149*2bfe3f2eSlogwang 							ev + enq, nb_rx - enq);
150*2bfe3f2eSlogwang 		}
151*2bfe3f2eSlogwang 	}
152*2bfe3f2eSlogwang 	return 0;
153*2bfe3f2eSlogwang }
154*2bfe3f2eSlogwang 
155*2bfe3f2eSlogwang static int
156*2bfe3f2eSlogwang worker_wrapper(void *arg)
157*2bfe3f2eSlogwang {
158*2bfe3f2eSlogwang 	struct worker_data *w  = arg;
159*2bfe3f2eSlogwang 	struct evt_options *opt = w->t->opt;
160*2bfe3f2eSlogwang 
161*2bfe3f2eSlogwang 	const bool burst = evt_has_burst_mode(w->dev_id);
162*2bfe3f2eSlogwang 	const int fwd_latency = opt->fwd_latency;
163*2bfe3f2eSlogwang 
164*2bfe3f2eSlogwang 	/* allow compiler to optimize */
165*2bfe3f2eSlogwang 	if (!burst && !fwd_latency)
166*2bfe3f2eSlogwang 		return perf_atq_worker(arg, 0);
167*2bfe3f2eSlogwang 	else if (!burst && fwd_latency)
168*2bfe3f2eSlogwang 		return perf_atq_worker(arg, 1);
169*2bfe3f2eSlogwang 	else if (burst && !fwd_latency)
170*2bfe3f2eSlogwang 		return perf_atq_worker_burst(arg, 0);
171*2bfe3f2eSlogwang 	else if (burst && fwd_latency)
172*2bfe3f2eSlogwang 		return perf_atq_worker_burst(arg, 1);
173*2bfe3f2eSlogwang 
174*2bfe3f2eSlogwang 	rte_panic("invalid worker\n");
175*2bfe3f2eSlogwang }
176*2bfe3f2eSlogwang 
177*2bfe3f2eSlogwang static int
178*2bfe3f2eSlogwang perf_atq_launch_lcores(struct evt_test *test, struct evt_options *opt)
179*2bfe3f2eSlogwang {
180*2bfe3f2eSlogwang 	return perf_launch_lcores(test, opt, worker_wrapper);
181*2bfe3f2eSlogwang }
182*2bfe3f2eSlogwang 
183*2bfe3f2eSlogwang static int
184*2bfe3f2eSlogwang perf_atq_eventdev_setup(struct evt_test *test, struct evt_options *opt)
185*2bfe3f2eSlogwang {
186*2bfe3f2eSlogwang 	int ret;
187*2bfe3f2eSlogwang 	uint8_t queue;
188*2bfe3f2eSlogwang 
189*2bfe3f2eSlogwang 	const struct rte_event_dev_config config = {
190*2bfe3f2eSlogwang 			.nb_event_queues = atq_nb_event_queues(opt),
191*2bfe3f2eSlogwang 			.nb_event_ports = perf_nb_event_ports(opt),
192*2bfe3f2eSlogwang 			.nb_events_limit  = 4096,
193*2bfe3f2eSlogwang 			.nb_event_queue_flows = opt->nb_flows,
194*2bfe3f2eSlogwang 			.nb_event_port_dequeue_depth = 128,
195*2bfe3f2eSlogwang 			.nb_event_port_enqueue_depth = 128,
196*2bfe3f2eSlogwang 	};
197*2bfe3f2eSlogwang 
198*2bfe3f2eSlogwang 	ret = rte_event_dev_configure(opt->dev_id, &config);
199*2bfe3f2eSlogwang 	if (ret) {
200*2bfe3f2eSlogwang 		evt_err("failed to configure eventdev %d", opt->dev_id);
201*2bfe3f2eSlogwang 		return ret;
202*2bfe3f2eSlogwang 	}
203*2bfe3f2eSlogwang 
204*2bfe3f2eSlogwang 	struct rte_event_queue_conf q_conf = {
205*2bfe3f2eSlogwang 			.priority = RTE_EVENT_DEV_PRIORITY_NORMAL,
206*2bfe3f2eSlogwang 			.event_queue_cfg = RTE_EVENT_QUEUE_CFG_ALL_TYPES,
207*2bfe3f2eSlogwang 			.nb_atomic_flows = opt->nb_flows,
208*2bfe3f2eSlogwang 			.nb_atomic_order_sequences = opt->nb_flows,
209*2bfe3f2eSlogwang 	};
210*2bfe3f2eSlogwang 	/* queue configurations */
211*2bfe3f2eSlogwang 	for (queue = 0; queue < atq_nb_event_queues(opt); queue++) {
212*2bfe3f2eSlogwang 		ret = rte_event_queue_setup(opt->dev_id, queue, &q_conf);
213*2bfe3f2eSlogwang 		if (ret) {
214*2bfe3f2eSlogwang 			evt_err("failed to setup queue=%d", queue);
215*2bfe3f2eSlogwang 			return ret;
216*2bfe3f2eSlogwang 		}
217*2bfe3f2eSlogwang 	}
218*2bfe3f2eSlogwang 
219*2bfe3f2eSlogwang 	ret = perf_event_dev_port_setup(test, opt, 1 /* stride */,
220*2bfe3f2eSlogwang 					atq_nb_event_queues(opt));
221*2bfe3f2eSlogwang 	if (ret)
222*2bfe3f2eSlogwang 		return ret;
223*2bfe3f2eSlogwang 
224*2bfe3f2eSlogwang 	ret = evt_service_setup(opt->dev_id);
225*2bfe3f2eSlogwang 	if (ret) {
226*2bfe3f2eSlogwang 		evt_err("No service lcore found to run event dev.");
227*2bfe3f2eSlogwang 		return ret;
228*2bfe3f2eSlogwang 	}
229*2bfe3f2eSlogwang 
230*2bfe3f2eSlogwang 	ret = rte_event_dev_start(opt->dev_id);
231*2bfe3f2eSlogwang 	if (ret) {
232*2bfe3f2eSlogwang 		evt_err("failed to start eventdev %d", opt->dev_id);
233*2bfe3f2eSlogwang 		return ret;
234*2bfe3f2eSlogwang 	}
235*2bfe3f2eSlogwang 
236*2bfe3f2eSlogwang 	return 0;
237*2bfe3f2eSlogwang }
238*2bfe3f2eSlogwang 
239*2bfe3f2eSlogwang static void
240*2bfe3f2eSlogwang perf_atq_opt_dump(struct evt_options *opt)
241*2bfe3f2eSlogwang {
242*2bfe3f2eSlogwang 	perf_opt_dump(opt, atq_nb_event_queues(opt));
243*2bfe3f2eSlogwang }
244*2bfe3f2eSlogwang 
245*2bfe3f2eSlogwang static int
246*2bfe3f2eSlogwang perf_atq_opt_check(struct evt_options *opt)
247*2bfe3f2eSlogwang {
248*2bfe3f2eSlogwang 	return perf_opt_check(opt, atq_nb_event_queues(opt));
249*2bfe3f2eSlogwang }
250*2bfe3f2eSlogwang 
251*2bfe3f2eSlogwang static bool
252*2bfe3f2eSlogwang perf_atq_capability_check(struct evt_options *opt)
253*2bfe3f2eSlogwang {
254*2bfe3f2eSlogwang 	struct rte_event_dev_info dev_info;
255*2bfe3f2eSlogwang 
256*2bfe3f2eSlogwang 	rte_event_dev_info_get(opt->dev_id, &dev_info);
257*2bfe3f2eSlogwang 	if (dev_info.max_event_queues < atq_nb_event_queues(opt) ||
258*2bfe3f2eSlogwang 			dev_info.max_event_ports < perf_nb_event_ports(opt)) {
259*2bfe3f2eSlogwang 		evt_err("not enough eventdev queues=%d/%d or ports=%d/%d",
260*2bfe3f2eSlogwang 			atq_nb_event_queues(opt), dev_info.max_event_queues,
261*2bfe3f2eSlogwang 			perf_nb_event_ports(opt), dev_info.max_event_ports);
262*2bfe3f2eSlogwang 	}
263*2bfe3f2eSlogwang 	if (!evt_has_all_types_queue(opt->dev_id))
264*2bfe3f2eSlogwang 		return false;
265*2bfe3f2eSlogwang 
266*2bfe3f2eSlogwang 	return true;
267*2bfe3f2eSlogwang }
268*2bfe3f2eSlogwang 
269*2bfe3f2eSlogwang static const struct evt_test_ops perf_atq =  {
270*2bfe3f2eSlogwang 	.cap_check          = perf_atq_capability_check,
271*2bfe3f2eSlogwang 	.opt_check          = perf_atq_opt_check,
272*2bfe3f2eSlogwang 	.opt_dump           = perf_atq_opt_dump,
273*2bfe3f2eSlogwang 	.test_setup         = perf_test_setup,
274*2bfe3f2eSlogwang 	.mempool_setup      = perf_mempool_setup,
275*2bfe3f2eSlogwang 	.eventdev_setup     = perf_atq_eventdev_setup,
276*2bfe3f2eSlogwang 	.launch_lcores      = perf_atq_launch_lcores,
277*2bfe3f2eSlogwang 	.eventdev_destroy   = perf_eventdev_destroy,
278*2bfe3f2eSlogwang 	.mempool_destroy    = perf_mempool_destroy,
279*2bfe3f2eSlogwang 	.test_result        = perf_test_result,
280*2bfe3f2eSlogwang 	.test_destroy       = perf_test_destroy,
281*2bfe3f2eSlogwang };
282*2bfe3f2eSlogwang 
283*2bfe3f2eSlogwang EVT_TEST_REGISTER(perf_atq);
284