iwlwifi: avoid Tx queue memory allocation in interface down

We used to free all the Tx queues memory when interface is brought
down and reallocate them again in interface up. This requires
order-4 allocation for txq->cmd[]. In situations like s2ram, this
usually leads to allocation failure in the memory subsystem. The
patch fixed this problem by allocating the Tx queues memory only at
the first time. Later iwl_down/iwl_up only initialize but don't
free and reallocate them. The memory is freed at the device removal
time. BTW, we have already done this for the Rx queue.

This fixed bug https://bugzilla.kernel.org/show_bug.cgi?id=15551

Signed-off-by: Zhu Yi <yi.zhu@intel.com>
Acked-by: Wey-Yi Guy <wey-yi.w.guy@intel.com>
Signed-off-by: Reinette Chatre <reinette.chatre@intel.com>

authored by Zhu Yi and committed by Reinette Chatre de0f60ea 04f2dec1

+60 -15
+7 -4
drivers/net/wireless/iwlwifi/iwl-core.c
··· 307 307 308 308 spin_unlock_irqrestore(&priv->lock, flags); 309 309 310 - /* Allocate and init all Tx and Command queues */ 311 - ret = iwl_txq_ctx_reset(priv); 312 - if (ret) 313 - return ret; 310 + /* Allocate or reset and init all Tx and Command queues */ 311 + if (!priv->txq) { 312 + ret = iwl_txq_ctx_alloc(priv); 313 + if (ret) 314 + return ret; 315 + } else 316 + iwl_txq_ctx_reset(priv); 314 317 315 318 set_bit(STATUS_INIT, &priv->status); 316 319
+4 -1
drivers/net/wireless/iwlwifi/iwl-core.h
··· 442 442 /***************************************************** 443 443 * TX 444 444 ******************************************************/ 445 - int iwl_txq_ctx_reset(struct iwl_priv *priv); 445 + int iwl_txq_ctx_alloc(struct iwl_priv *priv); 446 + void iwl_txq_ctx_reset(struct iwl_priv *priv); 446 447 void iwl_hw_txq_free_tfd(struct iwl_priv *priv, struct iwl_tx_queue *txq); 447 448 int iwl_hw_txq_attach_buf_to_tfd(struct iwl_priv *priv, 448 449 struct iwl_tx_queue *txq, ··· 457 456 void iwl_txq_update_write_ptr(struct iwl_priv *priv, struct iwl_tx_queue *txq); 458 457 int iwl_tx_queue_init(struct iwl_priv *priv, struct iwl_tx_queue *txq, 459 458 int slots_num, u32 txq_id); 459 + void iwl_tx_queue_reset(struct iwl_priv *priv, struct iwl_tx_queue *txq, 460 + int slots_num, u32 txq_id); 460 461 void iwl_tx_queue_free(struct iwl_priv *priv, int txq_id); 461 462 int iwl_tx_agg_start(struct iwl_priv *priv, const u8 *ra, u16 tid, u16 *ssn); 462 463 int iwl_tx_agg_stop(struct iwl_priv *priv , const u8 *ra, u16 tid);
+49 -10
drivers/net/wireless/iwlwifi/iwl-tx.c
··· 433 433 } 434 434 EXPORT_SYMBOL(iwl_tx_queue_init); 435 435 436 + void iwl_tx_queue_reset(struct iwl_priv *priv, struct iwl_tx_queue *txq, 437 + int slots_num, u32 txq_id) 438 + { 439 + int actual_slots = slots_num; 440 + 441 + if (txq_id == IWL_CMD_QUEUE_NUM) 442 + actual_slots++; 443 + 444 + memset(txq->meta, 0, sizeof(struct iwl_cmd_meta) * actual_slots); 445 + 446 + txq->need_update = 0; 447 + 448 + /* Initialize queue's high/low-water marks, and head/tail indexes */ 449 + iwl_queue_init(priv, &txq->q, TFD_QUEUE_SIZE_MAX, slots_num, txq_id); 450 + 451 + /* Tell device where to find queue */ 452 + priv->cfg->ops->lib->txq_init(priv, txq); 453 + } 454 + EXPORT_SYMBOL(iwl_tx_queue_reset); 455 + 436 456 /** 437 457 * iwl_hw_txq_ctx_free - Free TXQ Context 438 458 * ··· 464 444 465 445 /* Tx queues */ 466 446 if (priv->txq) { 467 - for (txq_id = 0; txq_id < priv->hw_params.max_txq_num; 468 - txq_id++) 447 + for (txq_id = 0; txq_id < priv->hw_params.max_txq_num; txq_id++) 469 448 if (txq_id == IWL_CMD_QUEUE_NUM) 470 449 iwl_cmd_queue_free(priv); 471 450 else ··· 480 461 EXPORT_SYMBOL(iwl_hw_txq_ctx_free); 481 462 482 463 /** 483 - * iwl_txq_ctx_reset - Reset TX queue context 484 - * Destroys all DMA structures and initialize them again 464 + * iwl_txq_ctx_alloc - allocate TX queue context 465 + * Allocate all Tx DMA structures and initialize them 485 466 * 486 467 * @param priv 487 468 * @return error code 488 469 */ 489 - int iwl_txq_ctx_reset(struct iwl_priv *priv) 470 + int iwl_txq_ctx_alloc(struct iwl_priv *priv) 490 471 { 491 - int ret = 0; 472 + int ret; 492 473 int txq_id, slots_num; 493 474 unsigned long flags; 494 475 ··· 546 527 return ret; 547 528 } 548 529 530 + void iwl_txq_ctx_reset(struct iwl_priv *priv) 531 + { 532 + int txq_id, slots_num; 533 + unsigned long flags; 534 + 535 + spin_lock_irqsave(&priv->lock, flags); 536 + 537 + /* Turn off all Tx DMA fifos */ 538 + priv->cfg->ops->lib->txq_set_sched(priv, 0); 539 + 540 + /* Tell NIC where to find the "keep warm" buffer */ 541 + iwl_write_direct32(priv, FH_KW_MEM_ADDR_REG, priv->kw.dma >> 4); 542 + 543 + spin_unlock_irqrestore(&priv->lock, flags); 544 + 545 + /* Alloc and init all Tx queues, including the command queue (#4) */ 546 + for (txq_id = 0; txq_id < priv->hw_params.max_txq_num; txq_id++) { 547 + slots_num = txq_id == IWL_CMD_QUEUE_NUM ? 548 + TFD_CMD_SLOTS : TFD_TX_CMD_SLOTS; 549 + iwl_tx_queue_reset(priv, &priv->txq[txq_id], slots_num, txq_id); 550 + } 551 + } 552 + 549 553 /** 550 - * iwl_txq_ctx_stop - Stop all Tx DMA channels, free Tx queue memory 554 + * iwl_txq_ctx_stop - Stop all Tx DMA channels 551 555 */ 552 556 void iwl_txq_ctx_stop(struct iwl_priv *priv) 553 557 { ··· 590 548 1000); 591 549 } 592 550 spin_unlock_irqrestore(&priv->lock, flags); 593 - 594 - /* Deallocate memory for all Tx queues */ 595 - iwl_hw_txq_ctx_free(priv); 596 551 } 597 552 EXPORT_SYMBOL(iwl_txq_ctx_stop); 598 553