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