aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/net
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
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')
-rw-r--r--drivers/net/wireless/iwlwifi/iwl-agn-rx.c2
-rw-r--r--drivers/net/wireless/iwlwifi/iwl-trans-pcie-rx.c129
-rw-r--r--drivers/net/wireless/iwlwifi/iwl-trans-pcie.c1
-rw-r--r--drivers/net/wireless/iwlwifi/iwl-trans.h17
4 files changed, 85 insertions, 64 deletions
diff --git a/drivers/net/wireless/iwlwifi/iwl-agn-rx.c b/drivers/net/wireless/iwlwifi/iwl-agn-rx.c
index f4b84d1596e3..e4a86b31504a 100644
--- a/drivers/net/wireless/iwlwifi/iwl-agn-rx.c
+++ b/drivers/net/wireless/iwlwifi/iwl-agn-rx.c
@@ -794,7 +794,7 @@ static void iwlagn_pass_packet_to_mac80211(struct iwl_priv *priv,
794 return; 794 return;
795 } 795 }
796 796
797 offset = (void *)hdr - rxb_addr(rxb); 797 offset = (void *)hdr - rxb_addr(rxb) + rxb_offset(rxb);
798 p = rxb_steal_page(rxb); 798 p = rxb_steal_page(rxb);
799 skb_add_rx_frag(skb, 0, p, offset, len, len); 799 skb_add_rx_frag(skb, 0, p, offset, len, len);
800 800
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
diff --git a/drivers/net/wireless/iwlwifi/iwl-trans-pcie.c b/drivers/net/wireless/iwlwifi/iwl-trans-pcie.c
index b4f796c82e1e..98cd71fb385e 100644
--- a/drivers/net/wireless/iwlwifi/iwl-trans-pcie.c
+++ b/drivers/net/wireless/iwlwifi/iwl-trans-pcie.c
@@ -180,7 +180,6 @@ static void iwl_trans_rx_hw_init(struct iwl_trans *trans,
180 FH_RCSR_RX_CONFIG_CHNL_EN_ENABLE_VAL | 180 FH_RCSR_RX_CONFIG_CHNL_EN_ENABLE_VAL |
181 FH_RCSR_CHNL0_RX_IGNORE_RXF_EMPTY | 181 FH_RCSR_CHNL0_RX_IGNORE_RXF_EMPTY |
182 FH_RCSR_CHNL0_RX_CONFIG_IRQ_DEST_INT_HOST_VAL | 182 FH_RCSR_CHNL0_RX_CONFIG_IRQ_DEST_INT_HOST_VAL |
183 FH_RCSR_CHNL0_RX_CONFIG_SINGLE_FRAME_MSK |
184 rb_size| 183 rb_size|
185 (rb_timeout << FH_RCSR_RX_CONFIG_REG_IRQ_RBTH_POS)| 184 (rb_timeout << FH_RCSR_RX_CONFIG_REG_IRQ_RBTH_POS)|
186 (rfdnlog << FH_RCSR_RX_CONFIG_RBDCB_SIZE_POS)); 185 (rfdnlog << FH_RCSR_RX_CONFIG_RBDCB_SIZE_POS));
diff --git a/drivers/net/wireless/iwlwifi/iwl-trans.h b/drivers/net/wireless/iwlwifi/iwl-trans.h
index 0c81cbaa8088..57d8ae7b7ba9 100644
--- a/drivers/net/wireless/iwlwifi/iwl-trans.h
+++ b/drivers/net/wireless/iwlwifi/iwl-trans.h
@@ -162,6 +162,8 @@ struct iwl_cmd_header {
162 162
163 163
164#define FH_RSCSR_FRAME_SIZE_MSK 0x00003FFF /* bits 0-13 */ 164#define FH_RSCSR_FRAME_SIZE_MSK 0x00003FFF /* bits 0-13 */
165#define FH_RSCSR_FRAME_INVALID 0x55550000
166#define FH_RSCSR_FRAME_ALIGN 0x40
165 167
166struct iwl_rx_packet { 168struct iwl_rx_packet {
167 /* 169 /*
@@ -260,18 +262,25 @@ static inline void iwl_free_resp(struct iwl_host_cmd *cmd)
260 262
261struct iwl_rx_cmd_buffer { 263struct iwl_rx_cmd_buffer {
262 struct page *_page; 264 struct page *_page;
265 int _offset;
266 bool _page_stolen;
263}; 267};
264 268
265static inline void *rxb_addr(struct iwl_rx_cmd_buffer *r) 269static inline void *rxb_addr(struct iwl_rx_cmd_buffer *r)
266{ 270{
267 return page_address(r->_page); 271 return (void *)((unsigned long)page_address(r->_page) + r->_offset);
272}
273
274static inline int rxb_offset(struct iwl_rx_cmd_buffer *r)
275{
276 return r->_offset;
268} 277}
269 278
270static inline struct page *rxb_steal_page(struct iwl_rx_cmd_buffer *r) 279static inline struct page *rxb_steal_page(struct iwl_rx_cmd_buffer *r)
271{ 280{
272 struct page *p = r->_page; 281 r->_page_stolen = true;
273 r->_page = NULL; 282 get_page(r->_page);
274 return p; 283 return r->_page;
275} 284}
276 285
277#define MAX_NO_RECLAIM_CMDS 6 286#define MAX_NO_RECLAIM_CMDS 6