xref: /wlan-driver/qcacld-3.0/components/dsc/test/wlan_dsc_test.c (revision 5113495b16420b49004c444715d2daae2066e7dc)
1 /*
2  * Copyright (c) 2018-2020 The Linux Foundation. All rights reserved.
3  * Copyright (c) 2022 Qualcomm Innovation Center, Inc. All rights reserved.
4  *
5  * Permission to use, copy, modify, and/or distribute this software for
6  * any purpose with or without fee is hereby granted, provided that the
7  * above copyright notice and this permission notice appear in all
8  * copies.
9  *
10  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL
11  * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
12  * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE
13  * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
14  * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
15  * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
16  * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
17  * PERFORMANCE OF THIS SOFTWARE.
18  */
19 
20 #include "__wlan_dsc.h"
21 #include "qdf_event.h"
22 #include "qdf_threads.h"
23 #include "qdf_trace.h"
24 #include "qdf_types.h"
25 #include "wlan_dsc.h"
26 #include "wlan_dsc_test.h"
27 #include "cds_api.h"
28 
29 #define dsc_driver_trans_start(driver) dsc_driver_trans_start(driver, __func__)
30 #define dsc_psoc_trans_start(psoc) dsc_psoc_trans_start(psoc, __func__)
31 #define dsc_vdev_trans_start(vdev) dsc_vdev_trans_start(vdev, __func__)
32 
33 #define dsc_driver_trans_start_wait(driver) \
34 	dsc_driver_trans_start_wait(driver, "")
35 #define dsc_psoc_trans_start_wait(psoc) \
36 	dsc_psoc_trans_start_wait(psoc, __func__)
37 #define dsc_vdev_trans_start_wait(vdev) \
38 	dsc_vdev_trans_start_wait(vdev, __func__)
39 
nth_psoc(struct dsc_driver * driver,int n)40 static struct dsc_psoc *nth_psoc(struct dsc_driver *driver, int n)
41 {
42 	struct dsc_psoc *psoc;
43 
44 	QDF_BUG(n > 0);
45 	if (n <= 0)
46 		return NULL;
47 
48 	dsc_for_each_driver_psoc(driver, psoc) {
49 		n--;
50 		if (n)
51 			continue;
52 
53 		return psoc;
54 	}
55 
56 	QDF_DEBUG_PANIC("Failed to find nth psoc: %d", n);
57 
58 	return NULL;
59 }
60 
nth_vdev(struct dsc_psoc * psoc,int n)61 static struct dsc_vdev *nth_vdev(struct dsc_psoc *psoc, int n)
62 {
63 	struct dsc_vdev *vdev;
64 
65 	QDF_BUG(n > 0);
66 	if (n <= 0)
67 		return NULL;
68 
69 	dsc_for_each_psoc_vdev(psoc, vdev) {
70 		n--;
71 		if (n)
72 			continue;
73 
74 		return vdev;
75 	}
76 
77 	QDF_DEBUG_PANIC("Failed to find nth vdev: %d", n);
78 
79 	return NULL;
80 }
81 
__dsc_tree_destroy(struct dsc_driver * driver)82 static void __dsc_tree_destroy(struct dsc_driver *driver)
83 {
84 	struct dsc_psoc *psoc;
85 	struct dsc_psoc *next_psoc;
86 
87 	QDF_BUG(driver);
88 
89 	qdf_list_for_each_del(&driver->psocs, psoc, next_psoc, node) {
90 		struct dsc_vdev *vdev;
91 		struct dsc_vdev *next_vdev;
92 
93 		qdf_list_for_each_del(&psoc->vdevs, vdev, next_vdev, node)
94 			dsc_vdev_destroy(&vdev);
95 
96 		dsc_psoc_destroy(&psoc);
97 	}
98 
99 	dsc_driver_destroy(&driver);
100 }
101 
__dsc_tree_create(struct dsc_driver ** out_driver,uint8_t psocs_per_driver,uint8_t vdevs_per_psoc)102 static QDF_STATUS __dsc_tree_create(struct dsc_driver **out_driver,
103 				    uint8_t psocs_per_driver,
104 				    uint8_t vdevs_per_psoc)
105 {
106 	QDF_STATUS status;
107 	struct dsc_driver *driver;
108 	int i, j;
109 
110 	status = dsc_driver_create(&driver);
111 	if (QDF_IS_STATUS_ERROR(status)) {
112 		dsc_err("Failed to create driver; status:%u", status);
113 		return status;
114 	}
115 
116 	for (i = 0; i < psocs_per_driver; i++) {
117 		struct dsc_psoc *psoc;
118 
119 		status = dsc_psoc_create(driver, &psoc);
120 		if (QDF_IS_STATUS_ERROR(status)) {
121 			dsc_err("Failed to create psoc; status:%u", status);
122 			goto free_tree;
123 		}
124 
125 		for (j = 0; j < vdevs_per_psoc; j++) {
126 			struct dsc_vdev *vdev;
127 
128 			status = dsc_vdev_create(psoc, &vdev);
129 			if (QDF_IS_STATUS_ERROR(status)) {
130 				dsc_err("Failed to create vdev; status:%u",
131 					status);
132 				goto free_tree;
133 			}
134 		}
135 	}
136 
137 	*out_driver = driver;
138 
139 	return QDF_STATUS_SUCCESS;
140 
141 free_tree:
142 	__dsc_tree_destroy(driver);
143 
144 	return status;
145 }
146 
dsc_test_create_destroy(void)147 static uint32_t dsc_test_create_destroy(void)
148 {
149 	uint32_t errors = 0;
150 	QDF_STATUS status;
151 	struct dsc_driver *driver;
152 
153 	dsc_enter();
154 
155 	status = __dsc_tree_create(&driver, 2, 2);
156 	if (QDF_IS_STATUS_ERROR(status)) {
157 		errors++;
158 		goto exit;
159 	}
160 
161 	__dsc_tree_destroy(driver);
162 
163 exit:
164 	dsc_exit();
165 
166 	return errors;
167 }
168 
169 #define action_expect(obj, action, status, errors) \
170 do { \
171 	void *__obj = obj; \
172 	QDF_STATUS __expected = status; \
173 	QDF_STATUS __result; \
174 \
175 	__result = dsc_##obj##_##action##_start(__obj); \
176 	if (__result != __expected) { \
177 		dsc_err("FAIL: " #obj " " #action \
178 			"; expected " #status " (%u), found %u", \
179 			__expected, __result); \
180 		(errors)++; \
181 	} \
182 	if (QDF_IS_STATUS_SUCCESS(__result) && QDF_IS_STATUS_ERROR(__expected))\
183 		dsc_##obj##_##action##_stop(__obj); \
184 } while (false)
185 
dsc_test_driver_trans_blocks(void)186 static uint32_t dsc_test_driver_trans_blocks(void)
187 {
188 	uint32_t errors = 0;
189 	QDF_STATUS status;
190 	struct dsc_driver *driver;
191 	struct dsc_psoc *psoc;
192 	struct dsc_vdev *vdev;
193 
194 	dsc_enter();
195 
196 	/* setup */
197 
198 	status = __dsc_tree_create(&driver, 2, 2);
199 	if (QDF_IS_STATUS_ERROR(status)) {
200 		errors++;
201 		goto exit;
202 	}
203 
204 	/* test */
205 	/* a driver in transition should cause ... */
206 	action_expect(driver, trans, QDF_STATUS_SUCCESS, errors);
207 
208 	/* ... the same driver trans/ops to fail */
209 	action_expect(driver, trans, QDF_STATUS_E_AGAIN, errors);
210 	action_expect(driver, op, QDF_STATUS_E_AGAIN, errors);
211 
212 	/* ... children psoc trans/ops to fail */
213 	dsc_for_each_driver_psoc(driver, psoc) {
214 		action_expect(psoc, trans, QDF_STATUS_E_INVAL, errors);
215 		action_expect(psoc, op, QDF_STATUS_E_INVAL, errors);
216 
217 		/* ... grandchildren vdev trans/ops to fail */
218 		dsc_for_each_psoc_vdev(psoc, vdev) {
219 			action_expect(vdev, trans, QDF_STATUS_E_INVAL, errors);
220 			action_expect(vdev, op, QDF_STATUS_E_INVAL, errors);
221 		}
222 	}
223 
224 	/* teardown */
225 
226 	dsc_driver_trans_stop(driver);
227 
228 	__dsc_tree_destroy(driver);
229 
230 exit:
231 	dsc_exit();
232 
233 	return errors;
234 }
235 
dsc_test_psoc_trans_blocks(void)236 static uint32_t dsc_test_psoc_trans_blocks(void)
237 {
238 	uint32_t errors = 0;
239 	QDF_STATUS status;
240 	struct dsc_driver *driver;
241 	struct dsc_psoc *psoc;
242 	struct dsc_vdev *vdev;
243 
244 	dsc_enter();
245 
246 	/* setup */
247 
248 	status = __dsc_tree_create(&driver, 2, 2);
249 	if (QDF_IS_STATUS_ERROR(status)) {
250 		errors++;
251 		goto exit;
252 	}
253 
254 	/* test */
255 	/* a psoc in transition should cause ... */
256 	psoc = nth_psoc(driver, 1);
257 	action_expect(psoc, trans, QDF_STATUS_SUCCESS, errors);
258 
259 	/* ... driver trans/ops to fail */
260 	action_expect(driver, trans, QDF_STATUS_E_AGAIN, errors);
261 	action_expect(driver, op, QDF_STATUS_E_AGAIN, errors);
262 
263 	/* ... the same psoc trans/ops to fail */
264 	action_expect(psoc, trans, QDF_STATUS_E_AGAIN, errors);
265 	action_expect(psoc, op, QDF_STATUS_E_AGAIN, errors);
266 
267 	/* ... children vdev trans/ops to fail */
268 	dsc_for_each_psoc_vdev(psoc, vdev) {
269 		action_expect(vdev, trans, QDF_STATUS_E_BUSY, errors);
270 		action_expect(vdev, op, QDF_STATUS_E_BUSY, errors);
271 	}
272 
273 	/* ... while driver unload in progress vdev op and trans should be
274 	 * rejected with EINVAL
275 	 */
276 	cds_set_unload_in_progress(true);
277 	dsc_for_each_psoc_vdev(psoc, vdev) {
278 		action_expect(vdev, trans, QDF_STATUS_E_INVAL, errors);
279 		action_expect(vdev, op, QDF_STATUS_E_INVAL, errors);
280 	}
281 	cds_set_unload_in_progress(false);
282 
283 	/* ... while SSR recovery in progress vdev op and trans should be
284 	 * rejected with EINVAL
285 	 */
286 	cds_set_recovery_in_progress(true);
287 	dsc_for_each_psoc_vdev(psoc, vdev) {
288 		action_expect(vdev, trans, QDF_STATUS_E_INVAL, errors);
289 		action_expect(vdev, op, QDF_STATUS_E_INVAL, errors);
290 	}
291 	cds_set_recovery_in_progress(false);
292 
293 	/* a sibling psoc in transition should succeed and cause ... */
294 	psoc = nth_psoc(driver, 2);
295 	action_expect(psoc, trans, QDF_STATUS_SUCCESS, errors);
296 
297 	/* ... driver trans/ops to fail */
298 	action_expect(driver, trans, QDF_STATUS_E_AGAIN, errors);
299 	action_expect(driver, op, QDF_STATUS_E_AGAIN, errors);
300 
301 	/* ... the same psoc trans/ops to fail */
302 	action_expect(psoc, trans, QDF_STATUS_E_AGAIN, errors);
303 	action_expect(psoc, op, QDF_STATUS_E_AGAIN, errors);
304 
305 	/* ... children vdev trans/ops to fail */
306 	dsc_for_each_psoc_vdev(psoc, vdev) {
307 		action_expect(vdev, trans, QDF_STATUS_E_BUSY, errors);
308 		action_expect(vdev, op, QDF_STATUS_E_BUSY, errors);
309 	}
310 
311 	/* teardown */
312 
313 	dsc_for_each_driver_psoc(driver, psoc)
314 		dsc_psoc_trans_stop(psoc);
315 
316 	__dsc_tree_destroy(driver);
317 
318 exit:
319 	dsc_exit();
320 
321 	return errors;
322 }
323 
dsc_test_vdev_trans_blocks(void)324 static uint32_t dsc_test_vdev_trans_blocks(void)
325 {
326 	uint32_t errors = 0;
327 	QDF_STATUS status;
328 	struct dsc_driver *driver;
329 	struct dsc_psoc *psoc;
330 	struct dsc_vdev *vdev;
331 
332 	dsc_enter();
333 
334 	/* setup */
335 
336 	status = __dsc_tree_create(&driver, 2, 2);
337 	if (QDF_IS_STATUS_ERROR(status)) {
338 		errors++;
339 		goto exit;
340 	}
341 
342 	/* test */
343 
344 	/* a vdev in transition should cause ... */
345 	dsc_for_each_driver_psoc(driver, psoc) {
346 		dsc_for_each_psoc_vdev(psoc, vdev)
347 			action_expect(vdev, trans, QDF_STATUS_SUCCESS, errors);
348 	}
349 
350 	/* ... driver trans/ops to fail */
351 	action_expect(driver, trans, QDF_STATUS_E_AGAIN, errors);
352 	action_expect(driver, op, QDF_STATUS_E_AGAIN, errors);
353 
354 	/* ... psoc trans/ops to fail */
355 	dsc_for_each_driver_psoc(driver, psoc) {
356 		action_expect(psoc, trans, QDF_STATUS_E_AGAIN, errors);
357 		action_expect(psoc, op, QDF_STATUS_E_AGAIN, errors);
358 
359 		/* ... the same vdev trans/ops to fail */
360 		dsc_for_each_psoc_vdev(psoc, vdev) {
361 			action_expect(vdev, trans, QDF_STATUS_E_BUSY, errors);
362 			action_expect(vdev, op, QDF_STATUS_E_BUSY, errors);
363 		}
364 	}
365 
366 	/* teardown */
367 
368 	dsc_for_each_driver_psoc(driver, psoc) {
369 		dsc_for_each_psoc_vdev(psoc, vdev)
370 			dsc_vdev_trans_stop(vdev);
371 	}
372 
373 	__dsc_tree_destroy(driver);
374 
375 exit:
376 	dsc_exit();
377 
378 	return errors;
379 }
380 
381 #define THREAD_TIMEOUT 1000 /* ms */
382 #define dsc_event_wait(event) qdf_wait_single_event(event, THREAD_TIMEOUT)
383 
384 #define step_assert(field, expected) \
385 do { \
386 	uint32_t _step = ++(field); \
387 	uint32_t _expected = (expected); \
388 \
389 	if (_step != _expected) \
390 		QDF_DEBUG_PANIC("Step count is %u; Expected %u", \
391 				_step, _expected); \
392 } while (false)
393 
394 #define trans_waiting(ctx) (!qdf_list_empty(&(ctx)->trans.queue))
395 
396 struct thread_ctx {
397 	struct dsc_driver *driver;
398 	qdf_event_t start_vdev_trans;
399 	qdf_event_t start_vdev_wait;
400 	uint32_t step;
401 };
402 
dsc_thread_ops(void * context)403 static QDF_STATUS dsc_thread_ops(void *context)
404 {
405 	struct thread_ctx *ctx = context;
406 	struct dsc_driver *driver = ctx->driver;
407 	struct dsc_psoc *psoc = nth_psoc(driver, 1);
408 	struct dsc_vdev *vdev = nth_vdev(psoc, 1);
409 
410 	dsc_enter();
411 
412 	/* thread 1 is doing some operations ... */
413 	step_assert(ctx->step, 1);
414 	dsc_assert_success(dsc_driver_op_start(driver));
415 	dsc_assert_success(dsc_psoc_op_start(psoc));
416 	dsc_assert_success(dsc_vdev_op_start(vdev));
417 	step_assert(ctx->step, 2);
418 
419 	/* ... at which point, thread 2 starts to transition the vdevs */
420 	qdf_event_set(&ctx->start_vdev_trans);
421 
422 	/* meanwhile, thread 3,4,5 queue up to transition vdev/psoc/driver */
423 	while (!trans_waiting(driver))
424 		schedule();
425 
426 	/* at this point, each thread is:
427 	 * 1) doing operations
428 	 * 2) transitioning vdevs 1/2, waiting for ops to finish
429 	 * 3) waiting to transition vdev 1
430 	 * 4) waiting to transition psoc
431 	 * 5) waitint to transition driver
432 	 */
433 
434 	step_assert(ctx->step, 8);
435 	dsc_driver_op_stop(driver);
436 	schedule();
437 	dsc_psoc_op_stop(psoc);
438 	schedule();
439 	dsc_vdev_op_stop(vdev);
440 
441 	/* all operations complete; thread2 is now unblocked */
442 
443 	dsc_exit();
444 
445 	return QDF_STATUS_SUCCESS;
446 }
447 
dsc_thread_vdev_trans(void * context)448 static QDF_STATUS dsc_thread_vdev_trans(void *context)
449 {
450 	struct thread_ctx *ctx = context;
451 	struct dsc_driver *driver = ctx->driver;
452 	struct dsc_psoc *psoc = nth_psoc(driver, 1);
453 	struct dsc_vdev *vdev;
454 
455 	dsc_enter();
456 
457 	/* wait for thread 1 to start operations */
458 	dsc_assert_success(dsc_event_wait(&ctx->start_vdev_trans));
459 
460 	/* start transitions on all vdevs */
461 	step_assert(ctx->step, 3);
462 	dsc_for_each_psoc_vdev(psoc, vdev)
463 		dsc_assert_success(dsc_vdev_trans_start(vdev));
464 	step_assert(ctx->step, 4);
465 
466 	/* meanwhile, thread 3,4,5 queue up to transition vdev/psoc/driver */
467 	qdf_event_set(&ctx->start_vdev_wait);
468 
469 	/* wait for thread 1 to complete pending vdev ops */
470 	dsc_for_each_psoc_vdev(psoc, vdev)
471 		dsc_vdev_wait_for_ops(vdev);
472 
473 	/* actual vdev transition work would happen here */
474 
475 	/* stop transition on vdev 1 */
476 	step_assert(ctx->step, 9);
477 	dsc_vdev_trans_stop(nth_vdev(psoc, 1));
478 
479 	/* psoc trans should not start until both vdev trans are complete */
480 	schedule();
481 	step_assert(ctx->step, 10);
482 	dsc_vdev_trans_stop(nth_vdev(psoc, 2));
483 
484 	dsc_exit();
485 
486 	return QDF_STATUS_SUCCESS;
487 }
488 
dsc_thread_vdev_wait(void * context)489 static QDF_STATUS dsc_thread_vdev_wait(void *context)
490 {
491 	struct thread_ctx *ctx = context;
492 	struct dsc_vdev *vdev = nth_vdev(nth_psoc(ctx->driver, 1), 1);
493 
494 	dsc_enter();
495 
496 	dsc_assert_success(dsc_event_wait(&ctx->start_vdev_wait));
497 
498 	step_assert(ctx->step, 5);
499 	/* vdev trans queues first ... */
500 	dsc_assert_success(dsc_vdev_trans_start_wait(vdev));
501 	/* ... but de-queues third */
502 	step_assert(ctx->step, 15);
503 
504 	dsc_vdev_wait_for_ops(vdev);
505 
506 	step_assert(ctx->step, 16);
507 	dsc_vdev_trans_stop(vdev);
508 
509 	dsc_exit();
510 
511 	return QDF_STATUS_SUCCESS;
512 }
513 
dsc_thread_psoc_wait(void * context)514 static QDF_STATUS dsc_thread_psoc_wait(void *context)
515 {
516 	struct thread_ctx *ctx = context;
517 	struct dsc_psoc *psoc = nth_psoc(ctx->driver, 1);
518 	struct dsc_vdev *vdev = nth_vdev(psoc, 1);
519 
520 	dsc_enter();
521 
522 	while (!trans_waiting(vdev))
523 		schedule();
524 
525 	step_assert(ctx->step, 6);
526 	/* psoc trans queues second ... */
527 	dsc_assert_success(dsc_psoc_trans_start_wait(psoc));
528 	/* ... and de-queues second */
529 	step_assert(ctx->step, 13);
530 
531 	dsc_psoc_wait_for_ops(psoc);
532 
533 	step_assert(ctx->step, 14);
534 	dsc_psoc_trans_stop(psoc);
535 
536 	dsc_exit();
537 
538 	return QDF_STATUS_SUCCESS;
539 }
540 
dsc_thread_driver_wait(void * context)541 static QDF_STATUS dsc_thread_driver_wait(void *context)
542 {
543 	struct thread_ctx *ctx = context;
544 	struct dsc_driver *driver = ctx->driver;
545 	struct dsc_psoc *psoc = nth_psoc(driver, 1);
546 
547 	dsc_enter();
548 
549 	while (!trans_waiting(psoc))
550 		schedule();
551 
552 	step_assert(ctx->step, 7);
553 	/* driver trans queues third ... */
554 	dsc_assert_success(dsc_driver_trans_start_wait(driver));
555 	/* ... but de-queues first */
556 	step_assert(ctx->step, 11);
557 
558 	dsc_driver_wait_for_ops(driver);
559 
560 	step_assert(ctx->step, 12);
561 	dsc_driver_trans_stop(driver);
562 
563 	dsc_exit();
564 
565 	return QDF_STATUS_SUCCESS;
566 }
567 
dsc_test_trans_wait(void)568 static uint32_t dsc_test_trans_wait(void)
569 {
570 	uint32_t errors = 0;
571 	QDF_STATUS status;
572 	qdf_thread_t *ops_thread;
573 	qdf_thread_t *vdev_trans_thread;
574 	qdf_thread_t *vdev_wait_thread;
575 	qdf_thread_t *psoc_wait_thread;
576 	qdf_thread_t *driver_wait_thread;
577 	struct thread_ctx ctx = { 0 };
578 
579 	dsc_enter();
580 
581 	status = __dsc_tree_create(&ctx.driver, 1, 2);
582 	if (QDF_IS_STATUS_ERROR(status)) {
583 		errors++;
584 		goto exit;
585 	}
586 
587 	dsc_assert_success(qdf_event_create(&ctx.start_vdev_trans));
588 	dsc_assert_success(qdf_event_create(&ctx.start_vdev_wait));
589 
590 	dsc_debug("starting threads");
591 
592 	ops_thread = qdf_thread_run(dsc_thread_ops, &ctx);
593 	vdev_trans_thread = qdf_thread_run(dsc_thread_vdev_trans, &ctx);
594 	vdev_wait_thread = qdf_thread_run(dsc_thread_vdev_wait, &ctx);
595 	psoc_wait_thread = qdf_thread_run(dsc_thread_psoc_wait, &ctx);
596 	driver_wait_thread = qdf_thread_run(dsc_thread_driver_wait, &ctx);
597 
598 	qdf_thread_join(ops_thread);
599 	qdf_thread_join(vdev_trans_thread);
600 	qdf_thread_join(vdev_wait_thread);
601 	qdf_thread_join(psoc_wait_thread);
602 	qdf_thread_join(driver_wait_thread);
603 
604 	dsc_debug("threads joined");
605 
606 	qdf_event_destroy(&ctx.start_vdev_wait);
607 	qdf_event_destroy(&ctx.start_vdev_trans);
608 
609 	__dsc_tree_destroy(ctx.driver);
610 
611 exit:
612 	dsc_exit();
613 
614 	return errors;
615 }
616 
dsc_unit_test(void)617 uint32_t dsc_unit_test(void)
618 {
619 	uint32_t errors = 0;
620 
621 	errors += dsc_test_create_destroy();
622 	errors += dsc_test_driver_trans_blocks();
623 	errors += dsc_test_psoc_trans_blocks();
624 	errors += dsc_test_vdev_trans_blocks();
625 	errors += dsc_test_trans_wait();
626 
627 	return errors;
628 }
629 
630