diff options
author | Emmanuel Grumbach <emmanuel.grumbach@intel.com> | 2012-05-16 16:35:58 -0400 |
---|---|---|
committer | John W. Linville <linville@tuxdriver.com> | 2012-05-25 11:12:40 -0400 |
commit | ebed633c61c023e5d1aa4ed159cd67406e9e37c2 (patch) | |
tree | f9cf015573f8b75f93105323da4a69649bbc6a5c /drivers/net/wireless | |
parent | 882dde8eb0d49ce0f853f8f4084dde56a21fe55f (diff) |
iwlwifi: fix the Transmit Frame Descriptor rings
The logic that allows to have a short TFD queue was completely wrong.
We do maintain 256 Transmit Frame Descriptors, but they point to
recycled buffers. We used to attach and de-attach different TFDs for
the same buffer and it worked since they pointed to the same buffer.
Also zero the number of BDs after unmapping a TFD. This seems not
necessary since we don't reclaim the same TFD twice, but I like
housekeeping.
This patch solves this warning:
[ 6427.079855] WARNING: at lib/dma-debug.c:866 check_unmap+0x727/0x7a0()
[ 6427.079859] Hardware name: Latitude E6410
[ 6427.079865] iwlwifi 0000:02:00.0: DMA-API: device driver tries to free DMA memory it has not allocated [device address=0x00000000296d393c] [size=8 bytes]
[ 6427.079870] Modules linked in: ...
[ 6427.079950] Pid: 6613, comm: ifconfig Tainted: G O 3.3.3 #5
[ 6427.079954] Call Trace:
[ 6427.079963] [<c10337a2>] warn_slowpath_common+0x72/0xa0
[ 6427.079982] [<c1033873>] warn_slowpath_fmt+0x33/0x40
[ 6427.079988] [<c12dcb77>] check_unmap+0x727/0x7a0
[ 6427.079995] [<c12dcdaa>] debug_dma_unmap_page+0x5a/0x80
[ 6427.080024] [<fe2312ac>] iwlagn_unmap_tfd+0x12c/0x180 [iwlwifi]
[ 6427.080048] [<fe231349>] iwlagn_txq_free_tfd+0x49/0xb0 [iwlwifi]
[ 6427.080071] [<fe228e37>] iwl_tx_queue_unmap+0x67/0x90 [iwlwifi]
[ 6427.080095] [<fe22d221>] iwl_trans_pcie_stop_device+0x341/0x7b0 [iwlwifi]
[ 6427.080113] [<fe204b0e>] iwl_down+0x17e/0x260 [iwlwifi]
[ 6427.080132] [<fe20efec>] iwlagn_mac_stop+0x6c/0xf0 [iwlwifi]
[ 6427.080168] [<fd8480ce>] ieee80211_stop_device+0x5e/0x190 [mac80211]
[ 6427.080198] [<fd833208>] ieee80211_do_stop+0x288/0x620 [mac80211]
[ 6427.080243] [<fd8335b7>] ieee80211_stop+0x17/0x20 [mac80211]
[ 6427.080250] [<c148dac1>] __dev_close_many+0x81/0xd0
[ 6427.080270] [<c148db3d>] __dev_close+0x2d/0x50
[ 6427.080276] [<c148d152>] __dev_change_flags+0x82/0x150
[ 6427.080282] [<c148e3e3>] dev_change_flags+0x23/0x60
[ 6427.080289] [<c14f6320>] devinet_ioctl+0x6a0/0x770
[ 6427.080296] [<c14f8705>] inet_ioctl+0x95/0xb0
[ 6427.080304] [<c147a0f0>] sock_ioctl+0x70/0x270
Cc: stable@vger.kernel.org
Reported-by: Antonio Quartulli <ordex@autistici.org>
Tested-by: Antonio Quartulli <ordex@autistici.org>
Signed-off-by: Emmanuel Grumbach <emmanuel.grumbach@intel.com>
Reviewed-by: Wey-Yi W Guy <wey-yi.w.guy@intel.com>
Signed-off-by: Johannes Berg <johannes.berg@intel.com>
Signed-off-by: John W. Linville <linville@tuxdriver.com>
Diffstat (limited to 'drivers/net/wireless')
-rw-r--r-- | drivers/net/wireless/iwlwifi/iwl-trans-pcie-int.h | 2 | ||||
-rw-r--r-- | drivers/net/wireless/iwlwifi/iwl-trans-pcie-tx.c | 22 | ||||
-rw-r--r-- | drivers/net/wireless/iwlwifi/iwl-trans-pcie.c | 4 |
3 files changed, 16 insertions, 12 deletions
diff --git a/drivers/net/wireless/iwlwifi/iwl-trans-pcie-int.h b/drivers/net/wireless/iwlwifi/iwl-trans-pcie-int.h index 6213c05a4b52..e959207c630a 100644 --- a/drivers/net/wireless/iwlwifi/iwl-trans-pcie-int.h +++ b/drivers/net/wireless/iwlwifi/iwl-trans-pcie-int.h | |||
@@ -347,7 +347,7 @@ void iwl_trans_tx_queue_set_status(struct iwl_trans *trans, | |||
347 | void iwl_trans_pcie_tx_agg_setup(struct iwl_trans *trans, int queue, int fifo, | 347 | void iwl_trans_pcie_tx_agg_setup(struct iwl_trans *trans, int queue, int fifo, |
348 | int sta_id, int tid, int frame_limit, u16 ssn); | 348 | int sta_id, int tid, int frame_limit, u16 ssn); |
349 | void iwlagn_txq_free_tfd(struct iwl_trans *trans, struct iwl_tx_queue *txq, | 349 | void iwlagn_txq_free_tfd(struct iwl_trans *trans, struct iwl_tx_queue *txq, |
350 | int index, enum dma_data_direction dma_dir); | 350 | enum dma_data_direction dma_dir); |
351 | int iwl_tx_queue_reclaim(struct iwl_trans *trans, int txq_id, int index, | 351 | int iwl_tx_queue_reclaim(struct iwl_trans *trans, int txq_id, int index, |
352 | struct sk_buff_head *skbs); | 352 | struct sk_buff_head *skbs); |
353 | int iwl_queue_space(const struct iwl_queue *q); | 353 | int iwl_queue_space(const struct iwl_queue *q); |
diff --git a/drivers/net/wireless/iwlwifi/iwl-trans-pcie-tx.c b/drivers/net/wireless/iwlwifi/iwl-trans-pcie-tx.c index 21a8a672fbb2..a8750238ee09 100644 --- a/drivers/net/wireless/iwlwifi/iwl-trans-pcie-tx.c +++ b/drivers/net/wireless/iwlwifi/iwl-trans-pcie-tx.c | |||
@@ -204,33 +204,39 @@ static void iwlagn_unmap_tfd(struct iwl_trans *trans, struct iwl_cmd_meta *meta, | |||
204 | for (i = 1; i < num_tbs; i++) | 204 | for (i = 1; i < num_tbs; i++) |
205 | dma_unmap_single(trans->dev, iwl_tfd_tb_get_addr(tfd, i), | 205 | dma_unmap_single(trans->dev, iwl_tfd_tb_get_addr(tfd, i), |
206 | iwl_tfd_tb_get_len(tfd, i), dma_dir); | 206 | iwl_tfd_tb_get_len(tfd, i), dma_dir); |
207 | |||
208 | tfd->num_tbs = 0; | ||
207 | } | 209 | } |
208 | 210 | ||
209 | /** | 211 | /** |
210 | * iwlagn_txq_free_tfd - Free all chunks referenced by TFD [txq->q.read_ptr] | 212 | * iwlagn_txq_free_tfd - Free all chunks referenced by TFD [txq->q.read_ptr] |
211 | * @trans - transport private data | 213 | * @trans - transport private data |
212 | * @txq - tx queue | 214 | * @txq - tx queue |
213 | * @index - the index of the TFD to be freed | 215 | * @dma_dir - the direction of the DMA mapping |
214 | *@dma_dir - the direction of the DMA mapping | ||
215 | * | 216 | * |
216 | * Does NOT advance any TFD circular buffer read/write indexes | 217 | * Does NOT advance any TFD circular buffer read/write indexes |
217 | * Does NOT free the TFD itself (which is within circular buffer) | 218 | * Does NOT free the TFD itself (which is within circular buffer) |
218 | */ | 219 | */ |
219 | void iwlagn_txq_free_tfd(struct iwl_trans *trans, struct iwl_tx_queue *txq, | 220 | void iwlagn_txq_free_tfd(struct iwl_trans *trans, struct iwl_tx_queue *txq, |
220 | int index, enum dma_data_direction dma_dir) | 221 | enum dma_data_direction dma_dir) |
221 | { | 222 | { |
222 | struct iwl_tfd *tfd_tmp = txq->tfds; | 223 | struct iwl_tfd *tfd_tmp = txq->tfds; |
223 | 224 | ||
225 | /* rd_ptr is bounded by n_bd and idx is bounded by n_window */ | ||
226 | int rd_ptr = txq->q.read_ptr; | ||
227 | int idx = get_cmd_index(&txq->q, rd_ptr); | ||
228 | |||
224 | lockdep_assert_held(&txq->lock); | 229 | lockdep_assert_held(&txq->lock); |
225 | 230 | ||
226 | iwlagn_unmap_tfd(trans, &txq->entries[index].meta, | 231 | /* We have only q->n_window txq->entries, but we use q->n_bd tfds */ |
227 | &tfd_tmp[index], dma_dir); | 232 | iwlagn_unmap_tfd(trans, &txq->entries[idx].meta, |
233 | &tfd_tmp[rd_ptr], dma_dir); | ||
228 | 234 | ||
229 | /* free SKB */ | 235 | /* free SKB */ |
230 | if (txq->entries) { | 236 | if (txq->entries) { |
231 | struct sk_buff *skb; | 237 | struct sk_buff *skb; |
232 | 238 | ||
233 | skb = txq->entries[index].skb; | 239 | skb = txq->entries[idx].skb; |
234 | 240 | ||
235 | /* Can be called from irqs-disabled context | 241 | /* Can be called from irqs-disabled context |
236 | * If skb is not NULL, it means that the whole queue is being | 242 | * If skb is not NULL, it means that the whole queue is being |
@@ -238,7 +244,7 @@ void iwlagn_txq_free_tfd(struct iwl_trans *trans, struct iwl_tx_queue *txq, | |||
238 | */ | 244 | */ |
239 | if (skb) { | 245 | if (skb) { |
240 | iwl_op_mode_free_skb(trans->op_mode, skb); | 246 | iwl_op_mode_free_skb(trans->op_mode, skb); |
241 | txq->entries[index].skb = NULL; | 247 | txq->entries[idx].skb = NULL; |
242 | } | 248 | } |
243 | } | 249 | } |
244 | } | 250 | } |
@@ -973,7 +979,7 @@ int iwl_tx_queue_reclaim(struct iwl_trans *trans, int txq_id, int index, | |||
973 | 979 | ||
974 | iwlagn_txq_inval_byte_cnt_tbl(trans, txq); | 980 | iwlagn_txq_inval_byte_cnt_tbl(trans, txq); |
975 | 981 | ||
976 | iwlagn_txq_free_tfd(trans, txq, txq->q.read_ptr, DMA_TO_DEVICE); | 982 | iwlagn_txq_free_tfd(trans, txq, DMA_TO_DEVICE); |
977 | freed++; | 983 | freed++; |
978 | } | 984 | } |
979 | 985 | ||
diff --git a/drivers/net/wireless/iwlwifi/iwl-trans-pcie.c b/drivers/net/wireless/iwlwifi/iwl-trans-pcie.c index 2e57161854b9..ec6fb395b84d 100644 --- a/drivers/net/wireless/iwlwifi/iwl-trans-pcie.c +++ b/drivers/net/wireless/iwlwifi/iwl-trans-pcie.c | |||
@@ -435,9 +435,7 @@ static void iwl_tx_queue_unmap(struct iwl_trans *trans, int txq_id) | |||
435 | 435 | ||
436 | spin_lock_bh(&txq->lock); | 436 | spin_lock_bh(&txq->lock); |
437 | while (q->write_ptr != q->read_ptr) { | 437 | while (q->write_ptr != q->read_ptr) { |
438 | /* The read_ptr needs to bound by q->n_window */ | 438 | iwlagn_txq_free_tfd(trans, txq, dma_dir); |
439 | iwlagn_txq_free_tfd(trans, txq, get_cmd_index(q, q->read_ptr), | ||
440 | dma_dir); | ||
441 | q->read_ptr = iwl_queue_inc_wrap(q->read_ptr, q->n_bd); | 439 | q->read_ptr = iwl_queue_inc_wrap(q->read_ptr, q->n_bd); |
442 | } | 440 | } |
443 | spin_unlock_bh(&txq->lock); | 441 | spin_unlock_bh(&txq->lock); |