Sophie

Sophie

distrib > Scientific%20Linux > 5x > x86_64 > by-pkgid > fc11cd6e1c513a17304da94a5390f3cd > files > 3858

kernel-2.6.18-194.11.1.el5.src.rpm

From: John W. Linville <linville@redhat.com>
Date: Tue, 21 Apr 2009 13:54:59 -0400
Subject: [wireless] iwlwifi: problems switching b/w WPA and WEP
Message-id: 20090421175458.GD6194@redhat.com
O-Subject: [RHEL5.4 PATCH] iwlwifi: address problems switching between WPA and WEP
Bugzilla: 474699

Below is a back-port of three fixes for iwlwifi that improve moving
between wireless networks with different encryption capabilities.  It is
meant to further address:

BZ474699

Back-port of the following commit from the -stable series for 2.6.27...

commit d3bbe24b08edbfb28f68dea0187933b6908d4d85
Author: Tomas Winkler <tomas.winkler@intel.com>
Date:   Tue Nov 25 23:29:03 2008 +0200

    iwlwifi: clean key table in iwl_clear_stations_table function

    commit 40a9a8299116297429298e8fcee08235134883f7 upstream.

    This patch cleans uCode key table bit map iwl_clear_stations_table
    since all stations are cleared also the key table must be.

    Since the keys are not removed properly on suspend by mac80211
    this may result in exhausting key table on resume leading
    to memory corruption during removal

    This patch also fixes a memory corruption problem reported in
    http://marc.info/?l=linux-wireless&m=122641417231586&w=2 and tracked in
    http://bugzilla.kernel.org/show_bug.cgi?id=12040.

    When the key is removed a second time the offset is set to 255 - this
    index is not valid for the ucode_key_table and corrupts the eeprom pointer
    (which is 255 bits from ucode_key_table).

    Signed-off-by: Tomas Winkler <tomas.winkler@intel.com>
    Signed-off-by: Zhu Yi <yi.zhu@intel.com>
    Reported-by: Carlos R. Mafra <crmafra2@gmail.com>
    Reported-by: Lukas Hejtmanek <xhejtman@ics.muni.cz>
    Signed-off-by: John W. Linville <linville@tuxdriver.com>
    Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>

Back-port of the following commit from the -stable series for 2.6.27...

commit fa5c9e490836ada4cd4bcd97cc44da3696811c7d
Author: Johannes Berg <johannes@sipsolutions.net>
Date:   Tue Nov 18 01:47:21 2008 +0100

    iwlagn: fix RX skb alignment

    commit 4018517a1a69a85c3d61b20fa02f187b80773137 upstream.

    So I dug deeper into the DMA problems I had with iwlagn and a kind soul
    helped me in that he said something about pci-e alignment and mentioned
    the iwl_rx_allocate function to check for crossing 4KB boundaries.  Since
    there's 8KB A-MPDU support, crossing 4k boundaries didn't seem like
    something the device would fail with, but when I looked into the
    function for a minute anyway I stumbled over this little gem:

        BUG_ON(rxb->dma_addr & (~DMA_BIT_MASK(36) & 0xff));

    Clearly, that is a totally bogus check, one would hope the compiler
    removes it entirely. (Think about it)

    After fixing it, I obviously ran into it, nothing guarantees the
    alignment the way you want it,  because of the way skbs and their
    headroom are allocated. I won't explain that here nor double-check that
    I'm right, that goes beyond what most of the CC'ed people care about.

    So then I came up with the patch below, and so far my system has
    survived minutes with 64K pages, when it would previously fail in
    seconds. And I haven't seen a single instance of the TX bug either. But
    when you see the patch it'll be pretty obvious to you why.

    This should fix the following reported kernel bugs:

    http://bugzilla.kernel.org/show_bug.cgi?id=11596
    http://bugzilla.kernel.org/show_bug.cgi?id=11393
    http://bugzilla.kernel.org/show_bug.cgi?id=11983

    I haven't checked if there are any elsewhere, but I suppose RHBZ will
    have a few instances too...

    I'd like to ask anyone who is CC'ed (those are people I know ran into
    the bug) to try this patch.

    I am convinced that this patch is correct in spirit, but I haven't
    understood why, for example, there are so many unmap calls. I'm not
    entirely convinced that this is the only bug leading to the TX reply
    errors.

    Signed-off-by: Johannes Berg <johannes@sipsolutions.net>
    Signed-off-by: John W. Linville <linville@tuxdriver.com>
    Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>

