ACPI APEI: Convert atomicio routines
APEI needs memory access in interrupt context.  The obvious choice is
acpi_read(), but originally it couldn't be used in interrupt context
because it makes temporary mappings with ioremap().  Therefore, we added
drivers/acpi/atomicio.c, which provides:
    acpi_pre_map_gar()     -- ioremap in process context
	acpi_atomic_read()     -- memory access in interrupt context
	acpi_post_unmap_gar()  -- iounmap
Later we added acpi_os_map_generic_address() (2971852) and enhanced
acpi_read() so it works in interrupt context as long as the address has
been previously mapped (620242a).  Now this sequence:
    acpi_os_map_generic_address()    -- ioremap in process context
    acpi_read()/apei_read()          -- now OK in interrupt context
    acpi_os_unmap_generic_address()
is equivalent to what atomicio.c provides.
This patch introduces apei_read() and apei_write(), which currently are
functional equivalents of acpi_read() and acpi_write().  This is mainly
proactive, to prevent APEI breakages if acpi_read() and acpi_write()
are ever augmented to support the 'bit_offset' field of GAS, as APEI's
__apei_exec_write_register() precludes splitting up functionality
related to 'bit_offset' and APEI's 'mask' (see its
APEI_EXEC_PRESERVE_REGISTER block).
With apei_read() and apei_write() in place, usages of atomicio routines
are converted to apei_read()/apei_write() and existing calls within
osl.c and the CA, based on the re-factoring that was done in an earlier
patch series - http://marc.info/?l=linux-acpi&m=128769263327206&w=2:
    acpi_pre_map_gar()     -->  acpi_os_map_generic_address()
    acpi_post_unmap_gar()  -->  acpi_os_unmap_generic_address()
    acpi_atomic_read()     -->  apei_read()
    acpi_atomic_write()    -->  apei_write()
Note that acpi_read() and acpi_write() currently use 'bit_width'
for accessing GARs which seems incorrect.  'bit_width' is the size of
the register, while 'access_width' is the size of the access the
processor must generate on the bus.  The 'access_width' may be larger,
for example, if the hardware only supports 32-bit or 64-bit reads.  I
wanted to minimize any possible impacts with this patch series so I
did *not* change this behavior.
Signed-off-by: Myron Stowe <myron.stowe@redhat.com>
Signed-off-by: Len Brown <len.brown@intel.com>
	
	
This commit is contained in:
		
					parent
					
						
							
								6f68c91c55
							
						
					
				
			
			
				commit
				
					
						700130b41f
					
				
			
		
					 3 changed files with 104 additions and 11 deletions
				
			
		|  | @ -34,13 +34,13 @@ | |||
| #include <linux/module.h> | ||||
| #include <linux/init.h> | ||||
| #include <linux/acpi.h> | ||||
| #include <linux/acpi_io.h> | ||||
| #include <linux/slab.h> | ||||
| #include <linux/io.h> | ||||
| #include <linux/kref.h> | ||||
| #include <linux/rculist.h> | ||||
| #include <linux/interrupt.h> | ||||
| #include <linux/debugfs.h> | ||||
| #include <acpi/atomicio.h> | ||||
| 
 | ||||
| #include "apei-internal.h" | ||||
| 
 | ||||
