--- drivers/acpi/apei/ghes.c | 34 ++++++++++++++++++++++++++++++---- 1 file changed, 30 insertions(+), 4 deletions(-) --- a/drivers/acpi/apei/ghes.c +++ b/drivers/acpi/apei/ghes.c @@ -72,6 +72,8 @@ /* Prevent too many caches are allocated because of RCU */ #define GHES_ESTATUS_CACHE_ALLOCED_MAX (GHES_ESTATUS_CACHES_SIZE * 3 / 2) +#define GHES_FW_ERROR_COUNT_MAX 10 + #define GHES_ESTATUS_CACHE_LEN(estatus_len) \ (sizeof(struct ghes_estatus_cache) + (estatus_len)) #define GHES_ESTATUS_FROM_CACHE(estatus_cache) \ @@ -98,6 +100,8 @@ struct ghes { u64 buffer_paddr; bool exiting; bool to_clear; + bool disabled; + unsigned int fw_error_count; union { struct list_head list; struct timer_list timer; @@ -331,6 +335,22 @@ static void ghes_fini(struct ghes *ghes) acpi_os_unmap_generic_address(&ghes->generic->error_status_address); } +/* + * If there is firmware error, so that we failed to check/get the + * hardware error status, we need to report the firmware error. But + * too many firmware error reporting may flush the kernel log. So + * disable the hardware error checking/getting if there is too many + * firmware error. + */ +static void ghes_fw_error_count_inc(struct ghes *ghes) +{ + if (ghes->disabled) + return; + ghes->fw_error_count++; + if (ghes->fw_error_count >= GHES_FW_ERROR_COUNT_MAX) + ghes->disabled = true; +} + enum { GHES_SEV_NO = 0x0, GHES_SEV_CORRECTED = 0x1, @@ -399,8 +419,11 @@ static int ghes_read_estatus(struct ghes u32 len; int rc; + if (ghes->disabled) + return -ENOENT; rc = apei_read(&buf_paddr, &g->error_status_address); if (rc) { + ghes_fw_error_count_inc(ghes); if (!silent && printk_ratelimit()) pr_warning(FW_WARN GHES_PFX "Failed to read error status block address for hardware error source: %d.\n", @@ -434,9 +457,12 @@ static int ghes_read_estatus(struct ghes rc = 0; err_read_block: - if (rc && !silent && printk_ratelimit()) - pr_warning(FW_WARN GHES_PFX - "Failed to read error status block!\n"); + if (rc) { + ghes_fw_error_count_inc(ghes); + if (!silent && printk_ratelimit()) + pr_warning(FW_WARN GHES_PFX + "Failed to read error status block!\n"); + } return rc; } @@ -706,7 +732,7 @@ static void ghes_poll_func(unsigned long struct ghes *ghes = (void *)data; ghes_proc(ghes); - if (!ghes->exiting) + if (!ghes->exiting && !ghes->disabled) ghes_add_timer(ghes); }