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