From 073b4964b3b75fd9e19bf3933b26d9c23591c9db Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Tue, 8 Feb 2011 23:37:31 +0100 Subject: ACPI: Do not export functions that are only used in osl.c The functions acpi_os_map_generic_address() and acpi_os_unmap_generic_address() are only used in drivers/acpi/osl.c, so make them static and remove the extern definitions of them from include/linux/acpi_io.h. Signed-off-by: Rafael J. Wysocki --- include/linux/acpi_io.h | 3 --- 1 file changed, 3 deletions(-) (limited to 'include') diff --git a/include/linux/acpi_io.h b/include/linux/acpi_io.h index 7180013a4a3a..28a3ae279d7a 100644 --- a/include/linux/acpi_io.h +++ b/include/linux/acpi_io.h @@ -10,7 +10,4 @@ static inline void __iomem *acpi_os_ioremap(acpi_physical_address phys, return ioremap_cache(phys, size); } -int acpi_os_map_generic_address(struct acpi_generic_address *addr); -void acpi_os_unmap_generic_address(struct acpi_generic_address *addr); - #endif -- cgit v1.2.3 From 13606a2de1996f8d83a9ce296f74022bdbadf712 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Tue, 8 Feb 2011 23:38:25 +0100 Subject: ACPI: Introduce acpi_os_get_iomem() Introduce function acpi_os_get_iomem() that may be used by its callers to get a reference to an ACPI iomap. Signed-off-by: Rafael J. Wysocki --- drivers/acpi/osl.c | 16 ++++++++++++++++ include/linux/acpi_io.h | 2 ++ 2 files changed, 18 insertions(+) (limited to 'include') diff --git a/drivers/acpi/osl.c b/drivers/acpi/osl.c index 5389f9f2e2ff..52ca8721bc9c 100644 --- a/drivers/acpi/osl.c +++ b/drivers/acpi/osl.c @@ -285,6 +285,22 @@ acpi_map_vaddr_lookup(acpi_physical_address phys, unsigned int size) return NULL; } +void __iomem *acpi_os_get_iomem(acpi_physical_address phys, unsigned int size) +{ + struct acpi_ioremap *map; + void __iomem *virt = NULL; + + mutex_lock(&acpi_ioremap_lock); + map = acpi_map_lookup(phys, size); + if (map) { + virt = map->virt + (phys - map->phys); + map->refcount++; + } + mutex_unlock(&acpi_ioremap_lock); + return virt; +} +EXPORT_SYMBOL_GPL(acpi_os_get_iomem); + /* Must be called with 'acpi_ioremap_lock' or RCU read lock held. */ static struct acpi_ioremap * acpi_map_lookup_virt(void __iomem *virt, acpi_size size) diff --git a/include/linux/acpi_io.h b/include/linux/acpi_io.h index 28a3ae279d7a..4afd7102459d 100644 --- a/include/linux/acpi_io.h +++ b/include/linux/acpi_io.h @@ -10,4 +10,6 @@ static inline void __iomem *acpi_os_ioremap(acpi_physical_address phys, return ioremap_cache(phys, size); } +void __iomem *acpi_os_get_iomem(acpi_physical_address phys, unsigned int size); + #endif -- cgit v1.2.3 From 5190726765b40774c069e187a958e10ccd970e65 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Tue, 8 Feb 2011 23:40:37 +0100 Subject: ACPI: Remove the wakeup.run_wake_count device field The wakeup.run_wake_count ACPI device field is only used by the PCI runtime PM code to "protect" devices from being prepared for generating wakeup signals more than once in a row. However, it really doesn't provide any protection, because (1) all of the functions it is supposed to protect use their own reference counters effectively ensuring that the device will be set up for generating wakeup signals just once and (2) the PCI runtime PM code uses wakeup.run_wake_count in a racy way, since nothing prevents acpi_dev_run_wake() from being called concurrently from two different threads for the same device. Remove the wakeup.run_wake_count ACPI device field which is unnecessary, confusing and used in a wrong way. Signed-off-by: Rafael J. Wysocki --- drivers/acpi/button.c | 2 -- drivers/acpi/scan.c | 1 - drivers/pci/pci-acpi.c | 16 ++++------------ include/acpi/acpi_bus.h | 1 - 4 files changed, 4 insertions(+), 16 deletions(-) (limited to 'include') diff --git a/drivers/acpi/button.c b/drivers/acpi/button.c index 76bbb78a5ad9..e643a0936dc4 100644 --- a/drivers/acpi/button.c +++ b/drivers/acpi/button.c @@ -430,7 +430,6 @@ static int acpi_button_add(struct acpi_device *device) /* Button's GPE is run-wake GPE */ acpi_enable_gpe(device->wakeup.gpe_device, device->wakeup.gpe_number); - device->wakeup.run_wake_count++; device_set_wakeup_enable(&device->dev, true); } @@ -453,7 +452,6 @@ static int acpi_button_remove(struct acpi_device *device, int type) if (device->wakeup.flags.valid) { acpi_disable_gpe(device->wakeup.gpe_device, device->wakeup.gpe_number); - device->wakeup.run_wake_count--; device_set_wakeup_enable(&device->dev, false); } diff --git a/drivers/acpi/scan.c b/drivers/acpi/scan.c index b99e62494607..b136c9c1e531 100644 --- a/drivers/acpi/scan.c +++ b/drivers/acpi/scan.c @@ -797,7 +797,6 @@ static void acpi_bus_set_run_wake_flags(struct acpi_device *device) acpi_status status; acpi_event_status event_status; - device->wakeup.run_wake_count = 0; device->wakeup.flags.notifier_present = 0; /* Power button, Lid switch always enable wakeup */ diff --git a/drivers/pci/pci-acpi.c b/drivers/pci/pci-acpi.c index 6fe0772e0e7d..7c3b18e78cee 100644 --- a/drivers/pci/pci-acpi.c +++ b/drivers/pci/pci-acpi.c @@ -293,19 +293,11 @@ static int acpi_dev_run_wake(struct device *phys_dev, bool enable) } if (enable) { - if (!dev->wakeup.run_wake_count++) { - acpi_enable_wakeup_device_power(dev, ACPI_STATE_S0); - acpi_enable_gpe(dev->wakeup.gpe_device, - dev->wakeup.gpe_number); - } - } else if (dev->wakeup.run_wake_count > 0) { - if (!--dev->wakeup.run_wake_count) { - acpi_disable_gpe(dev->wakeup.gpe_device, - dev->wakeup.gpe_number); - acpi_disable_wakeup_device_power(dev); - } + acpi_enable_wakeup_device_power(dev, ACPI_STATE_S0); + acpi_enable_gpe(dev->wakeup.gpe_device, dev->wakeup.gpe_number); } else { - error = -EALREADY; + acpi_disable_gpe(dev->wakeup.gpe_device, dev->wakeup.gpe_number); + acpi_disable_wakeup_device_power(dev); } return error; diff --git a/include/acpi/acpi_bus.h b/include/acpi/acpi_bus.h index 78ca429929f7..f50ebb9bc53b 100644 --- a/include/acpi/acpi_bus.h +++ b/include/acpi/acpi_bus.h @@ -250,7 +250,6 @@ struct acpi_device_wakeup { struct acpi_handle_list resources; struct acpi_device_wakeup_flags flags; int prepare_count; - int run_wake_count; }; /* Device */ -- cgit v1.2.3 From 64b3db22c04586997ab4be46dd5a5b99f8a2d390 Mon Sep 17 00:00:00 2001 From: Bob Moore Date: Mon, 14 Feb 2011 15:50:42 +0800 Subject: ACPICA: Remove use of unreliable FADT revision field The revision number in the FADT has been found to be completely unreliable and cannot be trusted. Only the table length can be used to infer the actual version. Signed-off-by: Bob Moore Signed-off-by: Lin Ming Signed-off-by: Len Brown --- drivers/acpi/acpica/tbfadt.c | 5 ++++- include/acpi/actbl.h | 16 ++++++++++++++++ 2 files changed, 20 insertions(+), 1 deletion(-) (limited to 'include') diff --git a/drivers/acpi/acpica/tbfadt.c b/drivers/acpi/acpica/tbfadt.c index 428d44e2d162..6f5588e62c0a 100644 --- a/drivers/acpi/acpica/tbfadt.c +++ b/drivers/acpi/acpica/tbfadt.c @@ -384,8 +384,11 @@ static void acpi_tb_convert_fadt(void) * * The ACPI 1.0 reserved fields that will be zeroed are the bytes located at * offset 45, 55, 95, and the word located at offset 109, 110. + * + * Note: The FADT revision value is unreliable. Only the length can be + * trusted. */ - if (acpi_gbl_FADT.header.revision < FADT2_REVISION_ID) { + if (acpi_gbl_FADT.header.length <= ACPI_FADT_V2_SIZE) { acpi_gbl_FADT.preferred_profile = 0; acpi_gbl_FADT.pstate_control = 0; acpi_gbl_FADT.cst_control = 0; diff --git a/include/acpi/actbl.h b/include/acpi/actbl.h index 7e42bfee0e29..d41c94885211 100644 --- a/include/acpi/actbl.h +++ b/include/acpi/actbl.h @@ -343,4 +343,20 @@ struct acpi_table_desc { #include #include +/* + * Sizes of the various flavors of FADT. We need to look closely + * at the FADT length because the version number essentially tells + * us nothing because of many BIOS bugs where the version does not + * match the expected length. In other words, the length of the + * FADT is the bottom line as to what the version really is. + * + * For reference, the values below are as follows: + * FADT V1 size: 0x74 + * FADT V2 size: 0x84 + * FADT V3+ size: 0xF4 + */ +#define ACPI_FADT_V1_SIZE (u32) (ACPI_FADT_OFFSET (flags) + 4) +#define ACPI_FADT_V2_SIZE (u32) (ACPI_FADT_OFFSET (reserved4[0]) + 3) +#define ACPI_FADT_V3_SIZE (u32) (sizeof (struct acpi_table_fadt)) + #endif /* __ACTBL_H__ */ -- cgit v1.2.3 From d59a3c6b35995615fa9e4f21d63fdf21328c5b6b Mon Sep 17 00:00:00 2001 From: Bob Moore Date: Mon, 14 Feb 2011 16:02:36 +0800 Subject: ACPICA: Fix unresolved name issue for no-debug and no-error-msg cases The _acpi_module_name was left undefined in these cases, but it is actually needed as a parameter to some interfaces. Define _acpi_module_name as a null string in these cases. Acpica BZ 888. http://www.acpica.org/bugzilla/show_bug.cgi?id=888 Signed-off-by: Bob Moore Signed-off-by: Lin Ming Signed-off-by: Len Brown --- include/acpi/acoutput.h | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) (limited to 'include') diff --git a/include/acpi/acoutput.h b/include/acpi/acoutput.h index ef1cef77d32b..d7bd661bfae7 100644 --- a/include/acpi/acoutput.h +++ b/include/acpi/acoutput.h @@ -183,13 +183,19 @@ #if defined (ACPI_DEBUG_OUTPUT) || !defined (ACPI_NO_ERROR_MESSAGES) /* - * Module name is included in both debug and non-debug versions primarily for - * error messages. The __FILE__ macro is not very useful for this, because it - * often includes the entire pathname to the module + * The module name is used primarily for error and debug messages. + * The __FILE__ macro is not very useful for this, because it + * usually includes the entire pathname to the module making the + * debug output difficult to read. */ #define ACPI_MODULE_NAME(name) static const char ACPI_UNUSED_VAR _acpi_module_name[] = name; #else +/* + * For the no-debug and no-error-msg cases, we must at least define + * a null module name. + */ #define ACPI_MODULE_NAME(name) +#define _acpi_module_name "" #endif /* -- cgit v1.2.3 From 589c7a39ae2f2b74fd13ae344ca1dcca61da6bca Mon Sep 17 00:00:00 2001 From: Bob Moore Date: Mon, 14 Feb 2011 16:15:21 +0800 Subject: ACPICA: Update version to 20110211. Version 20110211. Signed-off-by: Bob Moore Signed-off-by: Lin Ming Signed-off-by: Len Brown --- include/acpi/acpixf.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'include') diff --git a/include/acpi/acpixf.h b/include/acpi/acpixf.h index e46ec95a8ada..ec908540d346 100644 --- a/include/acpi/acpixf.h +++ b/include/acpi/acpixf.h @@ -47,7 +47,7 @@ /* Current ACPICA subsystem version in YYYYMMDD format */ -#define ACPI_CA_VERSION 0x20110112 +#define ACPI_CA_VERSION 0x20110211 #include "actypes.h" #include "actbl.h" -- cgit v1.2.3 From 885b976fada5bc6595a9fd3e67e3cb1a3d11f50b Mon Sep 17 00:00:00 2001 From: Huang Ying Date: Mon, 21 Feb 2011 13:54:41 +0800 Subject: ACPI, APEI, Add ERST record ID cache APEI ERST firmware interface and implementation has no multiple users in mind. For example, if there is four records in storage with ID: 1, 2, 3 and 4, if two ERST readers enumerate the records via GET_NEXT_RECORD_ID as follow, reader 1 reader 2 1 2 3 4 -1 -1 where -1 signals there is no more record ID. Reader 1 has no chance to check record 2 and 4, while reader 2 has no chance to check record 1 and 3. And any other GET_NEXT_RECORD_ID will return -1, that is, other readers will has no chance to check any record even they are not cleared by anyone. This makes raw GET_NEXT_RECORD_ID not suitable for used by multiple users. To solve the issue, an in-memory ERST record ID cache is designed and implemented. When enumerating record ID, the ID returned by GET_NEXT_RECORD_ID is added into cache in addition to be returned to caller. So other readers can check the cache to get all record ID available. Signed-off-by: Huang Ying Reviewed-by: Andi Kleen Signed-off-by: Len Brown --- arch/x86/kernel/cpu/mcheck/mce-apei.c | 42 +++--- drivers/acpi/apei/erst-dbg.c | 24 +++- drivers/acpi/apei/erst.c | 235 +++++++++++++++++++++++++++------- include/acpi/apei.h | 5 +- 4 files changed, 241 insertions(+), 65 deletions(-) (limited to 'include') diff --git a/arch/x86/kernel/cpu/mcheck/mce-apei.c b/arch/x86/kernel/cpu/mcheck/mce-apei.c index 8209472b27a5..83930deec3c6 100644 --- a/arch/x86/kernel/cpu/mcheck/mce-apei.c +++ b/arch/x86/kernel/cpu/mcheck/mce-apei.c @@ -106,24 +106,34 @@ int apei_write_mce(struct mce *m) ssize_t apei_read_mce(struct mce *m, u64 *record_id) { struct cper_mce_record rcd; - ssize_t len; - - len = erst_read_next(&rcd.hdr, sizeof(rcd)); - if (len <= 0) - return len; - /* Can not skip other records in storage via ERST unless clear them */ - else if (len != sizeof(rcd) || - uuid_le_cmp(rcd.hdr.creator_id, CPER_CREATOR_MCE)) { - if (printk_ratelimit()) - pr_warning( - "MCE-APEI: Can not skip the unknown record in ERST"); - return -EIO; - } - + int rc, pos; + + rc = erst_get_record_id_begin(&pos); + if (rc) + return rc; +retry: + rc = erst_get_record_id_next(&pos, record_id); + if (rc) + goto out; + /* no more record */ + if (*record_id == APEI_ERST_INVALID_RECORD_ID) + goto out; + rc = erst_read(*record_id, &rcd.hdr, sizeof(rcd)); + /* someone else has cleared the record, try next one */ + if (rc == -ENOENT) + goto retry; + else if (rc < 0) + goto out; + /* try to skip other type records in storage */ + else if (rc != sizeof(rcd) || + uuid_le_cmp(rcd.hdr.creator_id, CPER_CREATOR_MCE)) + goto retry; memcpy(m, &rcd.mce, sizeof(*m)); - *record_id = rcd.hdr.record_id; + rc = sizeof(*m); +out: + erst_get_record_id_end(); - return sizeof(*m); + return rc; } /* Check whether there is record in ERST */ diff --git a/drivers/acpi/apei/erst-dbg.c b/drivers/acpi/apei/erst-dbg.c index de73caf3cebc..a4cfb64c86a1 100644 --- a/drivers/acpi/apei/erst-dbg.c +++ b/drivers/acpi/apei/erst-dbg.c @@ -43,12 +43,27 @@ static DEFINE_MUTEX(erst_dbg_mutex); static int erst_dbg_open(struct inode *inode, struct file *file) { + int rc, *pos; + if (erst_disable) return -ENODEV; + pos = (int *)&file->private_data; + + rc = erst_get_record_id_begin(pos); + if (rc) + return rc; + return nonseekable_open(inode, file); } +static int erst_dbg_release(struct inode *inode, struct file *file) +{ + erst_get_record_id_end(); + + return 0; +} + static long erst_dbg_ioctl(struct file *f, unsigned int cmd, unsigned long arg) { int rc; @@ -79,18 +94,20 @@ static long erst_dbg_ioctl(struct file *f, unsigned int cmd, unsigned long arg) static ssize_t erst_dbg_read(struct file *filp, char __user *ubuf, size_t usize, loff_t *off) { - int rc; + int rc, *pos; ssize_t len = 0; u64 id; - if (*off != 0) + if (*off) return -EINVAL; if (mutex_lock_interruptible(&erst_dbg_mutex) != 0) return -EINTR; + pos = (int *)&filp->private_data; + retry_next: - rc = erst_get_next_record_id(&id); + rc = erst_get_record_id_next(pos, &id); if (rc) goto out; /* no more record */ @@ -181,6 +198,7 @@ out: static const struct file_operations erst_dbg_ops = { .owner = THIS_MODULE, .open = erst_dbg_open, + .release = erst_dbg_release, .read = erst_dbg_read, .write = erst_dbg_write, .unlocked_ioctl = erst_dbg_ioctl, diff --git a/drivers/acpi/apei/erst.c b/drivers/acpi/apei/erst.c index cf6db6b7662a..8ff8c32fef58 100644 --- a/drivers/acpi/apei/erst.c +++ b/drivers/acpi/apei/erst.c @@ -429,6 +429,22 @@ ssize_t erst_get_record_count(void) } 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 +459,179 @@ 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_warning(FW_WARN ERST_PFX + "too many record ID!\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; - raw_spin_lock_irqsave(&erst_lock, flags); - rc = __erst_get_next_record_id(record_id); - raw_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) + memcpy(&entries[wpos], &entries[i], sizeof(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) { @@ -703,56 +872,34 @@ ssize_t erst_read(u64 record_id, struct cper_record_header *record, } 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; - - raw_spin_lock_irqsave(&erst_lock, flags); - rc = __erst_get_next_record_id(&record_id); - if (rc) { - raw_spin_unlock_irqrestore(&erst_lock, flags); - return rc; - } - /* no more record */ - if (record_id == APEI_ERST_INVALID_RECORD_ID) { - raw_spin_unlock_irqrestore(&erst_lock, flags); - return 0; - } - - len = __erst_read(record_id, record, buflen); - raw_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; + 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); 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); diff --git a/include/acpi/apei.h b/include/acpi/apei.h index c4dbb132d902..e67b523a50e1 100644 --- a/include/acpi/apei.h +++ b/include/acpi/apei.h @@ -30,10 +30,11 @@ int apei_hest_parse(apei_hest_func_t func, void *data); int erst_write(const struct cper_record_header *record); ssize_t erst_get_record_count(void); -int erst_get_next_record_id(u64 *record_id); +int erst_get_record_id_begin(int *pos); +int erst_get_record_id_next(int *pos, u64 *record_id); +void erst_get_record_id_end(void); ssize_t erst_read(u64 record_id, struct cper_record_header *record, size_t buflen); -ssize_t erst_read_next(struct cper_record_header *record, size_t buflen); int erst_clear(u64 record_id); #endif -- cgit v1.2.3 From c413d7682020a127f54744a1b30f597692aea1fd Mon Sep 17 00:00:00 2001 From: Huang Ying Date: Mon, 21 Feb 2011 13:54:43 +0800 Subject: ACPI, APEI, Add PCIe AER error information printing support The AER error information printing support is implemented in drivers/pci/pcie/aer/aer_print.c. So some string constants, functions and macros definitions can be re-used without being exported. The original PCIe AER error information printing function is not re-used directly because the overall format is quite different. And changing the original printing format may make some original users' scripts broken. Signed-off-by: Huang Ying CC: Jesse Barnes CC: Zhang Yanmin Signed-off-by: Len Brown --- Documentation/acpi/apei/output_format.txt | 25 +++++++++++++ drivers/acpi/apei/Kconfig | 7 ++++ drivers/acpi/apei/cper.c | 18 +++++++--- drivers/pci/pcie/aer/aerdrv.h | 9 +---- drivers/pci/pcie/aer/aerdrv_errprint.c | 59 +++++++++++++++++++++++++++++++ include/linux/aer.h | 24 +++++++++++++ include/linux/cper.h | 2 ++ 7 files changed, 132 insertions(+), 12 deletions(-) (limited to 'include') diff --git a/Documentation/acpi/apei/output_format.txt b/Documentation/acpi/apei/output_format.txt index 9146952c612a..0c49c197c47a 100644 --- a/Documentation/acpi/apei/output_format.txt +++ b/Documentation/acpi/apei/output_format.txt @@ -92,6 +92,11 @@ vendor_id: , device_id: class_code: ] [serial number: , ] [bridge: secondary_status: , control: ] +[aer_status: , aer_mask: + +[aer_uncor_severity: ] +aer_layer=, aer_agent= +aer_tlp_header: ] * := PCIe end point | legacy PCI end point | \ unknown | unknown | root port | upstream switch port | \ @@ -99,6 +104,26 @@ downstream switch port | PCIe to PCI/PCI-X bridge | \ PCI/PCI-X to PCIe bridge | root complex integrated endpoint device | \ root complex event collector +if section severity is fatal or recoverable +# := +unknown | unknown | unknown | unknown | Data Link Protocol | \ +unknown | unknown | unknown | unknown | unknown | unknown | unknown | \ +Poisoned TLP | Flow Control Protocol | Completion Timeout | \ +Completer Abort | Unexpected Completion | Receiver Overflow | \ +Malformed TLP | ECRC | Unsupported Request +else +# := +Receiver Error | unknown | unknown | unknown | unknown | unknown | \ +Bad TLP | Bad DLLP | RELAY_NUM Rollover | unknown | unknown | unknown | \ +Replay Timer Timeout | Advisory Non-Fatal +fi + + := +Physical Layer | Data Link Layer | Transaction Layer + + := +Receiver ID | Requester ID | Completer ID | Transmitter ID + Where, [] designate corresponding content is optional All description with * has the following format: diff --git a/drivers/acpi/apei/Kconfig b/drivers/acpi/apei/Kconfig index fca34ccfd294..9ecf6feae830 100644 --- a/drivers/acpi/apei/Kconfig +++ b/drivers/acpi/apei/Kconfig @@ -21,6 +21,13 @@ config ACPI_APEI_GHES by firmware to produce more valuable hardware error information for Linux. +config ACPI_APEI_PCIEAER + bool "APEI PCIe AER logging/recovering support" + depends on ACPI_APEI && PCIEAER + help + PCIe AER errors may be reported via APEI firmware first mode. + Turn on this option to enable the corresponding support. + config ACPI_APEI_EINJ tristate "APEI Error INJection (EINJ)" depends on ACPI_APEI && DEBUG_FS diff --git a/drivers/acpi/apei/cper.c b/drivers/acpi/apei/cper.c index 31464a006d76..5d4189464d63 100644 --- a/drivers/acpi/apei/cper.c +++ b/drivers/acpi/apei/cper.c @@ -29,6 +29,7 @@ #include #include #include +#include /* * CPER record ID need to be unique even after reboot, because record @@ -70,8 +71,8 @@ static const char *cper_severity_str(unsigned int severity) * If the output length is longer than 80, multiple line will be * printed, with @pfx is printed at the beginning of each line. */ -static void cper_print_bits(const char *pfx, unsigned int bits, - const char *strs[], unsigned int strs_size) +void cper_print_bits(const char *pfx, unsigned int bits, + const char *strs[], unsigned int strs_size) { int i, len = 0; const char *str; @@ -81,6 +82,8 @@ static void cper_print_bits(const char *pfx, unsigned int bits, if (!(bits & (1U << i))) continue; str = strs[i]; + if (!str) + continue; if (len && len + strlen(str) + 2 > 80) { printk("%s\n", buf); len = 0; @@ -243,7 +246,8 @@ static const char *cper_pcie_port_type_strs[] = { "root complex event collector", }; -static void cper_print_pcie(const char *pfx, const struct cper_sec_pcie *pcie) +static void cper_print_pcie(const char *pfx, const struct cper_sec_pcie *pcie, + const struct acpi_hest_generic_data *gdata) { if (pcie->validation_bits & CPER_PCIE_VALID_PORT_TYPE) printk("%s""port_type: %d, %s\n", pfx, pcie->port_type, @@ -276,6 +280,12 @@ static void cper_print_pcie(const char *pfx, const struct cper_sec_pcie *pcie) printk( "%s""bridge: secondary_status: 0x%04x, control: 0x%04x\n", pfx, pcie->bridge.secondary_status, pcie->bridge.control); +#ifdef CONFIG_ACPI_APEI_PCIEAER + if (pcie->validation_bits & CPER_PCIE_VALID_AER_INFO) { + struct aer_capability_regs *aer_regs = (void *)pcie->aer_info; + cper_print_aer(pfx, gdata->error_severity, aer_regs); + } +#endif } static const char *apei_estatus_section_flag_strs[] = { @@ -322,7 +332,7 @@ static void apei_estatus_print_section( struct cper_sec_pcie *pcie = (void *)(gdata + 1); printk("%s""section_type: PCIe error\n", pfx); if (gdata->error_data_length >= sizeof(*pcie)) - cper_print_pcie(pfx, pcie); + cper_print_pcie(pfx, pcie, gdata); else goto err_section_too_small; } else diff --git a/drivers/pci/pcie/aer/aerdrv.h b/drivers/pci/pcie/aer/aerdrv.h index 80c11d131499..3eb77080366a 100644 --- a/drivers/pci/pcie/aer/aerdrv.h +++ b/drivers/pci/pcie/aer/aerdrv.h @@ -35,13 +35,6 @@ PCI_ERR_UNC_UNX_COMP| \ PCI_ERR_UNC_MALF_TLP) -struct header_log_regs { - unsigned int dw0; - unsigned int dw1; - unsigned int dw2; - unsigned int dw3; -}; - #define AER_MAX_MULTI_ERR_DEVICES 5 /* Not likely to have more */ struct aer_err_info { struct pci_dev *dev[AER_MAX_MULTI_ERR_DEVICES]; @@ -59,7 +52,7 @@ struct aer_err_info { unsigned int status; /* COR/UNCOR Error Status */ unsigned int mask; /* COR/UNCOR Error Mask */ - struct header_log_regs tlp; /* TLP Header */ + struct aer_header_log_regs tlp; /* TLP Header */ }; struct aer_err_source { diff --git a/drivers/pci/pcie/aer/aerdrv_errprint.c b/drivers/pci/pcie/aer/aerdrv_errprint.c index 7a237f67129e..b07a42e0b350 100644 --- a/drivers/pci/pcie/aer/aerdrv_errprint.c +++ b/drivers/pci/pcie/aer/aerdrv_errprint.c @@ -19,6 +19,7 @@ #include #include #include +#include #include "aerdrv.h" @@ -201,3 +202,61 @@ void aer_print_port_info(struct pci_dev *dev, struct aer_err_info *info) info->multi_error_valid ? "Multiple " : "", aer_error_severity_string[info->severity], info->id); } + +#ifdef CONFIG_ACPI_APEI_PCIEAER +static int cper_severity_to_aer(int cper_severity) +{ + switch (cper_severity) { + case CPER_SEV_RECOVERABLE: + return AER_NONFATAL; + case CPER_SEV_FATAL: + return AER_FATAL; + default: + return AER_CORRECTABLE; + } +} + +void cper_print_aer(const char *prefix, int cper_severity, + struct aer_capability_regs *aer) +{ + int aer_severity, layer, agent, status_strs_size, tlp_header_valid = 0; + u32 status, mask; + const char **status_strs; + + aer_severity = cper_severity_to_aer(cper_severity); + if (aer_severity == AER_CORRECTABLE) { + status = aer->cor_status; + mask = aer->cor_mask; + status_strs = aer_correctable_error_string; + status_strs_size = ARRAY_SIZE(aer_correctable_error_string); + } else { + status = aer->uncor_status; + mask = aer->uncor_mask; + status_strs = aer_uncorrectable_error_string; + status_strs_size = ARRAY_SIZE(aer_uncorrectable_error_string); + tlp_header_valid = status & AER_LOG_TLP_MASKS; + } + layer = AER_GET_LAYER_ERROR(aer_severity, status); + agent = AER_GET_AGENT(aer_severity, status); + printk("%s""aer_status: 0x%08x, aer_mask: 0x%08x\n", + prefix, status, mask); + cper_print_bits(prefix, status, status_strs, status_strs_size); + printk("%s""aer_layer=%s, aer_agent=%s\n", prefix, + aer_error_layer[layer], aer_agent_string[agent]); + if (aer_severity != AER_CORRECTABLE) + printk("%s""aer_uncor_severity: 0x%08x\n", + prefix, aer->uncor_severity); + if (tlp_header_valid) { + const unsigned char *tlp; + tlp = (const unsigned char *)&aer->header_log; + printk("%s""aer_tlp_header:" + " %02x%02x%02x%02x %02x%02x%02x%02x" + " %02x%02x%02x%02x %02x%02x%02x%02x\n", + prefix, *(tlp + 3), *(tlp + 2), *(tlp + 1), *tlp, + *(tlp + 7), *(tlp + 6), *(tlp + 5), *(tlp + 4), + *(tlp + 11), *(tlp + 10), *(tlp + 9), + *(tlp + 8), *(tlp + 15), *(tlp + 14), + *(tlp + 13), *(tlp + 12)); + } +} +#endif diff --git a/include/linux/aer.h b/include/linux/aer.h index f7df1eefc107..8414de22a779 100644 --- a/include/linux/aer.h +++ b/include/linux/aer.h @@ -7,6 +7,28 @@ #ifndef _AER_H_ #define _AER_H_ +struct aer_header_log_regs { + unsigned int dw0; + unsigned int dw1; + unsigned int dw2; + unsigned int dw3; +}; + +struct aer_capability_regs { + u32 header; + u32 uncor_status; + u32 uncor_mask; + u32 uncor_severity; + u32 cor_status; + u32 cor_mask; + u32 cap_control; + struct aer_header_log_regs header_log; + u32 root_command; + u32 root_status; + u16 cor_err_source; + u16 uncor_err_source; +}; + #if defined(CONFIG_PCIEAER) /* pci-e port driver needs this function to enable aer */ extern int pci_enable_pcie_error_reporting(struct pci_dev *dev); @@ -27,5 +49,7 @@ static inline int pci_cleanup_aer_uncorrect_error_status(struct pci_dev *dev) } #endif +extern void cper_print_aer(const char *prefix, int cper_severity, + struct aer_capability_regs *aer); #endif //_AER_H_ diff --git a/include/linux/cper.h b/include/linux/cper.h index 3104aaff5dd0..372a25839fd1 100644 --- a/include/linux/cper.h +++ b/include/linux/cper.h @@ -388,5 +388,7 @@ struct cper_sec_pcie { #pragma pack() u64 cper_next_record_id(void); +void cper_print_bits(const char *prefix, unsigned int bits, + const char *strs[], unsigned int strs_size); #endif -- cgit v1.2.3 From 5cf4d7338ba23aa8d9eeaf313f16bb50494be369 Mon Sep 17 00:00:00 2001 From: Bob Moore Date: Fri, 18 Mar 2011 09:49:41 +0800 Subject: ACPICA: Header support for SLIC table We finally have the definition for this table. Signed-off-by: Bob Moore Signed-off-by: Lin Ming Signed-off-by: Len Brown --- include/acpi/actbl2.h | 64 ++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 63 insertions(+), 1 deletion(-) (limited to 'include') diff --git a/include/acpi/actbl2.h b/include/acpi/actbl2.h index 0fc15dfb2e22..58bdd0545c5a 100644 --- a/include/acpi/actbl2.h +++ b/include/acpi/actbl2.h @@ -1,6 +1,6 @@ /****************************************************************************** * - * Name: actbl2.h - ACPI Specification Revision 2.0 Tables + * Name: actbl2.h - ACPI Table Definitions (tables not in ACPI spec) * *****************************************************************************/ @@ -714,6 +714,68 @@ struct acpi_table_mchi { u8 pci_function; }; +/******************************************************************************* + * + * SLIC - Software Licensing Description Table + * Version 1 + * + * Conforms to "OEM Activation 2.0 for Windows Vista Operating Systems", + * Copyright 2006 + * + ******************************************************************************/ + +/* Basic SLIC table is only the common ACPI header */ + +struct acpi_table_slic { + struct acpi_table_header header; /* Common ACPI table header */ +}; + +/* Common SLIC subtable header */ + +struct acpi_slic_header { + u32 type; + u32 length; +}; + +/* Values for Type field above */ + +enum acpi_slic_type { + ACPI_SLIC_TYPE_PUBLIC_KEY = 0, + ACPI_SLIC_TYPE_WINDOWS_MARKER = 1, + ACPI_SLIC_TYPE_RESERVED = 2 /* 2 and greater are reserved */ +}; + +/* + * SLIC Sub-tables, correspond to Type in struct acpi_slic_header + */ + +/* 0: Public Key Structure */ + +struct acpi_slic_key { + struct acpi_slic_header header; + u8 key_type; + u8 version; + u16 reserved; + u32 algorithm; + char magic[4]; + u32 bit_length; + u32 exponent; + u8 modulus[128]; +}; + +/* 1: Windows Marker Structure */ + +struct acpi_slic_marker { + struct acpi_slic_header header; + u32 version; + char oem_id[ACPI_OEM_ID_SIZE]; /* ASCII OEM identification */ + char oem_table_id[ACPI_OEM_TABLE_ID_SIZE]; /* ASCII OEM table identification */ + char windows_flag[8]; + u32 slic_version; + u8 reserved[16]; + u8 signature[128]; +}; + /******************************************************************************* * * SPCR - Serial Port Console Redirection table -- cgit v1.2.3 From d8d75b0ee6dbd65c8a2189aef50a03667d81c937 Mon Sep 17 00:00:00 2001 From: Bob Moore Date: Tue, 22 Mar 2011 11:27:29 +0800 Subject: ACPICA: Update version to 20110316 Version 20110316. Signed-off-by: Bob Moore Signed-off-by: Lin Ming Signed-off-by: Len Brown --- include/acpi/acpixf.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'include') diff --git a/include/acpi/acpixf.h b/include/acpi/acpixf.h index ec908540d346..f6ad63d25b73 100644 --- a/include/acpi/acpixf.h +++ b/include/acpi/acpixf.h @@ -47,7 +47,7 @@ /* Current ACPICA subsystem version in YYYYMMDD format */ -#define ACPI_CA_VERSION 0x20110211 +#define ACPI_CA_VERSION 0x20110316 #include "actypes.h" #include "actbl.h" -- cgit v1.2.3