diff options
Diffstat (limited to 'drivers/acpi/apei/erst.c')
| -rw-r--r-- | drivers/acpi/apei/erst.c | 533 | 
1 files changed, 444 insertions, 89 deletions
diff --git a/drivers/acpi/apei/erst.c b/drivers/acpi/apei/erst.c index 1211c03149e..ed65e9c4b5b 100644 --- a/drivers/acpi/apei/erst.c +++ b/drivers/acpi/apei/erst.c @@ -34,11 +34,13 @@  #include <linux/cper.h>  #include <linux/nmi.h>  #include <linux/hardirq.h> +#include <linux/pstore.h>  #include <acpi/apei.h>  #include "apei-internal.h" -#define ERST_PFX "ERST: " +#undef pr_fmt +#define pr_fmt(fmt) "ERST: " fmt  /* ERST command status */  #define ERST_STATUS_SUCCESS			0x0 @@ -53,7 +55,7 @@  				     sizeof(struct acpi_table_erst)))  #define SPIN_UNIT		100			/* 100ns */ -/* Firmware should respond within 1 miliseconds */ +/* Firmware should respond within 1 milliseconds */  #define FIRMWARE_TIMEOUT	(1 * NSEC_PER_MSEC)  #define FIRMWARE_MAX_STALL	50			/* 50us */ @@ -86,7 +88,7 @@ static struct erst_erange {   * It is used to provide exclusive accessing for ERST Error Log   * Address Range too.   */ -static DEFINE_SPINLOCK(erst_lock); +static DEFINE_RAW_SPINLOCK(erst_lock);  static inline int erst_errno(int command_status)  { @@ -108,8 +110,7 @@ static inline int erst_errno(int command_status)  static int erst_timedout(u64 *t, u64 spin_unit)  {  	if ((s64)*t < spin_unit) { -		pr_warning(FW_WARN ERST_PFX -			   "Firmware does not respond in time\n"); +		pr_warn(FW_WARN "Firmware does not respond in time.\n");  		return 1;  	}  	*t -= spin_unit; @@ -185,8 +186,8 @@ static int erst_exec_stall(struct apei_exec_context *ctx,  	if (ctx->value > FIRMWARE_MAX_STALL) {  		if (!in_nmi()) -			pr_warning(FW_WARN ERST_PFX -			"Too long stall time for stall instruction: %llx.\n", +			pr_warn(FW_WARN +			"Too long stall time for stall instruction: 0x%llx.\n",  				   ctx->value);  		stall_time = FIRMWARE_MAX_STALL;  	} else @@ -205,8 +206,8 @@ static int erst_exec_stall_while_true(struct apei_exec_context *ctx,  	if (ctx->var1 > FIRMWARE_MAX_STALL) {  		if (!in_nmi()) -			pr_warning(FW_WARN ERST_PFX -		"Too long stall time for stall while true instruction: %llx.\n", +			pr_warn(FW_WARN +		"Too long stall time for stall while true instruction: 0x%llx.\n",  				   ctx->var1);  		stall_time = FIRMWARE_MAX_STALL;  	} else @@ -270,8 +271,7 @@ static int erst_exec_move_data(struct apei_exec_context *ctx,  	/* ioremap does not work in interrupt context */  	if (in_interrupt()) { -		pr_warning(ERST_PFX -			   "MOVE_DATA can not be used in interrupt context"); +		pr_warn("MOVE_DATA can not be used in interrupt context.\n");  		return -EBUSY;  	} @@ -283,8 +283,10 @@ static int erst_exec_move_data(struct apei_exec_context *ctx,  	if (!src)  		return -ENOMEM;  	dst = ioremap(ctx->dst_base + offset, ctx->var2); -	if (!dst) +	if (!dst) { +		iounmap(src);  		return -ENOMEM; +	}  	memmove(dst, src, ctx->var2); @@ -421,14 +423,30 @@ ssize_t erst_get_record_count(void)  	if (erst_disable)  		return -ENODEV; -	spin_lock_irqsave(&erst_lock, flags); +	raw_spin_lock_irqsave(&erst_lock, flags);  	count = __erst_get_record_count(); -	spin_unlock_irqrestore(&erst_lock, flags); +	raw_spin_unlock_irqrestore(&erst_lock, flags);  	return count;  }  EXPORT_SYMBOL_GPL(erst_get_record_count); +#define ERST_RECORD_ID_CACHE_SIZE_MIN	16 +#define ERST_RECORD_ID_CACHE_SIZE_MAX	1024 + +struct erst_record_id_cache { +	struct mutex lock; +	u64 *entries; +	int len; +	int size; +	int refcount; +}; + +static struct erst_record_id_cache erst_record_id_cache = { +	.lock = __MUTEX_INITIALIZER(erst_record_id_cache.lock), +	.refcount = 0, +}; +  static int __erst_get_next_record_id(u64 *record_id)  {  	struct apei_exec_context ctx; @@ -443,26 +461,178 @@ static int __erst_get_next_record_id(u64 *record_id)  	return 0;  } +int erst_get_record_id_begin(int *pos) +{ +	int rc; + +	if (erst_disable) +		return -ENODEV; + +	rc = mutex_lock_interruptible(&erst_record_id_cache.lock); +	if (rc) +		return rc; +	erst_record_id_cache.refcount++; +	mutex_unlock(&erst_record_id_cache.lock); + +	*pos = 0; + +	return 0; +} +EXPORT_SYMBOL_GPL(erst_get_record_id_begin); + +/* erst_record_id_cache.lock must be held by caller */ +static int __erst_record_id_cache_add_one(void) +{ +	u64 id, prev_id, first_id; +	int i, rc; +	u64 *entries; +	unsigned long flags; + +	id = prev_id = first_id = APEI_ERST_INVALID_RECORD_ID; +retry: +	raw_spin_lock_irqsave(&erst_lock, flags); +	rc = __erst_get_next_record_id(&id); +	raw_spin_unlock_irqrestore(&erst_lock, flags); +	if (rc == -ENOENT) +		return 0; +	if (rc) +		return rc; +	if (id == APEI_ERST_INVALID_RECORD_ID) +		return 0; +	/* can not skip current ID, or loop back to first ID */ +	if (id == prev_id || id == first_id) +		return 0; +	if (first_id == APEI_ERST_INVALID_RECORD_ID) +		first_id = id; +	prev_id = id; + +	entries = erst_record_id_cache.entries; +	for (i = 0; i < erst_record_id_cache.len; i++) { +		if (entries[i] == id) +			break; +	} +	/* record id already in cache, try next */ +	if (i < erst_record_id_cache.len) +		goto retry; +	if (erst_record_id_cache.len >= erst_record_id_cache.size) { +		int new_size, alloc_size; +		u64 *new_entries; + +		new_size = erst_record_id_cache.size * 2; +		new_size = clamp_val(new_size, ERST_RECORD_ID_CACHE_SIZE_MIN, +				     ERST_RECORD_ID_CACHE_SIZE_MAX); +		if (new_size <= erst_record_id_cache.size) { +			if (printk_ratelimit()) +				pr_warn(FW_WARN "too many record IDs!\n"); +			return 0; +		} +		alloc_size = new_size * sizeof(entries[0]); +		if (alloc_size < PAGE_SIZE) +			new_entries = kmalloc(alloc_size, GFP_KERNEL); +		else +			new_entries = vmalloc(alloc_size); +		if (!new_entries) +			return -ENOMEM; +		memcpy(new_entries, entries, +		       erst_record_id_cache.len * sizeof(entries[0])); +		if (erst_record_id_cache.size < PAGE_SIZE) +			kfree(entries); +		else +			vfree(entries); +		erst_record_id_cache.entries = entries = new_entries; +		erst_record_id_cache.size = new_size; +	} +	entries[i] = id; +	erst_record_id_cache.len++; + +	return 1; +} +  /*   * Get the record ID of an existing error record on the persistent   * storage. If there is no error record on the persistent storage, the   * returned record_id is APEI_ERST_INVALID_RECORD_ID.   */ -int erst_get_next_record_id(u64 *record_id) +int erst_get_record_id_next(int *pos, u64 *record_id)  { -	int rc; -	unsigned long flags; +	int rc = 0; +	u64 *entries;  	if (erst_disable)  		return -ENODEV; -	spin_lock_irqsave(&erst_lock, flags); -	rc = __erst_get_next_record_id(record_id); -	spin_unlock_irqrestore(&erst_lock, flags); +	/* must be enclosed by erst_get_record_id_begin/end */ +	BUG_ON(!erst_record_id_cache.refcount); +	BUG_ON(*pos < 0 || *pos > erst_record_id_cache.len); + +	mutex_lock(&erst_record_id_cache.lock); +	entries = erst_record_id_cache.entries; +	for (; *pos < erst_record_id_cache.len; (*pos)++) +		if (entries[*pos] != APEI_ERST_INVALID_RECORD_ID) +			break; +	/* found next record id in cache */ +	if (*pos < erst_record_id_cache.len) { +		*record_id = entries[*pos]; +		(*pos)++; +		goto out_unlock; +	} + +	/* Try to add one more record ID to cache */ +	rc = __erst_record_id_cache_add_one(); +	if (rc < 0) +		goto out_unlock; +	/* successfully add one new ID */ +	if (rc == 1) { +		*record_id = erst_record_id_cache.entries[*pos]; +		(*pos)++; +		rc = 0; +	} else { +		*pos = -1; +		*record_id = APEI_ERST_INVALID_RECORD_ID; +	} +out_unlock: +	mutex_unlock(&erst_record_id_cache.lock);  	return rc;  } -EXPORT_SYMBOL_GPL(erst_get_next_record_id); +EXPORT_SYMBOL_GPL(erst_get_record_id_next); + +/* erst_record_id_cache.lock must be held by caller */ +static void __erst_record_id_cache_compact(void) +{ +	int i, wpos = 0; +	u64 *entries; + +	if (erst_record_id_cache.refcount) +		return; + +	entries = erst_record_id_cache.entries; +	for (i = 0; i < erst_record_id_cache.len; i++) { +		if (entries[i] == APEI_ERST_INVALID_RECORD_ID) +			continue; +		if (wpos != i) +			entries[wpos] = entries[i]; +		wpos++; +	} +	erst_record_id_cache.len = wpos; +} + +void erst_get_record_id_end(void) +{ +	/* +	 * erst_disable != 0 should be detected by invoker via the +	 * return value of erst_get_record_id_begin/next, so this +	 * function should not be called for erst_disable != 0. +	 */ +	BUG_ON(erst_disable); + +	mutex_lock(&erst_record_id_cache.lock); +	erst_record_id_cache.refcount--; +	BUG_ON(erst_record_id_cache.refcount < 0); +	__erst_record_id_cache_compact(); +	mutex_unlock(&erst_record_id_cache.lock); +} +EXPORT_SYMBOL_GPL(erst_get_record_id_end);  static int __erst_write_to_storage(u64 offset)  { @@ -472,7 +642,7 @@ static int __erst_write_to_storage(u64 offset)  	int rc;  	erst_exec_ctx_init(&ctx); -	rc = apei_exec_run(&ctx, ACPI_ERST_BEGIN_WRITE); +	rc = apei_exec_run_optional(&ctx, ACPI_ERST_BEGIN_WRITE);  	if (rc)  		return rc;  	apei_exec_ctx_set_input(&ctx, offset); @@ -496,7 +666,7 @@ static int __erst_write_to_storage(u64 offset)  	if (rc)  		return rc;  	val = apei_exec_ctx_get_output(&ctx); -	rc = apei_exec_run(&ctx, ACPI_ERST_END); +	rc = apei_exec_run_optional(&ctx, ACPI_ERST_END);  	if (rc)  		return rc; @@ -511,7 +681,7 @@ static int __erst_read_from_storage(u64 record_id, u64 offset)  	int rc;  	erst_exec_ctx_init(&ctx); -	rc = apei_exec_run(&ctx, ACPI_ERST_BEGIN_READ); +	rc = apei_exec_run_optional(&ctx, ACPI_ERST_BEGIN_READ);  	if (rc)  		return rc;  	apei_exec_ctx_set_input(&ctx, offset); @@ -539,7 +709,7 @@ static int __erst_read_from_storage(u64 record_id, u64 offset)  	if (rc)  		return rc;  	val = apei_exec_ctx_get_output(&ctx); -	rc = apei_exec_run(&ctx, ACPI_ERST_END); +	rc = apei_exec_run_optional(&ctx, ACPI_ERST_END);  	if (rc)  		return rc; @@ -554,7 +724,7 @@ static int __erst_clear_from_storage(u64 record_id)  	int rc;  	erst_exec_ctx_init(&ctx); -	rc = apei_exec_run(&ctx, ACPI_ERST_BEGIN_CLEAR); +	rc = apei_exec_run_optional(&ctx, ACPI_ERST_BEGIN_CLEAR);  	if (rc)  		return rc;  	apei_exec_ctx_set_input(&ctx, record_id); @@ -578,7 +748,7 @@ static int __erst_clear_from_storage(u64 record_id)  	if (rc)  		return rc;  	val = apei_exec_ctx_get_output(&ctx); -	rc = apei_exec_run(&ctx, ACPI_ERST_END); +	rc = apei_exec_run_optional(&ctx, ACPI_ERST_END);  	if (rc)  		return rc; @@ -589,8 +759,7 @@ static int __erst_clear_from_storage(u64 record_id)  static void pr_unimpl_nvram(void)  {  	if (printk_ratelimit()) -		pr_warning(ERST_PFX -		"NVRAM ERST Log Address Range is not implemented yet\n"); +		pr_warn("NVRAM ERST Log Address Range not implemented yet.\n");  }  static int __erst_write_to_nvram(const struct cper_record_header *record) @@ -624,17 +793,17 @@ int erst_write(const struct cper_record_header *record)  		return -EINVAL;  	if (erst_erange.attr & ERST_RANGE_NVRAM) { -		if (!spin_trylock_irqsave(&erst_lock, flags)) +		if (!raw_spin_trylock_irqsave(&erst_lock, flags))  			return -EBUSY;  		rc = __erst_write_to_nvram(record); -		spin_unlock_irqrestore(&erst_lock, flags); +		raw_spin_unlock_irqrestore(&erst_lock, flags);  		return rc;  	}  	if (record->record_length > erst_erange.size)  		return -EINVAL; -	if (!spin_trylock_irqsave(&erst_lock, flags)) +	if (!raw_spin_trylock_irqsave(&erst_lock, flags))  		return -EBUSY;  	memcpy(erst_erange.vaddr, record, record->record_length);  	rcd_erange = erst_erange.vaddr; @@ -642,7 +811,7 @@ int erst_write(const struct cper_record_header *record)  	memcpy(&rcd_erange->persistence_information, "ER", 2);  	rc = __erst_write_to_storage(0); -	spin_unlock_irqrestore(&erst_lock, flags); +	raw_spin_unlock_irqrestore(&erst_lock, flags);  	return rc;  } @@ -696,63 +865,41 @@ ssize_t erst_read(u64 record_id, struct cper_record_header *record,  	if (erst_disable)  		return -ENODEV; -	spin_lock_irqsave(&erst_lock, flags); +	raw_spin_lock_irqsave(&erst_lock, flags);  	len = __erst_read(record_id, record, buflen); -	spin_unlock_irqrestore(&erst_lock, flags); +	raw_spin_unlock_irqrestore(&erst_lock, flags);  	return len;  }  EXPORT_SYMBOL_GPL(erst_read); -/* - * If return value > buflen, the buffer size is not big enough, - * else if return value = 0, there is no more record to read, - * else if return value < 0, something goes wrong, - * else everything is OK, and return value is record length - */ -ssize_t erst_read_next(struct cper_record_header *record, size_t buflen) -{ -	int rc; -	ssize_t len; -	unsigned long flags; -	u64 record_id; - -	if (erst_disable) -		return -ENODEV; - -	spin_lock_irqsave(&erst_lock, flags); -	rc = __erst_get_next_record_id(&record_id); -	if (rc) { -		spin_unlock_irqrestore(&erst_lock, flags); -		return rc; -	} -	/* no more record */ -	if (record_id == APEI_ERST_INVALID_RECORD_ID) { -		spin_unlock_irqrestore(&erst_lock, flags); -		return 0; -	} - -	len = __erst_read(record_id, record, buflen); -	spin_unlock_irqrestore(&erst_lock, flags); - -	return len; -} -EXPORT_SYMBOL_GPL(erst_read_next); -  int erst_clear(u64 record_id)  { -	int rc; +	int rc, i;  	unsigned long flags; +	u64 *entries;  	if (erst_disable)  		return -ENODEV; -	spin_lock_irqsave(&erst_lock, flags); +	rc = mutex_lock_interruptible(&erst_record_id_cache.lock); +	if (rc) +		return rc; +	raw_spin_lock_irqsave(&erst_lock, flags);  	if (erst_erange.attr & ERST_RANGE_NVRAM)  		rc = __erst_clear_from_nvram(record_id);  	else  		rc = __erst_clear_from_storage(record_id); -	spin_unlock_irqrestore(&erst_lock, flags); - +	raw_spin_unlock_irqrestore(&erst_lock, flags); +	if (rc) +		goto out; +	entries = erst_record_id_cache.entries; +	for (i = 0; i < erst_record_id_cache.len; i++) { +		if (entries[i] == record_id) +			entries[i] = APEI_ERST_INVALID_RECORD_ID; +	} +	__erst_record_id_cache_compact(); +out: +	mutex_unlock(&erst_record_id_cache.lock);  	return rc;  }  EXPORT_SYMBOL_GPL(erst_clear); @@ -769,7 +916,7 @@ static int erst_check_table(struct acpi_table_erst *erst_tab)  {  	if ((erst_tab->header_length !=  	     (sizeof(struct acpi_table_erst) - sizeof(erst_tab->header))) -	    && (erst_tab->header_length != sizeof(struct acpi_table_einj))) +	    && (erst_tab->header_length != sizeof(struct acpi_table_erst)))  		return -EINVAL;  	if (erst_tab->header.length < sizeof(struct acpi_table_erst))  		return -EINVAL; @@ -781,6 +928,196 @@ static int erst_check_table(struct acpi_table_erst *erst_tab)  	return 0;  } +static int erst_open_pstore(struct pstore_info *psi); +static int erst_close_pstore(struct pstore_info *psi); +static ssize_t erst_reader(u64 *id, enum pstore_type_id *type, int *count, +			   struct timespec *time, char **buf, +			   bool *compressed, struct pstore_info *psi); +static int erst_writer(enum pstore_type_id type, enum kmsg_dump_reason reason, +		       u64 *id, unsigned int part, int count, bool compressed, +		       size_t size, struct pstore_info *psi); +static int erst_clearer(enum pstore_type_id type, u64 id, int count, +			struct timespec time, struct pstore_info *psi); + +static struct pstore_info erst_info = { +	.owner		= THIS_MODULE, +	.name		= "erst", +	.flags		= PSTORE_FLAGS_FRAGILE, +	.open		= erst_open_pstore, +	.close		= erst_close_pstore, +	.read		= erst_reader, +	.write		= erst_writer, +	.erase		= erst_clearer +}; + +#define CPER_CREATOR_PSTORE						\ +	UUID_LE(0x75a574e3, 0x5052, 0x4b29, 0x8a, 0x8e, 0xbe, 0x2c,	\ +		0x64, 0x90, 0xb8, 0x9d) +#define CPER_SECTION_TYPE_DMESG						\ +	UUID_LE(0xc197e04e, 0xd545, 0x4a70, 0x9c, 0x17, 0xa5, 0x54,	\ +		0x94, 0x19, 0xeb, 0x12) +#define CPER_SECTION_TYPE_DMESG_Z					\ +	UUID_LE(0x4f118707, 0x04dd, 0x4055, 0xb5, 0xdd, 0x95, 0x6d,	\ +		0x34, 0xdd, 0xfa, 0xc6) +#define CPER_SECTION_TYPE_MCE						\ +	UUID_LE(0xfe08ffbe, 0x95e4, 0x4be7, 0xbc, 0x73, 0x40, 0x96,	\ +		0x04, 0x4a, 0x38, 0xfc) + +struct cper_pstore_record { +	struct cper_record_header hdr; +	struct cper_section_descriptor sec_hdr; +	char data[]; +} __packed; + +static int reader_pos; + +static int erst_open_pstore(struct pstore_info *psi) +{ +	int rc; + +	if (erst_disable) +		return -ENODEV; + +	rc = erst_get_record_id_begin(&reader_pos); + +	return rc; +} + +static int erst_close_pstore(struct pstore_info *psi) +{ +	erst_get_record_id_end(); + +	return 0; +} + +static ssize_t erst_reader(u64 *id, enum pstore_type_id *type, int *count, +			   struct timespec *time, char **buf, +			   bool *compressed, struct pstore_info *psi) +{ +	int rc; +	ssize_t len = 0; +	u64 record_id; +	struct cper_pstore_record *rcd; +	size_t rcd_len = sizeof(*rcd) + erst_info.bufsize; + +	if (erst_disable) +		return -ENODEV; + +	rcd = kmalloc(rcd_len, GFP_KERNEL); +	if (!rcd) { +		rc = -ENOMEM; +		goto out; +	} +skip: +	rc = erst_get_record_id_next(&reader_pos, &record_id); +	if (rc) +		goto out; + +	/* no more record */ +	if (record_id == APEI_ERST_INVALID_RECORD_ID) { +		rc = -EINVAL; +		goto out; +	} + +	len = erst_read(record_id, &rcd->hdr, rcd_len); +	/* The record may be cleared by others, try read next record */ +	if (len == -ENOENT) +		goto skip; +	else if (len < sizeof(*rcd)) { +		rc = -EIO; +		goto out; +	} +	if (uuid_le_cmp(rcd->hdr.creator_id, CPER_CREATOR_PSTORE) != 0) +		goto skip; + +	*buf = kmalloc(len, GFP_KERNEL); +	if (*buf == NULL) { +		rc = -ENOMEM; +		goto out; +	} +	memcpy(*buf, rcd->data, len - sizeof(*rcd)); +	*id = record_id; +	*compressed = false; +	if (uuid_le_cmp(rcd->sec_hdr.section_type, +			CPER_SECTION_TYPE_DMESG_Z) == 0) { +		*type = PSTORE_TYPE_DMESG; +		*compressed = true; +	} else if (uuid_le_cmp(rcd->sec_hdr.section_type, +			CPER_SECTION_TYPE_DMESG) == 0) +		*type = PSTORE_TYPE_DMESG; +	else if (uuid_le_cmp(rcd->sec_hdr.section_type, +			     CPER_SECTION_TYPE_MCE) == 0) +		*type = PSTORE_TYPE_MCE; +	else +		*type = PSTORE_TYPE_UNKNOWN; + +	if (rcd->hdr.validation_bits & CPER_VALID_TIMESTAMP) +		time->tv_sec = rcd->hdr.timestamp; +	else +		time->tv_sec = 0; +	time->tv_nsec = 0; + +out: +	kfree(rcd); +	return (rc < 0) ? rc : (len - sizeof(*rcd)); +} + +static int erst_writer(enum pstore_type_id type, enum kmsg_dump_reason reason, +		       u64 *id, unsigned int part, int count, bool compressed, +		       size_t size, struct pstore_info *psi) +{ +	struct cper_pstore_record *rcd = (struct cper_pstore_record *) +					(erst_info.buf - sizeof(*rcd)); +	int ret; + +	memset(rcd, 0, sizeof(*rcd)); +	memcpy(rcd->hdr.signature, CPER_SIG_RECORD, CPER_SIG_SIZE); +	rcd->hdr.revision = CPER_RECORD_REV; +	rcd->hdr.signature_end = CPER_SIG_END; +	rcd->hdr.section_count = 1; +	rcd->hdr.error_severity = CPER_SEV_FATAL; +	/* timestamp valid. platform_id, partition_id are invalid */ +	rcd->hdr.validation_bits = CPER_VALID_TIMESTAMP; +	rcd->hdr.timestamp = get_seconds(); +	rcd->hdr.record_length = sizeof(*rcd) + size; +	rcd->hdr.creator_id = CPER_CREATOR_PSTORE; +	rcd->hdr.notification_type = CPER_NOTIFY_MCE; +	rcd->hdr.record_id = cper_next_record_id(); +	rcd->hdr.flags = CPER_HW_ERROR_FLAGS_PREVERR; + +	rcd->sec_hdr.section_offset = sizeof(*rcd); +	rcd->sec_hdr.section_length = size; +	rcd->sec_hdr.revision = CPER_SEC_REV; +	/* fru_id and fru_text is invalid */ +	rcd->sec_hdr.validation_bits = 0; +	rcd->sec_hdr.flags = CPER_SEC_PRIMARY; +	switch (type) { +	case PSTORE_TYPE_DMESG: +		if (compressed) +			rcd->sec_hdr.section_type = CPER_SECTION_TYPE_DMESG_Z; +		else +			rcd->sec_hdr.section_type = CPER_SECTION_TYPE_DMESG; +		break; +	case PSTORE_TYPE_MCE: +		rcd->sec_hdr.section_type = CPER_SECTION_TYPE_MCE; +		break; +	default: +		return -EINVAL; +	} +	rcd->sec_hdr.section_severity = CPER_SEV_FATAL; + +	ret = erst_write(&rcd->hdr); +	*id = rcd->hdr.record_id; + +	return ret; +} + +static int erst_clearer(enum pstore_type_id type, u64 id, int count, +			struct timespec time, struct pstore_info *psi) +{ +	return erst_clear(id); +} +  static int __init erst_init(void)  {  	int rc = 0; @@ -788,31 +1125,31 @@ static int __init erst_init(void)  	struct apei_exec_context ctx;  	struct apei_resources erst_resources;  	struct resource *r; +	char *buf;  	if (acpi_disabled)  		goto err;  	if (erst_disable) { -		pr_info(ERST_PFX +		pr_info(  	"Error Record Serialization Table (ERST) support is disabled.\n");  		goto err;  	}  	status = acpi_get_table(ACPI_SIG_ERST, 0,  				(struct acpi_table_header **)&erst_tab); -	if (status == AE_NOT_FOUND) { -		pr_info(ERST_PFX "Table is not found!\n"); +	if (status == AE_NOT_FOUND)  		goto err; -	} else if (ACPI_FAILURE(status)) { +	else if (ACPI_FAILURE(status)) {  		const char *msg = acpi_format_exception(status); -		pr_err(ERST_PFX "Failed to get table, %s\n", msg); +		pr_err("Failed to get table, %s\n", msg);  		rc = -EINVAL;  		goto err;  	}  	rc = erst_check_table(erst_tab);  	if (rc) { -		pr_err(FW_BUG ERST_PFX "ERST table is invalid\n"); +		pr_err(FW_BUG "ERST table is invalid.\n");  		goto err;  	} @@ -830,21 +1167,19 @@ static int __init erst_init(void)  	rc = erst_get_erange(&erst_erange);  	if (rc) {  		if (rc == -ENODEV) -			pr_info(ERST_PFX +			pr_info(  	"The corresponding hardware device or firmware implementation "  	"is not available.\n");  		else -			pr_err(ERST_PFX -			       "Failed to get Error Log Address Range.\n"); +			pr_err("Failed to get Error Log Address Range.\n");  		goto err_unmap_reg;  	}  	r = request_mem_region(erst_erange.base, erst_erange.size, "APEI ERST");  	if (!r) { -		pr_err(ERST_PFX -		"Can not request iomem region <0x%16llx-0x%16llx> for ERST.\n", -		(unsigned long long)erst_erange.base, -		(unsigned long long)erst_erange.base + erst_erange.size); +		pr_err("Can not request [mem %#010llx-%#010llx] for ERST.\n", +		       (unsigned long long)erst_erange.base, +		       (unsigned long long)erst_erange.base + erst_erange.size - 1);  		rc = -EIO;  		goto err_unmap_reg;  	} @@ -854,9 +1189,29 @@ static int __init erst_init(void)  	if (!erst_erange.vaddr)  		goto err_release_erange; -	pr_info(ERST_PFX +	pr_info(  	"Error Record Serialization Table (ERST) support is initialized.\n"); +	buf = kmalloc(erst_erange.size, GFP_KERNEL); +	spin_lock_init(&erst_info.buf_lock); +	if (buf) { +		erst_info.buf = buf + sizeof(struct cper_pstore_record); +		erst_info.bufsize = erst_erange.size - +				    sizeof(struct cper_pstore_record); +		rc = pstore_register(&erst_info); +		if (rc) { +			if (rc != -EPERM) +				pr_info( +				"Could not register with persistent store.\n"); +			erst_info.buf = NULL; +			erst_info.bufsize = 0; +			kfree(buf); +		} +	} else +		pr_err( +		"Failed to allocate %lld bytes for persistent store error log.\n", +		erst_erange.size); +  	return 0;  err_release_erange:  | 
