1/*
2 * Copyright (c) 2016-2017  Intel Corporation.
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at:
6 *
7 *     http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
14 */
15
16#ifndef TEST_TLE_DRING_H_
17#define TEST_TLE_DRING_H_
18
19#include <string.h>
20#include <stdarg.h>
21#include <stdio.h>
22#include <stdlib.h>
23#include <stdint.h>
24#include <inttypes.h>
25#include <errno.h>
26
27#include <gtest/gtest.h>
28#include <gmock/gmock.h>
29
30#include <rte_common.h>
31#include <rte_log.h>
32#include <rte_errno.h>
33#include <rte_launch.h>
34#include <rte_cycles.h>
35#include <rte_eal.h>
36#include <rte_per_lcore.h>
37#include <rte_lcore.h>
38#include <rte_ring.h>
39#include <rte_random.h>
40
41#include <tle_dring.h>
42
43struct dring_arg {
44	struct tle_dring *dr;
45	struct rte_ring *r;
46	uint32_t iter;
47	int32_t enq_type;
48	int32_t deq_type;
49	uint32_t enq;
50	uint32_t deq;
51};
52
53class dring: public ::testing::Test {
54protected:
55
56	virtual void SetUp(void) {};
57	virtual void TearDown(void) {};
58
59	int32_t rc;
60	struct rte_ring *r;
61	struct tle_dring dr;
62	struct dring_arg arg[RTE_MAX_LCORE];
63
64};
65
66#define OBJ_NUM		UINT16_MAX
67#define ITER_NUM	(4 * OBJ_NUM)
68
69enum {
70	NONE,
71	SINGLE,
72	MULTI,
73};
74
75/*
76 * Each enqueued object will contain:
77 * [2-3]B: it's own sequence number.
78 * [0-1]B: next object sequence number, or UINT16_MAX.
79 */
80static void
81test_fill_obj(uintptr_t obj[], uint32_t num)
82{
83	uint32_t i;
84
85	for (i = 0; i != num - 1; i++)
86		obj[i] = i << 16 | (i + 1);
87
88	obj[i] = i << 16 | UINT16_MAX;
89}
90
91static uint32_t
92test_check_obj(uintptr_t obj[], uint32_t num)
93{
94	uint32_t i, h, l, oh, ol;
95
96	h = obj[0] >> 16;
97	l = obj[0] & UINT16_MAX;
98
99	if (h + 1 != l && l != UINT16_MAX)
100		return 0;
101
102	if (l == UINT16_MAX)
103		l = 0;
104
105	for (i = 1; i != num; i++) {
106
107		oh = obj[i] >> 16;
108		ol = obj[i] & UINT16_MAX;
109
110		if (l != oh || (oh + 1 != ol && ol != UINT16_MAX))
111			return i;
112
113		l = ol;
114		if (l == UINT16_MAX)
115			l = 0;
116	}
117
118	return num;
119}
120
121/*
122 * free memory allocated for drbs and for the ring itself.
123 */
124static void
125fini_drb_ring(struct rte_ring *r)
126{
127	struct tle_drb *drb;
128
129	/* free drbs. */
130	while (rte_ring_dequeue(r, (void **)&drb) == 0)
131		free(drb);
132
133	/* free ring. */
134	free(r);
135}
136
137static struct rte_ring *
138init_drb_ring(uint32_t num)
139{
140	uint32_t i, k, n;
141	size_t sz, tsz;
142	struct rte_ring *r;
143	struct tle_drb *drb;
144
145	/* allocate and initialise rte_ring. */
146
147	n = rte_align32pow2(num);
148	sz =  rte_ring_get_memsize(n);
149
150	r = (struct rte_ring *)calloc(1, sz);
151	if (r == NULL) {
152		printf("%s:%d(%u) failed to allocate %zu bytes;\n",
153			__func__, __LINE__, num, sz);
154		return NULL;
155	}
156
157	rte_ring_init(r, __func__, n, 0);
158
159	/* allocate drbs and put them into the ring. */
160
161	tsz = sz;
162	for (i = 0; i != num; i += k) {
163		k =  rte_rand() % (UINT8_MAX + 1) + 1;
164		k = RTE_MIN(k, num - i);
165		sz = tle_drb_calc_size(k);
166		drb = (struct tle_drb *)calloc(1, sz);
167		if (drb == NULL) {
168			printf("%s:%d(%u) %u-th iteration: "
169				"failed to allocate %zu bytes;\n",
170				__func__, __LINE__, num, i, sz);
171			fini_drb_ring(r);
172			return NULL;
173		}
174		drb->size = k;
175		rte_ring_enqueue(r, drb);
176		tsz += sz;
177	}
178
179	printf("%s(%u) total %zu bytes allocated, number of drbs: %u;\n",
180		__func__, num, tsz, rte_ring_count(r));
181	return r;
182}
183
184static int
185test_dring_enqueue(struct tle_dring *dr, struct rte_ring *r, uint32_t num,
186	int32_t type)
187{
188	uint32_t i, j, k, lc, nb;
189	struct tle_drb *drb[num];
190	uintptr_t obj[num];
191
192	lc = rte_lcore_id();
193
194	/* prepare drbs to enqueue up to *num* objects. */
195	for (i = 0, j = 0; i != num; i += k, j++) {
196
197		if (rte_ring_dequeue(r, (void **)&drb[j]) != 0)
198			break;
199
200		/* udata value for unused drb should be zero. */
201		if (drb[j]->udata != NULL) {
202			printf("error @ %s:%d(%p, %u) at lcore %u: "
203				"erroneous drb@%p={udata=%p, size=%u,};\n",
204				__func__, __LINE__, dr, num, lc, drb[j],
205				drb[j]->udata, drb[j]->size);
206			return -EFAULT;
207		}
208
209		/* update udata value with current lcore id. */
210		drb[j]->udata = (void *)(uintptr_t)(lc + 1);
211		k = drb[j]->size;
212		k = RTE_MIN(k, num - i);
213	}
214
215	/* no free drbs left. */
216	if (i == 0)
217		return 0;
218
219	/* fill objects to enqueue. */
220	test_fill_obj(obj, i);
221
222	/* enqueue into the dring. */
223	nb = j;
224	if (type == SINGLE)
225		k = tle_dring_sp_enqueue(dr, (const void **)obj, i, drb, &nb);
226	else if (type == MULTI)
227		k = tle_dring_mp_enqueue(dr, (const void **)obj, i, drb, &nb);
228	else
229		return -EINVAL;
230
231	if (k != i) {
232		printf("%s:%d(%p, %p, %u): failed to enqueue %u objects;\n",
233			__func__, __LINE__, dr, r, num, i);
234	}
235
236	/* free unused drbs */
237	for (i = j - nb; i != j; i++) {
238		if ((uintptr_t)drb[i]->udata != lc + 1) {
239			printf("error @ %s:%d(%p, %u) at lcore %u: "
240				"erroneous drb@%p={udata=%p, size=%u,};\n",
241				__func__, __LINE__, dr, num, lc, drb[i],
242				drb[i]->udata, drb[i]->size);
243			return -EFAULT;
244		}
245		drb[i]->udata = NULL;
246		rte_ring_enqueue(r, drb[i]);
247	}
248
249	return k;
250}
251
252static int
253test_dring_dequeue(struct tle_dring *dr, struct rte_ring *r, uint32_t num,
254	int32_t type)
255{
256	uint32_t i, k, lc, n, t;
257	struct tle_drb *drb[num];
258	uintptr_t obj[num];
259
260	lc = rte_lcore_id();
261	k = num;
262
263	/* dequeue objects. */
264	if (type == SINGLE)
265		n = tle_dring_sc_dequeue(dr, (const void **)obj, num, drb, &k);
266	else if (type == MULTI)
267		n = tle_dring_mc_dequeue(dr, (const void **)obj, num, drb, &k);
268	else
269		return -EINVAL;
270
271	if (n == 0)
272		return 0;
273
274	/* check the data returned. */
275	t = test_check_obj(obj, n);
276	if (t != n) {
277		printf("%s:%d(%p, %u) at lcore %u: invalid dequeued object, "
278			"n=%u, idx=%u, obj=%#x, prev obj=%#x;\n",
279			__func__, __LINE__, dr, num, lc, n, t,
280			(uint32_t)obj[t], (t == 0) ? 0 : (uint32_t)obj[t - 1]);
281		return -EFAULT;
282	}
283
284	/* check and free drbs. */
285	for (i = 0; i != k; i++) {
286		/* udata value for drb in use shouldn't be zero. */
287		if (drb[i]->udata == NULL) {
288			printf("error @ %s:%d(%p, %u) at lcore %u: "
289				"erroneous drb@%p={udata=%p, size=%u,};\n",
290				__func__, __LINE__, dr, num, lc, drb[i],
291				drb[i]->udata, drb[i]->size);
292			return -EFAULT;
293		}
294		drb[i]->udata = NULL;
295		rte_ring_enqueue(r, drb[i]);
296	}
297
298	return n;
299}
300
301static int
302test_dring_enq_deq(struct dring_arg *arg)
303{
304	int32_t rc;
305	uint32_t i, lc, n;
306
307	rc = 0;
308	arg->enq = 0;
309	arg->deq = 0;
310	lc = rte_lcore_id();
311
312	for (i = 0; i != arg->iter; i++) {
313
314		/* try to enqueue random number of objects. */
315		if (arg->enq_type != NONE) {
316			n = rte_rand() % (UINT8_MAX + 1);
317			rc = test_dring_enqueue(arg->dr, arg->r, n,
318				arg->enq_type);
319			if (rc < 0)
320				break;
321			arg->enq += rc;
322		}
323
324		/* try to dequeue random number of objects. */
325		if (arg->deq_type != NONE) {
326			n = rte_rand() % (UINT8_MAX + 1);
327			rc = test_dring_dequeue(arg->dr, arg->r, n,
328				arg->deq_type);
329			if (rc < 0)
330				break;
331			arg->deq += rc;
332		}
333	}
334
335	if (rc < 0)
336		return rc;
337
338	/* dequeue remaining objects. */
339	while (arg->deq_type != NONE && arg->enq != arg->deq) {
340
341		/* try to dequeue random number of objects. */
342		n = rte_rand() % (UINT8_MAX + 1) + 1;
343		rc = test_dring_dequeue(arg->dr, arg->r, n, arg->deq_type);
344		if (rc <= 0)
345			break;
346		arg->deq += rc;
347	}
348
349	printf("%s:%d(lcore=%u, enq_type=%d, deq_type=%d): "
350		"%u objects enqueued, %u objects dequeued\n",
351		__func__, __LINE__, lc, arg->enq_type, arg->deq_type,
352		arg->enq, arg->deq);
353	return 0;
354}
355
356static int
357test_dring_worker(void *arg)
358{
359	struct dring_arg *p;
360
361	p = (struct dring_arg *)arg;
362	return test_dring_enq_deq(p);
363}
364
365/*
366 * enqueue/dequeue by multiple threads.
367 */
368static int
369test_dring_mt(int32_t master_enq_type, int32_t master_deq_type,
370	int32_t slave_enq_type, int32_t slave_deq_type)
371{
372	int32_t rc;
373	uint32_t lc;
374	uint64_t deq, enq;
375	struct rte_ring *r;
376	struct tle_dring dr;
377	struct dring_arg arg[RTE_MAX_LCORE];
378
379	tle_dring_reset(&dr, 0);
380	r = init_drb_ring(OBJ_NUM);
381	if (r == NULL)
382		return -ENOMEM;
383
384	memset(arg, 0, sizeof(arg));
385
386	/* launch on all slaves */
387	RTE_LCORE_FOREACH_SLAVE(lc) {
388		arg[lc].dr = &dr;
389		arg[lc].r = r;
390		arg[lc].iter = ITER_NUM;
391		arg[lc].enq_type = slave_enq_type;
392		arg[lc].deq_type = slave_deq_type;
393		rte_eal_remote_launch(test_dring_worker, &arg[lc], lc);
394	}
395
396	/* launch on master */
397	lc = rte_lcore_id();
398	arg[lc].dr = &dr;
399	arg[lc].r = r;
400	arg[lc].iter = ITER_NUM;
401	arg[lc].enq_type = master_enq_type;
402	arg[lc].deq_type = master_deq_type;
403	rc = test_dring_worker(&arg[lc]);
404	enq = arg[lc].enq;
405	deq = arg[lc].deq;
406
407	/* wait for slaves. */
408	RTE_LCORE_FOREACH_SLAVE(lc) {
409		rc |= rte_eal_wait_lcore(lc);
410		enq += arg[lc].enq;
411		deq += arg[lc].deq;
412	}
413
414	printf("%s:%d: total %" PRIu64 " objects enqueued, %"
415		PRIu64 " objects dequeued\n",
416		__func__, __LINE__, enq, deq);
417
418	rc = (rc != 0) ? rc : (enq != deq);
419	if (rc != 0)
420		tle_dring_dump(stdout, 1, &dr);
421
422	fini_drb_ring(r);
423	return rc;
424}
425
426#endif /* TEST_TLE_DRING_H_ */
427