--- /dev/null
+From 34a5b4b6af104cf18eb50748509528b9bdbc4036 Mon Sep 17 00:00:00 2001
+From: Wey-Yi Guy <wey-yi.w.guy@intel.com>
+Date: Fri, 2 Dec 2011 08:19:18 -0800
+Subject: iwlwifi: do not re-configure HT40 after associated
+
+From: Wey-Yi Guy <wey-yi.w.guy@intel.com>
+
+commit 34a5b4b6af104cf18eb50748509528b9bdbc4036 upstream.
+
+The ht40 setting should not change after association unless channel switch
+
+This fix a problem we are seeing which cause uCode assert because driver
+sending invalid information and make uCode confuse
+
+Here is the firmware assert message:
+kernel: iwlagn 0000:03:00.0: Microcode SW error detected. Restarting 0x82000000.
+kernel: iwlagn 0000:03:00.0: Loaded firmware version: 17.168.5.3 build 42301
+kernel: iwlagn 0000:03:00.0: Start IWL Error Log Dump:
+kernel: iwlagn 0000:03:00.0: Status: 0x000512E4, count: 6
+kernel: iwlagn 0000:03:00.0: 0x00002078 | ADVANCED_SYSASSERT
+kernel: iwlagn 0000:03:00.0: 0x00009514 | uPc
+kernel: iwlagn 0000:03:00.0: 0x00009496 | branchlink1
+kernel: iwlagn 0000:03:00.0: 0x00009496 | branchlink2
+kernel: iwlagn 0000:03:00.0: 0x0000D1F2 | interruptlink1
+kernel: iwlagn 0000:03:00.0: 0x00000000 | interruptlink2
+kernel: iwlagn 0000:03:00.0: 0x01008035 | data1
+kernel: iwlagn 0000:03:00.0: 0x0000C90F | data2
+kernel: iwlagn 0000:03:00.0: 0x000005A7 | line
+kernel: iwlagn 0000:03:00.0: 0x5080B520 | beacon time
+kernel: iwlagn 0000:03:00.0: 0xCC515AE0 | tsf low
+kernel: iwlagn 0000:03:00.0: 0x00000003 | tsf hi
+kernel: iwlagn 0000:03:00.0: 0x00000000 | time gp1
+kernel: iwlagn 0000:03:00.0: 0x29703BF0 | time gp2
+kernel: iwlagn 0000:03:00.0: 0x00000000 | time gp3
+kernel: iwlagn 0000:03:00.0: 0x000111A8 | uCode version
+kernel: iwlagn 0000:03:00.0: 0x000000B0 | hw version
+kernel: iwlagn 0000:03:00.0: 0x00480303 | board version
+kernel: iwlagn 0000:03:00.0: 0x09E8004E | hcmd
+kernel: iwlagn 0000:03:00.0: CSR values:
+kernel: iwlagn 0000:03:00.0: (2nd byte of CSR_INT_COALESCING is CSR_INT_PERIODIC_REG)
+kernel: iwlagn 0000:03:00.0: CSR_HW_IF_CONFIG_REG: 0X00480303
+kernel: iwlagn 0000:03:00.0: CSR_INT_COALESCING: 0X0000ff40
+kernel: iwlagn 0000:03:00.0: CSR_INT: 0X00000000
+kernel: iwlagn 0000:03:00.0: CSR_INT_MASK: 0X00000000
+kernel: iwlagn 0000:03:00.0: CSR_FH_INT_STATUS: 0X00000000
+kernel: iwlagn 0000:03:00.0: CSR_GPIO_IN: 0X00000030
+kernel: iwlagn 0000:03:00.0: CSR_RESET: 0X00000000
+kernel: iwlagn 0000:03:00.0: CSR_GP_CNTRL: 0X080403c5
+kernel: iwlagn 0000:03:00.0: CSR_HW_REV: 0X000000b0
+kernel: iwlagn 0000:03:00.0: CSR_EEPROM_REG: 0X07d60ffd
+kernel: iwlagn 0000:03:00.0: CSR_EEPROM_GP: 0X90000001
+kernel: iwlagn 0000:03:00.0: CSR_OTP_GP_REG: 0X00030001
+kernel: iwlagn 0000:03:00.0: CSR_GIO_REG: 0X00080044
+kernel: iwlagn 0000:03:00.0: CSR_GP_UCODE_REG: 0X000093bb
+kernel: iwlagn 0000:03:00.0: CSR_GP_DRIVER_REG: 0X00000000
+kernel: iwlagn 0000:03:00.0: CSR_UCODE_DRV_GP1: 0X00000000
+kernel: iwlagn 0000:03:00.0: CSR_UCODE_DRV_GP2: 0X00000000
+kernel: iwlagn 0000:03:00.0: CSR_LED_REG: 0X00000078
+kernel: iwlagn 0000:03:00.0: CSR_DRAM_INT_TBL_REG: 0X88214dd2
+kernel: iwlagn 0000:03:00.0: CSR_GIO_CHICKEN_BITS: 0X27800200
+kernel: iwlagn 0000:03:00.0: CSR_ANA_PLL_CFG: 0X00000000
+kernel: iwlagn 0000:03:00.0: CSR_HW_REV_WA_REG: 0X0001001a
+kernel: iwlagn 0000:03:00.0: CSR_DBG_HPET_MEM_REG: 0Xffff0010
+kernel: iwlagn 0000:03:00.0: FH register values:
+kernel: iwlagn 0000:03:00.0: FH_RSCSR_CHNL0_STTS_WPTR_REG: 0X21316d00
+kernel: iwlagn 0000:03:00.0: FH_RSCSR_CHNL0_RBDCB_BASE_REG: 0X021479c0
+kernel: iwlagn 0000:03:00.0: FH_RSCSR_CHNL0_WPTR: 0X00000060
+kernel: iwlagn 0000:03:00.0: FH_MEM_RCSR_CHNL0_CONFIG_REG: 0X80819104
+kernel: iwlagn 0000:03:00.0: FH_MEM_RSSR_SHARED_CTRL_REG: 0X000000fc
+kernel: iwlagn 0000:03:00.0: FH_MEM_RSSR_RX_STATUS_REG: 0X07030000
+kernel: iwlagn 0000:03:00.0: FH_MEM_RSSR_RX_ENABLE_ERR_IRQ2DRV: 0X00000000
+kernel: iwlagn 0000:03:00.0: FH_TSSR_TX_STATUS_REG: 0X07ff0001
+kernel: iwlagn 0000:03:00.0: FH_TSSR_TX_ERROR_REG: 0X00000000
+kernel: iwlagn 0000:03:00.0: Start IWL Event Log Dump: display last 20 entries
+kernel: ------------[ cut here ]------------
+WARNING: at net/mac80211/util.c:1208 ieee80211_reconfig+0x1f1/0x407()
+kernel: Hardware name: 4290W4H
+kernel: Pid: 1896, comm: kworker/0:0 Not tainted 3.1.0 #2
+kernel: Call Trace:
+kernel: [<ffffffff81036558>] ? warn_slowpath_common+0x73/0x87
+kernel: [<ffffffff813b8966>] ? ieee80211_reconfig+0x1f1/0x407
+kernel: [<ffffffff8139e8dc>] ? ieee80211_recalc_smps_work+0x32/0x32
+kernel: [<ffffffff8139e95a>] ? ieee80211_restart_work+0x7e/0x87
+kernel: [<ffffffff810472fa>] ? process_one_work+0x1c8/0x2e3
+kernel: [<ffffffff810480c9>] ? worker_thread+0x17a/0x23a
+kernel: [<ffffffff81047f4f>] ? manage_workers.clone.18+0x15b/0x15b
+kernel: [<ffffffff81047f4f>] ? manage_workers.clone.18+0x15b/0x15b
+kernel: [<ffffffff8104ba97>] ? kthread+0x7a/0x82
+kernel: [<ffffffff813d21b4>] ? kernel_thread_helper+0x4/0x10
+kernel: [<ffffffff8104ba1d>] ? kthread_flush_work_fn+0x11/0x11
+kernel: [<ffffffff813d21b0>] ? gs_change+0xb/0xb
+
+Reported-by: Udo Steinberg <udo@hypervisor.org>
+Signed-off-by: Wey-Yi Guy <wey-yi.w.guy@intel.com>
+Signed-off-by: John W. Linville <linville@tuxdriver.com>
+Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
+
+--- a/drivers/net/wireless/iwlwifi/iwl-agn-rxon.c
++++ b/drivers/net/wireless/iwlwifi/iwl-agn-rxon.c
+@@ -528,6 +528,24 @@ int iwlagn_commit_rxon(struct iwl_priv *priv, struct iwl_rxon_context *ctx)
+ return 0;
+ }
+
++void iwlagn_config_ht40(struct ieee80211_conf *conf,
++ struct iwl_rxon_context *ctx)
++{
++ if (conf_is_ht40_minus(conf)) {
++ ctx->ht.extension_chan_offset =
++ IEEE80211_HT_PARAM_CHA_SEC_BELOW;
++ ctx->ht.is_40mhz = true;
++ } else if (conf_is_ht40_plus(conf)) {
++ ctx->ht.extension_chan_offset =
++ IEEE80211_HT_PARAM_CHA_SEC_ABOVE;
++ ctx->ht.is_40mhz = true;
++ } else {
++ ctx->ht.extension_chan_offset =
++ IEEE80211_HT_PARAM_CHA_SEC_NONE;
++ ctx->ht.is_40mhz = false;
++ }
++}
++
+ int iwlagn_mac_config(struct ieee80211_hw *hw, u32 changed)
+ {
+ struct iwl_priv *priv = hw->priv;
+@@ -586,19 +604,11 @@ int iwlagn_mac_config(struct ieee80211_hw *hw, u32 changed)
+ ctx->ht.enabled = conf_is_ht(conf);
+
+ if (ctx->ht.enabled) {
+- if (conf_is_ht40_minus(conf)) {
+- ctx->ht.extension_chan_offset =
+- IEEE80211_HT_PARAM_CHA_SEC_BELOW;
+- ctx->ht.is_40mhz = true;
+- } else if (conf_is_ht40_plus(conf)) {
+- ctx->ht.extension_chan_offset =
+- IEEE80211_HT_PARAM_CHA_SEC_ABOVE;
+- ctx->ht.is_40mhz = true;
+- } else {
+- ctx->ht.extension_chan_offset =
+- IEEE80211_HT_PARAM_CHA_SEC_NONE;
+- ctx->ht.is_40mhz = false;
+- }
++ /* if HT40 is used, it should not change
++ * after associated except channel switch */
++ if (iwl_is_associated_ctx(ctx) &&
++ !ctx->ht.is_40mhz)
++ iwlagn_config_ht40(conf, ctx);
+ } else
+ ctx->ht.is_40mhz = false;
+
+diff --git a/drivers/net/wireless/iwlwifi/iwl-agn.c b/drivers/net/wireless/iwlwifi/iwl-agn.c
+index 6094dea..f556451 100644
+--- a/drivers/net/wireless/iwlwifi/iwl-agn.c
++++ b/drivers/net/wireless/iwlwifi/iwl-agn.c
+@@ -2610,21 +2610,9 @@ static void iwlagn_mac_channel_switch(struct ieee80211_hw *hw,
+
+ /* Configure HT40 channels */
+ ctx->ht.enabled = conf_is_ht(conf);
+- if (ctx->ht.enabled) {
+- if (conf_is_ht40_minus(conf)) {
+- ctx->ht.extension_chan_offset =
+- IEEE80211_HT_PARAM_CHA_SEC_BELOW;
+- ctx->ht.is_40mhz = true;
+- } else if (conf_is_ht40_plus(conf)) {
+- ctx->ht.extension_chan_offset =
+- IEEE80211_HT_PARAM_CHA_SEC_ABOVE;
+- ctx->ht.is_40mhz = true;
+- } else {
+- ctx->ht.extension_chan_offset =
+- IEEE80211_HT_PARAM_CHA_SEC_NONE;
+- ctx->ht.is_40mhz = false;
+- }
+- } else
++ if (ctx->ht.enabled)
++ iwlagn_config_ht40(conf, ctx);
++ else
+ ctx->ht.is_40mhz = false;
+
+ if ((le16_to_cpu(ctx->staging.channel) != ch))
+diff --git a/drivers/net/wireless/iwlwifi/iwl-agn.h b/drivers/net/wireless/iwlwifi/iwl-agn.h
+index 5b936ec..3856aba 100644
+--- a/drivers/net/wireless/iwlwifi/iwl-agn.h
++++ b/drivers/net/wireless/iwlwifi/iwl-agn.h
+@@ -86,6 +86,8 @@ void iwlagn_bss_info_changed(struct ieee80211_hw *hw,
+ struct ieee80211_vif *vif,
+ struct ieee80211_bss_conf *bss_conf,
+ u32 changes);
++void iwlagn_config_ht40(struct ieee80211_conf *conf,
++ struct iwl_rxon_context *ctx);
+
+ /* uCode */
+ int iwlagn_rx_calib_result(struct iwl_priv *priv,
--- /dev/null
+From a855b84c3d8c73220d4d3cd392a7bee7c83de70e Mon Sep 17 00:00:00 2001
+From: Tejun Heo <tj@kernel.org>
+Date: Fri, 18 Nov 2011 10:55:35 -0800
+Subject: percpu: fix chunk range calculation
+
+From: Tejun Heo <tj@kernel.org>
+
+commit a855b84c3d8c73220d4d3cd392a7bee7c83de70e upstream.
+
+Percpu allocator recorded the cpus which map to the first and last
+units in pcpu_first/last_unit_cpu respectively and used them to
+determine the address range of a chunk - e.g. it assumed that the
+first unit has the lowest address in a chunk while the last unit has
+the highest address.
+
+This simply isn't true. Groups in a chunk can have arbitrary positive
+or negative offsets from the previous one and there is no guarantee
+that the first unit occupies the lowest offset while the last one the
+highest.
+
+Fix it by actually comparing unit offsets to determine cpus occupying
+the lowest and highest offsets. Also, rename pcu_first/last_unit_cpu
+to pcpu_low/high_unit_cpu to avoid confusion.
+
+The chunk address range is used to flush cache on vmalloc area
+map/unmap and decide whether a given address is in the first chunk by
+per_cpu_ptr_to_phys() and the bug was discovered by invalid
+per_cpu_ptr_to_phys() translation for crash_note.
+
+Kudos to Dave Young for tracking down the problem.
+
+Signed-off-by: Tejun Heo <tj@kernel.org>
+Reported-by: WANG Cong <xiyou.wangcong@gmail.com>
+Reported-by: Dave Young <dyoung@redhat.com>
+Tested-by: Dave Young <dyoung@redhat.com>
+LKML-Reference: <4EC21F67.10905@redhat.com>
+Signed-off-by: Thomas Renninger <trenn@suse.de>
+Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
+
+---
+ mm/percpu-vm.c | 12 ++++++------
+ mm/percpu.c | 34 ++++++++++++++++++++--------------
+ 2 files changed, 26 insertions(+), 20 deletions(-)
+
+--- a/mm/percpu-vm.c
++++ b/mm/percpu-vm.c
+@@ -143,8 +143,8 @@ static void pcpu_pre_unmap_flush(struct
+ int page_start, int page_end)
+ {
+ flush_cache_vunmap(
+- pcpu_chunk_addr(chunk, pcpu_first_unit_cpu, page_start),
+- pcpu_chunk_addr(chunk, pcpu_last_unit_cpu, page_end));
++ pcpu_chunk_addr(chunk, pcpu_low_unit_cpu, page_start),
++ pcpu_chunk_addr(chunk, pcpu_high_unit_cpu, page_end));
+ }
+
+ static void __pcpu_unmap_pages(unsigned long addr, int nr_pages)
+@@ -206,8 +206,8 @@ static void pcpu_post_unmap_tlb_flush(st
+ int page_start, int page_end)
+ {
+ flush_tlb_kernel_range(
+- pcpu_chunk_addr(chunk, pcpu_first_unit_cpu, page_start),
+- pcpu_chunk_addr(chunk, pcpu_last_unit_cpu, page_end));
++ pcpu_chunk_addr(chunk, pcpu_low_unit_cpu, page_start),
++ pcpu_chunk_addr(chunk, pcpu_high_unit_cpu, page_end));
+ }
+
+ static int __pcpu_map_pages(unsigned long addr, struct page **pages,
+@@ -284,8 +284,8 @@ static void pcpu_post_map_flush(struct p
+ int page_start, int page_end)
+ {
+ flush_cache_vmap(
+- pcpu_chunk_addr(chunk, pcpu_first_unit_cpu, page_start),
+- pcpu_chunk_addr(chunk, pcpu_last_unit_cpu, page_end));
++ pcpu_chunk_addr(chunk, pcpu_low_unit_cpu, page_start),
++ pcpu_chunk_addr(chunk, pcpu_high_unit_cpu, page_end));
+ }
+
+ /**
+--- a/mm/percpu.c
++++ b/mm/percpu.c
+@@ -116,9 +116,9 @@ static int pcpu_atom_size __read_mostly;
+ static int pcpu_nr_slots __read_mostly;
+ static size_t pcpu_chunk_struct_size __read_mostly;
+
+-/* cpus with the lowest and highest unit numbers */
+-static unsigned int pcpu_first_unit_cpu __read_mostly;
+-static unsigned int pcpu_last_unit_cpu __read_mostly;
++/* cpus with the lowest and highest unit addresses */
++static unsigned int pcpu_low_unit_cpu __read_mostly;
++static unsigned int pcpu_high_unit_cpu __read_mostly;
+
+ /* the address of the first chunk which starts with the kernel static area */
+ void *pcpu_base_addr __read_mostly;
+@@ -984,19 +984,19 @@ phys_addr_t per_cpu_ptr_to_phys(void *ad
+ {
+ void __percpu *base = __addr_to_pcpu_ptr(pcpu_base_addr);
+ bool in_first_chunk = false;
+- unsigned long first_start, first_end;
++ unsigned long first_low, first_high;
+ unsigned int cpu;
+
+ /*
+- * The following test on first_start/end isn't strictly
++ * The following test on unit_low/high isn't strictly
+ * necessary but will speed up lookups of addresses which
+ * aren't in the first chunk.
+ */
+- first_start = pcpu_chunk_addr(pcpu_first_chunk, pcpu_first_unit_cpu, 0);
+- first_end = pcpu_chunk_addr(pcpu_first_chunk, pcpu_last_unit_cpu,
+- pcpu_unit_pages);
+- if ((unsigned long)addr >= first_start &&
+- (unsigned long)addr < first_end) {
++ first_low = pcpu_chunk_addr(pcpu_first_chunk, pcpu_low_unit_cpu, 0);
++ first_high = pcpu_chunk_addr(pcpu_first_chunk, pcpu_high_unit_cpu,
++ pcpu_unit_pages);
++ if ((unsigned long)addr >= first_low &&
++ (unsigned long)addr < first_high) {
+ for_each_possible_cpu(cpu) {
+ void *start = per_cpu_ptr(base, cpu);
+
+@@ -1233,7 +1233,9 @@ int __init pcpu_setup_first_chunk(const
+
+ for (cpu = 0; cpu < nr_cpu_ids; cpu++)
+ unit_map[cpu] = UINT_MAX;
+- pcpu_first_unit_cpu = NR_CPUS;
++
++ pcpu_low_unit_cpu = NR_CPUS;
++ pcpu_high_unit_cpu = NR_CPUS;
+
+ for (group = 0, unit = 0; group < ai->nr_groups; group++, unit += i) {
+ const struct pcpu_group_info *gi = &ai->groups[group];
+@@ -1253,9 +1255,13 @@ int __init pcpu_setup_first_chunk(const
+ unit_map[cpu] = unit + i;
+ unit_off[cpu] = gi->base_offset + i * ai->unit_size;
+
+- if (pcpu_first_unit_cpu == NR_CPUS)
+- pcpu_first_unit_cpu = cpu;
+- pcpu_last_unit_cpu = cpu;
++ /* determine low/high unit_cpu */
++ if (pcpu_low_unit_cpu == NR_CPUS ||
++ unit_off[cpu] < unit_off[pcpu_low_unit_cpu])
++ pcpu_low_unit_cpu = cpu;
++ if (pcpu_high_unit_cpu == NR_CPUS ||
++ unit_off[cpu] > unit_off[pcpu_high_unit_cpu])
++ pcpu_high_unit_cpu = cpu;
+ }
+ }
+ pcpu_nr_units = unit;