Commit 39f26d10 authored by Rafael J. Wysocki's avatar Rafael J. Wysocki
Browse files

Merge branches 'acpi-apei', 'acpi-x86', 'acpi-battery' and 'acpi-pfrut'

Make ACPI APEI updates, x86-specific ACPI updates, ACPI battery driver
fix and ACPI PFRU/T driver fixes for 6.2-rc1:

 - Drop unsetting ACPI APEI driver data on remove (Uwe Kleine-König).

 - Use xchg_release() instead of cmpxchg() for updating new GHES cache
   slots (Ard Biesheuvel).

 - Clean up the ACPI APEI code (Sudeep Holla, Christophe JAILLET, Jay Lu).

 - Add new I2C device enumeration quirks for Medion Lifetab S10346 and
   Lenovo Yoga Tab 3 Pro (YT3-X90F) (Hans de Goede).

 - Make the ACPI battery driver notify user space about adding new
   battery hooks and removing the existing ones (Armin Wolf).

 - Modify the pfr_update and pfr_telemetry drivers to use ACPI_FREE()
   for freeing acpi_object structures to help diagnostics (Wang ShaoBo).

* acpi-apei:
  ACPI: APEI: EINJ: Refactor available_error_type_show()
  ACPI: APEI: EINJ: Fix formatting errors
  ACPI: APEI: Remove a useless include
  ACPI: APEI: Silence missing prototype warnings
  apei/ghes: Use xchg_release() for updating new cache slot instead of cmpxchg()
  ACPI: APEI: Drop unsetting driver data on remove

* acpi-x86:
  ACPI: x86: Add skip i2c clients quirk for Medion Lifetab S10346
  ACPI: x86: Add skip i2c clients quirk for Lenovo Yoga Tab 3 Pro (YT3-X90F)

* acpi-battery:
  ACPI: battery: Call power_supply_changed() when adding hooks

* acpi-pfrut:
  ACPI: pfr_update: use ACPI_FREE() to free acpi_object
  ACPI: pfr_telemetry: use ACPI_FREE() to free acpi_object
Loading
Loading
Loading
Loading
+1 −1
Original line number Diff line number Diff line
@@ -25,9 +25,9 @@
#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"
+25 −31
Original line number Diff line number Diff line
@@ -358,6 +358,7 @@ static int __einj_error_trigger(u64 trigger_paddr, u32 type,
	 */
	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);
@@ -466,6 +467,7 @@ static int __einj_error_inject(u32 type, u32 flags, u64 param1, u64 param2,
			return rc;
		if (einj_param) {
			struct einj_parameter *v4param = einj_param;

			v4param->param1 = param1;
			v4param->param2 = param2;
		}
@@ -569,6 +571,20 @@ static u64 error_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)
{
@@ -578,30 +594,9 @@ 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;
}
@@ -689,8 +684,7 @@ static int __init einj_init(void)
	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;
+33 −29
Original line number Diff line number Diff line
@@ -138,7 +138,7 @@ struct ghes_vendor_record_entry {
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;
@@ -773,48 +773,42 @@ static struct ghes_estatus_cache *ghes_estatus_cache_alloc(
	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,
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);
@@ -823,18 +817,30 @@ static void ghes_estatus_cache_add(
		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,
@@ -1444,8 +1450,6 @@ static int ghes_remove(struct platform_device *ghes_dev)

	kfree(ghes);

	platform_set_drvdata(ghes_dev, NULL);

	return 0;
}

+4 −1
Original line number Diff line number Diff line
@@ -696,7 +696,8 @@ static void __battery_hook_unregister(struct acpi_battery_hook *hook, int lock)
	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)
@@ -735,6 +736,8 @@ void battery_hook_register(struct acpi_battery_hook *hook)
			__battery_hook_unregister(hook, 0);
			goto end;
		}

		power_supply_changed(battery->bat);
	}
	pr_info("new extension: %s\n", hook->name);
end:
+3 −3
Original line number Diff line number Diff line
@@ -144,7 +144,7 @@ static int get_pfrt_log_data_info(struct pfrt_log_data_info *data_info,
	ret = 0;

free_acpi_buffer:
	kfree(out_obj);
	ACPI_FREE(out_obj);

	return ret;
}
@@ -180,7 +180,7 @@ static int set_pfrt_log_level(int level, struct pfrt_log_device *pfrt_log_dev)
		ret = -EBUSY;
	}

	kfree(out_obj);
	ACPI_FREE(out_obj);

	return ret;
}
@@ -218,7 +218,7 @@ static int get_pfrt_log_level(struct pfrt_log_device *pfrt_log_dev)
	ret = obj->integer.value;

free_acpi_buffer:
	kfree(out_obj);
	ACPI_FREE(out_obj);

	return ret;
}
Loading