Back-port of the following upstream commit...

commit 48676eb3c3de9013de7d9a63fb8ffb70cd540d95
Author: Mohamed Abbas <mohamed.abbas@intel.com>
Date:   Wed Mar 11 11:17:59 2009 -0700

    iwlagn: fix warning when set WEP key

    iwl_clear_station_table will be called every time rxon called.
    In this function ucode_key_table is set to 0 even though a static
    WEP security is set. This will cause in many warning and might be
    an issue if dynamic WEP is set. This patch make sure we keep track
    of all existing static WEP when this function is called.

    Signed-off-by: Mohamed Abbas <mohamed.abbas@intel.com>
    Signed-off-by: Reinette Chatre <reinette.chatre@intel.com>
    Signed-off-by: John W. Linville <linville@tuxdriver.com>

Testing performed by me and testers inside of Intel.  One of the Intel
testers put the results this way: "we are on the right way and get nearer
and nearer to final goal".  While things are still not perfect, this
combined patch makes things better at very low risk.  Please apply this
for RHEL5.4.

diff --git a/drivers/net/wireless/iwlwifi/iwl-agn.c b/drivers/net/wireless/iwlwifi/iwl-agn.c
index 116afca..3a8f53a 100644
--- a/drivers/net/wireless/iwlwifi/iwl-agn.c
+++ b/drivers/net/wireless/iwlwifi/iwl-agn.c
@@ -1386,7 +1386,7 @@ void iwl_rx_handle(struct iwl_priv *priv)
 
 		rxq->queue[i] = NULL;
 
-		pci_dma_sync_single_for_cpu(priv->pci_dev, rxb->dma_addr,
+		pci_dma_sync_single_for_cpu(priv->pci_dev, rxb->aligned_dma_addr,
 					    priv->hw_params.rx_buf_size,
 					    PCI_DMA_FROMDEVICE);
 		pkt = (struct iwl_rx_packet *)rxb->skb->data;
@@ -1438,8 +1438,8 @@ void iwl_rx_handle(struct iwl_priv *priv)
 			rxb->skb = NULL;
 		}
 
