aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/net
diff options
context:
space:
mode:
authorStanislaw Gruszka <sgruszka@redhat.com>2011-02-28 08:33:13 -0500
committerJohn W. Linville <linville@tuxdriver.com>2011-02-28 14:06:56 -0500
commit387f3381f732d8fa1b62213ae3276f2ae712dbe2 (patch)
treea736dbba4c9087f33e603befd7ac9d24c9e7e288 /drivers/net
parent15178535ad2f8fd8f7e803791f25395aa69f80ac (diff)
iwlwifi: fix dma mappings and skbs leak
Since commit commit 470058e0ad82fcfaaffd57307d8bf8c094e8e9d7 "iwlwifi: avoid Tx queue memory allocation in interface down" we do not unmap dma and free skbs when down device and there is pending transfer. What in consequence may cause that system hung (waiting for free skb's) when performing shutdown at iptables module unload. DMA leak manifest itself following warning: WARNING: at lib/dma-debug.c:689 dma_debug_device_change+0x15a/0x1b0() Hardware name: HP xw8600 Workstation pci 0000:80:00.0: DMA-API: device driver has pending DMA allocations while released from device [count=240] Modules linked in: iwlagn(-) aes_x86_64 aes_generic fuse cpufreq_ondemand acpi_cpufreq freq_table mperf xt_physdev ipt_REJECT nf_conntrack_ipv4 nf_defrag_ipv4 iptable_filter ip_tables ip6t_REJECT nf_conntrack_ipv6 nf_defrag_ipv6 xt_state nf_conntrack ip6table_filter ip6_tables ipv6 ext3 jbd dm_mirror dm_region_hash dm_log dm_mod uinput hp_wmi sparse_keymap sg wmi microcode serio_raw tg3 arc4 ecb shpchp mac80211 cfg80211 rfkill ext4 mbcache jbd2 sr_mod cdrom sd_mod crc_t10dif firewire_ohci firewire_core crc_itu_t mptsas mptscsih mptbase scsi_transport_sas pata_acpi ata_generic ata_piix ahci libahci floppy nouveau ttm drm_kms_helper drm i2c_algo_bit i2c_core video [last unloaded: iwlagn] Pid: 9131, comm: rmmod Tainted: G W 2.6.38-rc6-wl+ #33 Call Trace: [<ffffffff810649ef>] ? warn_slowpath_common+0x7f/0xc0 [<ffffffff81064ae6>] ? warn_slowpath_fmt+0x46/0x50 [<ffffffff812320ab>] ? dma_debug_device_change+0xdb/0x1b0 [<ffffffff8123212a>] ? dma_debug_device_change+0x15a/0x1b0 [<ffffffff8149dc18>] ? notifier_call_chain+0x58/0xb0 [<ffffffff8108e370>] ? __blocking_notifier_call_chain+0x60/0x90 [<ffffffff8108e3b6>] ? blocking_notifier_call_chain+0x16/0x20 [<ffffffff812f570c>] ? __device_release_driver+0xbc/0xe0 [<ffffffff812f5808>] ? driver_detach+0xd8/0xe0 [<ffffffff812f45d1>] ? bus_remove_driver+0x91/0x100 [<ffffffff812f6022>] ? driver_unregister+0x62/0xa0 [<ffffffff8123d5d4>] ? pci_unregister_driver+0x44/0xa0 [<ffffffffa05632d1>] ? iwl_exit+0x15/0x1c [iwlagn] [<ffffffff810ab492>] ? sys_delete_module+0x1a2/0x270 [<ffffffff81498da9>] ? trace_hardirqs_on_thunk+0x3a/0x3f [<ffffffff8100bf42>] ? system_call_fastpath+0x16/0x1b I still can observe above warning after apply patch, but it is very hard to reproduce it, and have count=1. Whereas that one is easy to reproduce using debugfs force_reset while transmitting data, and have very big counts eg. 240, like quoted here. So count=1 WARNING seems to be different issue that need to be resolved separately. v1 -> v2: fix infinity loop bug I made during "for" to "while" loop transition. v2 -> v3: remove unneeded EXPORT_SYMBOL Signed-off-by: Stanislaw Gruszka <sgruszka@redhat.com> Acked-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-tx.c12
-rw-r--r--drivers/net/wireless/iwlwifi/iwl-core.h2
-rw-r--r--drivers/net/wireless/iwlwifi/iwl-tx.c71
3 files changed, 59 insertions, 26 deletions
diff --git a/drivers/net/wireless/iwlwifi/iwl-agn-tx.c b/drivers/net/wireless/iwlwifi/iwl-agn-tx.c
index 266490d8a397..a709d05c5868 100644
--- a/drivers/net/wireless/iwlwifi/iwl-agn-tx.c
+++ b/drivers/net/wireless/iwlwifi/iwl-agn-tx.c
@@ -947,7 +947,7 @@ void iwlagn_txq_ctx_reset(struct iwl_priv *priv)
947 */ 947 */
948void iwlagn_txq_ctx_stop(struct iwl_priv *priv) 948void iwlagn_txq_ctx_stop(struct iwl_priv *priv)
949{ 949{
950 int ch; 950 int ch, txq_id;
951 unsigned long flags; 951 unsigned long flags;
952 952
953 /* Turn off all Tx DMA fifos */ 953 /* Turn off all Tx DMA fifos */
@@ -966,6 +966,16 @@ void iwlagn_txq_ctx_stop(struct iwl_priv *priv)
966 iwl_read_direct32(priv, FH_TSSR_TX_STATUS_REG)); 966 iwl_read_direct32(priv, FH_TSSR_TX_STATUS_REG));
967 } 967 }
968 spin_unlock_irqrestore(&priv->lock, flags); 968 spin_unlock_irqrestore(&priv->lock, flags);
969
970 if (!priv->txq)
971 return;
972
973 /* Unmap DMA from host system and free skb's */
974 for (txq_id = 0; txq_id < priv->hw_params.max_txq_num; txq_id++)
975 if (txq_id == priv->cmd_queue)
976 iwl_cmd_queue_unmap(priv);
977 else
978 iwl_tx_queue_unmap(priv, txq_id);
969} 979}
970 980
971/* 981/*
diff --git a/drivers/net/wireless/iwlwifi/iwl-core.h b/drivers/net/wireless/iwlwifi/iwl-core.h
index 909b42d5d9c0..ce368d8d402b 100644
--- a/drivers/net/wireless/iwlwifi/iwl-core.h
+++ b/drivers/net/wireless/iwlwifi/iwl-core.h
@@ -510,6 +510,7 @@ void iwl_rx_reply_error(struct iwl_priv *priv,
510* RX 510* RX
511******************************************************/ 511******************************************************/
512void iwl_cmd_queue_free(struct iwl_priv *priv); 512void iwl_cmd_queue_free(struct iwl_priv *priv);
513void iwl_cmd_queue_unmap(struct iwl_priv *priv);
513int iwl_rx_queue_alloc(struct iwl_priv *priv); 514int iwl_rx_queue_alloc(struct iwl_priv *priv);
514void iwl_rx_queue_update_write_ptr(struct iwl_priv *priv, 515void iwl_rx_queue_update_write_ptr(struct iwl_priv *priv,
515 struct iwl_rx_queue *q); 516 struct iwl_rx_queue *q);
@@ -534,6 +535,7 @@ int iwl_tx_queue_init(struct iwl_priv *priv, struct iwl_tx_queue *txq,
534void iwl_tx_queue_reset(struct iwl_priv *priv, struct iwl_tx_queue *txq, 535void iwl_tx_queue_reset(struct iwl_priv *priv, struct iwl_tx_queue *txq,
535 int slots_num, u32 txq_id); 536 int slots_num, u32 txq_id);
536void iwl_tx_queue_free(struct iwl_priv *priv, int txq_id); 537void iwl_tx_queue_free(struct iwl_priv *priv, int txq_id);
538void iwl_tx_queue_unmap(struct iwl_priv *priv, int txq_id);
537void iwl_setup_watchdog(struct iwl_priv *priv); 539void iwl_setup_watchdog(struct iwl_priv *priv);
538/***************************************************** 540/*****************************************************
539 * TX power 541 * TX power
diff --git a/drivers/net/wireless/iwlwifi/iwl-tx.c b/drivers/net/wireless/iwlwifi/iwl-tx.c
index 7e607d39da1c..277c9175dcf6 100644
--- a/drivers/net/wireless/iwlwifi/iwl-tx.c
+++ b/drivers/net/wireless/iwlwifi/iwl-tx.c
@@ -86,6 +86,23 @@ void iwl_txq_update_write_ptr(struct iwl_priv *priv, struct iwl_tx_queue *txq)
86} 86}
87 87
88/** 88/**
89 * iwl_tx_queue_unmap - Unmap any remaining DMA mappings and free skb's
90 */
91void iwl_tx_queue_unmap(struct iwl_priv *priv, int txq_id)
92{
93 struct iwl_tx_queue *txq = &priv->txq[txq_id];
94 struct iwl_queue *q = &txq->q;
95
96 if (q->n_bd == 0)
97 return;
98
99 while (q->write_ptr != q->read_ptr) {
100 priv->cfg->ops->lib->txq_free_tfd(priv, txq);
101 q->read_ptr = iwl_queue_inc_wrap(q->read_ptr, q->n_bd);
102 }
103}
104
105/**
89 * iwl_tx_queue_free - Deallocate DMA queue. 106 * iwl_tx_queue_free - Deallocate DMA queue.
90 * @txq: Transmit queue to deallocate. 107 * @txq: Transmit queue to deallocate.
91 * 108 *
@@ -96,17 +113,10 @@ void iwl_txq_update_write_ptr(struct iwl_priv *priv, struct iwl_tx_queue *txq)
96void iwl_tx_queue_free(struct iwl_priv *priv, int txq_id) 113void iwl_tx_queue_free(struct iwl_priv *priv, int txq_id)
97{ 114{
98 struct iwl_tx_queue *txq = &priv->txq[txq_id]; 115 struct iwl_tx_queue *txq = &priv->txq[txq_id];
99 struct iwl_queue *q = &txq->q;
100 struct device *dev = &priv->pci_dev->dev; 116 struct device *dev = &priv->pci_dev->dev;
101 int i; 117 int i;
102 118
103 if (q->n_bd == 0) 119 iwl_tx_queue_unmap(priv, txq_id);
104 return;
105
106 /* first, empty all BD's */
107 for (; q->write_ptr != q->read_ptr;
108 q->read_ptr = iwl_queue_inc_wrap(q->read_ptr, q->n_bd))
109 priv->cfg->ops->lib->txq_free_tfd(priv, txq);
110 120
111 /* De-alloc array of command/tx buffers */ 121 /* De-alloc array of command/tx buffers */
112 for (i = 0; i < TFD_TX_CMD_SLOTS; i++) 122 for (i = 0; i < TFD_TX_CMD_SLOTS; i++)
@@ -132,39 +142,33 @@ void iwl_tx_queue_free(struct iwl_priv *priv, int txq_id)
132} 142}
133 143
134/** 144/**
135 * iwl_cmd_queue_free - Deallocate DMA queue. 145 * iwl_cmd_queue_unmap - Unmap any remaining DMA mappings from command queue
136 * @txq: Transmit queue to deallocate.
137 *
138 * Empty queue by removing and destroying all BD's.
139 * Free all buffers.
140 * 0-fill, but do not free "txq" descriptor structure.
141 */ 146 */
142void iwl_cmd_queue_free(struct iwl_priv *priv) 147void iwl_cmd_queue_unmap(struct iwl_priv *priv)
143{ 148{
144 struct iwl_tx_queue *txq = &priv->txq[priv->cmd_queue]; 149 struct iwl_tx_queue *txq = &priv->txq[priv->cmd_queue];
145 struct iwl_queue *q = &txq->q; 150 struct iwl_queue *q = &txq->q;
146 struct device *dev = &priv->pci_dev->dev;
147 int i; 151 int i;
148 bool huge = false; 152 bool huge = false;
149 153
150 if (q->n_bd == 0) 154 if (q->n_bd == 0)
151 return; 155 return;
152 156
153 for (; q->read_ptr != q->write_ptr; 157 while (q->read_ptr != q->write_ptr) {
154 q->read_ptr = iwl_queue_inc_wrap(q->read_ptr, q->n_bd)) {
155 /* we have no way to tell if it is a huge cmd ATM */ 158 /* we have no way to tell if it is a huge cmd ATM */
156 i = get_cmd_index(q, q->read_ptr, 0); 159 i = get_cmd_index(q, q->read_ptr, 0);
157 160
158 if (txq->meta[i].flags & CMD_SIZE_HUGE) { 161 if (txq->meta[i].flags & CMD_SIZE_HUGE)
159 huge = true; 162 huge = true;
160 continue; 163 else
161 } 164 pci_unmap_single(priv->pci_dev,
165 dma_unmap_addr(&txq->meta[i], mapping),
166 dma_unmap_len(&txq->meta[i], len),
167 PCI_DMA_BIDIRECTIONAL);
162 168
163 pci_unmap_single(priv->pci_dev, 169 q->read_ptr = iwl_queue_inc_wrap(q->read_ptr, q->n_bd);
164 dma_unmap_addr(&txq->meta[i], mapping),
165 dma_unmap_len(&txq->meta[i], len),
166 PCI_DMA_BIDIRECTIONAL);
167 } 170 }
171
168 if (huge) { 172 if (huge) {
169 i = q->n_window; 173 i = q->n_window;
170 pci_unmap_single(priv->pci_dev, 174 pci_unmap_single(priv->pci_dev,
@@ -172,6 +176,23 @@ void iwl_cmd_queue_free(struct iwl_priv *priv)
172 dma_unmap_len(&txq->meta[i], len), 176 dma_unmap_len(&txq->meta[i], len),
173 PCI_DMA_BIDIRECTIONAL); 177 PCI_DMA_BIDIRECTIONAL);
174 } 178 }
179}
180
181/**
182 * iwl_cmd_queue_free - Deallocate DMA queue.
183 * @txq: Transmit queue to deallocate.
184 *
185 * Empty queue by removing and destroying all BD's.
186 * Free all buffers.
187 * 0-fill, but do not free "txq" descriptor structure.
188 */
189void iwl_cmd_queue_free(struct iwl_priv *priv)
190{
191 struct iwl_tx_queue *txq = &priv->txq[priv->cmd_queue];
192 struct device *dev = &priv->pci_dev->dev;
193 int i;
194
195 iwl_cmd_queue_unmap(priv);
175 196
176 /* De-alloc array of command/tx buffers */ 197 /* De-alloc array of command/tx buffers */
177 for (i = 0; i <= TFD_CMD_SLOTS; i++) 198 for (i = 0; i <= TFD_CMD_SLOTS; i++)