diff options
-rw-r--r-- | arch/x86/kernel/cpu/mcheck/mce-internal.h | 14 | ||||
-rw-r--r-- | arch/x86/kernel/cpu/mcheck/mce.c | 109 |
2 files changed, 67 insertions, 56 deletions
diff --git a/arch/x86/kernel/cpu/mcheck/mce-internal.h b/arch/x86/kernel/cpu/mcheck/mce-internal.h index 54dcb8ff12e5..6bd51e7ba87b 100644 --- a/arch/x86/kernel/cpu/mcheck/mce-internal.h +++ b/arch/x86/kernel/cpu/mcheck/mce-internal.h | |||
@@ -1,3 +1,4 @@ | |||
1 | #include <linux/sysdev.h> | ||
1 | #include <asm/mce.h> | 2 | #include <asm/mce.h> |
2 | 3 | ||
3 | enum severity_level { | 4 | enum severity_level { |
@@ -10,6 +11,19 @@ enum severity_level { | |||
10 | MCE_PANIC_SEVERITY, | 11 | MCE_PANIC_SEVERITY, |
11 | }; | 12 | }; |
12 | 13 | ||
14 | #define ATTR_LEN 16 | ||
15 | |||
16 | /* One object for each MCE bank, shared by all CPUs */ | ||
17 | struct mce_bank { | ||
18 | u64 ctl; /* subevents to enable */ | ||
19 | unsigned char init; /* initialise bank? */ | ||
20 | struct sysdev_attribute attr; /* sysdev attribute */ | ||
21 | char attrname[ATTR_LEN]; /* attribute name */ | ||
22 | }; | ||
23 | |||
13 | int mce_severity(struct mce *a, int tolerant, char **msg); | 24 | int mce_severity(struct mce *a, int tolerant, char **msg); |
14 | 25 | ||
15 | extern int mce_ser; | 26 | extern int mce_ser; |
27 | |||
28 | extern struct mce_bank *mce_banks; | ||
29 | |||
diff --git a/arch/x86/kernel/cpu/mcheck/mce.c b/arch/x86/kernel/cpu/mcheck/mce.c index e16271f01ac4..a04806e01a82 100644 --- a/arch/x86/kernel/cpu/mcheck/mce.c +++ b/arch/x86/kernel/cpu/mcheck/mce.c | |||
@@ -64,7 +64,6 @@ DEFINE_PER_CPU(unsigned, mce_exception_count); | |||
64 | */ | 64 | */ |
65 | static int tolerant __read_mostly = 1; | 65 | static int tolerant __read_mostly = 1; |
66 | static int banks __read_mostly; | 66 | static int banks __read_mostly; |
67 | static u64 *bank __read_mostly; | ||
68 | static int rip_msr __read_mostly; | 67 | static int rip_msr __read_mostly; |
69 | static int mce_bootlog __read_mostly = -1; | 68 | static int mce_bootlog __read_mostly = -1; |
70 | static int monarch_timeout __read_mostly = -1; | 69 | static int monarch_timeout __read_mostly = -1; |
@@ -74,13 +73,13 @@ int mce_cmci_disabled __read_mostly; | |||
74 | int mce_ignore_ce __read_mostly; | 73 | int mce_ignore_ce __read_mostly; |
75 | int mce_ser __read_mostly; | 74 | int mce_ser __read_mostly; |
76 | 75 | ||
76 | struct mce_bank *mce_banks __read_mostly; | ||
77 | |||
77 | /* User mode helper program triggered by machine check event */ | 78 | /* User mode helper program triggered by machine check event */ |
78 | static unsigned long mce_need_notify; | 79 | static unsigned long mce_need_notify; |
79 | static char mce_helper[128]; | 80 | static char mce_helper[128]; |
80 | static char *mce_helper_argv[2] = { mce_helper, NULL }; | 81 | static char *mce_helper_argv[2] = { mce_helper, NULL }; |
81 | 82 | ||
82 | static unsigned long dont_init_banks; | ||
83 | |||
84 | static DECLARE_WAIT_QUEUE_HEAD(mce_wait); | 83 | static DECLARE_WAIT_QUEUE_HEAD(mce_wait); |
85 | static DEFINE_PER_CPU(struct mce, mces_seen); | 84 | static DEFINE_PER_CPU(struct mce, mces_seen); |
86 | static int cpu_missing; | 85 | static int cpu_missing; |
@@ -91,11 +90,6 @@ DEFINE_PER_CPU(mce_banks_t, mce_poll_banks) = { | |||
91 | [0 ... BITS_TO_LONGS(MAX_NR_BANKS)-1] = ~0UL | 90 | [0 ... BITS_TO_LONGS(MAX_NR_BANKS)-1] = ~0UL |
92 | }; | 91 | }; |
93 | 92 | ||
94 | static inline int skip_bank_init(int i) | ||
95 | { | ||
96 | return i < BITS_PER_LONG && test_bit(i, &dont_init_banks); | ||
97 | } | ||
98 | |||
99 | static DEFINE_PER_CPU(struct work_struct, mce_work); | 93 | static DEFINE_PER_CPU(struct work_struct, mce_work); |
100 | 94 | ||
101 | /* Do initial initialization of a struct mce */ | 95 | /* Do initial initialization of a struct mce */ |
@@ -482,7 +476,7 @@ void machine_check_poll(enum mcp_flags flags, mce_banks_t *b) | |||
482 | 476 | ||
483 | m.mcgstatus = mce_rdmsrl(MSR_IA32_MCG_STATUS); | 477 | m.mcgstatus = mce_rdmsrl(MSR_IA32_MCG_STATUS); |
484 | for (i = 0; i < banks; i++) { | 478 | for (i = 0; i < banks; i++) { |
485 | if (!bank[i] || !test_bit(i, *b)) | 479 | if (!mce_banks[i].ctl || !test_bit(i, *b)) |
486 | continue; | 480 | continue; |
487 | 481 | ||
488 | m.misc = 0; | 482 | m.misc = 0; |
@@ -903,7 +897,7 @@ void do_machine_check(struct pt_regs *regs, long error_code) | |||
903 | order = mce_start(&no_way_out); | 897 | order = mce_start(&no_way_out); |
904 | for (i = 0; i < banks; i++) { | 898 | for (i = 0; i < banks; i++) { |
905 | __clear_bit(i, toclear); | 899 | __clear_bit(i, toclear); |
906 | if (!bank[i]) | 900 | if (!mce_banks[i].ctl) |
907 | continue; | 901 | continue; |
908 | 902 | ||
909 | m.misc = 0; | 903 | m.misc = 0; |
@@ -1146,6 +1140,21 @@ int mce_notify_irq(void) | |||
1146 | } | 1140 | } |
1147 | EXPORT_SYMBOL_GPL(mce_notify_irq); | 1141 | EXPORT_SYMBOL_GPL(mce_notify_irq); |
1148 | 1142 | ||
1143 | static int mce_banks_init(void) | ||
1144 | { | ||
1145 | int i; | ||
1146 | |||
1147 | mce_banks = kzalloc(banks * sizeof(struct mce_bank), GFP_KERNEL); | ||
1148 | if (!mce_banks) | ||
1149 | return -ENOMEM; | ||
1150 | for (i = 0; i < banks; i++) { | ||
1151 | struct mce_bank *b = &mce_banks[i]; | ||
1152 | b->ctl = -1ULL; | ||
1153 | b->init = 1; | ||
1154 | } | ||
1155 | return 0; | ||
1156 | } | ||
1157 | |||
1149 | /* | 1158 | /* |
1150 | * Initialize Machine Checks for a CPU. | 1159 | * Initialize Machine Checks for a CPU. |
1151 | */ | 1160 | */ |
@@ -1169,11 +1178,10 @@ static int mce_cap_init(void) | |||
1169 | /* Don't support asymmetric configurations today */ | 1178 | /* Don't support asymmetric configurations today */ |
1170 | WARN_ON(banks != 0 && b != banks); | 1179 | WARN_ON(banks != 0 && b != banks); |
1171 | banks = b; | 1180 | banks = b; |
1172 | if (!bank) { | 1181 | if (!mce_banks) { |
1173 | bank = kmalloc(banks * sizeof(u64), GFP_KERNEL); | 1182 | int err = mce_banks_init(); |
1174 | if (!bank) | 1183 | if (err) |
1175 | return -ENOMEM; | 1184 | return err; |
1176 | memset(bank, 0xff, banks * sizeof(u64)); | ||
1177 | } | 1185 | } |
1178 | 1186 | ||
1179 | /* Use accurate RIP reporting if available. */ | 1187 | /* Use accurate RIP reporting if available. */ |
@@ -1205,9 +1213,10 @@ static void mce_init(void) | |||
1205 | wrmsr(MSR_IA32_MCG_CTL, 0xffffffff, 0xffffffff); | 1213 | wrmsr(MSR_IA32_MCG_CTL, 0xffffffff, 0xffffffff); |
1206 | 1214 | ||
1207 | for (i = 0; i < banks; i++) { | 1215 | for (i = 0; i < banks; i++) { |
1208 | if (skip_bank_init(i)) | 1216 | struct mce_bank *b = &mce_banks[i]; |
1217 | if (!b->init) | ||
1209 | continue; | 1218 | continue; |
1210 | wrmsrl(MSR_IA32_MC0_CTL+4*i, bank[i]); | 1219 | wrmsrl(MSR_IA32_MC0_CTL+4*i, b->ctl); |
1211 | wrmsrl(MSR_IA32_MC0_STATUS+4*i, 0); | 1220 | wrmsrl(MSR_IA32_MC0_STATUS+4*i, 0); |
1212 | } | 1221 | } |
1213 | } | 1222 | } |
@@ -1223,7 +1232,7 @@ static void mce_cpu_quirks(struct cpuinfo_x86 *c) | |||
1223 | * trips off incorrectly with the IOMMU & 3ware | 1232 | * trips off incorrectly with the IOMMU & 3ware |
1224 | * & Cerberus: | 1233 | * & Cerberus: |
1225 | */ | 1234 | */ |
1226 | clear_bit(10, (unsigned long *)&bank[4]); | 1235 | clear_bit(10, (unsigned long *)&mce_banks[4].ctl); |
1227 | } | 1236 | } |
1228 | if (c->x86 <= 17 && mce_bootlog < 0) { | 1237 | if (c->x86 <= 17 && mce_bootlog < 0) { |
1229 | /* | 1238 | /* |
@@ -1237,7 +1246,7 @@ static void mce_cpu_quirks(struct cpuinfo_x86 *c) | |||
1237 | * by default. | 1246 | * by default. |
1238 | */ | 1247 | */ |
1239 | if (c->x86 == 6 && banks > 0) | 1248 | if (c->x86 == 6 && banks > 0) |
1240 | bank[0] = 0; | 1249 | mce_banks[0].ctl = 0; |
1241 | } | 1250 | } |
1242 | 1251 | ||
1243 | if (c->x86_vendor == X86_VENDOR_INTEL) { | 1252 | if (c->x86_vendor == X86_VENDOR_INTEL) { |
@@ -1250,8 +1259,8 @@ static void mce_cpu_quirks(struct cpuinfo_x86 *c) | |||
1250 | * valid event later, merely don't write CTL0. | 1259 | * valid event later, merely don't write CTL0. |
1251 | */ | 1260 | */ |
1252 | 1261 | ||
1253 | if (c->x86 == 6 && c->x86_model < 0x1A) | 1262 | if (c->x86 == 6 && c->x86_model < 0x1A && banks > 0) |
1254 | __set_bit(0, &dont_init_banks); | 1263 | mce_banks[0].init = 0; |
1255 | 1264 | ||
1256 | /* | 1265 | /* |
1257 | * All newer Intel systems support MCE broadcasting. Enable | 1266 | * All newer Intel systems support MCE broadcasting. Enable |
@@ -1578,7 +1587,8 @@ static int mce_disable(void) | |||
1578 | int i; | 1587 | int i; |
1579 | 1588 | ||
1580 | for (i = 0; i < banks; i++) { | 1589 | for (i = 0; i < banks; i++) { |
1581 | if (!skip_bank_init(i)) | 1590 | struct mce_bank *b = &mce_banks[i]; |
1591 | if (b->init) | ||
1582 | wrmsrl(MSR_IA32_MC0_CTL + i*4, 0); | 1592 | wrmsrl(MSR_IA32_MC0_CTL + i*4, 0); |
1583 | } | 1593 | } |
1584 | return 0; | 1594 | return 0; |
@@ -1654,14 +1664,15 @@ DEFINE_PER_CPU(struct sys_device, mce_dev); | |||
1654 | __cpuinitdata | 1664 | __cpuinitdata |
1655 | void (*threshold_cpu_callback)(unsigned long action, unsigned int cpu); | 1665 | void (*threshold_cpu_callback)(unsigned long action, unsigned int cpu); |
1656 | 1666 | ||
1657 | static struct sysdev_attribute *bank_attrs; | 1667 | static inline struct mce_bank *attr_to_bank(struct sysdev_attribute *attr) |
1668 | { | ||
1669 | return container_of(attr, struct mce_bank, attr); | ||
1670 | } | ||
1658 | 1671 | ||
1659 | static ssize_t show_bank(struct sys_device *s, struct sysdev_attribute *attr, | 1672 | static ssize_t show_bank(struct sys_device *s, struct sysdev_attribute *attr, |
1660 | char *buf) | 1673 | char *buf) |
1661 | { | 1674 | { |
1662 | u64 b = bank[attr - bank_attrs]; | 1675 | return sprintf(buf, "%llx\n", attr_to_bank(attr)->ctl); |
1663 | |||
1664 | return sprintf(buf, "%llx\n", b); | ||
1665 | } | 1676 | } |
1666 | 1677 | ||
1667 | static ssize_t set_bank(struct sys_device *s, struct sysdev_attribute *attr, | 1678 | static ssize_t set_bank(struct sys_device *s, struct sysdev_attribute *attr, |
@@ -1672,7 +1683,7 @@ static ssize_t set_bank(struct sys_device *s, struct sysdev_attribute *attr, | |||
1672 | if (strict_strtoull(buf, 0, &new) < 0) | 1683 | if (strict_strtoull(buf, 0, &new) < 0) |
1673 | return -EINVAL; | 1684 | return -EINVAL; |
1674 | 1685 | ||
1675 | bank[attr - bank_attrs] = new; | 1686 | attr_to_bank(attr)->ctl = new; |
1676 | mce_restart(); | 1687 | mce_restart(); |
1677 | 1688 | ||
1678 | return size; | 1689 | return size; |
@@ -1816,7 +1827,7 @@ static __cpuinit int mce_create_device(unsigned int cpu) | |||
1816 | } | 1827 | } |
1817 | for (j = 0; j < banks; j++) { | 1828 | for (j = 0; j < banks; j++) { |
1818 | err = sysdev_create_file(&per_cpu(mce_dev, cpu), | 1829 | err = sysdev_create_file(&per_cpu(mce_dev, cpu), |
1819 | &bank_attrs[j]); | 1830 | &mce_banks[j].attr); |
1820 | if (err) | 1831 | if (err) |
1821 | goto error2; | 1832 | goto error2; |
1822 | } | 1833 | } |
@@ -1825,10 +1836,10 @@ static __cpuinit int mce_create_device(unsigned int cpu) | |||
1825 | return 0; | 1836 | return 0; |
1826 | error2: | 1837 | error2: |
1827 | while (--j >= 0) | 1838 | while (--j >= 0) |
1828 | sysdev_remove_file(&per_cpu(mce_dev, cpu), &bank_attrs[j]); | 1839 | sysdev_remove_file(&per_cpu(mce_dev, cpu), &mce_banks[j].attr); |
1829 | error: | 1840 | error: |
1830 | while (--i >= 0) | 1841 | while (--i >= 0) |
1831 | sysdev_remove_file(&per_cpu(mce_dev, cpu), mce_attrs[i]); | 1842 | sysdev_remove_file(&per_cpu(mce_dev, cpu), &mce_banks[i].attr); |
1832 | 1843 | ||
1833 | sysdev_unregister(&per_cpu(mce_dev, cpu)); | 1844 | sysdev_unregister(&per_cpu(mce_dev, cpu)); |
1834 | 1845 | ||
@@ -1846,7 +1857,7 @@ static __cpuinit void mce_remove_device(unsigned int cpu) | |||
1846 | sysdev_remove_file(&per_cpu(mce_dev, cpu), mce_attrs[i]); | 1857 | sysdev_remove_file(&per_cpu(mce_dev, cpu), mce_attrs[i]); |
1847 | 1858 | ||
1848 | for (i = 0; i < banks; i++) | 1859 | for (i = 0; i < banks; i++) |
1849 | sysdev_remove_file(&per_cpu(mce_dev, cpu), &bank_attrs[i]); | 1860 | sysdev_remove_file(&per_cpu(mce_dev, cpu), &mce_banks[i].attr); |
1850 | 1861 | ||
1851 | sysdev_unregister(&per_cpu(mce_dev, cpu)); | 1862 | sysdev_unregister(&per_cpu(mce_dev, cpu)); |
1852 | cpumask_clear_cpu(cpu, mce_dev_initialized); | 1863 | cpumask_clear_cpu(cpu, mce_dev_initialized); |
@@ -1863,7 +1874,8 @@ static void mce_disable_cpu(void *h) | |||
1863 | if (!(action & CPU_TASKS_FROZEN)) | 1874 | if (!(action & CPU_TASKS_FROZEN)) |
1864 | cmci_clear(); | 1875 | cmci_clear(); |
1865 | for (i = 0; i < banks; i++) { | 1876 | for (i = 0; i < banks; i++) { |
1866 | if (!skip_bank_init(i)) | 1877 | struct mce_bank *b = &mce_banks[i]; |
1878 | if (b->init) | ||
1867 | wrmsrl(MSR_IA32_MC0_CTL + i*4, 0); | 1879 | wrmsrl(MSR_IA32_MC0_CTL + i*4, 0); |
1868 | } | 1880 | } |
1869 | } | 1881 | } |
@@ -1879,8 +1891,9 @@ static void mce_reenable_cpu(void *h) | |||
1879 | if (!(action & CPU_TASKS_FROZEN)) | 1891 | if (!(action & CPU_TASKS_FROZEN)) |
1880 | cmci_reenable(); | 1892 | cmci_reenable(); |
1881 | for (i = 0; i < banks; i++) { | 1893 | for (i = 0; i < banks; i++) { |
1882 | if (!skip_bank_init(i)) | 1894 | struct mce_bank *b = &mce_banks[i]; |
1883 | wrmsrl(MSR_IA32_MC0_CTL + i*4, bank[i]); | 1895 | if (b->init) |
1896 | wrmsrl(MSR_IA32_MC0_CTL + i*4, b->ctl); | ||
1884 | } | 1897 | } |
1885 | } | 1898 | } |
1886 | 1899 | ||
@@ -1928,35 +1941,21 @@ static struct notifier_block mce_cpu_notifier __cpuinitdata = { | |||
1928 | .notifier_call = mce_cpu_callback, | 1941 | .notifier_call = mce_cpu_callback, |
1929 | }; | 1942 | }; |
1930 | 1943 | ||
1931 | static __init int mce_init_banks(void) | 1944 | static __init void mce_init_banks(void) |
1932 | { | 1945 | { |
1933 | int i; | 1946 | int i; |
1934 | 1947 | ||
1935 | bank_attrs = kzalloc(sizeof(struct sysdev_attribute) * banks, | ||
1936 | GFP_KERNEL); | ||
1937 | if (!bank_attrs) | ||
1938 | return -ENOMEM; | ||
1939 | |||
1940 | for (i = 0; i < banks; i++) { | 1948 | for (i = 0; i < banks; i++) { |
1941 | struct sysdev_attribute *a = &bank_attrs[i]; | 1949 | struct mce_bank *b = &mce_banks[i]; |
1950 | struct sysdev_attribute *a = &b->attr; | ||
1942 | 1951 | ||
1943 | a->attr.name = kasprintf(GFP_KERNEL, "bank%d", i); | 1952 | a->attr.name = b->attrname; |
1944 | if (!a->attr.name) | 1953 | snprintf(b->attrname, ATTR_LEN, "bank%d", i); |
1945 | goto nomem; | ||
1946 | 1954 | ||
1947 | a->attr.mode = 0644; | 1955 | a->attr.mode = 0644; |
1948 | a->show = show_bank; | 1956 | a->show = show_bank; |
1949 | a->store = set_bank; | 1957 | a->store = set_bank; |
1950 | } | 1958 | } |
1951 | return 0; | ||
1952 | |||
1953 | nomem: | ||
1954 | while (--i >= 0) | ||
1955 | kfree(bank_attrs[i].attr.name); | ||
1956 | kfree(bank_attrs); | ||
1957 | bank_attrs = NULL; | ||
1958 | |||
1959 | return -ENOMEM; | ||
1960 | } | 1959 | } |
1961 | 1960 | ||
1962 | static __init int mce_init_device(void) | 1961 | static __init int mce_init_device(void) |
@@ -1969,9 +1968,7 @@ static __init int mce_init_device(void) | |||
1969 | 1968 | ||
1970 | zalloc_cpumask_var(&mce_dev_initialized, GFP_KERNEL); | 1969 | zalloc_cpumask_var(&mce_dev_initialized, GFP_KERNEL); |
1971 | 1970 | ||
1972 | err = mce_init_banks(); | 1971 | mce_init_banks(); |
1973 | if (err) | ||
1974 | return err; | ||
1975 | 1972 | ||
1976 | err = sysdev_class_register(&mce_sysclass); | 1973 | err = sysdev_class_register(&mce_sysclass); |
1977 | if (err) | 1974 | if (err) |