|  | @ -70,7 +70,7 @@ int __apei_exec_read_register(struct acpi_whea_header *entry, u64 *val) | |||
| { | ||||
| 	int rc; | ||||
| 
 | ||||
| 	rc = acpi_atomic_read(val, &entry->register_region); | ||||
| 	rc = apei_read(val, &entry->register_region); | ||||
| 	if (rc) | ||||
| 		return rc; | ||||
| 	*val >>= entry->register_region.bit_offset; | ||||
|  | @ -116,13 +116,13 @@ int __apei_exec_write_register(struct acpi_whea_header *entry, u64 val) | |||
| 	val <<= entry->register_region.bit_offset; | ||||
| 	if (entry->flags & APEI_EXEC_PRESERVE_REGISTER) { | ||||
| 		u64 valr = 0; | ||||
| 		rc = acpi_atomic_read(&valr, &entry->register_region); | ||||
| 		rc = apei_read(&valr, &entry->register_region); | ||||
| 		if (rc) | ||||
| 			return rc; | ||||
| 		valr &= ~(entry->mask << entry->register_region.bit_offset); | ||||
| 		val |= valr; | ||||
| 	} | ||||
| 	rc = acpi_atomic_write(val, &entry->register_region); | ||||
| 	rc = apei_write(val, &entry->register_region); | ||||
| 
 | ||||
| 	return rc; | ||||
| } | ||||
|  | @ -243,7 +243,7 @@ static int pre_map_gar_callback(struct apei_exec_context *ctx, | |||
| 	u8 ins = entry->instruction; | ||||
| 
 | ||||
| 	if (ctx->ins_table[ins].flags & APEI_EXEC_INS_ACCESS_REGISTER) | ||||
| 		return acpi_pre_map_gar(&entry->register_region); | ||||
| 		return acpi_os_map_generic_address(&entry->register_region); | ||||
| 
 | ||||
| 	return 0; | ||||
| } | ||||
|  | @ -276,7 +276,7 @@ static int post_unmap_gar_callback(struct apei_exec_context *ctx, | |||
| 	u8 ins = entry->instruction; | ||||
| 
 | ||||
| 	if (ctx->ins_table[ins].flags & APEI_EXEC_INS_ACCESS_REGISTER) | ||||
| 		acpi_post_unmap_gar(&entry->register_region); | ||||
| 		acpi_os_unmap_generic_address(&entry->register_region); | ||||
| 
 | ||||
| 	return 0; | ||||
| } | ||||
|  | @ -591,6 +591,96 @@ static int apei_check_gar(struct acpi_generic_address *reg, u64 *paddr) | |||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| /* read GAR in interrupt (including NMI) or process context */ | ||||
| int apei_read(u64 *val, struct acpi_generic_address *reg) | ||||
| { | ||||
| 	int rc; | ||||
| 	u64 address; | ||||
| 	u32 tmp, width = reg->bit_width; | ||||
| 	acpi_status status; | ||||
| 
 | ||||
| 	rc = apei_check_gar(reg, &address); | ||||
| 	if (rc) | ||||
| 		return rc; | ||||
| 
 | ||||
| 	if (width == 64) | ||||
| 		width = 32;	/* Break into two 32-bit transfers */ | ||||
| 
 | ||||
| 	*val = 0; | ||||
| 	switch(reg->space_id) { | ||||
| 	case ACPI_ADR_SPACE_SYSTEM_MEMORY: | ||||
| 		status = acpi_os_read_memory((acpi_physical_address) | ||||
| 					     address, &tmp, width); | ||||
| 		if (ACPI_FAILURE(status)) | ||||
| 			return -EIO; | ||||
| 		*val = tmp; | ||||
| 
 | ||||
| 		if (reg->bit_width == 64) { | ||||
| 			/* Read the top 32 bits */ | ||||
| 			status = acpi_os_read_memory((acpi_physical_address) | ||||
| 						     (address + 4), &tmp, 32); | ||||
| 			if (ACPI_FAILURE(status)) | ||||
| 				return -EIO; | ||||
| 			*val |= ((u64)tmp << 32); | ||||
| 		} | ||||
| 		break; | ||||
| 	case ACPI_ADR_SPACE_SYSTEM_IO: | ||||
| 		status = acpi_os_read_port(address, (u32 *)val, reg->bit_width); | ||||
| 		if (ACPI_FAILURE(status)) | ||||
| 			return -EIO; | ||||
| 		break; | ||||
| 	default: | ||||
| 		return -EINVAL; | ||||
| 	} | ||||
| 
 | ||||
| 	return 0; | ||||
| } | ||||
| EXPORT_SYMBOL_GPL(apei_read); | ||||
| 
 | ||||
| /* write GAR in interrupt (including NMI) or process context */ | ||||
| int apei_write(u64 val, struct acpi_generic_address *reg) | ||||
| { | ||||
| 	int rc; | ||||
| 	u64 address; | ||||
| 	u32 width = reg->bit_width; | ||||
| 	acpi_status status; | ||||
| 
 | ||||
| 	rc = apei_check_gar(reg, &address); | ||||
| 	if (rc) | ||||
| 		return rc; | ||||
| 
 | ||||
| 	if (width == 64) | ||||
| 		width = 32;	/* Break into two 32-bit transfers */ | ||||
| 
 | ||||
| 	switch (reg->space_id) { | ||||
| 	case ACPI_ADR_SPACE_SYSTEM_MEMORY: | ||||
| 		status = acpi_os_write_memory((acpi_physical_address) | ||||
| 					      address, ACPI_LODWORD(val), | ||||
| 					      width); | ||||
| 		if (ACPI_FAILURE(status)) | ||||
| 			return -EIO; | ||||
| 
 | ||||
| 		if (reg->bit_width == 64) { | ||||
| 			status = acpi_os_write_memory((acpi_physical_address) | ||||
| 						      (address + 4), | ||||
| 						      ACPI_HIDWORD(val), 32); | ||||
| 			if (ACPI_FAILURE(status)) | ||||
| 				return -EIO; | ||||
| 		} | ||||
| 		break; | ||||
| 	case ACPI_ADR_SPACE_SYSTEM_IO: | ||||
| 		status = acpi_os_write_port(address, val, reg->bit_width); | ||||
| 		if (ACPI_FAILURE(status)) | ||||
| 			return -EIO; | ||||
| 		break; | ||||
| 	default: | ||||
| 		return -EINVAL; | ||||
| 	} | ||||
| 
 | ||||
| 	return 0; | ||||
| } | ||||
| EXPORT_SYMBOL_GPL(apei_write); | ||||
| 
 | ||||
