iwlwifi: Allocate pages for RX buffers, reducing the probability of allocation failure (Closes: #580124)

svn path=/dists/sid/linux-2.6/; revision=15986
This commit is contained in:
Ben Hutchings 2010-07-14 00:56:24 +00:00
parent c1b146f80e
commit 8c01e4e155
5 changed files with 1704 additions and 0 deletions

8
debian/changelog vendored
View File

@ -1,3 +1,11 @@
linux-2.6 (2.6.32-18) UNRELEASED; urgency=low
[ Ben Hutchings ]
* iwlwifi: Allocate pages for RX buffers, reducing the probability of
allocation failure (Closes: #580124)
-- Ben Hutchings <ben@decadent.org.uk> Wed, 14 Jul 2010 01:52:56 +0100
linux-2.6 (2.6.32-17) unstable; urgency=low
[ maximilian attems ]

View File

@ -0,0 +1,42 @@
From b2adbfe0a1a5c4c0c3f490000cf5ee11903ae674 Mon Sep 17 00:00:00 2001
From: Shanyu Zhao <shanyu.zhao@intel.com>
Date: Thu, 11 Feb 2010 10:42:22 -0800
Subject: [PATCH 3/3] iwlwifi: fix AMSDU Rx after paged Rx patch
commit fafaf31bf9f965d91462ee115e27ef6c262b74ea upstream.
Previous patch "use paged Rx" broke AMSDU Rx functionality. If an AP
sends out A-MSDU packets the station will crash. Fix it by linearizing
skbuff for AMSDU packet before handing it to mac80211 since mac80211
doesn't support paged skbuff.
This fixes http://bugzilla.intellinuxwireless.org/show_bug.cgi?id=2155
Reported-by: Norbert Preining <preining@logic.at>
Signed-off-by: Shanyu Zhao <shanyu.zhao@intel.com>
Acked-by: Zhu Yi <yi.zhu@intel.com>
Signed-off-by: Reinette Chatre <reinette.chatre@intel.com>
Signed-off-by: John W. Linville <linville@tuxdriver.com>
---
drivers/net/wireless/iwlwifi/iwl-rx.c | 5 ++++-
1 files changed, 4 insertions(+), 1 deletions(-)
diff --git a/drivers/net/wireless/iwlwifi/iwl-rx.c b/drivers/net/wireless/iwlwifi/iwl-rx.c
index c6e6ce0..bbdfe3b 100644
--- a/drivers/net/wireless/iwlwifi/iwl-rx.c
+++ b/drivers/net/wireless/iwlwifi/iwl-rx.c
@@ -914,7 +914,10 @@ static void iwl_pass_packet_to_mac80211(struct iwl_priv *priv,
if (ieee80211_is_mgmt(fc) ||
ieee80211_has_protected(fc) ||
ieee80211_has_morefrags(fc) ||
- le16_to_cpu(hdr->seq_ctrl) & IEEE80211_SCTL_FRAG)
+ le16_to_cpu(hdr->seq_ctrl) & IEEE80211_SCTL_FRAG ||
+ (ieee80211_is_data_qos(fc) &&
+ *ieee80211_get_qos_ctl(hdr) &
+ IEEE80211_QOS_CONTROL_A_MSDU_PRESENT))
ret = skb_linearize(skb);
else
ret = __pskb_pull_tail(skb, min_t(u16, IWL_LINK_HDR_MAX, len)) ?
--
1.7.1

View File

@ -0,0 +1,237 @@
From 64dc06ae744f4fb0a99649461a2a02489b77c4b9 Mon Sep 17 00:00:00 2001
From: Zhu Yi <yi.zhu@intel.com>
Date: Fri, 23 Oct 2009 13:42:25 -0700
Subject: [PATCH 2/3] iwlwifi: fix use after free bug for paged rx
commit 29b1b2688fd71346f78f175d9669c006686b6dc3 upstream.
In the paged rx patch (4854fde2), I introduced a bug that could possibly
touch an already freed page. It is fixed by avoiding the access in this
patch. I've also added some comments so that other people touching the
code won't make the same mistake. In the future, if we cannot avoid
access the page after being handled to the upper layer, we can use
get_page/put_page to handle it. For now, it's just not necessary.
It also fixed a debug message print bug reported by Stanislaw Gruszka
<sgruszka@redhat.com>.
Signed-off-by: Zhu Yi <yi.zhu@intel.com>
Signed-off-by: Reinette Chatre <reinette.chatre@intel.com>
Signed-off-by: John W. Linville <linville@tuxdriver.com>
[bwh: Backport to 2.6.32]
---
drivers/net/wireless/iwlwifi/iwl-3945.c | 19 ++++++++++++-------
drivers/net/wireless/iwlwifi/iwl-agn.c | 11 +++++++++--
drivers/net/wireless/iwlwifi/iwl-rx.c | 21 ++++++++++++++-------
drivers/net/wireless/iwlwifi/iwl3945-base.c | 18 +++++++++++++-----
4 files changed, 48 insertions(+), 21 deletions(-)
diff --git a/drivers/net/wireless/iwlwifi/iwl-3945.c b/drivers/net/wireless/iwlwifi/iwl-3945.c
index 4dde964..0dbfd49 100644
--- a/drivers/net/wireless/iwlwifi/iwl-3945.c
+++ b/drivers/net/wireless/iwlwifi/iwl-3945.c
@@ -552,6 +552,7 @@ static void iwl3945_pass_packet_to_mac80211(struct iwl_priv *priv,
u16 len = le16_to_cpu(rx_hdr->len);
struct sk_buff *skb;
int ret;
+ __le16 fc = hdr->frame_control;
/* We received data from the HW, so stop the watchdog */
if (unlikely(len + IWL39_RX_FRAME_SIZE >
@@ -584,9 +585,9 @@ static void iwl3945_pass_packet_to_mac80211(struct iwl_priv *priv,
/* mac80211 currently doesn't support paged SKB. Convert it to
* linear SKB for management frame and data frame requires
* software decryption or software defragementation. */
- if (ieee80211_is_mgmt(hdr->frame_control) ||
- ieee80211_has_protected(hdr->frame_control) ||
- ieee80211_has_morefrags(hdr->frame_control) ||
+ if (ieee80211_is_mgmt(fc) ||
+ ieee80211_has_protected(fc) ||
+ ieee80211_has_morefrags(fc) ||
le16_to_cpu(hdr->seq_ctrl) & IEEE80211_SCTL_FRAG)
ret = skb_linearize(skb);
else
@@ -598,15 +599,19 @@ static void iwl3945_pass_packet_to_mac80211(struct iwl_priv *priv,
goto out;
}
+ /*
+ * XXX: We cannot touch the page and its virtual memory (pkt) after
+ * here. It might have already been freed by the above skb change.
+ */
+
#ifdef CONFIG_IWLWIFI_LEDS
- if (ieee80211_is_data(hdr->frame_control))
+ if (ieee80211_is_data(fc))
priv->rxtxpackets += len;
#endif
- iwl_update_stats(priv, false, hdr->frame_control, len);
-
+ iwl_update_stats(priv, false, fc, len);
memcpy(IEEE80211_SKB_RXCB(skb), stats, sizeof(*stats));
- ieee80211_rx(priv->hw, skb);
+ ieee80211_rx(priv->hw, skb);
out:
priv->alloc_rxb_page--;
rxb->page = NULL;
diff --git a/drivers/net/wireless/iwlwifi/iwl-agn.c b/drivers/net/wireless/iwlwifi/iwl-agn.c
index 3d90530..c56d355 100644
--- a/drivers/net/wireless/iwlwifi/iwl-agn.c
+++ b/drivers/net/wireless/iwlwifi/iwl-agn.c
@@ -812,8 +812,8 @@ void iwl_rx_handle(struct iwl_priv *priv)
if (priv->rx_handlers[pkt->hdr.cmd]) {
IWL_DEBUG_RX(priv, "r = %d, i = %d, %s, 0x%02x\n", r,
i, get_cmd_string(pkt->hdr.cmd), pkt->hdr.cmd);
- priv->rx_handlers[pkt->hdr.cmd] (priv, rxb);
priv->isr_stats.rx_handlers[pkt->hdr.cmd]++;
+ priv->rx_handlers[pkt->hdr.cmd] (priv, rxb);
} else {
/* No handling needed */
IWL_DEBUG_RX(priv,
@@ -822,11 +822,18 @@ void iwl_rx_handle(struct iwl_priv *priv)
pkt->hdr.cmd);
}
+ /*
+ * XXX: After here, we should always check rxb->page
+ * against NULL before touching it or its virtual
+ * memory (pkt). Because some rx_handler might have
+ * already taken or freed the pages.
+ */
+
if (reclaim) {
/* Invoke any callbacks, transfer the buffer to caller,
* and fire off the (possibly) blocking iwl_send_cmd()
* as we reclaim the driver command queue */
- if (rxb && rxb->page)
+ if (rxb->page)
iwl_tx_cmd_complete(priv, rxb);
else
IWL_WARN(priv, "Claim null rxb?\n");
diff --git a/drivers/net/wireless/iwlwifi/iwl-rx.c b/drivers/net/wireless/iwlwifi/iwl-rx.c
index 5d10953..c6e6ce0 100644
--- a/drivers/net/wireless/iwlwifi/iwl-rx.c
+++ b/drivers/net/wireless/iwlwifi/iwl-rx.c
@@ -241,6 +241,7 @@ void iwl_rx_allocate(struct iwl_priv *priv, gfp_t priority)
struct iwl_rx_mem_buffer *rxb;
struct page *page;
unsigned long flags;
+ gfp_t gfp_mask = priority;
while (1) {
spin_lock_irqsave(&rxq->lock, flags);
@@ -251,13 +252,13 @@ void iwl_rx_allocate(struct iwl_priv *priv, gfp_t priority)
spin_unlock_irqrestore(&rxq->lock, flags);
if (rxq->free_count > RX_LOW_WATERMARK)
- priority |= __GFP_NOWARN;
+ gfp_mask |= __GFP_NOWARN;
if (priv->hw_params.rx_page_order > 0)
- priority |= __GFP_COMP;
+ gfp_mask |= __GFP_COMP;
/* Alloc a new receive buffer */
- page = alloc_pages(priority, priv->hw_params.rx_page_order);
+ page = alloc_pages(gfp_mask, priv->hw_params.rx_page_order);
if (!page) {
if (net_ratelimit())
IWL_DEBUG_INFO(priv, "alloc_pages failed, "
@@ -885,6 +886,7 @@ static void iwl_pass_packet_to_mac80211(struct iwl_priv *priv,
{
struct sk_buff *skb;
int ret = 0;
+ __le16 fc = hdr->frame_control;
/* We only process data packets if the interface is open */
if (unlikely(!priv->is_open)) {
@@ -909,9 +911,9 @@ static void iwl_pass_packet_to_mac80211(struct iwl_priv *priv,
/* mac80211 currently doesn't support paged SKB. Convert it to
* linear SKB for management frame and data frame requires
* software decryption or software defragementation. */
- if (ieee80211_is_mgmt(hdr->frame_control) ||
- ieee80211_has_protected(hdr->frame_control) ||
- ieee80211_has_morefrags(hdr->frame_control) ||
+ if (ieee80211_is_mgmt(fc) ||
+ ieee80211_has_protected(fc) ||
+ ieee80211_has_morefrags(fc) ||
le16_to_cpu(hdr->seq_ctrl) & IEEE80211_SCTL_FRAG)
ret = skb_linearize(skb);
else
@@ -923,7 +925,12 @@ static void iwl_pass_packet_to_mac80211(struct iwl_priv *priv,
goto out;
}
- iwl_update_stats(priv, false, hdr->frame_control, len);
+ /*
+ * XXX: We cannot touch the page and its virtual memory (hdr) after
+ * here. It might have already been freed by the above skb change.
+ */
+
+ iwl_update_stats(priv, false, fc, len);
memcpy(IEEE80211_SKB_RXCB(skb), stats, sizeof(*stats));
ieee80211_rx(priv->hw, skb);
diff --git a/drivers/net/wireless/iwlwifi/iwl3945-base.c b/drivers/net/wireless/iwlwifi/iwl3945-base.c
index 340e1c7..9030585 100644
--- a/drivers/net/wireless/iwlwifi/iwl3945-base.c
+++ b/drivers/net/wireless/iwlwifi/iwl3945-base.c
@@ -1140,6 +1140,7 @@ static void iwl3945_rx_allocate(struct iwl_priv *priv, gfp_t priority)
struct iwl_rx_mem_buffer *rxb;
struct page *page;
unsigned long flags;
+ gfp_t gfp_mask = priority;
while (1) {
spin_lock_irqsave(&rxq->lock, flags);
@@ -1151,13 +1152,13 @@ static void iwl3945_rx_allocate(struct iwl_priv *priv, gfp_t priority)
spin_unlock_irqrestore(&rxq->lock, flags);
if (rxq->free_count > RX_LOW_WATERMARK)
- priority |= __GFP_NOWARN;
+ gfp_mask |= __GFP_NOWARN;
if (priv->hw_params.rx_page_order > 0)
- priority |= __GFP_COMP;
+ gfp_mask |= __GFP_COMP;
/* Alloc a new receive buffer */
- page = alloc_pages(priority, priv->hw_params.rx_page_order);
+ page = alloc_pages(gfp_mask, priv->hw_params.rx_page_order);
if (!page) {
if (net_ratelimit())
IWL_DEBUG_INFO(priv, "Failed to allocate SKB buffer.\n");
@@ -1423,8 +1424,8 @@ static void iwl3945_rx_handle(struct iwl_priv *priv)
if (priv->rx_handlers[pkt->hdr.cmd]) {
IWL_DEBUG_RX(priv, "r = %d, i = %d, %s, 0x%02x\n", r, i,
get_cmd_string(pkt->hdr.cmd), pkt->hdr.cmd);
- priv->rx_handlers[pkt->hdr.cmd] (priv, rxb);
priv->isr_stats.rx_handlers[pkt->hdr.cmd]++;
+ priv->rx_handlers[pkt->hdr.cmd] (priv, rxb);
} else {
/* No handling needed */
IWL_DEBUG_RX(priv,
@@ -1433,11 +1434,18 @@ static void iwl3945_rx_handle(struct iwl_priv *priv)
pkt->hdr.cmd);
}
+ /*
+ * XXX: After here, we should always check rxb->page
+ * against NULL before touching it or its virtual
+ * memory (pkt). Because some rx_handler might have
+ * already taken or freed the pages.
+ */
+
if (reclaim) {
/* Invoke any callbacks, transfer the buffer to caller,
* and fire off the (possibly) blocking iwl_send_cmd()
* as we reclaim the driver command queue */
- if (rxb && rxb->page)
+ if (rxb->page)
iwl_tx_cmd_complete(priv, rxb);
else
IWL_WARN(priv, "Claim null rxb?\n");
--
1.7.1

File diff suppressed because it is too large Load Diff

3
debian/patches/series/18 vendored Normal file
View File

@ -0,0 +1,3 @@
+ features/all/iwlwifi-use-paged-Rx.patch
+ bugfix/all/iwlwifi-fix-use-after-free-bug-for-paged-rx.patch
+ bugfix/all/iwlwifi-fix-AMSDU-Rx-after-paged-Rx-patch.patch