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