| static int collect_res_callback(struct apei_exec_context *ctx, | ||||
| 				struct acpi_whea_header *entry, | ||||
| 				void *data) | ||||
|  |  | |||
|  | @ -68,6 +68,9 @@ static inline int apei_exec_run_optional(struct apei_exec_context *ctx, u8 actio | |||
| /* IP has been set in instruction function */ | ||||
| #define APEI_EXEC_SET_IP	1 | ||||
| 
 | ||||
| int apei_read(u64 *val, struct acpi_generic_address *reg); | ||||
| int apei_write(u64 val, struct acpi_generic_address *reg); | ||||
| 
 | ||||
| int __apei_exec_read_register(struct acpi_whea_header *entry, u64 *val); | ||||
| int __apei_exec_write_register(struct acpi_whea_header *entry, u64 val); | ||||
| int apei_exec_read_register(struct apei_exec_context *ctx, | ||||
|  |  | |||
|  | @ -33,6 +33,7 @@ | |||
| #include <linux/module.h> | ||||
| #include <linux/init.h> | ||||
| #include <linux/acpi.h> | ||||
| #include <linux/acpi_io.h> | ||||
| #include <linux/io.h> | ||||
| #include <linux/interrupt.h> | ||||
| #include <linux/timer.h> | ||||
|  | @ -48,7 +49,6 @@ | |||
| #include <linux/pci.h> | ||||
| #include <linux/aer.h> | ||||
| #include <acpi/apei.h> | ||||
| #include <acpi/atomicio.h> | ||||
| #include <acpi/hed.h> | ||||
| #include <asm/mce.h> | ||||
| #include <asm/tlbflush.h> | ||||
|  | @ -301,7 +301,7 @@ static struct ghes *ghes_new(struct acpi_hest_generic *generic) | |||
| 	if (!ghes) | ||||
| 		return ERR_PTR(-ENOMEM); | ||||
| 	ghes->generic = generic; | ||||
| 	rc = acpi_pre_map_gar(&generic->error_status_address); | ||||
| 	rc = acpi_os_map_generic_address(&generic->error_status_address); | ||||
| 	if (rc) | ||||
| 		goto err_free; | ||||
| 	error_block_length = generic->error_block_length; | ||||
|  | @ -321,7 +321,7 @@ static struct ghes *ghes_new(struct acpi_hest_generic *generic) | |||
| 	return ghes; | ||||
| 
 | ||||
| err_unmap: | ||||
| 	acpi_post_unmap_gar(&generic->error_status_address); | ||||
| 	acpi_os_unmap_generic_address(&generic->error_status_address); | ||||
| err_free: | ||||
| 	kfree(ghes); | ||||
| 	return ERR_PTR(rc); | ||||
|  | @ -330,7 +330,7 @@ err_free: | |||
| static void ghes_fini(struct ghes *ghes) | ||||
| { | ||||
| 	kfree(ghes->estatus); | ||||
| 	acpi_post_unmap_gar(&ghes->generic->error_status_address); | ||||
| 	acpi_os_unmap_generic_address(&ghes->generic->error_status_address); | ||||
| } | ||||
| 
 | ||||
| enum { | ||||
|  | @ -401,7 +401,7 @@ static int ghes_read_estatus(struct ghes *ghes, int silent) | |||
| 	u32 len; | ||||
| 	int rc; | ||||
| 
 | ||||
| 	rc = acpi_atomic_read(&buf_paddr, &g->error_status_address); | ||||
| 	rc = apei_read(&buf_paddr, &g->error_status_address); | ||||
| 	if (rc) { | ||||
| 		if (!silent && printk_ratelimit()) | ||||
| 			pr_warning(FW_WARN GHES_PFX | ||||
|  |  | |||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue
	
	 Myron Stowe
				Myron Stowe