📄 mce.c
字号:
set_in_cr4(X86_CR4_MCE); if (cap & MCG_CTL_P) wrmsr(MSR_IA32_MCG_CTL, 0xffffffff, 0xffffffff); for (i = 0; i < banks; i++) { wrmsrl(MSR_IA32_MC0_CTL+4*i, bank[i]); wrmsrl(MSR_IA32_MC0_STATUS+4*i, 0); } }/* Add per CPU specific workarounds here */static void __cpuinit mce_cpu_quirks(struct cpuinfo_x86 *c){ /* This should be disabled by the BIOS, but isn't always */ if (c->x86_vendor == X86_VENDOR_AMD && c->x86 == 15) { /* disable GART TBL walk error reporting, which trips off incorrectly with the IOMMU & 3ware & Cerberus. */ clear_bit(10, &bank[4]); /* Lots of broken BIOS around that don't clear them by default and leave crap in there. Don't log. */ mce_bootlog = 0; }} static void __cpuinit mce_cpu_features(struct cpuinfo_x86 *c){ switch (c->x86_vendor) { case X86_VENDOR_INTEL: mce_intel_feature_init(c); break; case X86_VENDOR_AMD: mce_amd_feature_init(c); break; default: break; }}/* * Called for each booted CPU to set up machine checks. * Must be called with preempt off. */void __cpuinit mcheck_init(struct cpuinfo_x86 *c){ static cpumask_t mce_cpus __initdata = CPU_MASK_NONE; mce_cpu_quirks(c); if (mce_dont_init || cpu_test_and_set(smp_processor_id(), mce_cpus) || !mce_available(c)) return; mce_init(NULL); mce_cpu_features(c);}/* * Character device to read and clear the MCE log. */static void collect_tscs(void *data) { unsigned long *cpu_tsc = (unsigned long *)data; rdtscll(cpu_tsc[smp_processor_id()]);} static ssize_t mce_read(struct file *filp, char __user *ubuf, size_t usize, loff_t *off){ unsigned long *cpu_tsc; static DECLARE_MUTEX(mce_read_sem); unsigned next; char __user *buf = ubuf; int i, err; cpu_tsc = kmalloc(NR_CPUS * sizeof(long), GFP_KERNEL); if (!cpu_tsc) return -ENOMEM; down(&mce_read_sem); next = rcu_dereference(mcelog.next); /* Only supports full reads right now */ if (*off != 0 || usize < MCE_LOG_LEN*sizeof(struct mce)) { up(&mce_read_sem); kfree(cpu_tsc); return -EINVAL; } err = 0; for (i = 0; i < next; i++) { unsigned long start = jiffies; while (!mcelog.entry[i].finished) { if (!time_before(jiffies, start + 2)) { memset(mcelog.entry + i,0, sizeof(struct mce)); continue; } cpu_relax(); } smp_rmb(); err |= copy_to_user(buf, mcelog.entry + i, sizeof(struct mce)); buf += sizeof(struct mce); } memset(mcelog.entry, 0, next * sizeof(struct mce)); mcelog.next = 0; synchronize_sched(); /* Collect entries that were still getting written before the synchronize. */ on_each_cpu(collect_tscs, cpu_tsc, 1, 1); for (i = next; i < MCE_LOG_LEN; i++) { if (mcelog.entry[i].finished && mcelog.entry[i].tsc < cpu_tsc[mcelog.entry[i].cpu]) { err |= copy_to_user(buf, mcelog.entry+i, sizeof(struct mce)); smp_rmb(); buf += sizeof(struct mce); memset(&mcelog.entry[i], 0, sizeof(struct mce)); } } up(&mce_read_sem); kfree(cpu_tsc); return err ? -EFAULT : buf - ubuf; }static int mce_ioctl(struct inode *i, struct file *f,unsigned int cmd, unsigned long arg){ int __user *p = (int __user *)arg; if (!capable(CAP_SYS_ADMIN)) return -EPERM; switch (cmd) { case MCE_GET_RECORD_LEN: return put_user(sizeof(struct mce), p); case MCE_GET_LOG_LEN: return put_user(MCE_LOG_LEN, p); case MCE_GETCLEAR_FLAGS: { unsigned flags; do { flags = mcelog.flags; } while (cmpxchg(&mcelog.flags, flags, 0) != flags); return put_user(flags, p); } default: return -ENOTTY; } }static struct file_operations mce_chrdev_ops = { .read = mce_read, .ioctl = mce_ioctl,};static struct miscdevice mce_log_device = { MISC_MCELOG_MINOR, "mcelog", &mce_chrdev_ops,};/* * Old style boot options parsing. Only for compatibility. */static int __init mcheck_disable(char *str){ mce_dont_init = 1; return 0;}/* mce=off disables machine check. Note you can reenable it later using sysfs. mce=TOLERANCELEVEL (number, see above) mce=bootlog Log MCEs from before booting. Disabled by default on AMD. mce=nobootlog Don't log MCEs from before booting. */static int __init mcheck_enable(char *str){ if (*str == '=') str++; if (!strcmp(str, "off")) mce_dont_init = 1; else if (!strcmp(str, "bootlog") || !strcmp(str,"nobootlog")) mce_bootlog = str[0] == 'b'; else if (isdigit(str[0])) get_option(&str, &tolerant); else printk("mce= argument %s ignored. Please use /sys", str); return 0;}__setup("nomce", mcheck_disable);__setup("mce", mcheck_enable);/* * Sysfs support */ /* On resume clear all MCE state. Don't want to see leftovers from the BIOS. Only one CPU is active at this time, the others get readded later using CPU hotplug. */static int mce_resume(struct sys_device *dev){ mce_init(NULL); return 0;}/* Reinit MCEs after user configuration changes */static void mce_restart(void) { if (check_interval) cancel_delayed_work(&mcheck_work); /* Timer race is harmless here */ on_each_cpu(mce_init, NULL, 1, 1); if (check_interval) schedule_delayed_work(&mcheck_work, check_interval*HZ);}static struct sysdev_class mce_sysclass = { .resume = mce_resume, set_kset_name("machinecheck"),};static DEFINE_PER_CPU(struct sys_device, device_mce);/* Why are there no generic functions for this? */#define ACCESSOR(name, var, start) \ static ssize_t show_ ## name(struct sys_device *s, char *buf) { \ return sprintf(buf, "%lx\n", (unsigned long)var); \ } \ static ssize_t set_ ## name(struct sys_device *s,const char *buf,size_t siz) { \ char *end; \ unsigned long new = simple_strtoul(buf, &end, 0); \ if (end == buf) return -EINVAL; \ var = new; \ start; \ return end-buf; \ } \ static SYSDEV_ATTR(name, 0644, show_ ## name, set_ ## name);ACCESSOR(bank0ctl,bank[0],mce_restart())ACCESSOR(bank1ctl,bank[1],mce_restart())ACCESSOR(bank2ctl,bank[2],mce_restart())ACCESSOR(bank3ctl,bank[3],mce_restart())ACCESSOR(bank4ctl,bank[4],mce_restart())ACCESSOR(tolerant,tolerant,)ACCESSOR(check_interval,check_interval,mce_restart())/* Per cpu sysdev init. All of the cpus still share the same ctl bank */static __cpuinit int mce_create_device(unsigned int cpu){ int err; if (!mce_available(&cpu_data[cpu])) return -EIO; per_cpu(device_mce,cpu).id = cpu; per_cpu(device_mce,cpu).cls = &mce_sysclass; err = sysdev_register(&per_cpu(device_mce,cpu)); if (!err) { sysdev_create_file(&per_cpu(device_mce,cpu), &attr_bank0ctl); sysdev_create_file(&per_cpu(device_mce,cpu), &attr_bank1ctl); sysdev_create_file(&per_cpu(device_mce,cpu), &attr_bank2ctl); sysdev_create_file(&per_cpu(device_mce,cpu), &attr_bank3ctl); sysdev_create_file(&per_cpu(device_mce,cpu), &attr_bank4ctl); sysdev_create_file(&per_cpu(device_mce,cpu), &attr_tolerant); sysdev_create_file(&per_cpu(device_mce,cpu), &attr_check_interval); } return err;}#ifdef CONFIG_HOTPLUG_CPUstatic __cpuinit void mce_remove_device(unsigned int cpu){ sysdev_remove_file(&per_cpu(device_mce,cpu), &attr_bank0ctl); sysdev_remove_file(&per_cpu(device_mce,cpu), &attr_bank1ctl); sysdev_remove_file(&per_cpu(device_mce,cpu), &attr_bank2ctl); sysdev_remove_file(&per_cpu(device_mce,cpu), &attr_bank3ctl); sysdev_remove_file(&per_cpu(device_mce,cpu), &attr_bank4ctl); sysdev_remove_file(&per_cpu(device_mce,cpu), &attr_tolerant); sysdev_remove_file(&per_cpu(device_mce,cpu), &attr_check_interval); sysdev_unregister(&per_cpu(device_mce,cpu));}#endif/* Get notified when a cpu comes on/off. Be hotplug friendly. */static __cpuinit intmce_cpu_callback(struct notifier_block *nfb, unsigned long action, void *hcpu){ unsigned int cpu = (unsigned long)hcpu; switch (action) { case CPU_ONLINE: mce_create_device(cpu); break;#ifdef CONFIG_HOTPLUG_CPU case CPU_DEAD: mce_remove_device(cpu); break;#endif } return NOTIFY_OK;}static struct notifier_block mce_cpu_notifier = { .notifier_call = mce_cpu_callback,};static __init int mce_init_device(void){ int err; int i = 0; if (!mce_available(&boot_cpu_data)) return -EIO; err = sysdev_class_register(&mce_sysclass); for_each_online_cpu(i) { mce_create_device(i); } register_cpu_notifier(&mce_cpu_notifier); misc_register(&mce_log_device); return err;}device_initcall(mce_init_device);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -