aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/net/wireless/iwlwifi
diff options
context:
space:
mode:
authorJohannes Berg <johannes.berg@intel.com>2012-10-19 08:24:43 -0400
committerJohannes Berg <johannes.berg@intel.com>2012-10-29 06:27:05 -0400
commitf4feb8ac6e666d2ca37cf722166bbfadf2c6adf8 (patch)
tree950d1e7ab7ef3098f41b095f4e59d65520c6aeea /drivers/net/wireless/iwlwifi
parent86052a77067df53ad48800e74984f84806baddbd (diff)
iwlwifi: support host command with copied data
In addition to the NOCOPY flag, add a DUP flag that tells the transport to kmemdup() the buffer and free it after the command completes. Currently this is only supported for a single buffer in a given command, but that could be extended if it should be needed. Reviewed-by: Emmanuel Grumbach <emmanuel.grumbach@intel.com> Signed-off-by: Johannes Berg <johannes.berg@intel.com>
Diffstat (limited to 'drivers/net/wireless/iwlwifi')
-rw-r--r--drivers/net/wireless/iwlwifi/iwl-trans.h6
-rw-r--r--drivers/net/wireless/iwlwifi/pcie/internal.h2
-rw-r--r--drivers/net/wireless/iwlwifi/pcie/rx.c3
-rw-r--r--drivers/net/wireless/iwlwifi/pcie/trans.c1
-rw-r--r--drivers/net/wireless/iwlwifi/pcie/tx.c57
5 files changed, 59 insertions, 10 deletions
diff --git a/drivers/net/wireless/iwlwifi/iwl-trans.h b/drivers/net/wireless/iwlwifi/iwl-trans.h
index f75ea6d73ffc..76c52378f8f7 100644
--- a/drivers/net/wireless/iwlwifi/iwl-trans.h
+++ b/drivers/net/wireless/iwlwifi/iwl-trans.h
@@ -221,14 +221,18 @@ struct iwl_device_cmd {
221/** 221/**
222 * struct iwl_hcmd_dataflag - flag for each one of the chunks of the command 222 * struct iwl_hcmd_dataflag - flag for each one of the chunks of the command
223 * 223 *
224 * IWL_HCMD_DFL_NOCOPY: By default, the command is copied to the host command's 224 * @IWL_HCMD_DFL_NOCOPY: By default, the command is copied to the host command's
225 * ring. The transport layer doesn't map the command's buffer to DMA, but 225 * ring. The transport layer doesn't map the command's buffer to DMA, but
226 * rather copies it to an previously allocated DMA buffer. This flag tells 226 * rather copies it to an previously allocated DMA buffer. This flag tells
227 * the transport layer not to copy the command, but to map the existing 227 * the transport layer not to copy the command, but to map the existing
228 * buffer. This can save memcpy and is worth with very big comamnds. 228 * buffer. This can save memcpy and is worth with very big comamnds.
229 * @IWL_HCMD_DFL_DUP: Only valid without NOCOPY, duplicate the memory for this
230 * chunk internally and free it again after the command completes. This
231 * can (currently) be used only once per command.
229 */ 232 */
230enum iwl_hcmd_dataflag { 233enum iwl_hcmd_dataflag {
231 IWL_HCMD_DFL_NOCOPY = BIT(0), 234 IWL_HCMD_DFL_NOCOPY = BIT(0),
235 IWL_HCMD_DFL_DUP = BIT(1),
232}; 236};
233 237
234/** 238/**
diff --git a/drivers/net/wireless/iwlwifi/pcie/internal.h b/drivers/net/wireless/iwlwifi/pcie/internal.h
index 6ce58f03bc53..ae0f87e0e585 100644
--- a/drivers/net/wireless/iwlwifi/pcie/internal.h
+++ b/drivers/net/wireless/iwlwifi/pcie/internal.h
@@ -186,6 +186,8 @@ struct iwl_pcie_tx_queue_entry {
186 struct iwl_device_cmd *cmd; 186 struct iwl_device_cmd *cmd;
187 struct iwl_device_cmd *copy_cmd; 187 struct iwl_device_cmd *copy_cmd;
188 struct sk_buff *skb; 188 struct sk_buff *skb;
189 /* buffer to free after command completes */
190 const void *free_buf;
189 struct iwl_cmd_meta meta; 191 struct iwl_cmd_meta meta;
190}; 192};
191 193
diff --git a/drivers/net/wireless/iwlwifi/pcie/rx.c b/drivers/net/wireless/iwlwifi/pcie/rx.c
index 137af4c46a6c..3f03f6e322c3 100644
--- a/drivers/net/wireless/iwlwifi/pcie/rx.c
+++ b/drivers/net/wireless/iwlwifi/pcie/rx.c
@@ -452,6 +452,9 @@ static void iwl_rx_handle_rxbuf(struct iwl_trans *trans,
452 /* The original command isn't needed any more */ 452 /* The original command isn't needed any more */
453 kfree(txq->entries[cmd_index].copy_cmd); 453 kfree(txq->entries[cmd_index].copy_cmd);
454 txq->entries[cmd_index].copy_cmd = NULL; 454 txq->entries[cmd_index].copy_cmd = NULL;
455 /* nor is the duplicated part of the command */
456 kfree(txq->entries[cmd_index].free_buf);
457 txq->entries[cmd_index].free_buf = NULL;
455 } 458 }
456 459
457 /* 460 /*
diff --git a/drivers/net/wireless/iwlwifi/pcie/trans.c b/drivers/net/wireless/iwlwifi/pcie/trans.c
index a6a518116c7f..b8a155af12cc 100644
--- a/drivers/net/wireless/iwlwifi/pcie/trans.c
+++ b/drivers/net/wireless/iwlwifi/pcie/trans.c
@@ -496,6 +496,7 @@ static void iwl_tx_queue_free(struct iwl_trans *trans, int txq_id)
496 for (i = 0; i < txq->q.n_window; i++) { 496 for (i = 0; i < txq->q.n_window; i++) {
497 kfree(txq->entries[i].cmd); 497 kfree(txq->entries[i].cmd);
498 kfree(txq->entries[i].copy_cmd); 498 kfree(txq->entries[i].copy_cmd);
499 kfree(txq->entries[i].free_buf);
499 } 500 }
500 501
501 /* De-alloc circular buffer of TFDs */ 502 /* De-alloc circular buffer of TFDs */
diff --git a/drivers/net/wireless/iwlwifi/pcie/tx.c b/drivers/net/wireless/iwlwifi/pcie/tx.c
index 5db03145186c..9cb30ae5e9a1 100644
--- a/drivers/net/wireless/iwlwifi/pcie/tx.c
+++ b/drivers/net/wireless/iwlwifi/pcie/tx.c
@@ -517,8 +517,9 @@ static int iwl_enqueue_hcmd(struct iwl_trans *trans, struct iwl_host_cmd *cmd)
517 struct iwl_queue *q = &txq->q; 517 struct iwl_queue *q = &txq->q;
518 struct iwl_device_cmd *out_cmd; 518 struct iwl_device_cmd *out_cmd;
519 struct iwl_cmd_meta *out_meta; 519 struct iwl_cmd_meta *out_meta;
520 void *dup_buf = NULL;
520 dma_addr_t phys_addr; 521 dma_addr_t phys_addr;
521 u32 idx; 522 int idx;
522 u16 copy_size, cmd_size; 523 u16 copy_size, cmd_size;
523 bool had_nocopy = false; 524 bool had_nocopy = false;
524 int i; 525 int i;
@@ -535,10 +536,33 @@ static int iwl_enqueue_hcmd(struct iwl_trans *trans, struct iwl_host_cmd *cmd)
535 continue; 536 continue;
536 if (cmd->dataflags[i] & IWL_HCMD_DFL_NOCOPY) { 537 if (cmd->dataflags[i] & IWL_HCMD_DFL_NOCOPY) {
537 had_nocopy = true; 538 had_nocopy = true;
539 if (WARN_ON(cmd->dataflags[i] & IWL_HCMD_DFL_DUP)) {
540 idx = -EINVAL;
541 goto free_dup_buf;
542 }
543 } else if (cmd->dataflags[i] & IWL_HCMD_DFL_DUP) {
544 /*
545 * This is also a chunk that isn't copied
546 * to the static buffer so set had_nocopy.
547 */
548 had_nocopy = true;
549
550 /* only allowed once */
551 if (WARN_ON(dup_buf)) {
552 idx = -EINVAL;
553 goto free_dup_buf;
554 }
555
556 dup_buf = kmemdup(cmd->data[i], cmd->len[i],
557 GFP_ATOMIC);
558 if (!dup_buf)
559 return -ENOMEM;
538 } else { 560 } else {
539 /* NOCOPY must not be followed by normal! */ 561 /* NOCOPY must not be followed by normal! */
540 if (WARN_ON(had_nocopy)) 562 if (WARN_ON(had_nocopy)) {
541 return -EINVAL; 563 idx = -EINVAL;
564 goto free_dup_buf;
565 }
542 copy_size += cmd->len[i]; 566 copy_size += cmd->len[i];
543 } 567 }
544 cmd_size += cmd->len[i]; 568 cmd_size += cmd->len[i];
@@ -553,8 +577,10 @@ static int iwl_enqueue_hcmd(struct iwl_trans *trans, struct iwl_host_cmd *cmd)
553 if (WARN(copy_size > TFD_MAX_PAYLOAD_SIZE, 577 if (WARN(copy_size > TFD_MAX_PAYLOAD_SIZE,
554 "Command %s (%#x) is too large (%d bytes)\n", 578 "Command %s (%#x) is too large (%d bytes)\n",
555 trans_pcie_get_cmd_string(trans_pcie, cmd->id), 579 trans_pcie_get_cmd_string(trans_pcie, cmd->id),
556 cmd->id, copy_size)) 580 cmd->id, copy_size)) {
557 return -EINVAL; 581 idx = -EINVAL;
582 goto free_dup_buf;
583 }
558 584
559 spin_lock_bh(&txq->lock); 585 spin_lock_bh(&txq->lock);
560 586
@@ -563,7 +589,8 @@ static int iwl_enqueue_hcmd(struct iwl_trans *trans, struct iwl_host_cmd *cmd)
563 589
564 IWL_ERR(trans, "No space in command queue\n"); 590 IWL_ERR(trans, "No space in command queue\n");
565 iwl_op_mode_cmd_queue_full(trans->op_mode); 591 iwl_op_mode_cmd_queue_full(trans->op_mode);
566 return -ENOSPC; 592 idx = -ENOSPC;
593 goto free_dup_buf;
567 } 594 }
568 595
569 idx = get_cmd_index(q, q->write_ptr); 596 idx = get_cmd_index(q, q->write_ptr);
@@ -587,7 +614,8 @@ static int iwl_enqueue_hcmd(struct iwl_trans *trans, struct iwl_host_cmd *cmd)
587 for (i = 0; i < IWL_MAX_CMD_TFDS; i++) { 614 for (i = 0; i < IWL_MAX_CMD_TFDS; i++) {
588 if (!cmd->len[i]) 615 if (!cmd->len[i])
589 continue; 616 continue;
590 if (cmd->dataflags[i] & IWL_HCMD_DFL_NOCOPY) 617 if (cmd->dataflags[i] & (IWL_HCMD_DFL_NOCOPY |
618 IWL_HCMD_DFL_DUP))
591 break; 619 break;
592 memcpy((u8 *)out_cmd + cmd_pos, cmd->data[i], cmd->len[i]); 620 memcpy((u8 *)out_cmd + cmd_pos, cmd->data[i], cmd->len[i]);
593 cmd_pos += cmd->len[i]; 621 cmd_pos += cmd->len[i];
@@ -629,11 +657,16 @@ static int iwl_enqueue_hcmd(struct iwl_trans *trans, struct iwl_host_cmd *cmd)
629 iwlagn_txq_attach_buf_to_tfd(trans, txq, phys_addr, copy_size, 1); 657 iwlagn_txq_attach_buf_to_tfd(trans, txq, phys_addr, copy_size, 1);
630 658
631 for (i = 0; i < IWL_MAX_CMD_TFDS; i++) { 659 for (i = 0; i < IWL_MAX_CMD_TFDS; i++) {
660 const void *data = cmd->data[i];
661
632 if (!cmd->len[i]) 662 if (!cmd->len[i])
633 continue; 663 continue;
634 if (!(cmd->dataflags[i] & IWL_HCMD_DFL_NOCOPY)) 664 if (!(cmd->dataflags[i] & (IWL_HCMD_DFL_NOCOPY |
665 IWL_HCMD_DFL_DUP)))
635 continue; 666 continue;
636 phys_addr = dma_map_single(trans->dev, (void *)cmd->data[i], 667 if (cmd->dataflags[i] & IWL_HCMD_DFL_DUP)
668 data = dup_buf;
669 phys_addr = dma_map_single(trans->dev, (void *)data,
637 cmd->len[i], DMA_BIDIRECTIONAL); 670 cmd->len[i], DMA_BIDIRECTIONAL);
638 if (dma_mapping_error(trans->dev, phys_addr)) { 671 if (dma_mapping_error(trans->dev, phys_addr)) {
639 iwl_unmap_tfd(trans, out_meta, 672 iwl_unmap_tfd(trans, out_meta,
@@ -648,6 +681,9 @@ static int iwl_enqueue_hcmd(struct iwl_trans *trans, struct iwl_host_cmd *cmd)
648 } 681 }
649 682
650 out_meta->flags = cmd->flags; 683 out_meta->flags = cmd->flags;
684 if (WARN_ON_ONCE(txq->entries[idx].free_buf))
685 kfree(txq->entries[idx].free_buf);
686 txq->entries[idx].free_buf = dup_buf;
651 687
652 txq->need_update = 1; 688 txq->need_update = 1;
653 689
@@ -664,6 +700,9 @@ static int iwl_enqueue_hcmd(struct iwl_trans *trans, struct iwl_host_cmd *cmd)
664 700
665 out: 701 out:
666 spin_unlock_bh(&txq->lock); 702 spin_unlock_bh(&txq->lock);
703 free_dup_buf:
704 if (idx < 0)
705 kfree(dup_buf);
667 return idx; 706 return idx;
668} 707}
669 708