1 /* 2 * Copyright (c) 2017-2018 The Linux Foundation. All rights reserved. 3 * 4 * Permission to use, copy, modify, and/or distribute this software for 5 * any purpose with or without fee is hereby granted, provided that the 6 * above copyright notice and this permission notice appear in all 7 * copies. 8 * 9 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL 10 * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED 11 * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE 12 * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL 13 * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR 14 * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER 15 * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR 16 * PERFORMANCE OF THIS SOFTWARE. 17 */ 18 /** 19 * DOC: wlan_serialization_non_scan.c 20 * This file defines the functions which deals with 21 * serialization non scan commands. 22 */ 23 24 #include <wlan_objmgr_psoc_obj.h> 25 #include <wlan_objmgr_pdev_obj.h> 26 #include <wlan_objmgr_vdev_obj.h> 27 #include "wlan_serialization_main_i.h" 28 #include "wlan_serialization_utils_i.h" 29 #include "wlan_serialization_non_scan_i.h" 30 31 /** 32 * wlan_serialization_is_active_nonscan_cmd_allowed() - find if cmd allowed 33 * @pdev: pointer to pdev object 34 * 35 * This API will be called to find out if non scan cmd is allowed. 36 * 37 * Return: true or false 38 */ 39 bool 40 wlan_serialization_is_active_non_scan_cmd_allowed( 41 struct wlan_serialization_command *cmd) 42 { 43 struct wlan_serialization_pdev_queue *pdev_queue; 44 struct wlan_ser_pdev_obj *ser_pdev_obj; 45 uint32_t vdev_active_cmd_bitmap; 46 bool blocking_cmd_active = 0; 47 uint8_t blocking_cmd_waiting = 0; 48 bool status = false; 49 uint32_t vdev_id; 50 51 ser_pdev_obj = wlan_serialization_get_pdev_obj( 52 wlan_serialization_get_pdev_from_cmd(cmd)); 53 54 pdev_queue = wlan_serialization_get_pdev_queue_obj(ser_pdev_obj, 55 cmd->cmd_type); 56 57 vdev_active_cmd_bitmap = pdev_queue->vdev_active_cmd_bitmap; 58 blocking_cmd_active = pdev_queue->blocking_cmd_active; 59 blocking_cmd_waiting = pdev_queue->blocking_cmd_waiting; 60 61 /* 62 * Command is blocking 63 */ 64 if (cmd->is_blocking) { 65 /* 66 * For blocking commands, no other 67 * commands from any vdev should be active 68 */ 69 if (vdev_active_cmd_bitmap) { 70 status = false; 71 pdev_queue->blocking_cmd_waiting++; 72 } else { 73 status = true; 74 } 75 } else { 76 /* 77 * Command is non blocking 78 * For activating non blocking commands, if there any blocking 79 * commands, waiting or active, put it to pending queue 80 */ 81 if (blocking_cmd_active || blocking_cmd_waiting) { 82 status = false; 83 } else { 84 /* 85 * For non blocking command, and no blocking commands 86 * waiting or active, check if a cmd for that vdev is active 87 * If not active, put to active else pending queue 88 */ 89 vdev_id = wlan_vdev_get_id(cmd->vdev); 90 status = vdev_active_cmd_bitmap & (1 << vdev_id) 91 ? false : true; 92 } 93 } 94 return status; 95 } 96 97 enum wlan_serialization_status wlan_ser_add_non_scan_cmd( 98 struct wlan_ser_pdev_obj *ser_pdev_obj, 99 struct wlan_serialization_command_list *cmd_list, 100 uint8_t is_cmd_for_active_queue) 101 { 102 enum wlan_serialization_status pdev_status, vdev_status; 103 enum wlan_serialization_status status = WLAN_SER_CMD_DENIED_UNSPECIFIED; 104 struct wlan_serialization_command_list *pcmd_list; 105 uint8_t vdev_id; 106 struct wlan_serialization_pdev_queue *pdev_queue; 107 108 ser_debug("add non scan cmd: type[%d] id[%d] prio[%d] blocking[%d]", 109 cmd_list->cmd.cmd_type, 110 cmd_list->cmd.cmd_id, 111 cmd_list->cmd.is_high_priority, 112 cmd_list->cmd.is_blocking); 113 114 vdev_status = wlan_serialization_add_cmd_to_vdev_queue( 115 ser_pdev_obj, cmd_list, is_cmd_for_active_queue); 116 117 if (vdev_status == WLAN_SER_CMD_DENIED_LIST_FULL) { 118 status = vdev_status; 119 goto vdev_error; 120 } 121 122 if (is_cmd_for_active_queue) { 123 if (vdev_status != WLAN_SER_CMD_ACTIVE) { 124 ser_err("Failed to add to vdev active queue"); 125 QDF_ASSERT(0); 126 goto vdev_error; 127 } 128 } else { 129 if (vdev_status != WLAN_SER_CMD_PENDING) { 130 ser_err("Failed to add to vdev pending queue"); 131 QDF_ASSERT(0); 132 goto vdev_error; 133 } 134 } 135 136 pdev_status = wlan_serialization_add_cmd_to_pdev_queue( 137 ser_pdev_obj, cmd_list, is_cmd_for_active_queue); 138 139 if (pdev_status == WLAN_SER_CMD_DENIED_LIST_FULL) { 140 status = pdev_status; 141 goto pdev_error; 142 } 143 144 if (is_cmd_for_active_queue) { 145 if (pdev_status != WLAN_SER_CMD_ACTIVE) { 146 ser_err("Failed to add to pdev active queue"); 147 QDF_ASSERT(0); 148 goto pdev_error; 149 } 150 } else { 151 if (pdev_status != WLAN_SER_CMD_PENDING) { 152 ser_err("Failed to add to pdev pending queue"); 153 QDF_ASSERT(0); 154 goto pdev_error; 155 } 156 } 157 pdev_error: 158 /* 159 * If cmd added to vdev queue, but failed while 160 * adding to pdev queue, remove cmd from vdev queue as well 161 */ 162 if (pdev_status != vdev_status) { 163 wlan_serialization_remove_cmd_from_vdev_queue( 164 ser_pdev_obj, &pcmd_list, 165 &cmd_list->cmd, 166 is_cmd_for_active_queue); 167 } else { 168 status = pdev_status; 169 } 170 171 if (is_cmd_for_active_queue) { 172 pdev_queue = wlan_serialization_get_pdev_queue_obj( 173 ser_pdev_obj, cmd_list->cmd.cmd_type); 174 vdev_id = wlan_vdev_get_id(cmd_list->cmd.vdev); 175 pdev_queue->vdev_active_cmd_bitmap |= (1 << vdev_id); 176 177 if (cmd_list->cmd.is_blocking) 178 pdev_queue->blocking_cmd_active = 1; 179 } 180 181 vdev_error: 182 return status; 183 } 184 185 enum wlan_serialization_status 186 wlan_ser_move_non_scan_pending_to_active( 187 struct wlan_ser_pdev_obj *ser_pdev_obj, 188 struct wlan_objmgr_vdev *vdev, 189 bool blocking_cmd_removed) 190 { 191 struct wlan_serialization_command_list *pending_cmd_list = NULL; 192 struct wlan_serialization_command_list *active_cmd_list; 193 struct wlan_serialization_command cmd_to_remove; 194 enum wlan_serialization_status status = WLAN_SER_CMD_DENIED_UNSPECIFIED; 195 struct wlan_serialization_pdev_queue *pdev_queue; 196 struct wlan_serialization_vdev_queue *vdev_queue; 197 198 struct wlan_ser_vdev_obj *ser_vdev_obj; 199 200 qdf_list_t *pending_queue; 201 qdf_list_node_t *pending_node = NULL; 202 QDF_STATUS qdf_status = QDF_STATUS_E_FAILURE; 203 uint32_t blocking_cmd_waiting = 0; 204 uint32_t vdev_id; 205 uint32_t qsize; 206 bool vdev_cmd_active = 0; 207 bool vdev_queue_lookup = false; 208 209 pdev_queue = &ser_pdev_obj->pdev_q[SER_PDEV_QUEUE_COMP_NON_SCAN]; 210 211 ser_vdev_obj = wlan_serialization_get_vdev_obj(vdev); 212 vdev_queue = &ser_vdev_obj->vdev_q[SER_VDEV_QUEUE_COMP_NON_SCAN]; 213 214 ser_enter(); 215 216 if (!ser_pdev_obj) { 217 ser_err("Can't find ser_pdev_obj"); 218 goto error; 219 } 220 221 wlan_serialization_acquire_lock(&pdev_queue->pdev_queue_lock); 222 223 blocking_cmd_waiting = pdev_queue->blocking_cmd_waiting; 224 225 if (!blocking_cmd_removed && !blocking_cmd_waiting) { 226 pending_queue = &vdev_queue->pending_list; 227 vdev_queue_lookup = true; 228 } else { 229 pending_queue = &pdev_queue->pending_list; 230 } 231 232 qsize = wlan_serialization_list_size(pending_queue); 233 if (!qsize) { 234 wlan_serialization_release_lock( 235 &pdev_queue->pdev_queue_lock); 236 ser_err("Pending Queue is empty"); 237 goto error; 238 } 239 240 while (qsize--) { 241 qdf_status = wlan_serialization_get_cmd_from_queue( 242 pending_queue, &pending_node); 243 if (qdf_status != QDF_STATUS_SUCCESS) { 244 ser_err("can't peek cmd"); 245 break; 246 } 247 248 if (vdev_queue_lookup) { 249 pending_cmd_list = 250 qdf_container_of( 251 pending_node, 252 struct wlan_serialization_command_list, 253 vdev_node); 254 } else { 255 pending_cmd_list = 256 qdf_container_of( 257 pending_node, 258 struct wlan_serialization_command_list, 259 pdev_node); 260 } 261 262 if (!pending_cmd_list) { 263 wlan_serialization_release_lock( 264 &pdev_queue->pdev_queue_lock); 265 ser_debug( 266 "non scan cmd cannot move frm pendin to actv"); 267 goto error; 268 } 269 270 if (!vdev_queue_lookup) { 271 if (pending_cmd_list->cmd.is_blocking && 272 pdev_queue->vdev_active_cmd_bitmap) { 273 break; 274 } 275 276 vdev_id = wlan_vdev_get_id(pending_cmd_list->cmd.vdev); 277 vdev_cmd_active = 278 pdev_queue->vdev_active_cmd_bitmap & 279 (1 << vdev_id); 280 if (vdev_cmd_active) 281 continue; 282 } 283 284 qdf_mem_copy(&cmd_to_remove, &pending_cmd_list->cmd, 285 sizeof(struct wlan_serialization_command)); 286 287 qdf_status = wlan_ser_remove_non_scan_cmd(ser_pdev_obj, 288 &pending_cmd_list, 289 &cmd_to_remove, 290 false); 291 292 if (QDF_STATUS_SUCCESS != qdf_status) { 293 wlan_serialization_release_lock( 294 &pdev_queue->pdev_queue_lock); 295 ser_err("Can't remove cmd from pendingQ id-%d type-%d", 296 pending_cmd_list->cmd.cmd_id, 297 pending_cmd_list->cmd.cmd_type); 298 QDF_ASSERT(0); 299 status = WLAN_SER_CMD_DENIED_UNSPECIFIED; 300 goto error; 301 } 302 303 active_cmd_list = pending_cmd_list; 304 305 status = wlan_ser_add_non_scan_cmd( 306 ser_pdev_obj, active_cmd_list, true); 307 308 if (WLAN_SER_CMD_ACTIVE != status) { 309 wlan_serialization_release_lock( 310 &pdev_queue->pdev_queue_lock); 311 ser_err("Can't move cmd to activeQ id-%d type-%d", 312 pending_cmd_list->cmd.cmd_id, 313 pending_cmd_list->cmd.cmd_type); 314 wlan_serialization_insert_back( 315 &pdev_queue->cmd_pool_list, 316 &active_cmd_list->pdev_node); 317 status = WLAN_SER_CMD_DENIED_UNSPECIFIED; 318 goto error; 319 } 320 321 qdf_atomic_set_bit(CMD_MARKED_FOR_ACTIVATION, 322 &active_cmd_list->cmd_in_use); 323 324 wlan_serialization_release_lock(&pdev_queue->pdev_queue_lock); 325 326 wlan_serialization_activate_cmd(active_cmd_list, ser_pdev_obj); 327 328 wlan_serialization_acquire_lock(&pdev_queue->pdev_queue_lock); 329 330 if (vdev_queue_lookup) 331 break; 332 333 pending_node = NULL; 334 335 if (active_cmd_list->cmd.is_blocking) { 336 pdev_queue->blocking_cmd_waiting--; 337 break; 338 } 339 } 340 341 wlan_serialization_release_lock(&pdev_queue->pdev_queue_lock); 342 error: 343 ser_exit(); 344 return status; 345 } 346 347 QDF_STATUS wlan_ser_remove_non_scan_cmd( 348 struct wlan_ser_pdev_obj *ser_pdev_obj, 349 struct wlan_serialization_command_list **pcmd_list, 350 struct wlan_serialization_command *cmd, 351 uint8_t is_active_cmd) 352 { 353 QDF_STATUS pdev_status, vdev_status; 354 QDF_STATUS status = QDF_STATUS_E_FAILURE; 355 uint32_t vdev_id; 356 bool blocking_cmd_removed = 0; 357 struct wlan_serialization_pdev_queue *pdev_queue; 358 359 ser_debug("remove non scan cmd: type[%d] id[%d] prio[%d] blocking[%d]", 360 cmd->cmd_type, 361 cmd->cmd_id, 362 cmd->is_high_priority, 363 cmd->is_blocking); 364 365 vdev_status = 366 wlan_serialization_remove_cmd_from_vdev_queue(ser_pdev_obj, 367 pcmd_list, 368 cmd, 369 is_active_cmd); 370 371 if (vdev_status != QDF_STATUS_SUCCESS) { 372 ser_err("Failed to remove cmd from vdev active/pending queue"); 373 goto error; 374 } 375 376 pdev_status = 377 wlan_serialization_remove_cmd_from_pdev_queue(ser_pdev_obj, 378 pcmd_list, 379 cmd, 380 is_active_cmd); 381 382 if (pdev_status != QDF_STATUS_SUCCESS) { 383 ser_err("Failed to remove cmd from pdev active/pending queue"); 384 goto error; 385 } 386 387 if (is_active_cmd) { 388 blocking_cmd_removed = (*pcmd_list)->cmd.is_blocking; 389 pdev_queue = wlan_serialization_get_pdev_queue_obj( 390 ser_pdev_obj, (*pcmd_list)->cmd.cmd_type); 391 392 if (blocking_cmd_removed) 393 pdev_queue->blocking_cmd_active = 0; 394 395 vdev_id = wlan_vdev_get_id(cmd->vdev); 396 pdev_queue->vdev_active_cmd_bitmap &= ~(1 << vdev_id); 397 } 398 399 status = QDF_STATUS_SUCCESS; 400 401 error: 402 return status; 403 } 404 405 enum wlan_serialization_cmd_status 406 wlan_ser_cancel_non_scan_cmd( 407 struct wlan_ser_pdev_obj *ser_pdev_obj, 408 struct wlan_objmgr_pdev *pdev, struct wlan_objmgr_vdev *vdev, 409 struct wlan_serialization_command *cmd, 410 enum wlan_serialization_cmd_type cmd_type, 411 uint8_t is_active_queue) 412 { 413 qdf_list_t *pdev_queue; 414 qdf_list_t *vdev_queue; 415 struct wlan_serialization_pdev_queue *pdev_q; 416 uint32_t qsize; 417 struct wlan_serialization_command_list *cmd_list = NULL; 418 struct wlan_serialization_command cmd_bkup; 419 qdf_list_node_t *nnode = NULL, *pnode = NULL; 420 enum wlan_serialization_cmd_status status = WLAN_SER_CMD_NOT_FOUND; 421 struct wlan_objmgr_psoc *psoc = NULL; 422 QDF_STATUS qdf_status; 423 QDF_STATUS pdev_status, vdev_status; 424 struct wlan_ser_vdev_obj *ser_vdev_obj; 425 426 ser_enter(); 427 428 pdev_q = wlan_serialization_get_pdev_queue_obj(ser_pdev_obj, cmd_type); 429 430 pdev_queue = wlan_serialization_get_list_from_pdev_queue( 431 ser_pdev_obj, cmd_type, is_active_queue); 432 433 if (pdev) 434 psoc = wlan_pdev_get_psoc(pdev); 435 else if (vdev) 436 psoc = wlan_vdev_get_psoc(vdev); 437 else if (cmd && cmd->vdev) 438 psoc = wlan_vdev_get_psoc(cmd->vdev); 439 else 440 ser_debug("Can't find psoc"); 441 442 wlan_serialization_acquire_lock(&pdev_q->pdev_queue_lock); 443 444 qsize = wlan_serialization_list_size(pdev_queue); 445 while (!wlan_serialization_list_empty(pdev_queue) && qsize--) { 446 if (wlan_serialization_get_cmd_from_queue(pdev_queue, &nnode) 447 != QDF_STATUS_SUCCESS) { 448 ser_err("can't read cmd from queue"); 449 status = WLAN_SER_CMD_NOT_FOUND; 450 break; 451 } 452 cmd_list = 453 qdf_container_of(nnode, 454 struct wlan_serialization_command_list, 455 pdev_node); 456 if (cmd && !wlan_serialization_match_cmd_id_type( 457 nnode, cmd, 458 WLAN_SER_PDEV_NODE)) { 459 pnode = nnode; 460 continue; 461 } 462 463 if (vdev && 464 !wlan_serialization_match_cmd_vdev(nnode, 465 vdev, 466 WLAN_SER_PDEV_NODE)) { 467 pnode = nnode; 468 continue; 469 } 470 471 if (pdev && 472 !wlan_serialization_match_cmd_pdev(nnode, 473 pdev, 474 WLAN_SER_PDEV_NODE)) { 475 pnode = nnode; 476 continue; 477 } 478 479 /* 480 * active queue can't be removed directly, requester needs to 481 * wait for active command response and send remove request for 482 * active command separately 483 */ 484 if (is_active_queue) { 485 if (!psoc || !cmd_list) { 486 ser_err("psoc:0x%pK, cmd_list:0x%pK", 487 psoc, cmd_list); 488 status = WLAN_SER_CMD_NOT_FOUND; 489 break; 490 } 491 492 /* Cancel request received for a cmd in active 493 * queue which has not been activated yet, we 494 * should assert here 495 */ 496 if (qdf_atomic_test_bit(CMD_MARKED_FOR_ACTIVATION, 497 &cmd_list->cmd_in_use)) { 498 wlan_serialization_release_lock( 499 &pdev_q->pdev_queue_lock); 500 status = WLAN_SER_CMD_MARKED_FOR_ACTIVATION; 501 goto error; 502 } 503 504 qdf_status = wlan_serialization_find_and_stop_timer( 505 psoc, &cmd_list->cmd); 506 if (QDF_STATUS_SUCCESS != qdf_status) { 507 ser_err("Can't fix timer for active cmd"); 508 status = WLAN_SER_CMD_NOT_FOUND; 509 break; 510 } 511 status = WLAN_SER_CMD_IN_ACTIVE_LIST; 512 } 513 514 qdf_mem_copy(&cmd_bkup, &cmd_list->cmd, 515 sizeof(struct wlan_serialization_command)); 516 517 pdev_status = 518 wlan_serialization_remove_node(pdev_queue, 519 &cmd_list->pdev_node); 520 521 ser_vdev_obj = wlan_serialization_get_vdev_obj( 522 cmd_list->cmd.vdev); 523 524 vdev_queue = wlan_serialization_get_list_from_vdev_queue( 525 ser_vdev_obj, cmd_type, is_active_queue); 526 527 vdev_status = 528 wlan_serialization_remove_node(vdev_queue, 529 &cmd_list->vdev_node); 530 531 if (pdev_status != QDF_STATUS_SUCCESS || 532 vdev_status != QDF_STATUS_SUCCESS) { 533 ser_err("can't remove cmd from pdev/vdev queue"); 534 status = WLAN_SER_CMD_NOT_FOUND; 535 break; 536 } 537 538 qdf_mem_zero(&cmd_list->cmd, 539 sizeof(struct wlan_serialization_command)); 540 541 qdf_status = wlan_serialization_insert_back( 542 &pdev_q->cmd_pool_list, 543 &cmd_list->pdev_node); 544 545 if (QDF_STATUS_SUCCESS != qdf_status) { 546 ser_err("can't remove cmd from queue"); 547 status = WLAN_SER_CMD_NOT_FOUND; 548 break; 549 } 550 nnode = pnode; 551 552 wlan_serialization_release_lock(&pdev_q->pdev_queue_lock); 553 /* 554 * call pending cmd's callback to notify that 555 * it is being removed 556 */ 557 if (cmd_bkup.cmd_cb) { 558 /* caller should now do necessary clean up */ 559 ser_debug("cmd cb: type[%d] id[%d]", 560 cmd_bkup.cmd_type, 561 cmd_bkup.cmd_id); 562 ser_debug("reason: WLAN_SER_CB_CANCEL_CMD"); 563 cmd_bkup.cmd_cb(&cmd_bkup, 564 WLAN_SER_CB_CANCEL_CMD); 565 /* caller should release the memory */ 566 ser_debug("reason: WLAN_SER_CB_RELEASE_MEM_CMD"); 567 cmd_bkup.cmd_cb(&cmd_bkup, 568 WLAN_SER_CB_RELEASE_MEM_CMD); 569 } 570 571 wlan_serialization_acquire_lock(&pdev_q->pdev_queue_lock); 572 573 if (is_active_queue) { 574 if (cmd_bkup.is_blocking) 575 pdev_q->blocking_cmd_active = 0; 576 pdev_q->vdev_active_cmd_bitmap &= 577 ~(1 << wlan_vdev_get_id(cmd_bkup.vdev)); 578 } else { 579 if (cmd_bkup.is_blocking) 580 pdev_q->blocking_cmd_waiting--; 581 582 status = WLAN_SER_CMD_IN_PENDING_LIST; 583 } 584 585 586 if (!vdev && !pdev) 587 break; 588 } 589 590 wlan_serialization_release_lock(&pdev_q->pdev_queue_lock); 591 592 error: 593 ser_exit(); 594 return status; 595 } 596