diff options
| -rw-r--r-- | arch/x86/kernel/cpu/mcheck/mce-apei.c | 14 | ||||
| -rw-r--r-- | arch/x86/kernel/cpu/mcheck/mce.c | 8 | ||||
| -rw-r--r-- | drivers/acpi/apei/apei-base.c | 4 | ||||
| -rw-r--r-- | drivers/acpi/apei/einj.c | 19 | ||||
| -rw-r--r-- | drivers/acpi/apei/erst.c | 2 | ||||
| -rw-r--r-- | drivers/acpi/apei/ghes.c | 39 |
6 files changed, 47 insertions, 39 deletions
diff --git a/arch/x86/kernel/cpu/mcheck/mce-apei.c b/arch/x86/kernel/cpu/mcheck/mce-apei.c index de8b60a53f69..a1aef9533154 100644 --- a/arch/x86/kernel/cpu/mcheck/mce-apei.c +++ b/arch/x86/kernel/cpu/mcheck/mce-apei.c | |||
| @@ -33,22 +33,28 @@ | |||
| 33 | #include <linux/acpi.h> | 33 | #include <linux/acpi.h> |
| 34 | #include <linux/cper.h> | 34 | #include <linux/cper.h> |
| 35 | #include <acpi/apei.h> | 35 | #include <acpi/apei.h> |
| 36 | #include <acpi/ghes.h> | ||
| 36 | #include <asm/mce.h> | 37 | #include <asm/mce.h> |
| 37 | 38 | ||
| 38 | #include "mce-internal.h" | 39 | #include "mce-internal.h" |
| 39 | 40 | ||
| 40 | void apei_mce_report_mem_error(int corrected, struct cper_sec_mem_err *mem_err) | 41 | void apei_mce_report_mem_error(int severity, struct cper_sec_mem_err *mem_err) |
| 41 | { | 42 | { |
| 42 | struct mce m; | 43 | struct mce m; |
| 43 | 44 | ||
| 44 | /* Only corrected MC is reported */ | 45 | if (!(mem_err->validation_bits & CPER_MEM_VALID_PA)) |
| 45 | if (!corrected || !(mem_err->validation_bits & CPER_MEM_VALID_PA)) | ||
| 46 | return; | 46 | return; |
| 47 | 47 | ||
| 48 | mce_setup(&m); | 48 | mce_setup(&m); |
| 49 | m.bank = 1; | 49 | m.bank = 1; |
| 50 | /* Fake a memory read corrected error with unknown channel */ | 50 | /* Fake a memory read error with unknown channel */ |
| 51 | m.status = MCI_STATUS_VAL | MCI_STATUS_EN | MCI_STATUS_ADDRV | 0x9f; | 51 | m.status = MCI_STATUS_VAL | MCI_STATUS_EN | MCI_STATUS_ADDRV | 0x9f; |
| 52 | |||
| 53 | if (severity >= GHES_SEV_RECOVERABLE) | ||
| 54 | m.status |= MCI_STATUS_UC; | ||
| 55 | if (severity >= GHES_SEV_PANIC) | ||
| 56 | m.status |= MCI_STATUS_PCC; | ||
| 57 | |||
| 52 | m.addr = mem_err->physical_addr; | 58 | m.addr = mem_err->physical_addr; |
| 53 | mce_log(&m); | 59 | mce_log(&m); |
| 54 | mce_notify_irq(); | 60 | mce_notify_irq(); |
diff --git a/arch/x86/kernel/cpu/mcheck/mce.c b/arch/x86/kernel/cpu/mcheck/mce.c index a389c1d859ec..4d5419b249da 100644 --- a/arch/x86/kernel/cpu/mcheck/mce.c +++ b/arch/x86/kernel/cpu/mcheck/mce.c | |||
| @@ -1638,15 +1638,15 @@ static void __mcheck_cpu_init_vendor(struct cpuinfo_x86 *c) | |||
| 1638 | 1638 | ||
| 1639 | static void mce_start_timer(unsigned int cpu, struct timer_list *t) | 1639 | static void mce_start_timer(unsigned int cpu, struct timer_list *t) |
| 1640 | { | 1640 | { |
| 1641 | unsigned long iv = mce_adjust_timer(check_interval * HZ); | 1641 | unsigned long iv = check_interval * HZ; |
| 1642 | |||
| 1643 | __this_cpu_write(mce_next_interval, iv); | ||
| 1644 | 1642 | ||
| 1645 | if (mca_cfg.ignore_ce || !iv) | 1643 | if (mca_cfg.ignore_ce || !iv) |
| 1646 | return; | 1644 | return; |
| 1647 | 1645 | ||
| 1646 | per_cpu(mce_next_interval, cpu) = iv; | ||
| 1647 | |||
| 1648 | t->expires = round_jiffies(jiffies + iv); | 1648 | t->expires = round_jiffies(jiffies + iv); |
| 1649 | add_timer_on(t, smp_processor_id()); | 1649 | add_timer_on(t, cpu); |
| 1650 | } | 1650 | } |
| 1651 | 1651 | ||
| 1652 | static void __mcheck_cpu_init_timer(void) | 1652 | static void __mcheck_cpu_init_timer(void) |
diff --git a/drivers/acpi/apei/apei-base.c b/drivers/acpi/apei/apei-base.c index 6d2c49b86b7f..e55584a072c6 100644 --- a/drivers/acpi/apei/apei-base.c +++ b/drivers/acpi/apei/apei-base.c | |||
| @@ -41,6 +41,7 @@ | |||
| 41 | #include <linux/rculist.h> | 41 | #include <linux/rculist.h> |
| 42 | #include <linux/interrupt.h> | 42 | #include <linux/interrupt.h> |
| 43 | #include <linux/debugfs.h> | 43 | #include <linux/debugfs.h> |
| 44 | #include <asm/unaligned.h> | ||
| 44 | 45 | ||
| 45 | #include "apei-internal.h" | 46 | #include "apei-internal.h" |
| 46 | 47 | ||
| @@ -567,8 +568,7 @@ static int apei_check_gar(struct acpi_generic_address *reg, u64 *paddr, | |||
| 567 | bit_offset = reg->bit_offset; | 568 | bit_offset = reg->bit_offset; |
| 568 | access_size_code = reg->access_width; | 569 | access_size_code = reg->access_width; |
| 569 | space_id = reg->space_id; | 570 | space_id = reg->space_id; |
| 570 | /* Handle possible alignment issues */ | 571 | *paddr = get_unaligned(®->address); |
| 571 | memcpy(paddr, ®->address, sizeof(*paddr)); | ||
| 572 | if (!*paddr) { | 572 | if (!*paddr) { |
| 573 | pr_warning(FW_BUG APEI_PFX | 573 | pr_warning(FW_BUG APEI_PFX |
| 574 | "Invalid physical address in GAR [0x%llx/%u/%u/%u/%u]\n", | 574 | "Invalid physical address in GAR [0x%llx/%u/%u/%u/%u]\n", |
diff --git a/drivers/acpi/apei/einj.c b/drivers/acpi/apei/einj.c index c76674e2a01f..7dcc8a824aae 100644 --- a/drivers/acpi/apei/einj.c +++ b/drivers/acpi/apei/einj.c | |||
| @@ -34,6 +34,7 @@ | |||
| 34 | #include <linux/delay.h> | 34 | #include <linux/delay.h> |
| 35 | #include <linux/mm.h> | 35 | #include <linux/mm.h> |
| 36 | #include <acpi/acpi.h> | 36 | #include <acpi/acpi.h> |
| 37 | #include <asm/unaligned.h> | ||
| 37 | 38 | ||
| 38 | #include "apei-internal.h" | 39 | #include "apei-internal.h" |
| 39 | 40 | ||
| @@ -216,7 +217,7 @@ static void check_vendor_extension(u64 paddr, | |||
| 216 | static void *einj_get_parameter_address(void) | 217 | static void *einj_get_parameter_address(void) |
| 217 | { | 218 | { |
| 218 | int i; | 219 | int i; |
| 219 | u64 paddrv4 = 0, paddrv5 = 0; | 220 | u64 pa_v4 = 0, pa_v5 = 0; |
| 220 | struct acpi_whea_header *entry; | 221 | struct acpi_whea_header *entry; |
| 221 | 222 | ||
| 222 | entry = EINJ_TAB_ENTRY(einj_tab); | 223 | entry = EINJ_TAB_ENTRY(einj_tab); |
| @@ -225,30 +226,28 @@ static void *einj_get_parameter_address(void) | |||
| 225 | entry->instruction == ACPI_EINJ_WRITE_REGISTER && | 226 | entry->instruction == ACPI_EINJ_WRITE_REGISTER && |
| 226 | entry->register_region.space_id == | 227 | entry->register_region.space_id == |
| 227 | ACPI_ADR_SPACE_SYSTEM_MEMORY) | 228 | ACPI_ADR_SPACE_SYSTEM_MEMORY) |
| 228 | memcpy(&paddrv4, &entry->register_region.address, | 229 | pa_v4 = get_unaligned(&entry->register_region.address); |
| 229 | sizeof(paddrv4)); | ||
| 230 | if (entry->action == ACPI_EINJ_SET_ERROR_TYPE_WITH_ADDRESS && | 230 | if (entry->action == ACPI_EINJ_SET_ERROR_TYPE_WITH_ADDRESS && |
| 231 | entry->instruction == ACPI_EINJ_WRITE_REGISTER && | 231 | entry->instruction == ACPI_EINJ_WRITE_REGISTER && |
| 232 | entry->register_region.space_id == | 232 | entry->register_region.space_id == |
| 233 | ACPI_ADR_SPACE_SYSTEM_MEMORY) | 233 | ACPI_ADR_SPACE_SYSTEM_MEMORY) |
| 234 | memcpy(&paddrv5, &entry->register_region.address, | 234 | pa_v5 = get_unaligned(&entry->register_region.address); |
| 235 | sizeof(paddrv5)); | ||
| 236 | entry++; | 235 | entry++; |
| 237 | } | 236 | } |
| 238 | if (paddrv5) { | 237 | if (pa_v5) { |
| 239 | struct set_error_type_with_address *v5param; | 238 | struct set_error_type_with_address *v5param; |
| 240 | 239 | ||
| 241 | v5param = acpi_os_map_memory(paddrv5, sizeof(*v5param)); | 240 | v5param = acpi_os_map_memory(pa_v5, sizeof(*v5param)); |
| 242 | if (v5param) { | 241 | if (v5param) { |
| 243 | acpi5 = 1; | 242 | acpi5 = 1; |
| 244 | check_vendor_extension(paddrv5, v5param); | 243 | check_vendor_extension(pa_v5, v5param); |
| 245 | return v5param; | 244 | return v5param; |
| 246 | } | 245 | } |
| 247 | } | 246 | } |
| 248 | if (param_extension && paddrv4) { | 247 | if (param_extension && pa_v4) { |
| 249 | struct einj_parameter *v4param; | 248 | struct einj_parameter *v4param; |
| 250 | 249 | ||
| 251 | v4param = acpi_os_map_memory(paddrv4, sizeof(*v4param)); | 250 | v4param = acpi_os_map_memory(pa_v4, sizeof(*v4param)); |
| 252 | if (!v4param) | 251 | if (!v4param) |
| 253 | return NULL; | 252 | return NULL; |
| 254 | if (v4param->reserved1 || v4param->reserved2) { | 253 | if (v4param->reserved1 || v4param->reserved2) { |
diff --git a/drivers/acpi/apei/erst.c b/drivers/acpi/apei/erst.c index cb1d557fc22c..ed65e9c4b5b0 100644 --- a/drivers/acpi/apei/erst.c +++ b/drivers/acpi/apei/erst.c | |||
| @@ -611,7 +611,7 @@ static void __erst_record_id_cache_compact(void) | |||
| 611 | if (entries[i] == APEI_ERST_INVALID_RECORD_ID) | 611 | if (entries[i] == APEI_ERST_INVALID_RECORD_ID) |
| 612 | continue; | 612 | continue; |
| 613 | if (wpos != i) | 613 | if (wpos != i) |
| 614 | memcpy(&entries[wpos], &entries[i], sizeof(entries[i])); | 614 | entries[wpos] = entries[i]; |
| 615 | wpos++; | 615 | wpos++; |
| 616 | } | 616 | } |
| 617 | erst_record_id_cache.len = wpos; | 617 | erst_record_id_cache.len = wpos; |
diff --git a/drivers/acpi/apei/ghes.c b/drivers/acpi/apei/ghes.c index a30bc313787b..46766ef7ef5d 100644 --- a/drivers/acpi/apei/ghes.c +++ b/drivers/acpi/apei/ghes.c | |||
| @@ -413,27 +413,31 @@ static void ghes_handle_memory_failure(struct acpi_generic_data *gdata, int sev) | |||
| 413 | { | 413 | { |
| 414 | #ifdef CONFIG_ACPI_APEI_MEMORY_FAILURE | 414 | #ifdef CONFIG_ACPI_APEI_MEMORY_FAILURE |
| 415 | unsigned long pfn; | 415 | unsigned long pfn; |
| 416 | int flags = -1; | ||
| 416 | int sec_sev = ghes_severity(gdata->error_severity); | 417 | int sec_sev = ghes_severity(gdata->error_severity); |
| 417 | struct cper_sec_mem_err *mem_err; | 418 | struct cper_sec_mem_err *mem_err; |
| 418 | mem_err = (struct cper_sec_mem_err *)(gdata + 1); | 419 | mem_err = (struct cper_sec_mem_err *)(gdata + 1); |
| 419 | 420 | ||
| 420 | if (sec_sev == GHES_SEV_CORRECTED && | 421 | if (!(mem_err->validation_bits & CPER_MEM_VALID_PA)) |
| 421 | (gdata->flags & CPER_SEC_ERROR_THRESHOLD_EXCEEDED) && | 422 | return; |
| 422 | (mem_err->validation_bits & CPER_MEM_VALID_PA)) { | 423 | |
| 423 | pfn = mem_err->physical_addr >> PAGE_SHIFT; | 424 | pfn = mem_err->physical_addr >> PAGE_SHIFT; |
| 424 | if (pfn_valid(pfn)) | 425 | if (!pfn_valid(pfn)) { |
| 425 | memory_failure_queue(pfn, 0, MF_SOFT_OFFLINE); | 426 | pr_warn_ratelimited(FW_WARN GHES_PFX |
| 426 | else if (printk_ratelimit()) | 427 | "Invalid address in generic error data: %#llx\n", |
| 427 | pr_warn(FW_WARN GHES_PFX | 428 | mem_err->physical_addr); |
| 428 | "Invalid address in generic error data: %#llx\n", | 429 | return; |
| 429 | mem_err->physical_addr); | ||
| 430 | } | ||
| 431 | if (sev == GHES_SEV_RECOVERABLE && | ||
| 432 | sec_sev == GHES_SEV_RECOVERABLE && | ||
| 433 | mem_err->validation_bits & CPER_MEM_VALID_PA) { | ||
| 434 | pfn = mem_err->physical_addr >> PAGE_SHIFT; | ||
| 435 | memory_failure_queue(pfn, 0, 0); | ||
| 436 | } | 430 | } |
| 431 | |||
| 432 | /* iff following two events can be handled properly by now */ | ||
| 433 | if (sec_sev == GHES_SEV_CORRECTED && | ||
| 434 | (gdata->flags & CPER_SEC_ERROR_THRESHOLD_EXCEEDED)) | ||
| 435 | flags = MF_SOFT_OFFLINE; | ||
| 436 | if (sev == GHES_SEV_RECOVERABLE && sec_sev == GHES_SEV_RECOVERABLE) | ||
| 437 | flags = 0; | ||
| 438 | |||
| 439 | if (flags != -1) | ||
| 440 | memory_failure_queue(pfn, 0, flags); | ||
| 437 | #endif | 441 | #endif |
| 438 | } | 442 | } |
| 439 | 443 | ||
| @@ -453,8 +457,7 @@ static void ghes_do_proc(struct ghes *ghes, | |||
| 453 | ghes_edac_report_mem_error(ghes, sev, mem_err); | 457 | ghes_edac_report_mem_error(ghes, sev, mem_err); |
| 454 | 458 | ||
| 455 | #ifdef CONFIG_X86_MCE | 459 | #ifdef CONFIG_X86_MCE |
| 456 | apei_mce_report_mem_error(sev == GHES_SEV_CORRECTED, | 460 | apei_mce_report_mem_error(sev, mem_err); |
| 457 | mem_err); | ||
| 458 | #endif | 461 | #endif |
| 459 | ghes_handle_memory_failure(gdata, sev); | 462 | ghes_handle_memory_failure(gdata, sev); |
| 460 | } | 463 | } |