-		pci_unmap_single(priv->pci_dev, rxb->dma_addr,
-				 priv->hw_params.rx_buf_size,
+		pci_unmap_single(priv->pci_dev, rxb->real_dma_addr,
+				 priv->hw_params.rx_buf_size + 256,
 				 PCI_DMA_FROMDEVICE);
 		spin_lock_irqsave(&rxq->lock, flags);
 		list_add_tail(&rxb->list, &priv->rxq.rx_used);
diff --git a/drivers/net/wireless/iwlwifi/iwl-core.c b/drivers/net/wireless/iwlwifi/iwl-core.c
index aac3299..24cf0f1 100644
--- a/drivers/net/wireless/iwlwifi/iwl-core.c
+++ b/drivers/net/wireless/iwlwifi/iwl-core.c
@@ -282,6 +282,7 @@ EXPORT_SYMBOL(iwl_hw_nic_init);
 void iwl_clear_stations_table(struct iwl_priv *priv)
 {
 	unsigned long flags;
+	int i;
 
 	spin_lock_irqsave(&priv->sta_lock, flags);
 
@@ -293,6 +294,15 @@ void iwl_clear_stations_table(struct iwl_priv *priv)
 	priv->num_stations = 0;
 	memset(priv->stations, 0, sizeof(priv->stations));
 
+	/* clean ucode key table bit map */
+	priv->ucode_key_table = 0;
+
+	/* keep track of static keys */
+	for (i = 0; i < WEP_KEYS_MAX ; i++) {
+		if (priv->wep_keys[i].key_size)
+			test_and_set_bit(i, &priv->ucode_key_table);
+	}
+
 	spin_unlock_irqrestore(&priv->sta_lock, flags);
 }
 EXPORT_SYMBOL(iwl_clear_stations_table);
diff --git a/drivers/net/wireless/iwlwifi/iwl-dev.h b/drivers/net/wireless/iwlwifi/iwl-dev.h
index 8d1b845..e2acc22 100644
--- a/drivers/net/wireless/iwlwifi/iwl-dev.h
+++ b/drivers/net/wireless/iwlwifi/iwl-dev.h
@@ -89,7 +89,8 @@ extern struct iwl_cfg iwl5100_abg_cfg;
 #define	DEFAULT_LONG_RETRY_LIMIT  4U
 
 struct iwl_rx_mem_buffer {
-	dma_addr_t dma_addr;
+	dma_addr_t real_dma_addr;
+	dma_addr_t aligned_dma_addr;
 	struct sk_buff *skb;
 	struct list_head list;
 };
diff --git a/drivers/net/wireless/iwlwifi/iwl-rx.c b/drivers/net/wireless/iwlwifi/iwl-rx.c
index 02fbd79..966f0b3 100644
--- a/drivers/net/wireless/iwlwifi/iwl-rx.c
+++ b/drivers/net/wireless/iwlwifi/iwl-rx.c
@@ -204,7 +204,7 @@ int iwl_rx_queue_restock(struct iwl_priv *priv)
 		list_del(element);
 
 		/* Point to Rx buffer via next RBD in circular buffer */
-		rxq->bd[rxq->write] = iwl_dma_addr2rbd_ptr(priv, rxb->dma_addr);
+		rxq->bd[rxq->write] = iwl_dma_addr2rbd_ptr(priv, rxb->aligned_dma_addr);
 		rxq->queue[rxq->write] = rxb;
 		rxq->write = (rxq->write + 1) & RX_QUEUE_MASK;
 		rxq->free_count--;
@@ -251,7 +251,7 @@ void iwl_rx_allocate(struct iwl_priv *priv)
 		rxb = list_entry(element, struct iwl_rx_mem_buffer, list);
 
 		/* Alloc a new receive buffer */
-		rxb->skb = alloc_skb(priv->hw_params.rx_buf_size,
+		rxb->skb = alloc_skb(priv->hw_params.rx_buf_size + 256,
 				__GFP_NOWARN | GFP_ATOMIC);
 		if (!rxb->skb) {
 			if (net_ratelimit())
@@ -266,9 +266,17 @@ void iwl_rx_allocate(struct iwl_priv *priv)
 		list_del(element);
 
 		/* Get physical address of RB/SKB */
-		rxb->dma_addr =
-		    pci_map_single(priv->pci_dev, rxb->skb->data,
-			   priv->hw_params.rx_buf_size, PCI_DMA_FROMDEVICE);
+		rxb->real_dma_addr = pci_map_single(
+					priv->pci_dev,
+					rxb->skb->data,
+					priv->hw_params.rx_buf_size + 256,
+					PCI_DMA_FROMDEVICE);
+		/* dma address must be no more than 36 bits */
+		BUG_ON(rxb->real_dma_addr & ~DMA_BIT_MASK(36));
+		/* and also 256 byte aligned! */
+		rxb->aligned_dma_addr = ALIGN(rxb->real_dma_addr, 256);
+		skb_reserve(rxb->skb, rxb->aligned_dma_addr - rxb->real_dma_addr);
+
 		list_add_tail(&rxb->list, &rxq->rx_free);
 		rxq->free_count++;
 	}
@@ -300,8 +308,8 @@ void iwl_rx_queue_free(struct iwl_priv *priv, struct iwl_rx_queue *rxq)
 	for (i = 0; i < RX_QUEUE_SIZE + RX_FREE_BUFFERS; i++) {
 		if (rxq->pool[i].skb != NULL) {
 			pci_unmap_single(priv->pci_dev,
-					 rxq->pool[i].dma_addr,
-					 priv->hw_params.rx_buf_size,
+					 rxq->pool[i].real_dma_addr,
+					 priv->hw_params.rx_buf_size + 256,
 					 PCI_DMA_FROMDEVICE);
 			dev_kfree_skb(rxq->pool[i].skb);
 		}
@@ -354,8 +362,8 @@ void iwl_rx_queue_reset(struct iwl_priv *priv, struct iwl_rx_queue *rxq)
 		 * to an SKB, so we need to unmap and free potential storage */
 		if (rxq->pool[i].skb != NULL) {
 			pci_unmap_single(priv->pci_dev,
-					 rxq->pool[i].dma_addr,
-					 priv->hw_params.rx_buf_size,
+					 rxq->pool[i].real_dma_addr,
+					 priv->hw_params.rx_buf_size + 256,
 					 PCI_DMA_FROMDEVICE);
 			priv->alloc_rxb_skb--;
 			dev_kfree_skb(rxq->pool[i].skb);
diff --git a/drivers/net/wireless/iwlwifi/iwl-sta.c b/drivers/net/wireless/iwlwifi/iwl-sta.c
index 912309d..b590749 100644
--- a/drivers/net/wireless/iwlwifi/iwl-sta.c
+++ b/drivers/net/wireless/iwlwifi/iwl-sta.c
@@ -476,7 +476,7 @@ static int iwl_get_free_ucode_key_index(struct iwl_priv *priv)
 		if (!test_and_set_bit(i, &priv->ucode_key_table))
 			return i;
 
-	return -1;
+	return WEP_INVALID_OFFSET;
 }
 
 int iwl_send_static_wepkey_cmd(struct iwl_priv *priv, u8 send_if_empty)
@@ -623,6 +623,9 @@ static int iwl_set_wep_dynamic_key_info(struct iwl_priv *priv,
 	/* else, we are overriding an existing key => no need to allocated room
 	 * in uCode. */
 
+	if (priv->stations[sta_id].sta.key.key_offset == WEP_INVALID_OFFSET)
+		IWL_WARNING("no space for new kew");
+
 	priv->stations[sta_id].sta.key.key_flags = key_flags;
 	priv->stations[sta_id].sta.sta.modify_mask = STA_MODIFY_KEY_MASK;
 	priv->stations[sta_id].sta.mode = STA_CONTROL_MODIFY_MSK;
@@ -640,6 +643,7 @@ static int iwl_set_ccmp_dynamic_key_info(struct iwl_priv *priv,
 {
 	unsigned long flags;
 	__le16 key_flags = 0;
+	int ret;
 
 	key_flags |= (STA_KEY_FLG_CCMP | STA_KEY_FLG_MAP_KEY_MSK);
 	key_flags |= cpu_to_le16(keyconf->keyidx << STA_KEY_FLG_KEYID_POS);
@@ -669,14 +673,18 @@ static int iwl_set_ccmp_dynamic_key_info(struct iwl_priv *priv,
 	/* else, we are overriding an existing key => no need to allocated room
 	 * in uCode. */
 
+	if (priv->stations[sta_id].sta.key.key_offset == WEP_INVALID_OFFSET)
+		IWL_WARNING("no space for new kew");
+
 	priv->stations[sta_id].sta.key.key_flags = key_flags;
 	priv->stations[sta_id].sta.sta.modify_mask = STA_MODIFY_KEY_MASK;
 	priv->stations[sta_id].sta.mode = STA_CONTROL_MODIFY_MSK;
 
+	ret = iwl_send_add_sta(priv, &priv->stations[sta_id].sta, CMD_ASYNC);
+
 	spin_unlock_irqrestore(&priv->sta_lock, flags);
 
-	IWL_DEBUG_INFO("hwcrypto: modify ucode station key info\n");
-	return iwl_send_add_sta(priv, &priv->stations[sta_id].sta, CMD_ASYNC);
+	return ret;
 }
 
 static int iwl_set_tkip_dynamic_key_info(struct iwl_priv *priv,
@@ -703,6 +711,9 @@ static int iwl_set_tkip_dynamic_key_info(struct iwl_priv *priv,
 	/* else, we are overriding an existing key => no need to allocated room
 	 * in uCode. */
 
+	if (priv->stations[sta_id].sta.key.key_offset == WEP_INVALID_OFFSET)
+		IWL_WARNING("no space for new kew");
+
 	/* This copy is acutally not needed: we get the key with each TX */
 	memcpy(priv->stations[sta_id].keyinfo.key, keyconf->key, 16);
 
@@ -741,6 +752,13 @@ int iwl_remove_dynamic_key(struct iwl_priv *priv,
 		return 0;
 	}
 
+	if (priv->stations[sta_id].sta.key.key_offset == WEP_INVALID_OFFSET) {
+		IWL_WARNING("Removing wrong key %d 0x%x\n",
+			    keyconf->keyidx, key_flags);
+		spin_unlock_irqrestore(&priv->sta_lock, flags);
+		return 0;
+	}
+
 	if (!test_and_clear_bit(priv->stations[sta_id].sta.key.key_offset,
 		&priv->ucode_key_table))
 		IWL_ERROR("index %d not used in uCode key table.\n",