From: Greg Kroah-Hartman Date: Thu, 16 May 2013 21:48:58 +0000 (-0400) Subject: 3.0-stable patches X-Git-Tag: v3.0.79~15 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=ad10154ccd5214bbb16a97b9f94c4ab7d4a407e9;p=thirdparty%2Fkernel%2Fstable-queue.git 3.0-stable patches added patches: ath9k-fix-key-allocation-error-handling-for-powersave-keys.patch drm-don-t-check-modeset-locks-in-panic-handler.patch drm-mm-fix-dump-table-bug.patch ext4-limit-group-search-loop-for-non-extent-files.patch mwifiex-clear-is_suspended-flag-when-interrupt-is-received-early.patch mwifiex-fix-setting-of-multicast-filter.patch powerpc-kexec-fix-kexec-when-using-vmx-optimised-memcpy.patch tick-cleanup-nohz-per-cpu-data-on-cpu-down.patch timer-don-t-reinitialize-the-cpu-base-lock-during-cpu_up_prepare.patch --- diff --git a/queue-3.0/ath9k-fix-key-allocation-error-handling-for-powersave-keys.patch b/queue-3.0/ath9k-fix-key-allocation-error-handling-for-powersave-keys.patch new file mode 100644 index 00000000000..6e1d3205327 --- /dev/null +++ b/queue-3.0/ath9k-fix-key-allocation-error-handling-for-powersave-keys.patch @@ -0,0 +1,52 @@ +From 4ef69d0394cba8caa9f75d3f2e53429bfb8b3045 Mon Sep 17 00:00:00 2001 +From: Felix Fietkau +Date: Sat, 27 Apr 2013 11:47:01 +0200 +Subject: ath9k: fix key allocation error handling for powersave keys + +From: Felix Fietkau + +commit 4ef69d0394cba8caa9f75d3f2e53429bfb8b3045 upstream. + +If no keycache slots are available, ath_key_config can return -ENOSPC. +If the key index is not checked for errors, it can lead to logspam that +looks like this: "ath: wiphy0: keyreset: keycache entry 228 out of range" +This can cause follow-up errors if the invalid keycache index gets +used for tx. + +Signed-off-by: Felix Fietkau +Signed-off-by: John W. Linville +Signed-off-by: Greg Kroah-Hartman + +--- + drivers/net/wireless/ath/ath9k/main.c | 6 +++++- + 1 file changed, 5 insertions(+), 1 deletion(-) + +--- a/drivers/net/wireless/ath/ath9k/main.c ++++ b/drivers/net/wireless/ath/ath9k/main.c +@@ -1791,6 +1791,7 @@ static int ath9k_sta_add(struct ieee8021 + struct ath_common *common = ath9k_hw_common(sc->sc_ah); + struct ath_node *an = (struct ath_node *) sta->drv_priv; + struct ieee80211_key_conf ps_key = { }; ++ int key; + + ath_node_attach(sc, sta); + +@@ -1798,7 +1799,9 @@ static int ath9k_sta_add(struct ieee8021 + vif->type != NL80211_IFTYPE_AP_VLAN) + return 0; + +- an->ps_key = ath_key_config(common, vif, sta, &ps_key); ++ key = ath_key_config(common, vif, sta, &ps_key); ++ if (key > 0) ++ an->ps_key = key; + + return 0; + } +@@ -1815,6 +1818,7 @@ static void ath9k_del_ps_key(struct ath_ + return; + + ath_key_delete(common, &ps_key); ++ an->ps_key = 0; + } + + static int ath9k_sta_remove(struct ieee80211_hw *hw, diff --git a/queue-3.0/drm-don-t-check-modeset-locks-in-panic-handler.patch b/queue-3.0/drm-don-t-check-modeset-locks-in-panic-handler.patch new file mode 100644 index 00000000000..e9fce03ddef --- /dev/null +++ b/queue-3.0/drm-don-t-check-modeset-locks-in-panic-handler.patch @@ -0,0 +1,36 @@ +From a9b054e8ab06504c2afa0e307ee78d3778993a1d Mon Sep 17 00:00:00 2001 +From: Daniel Vetter +Date: Thu, 2 May 2013 09:43:05 +0200 +Subject: drm: don't check modeset locks in panic handler + +From: Daniel Vetter + +commit a9b054e8ab06504c2afa0e307ee78d3778993a1d upstream. + +Since we know that locking is broken in that case and it's more +important to not flood the dmesg with random gunk. + +References: http://lkml.kernel.org/r/20130502000206.GH15623@pd.tnic +Cc: Dave Airlie +Cc: Borislav Petkov +Reported-and-tested-by: Borislav Petkov +Signed-off-by: Daniel Vetter +Signed-off-by: Greg Kroah-Hartman + +--- + drivers/gpu/drm/drm_crtc.c | 4 ++++ + 1 file changed, 4 insertions(+) + +--- a/drivers/gpu/drm/drm_crtc.c ++++ b/drivers/gpu/drm/drm_crtc.c +@@ -907,6 +907,10 @@ int drm_mode_group_init_legacy_group(str + if ((ret = drm_mode_group_init(dev, group))) + return ret; + ++ /* Locking is currently fubar in the panic handler. */ ++ if (oops_in_progress) ++ return; ++ + list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) + group->id_list[group->num_crtcs++] = crtc->base.id; + diff --git a/queue-3.0/drm-mm-fix-dump-table-bug.patch b/queue-3.0/drm-mm-fix-dump-table-bug.patch new file mode 100644 index 00000000000..16693e51a4b --- /dev/null +++ b/queue-3.0/drm-mm-fix-dump-table-bug.patch @@ -0,0 +1,92 @@ +From 3a359f0b21ab218c1bf7a6a1b638b6fd143d0b99 Mon Sep 17 00:00:00 2001 +From: Daniel Vetter +Date: Sat, 20 Apr 2013 12:08:11 +0200 +Subject: drm/mm: fix dump table BUG + +From: Daniel Vetter + +commit 3a359f0b21ab218c1bf7a6a1b638b6fd143d0b99 upstream. + +In + +commit 9e8944ab564f2e3dde90a518cd32048c58918608 +Author: Chris Wilson +Date: Thu Nov 15 11:32:17 2012 +0000 + + drm: Introduce an iterator over holes in the drm_mm range manager + +helpers and iterators for hole handling have been introduced with some +debug BUG_ONs sprinkled over. Unfortunately this broke the mm dumper +which unconditionally tried to compute the size of the very first +hole. + +While at it unify the code a bit with the hole dumping in the loop. + +v2: Extract a hole dump helper. + +Reported-by: Christopher Harvey +Cc: Christopher Harvey +Cc: Dave Airlie +Cc: Chris Wilson +Acked-by: Dave Airlie +Signed-off-by: Daniel Vetter +Signed-off-by: Greg Kroah-Hartman + +--- + drivers/gpu/drm/drm_mm.c | 34 ++++++++++++++++++---------------- + 1 file changed, 18 insertions(+), 16 deletions(-) + +--- a/drivers/gpu/drm/drm_mm.c ++++ b/drivers/gpu/drm/drm_mm.c +@@ -679,33 +679,35 @@ void drm_mm_debug_table(struct drm_mm *m + EXPORT_SYMBOL(drm_mm_debug_table); + + #if defined(CONFIG_DEBUG_FS) +-int drm_mm_dump_table(struct seq_file *m, struct drm_mm *mm) ++static unsigned long drm_mm_dump_hole(struct seq_file *m, struct drm_mm_node *entry) + { +- struct drm_mm_node *entry; +- unsigned long total_used = 0, total_free = 0, total = 0; + unsigned long hole_start, hole_end, hole_size; + +- hole_start = drm_mm_hole_node_start(&mm->head_node); +- hole_end = drm_mm_hole_node_end(&mm->head_node); +- hole_size = hole_end - hole_start; +- if (hole_size) ++ if (entry->hole_follows) { ++ hole_start = drm_mm_hole_node_start(entry); ++ hole_end = drm_mm_hole_node_end(entry); ++ hole_size = hole_end - hole_start; + seq_printf(m, "0x%08lx-0x%08lx: 0x%08lx: free\n", + hole_start, hole_end, hole_size); +- total_free += hole_size; ++ return hole_size; ++ } ++ ++ return 0; ++} ++ ++int drm_mm_dump_table(struct seq_file *m, struct drm_mm *mm) ++{ ++ struct drm_mm_node *entry; ++ unsigned long total_used = 0, total_free = 0, total = 0; ++ ++ total_free += drm_mm_dump_hole(m, &mm->head_node); + + drm_mm_for_each_node(entry, mm) { + seq_printf(m, "0x%08lx-0x%08lx: 0x%08lx: used\n", + entry->start, entry->start + entry->size, + entry->size); + total_used += entry->size; +- if (entry->hole_follows) { +- hole_start = drm_mm_hole_node_start(entry); +- hole_end = drm_mm_hole_node_end(entry); +- hole_size = hole_end - hole_start; +- seq_printf(m, "0x%08lx-0x%08lx: 0x%08lx: free\n", +- hole_start, hole_end, hole_size); +- total_free += hole_size; +- } ++ total_free += drm_mm_dump_hole(m, entry); + } + total = total_free + total_used; + diff --git a/queue-3.0/ext4-limit-group-search-loop-for-non-extent-files.patch b/queue-3.0/ext4-limit-group-search-loop-for-non-extent-files.patch new file mode 100644 index 00000000000..c139e50318c --- /dev/null +++ b/queue-3.0/ext4-limit-group-search-loop-for-non-extent-files.patch @@ -0,0 +1,55 @@ +From e6155736ad76b2070652745f9e54cdea3f0d8567 Mon Sep 17 00:00:00 2001 +From: Lachlan McIlroy +Date: Sun, 5 May 2013 23:10:00 -0400 +Subject: ext4: limit group search loop for non-extent files + +From: Lachlan McIlroy + +commit e6155736ad76b2070652745f9e54cdea3f0d8567 upstream. + +In the case where we are allocating for a non-extent file, +we must limit the groups we allocate from to those below +2^32 blocks, and ext4_mb_regular_allocator() attempts to +do this initially by putting a cap on ngroups for the +subsequent search loop. + +However, the initial target group comes in from the +allocation context (ac), and it may already be beyond +the artificially limited ngroups. In this case, +the limit + + if (group == ngroups) + group = 0; + +at the top of the loop is never true, and the loop will +run away. + +Catch this case inside the loop and reset the search to +start at group 0. + +[sandeen@redhat.com: add commit msg & comments] + +Signed-off-by: Lachlan McIlroy +Signed-off-by: Eric Sandeen +Signed-off-by: "Theodore Ts'o" +Signed-off-by: Greg Kroah-Hartman + +--- + fs/ext4/mballoc.c | 6 +++++- + 1 file changed, 5 insertions(+), 1 deletion(-) + +--- a/fs/ext4/mballoc.c ++++ b/fs/ext4/mballoc.c +@@ -2026,7 +2026,11 @@ repeat: + group = ac->ac_g_ex.fe_group; + + for (i = 0; i < ngroups; group++, i++) { +- if (group == ngroups) ++ /* ++ * Artificially restricted ngroups for non-extent ++ * files makes group > ngroups possible on first loop. ++ */ ++ if (group >= ngroups) + group = 0; + + /* This now checks without needing the buddy page */ diff --git a/queue-3.0/mwifiex-clear-is_suspended-flag-when-interrupt-is-received-early.patch b/queue-3.0/mwifiex-clear-is_suspended-flag-when-interrupt-is-received-early.patch new file mode 100644 index 00000000000..d92cba802e1 --- /dev/null +++ b/queue-3.0/mwifiex-clear-is_suspended-flag-when-interrupt-is-received-early.patch @@ -0,0 +1,60 @@ +From 48795424acff7215d5eac0b52793a2c1eb3a6283 Mon Sep 17 00:00:00 2001 +From: Bing Zhao +Date: Mon, 6 May 2013 19:46:53 -0700 +Subject: mwifiex: clear is_suspended flag when interrupt is received early + +From: Bing Zhao + +commit 48795424acff7215d5eac0b52793a2c1eb3a6283 upstream. + +When the XO-4 with 8787 wireless is woken up due to wake-on-WLAN +mwifiex is often flooded with "not allowed while suspended" messages +and the interface is unusable. + +[ 202.171609] int: sdio_ireg = 0x1 +[ 202.180700] info: mwifiex_process_hs_config: auto cancelling host + sleep since there is interrupt from the firmware +[ 202.201880] event: wakeup device... +[ 202.211452] event: hs_deactivated +[ 202.514638] info: --- Rx: Data packet --- +[ 202.514753] data: 4294957544 BSS(0-0): Data <= kernel +[ 202.514825] PREP_CMD: device in suspended state +[ 202.514839] data: dequeuing the packet ec7248c0 ec4869c0 +[ 202.514886] mwifiex_write_data_sync: not allowed while suspended +[ 202.514886] host_to_card, write iomem (1) failed: -1 +[ 202.514917] mwifiex_write_data_sync: not allowed while suspended +[ 202.514936] host_to_card, write iomem (2) failed: -1 +[ 202.514949] mwifiex_write_data_sync: not allowed while suspended +[ 202.514965] host_to_card, write iomem (3) failed: -1 +[ 202.514976] mwifiex_write_data_async failed: 0xFFFFFFFF + +This can be readily reproduced when putting the XO-4 in a loop where +it goes to sleep due to inactivity, but then wakes up due to an +incoming ping. The error is hit within an hour or two. + +This issue happens when an interrupt comes in early while host sleep +is still activated. Driver handles this case by auto cancelling host +sleep. However is_suspended flag is still set which prevents any cmd +or data from being sent to firmware. Fix it by clearing is_suspended +flag in this path. + +Reported-by: Daniel Drake +Tested-by: Daniel Drake +Signed-off-by: Bing Zhao +Signed-off-by: John W. Linville +Signed-off-by: Greg Kroah-Hartman + +--- + drivers/net/wireless/mwifiex/cmdevt.c | 1 + + 1 file changed, 1 insertion(+) + +--- a/drivers/net/wireless/mwifiex/cmdevt.c ++++ b/drivers/net/wireless/mwifiex/cmdevt.c +@@ -1081,6 +1081,7 @@ mwifiex_process_hs_config(struct mwifiex + adapter->if_ops.wakeup(adapter); + adapter->hs_activated = false; + adapter->is_hs_configured = false; ++ adapter->is_suspended = false; + mwifiex_hs_activated_event(mwifiex_get_priv(adapter, + MWIFIEX_BSS_ROLE_ANY), false); + } diff --git a/queue-3.0/mwifiex-fix-setting-of-multicast-filter.patch b/queue-3.0/mwifiex-fix-setting-of-multicast-filter.patch new file mode 100644 index 00000000000..9717dae7aac --- /dev/null +++ b/queue-3.0/mwifiex-fix-setting-of-multicast-filter.patch @@ -0,0 +1,66 @@ +From ccd384b10420ac81ba3fb9b0a7d18272c7173552 Mon Sep 17 00:00:00 2001 +From: Daniel Drake +Date: Wed, 8 May 2013 15:37:19 -0400 +Subject: mwifiex: fix setting of multicast filter + +From: Daniel Drake + +commit ccd384b10420ac81ba3fb9b0a7d18272c7173552 upstream. + +A small bug in this code was causing the ALLMULTI filter to be set +when in fact we were just wanting to program a selective multicast list +to the hardware. + +Fix that bug and remove a redundant if condition in the code that +follows. + +This fixes wakeup behaviour when multicast WOL is enabled. Previously, +all multicast packets would wake up the system. Now, only those that the +host intended to receive trigger wakeups. + +Signed-off-by: Daniel Drake +Acked-by: Bing Zhao +Signed-off-by: John W. Linville +Signed-off-by: Greg Kroah-Hartman + +--- + drivers/net/wireless/mwifiex/sta_ioctl.c | 21 ++++++--------------- + 1 file changed, 6 insertions(+), 15 deletions(-) + +--- a/drivers/net/wireless/mwifiex/sta_ioctl.c ++++ b/drivers/net/wireless/mwifiex/sta_ioctl.c +@@ -100,7 +100,7 @@ int mwifiex_request_set_multicast_list(s + } else { + /* Multicast */ + priv->curr_pkt_filter &= ~HostCmd_ACT_MAC_PROMISCUOUS_ENABLE; +- if (mcast_list->mode == MWIFIEX_MULTICAST_MODE) { ++ if (mcast_list->mode == MWIFIEX_ALL_MULTI_MODE) { + dev_dbg(priv->adapter->dev, + "info: Enabling All Multicast!\n"); + priv->curr_pkt_filter |= +@@ -112,20 +112,11 @@ int mwifiex_request_set_multicast_list(s + dev_dbg(priv->adapter->dev, + "info: Set multicast list=%d\n", + mcast_list->num_multicast_addr); +- /* Set multicast addresses to firmware */ +- if (old_pkt_filter == priv->curr_pkt_filter) { +- /* Send request to firmware */ +- ret = mwifiex_send_cmd_async(priv, +- HostCmd_CMD_MAC_MULTICAST_ADR, +- HostCmd_ACT_GEN_SET, 0, +- mcast_list); +- } else { +- /* Send request to firmware */ +- ret = mwifiex_send_cmd_async(priv, +- HostCmd_CMD_MAC_MULTICAST_ADR, +- HostCmd_ACT_GEN_SET, 0, +- mcast_list); +- } ++ /* Send multicast addresses to firmware */ ++ ret = mwifiex_send_cmd_async(priv, ++ HostCmd_CMD_MAC_MULTICAST_ADR, ++ HostCmd_ACT_GEN_SET, 0, ++ mcast_list); + } + } + } diff --git a/queue-3.0/powerpc-kexec-fix-kexec-when-using-vmx-optimised-memcpy.patch b/queue-3.0/powerpc-kexec-fix-kexec-when-using-vmx-optimised-memcpy.patch new file mode 100644 index 00000000000..24cf839064b --- /dev/null +++ b/queue-3.0/powerpc-kexec-fix-kexec-when-using-vmx-optimised-memcpy.patch @@ -0,0 +1,54 @@ +From 79c66ce8f6448a3295a32efeac88c9debd7f7094 Mon Sep 17 00:00:00 2001 +From: Anton Blanchard +Date: Sun, 12 May 2013 15:04:53 +0000 +Subject: powerpc/kexec: Fix kexec when using VMX optimised memcpy + +From: Anton Blanchard + +commit 79c66ce8f6448a3295a32efeac88c9debd7f7094 upstream. + +commit b3f271e86e5a (powerpc: POWER7 optimised memcpy using VMX and +enhanced prefetch) uses VMX when it is safe to do so (ie not in +interrupt). It also looks at the task struct to decide if we have to +save the current tasks' VMX state. + +kexec calls memcpy() at a point where the task struct may have been +overwritten by the new kexec segments. If it has been overwritten +then when memcpy -> enable_altivec looks up current->thread.regs->msr +we get a cryptic oops or lockup. + +I also notice we aren't initialising thread_info->cpu, which means +smp_processor_id is broken. Fix that too. + +Signed-off-by: Anton Blanchard +Signed-off-by: Benjamin Herrenschmidt +Signed-off-by: Greg Kroah-Hartman + +--- + arch/powerpc/kernel/machine_kexec_64.c | 4 ++++ + 1 file changed, 4 insertions(+) + +--- a/arch/powerpc/kernel/machine_kexec_64.c ++++ b/arch/powerpc/kernel/machine_kexec_64.c +@@ -17,6 +17,7 @@ + #include + #include + #include ++#include + + #include + #include +@@ -336,10 +337,13 @@ void default_machine_kexec(struct kimage + pr_debug("kexec: Starting switchover sequence.\n"); + + /* switch to a staticly allocated stack. Based on irq stack code. ++ * We setup preempt_count to avoid using VMX in memcpy. + * XXX: the task struct will likely be invalid once we do the copy! + */ + kexec_stack.thread_info.task = current_thread_info()->task; + kexec_stack.thread_info.flags = 0; ++ kexec_stack.thread_info.preempt_count = HARDIRQ_OFFSET; ++ kexec_stack.thread_info.cpu = current_thread_info()->cpu; + + /* We need a static PACA, too; copy this CPU's PACA over and switch to + * it. Also poison per_cpu_offset to catch anyone using non-static diff --git a/queue-3.0/series b/queue-3.0/series index 5dc89dc80de..2d9b1d4703a 100644 --- a/queue-3.0/series +++ b/queue-3.0/series @@ -5,3 +5,12 @@ acpica-fix-possible-buffer-overflow-during-a-field-unit-read-operation.patch dm-snapshot-fix-error-return-code-in-snapshot_ctr.patch xen-vcpu-pvhvm-fix-vcpu-hotplugging-hanging.patch scsi-sd-fix-array-cache-flushing-bug-causing-performance-problems.patch +timer-don-t-reinitialize-the-cpu-base-lock-during-cpu_up_prepare.patch +tick-cleanup-nohz-per-cpu-data-on-cpu-down.patch +ext4-limit-group-search-loop-for-non-extent-files.patch +powerpc-kexec-fix-kexec-when-using-vmx-optimised-memcpy.patch +ath9k-fix-key-allocation-error-handling-for-powersave-keys.patch +mwifiex-clear-is_suspended-flag-when-interrupt-is-received-early.patch +mwifiex-fix-setting-of-multicast-filter.patch +drm-mm-fix-dump-table-bug.patch +drm-don-t-check-modeset-locks-in-panic-handler.patch diff --git a/queue-3.0/tick-cleanup-nohz-per-cpu-data-on-cpu-down.patch b/queue-3.0/tick-cleanup-nohz-per-cpu-data-on-cpu-down.patch new file mode 100644 index 00000000000..93ea9fbb367 --- /dev/null +++ b/queue-3.0/tick-cleanup-nohz-per-cpu-data-on-cpu-down.patch @@ -0,0 +1,38 @@ +From 4b0c0f294f60abcdd20994a8341a95c8ac5eeb96 Mon Sep 17 00:00:00 2001 +From: Thomas Gleixner +Date: Fri, 3 May 2013 15:02:50 +0200 +Subject: tick: Cleanup NOHZ per cpu data on cpu down + +From: Thomas Gleixner + +commit 4b0c0f294f60abcdd20994a8341a95c8ac5eeb96 upstream. + +Prarit reported a crash on CPU offline/online. The reason is that on +CPU down the NOHZ related per cpu data of the dead cpu is not cleaned +up. If at cpu online an interrupt happens before the per cpu tick +device is registered the irq_enter() check potentially sees stale data +and dereferences a NULL pointer. + +Cleanup the data after the cpu is dead. + +Reported-by: Prarit Bhargava +Cc: Mike Galbraith +Link: http://lkml.kernel.org/r/alpine.LFD.2.02.1305031451561.2886@ionos +Signed-off-by: Thomas Gleixner +Signed-off-by: Greg Kroah-Hartman + +--- + kernel/time/tick-sched.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +--- a/kernel/time/tick-sched.c ++++ b/kernel/time/tick-sched.c +@@ -811,7 +811,7 @@ void tick_cancel_sched_timer(int cpu) + hrtimer_cancel(&ts->sched_timer); + # endif + +- ts->nohz_mode = NOHZ_MODE_INACTIVE; ++ memset(ts, 0, sizeof(*ts)); + } + #endif + diff --git a/queue-3.0/timer-don-t-reinitialize-the-cpu-base-lock-during-cpu_up_prepare.patch b/queue-3.0/timer-don-t-reinitialize-the-cpu-base-lock-during-cpu_up_prepare.patch new file mode 100644 index 00000000000..778421f158e --- /dev/null +++ b/queue-3.0/timer-don-t-reinitialize-the-cpu-base-lock-during-cpu_up_prepare.patch @@ -0,0 +1,85 @@ +From 42a5cf46cd56f46267d2a9fcf2655f4078cd3042 Mon Sep 17 00:00:00 2001 +From: Tirupathi Reddy +Date: Tue, 14 May 2013 13:59:02 +0530 +Subject: timer: Don't reinitialize the cpu base lock during CPU_UP_PREPARE + +From: Tirupathi Reddy + +commit 42a5cf46cd56f46267d2a9fcf2655f4078cd3042 upstream. + +An inactive timer's base can refer to a offline cpu's base. + +In the current code, cpu_base's lock is blindly reinitialized each +time a CPU is brought up. If a CPU is brought online during the period +that another thread is trying to modify an inactive timer on that CPU +with holding its timer base lock, then the lock will be reinitialized +under its feet. This leads to following SPIN_BUG(). + +<0> BUG: spinlock already unlocked on CPU#3, kworker/u:3/1466 +<0> lock: 0xe3ebe000, .magic: dead4ead, .owner: kworker/u:3/1466, .owner_cpu: 1 +<4> [] (unwind_backtrace+0x0/0x11c) from [] (do_raw_spin_unlock+0x40/0xcc) +<4> [] (do_raw_spin_unlock+0x40/0xcc) from [] (_raw_spin_unlock+0x8/0x30) +<4> [] (_raw_spin_unlock+0x8/0x30) from [] (mod_timer+0x294/0x310) +<4> [] (mod_timer+0x294/0x310) from [] (queue_delayed_work_on+0x104/0x120) +<4> [] (queue_delayed_work_on+0x104/0x120) from [] (sdhci_msm_bus_voting+0x88/0x9c) +<4> [] (sdhci_msm_bus_voting+0x88/0x9c) from [] (sdhci_disable+0x40/0x48) +<4> [] (sdhci_disable+0x40/0x48) from [] (mmc_release_host+0x4c/0xb0) +<4> [] (mmc_release_host+0x4c/0xb0) from [] (mmc_sd_detect+0x90/0xfc) +<4> [] (mmc_sd_detect+0x90/0xfc) from [] (mmc_rescan+0x7c/0x2c4) +<4> [] (mmc_rescan+0x7c/0x2c4) from [] (process_one_work+0x27c/0x484) +<4> [] (process_one_work+0x27c/0x484) from [] (worker_thread+0x210/0x3b0) +<4> [] (worker_thread+0x210/0x3b0) from [] (kthread+0x80/0x8c) +<4> [] (kthread+0x80/0x8c) from [] (kernel_thread_exit+0x0/0x8) + +As an example, this particular crash occurred when CPU #3 is executing +mod_timer() on an inactive timer whose base is refered to offlined CPU +#2. The code locked the timer_base corresponding to CPU #2. Before it +could proceed, CPU #2 came online and reinitialized the spinlock +corresponding to its base. Thus now CPU #3 held a lock which was +reinitialized. When CPU #3 finally ended up unlocking the old cpu_base +corresponding to CPU #2, we hit the above SPIN_BUG(). + +CPU #0 CPU #3 CPU #2 +------ ------- ------- +..... ...... + mod_timer() + lock_timer_base + spin_lock_irqsave(&base->lock) + +cpu_up(2) ..... ...... + init_timers_cpu() +.... ..... spin_lock_init(&base->lock) +..... spin_unlock_irqrestore(&base->lock) ...... + + +Allocation of per_cpu timer vector bases is done only once under +"tvec_base_done[]" check. In the current code, spinlock_initialization +of base->lock isn't under this check. When a CPU is up each time the +base lock is reinitialized. Move base spinlock initialization under +the check. + +Signed-off-by: Tirupathi Reddy +Link: http://lkml.kernel.org/r/1368520142-4136-1-git-send-email-tirupath@codeaurora.org +Signed-off-by: Thomas Gleixner +Signed-off-by: Greg Kroah-Hartman + +--- + kernel/timer.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +--- a/kernel/timer.c ++++ b/kernel/timer.c +@@ -1630,12 +1630,12 @@ static int __cpuinit init_timers_cpu(int + boot_done = 1; + base = &boot_tvec_bases; + } ++ spin_lock_init(&base->lock); + tvec_base_done[cpu] = 1; + } else { + base = per_cpu(tvec_bases, cpu); + } + +- spin_lock_init(&base->lock); + + for (j = 0; j < TVN_SIZE; j++) { + INIT_LIST_HEAD(base->tv5.vec + j);