Jelajahi Sumber

rtlwifi: Fix kernel panic resulting from RX buffer allocation failure

To handle amsdu_8k capability, the PCI routine of this driver must
allocate receive buffers of order 2. Under heavy load, this causes
fragmentation of memory. The present code releases the current buffer
before checking to see if a new one is availble. Recovery from
allocation failures is not possible, which results in kernel panics.

The fix is to reorder the code to check that a new buffer can be
allocated before the old one is released. If not possible, the
received frame is dropped and the old one is reused. Without this
change, it is impossible to transfer a 2 GB file without a kernel panic.

Signed-off-by: Larry Finger <Larry.Finger@lwfinger.net>
Cc: Stable <stable@vger.kernel.org>              [2.6.{37,38,39}]
Signed-off-by: John W. Linville <linville@tuxdriver.com>
Larry Finger 14 tahun lalu
induk
melakukan
a9e1286975
1 mengubah file dengan 15 tambahan dan 13 penghapusan
  1. 15 13
      drivers/net/wireless/rtlwifi/pci.c

+ 15 - 13
drivers/net/wireless/rtlwifi/pci.c

@@ -669,11 +669,6 @@ static void _rtl_pci_rx_interrupt(struct ieee80211_hw *hw)
 							 &rx_status,
 							 &rx_status,
 							 (u8 *) pdesc, skb);
 							 (u8 *) pdesc, skb);
 
 
-			pci_unmap_single(rtlpci->pdev,
-					 *((dma_addr_t *) skb->cb),
-					 rtlpci->rxbuffersize,
-					 PCI_DMA_FROMDEVICE);
-
 			skb_put(skb, rtlpriv->cfg->ops->get_desc((u8 *) pdesc,
 			skb_put(skb, rtlpriv->cfg->ops->get_desc((u8 *) pdesc,
 							 false,
 							 false,
 							 HW_DESC_RXPKT_LEN));
 							 HW_DESC_RXPKT_LEN));
@@ -690,6 +685,21 @@ static void _rtl_pci_rx_interrupt(struct ieee80211_hw *hw)
 			hdr = rtl_get_hdr(skb);
 			hdr = rtl_get_hdr(skb);
 			fc = rtl_get_fc(skb);
 			fc = rtl_get_fc(skb);
 
 
+			/* try for new buffer - if allocation fails, drop
+			 * frame and reuse old buffer
+			 */
+			new_skb = dev_alloc_skb(rtlpci->rxbuffersize);
+			if (unlikely(!new_skb)) {
+				RT_TRACE(rtlpriv, (COMP_INTR | COMP_RECV),
+					 DBG_DMESG,
+					 ("can't alloc skb for rx\n"));
+				goto done;
+			}
+			pci_unmap_single(rtlpci->pdev,
+					 *((dma_addr_t *) skb->cb),
+					 rtlpci->rxbuffersize,
+					 PCI_DMA_FROMDEVICE);
+
 			if (!stats.crc || !stats.hwerror) {
 			if (!stats.crc || !stats.hwerror) {
 				memcpy(IEEE80211_SKB_RXCB(skb), &rx_status,
 				memcpy(IEEE80211_SKB_RXCB(skb), &rx_status,
 				       sizeof(rx_status));
 				       sizeof(rx_status));
@@ -758,15 +768,7 @@ static void _rtl_pci_rx_interrupt(struct ieee80211_hw *hw)
 				rtl_lps_leave(hw);
 				rtl_lps_leave(hw);
 			}
 			}
 
 
-			new_skb = dev_alloc_skb(rtlpci->rxbuffersize);
-			if (unlikely(!new_skb)) {
-				RT_TRACE(rtlpriv, (COMP_INTR | COMP_RECV),
-					 DBG_DMESG,
-					 ("can't alloc skb for rx\n"));
-				goto done;
-			}
 			skb = new_skb;
 			skb = new_skb;
-			/*skb->dev = dev; */
 
 
 			rtlpci->rx_ring[rx_queue_idx].rx_buf[rtlpci->
 			rtlpci->rx_ring[rx_queue_idx].rx_buf[rtlpci->
 							     rx_ring
 							     rx_ring