diff options
Diffstat (limited to 'drivers/net/wireless/iwlwifi/pcie/tx.c')
-rw-r--r-- | drivers/net/wireless/iwlwifi/pcie/tx.c | 60 |
1 files changed, 34 insertions, 26 deletions
diff --git a/drivers/net/wireless/iwlwifi/pcie/tx.c b/drivers/net/wireless/iwlwifi/pcie/tx.c index c47c92165aba..f45eb29c2ede 100644 --- a/drivers/net/wireless/iwlwifi/pcie/tx.c +++ b/drivers/net/wireless/iwlwifi/pcie/tx.c | |||
@@ -65,18 +65,30 @@ | |||
65 | ***************************************************/ | 65 | ***************************************************/ |
66 | static int iwl_queue_space(const struct iwl_queue *q) | 66 | static int iwl_queue_space(const struct iwl_queue *q) |
67 | { | 67 | { |
68 | int s = q->read_ptr - q->write_ptr; | 68 | unsigned int max; |
69 | 69 | unsigned int used; | |
70 | if (q->read_ptr > q->write_ptr) | 70 | |
71 | s -= q->n_bd; | 71 | /* |
72 | 72 | * To avoid ambiguity between empty and completely full queues, there | |
73 | if (s <= 0) | 73 | * should always be less than q->n_bd elements in the queue. |
74 | s += q->n_window; | 74 | * If q->n_window is smaller than q->n_bd, there is no need to reserve |
75 | /* keep some reserve to not confuse empty and full situations */ | 75 | * any queue entries for this purpose. |
76 | s -= 2; | 76 | */ |
77 | if (s < 0) | 77 | if (q->n_window < q->n_bd) |
78 | s = 0; | 78 | max = q->n_window; |
79 | return s; | 79 | else |
80 | max = q->n_bd - 1; | ||
81 | |||
82 | /* | ||
83 | * q->n_bd is a power of 2, so the following is equivalent to modulo by | ||
84 | * q->n_bd and is well defined for negative dividends. | ||
85 | */ | ||
86 | used = (q->write_ptr - q->read_ptr) & (q->n_bd - 1); | ||
87 | |||
88 | if (WARN_ON(used > max)) | ||
89 | return 0; | ||
90 | |||
91 | return max - used; | ||
80 | } | 92 | } |
81 | 93 | ||
82 | /* | 94 | /* |
@@ -451,13 +463,10 @@ static int iwl_pcie_txq_build_tfd(struct iwl_trans *trans, struct iwl_txq *txq, | |||
451 | return -EINVAL; | 463 | return -EINVAL; |
452 | } | 464 | } |
453 | 465 | ||
454 | if (WARN_ON(addr & ~DMA_BIT_MASK(36))) | 466 | if (WARN(addr & ~IWL_TX_DMA_MASK, |
467 | "Unaligned address = %llx\n", (unsigned long long)addr)) | ||
455 | return -EINVAL; | 468 | return -EINVAL; |
456 | 469 | ||
457 | if (unlikely(addr & ~IWL_TX_DMA_MASK)) | ||
458 | IWL_ERR(trans, "Unaligned address = %llx\n", | ||
459 | (unsigned long long)addr); | ||
460 | |||
461 | iwl_pcie_tfd_set_tb(tfd, num_tbs, addr, len); | 470 | iwl_pcie_tfd_set_tb(tfd, num_tbs, addr, len); |
462 | 471 | ||
463 | return 0; | 472 | return 0; |
@@ -829,7 +838,7 @@ static int iwl_pcie_tx_alloc(struct iwl_trans *trans) | |||
829 | sizeof(struct iwl_txq), GFP_KERNEL); | 838 | sizeof(struct iwl_txq), GFP_KERNEL); |
830 | if (!trans_pcie->txq) { | 839 | if (!trans_pcie->txq) { |
831 | IWL_ERR(trans, "Not enough memory for txq\n"); | 840 | IWL_ERR(trans, "Not enough memory for txq\n"); |
832 | ret = ENOMEM; | 841 | ret = -ENOMEM; |
833 | goto error; | 842 | goto error; |
834 | } | 843 | } |
835 | 844 | ||
@@ -1153,10 +1162,10 @@ void iwl_trans_pcie_txq_disable(struct iwl_trans *trans, int txq_id) | |||
1153 | /* | 1162 | /* |
1154 | * iwl_pcie_enqueue_hcmd - enqueue a uCode command | 1163 | * iwl_pcie_enqueue_hcmd - enqueue a uCode command |
1155 | * @priv: device private data point | 1164 | * @priv: device private data point |
1156 | * @cmd: a point to the ucode command structure | 1165 | * @cmd: a pointer to the ucode command structure |
1157 | * | 1166 | * |
1158 | * The function returns < 0 values to indicate the operation is | 1167 | * The function returns < 0 values to indicate the operation |
1159 | * failed. On success, it turns the index (> 0) of command in the | 1168 | * failed. On success, it returns the index (>= 0) of command in the |
1160 | * command queue. | 1169 | * command queue. |
1161 | */ | 1170 | */ |
1162 | static int iwl_pcie_enqueue_hcmd(struct iwl_trans *trans, | 1171 | static int iwl_pcie_enqueue_hcmd(struct iwl_trans *trans, |
@@ -1619,10 +1628,9 @@ int iwl_trans_pcie_tx(struct iwl_trans *trans, struct sk_buff *skb, | |||
1619 | txq = &trans_pcie->txq[txq_id]; | 1628 | txq = &trans_pcie->txq[txq_id]; |
1620 | q = &txq->q; | 1629 | q = &txq->q; |
1621 | 1630 | ||
1622 | if (unlikely(!test_bit(txq_id, trans_pcie->queue_used))) { | 1631 | if (WARN_ONCE(!test_bit(txq_id, trans_pcie->queue_used), |
1623 | WARN_ON_ONCE(1); | 1632 | "TX on unused queue %d\n", txq_id)) |
1624 | return -EINVAL; | 1633 | return -EINVAL; |
1625 | } | ||
1626 | 1634 | ||
1627 | spin_lock(&txq->lock); | 1635 | spin_lock(&txq->lock); |
1628 | 1636 | ||
@@ -1632,7 +1640,7 @@ int iwl_trans_pcie_tx(struct iwl_trans *trans, struct sk_buff *skb, | |||
1632 | * Check here that the packets are in the right place on the ring. | 1640 | * Check here that the packets are in the right place on the ring. |
1633 | */ | 1641 | */ |
1634 | wifi_seq = IEEE80211_SEQ_TO_SN(le16_to_cpu(hdr->seq_ctrl)); | 1642 | wifi_seq = IEEE80211_SEQ_TO_SN(le16_to_cpu(hdr->seq_ctrl)); |
1635 | WARN_ONCE(trans_pcie->txq[txq_id].ampdu && | 1643 | WARN_ONCE(txq->ampdu && |
1636 | (wifi_seq & 0xff) != q->write_ptr, | 1644 | (wifi_seq & 0xff) != q->write_ptr, |
1637 | "Q: %d WiFi Seq %d tfdNum %d", | 1645 | "Q: %d WiFi Seq %d tfdNum %d", |
1638 | txq_id, wifi_seq, q->write_ptr); | 1646 | txq_id, wifi_seq, q->write_ptr); |
@@ -1664,7 +1672,7 @@ int iwl_trans_pcie_tx(struct iwl_trans *trans, struct sk_buff *skb, | |||
1664 | */ | 1672 | */ |
1665 | len = sizeof(struct iwl_tx_cmd) + sizeof(struct iwl_cmd_header) + | 1673 | len = sizeof(struct iwl_tx_cmd) + sizeof(struct iwl_cmd_header) + |
1666 | hdr_len - IWL_HCMD_SCRATCHBUF_SIZE; | 1674 | hdr_len - IWL_HCMD_SCRATCHBUF_SIZE; |
1667 | tb1_len = (len + 3) & ~3; | 1675 | tb1_len = ALIGN(len, 4); |
1668 | 1676 | ||
1669 | /* Tell NIC about any 2-byte padding after MAC header */ | 1677 | /* Tell NIC about any 2-byte padding after MAC header */ |
1670 | if (tb1_len != len) | 1678 | if (tb1_len != len) |