aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/net/wireless/iwlwifi/iwl-trans-pcie-rx.c
diff options
context:
space:
mode:
authorJohannes Berg <johannes.berg@intel.com>2012-03-15 16:26:43 -0400
committerJohn W. Linville <linville@tuxdriver.com>2012-04-09 16:37:14 -0400
commit0c19744c344cf1bfda04f681ff4e1e46455577bd (patch)
treed6351ac483da68ca9bfe46df5642dccef313f17a /drivers/net/wireless/iwlwifi/iwl-trans-pcie-rx.c
parent88f10a176c7364a700c8638732e2b3110beaceb6 (diff)
iwlwifi: process multiple frames per RXB
The flow handler (hardware) can put multiple frames into a single RX buffer. To handle this, walk the RX buffer and check if there are multiple valid packets in it. To let the upper layer handle this correctly introduce rxb_offset() which is needed when we pass pages to mac80211 -- we need to know the offset into the page there. Also change the page handling scheme to use refcounting. Anyone who needs a page will "steal" it, which marks it as having been used & refcounts it. The RX handler then has to free its own reference and must not reuse the page. Finally, do not set the bit asking the FH to give us each packet in a single buffer. This really enables the feature. Signed-off-by: Johannes Berg <johannes.berg@intel.com> Signed-off-by: Wey-Yi Guy <wey-yi.w.guy@intel.com> Signed-off-by: John W. Linville <linville@tuxdriver.com>
Diffstat (limited to 'drivers/net/wireless/iwlwifi/iwl-trans-pcie-rx.c')
-rw-r--r--drivers/net/wireless/iwlwifi/iwl-trans-pcie-rx.c129
1 files changed, 71 insertions, 58 deletions
diff --git a/drivers/net/wireless/iwlwifi/iwl-trans-pcie-rx.c b/drivers/net/wireless/iwlwifi/iwl-trans-pcie-rx.c
index 8b1a7988e176..b28231196342 100644
--- a/drivers/net/wireless/iwlwifi/iwl-trans-pcie-rx.c
+++ b/drivers/net/wireless/iwlwifi/iwl-trans-pcie-rx.c
@@ -362,83 +362,96 @@ static void iwl_rx_handle_rxbuf(struct iwl_trans *trans,
362 struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); 362 struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans);
363 struct iwl_rx_queue *rxq = &trans_pcie->rxq; 363 struct iwl_rx_queue *rxq = &trans_pcie->rxq;
364 struct iwl_tx_queue *txq = &trans_pcie->txq[trans_pcie->cmd_queue]; 364 struct iwl_tx_queue *txq = &trans_pcie->txq[trans_pcie->cmd_queue];
365 struct iwl_device_cmd *cmd;
366 unsigned long flags; 365 unsigned long flags;
367 int len, err; 366 bool page_stolen = false;
368 u16 sequence; 367 int max_len = PAGE_SIZE << hw_params(trans).rx_page_order;
369 struct iwl_rx_cmd_buffer rxcb; 368 u32 offset = 0;
370 struct iwl_rx_packet *pkt;
371 bool reclaim;
372 int index, cmd_index;
373 369
374 if (WARN_ON(!rxb)) 370 if (WARN_ON(!rxb))
375 return; 371 return;
376 372
377 dma_unmap_page(trans->dev, rxb->page_dma, 373 dma_unmap_page(trans->dev, rxb->page_dma, max_len, DMA_FROM_DEVICE);
378 PAGE_SIZE << hw_params(trans).rx_page_order,
379 DMA_FROM_DEVICE);
380 374
381 rxcb._page = rxb->page; 375 while (offset + sizeof(u32) + sizeof(struct iwl_cmd_header) < max_len) {
382 pkt = rxb_addr(&rxcb); 376 struct iwl_rx_packet *pkt;
377 struct iwl_device_cmd *cmd;
378 u16 sequence;
379 bool reclaim;
380 int index, cmd_index, err, len;
381 struct iwl_rx_cmd_buffer rxcb = {
382 ._offset = offset,
383 ._page = rxb->page,
384 ._page_stolen = false,
385 };
383 386
384 IWL_DEBUG_RX(trans, "%s, 0x%02x\n", 387 pkt = rxb_addr(&rxcb);
385 get_cmd_string(pkt->hdr.cmd), pkt->hdr.cmd);
386 388
389 if (pkt->len_n_flags == cpu_to_le32(FH_RSCSR_FRAME_INVALID))
390 break;
387 391
388 len = le32_to_cpu(pkt->len_n_flags) & FH_RSCSR_FRAME_SIZE_MSK; 392 IWL_DEBUG_RX(trans, "cmd at offset %d: %s (0x%.2x)\n",
389 len += sizeof(u32); /* account for status word */ 393 rxcb._offset, get_cmd_string(pkt->hdr.cmd),
390 trace_iwlwifi_dev_rx(trans->dev, pkt, len); 394 pkt->hdr.cmd);
391 395
392 /* Reclaim a command buffer only if this packet is a response 396 len = le32_to_cpu(pkt->len_n_flags) & FH_RSCSR_FRAME_SIZE_MSK;
393 * to a (driver-originated) command. 397 len += sizeof(u32); /* account for status word */
394 * If the packet (e.g. Rx frame) originated from uCode, 398 trace_iwlwifi_dev_rx(trans->dev, pkt, len);
395 * there is no command buffer to reclaim. 399
396 * Ucode should set SEQ_RX_FRAME bit if ucode-originated, 400 /* Reclaim a command buffer only if this packet is a response
397 * but apparently a few don't get set; catch them here. */ 401 * to a (driver-originated) command.
398 reclaim = !(pkt->hdr.sequence & SEQ_RX_FRAME); 402 * If the packet (e.g. Rx frame) originated from uCode,
399 if (reclaim) { 403 * there is no command buffer to reclaim.
400 int i; 404 * Ucode should set SEQ_RX_FRAME bit if ucode-originated,
401 405 * but apparently a few don't get set; catch them here. */
402 for (i = 0; i < trans_pcie->n_no_reclaim_cmds; i++) { 406 reclaim = !(pkt->hdr.sequence & SEQ_RX_FRAME);
403 if (trans_pcie->no_reclaim_cmds[i] == pkt->hdr.cmd) { 407 if (reclaim) {
404 reclaim = false; 408 int i;
405 break; 409
410 for (i = 0; i < trans_pcie->n_no_reclaim_cmds; i++) {
411 if (trans_pcie->no_reclaim_cmds[i] ==
412 pkt->hdr.cmd) {
413 reclaim = false;
414 break;
415 }
406 } 416 }
407 } 417 }
408 }
409 418
410 sequence = le16_to_cpu(pkt->hdr.sequence); 419 sequence = le16_to_cpu(pkt->hdr.sequence);
411 index = SEQ_TO_INDEX(sequence); 420 index = SEQ_TO_INDEX(sequence);
412 cmd_index = get_cmd_index(&txq->q, index); 421 cmd_index = get_cmd_index(&txq->q, index);
413 422
414 if (reclaim) 423 if (reclaim)
415 cmd = txq->cmd[cmd_index]; 424 cmd = txq->cmd[cmd_index];
416 else 425 else
417 cmd = NULL; 426 cmd = NULL;
418 427
419 err = iwl_op_mode_rx(trans->op_mode, &rxcb, cmd); 428 err = iwl_op_mode_rx(trans->op_mode, &rxcb, cmd);
420 429
421 /* 430 /*
422 * XXX: After here, we should always check rxcb._page 431 * After here, we should always check rxcb._page_stolen,
423 * against NULL before touching it or its virtual 432 * if it is true then one of the handlers took the page.
424 * memory (pkt). Because some rx_handler might have 433 */
425 * already taken or freed the pages.
426 */
427 434
428 if (reclaim) { 435 if (reclaim) {
429 /* Invoke any callbacks, transfer the buffer to caller, 436 /* Invoke any callbacks, transfer the buffer to caller,
430 * and fire off the (possibly) blocking 437 * and fire off the (possibly) blocking
431 * iwl_trans_send_cmd() 438 * iwl_trans_send_cmd()
432 * as we reclaim the driver command queue */ 439 * as we reclaim the driver command queue */
433 if (rxcb._page) 440 if (!rxcb._page_stolen)
434 iwl_tx_cmd_complete(trans, &rxcb, err); 441 iwl_tx_cmd_complete(trans, &rxcb, err);
435 else 442 else
436 IWL_WARN(trans, "Claim null rxb?\n"); 443 IWL_WARN(trans, "Claim null rxb?\n");
444 }
445
446 page_stolen |= rxcb._page_stolen;
447 offset += ALIGN(len, FH_RSCSR_FRAME_ALIGN);
437 } 448 }
438 449
439 /* page was stolen from us */ 450 /* page was stolen from us -- free our reference */
440 if (rxcb._page == NULL) 451 if (page_stolen) {
452 __free_pages(rxb->page, hw_params(trans).rx_page_order);
441 rxb->page = NULL; 453 rxb->page = NULL;
454 }
442 455
443 /* Reuse the page if possible. For notification packets and 456 /* Reuse the page if possible. For notification packets and
444 * SKBs that fail to Rx correctly, add them back into the 457 * SKBs that fail to Rx correctly, add them back into the