aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/net/wireless
diff options
context:
space:
mode:
authorEmmanuel Grumbach <emmanuel.grumbach@intel.com>2012-05-16 16:35:58 -0400
committerJohn W. Linville <linville@tuxdriver.com>2012-05-25 11:12:40 -0400
commitebed633c61c023e5d1aa4ed159cd67406e9e37c2 (patch)
treef9cf015573f8b75f93105323da4a69649bbc6a5c /drivers/net/wireless
parent882dde8eb0d49ce0f853f8f4084dde56a21fe55f (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.h2
-rw-r--r--drivers/net/wireless/iwlwifi/iwl-trans-pcie-tx.c22
-rw-r--r--drivers/net/wireless/iwlwifi/iwl-trans-pcie.c4
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,
347void iwl_trans_pcie_tx_agg_setup(struct iwl_trans *trans, int queue, int fifo, 347void 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);
349void iwlagn_txq_free_tfd(struct iwl_trans *trans, struct iwl_tx_queue *txq, 349void 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);
351int iwl_tx_queue_reclaim(struct iwl_trans *trans, int txq_id, int index, 351int 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);
353int iwl_queue_space(const struct iwl_queue *q); 353int 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 */
219void iwlagn_txq_free_tfd(struct iwl_trans *trans, struct iwl_tx_queue *txq, 220void 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);