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 certain AC 41 * @start_threshold: start threshold of certain 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 threshold */ 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 threshold */ 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 threshold */ 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 :: Maximum 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 original 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_is_tx_flow_pool_delete_allowed() - Can flow pool be deleted 380 * @soc: Handle to struct dp_soc 381 * @vdev_id: vdev_id corresponding to flow pool 382 * 383 * Check if it is OK to go ahead delete the flow pool. One of the case is 384 * MLO where it is not OK to delete the flow pool when link switch happens. 385 * 386 * Return: 0 for success or error 387 */ 388 static bool dp_is_tx_flow_pool_delete_allowed(struct dp_soc *soc, 389 uint8_t vdev_id) 390 { 391 struct dp_vdev *vdev = NULL; 392 bool is_allow = true; 393 394 vdev = dp_vdev_get_ref_by_id(soc, vdev_id, DP_MOD_ID_MISC); 395 396 /* only check for sta mode */ 397 if (!vdev || vdev->opmode != wlan_op_mode_sta) 398 goto comp_ret; 399 400 /* 401 * Only if current vdev is belong to MLO connection and connected, 402 * then it's not allowed to delete current pool, for legacy 403 * connection, allowed always. 404 */ 405 is_allow = policy_mgr_is_mlo_sta_disconnected( 406 (struct wlan_objmgr_psoc *)soc->ctrl_psoc, 407 vdev_id); 408 comp_ret: 409 if (vdev) 410 dp_vdev_unref_delete(soc, vdev, DP_MOD_ID_MISC); 411 412 return is_allow; 413 } 414 415 /** 416 * dp_tx_delete_flow_pool() - delete flow pool 417 * @soc: Handle to struct dp_soc 418 * @pool: flow pool pointer 419 * @force: free pool forcefully 420 * 421 * Delete flow_pool if all tx descriptors are available. 422 * Otherwise put it in FLOW_POOL_INVALID state. 423 * If force is set then pull all available descriptors to 424 * global pool. 425 * 426 * Return: 0 for success or error 427 */ 428 int dp_tx_delete_flow_pool(struct dp_soc *soc, struct dp_tx_desc_pool_s *pool, 429 bool force) 430 { 431 struct dp_vdev *vdev; 432 enum flow_pool_status pool_status; 433 434 if (!soc || !pool) { 435 dp_err("pool or soc is NULL"); 436 QDF_ASSERT(0); 437 return ENOMEM; 438 } 439 440 dp_info("pool_id %d create_cnt=%d, avail_desc=%d, size=%d, status=%d", 441 pool->flow_pool_id, pool->pool_create_cnt, pool->avail_desc, 442 pool->pool_size, pool->status); 443 444 if (!dp_is_tx_flow_pool_delete_allowed(soc, pool->flow_pool_id)) { 445 dp_info("skip pool id %d delete as it's not allowed", 446 pool->flow_pool_id); 447 return -EAGAIN; 448 } 449 450 qdf_spin_lock_bh(&pool->flow_pool_lock); 451 if (!pool->pool_create_cnt) { 452 qdf_spin_unlock_bh(&pool->flow_pool_lock); 453 dp_err("flow pool either not created or already deleted"); 454 return -ENOENT; 455 } 456 pool->pool_create_cnt--; 457 if (pool->pool_create_cnt) { 458 qdf_spin_unlock_bh(&pool->flow_pool_lock); 459 dp_err("pool is still attached, pending detach %d", 460 pool->pool_create_cnt); 461 return -EAGAIN; 462 } 463 464 if (pool->avail_desc < pool->pool_size) { 465 pool_status = pool->status; 466 pool->status = FLOW_POOL_INVALID; 467 dp_tx_flow_ctrl_reset_subqueues(soc, pool, pool_status); 468 469 qdf_spin_unlock_bh(&pool->flow_pool_lock); 470 /* Reset TX desc associated to this Vdev as NULL */ 471 vdev = dp_vdev_get_ref_by_id(soc, pool->flow_pool_id, 472 DP_MOD_ID_MISC); 473 if (vdev) { 474 dp_tx_desc_flush(vdev->pdev, vdev, false); 475 dp_vdev_unref_delete(soc, vdev, 476 DP_MOD_ID_MISC); 477 } 478 dp_err("avail desc less than pool size"); 479 return -EAGAIN; 480 } 481 482 /* We have all the descriptors for the pool, we can delete the pool */ 483 dp_tx_desc_pool_deinit(soc, pool->flow_pool_id); 484 dp_tx_desc_pool_free(soc, pool->flow_pool_id); 485 qdf_spin_unlock_bh(&pool->flow_pool_lock); 486 return 0; 487 } 488 489 /** 490 * dp_tx_flow_pool_vdev_map() - Map flow_pool with vdev 491 * @pdev: Handle to struct dp_pdev 492 * @pool: flow_pool 493 * @vdev_id: flow_id /vdev_id 494 * 495 * Return: none 496 */ 497 static void dp_tx_flow_pool_vdev_map(struct dp_pdev *pdev, 498 struct dp_tx_desc_pool_s *pool, uint8_t vdev_id) 499 { 500 struct dp_vdev *vdev; 501 struct dp_soc *soc = pdev->soc; 502 503 vdev = dp_vdev_get_ref_by_id(soc, vdev_id, DP_MOD_ID_CDP); 504 if (!vdev) { 505 QDF_TRACE(QDF_MODULE_ID_DP, QDF_TRACE_LEVEL_ERROR, 506 "%s: invalid vdev_id %d", 507 __func__, vdev_id); 508 return; 509 } 510 511 vdev->pool = pool; 512 qdf_spin_lock_bh(&pool->flow_pool_lock); 513 pool->pool_owner_ctx = soc; 514 pool->flow_pool_id = vdev_id; 515 qdf_spin_unlock_bh(&pool->flow_pool_lock); 516 dp_vdev_unref_delete(soc, vdev, DP_MOD_ID_CDP); 517 } 518 519 /** 520 * dp_tx_flow_pool_vdev_unmap() - Unmap flow_pool from vdev 521 * @pdev: Handle to struct dp_pdev 522 * @pool: flow_pool 523 * @vdev_id: flow_id /vdev_id 524 * 525 * Return: none 526 */ 527 static void dp_tx_flow_pool_vdev_unmap(struct dp_pdev *pdev, 528 struct dp_tx_desc_pool_s *pool, uint8_t vdev_id) 529 { 530 struct dp_vdev *vdev; 531 struct dp_soc *soc = pdev->soc; 532 533 vdev = dp_vdev_get_ref_by_id(soc, vdev_id, DP_MOD_ID_CDP); 534 if (!vdev) { 535 QDF_TRACE(QDF_MODULE_ID_DP, QDF_TRACE_LEVEL_ERROR, 536 "%s: invalid vdev_id %d", 537 __func__, vdev_id); 538 return; 539 } 540 541 vdev->pool = NULL; 542 dp_vdev_unref_delete(soc, vdev, DP_MOD_ID_CDP); 543 } 544 545 /** 546 * dp_tx_flow_pool_map_handler() - Map flow_id with pool of descriptors 547 * @pdev: Handle to struct dp_pdev 548 * @flow_id: flow id 549 * @flow_type: flow type 550 * @flow_pool_id: pool id 551 * @flow_pool_size: pool size 552 * 553 * Process below target to host message 554 * HTT_T2H_MSG_TYPE_FLOW_POOL_MAP 555 * 556 * Return: none 557 */ 558 QDF_STATUS dp_tx_flow_pool_map_handler(struct dp_pdev *pdev, uint8_t flow_id, 559 uint8_t flow_type, uint8_t flow_pool_id, uint32_t flow_pool_size) 560 { 561 struct dp_soc *soc = pdev->soc; 562 struct dp_tx_desc_pool_s *pool; 563 enum htt_flow_type type = flow_type; 564 565 566 dp_info("flow_id %d flow_type %d flow_pool_id %d flow_pool_size %d", 567 flow_id, flow_type, flow_pool_id, flow_pool_size); 568 569 if (qdf_unlikely(!soc)) { 570 dp_err("soc is NULL"); 571 return QDF_STATUS_E_FAULT; 572 } 573 soc->pool_stats.pool_map_count++; 574 575 pool = dp_tx_create_flow_pool(soc, flow_pool_id, 576 flow_pool_size); 577 if (!pool) { 578 dp_err("creation of flow_pool %d size %d failed", 579 flow_pool_id, flow_pool_size); 580 return QDF_STATUS_E_RESOURCES; 581 } 582 583 switch (type) { 584 585 case FLOW_TYPE_VDEV: 586 dp_tx_flow_pool_vdev_map(pdev, pool, flow_id); 587 break; 588 default: 589 dp_err("flow type %d not supported", type); 590 break; 591 } 592 593 return QDF_STATUS_SUCCESS; 594 } 595 596 /** 597 * dp_tx_flow_pool_unmap_handler() - Unmap flow_id from pool of descriptors 598 * @pdev: Handle to struct dp_pdev 599 * @flow_id: flow id 600 * @flow_type: flow type 601 * @flow_pool_id: pool id 602 * 603 * Process below target to host message 604 * HTT_T2H_MSG_TYPE_FLOW_POOL_UNMAP 605 * 606 * Return: none 607 */ 608 void dp_tx_flow_pool_unmap_handler(struct dp_pdev *pdev, uint8_t flow_id, 609 uint8_t flow_type, uint8_t flow_pool_id) 610 { 611 struct dp_soc *soc = pdev->soc; 612 struct dp_tx_desc_pool_s *pool; 613 enum htt_flow_type type = flow_type; 614 615 dp_info("flow_id %d flow_type %d flow_pool_id %d", flow_id, flow_type, 616 flow_pool_id); 617 618 if (qdf_unlikely(!pdev)) { 619 QDF_TRACE(QDF_MODULE_ID_DP, QDF_TRACE_LEVEL_ERROR, 620 "%s: pdev is NULL", __func__); 621 return; 622 } 623 soc->pool_stats.pool_unmap_count++; 624 625 pool = &soc->tx_desc[flow_pool_id]; 626 if (!pool) { 627 QDF_TRACE(QDF_MODULE_ID_DP, QDF_TRACE_LEVEL_ERROR, 628 "%s: flow_pool not available flow_pool_id %d", 629 __func__, type); 630 return; 631 } 632 633 switch (type) { 634 635 case FLOW_TYPE_VDEV: 636 dp_tx_flow_pool_vdev_unmap(pdev, pool, flow_id); 637 break; 638 default: 639 QDF_TRACE(QDF_MODULE_ID_DP, QDF_TRACE_LEVEL_ERROR, 640 "%s: flow type %d not supported !!!", 641 __func__, type); 642 return; 643 } 644 645 /* only delete if all descriptors are available */ 646 dp_tx_delete_flow_pool(soc, pool, false); 647 } 648 649 /** 650 * dp_tx_flow_control_init() - Initialize tx flow control 651 * @tx_desc_pool: Handle to flow_pool 652 * 653 * Return: none 654 */ 655 void dp_tx_flow_control_init(struct dp_soc *soc) 656 { 657 qdf_spinlock_create(&soc->flow_pool_array_lock); 658 } 659 660 /** 661 * dp_tx_desc_pool_dealloc() - De-allocate tx desc pool 662 * @tx_desc_pool: Handle to flow_pool 663 * 664 * Return: none 665 */ 666 static inline void dp_tx_desc_pool_dealloc(struct dp_soc *soc) 667 { 668 struct dp_tx_desc_pool_s *tx_desc_pool; 669 int i; 670 671 for (i = 0; i < MAX_TXDESC_POOLS; i++) { 672 tx_desc_pool = &((soc)->tx_desc[i]); 673 if (!tx_desc_pool->desc_pages.num_pages) 674 continue; 675 676 dp_tx_desc_pool_deinit(soc, i); 677 dp_tx_desc_pool_free(soc, i); 678 } 679 } 680 681 /** 682 * dp_tx_flow_control_deinit() - Deregister fw based tx flow control 683 * @tx_desc_pool: Handle to flow_pool 684 * 685 * Return: none 686 */ 687 void dp_tx_flow_control_deinit(struct dp_soc *soc) 688 { 689 dp_tx_desc_pool_dealloc(soc); 690 691 qdf_spinlock_destroy(&soc->flow_pool_array_lock); 692 } 693 694 /** 695 * dp_txrx_register_pause_cb() - Register pause callback 696 * @ctx: Handle to struct dp_soc 697 * @pause_cb: Tx pause_cb 698 * 699 * Return: none 700 */ 701 QDF_STATUS dp_txrx_register_pause_cb(struct cdp_soc_t *handle, 702 tx_pause_callback pause_cb) 703 { 704 struct dp_soc *soc = (struct dp_soc *)handle; 705 706 if (!soc || !pause_cb) { 707 QDF_TRACE(QDF_MODULE_ID_DP, QDF_TRACE_LEVEL_ERROR, 708 FL("soc or pause_cb is NULL")); 709 return QDF_STATUS_E_INVAL; 710 } 711 soc->pause_cb = pause_cb; 712 713 return QDF_STATUS_SUCCESS; 714 } 715 716 QDF_STATUS dp_tx_flow_pool_map(struct cdp_soc_t *handle, uint8_t pdev_id, 717 uint8_t vdev_id) 718 { 719 struct dp_soc *soc = cdp_soc_t_to_dp_soc(handle); 720 struct dp_pdev *pdev = 721 dp_get_pdev_from_soc_pdev_id_wifi3(soc, pdev_id); 722 int tx_ring_size = wlan_cfg_get_num_tx_desc(soc->wlan_cfg_ctx); 723 724 if (!pdev) { 725 dp_err("pdev is NULL"); 726 return QDF_STATUS_E_INVAL; 727 } 728 729 return dp_tx_flow_pool_map_handler(pdev, vdev_id, FLOW_TYPE_VDEV, 730 vdev_id, tx_ring_size); 731 } 732 733 void dp_tx_flow_pool_unmap(struct cdp_soc_t *handle, uint8_t pdev_id, 734 uint8_t vdev_id) 735 { 736 struct dp_soc *soc = cdp_soc_t_to_dp_soc(handle); 737 struct dp_pdev *pdev = 738 dp_get_pdev_from_soc_pdev_id_wifi3(soc, pdev_id); 739 740 if (!pdev) { 741 dp_err("pdev is NULL"); 742 return; 743 } 744 745 return dp_tx_flow_pool_unmap_handler(pdev, vdev_id, 746 FLOW_TYPE_VDEV, vdev_id); 747 } 748