mirror of
https://github.com/torvalds/linux.git
synced 2024-12-27 05:11:48 +00:00
iwlwifi: pcie: avoid empty free RB queue
If all free RB queues are empty, the driver will never restock the free RB queue. That's because the restocking happens in the Rx flow, and if the free queue is empty there will be no Rx. Although there's a background worker (a.k.a. allocator) allocating memory for RBs so that the Rx handler can restock them, the worker may run only after the free queue has become empty (and then it is too late for restocking as explained above). There is a solution for that called 'emergency': If the number of used RB's reaches half the amount of all RB's, the Rx handler will not wait for the allocator but immediately allocate memory for the used RB's and restock the free queue. But, since the used RB's is per queue, it may happen that the used RB's are spread between the queues such that the emergency check will fail for each of the queues (and still run out of RBs, causing the above symptom). To fix it, move to emergency mode if the sum of *all* used RBs (for all Rx queues) reaches half the amount of all RB's Signed-off-by: Shaul Triebitz <shaul.triebitz@intel.com> Signed-off-by: Luca Coelho <luciano.coelho@intel.com>
This commit is contained in:
parent
1eda295f54
commit
868a1e863f
@ -1144,6 +1144,14 @@ void iwl_pcie_rx_free(struct iwl_trans *trans)
|
||||
kfree(trans_pcie->rxq);
|
||||
}
|
||||
|
||||
static void iwl_pcie_rx_move_to_allocator(struct iwl_rxq *rxq,
|
||||
struct iwl_rb_allocator *rba)
|
||||
{
|
||||
spin_lock(&rba->lock);
|
||||
list_splice_tail_init(&rxq->rx_used, &rba->rbd_empty);
|
||||
spin_unlock(&rba->lock);
|
||||
}
|
||||
|
||||
/*
|
||||
* iwl_pcie_rx_reuse_rbd - Recycle used RBDs
|
||||
*
|
||||
@ -1175,9 +1183,7 @@ static void iwl_pcie_rx_reuse_rbd(struct iwl_trans *trans,
|
||||
if ((rxq->used_count % RX_CLAIM_REQ_ALLOC) == RX_POST_REQ_ALLOC) {
|
||||
/* Move the 2 RBDs to the allocator ownership.
|
||||
Allocator has another 6 from pool for the request completion*/
|
||||
spin_lock(&rba->lock);
|
||||
list_splice_tail_init(&rxq->rx_used, &rba->rbd_empty);
|
||||
spin_unlock(&rba->lock);
|
||||
iwl_pcie_rx_move_to_allocator(rxq, rba);
|
||||
|
||||
atomic_inc(&rba->req_pending);
|
||||
queue_work(rba->alloc_wq, &rba->rx_alloc);
|
||||
@ -1400,10 +1406,18 @@ restart:
|
||||
IWL_DEBUG_RX(trans, "Q %d: HW = SW = %d\n", rxq->id, r);
|
||||
|
||||
while (i != r) {
|
||||
struct iwl_rb_allocator *rba = &trans_pcie->rba;
|
||||
struct iwl_rx_mem_buffer *rxb;
|
||||
/* number of RBDs still waiting for page allocation */
|
||||
u32 rb_pending_alloc =
|
||||
atomic_read(&trans_pcie->rba.req_pending) *
|
||||
RX_CLAIM_REQ_ALLOC;
|
||||
|
||||
if (unlikely(rxq->used_count == rxq->queue_size / 2))
|
||||
if (unlikely(rb_pending_alloc >= rxq->queue_size / 2 &&
|
||||
!emergency)) {
|
||||
iwl_pcie_rx_move_to_allocator(rxq, rba);
|
||||
emergency = true;
|
||||
}
|
||||
|
||||
rxb = iwl_pcie_get_rxb(trans, rxq, i);
|
||||
if (!rxb)
|
||||
@ -1425,17 +1439,13 @@ restart:
|
||||
iwl_pcie_rx_allocator_get(trans, rxq);
|
||||
|
||||
if (rxq->used_count % RX_CLAIM_REQ_ALLOC == 0 && !emergency) {
|
||||
struct iwl_rb_allocator *rba = &trans_pcie->rba;
|
||||
|
||||
/* Add the remaining empty RBDs for allocator use */
|
||||
spin_lock(&rba->lock);
|
||||
list_splice_tail_init(&rxq->rx_used, &rba->rbd_empty);
|
||||
spin_unlock(&rba->lock);
|
||||
iwl_pcie_rx_move_to_allocator(rxq, rba);
|
||||
} else if (emergency) {
|
||||
count++;
|
||||
if (count == 8) {
|
||||
count = 0;
|
||||
if (rxq->used_count < rxq->queue_size / 3)
|
||||
if (rb_pending_alloc < rxq->queue_size / 3)
|
||||
emergency = false;
|
||||
|
||||
rxq->read = i;
|
||||
|
Loading…
Reference in New Issue
Block a user