#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/apei.h>
#include <asm/unaligned.h>
#include "apei-internal.h"
*/
if ((param_extension || acpi5) && (type & MEM_ERROR_MASK) && param2) {
struct apei_resources addr_resources;
+
apei_resources_init(&addr_resources);
trigger_param_region = einj_get_trigger_parameter_region(
trigger_tab, param1, param2);
}
v5param->flags = vendor_flags;
} else if (flags) {
- v5param->flags = flags;
- v5param->memory_address = param1;
- v5param->memory_address_range = param2;
- v5param->apicid = param3;
- v5param->pcie_sbdf = param4;
+ v5param->flags = flags;
+ v5param->memory_address = param1;
+ v5param->memory_address_range = param2;
+ v5param->apicid = param3;
+ v5param->pcie_sbdf = param4;
} else {
switch (type) {
case ACPI_EINJ_PROCESSOR_CORRECTABLE:
return rc;
if (einj_param) {
struct einj_parameter *v4param = einj_param;
+
v4param->param1 = param1;
v4param->param2 = param2;
}
static u64 error_param3;
static u64 error_param4;
static struct dentry *einj_debug_dir;
+static const char * const einj_error_type_string[] = {
+ "0x00000001\tProcessor Correctable\n",
+ "0x00000002\tProcessor Uncorrectable non-fatal\n",
+ "0x00000004\tProcessor Uncorrectable fatal\n",
+ "0x00000008\tMemory Correctable\n",
+ "0x00000010\tMemory Uncorrectable non-fatal\n",
+ "0x00000020\tMemory Uncorrectable fatal\n",
+ "0x00000040\tPCI Express Correctable\n",
+ "0x00000080\tPCI Express Uncorrectable non-fatal\n",
+ "0x00000100\tPCI Express Uncorrectable fatal\n",
+ "0x00000200\tPlatform Correctable\n",
+ "0x00000400\tPlatform Uncorrectable non-fatal\n",
+ "0x00000800\tPlatform Uncorrectable fatal\n",
+};
static int available_error_type_show(struct seq_file *m, void *v)
{
rc = einj_get_available_error_type(&available_error_type);
if (rc)
return rc;
- if (available_error_type & 0x0001)
- seq_printf(m, "0x00000001\tProcessor Correctable\n");
- if (available_error_type & 0x0002)
- seq_printf(m, "0x00000002\tProcessor Uncorrectable non-fatal\n");
- if (available_error_type & 0x0004)
- seq_printf(m, "0x00000004\tProcessor Uncorrectable fatal\n");
- if (available_error_type & 0x0008)
- seq_printf(m, "0x00000008\tMemory Correctable\n");
- if (available_error_type & 0x0010)
- seq_printf(m, "0x00000010\tMemory Uncorrectable non-fatal\n");
- if (available_error_type & 0x0020)
- seq_printf(m, "0x00000020\tMemory Uncorrectable fatal\n");
- if (available_error_type & 0x0040)
- seq_printf(m, "0x00000040\tPCI Express Correctable\n");
- if (available_error_type & 0x0080)
- seq_printf(m, "0x00000080\tPCI Express Uncorrectable non-fatal\n");
- if (available_error_type & 0x0100)
- seq_printf(m, "0x00000100\tPCI Express Uncorrectable fatal\n");
- if (available_error_type & 0x0200)
- seq_printf(m, "0x00000200\tPlatform Correctable\n");
- if (available_error_type & 0x0400)
- seq_printf(m, "0x00000400\tPlatform Uncorrectable non-fatal\n");
- if (available_error_type & 0x0800)
- seq_printf(m, "0x00000800\tPlatform Uncorrectable fatal\n");
+ for (int pos = 0; pos < ARRAY_SIZE(einj_error_type_string); pos++)
+ if (available_error_type & BIT(pos))
+ seq_puts(m, einj_error_type_string[pos]);
return 0;
}
if (status == AE_NOT_FOUND) {
pr_warn("EINJ table not found.\n");
return -ENODEV;
- }
- else if (ACPI_FAILURE(status)) {
+ } else if (ACPI_FAILURE(status)) {
pr_err("Failed to get EINJ table: %s\n",
acpi_format_exception(status));
return -EINVAL;
static struct gen_pool *ghes_estatus_pool;
static unsigned long ghes_estatus_pool_size_request;
-static struct ghes_estatus_cache *ghes_estatus_caches[GHES_ESTATUS_CACHES_SIZE];
+static struct ghes_estatus_cache __rcu *ghes_estatus_caches[GHES_ESTATUS_CACHES_SIZE];
static atomic_t ghes_estatus_cache_alloced;
static int ghes_panic_timeout __read_mostly = 30;
return cache;
}
-static void ghes_estatus_cache_free(struct ghes_estatus_cache *cache)
+static void ghes_estatus_cache_rcu_free(struct rcu_head *head)
{
+ struct ghes_estatus_cache *cache;
u32 len;
+ cache = container_of(head, struct ghes_estatus_cache, rcu);
len = cper_estatus_len(GHES_ESTATUS_FROM_CACHE(cache));
len = GHES_ESTATUS_CACHE_LEN(len);
gen_pool_free(ghes_estatus_pool, (unsigned long)cache, len);
atomic_dec(&ghes_estatus_cache_alloced);
}
-static void ghes_estatus_cache_rcu_free(struct rcu_head *head)
-{
- struct ghes_estatus_cache *cache;
-
- cache = container_of(head, struct ghes_estatus_cache, rcu);
- ghes_estatus_cache_free(cache);
-}
-
-static void ghes_estatus_cache_add(
- struct acpi_hest_generic *generic,
- struct acpi_hest_generic_status *estatus)
+static void
+ghes_estatus_cache_add(struct acpi_hest_generic *generic,
+ struct acpi_hest_generic_status *estatus)
{
- int i, slot = -1, count;
unsigned long long now, duration, period, max_period = 0;
- struct ghes_estatus_cache *cache, *slot_cache = NULL, *new_cache;
+ struct ghes_estatus_cache *cache, *new_cache;
+ struct ghes_estatus_cache __rcu *victim;
+ int i, slot = -1, count;
new_cache = ghes_estatus_cache_alloc(generic, estatus);
- if (new_cache == NULL)
+ if (!new_cache)
return;
+
rcu_read_lock();
now = sched_clock();
for (i = 0; i < GHES_ESTATUS_CACHES_SIZE; i++) {
cache = rcu_dereference(ghes_estatus_caches[i]);
if (cache == NULL) {
slot = i;
- slot_cache = NULL;
break;
}
duration = now - cache->time_in;
if (duration >= GHES_ESTATUS_IN_CACHE_MAX_NSEC) {
slot = i;
- slot_cache = cache;
break;
}
count = atomic_read(&cache->count);
if (period > max_period) {
max_period = period;
slot = i;
- slot_cache = cache;
}
}
- /* new_cache must be put into array after its contents are written */
- smp_wmb();
- if (slot != -1 && cmpxchg(ghes_estatus_caches + slot,
- slot_cache, new_cache) == slot_cache) {
- if (slot_cache)
- call_rcu(&slot_cache->rcu, ghes_estatus_cache_rcu_free);
- } else
- ghes_estatus_cache_free(new_cache);
rcu_read_unlock();
+
+ if (slot != -1) {
+ /*
+ * Use release semantics to ensure that ghes_estatus_cached()
+ * running on another CPU will see the updated cache fields if
+ * it can see the new value of the pointer.
+ */
+ victim = xchg_release(&ghes_estatus_caches[slot],
+ RCU_INITIALIZER(new_cache));
+
+ /*
+ * At this point, victim may point to a cached item different
+ * from the one based on which we selected the slot. Instead of
+ * going to the loop again to pick another slot, let's just
+ * drop the other item anyway: this may cause a false cache
+ * miss later on, but that won't cause any problems.
+ */
+ if (victim)
+ call_rcu(&unrcu_pointer(victim)->rcu,
+ ghes_estatus_cache_rcu_free);
+ }
}
static void __ghes_panic(struct ghes *ghes,
kfree(ghes);
- platform_set_drvdata(ghes_dev, NULL);
-
return 0;
}
if (lock)
mutex_lock(&hook_mutex);
list_for_each_entry(battery, &acpi_battery_list, list) {
- hook->remove_battery(battery->bat);
+ if (!hook->remove_battery(battery->bat))
+ power_supply_changed(battery->bat);
}
list_del(&hook->list);
if (lock)
__battery_hook_unregister(hook, 0);
goto end;
}
+
+ power_supply_changed(battery->bat);
}
pr_info("new extension: %s\n", hook->name);
end:
ret = 0;
free_acpi_buffer:
- kfree(out_obj);
+ ACPI_FREE(out_obj);
return ret;
}
ret = -EBUSY;
}
- kfree(out_obj);
+ ACPI_FREE(out_obj);
return ret;
}
ret = obj->integer.value;
free_acpi_buffer:
- kfree(out_obj);
+ ACPI_FREE(out_obj);
return ret;
}
ret = 0;
free_acpi_buffer:
- kfree(out_obj);
+ ACPI_FREE(out_obj);
return ret;
}
ret = 0;
free_acpi_buffer:
- kfree(out_obj);
+ ACPI_FREE(out_obj);
return ret;
}
ret = 0;
free_acpi_buffer:
- kfree(out_obj);
+ ACPI_FREE(out_obj);
return ret;
}
ACPI_QUIRK_SKIP_ACPI_AC_AND_BATTERY),
},
{
- /* Lenovo Yoga Tablet 1050F/L */
+ /* Lenovo Yoga Tablet 2 1050F/L */
.matches = {
DMI_MATCH(DMI_SYS_VENDOR, "Intel Corp."),
DMI_MATCH(DMI_PRODUCT_NAME, "VALLEYVIEW C0 PLATFORM"),
.driver_data = (void *)(ACPI_QUIRK_SKIP_I2C_CLIENTS |
ACPI_QUIRK_SKIP_ACPI_AC_AND_BATTERY),
},
+ {
+ /* Lenovo Yoga Tab 3 Pro X90F */
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Intel Corporation"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "CHERRYVIEW D1 PLATFORM"),
+ DMI_MATCH(DMI_PRODUCT_VERSION, "Blade3-10A-001"),
+ },
+ .driver_data = (void *)(ACPI_QUIRK_SKIP_I2C_CLIENTS |
+ ACPI_QUIRK_SKIP_ACPI_AC_AND_BATTERY),
+ },
+ {
+ /* Medion Lifetab S10346 */
+ .matches = {
+ DMI_MATCH(DMI_BOARD_VENDOR, "AMI Corporation"),
+ DMI_MATCH(DMI_BOARD_NAME, "Aptio CRB"),
+ /* Way too generic, also match on BIOS data */
+ DMI_MATCH(DMI_BIOS_DATE, "10/22/2015"),
+ },
+ .driver_data = (void *)(ACPI_QUIRK_SKIP_I2C_CLIENTS |
+ ACPI_QUIRK_SKIP_ACPI_AC_AND_BATTERY),
+ },
{
/* Nextbook Ares 8 */
.matches = {
{ "10EC5640", 0 }, /* RealTek ALC5640 audio codec */
{ "INT33F4", 0 }, /* X-Powers AXP288 PMIC */
{ "INT33FD", 0 }, /* Intel Crystal Cove PMIC */
+ { "INT34D3", 0 }, /* Intel Whiskey Cove PMIC */
{ "NPCE69A", 0 }, /* Asus Transformer keyboard dock */
{}
};