Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

Merge branch 'core-locking-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip

Pull core locking updates from Ingo Molnar:
"This update:

- extends and simplifies x86 NMI callback handling code to enhance
and fix the HP hw-watchdog driver

- simplifies the x86 NMI callback handling code to fix a kmemcheck
bug.

- enhances the hung-task debugger"

* 'core-locking-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip:
x86/nmi: Fix the type of the nmiaction.flags field
x86/nmi: Fix page faults by nmiaction if kmemcheck is enabled
x86/nmi: Add new NMI queues to deal with IO_CHK and SERR
watchdog, hpwdt: Remove priority option for NMI callback
hung task debugging: Inject NMI when hung and going to panic

+71 -84
+20 -2
arch/x86/include/asm/nmi.h
··· 27 27 enum { 28 28 NMI_LOCAL=0, 29 29 NMI_UNKNOWN, 30 + NMI_SERR, 31 + NMI_IO_CHECK, 30 32 NMI_MAX 31 33 }; 32 34 ··· 37 35 38 36 typedef int (*nmi_handler_t)(unsigned int, struct pt_regs *); 39 37 40 - int register_nmi_handler(unsigned int, nmi_handler_t, unsigned long, 41 - const char *); 38 + struct nmiaction { 39 + struct list_head list; 40 + nmi_handler_t handler; 41 + unsigned long flags; 42 + const char *name; 43 + }; 44 + 45 + #define register_nmi_handler(t, fn, fg, n) \ 46 + ({ \ 47 + static struct nmiaction fn##_na = { \ 48 + .handler = (fn), \ 49 + .name = (n), \ 50 + .flags = (fg), \ 51 + }; \ 52 + __register_nmi_handler((t), &fn##_na); \ 53 + }) 54 + 55 + int __register_nmi_handler(unsigned int, struct nmiaction *); 42 56 43 57 void unregister_nmi_handler(unsigned int, const char *); 44 58
+24 -59
arch/x86/kernel/nmi.c
··· 31 31 #include <asm/nmi.h> 32 32 #include <asm/x86_init.h> 33 33 34 - #define NMI_MAX_NAMELEN 16 35 - struct nmiaction { 36 - struct list_head list; 37 - nmi_handler_t handler; 38 - unsigned int flags; 39 - char *name; 40 - }; 41 - 42 34 struct nmi_desc { 43 35 spinlock_t lock; 44 36 struct list_head head; ··· 45 53 { 46 54 .lock = __SPIN_LOCK_UNLOCKED(&nmi_desc[1].lock), 47 55 .head = LIST_HEAD_INIT(nmi_desc[1].head), 56 + }, 57 + { 58 + .lock = __SPIN_LOCK_UNLOCKED(&nmi_desc[2].lock), 59 + .head = LIST_HEAD_INIT(nmi_desc[2].head), 60 + }, 61 + { 62 + .lock = __SPIN_LOCK_UNLOCKED(&nmi_desc[3].lock), 63 + .head = LIST_HEAD_INIT(nmi_desc[3].head), 48 64 }, 49 65 50 66 }; ··· 107 107 return handled; 108 108 } 109 109 110 - static int __setup_nmi(unsigned int type, struct nmiaction *action) 110 + int __register_nmi_handler(unsigned int type, struct nmiaction *action) 111 111 { 112 112 struct nmi_desc *desc = nmi_to_desc(type); 113 113 unsigned long flags; 114 + 115 + if (!action->handler) 116 + return -EINVAL; 114 117 115 118 spin_lock_irqsave(&desc->lock, flags); 116 119 ··· 123 120 * to manage expectations 124 121 */ 125 122 WARN_ON_ONCE(type == NMI_UNKNOWN && !list_empty(&desc->head)); 123 + WARN_ON_ONCE(type == NMI_SERR && !list_empty(&desc->head)); 124 + WARN_ON_ONCE(type == NMI_IO_CHECK && !list_empty(&desc->head)); 126 125 127 126 /* 128 127 * some handlers need to be executed first otherwise a fake ··· 138 133 spin_unlock_irqrestore(&desc->lock, flags); 139 134 return 0; 140 135 } 136 + EXPORT_SYMBOL(__register_nmi_handler); 141 137 142 - static struct nmiaction *__free_nmi(unsigned int type, const char *name) 138 + void unregister_nmi_handler(unsigned int type, const char *name) 143 139 { 144 140 struct nmi_desc *desc = nmi_to_desc(type); 145 141 struct nmiaction *n; ··· 163 157 164 158 spin_unlock_irqrestore(&desc->lock, flags); 165 159 synchronize_rcu(); 166 - return (n); 167 160 } 168 - 169 - int register_nmi_handler(unsigned int type, nmi_handler_t handler, 170 - unsigned long nmiflags, const char *devname) 171 - { 172 - struct nmiaction *action; 173 - int retval = -ENOMEM; 174 - 175 - if (!handler) 176 - return -EINVAL; 177 - 178 - action = kzalloc(sizeof(struct nmiaction), GFP_KERNEL); 179 - if (!action) 180 - goto fail_action; 181 - 182 - action->handler = handler; 183 - action->flags = nmiflags; 184 - action->name = kstrndup(devname, NMI_MAX_NAMELEN, GFP_KERNEL); 185 - if (!action->name) 186 - goto fail_action_name; 187 - 188 - retval = __setup_nmi(type, action); 189 - 190 - if (retval) 191 - goto fail_setup_nmi; 192 - 193 - return retval; 194 - 195 - fail_setup_nmi: 196 - kfree(action->name); 197 - fail_action_name: 198 - kfree(action); 199 - fail_action: 200 - 201 - return retval; 202 - } 203 - EXPORT_SYMBOL_GPL(register_nmi_handler); 204 - 205 - void unregister_nmi_handler(unsigned int type, const char *name) 206 - { 207 - struct nmiaction *a; 208 - 209 - a = __free_nmi(type, name); 210 - if (a) { 211 - kfree(a->name); 212 - kfree(a); 213 - } 214 - } 215 - 216 161 EXPORT_SYMBOL_GPL(unregister_nmi_handler); 217 162 218 163 static notrace __kprobes void 219 164 pci_serr_error(unsigned char reason, struct pt_regs *regs) 220 165 { 166 + /* check to see if anyone registered against these types of errors */ 167 + if (nmi_handle(NMI_SERR, regs, false)) 168 + return; 169 + 221 170 pr_emerg("NMI: PCI system error (SERR) for reason %02x on CPU %d.\n", 222 171 reason, smp_processor_id()); 223 172 ··· 201 240 io_check_error(unsigned char reason, struct pt_regs *regs) 202 241 { 203 242 unsigned long i; 243 + 244 + /* check to see if anyone registered against these types of errors */ 245 + if (nmi_handle(NMI_IO_CHECK, regs, false)) 246 + return; 204 247 205 248 pr_emerg( 206 249 "NMI: IOCK error (debug interrupt?) for reason %02x on CPU %d.\n",
+24 -22
drivers/watchdog/hpwdt.c
··· 147 147 148 148 static unsigned int hpwdt_nmi_decoding; 149 149 static unsigned int allow_kdump; 150 - static unsigned int priority; /* hpwdt at end of die_notify list */ 151 150 static unsigned int is_icru; 152 151 static DEFINE_SPINLOCK(rom_lock); 153 152 static void *cru_rom_addr; ··· 722 723 } 723 724 724 725 /* 725 - * If the priority is set to 1, then we will be put first on the 726 - * die notify list to handle a critical NMI. The default is to 727 - * be last so other users of the NMI signal can function. 726 + * Only one function can register for NMI_UNKNOWN 728 727 */ 729 - retval = register_nmi_handler(NMI_UNKNOWN, hpwdt_pretimeout, 730 - (priority) ? NMI_FLAG_FIRST : 0, 731 - "hpwdt"); 732 - if (retval != 0) { 733 - dev_warn(&dev->dev, 734 - "Unable to register a die notifier (err=%d).\n", 735 - retval); 736 - if (cru_rom_addr) 737 - iounmap(cru_rom_addr); 738 - } 728 + retval = register_nmi_handler(NMI_UNKNOWN, hpwdt_pretimeout, 0, "hpwdt"); 729 + if (retval) 730 + goto error; 731 + retval = register_nmi_handler(NMI_SERR, hpwdt_pretimeout, 0, "hpwdt"); 732 + if (retval) 733 + goto error1; 734 + retval = register_nmi_handler(NMI_IO_CHECK, hpwdt_pretimeout, 0, "hpwdt"); 735 + if (retval) 736 + goto error2; 739 737 740 738 dev_info(&dev->dev, 741 739 "HP Watchdog Timer Driver: NMI decoding initialized" 742 - ", allow kernel dump: %s (default = 0/OFF)" 743 - ", priority: %s (default = 0/LAST).\n", 744 - (allow_kdump == 0) ? "OFF" : "ON", 745 - (priority == 0) ? "LAST" : "FIRST"); 740 + ", allow kernel dump: %s (default = 0/OFF)\n", 741 + (allow_kdump == 0) ? "OFF" : "ON"); 746 742 return 0; 743 + 744 + error2: 745 + unregister_nmi_handler(NMI_SERR, "hpwdt"); 746 + error1: 747 + unregister_nmi_handler(NMI_UNKNOWN, "hpwdt"); 748 + error: 749 + dev_warn(&dev->dev, 750 + "Unable to register a die notifier (err=%d).\n", 751 + retval); 752 + if (cru_rom_addr) 753 + iounmap(cru_rom_addr); 754 + return retval; 747 755 } 748 756 749 757 static void hpwdt_exit_nmi_decoding(void) ··· 887 881 #ifdef CONFIG_HPWDT_NMI_DECODING 888 882 module_param(allow_kdump, int, 0); 889 883 MODULE_PARM_DESC(allow_kdump, "Start a kernel dump after NMI occurs"); 890 - 891 - module_param(priority, int, 0); 892 - MODULE_PARM_DESC(priority, "The hpwdt driver handles NMIs first or last" 893 - " (default = 0/Last)\n"); 894 884 #endif /* !CONFIG_HPWDT_NMI_DECODING */ 895 885 896 886 module_init(hpwdt_init);
+3 -1
kernel/hung_task.c
··· 108 108 109 109 touch_nmi_watchdog(); 110 110 111 - if (sysctl_hung_task_panic) 111 + if (sysctl_hung_task_panic) { 112 + trigger_all_cpu_backtrace(); 112 113 panic("hung_task: blocked tasks"); 114 + } 113 115 } 114 116 115 117 /*