1 /* 2 * Copyright (c) 2015-2021 The Linux Foundation. All rights reserved. 3 * Copyright (c) 2021-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 <cds_api.h> 21 22 /* OS abstraction libraries */ 23 #include <qdf_nbuf.h> /* qdf_nbuf_t, etc. */ 24 #include <qdf_atomic.h> /* qdf_atomic_read, etc. */ 25 #include <qdf_util.h> /* qdf_unlikely */ 26 #include "dp_types.h" 27 #include "dp_tx_desc.h" 28 29 #include <cdp_txrx_handle.h> 30 #include "dp_internal.h" 31 #define INVALID_FLOW_ID 0xFF 32 #define MAX_INVALID_BIN 3 33 #define GLOBAL_FLOW_POOL_STATS_LEN 25 34 #define FLOW_POOL_LOG_LEN 50 35 36 #ifdef QCA_AC_BASED_FLOW_CONTROL 37 /** 38 * dp_tx_initialize_threshold() - Threshold of flow Pool initialization 39 * @pool: flow_pool 40 * @stop_threshold: stop threshold of certian AC 41 * @start_threshold: start threshold of certian AC 42 * @flow_pool_size: flow pool size 43 * 44 * Return: none 45 */ 46 static inline void 47 dp_tx_initialize_threshold(struct dp_tx_desc_pool_s *pool, 48 uint32_t start_threshold, 49 uint32_t stop_threshold, 50 uint16_t flow_pool_size) 51 { 52 /* BE_BK threshold is same as previous threahold */ 53 pool->start_th[DP_TH_BE_BK] = (start_threshold 54 * flow_pool_size) / 100; 55 pool->stop_th[DP_TH_BE_BK] = (stop_threshold 56 * flow_pool_size) / 100; 57 58 /* Update VI threshold based on BE_BK threashold */ 59 pool->start_th[DP_TH_VI] = (pool->start_th[DP_TH_BE_BK] 60 * FL_TH_VI_PERCENTAGE) / 100; 61 pool->stop_th[DP_TH_VI] = (pool->stop_th[DP_TH_BE_BK] 62 * FL_TH_VI_PERCENTAGE) / 100; 63 64 /* Update VO threshold based on BE_BK threashold */ 65 pool->start_th[DP_TH_VO] = (pool->start_th[DP_TH_BE_BK] 66 * FL_TH_VO_PERCENTAGE) / 100; 67 pool->stop_th[DP_TH_VO] = (pool->stop_th[DP_TH_BE_BK] 68 * FL_TH_VO_PERCENTAGE) / 100; 69 70 /* Update High Priority threshold based on BE_BK threashold */ 71 pool->start_th[DP_TH_HI] = (pool->start_th[DP_TH_BE_BK] 72 * FL_TH_HI_PERCENTAGE) / 100; 73 pool->stop_th[DP_TH_HI] = (pool->stop_th[DP_TH_BE_BK] 74 * FL_TH_HI_PERCENTAGE) / 100; 75 76 dp_debug("tx flow control threshold is set, pool size is %d", 77 flow_pool_size); 78 } 79 80 /** 81 * dp_tx_flow_pool_reattach() - Reattach flow_pool 82 * @pool: flow_pool 83 * 84 * Return: none 85 */ 86 static inline void 87 dp_tx_flow_pool_reattach(struct dp_tx_desc_pool_s *pool) 88 { 89 QDF_TRACE(QDF_MODULE_ID_DP, QDF_TRACE_LEVEL_ERROR, 90 "%s: flow pool already allocated, attached %d times", 91 __func__, pool->pool_create_cnt); 92 93 pool->status = FLOW_POOL_ACTIVE_UNPAUSED_REATTACH; 94 pool->pool_create_cnt++; 95 } 96 97 /** 98 * dp_tx_flow_pool_dump_threshold() - Dump threshold of the flow_pool 99 * @pool: flow_pool 100 * 101 * Return: none 102 */ 103 static inline void 104 dp_tx_flow_pool_dump_threshold(struct dp_tx_desc_pool_s *pool) 105 { 106 int i; 107 108 for (i = 0; i < FL_TH_MAX; i++) { 109 QDF_TRACE(QDF_MODULE_ID_DP, QDF_TRACE_LEVEL_ERROR, 110 "Level %d :: Start threshold %d :: Stop threshold %d", 111 i, pool->start_th[i], pool->stop_th[i]); 112 QDF_TRACE(QDF_MODULE_ID_DP, QDF_TRACE_LEVEL_ERROR, 113 "Level %d :: Maximun pause time %lu ms", 114 i, pool->max_pause_time[i]); 115 QDF_TRACE(QDF_MODULE_ID_DP, QDF_TRACE_LEVEL_ERROR, 116 "Level %d :: Latest pause timestamp %lu", 117 i, pool->latest_pause_time[i]); 118 } 119 } 120 121 /** 122 * dp_tx_flow_ctrl_reset_subqueues() - Reset subqueues to orginal state 123 * @soc: dp soc 124 * @pool: flow pool 125 * @pool_status: flow pool status 126 * 127 * Return: none 128 */ 129 static inline void 130 dp_tx_flow_ctrl_reset_subqueues(struct dp_soc *soc, 131 struct dp_tx_desc_pool_s *pool, 132 enum flow_pool_status pool_status) 133 { 134 switch (pool_status) { 135 case FLOW_POOL_ACTIVE_PAUSED: 136 soc->pause_cb(pool->flow_pool_id, 137 WLAN_NETIF_PRIORITY_QUEUE_ON, 138 WLAN_DATA_FLOW_CTRL_PRI); 139 fallthrough; 140 141 case FLOW_POOL_VO_PAUSED: 142 soc->pause_cb(pool->flow_pool_id, 143 WLAN_NETIF_VO_QUEUE_ON, 144 WLAN_DATA_FLOW_CTRL_VO); 145 fallthrough; 146 147 case FLOW_POOL_VI_PAUSED: 148 soc->pause_cb(pool->flow_pool_id, 149 WLAN_NETIF_VI_QUEUE_ON, 150 WLAN_DATA_FLOW_CTRL_VI); 151 fallthrough; 152 153 case FLOW_POOL_BE_BK_PAUSED: 154 soc->pause_cb(pool->flow_pool_id, 155 WLAN_NETIF_BE_BK_QUEUE_ON, 156 WLAN_DATA_FLOW_CTRL_BE_BK); 157 fallthrough; 158 default: 159 break; 160 } 161 } 162 163 #else 164 static inline void 165 dp_tx_initialize_threshold(struct dp_tx_desc_pool_s *pool, 166 uint32_t start_threshold, 167 uint32_t stop_threshold, 168 uint16_t flow_pool_size) 169 170 { 171 /* INI is in percentage so divide by 100 */ 172 pool->start_th = (start_threshold * flow_pool_size) / 100; 173 pool->stop_th = (stop_threshold * flow_pool_size) / 100; 174 } 175 176 static inline void 177 dp_tx_flow_pool_reattach(struct dp_tx_desc_pool_s *pool) 178 { 179 QDF_TRACE(QDF_MODULE_ID_DP, QDF_TRACE_LEVEL_ERROR, 180 "%s: flow pool already allocated, attached %d times", 181 __func__, pool->pool_create_cnt); 182 if (pool->avail_desc > pool->start_th) 183 pool->status = FLOW_POOL_ACTIVE_UNPAUSED; 184 else 185 pool->status = FLOW_POOL_ACTIVE_PAUSED; 186 187 pool->pool_create_cnt++; 188 } 189 190 static inline void 191 dp_tx_flow_pool_dump_threshold(struct dp_tx_desc_pool_s *pool) 192 { 193 QDF_TRACE(QDF_MODULE_ID_DP, QDF_TRACE_LEVEL_ERROR, 194 "Start threshold %d :: Stop threshold %d", 195 pool->start_th, pool->stop_th); 196 } 197 198 static inline void 199 dp_tx_flow_ctrl_reset_subqueues(struct dp_soc *soc, 200 struct dp_tx_desc_pool_s *pool, 201 enum flow_pool_status pool_status) 202 { 203 } 204 205 #endif 206 207 /** 208 * dp_tx_dump_flow_pool_info() - dump global_pool and flow_pool info 209 * 210 * @ctx: Handle to struct dp_soc. 211 * 212 * Return: none 213 */ 214 void dp_tx_dump_flow_pool_info(struct cdp_soc_t *soc_hdl) 215 { 216 struct dp_soc *soc = cdp_soc_t_to_dp_soc(soc_hdl); 217 struct dp_txrx_pool_stats *pool_stats = &soc->pool_stats; 218 struct dp_tx_desc_pool_s *pool = NULL; 219 struct dp_tx_desc_pool_s tmp_pool; 220 int i; 221 222 QDF_TRACE(QDF_MODULE_ID_DP, QDF_TRACE_LEVEL_ERROR, 223 "No of pool map received %d", pool_stats->pool_map_count); 224 QDF_TRACE(QDF_MODULE_ID_DP, QDF_TRACE_LEVEL_ERROR, 225 "No of pool unmap received %d", pool_stats->pool_unmap_count); 226 QDF_TRACE(QDF_MODULE_ID_DP, QDF_TRACE_LEVEL_ERROR, 227 "Pkt dropped due to unavailablity of pool %d", 228 pool_stats->pkt_drop_no_pool); 229 230 /* 231 * Nested spin lock. 232 * Always take in below order. 233 * flow_pool_array_lock -> flow_pool_lock 234 */ 235 qdf_spin_lock_bh(&soc->flow_pool_array_lock); 236 for (i = 0; i < MAX_TXDESC_POOLS; i++) { 237 pool = &soc->tx_desc[i]; 238 if (pool->status > FLOW_POOL_INVALID) 239 continue; 240 qdf_spin_lock_bh(&pool->flow_pool_lock); 241 qdf_mem_copy(&tmp_pool, pool, sizeof(tmp_pool)); 242 qdf_spin_unlock_bh(&pool->flow_pool_lock); 243 qdf_spin_unlock_bh(&soc->flow_pool_array_lock); 244 QDF_TRACE(QDF_MODULE_ID_DP, QDF_TRACE_LEVEL_ERROR, "\n"); 245 QDF_TRACE(QDF_MODULE_ID_DP, QDF_TRACE_LEVEL_ERROR, 246 "Flow_pool_id %d :: status %d", 247 tmp_pool.flow_pool_id, tmp_pool.status); 248 QDF_TRACE(QDF_MODULE_ID_DP, QDF_TRACE_LEVEL_ERROR, 249 "Total %d :: Available %d", 250 tmp_pool.pool_size, tmp_pool.avail_desc); 251 dp_tx_flow_pool_dump_threshold(&tmp_pool); 252 QDF_TRACE(QDF_MODULE_ID_DP, QDF_TRACE_LEVEL_ERROR, 253 "Member flow_id %d :: flow_type %d", 254 tmp_pool.flow_pool_id, tmp_pool.flow_type); 255 QDF_TRACE(QDF_MODULE_ID_DP, QDF_TRACE_LEVEL_ERROR, 256 "Pkt dropped due to unavailablity of descriptors %d", 257 tmp_pool.pkt_drop_no_desc); 258 qdf_spin_lock_bh(&soc->flow_pool_array_lock); 259 } 260 qdf_spin_unlock_bh(&soc->flow_pool_array_lock); 261 } 262 263 void dp_tx_dump_flow_pool_info_compact(struct dp_soc *soc) 264 { 265 struct dp_txrx_pool_stats *pool_stats = &soc->pool_stats; 266 struct dp_tx_desc_pool_s *pool = NULL; 267 char *comb_log_str; 268 uint32_t comb_log_str_size; 269 int bytes_written = 0; 270 int i; 271 272 comb_log_str_size = GLOBAL_FLOW_POOL_STATS_LEN + 273 (FLOW_POOL_LOG_LEN * MAX_TXDESC_POOLS) + 1; 274 comb_log_str = qdf_mem_malloc(comb_log_str_size); 275 if (!comb_log_str) 276 return; 277 278 bytes_written = qdf_snprintf(&comb_log_str[bytes_written], 279 comb_log_str_size, "G:(%d,%d,%d) ", 280 pool_stats->pool_map_count, 281 pool_stats->pool_unmap_count, 282 pool_stats->pkt_drop_no_pool); 283 284 for (i = 0; i < MAX_TXDESC_POOLS; i++) { 285 pool = &soc->tx_desc[i]; 286 if (pool->status > FLOW_POOL_INVALID) 287 continue; 288 bytes_written += qdf_snprintf(&comb_log_str[bytes_written], 289 (bytes_written >= comb_log_str_size) ? 0 : 290 comb_log_str_size - bytes_written, 291 "| %d %d: (%d,%d,%d)", 292 pool->flow_pool_id, pool->status, 293 pool->pool_size, pool->avail_desc, 294 pool->pkt_drop_no_desc); 295 } 296 297 QDF_TRACE(QDF_MODULE_ID_DP, QDF_TRACE_LEVEL_INFO_HIGH, 298 "FLOW_POOL_STATS %s", comb_log_str); 299 300 qdf_mem_free(comb_log_str); 301 } 302 303 /** 304 * dp_tx_clear_flow_pool_stats() - clear flow pool statistics 305 * 306 * @soc: Handle to struct dp_soc. 307 * 308 * Return: None 309 */ 310 void dp_tx_clear_flow_pool_stats(struct dp_soc *soc) 311 { 312 313 if (!soc) { 314 QDF_TRACE(QDF_MODULE_ID_DP, QDF_TRACE_LEVEL_ERROR, 315 "%s: soc is null", __func__); 316 return; 317 } 318 qdf_mem_zero(&soc->pool_stats, sizeof(soc->pool_stats)); 319 } 320 321 /** 322 * dp_tx_create_flow_pool() - create flow pool 323 * @soc: Handle to struct dp_soc 324 * @flow_pool_id: flow pool id 325 * @flow_pool_size: flow pool size 326 * 327 * Return: flow_pool pointer / NULL for error 328 */ 329 struct dp_tx_desc_pool_s *dp_tx_create_flow_pool(struct dp_soc *soc, 330 uint8_t flow_pool_id, uint32_t flow_pool_size) 331 { 332 struct dp_tx_desc_pool_s *pool; 333 uint32_t stop_threshold; 334 uint32_t start_threshold; 335 336 if (flow_pool_id >= MAX_TXDESC_POOLS) { 337 dp_err("invalid flow_pool_id %d", flow_pool_id); 338 return NULL; 339 } 340 pool = &soc->tx_desc[flow_pool_id]; 341 qdf_spin_lock_bh(&pool->flow_pool_lock); 342 if ((pool->status != FLOW_POOL_INACTIVE) || pool->pool_create_cnt) { 343 dp_tx_flow_pool_reattach(pool); 344 qdf_spin_unlock_bh(&pool->flow_pool_lock); 345 dp_err("cannot alloc desc, status=%d, create_cnt=%d", 346 pool->status, pool->pool_create_cnt); 347 return pool; 348 } 349 350 if (dp_tx_desc_pool_alloc(soc, flow_pool_id, flow_pool_size)) { 351 qdf_spin_unlock_bh(&pool->flow_pool_lock); 352 return NULL; 353 } 354 355 if (dp_tx_desc_pool_init(soc, flow_pool_id, flow_pool_size)) { 356 dp_tx_desc_pool_free(soc, flow_pool_id); 357 qdf_spin_unlock_bh(&pool->flow_pool_lock); 358 return NULL; 359 } 360 361 stop_threshold = wlan_cfg_get_tx_flow_stop_queue_th(soc->wlan_cfg_ctx); 362 start_threshold = stop_threshold + 363 wlan_cfg_get_tx_flow_start_queue_offset(soc->wlan_cfg_ctx); 364 365 pool->flow_pool_id = flow_pool_id; 366 pool->pool_size = flow_pool_size; 367 pool->avail_desc = flow_pool_size; 368 pool->status = FLOW_POOL_ACTIVE_UNPAUSED; 369 dp_tx_initialize_threshold(pool, start_threshold, stop_threshold, 370 flow_pool_size); 371 pool->pool_create_cnt++; 372 373 qdf_spin_unlock_bh(&pool->flow_pool_lock); 374 375 return pool; 376 } 377 378 /** 379 * dp_tx_delete_flow_pool() - delete flow pool 380 * @soc: Handle to struct dp_soc 381 * @pool: flow pool pointer 382 * @force: free pool forcefully 383 * 384 * Delete flow_pool if all tx descriptors are available. 385 * Otherwise put it in FLOW_POOL_INVALID state. 386 * If force is set then pull all available descriptors to 387 * global pool. 388 * 389 * Return: 0 for success or error 390 */ 391 int dp_tx_delete_flow_pool(struct dp_soc *soc, struct dp_tx_desc_pool_s *pool, 392 bool force) 393 { 394 struct dp_vdev *vdev; 395 enum flow_pool_status pool_status; 396 397 if (!soc || !pool) { 398 dp_err("pool or soc is NULL"); 399 QDF_ASSERT(0); 400 return ENOMEM; 401 } 402 403 dp_info("pool create_cnt=%d, avail_desc=%d, size=%d, status=%d", 404 pool->pool_create_cnt, pool->avail_desc, 405 pool->pool_size, pool->status); 406 qdf_spin_lock_bh(&pool->flow_pool_lock); 407 if (!pool->pool_create_cnt) { 408 qdf_spin_unlock_bh(&pool->flow_pool_lock); 409 dp_err("flow pool either not created or alread deleted"); 410 return -ENOENT; 411 } 412 pool->pool_create_cnt--; 413 if (pool->pool_create_cnt) { 414 qdf_spin_unlock_bh(&pool->flow_pool_lock); 415 dp_err("pool is still attached, pending detach %d", 416 pool->pool_create_cnt); 417 return -EAGAIN; 418 } 419 420 if (pool->avail_desc < pool->pool_size) { 421 pool_status = pool->status; 422 pool->status = FLOW_POOL_INVALID; 423 dp_tx_flow_ctrl_reset_subqueues(soc, pool, pool_status); 424 425 qdf_spin_unlock_bh(&pool->flow_pool_lock); 426 /* Reset TX desc associated to this Vdev as NULL */ 427 vdev = dp_vdev_get_ref_by_id(soc, pool->flow_pool_id, 428 DP_MOD_ID_MISC); 429 if (vdev) { 430 dp_tx_desc_flush(vdev->pdev, vdev, false); 431 dp_vdev_unref_delete(soc, vdev, 432 DP_MOD_ID_MISC); 433 } 434 dp_err("avail desc less than pool size"); 435 return -EAGAIN; 436 } 437 438 /* We have all the descriptors for the pool, we can delete the pool */ 439 dp_tx_desc_pool_deinit(soc, pool->flow_pool_id); 440 dp_tx_desc_pool_free(soc, pool->flow_pool_id); 441 qdf_spin_unlock_bh(&pool->flow_pool_lock); 442 return 0; 443 } 444 445 /** 446 * dp_tx_flow_pool_vdev_map() - Map flow_pool with vdev 447 * @pdev: Handle to struct dp_pdev 448 * @pool: flow_pool 449 * @vdev_id: flow_id /vdev_id 450 * 451 * Return: none 452 */ 453 static void dp_tx_flow_pool_vdev_map(struct dp_pdev *pdev, 454 struct dp_tx_desc_pool_s *pool, uint8_t vdev_id) 455 { 456 struct dp_vdev *vdev; 457 struct dp_soc *soc = pdev->soc; 458 459 vdev = dp_vdev_get_ref_by_id(soc, vdev_id, DP_MOD_ID_CDP); 460 if (!vdev) { 461 QDF_TRACE(QDF_MODULE_ID_DP, QDF_TRACE_LEVEL_ERROR, 462 "%s: invalid vdev_id %d", 463 __func__, vdev_id); 464 return; 465 } 466 467 vdev->pool = pool; 468 qdf_spin_lock_bh(&pool->flow_pool_lock); 469 pool->pool_owner_ctx = soc; 470 pool->flow_pool_id = vdev_id; 471 qdf_spin_unlock_bh(&pool->flow_pool_lock); 472 dp_vdev_unref_delete(soc, vdev, DP_MOD_ID_CDP); 473 } 474 475 /** 476 * dp_tx_flow_pool_vdev_unmap() - Unmap flow_pool from vdev 477 * @pdev: Handle to struct dp_pdev 478 * @pool: flow_pool 479 * @vdev_id: flow_id /vdev_id 480 * 481 * Return: none 482 */ 483 static void dp_tx_flow_pool_vdev_unmap(struct dp_pdev *pdev, 484 struct dp_tx_desc_pool_s *pool, uint8_t vdev_id) 485 { 486 struct dp_vdev *vdev; 487 struct dp_soc *soc = pdev->soc; 488 489 vdev = dp_vdev_get_ref_by_id(soc, vdev_id, DP_MOD_ID_CDP); 490 if (!vdev) { 491 QDF_TRACE(QDF_MODULE_ID_DP, QDF_TRACE_LEVEL_ERROR, 492 "%s: invalid vdev_id %d", 493 __func__, vdev_id); 494 return; 495 } 496 497 vdev->pool = NULL; 498 dp_vdev_unref_delete(soc, vdev, DP_MOD_ID_CDP); 499 } 500 501 /** 502 * dp_tx_flow_pool_map_handler() - Map flow_id with pool of descriptors 503 * @pdev: Handle to struct dp_pdev 504 * @flow_id: flow id 505 * @flow_type: flow type 506 * @flow_pool_id: pool id 507 * @flow_pool_size: pool size 508 * 509 * Process below target to host message 510 * HTT_T2H_MSG_TYPE_FLOW_POOL_MAP 511 * 512 * Return: none 513 */ 514 QDF_STATUS dp_tx_flow_pool_map_handler(struct dp_pdev *pdev, uint8_t flow_id, 515 uint8_t flow_type, uint8_t flow_pool_id, uint32_t flow_pool_size) 516 { 517 struct dp_soc *soc = pdev->soc; 518 struct dp_tx_desc_pool_s *pool; 519 enum htt_flow_type type = flow_type; 520 521 522 dp_info("flow_id %d flow_type %d flow_pool_id %d flow_pool_size %d", 523 flow_id, flow_type, flow_pool_id, flow_pool_size); 524 525 if (qdf_unlikely(!soc)) { 526 dp_err("soc is NULL"); 527 return QDF_STATUS_E_FAULT; 528 } 529 soc->pool_stats.pool_map_count++; 530 531 pool = dp_tx_create_flow_pool(soc, flow_pool_id, 532 flow_pool_size); 533 if (!pool) { 534 dp_err("creation of flow_pool %d size %d failed", 535 flow_pool_id, flow_pool_size); 536 return QDF_STATUS_E_RESOURCES; 537 } 538 539 switch (type) { 540 541 case FLOW_TYPE_VDEV: 542 dp_tx_flow_pool_vdev_map(pdev, pool, flow_id); 543 break; 544 default: 545 dp_err("flow type %d not supported", type); 546 break; 547 } 548 549 return QDF_STATUS_SUCCESS; 550 } 551 552 /** 553 * dp_tx_flow_pool_unmap_handler() - Unmap flow_id from pool of descriptors 554 * @pdev: Handle to struct dp_pdev 555 * @flow_id: flow id 556 * @flow_type: flow type 557 * @flow_pool_id: pool id 558 * 559 * Process below target to host message 560 * HTT_T2H_MSG_TYPE_FLOW_POOL_UNMAP 561 * 562 * Return: none 563 */ 564 void dp_tx_flow_pool_unmap_handler(struct dp_pdev *pdev, uint8_t flow_id, 565 uint8_t flow_type, uint8_t flow_pool_id) 566 { 567 struct dp_soc *soc = pdev->soc; 568 struct dp_tx_desc_pool_s *pool; 569 enum htt_flow_type type = flow_type; 570 571 QDF_TRACE(QDF_MODULE_ID_DP, QDF_TRACE_LEVEL_DEBUG, 572 "%s: flow_id %d flow_type %d flow_pool_id %d", 573 __func__, flow_id, flow_type, flow_pool_id); 574 575 if (qdf_unlikely(!pdev)) { 576 QDF_TRACE(QDF_MODULE_ID_DP, QDF_TRACE_LEVEL_ERROR, 577 "%s: pdev is NULL", __func__); 578 return; 579 } 580 soc->pool_stats.pool_unmap_count++; 581 582 pool = &soc->tx_desc[flow_pool_id]; 583 if (!pool) { 584 QDF_TRACE(QDF_MODULE_ID_DP, QDF_TRACE_LEVEL_ERROR, 585 "%s: flow_pool not available flow_pool_id %d", 586 __func__, type); 587 return; 588 } 589 590 switch (type) { 591 592 case FLOW_TYPE_VDEV: 593 dp_tx_flow_pool_vdev_unmap(pdev, pool, flow_id); 594 break; 595 default: 596 QDF_TRACE(QDF_MODULE_ID_DP, QDF_TRACE_LEVEL_ERROR, 597 "%s: flow type %d not supported !!!", 598 __func__, type); 599 return; 600 } 601 602 /* only delete if all descriptors are available */ 603 dp_tx_delete_flow_pool(soc, pool, false); 604 } 605 606 /** 607 * dp_tx_flow_control_init() - Initialize tx flow control 608 * @tx_desc_pool: Handle to flow_pool 609 * 610 * Return: none 611 */ 612 void dp_tx_flow_control_init(struct dp_soc *soc) 613 { 614 qdf_spinlock_create(&soc->flow_pool_array_lock); 615 } 616 617 /** 618 * dp_tx_desc_pool_dealloc() - De-allocate tx desc pool 619 * @tx_desc_pool: Handle to flow_pool 620 * 621 * Return: none 622 */ 623 static inline void dp_tx_desc_pool_dealloc(struct dp_soc *soc) 624 { 625 struct dp_tx_desc_pool_s *tx_desc_pool; 626 int i; 627 628 for (i = 0; i < MAX_TXDESC_POOLS; i++) { 629 tx_desc_pool = &((soc)->tx_desc[i]); 630 if (!tx_desc_pool->desc_pages.num_pages) 631 continue; 632 633 dp_tx_desc_pool_deinit(soc, i); 634 dp_tx_desc_pool_free(soc, i); 635 } 636 } 637 638 /** 639 * dp_tx_flow_control_deinit() - Deregister fw based tx flow control 640 * @tx_desc_pool: Handle to flow_pool 641 * 642 * Return: none 643 */ 644 void dp_tx_flow_control_deinit(struct dp_soc *soc) 645 { 646 dp_tx_desc_pool_dealloc(soc); 647 648 qdf_spinlock_destroy(&soc->flow_pool_array_lock); 649 } 650 651 /** 652 * dp_txrx_register_pause_cb() - Register pause callback 653 * @ctx: Handle to struct dp_soc 654 * @pause_cb: Tx pause_cb 655 * 656 * Return: none 657 */ 658 QDF_STATUS dp_txrx_register_pause_cb(struct cdp_soc_t *handle, 659 tx_pause_callback pause_cb) 660 { 661 struct dp_soc *soc = (struct dp_soc *)handle; 662 663 if (!soc || !pause_cb) { 664 QDF_TRACE(QDF_MODULE_ID_DP, QDF_TRACE_LEVEL_ERROR, 665 FL("soc or pause_cb is NULL")); 666 return QDF_STATUS_E_INVAL; 667 } 668 soc->pause_cb = pause_cb; 669 670 return QDF_STATUS_SUCCESS; 671 } 672 673 QDF_STATUS dp_tx_flow_pool_map(struct cdp_soc_t *handle, uint8_t pdev_id, 674 uint8_t vdev_id) 675 { 676 struct dp_soc *soc = cdp_soc_t_to_dp_soc(handle); 677 struct dp_pdev *pdev = 678 dp_get_pdev_from_soc_pdev_id_wifi3(soc, pdev_id); 679 int tx_ring_size = wlan_cfg_get_num_tx_desc(soc->wlan_cfg_ctx); 680 681 if (!pdev) { 682 dp_err("pdev is NULL"); 683 return QDF_STATUS_E_INVAL; 684 } 685 686 return dp_tx_flow_pool_map_handler(pdev, vdev_id, FLOW_TYPE_VDEV, 687 vdev_id, tx_ring_size); 688 } 689 690 void dp_tx_flow_pool_unmap(struct cdp_soc_t *handle, uint8_t pdev_id, 691 uint8_t vdev_id) 692 { 693 struct dp_soc *soc = cdp_soc_t_to_dp_soc(handle); 694 struct dp_pdev *pdev = 695 dp_get_pdev_from_soc_pdev_id_wifi3(soc, pdev_id); 696 697 if (!pdev) { 698 dp_err("pdev is NULL"); 699 return; 700 } 701 702 return dp_tx_flow_pool_unmap_handler(pdev, vdev_id, 703 FLOW_TYPE_VDEV, vdev_id); 704 } 705