diff options
Diffstat (limited to 'drivers/acpi/apei/ghes.c')
| -rw-r--r-- | drivers/acpi/apei/ghes.c | 63 | 
1 files changed, 63 insertions, 0 deletions
diff --git a/drivers/acpi/apei/ghes.c b/drivers/acpi/apei/ghes.c index 81bf71b10d44..99df00f64306 100644 --- a/drivers/acpi/apei/ghes.c +++ b/drivers/acpi/apei/ghes.c @@ -79,6 +79,12 @@  	((struct acpi_hest_generic_status *)				\  	 ((struct ghes_estatus_node *)(estatus_node) + 1)) +#define GHES_VENDOR_ENTRY_LEN(gdata_len)                               \ +	(sizeof(struct ghes_vendor_record_entry) + (gdata_len)) +#define GHES_GDATA_FROM_VENDOR_ENTRY(vendor_entry)                     \ +	((struct acpi_hest_generic_data *)                              \ +	((struct ghes_vendor_record_entry *)(vendor_entry) + 1)) +  /*   *  NMI-like notifications vary by architecture, before the compiler can prune   *  unused static functions it needs a value for these enums. @@ -123,6 +129,12 @@ static DEFINE_MUTEX(ghes_list_mutex);   */  static DEFINE_SPINLOCK(ghes_notify_lock_irq); +struct ghes_vendor_record_entry { +	struct work_struct work; +	int error_severity; +	char vendor_record[]; +}; +  static struct gen_pool *ghes_estatus_pool;  static unsigned long ghes_estatus_pool_size_request; @@ -511,6 +523,56 @@ static void ghes_handle_aer(struct acpi_hest_generic_data *gdata)  #endif  } +static BLOCKING_NOTIFIER_HEAD(vendor_record_notify_list); + +int ghes_register_vendor_record_notifier(struct notifier_block *nb) +{ +	return blocking_notifier_chain_register(&vendor_record_notify_list, nb); +} +EXPORT_SYMBOL_GPL(ghes_register_vendor_record_notifier); + +void ghes_unregister_vendor_record_notifier(struct notifier_block *nb) +{ +	blocking_notifier_chain_unregister(&vendor_record_notify_list, nb); +} +EXPORT_SYMBOL_GPL(ghes_unregister_vendor_record_notifier); + +static void ghes_vendor_record_work_func(struct work_struct *work) +{ +	struct ghes_vendor_record_entry *entry; +	struct acpi_hest_generic_data *gdata; +	u32 len; + +	entry = container_of(work, struct ghes_vendor_record_entry, work); +	gdata = GHES_GDATA_FROM_VENDOR_ENTRY(entry); + +	blocking_notifier_call_chain(&vendor_record_notify_list, +				     entry->error_severity, gdata); + +	len = GHES_VENDOR_ENTRY_LEN(acpi_hest_get_record_size(gdata)); +	gen_pool_free(ghes_estatus_pool, (unsigned long)entry, len); +} + +static void ghes_defer_non_standard_event(struct acpi_hest_generic_data *gdata, +					  int sev) +{ +	struct acpi_hest_generic_data *copied_gdata; +	struct ghes_vendor_record_entry *entry; +	u32 len; + +	len = GHES_VENDOR_ENTRY_LEN(acpi_hest_get_record_size(gdata)); +	entry = (void *)gen_pool_alloc(ghes_estatus_pool, len); +	if (!entry) +		return; + +	copied_gdata = GHES_GDATA_FROM_VENDOR_ENTRY(entry); +	memcpy(copied_gdata, gdata, acpi_hest_get_record_size(gdata)); +	entry->error_severity = sev; + +	INIT_WORK(&entry->work, ghes_vendor_record_work_func); +	schedule_work(&entry->work); +} +  static bool ghes_do_proc(struct ghes *ghes,  			 const struct acpi_hest_generic_status *estatus)  { @@ -549,6 +611,7 @@ static bool ghes_do_proc(struct ghes *ghes,  		} else {  			void *err = acpi_hest_get_payload(gdata); +			ghes_defer_non_standard_event(gdata, sev);  			log_non_standard_event(sec_type, fru_id, fru_text,  					       sec_sev, err,  					       gdata->error_data_length);  | 
