Merge branches 'acpi-apei', 'acpi-x86', 'acpi-battery' and 'acpi-pfrut'
authorRafael J. Wysocki <rafael.j.wysocki@intel.com>
Mon, 12 Dec 2022 14:13:52 +0000 (15:13 +0100)
committerRafael J. Wysocki <rafael.j.wysocki@intel.com>
Mon, 12 Dec 2022 14:13:52 +0000 (15:13 +0100)
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

drivers/acpi/apei/apei-base.c
drivers/acpi/apei/einj.c
drivers/acpi/apei/ghes.c
drivers/acpi/battery.c
drivers/acpi/pfr_telemetry.c
drivers/acpi/pfr_update.c
drivers/acpi/x86/utils.c

index 9b52482b4ed5ee7ddec61556c9b6ae540a9b1989..c7c26872f4cec11a3be409204b25e54bd727b482 100644 (file)
@@ -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"
index 6b583373c58a246819e46c8e8715fdaf3c608289..ab86b2f4e719f5fd409709ca1e7d12ae1c12b94a 100644 (file)
@@ -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);
@@ -432,11 +433,11 @@ static int __einj_error_inject(u32 type, u32 flags, u64 param1, u64 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:
@@ -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;
index 9952f3a792bad1070536932cac07271440777472..94c4872ae55ffc11f779baec5c6d786c90b655b2 100644 (file)
@@ -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,
-       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);
@@ -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;
 }
 
index 28b0d1d6e225bc1ba869e460bcea475d48b064b8..883c75757400eb8c097b2d15785b0ca6d253b124 100644 (file)
@@ -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:
index 9abf350bd7a5a28cc63d8960c395b02a0482b240..27fb6cdad75f978f055e87301c2540c576c8882a 100644 (file)
@@ -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;
 }
index 6bb0b778b5da527025772478e0f6d3113bac2607..9d2bdc13253a526c4008ec9af33d54de24ac747d 100644 (file)
@@ -178,7 +178,7 @@ static int query_capability(struct pfru_update_cap_info *cap_hdr,
        ret = 0;
 
 free_acpi_buffer:
-       kfree(out_obj);
+       ACPI_FREE(out_obj);
 
        return ret;
 }
@@ -224,7 +224,7 @@ static int query_buffer(struct pfru_com_buf_info *info,
        ret = 0;
 
 free_acpi_buffer:
-       kfree(out_obj);
+       ACPI_FREE(out_obj);
 
        return ret;
 }
@@ -385,7 +385,7 @@ static int start_update(int action, struct pfru_device *pfru_dev)
        ret = 0;
 
 free_acpi_buffer:
-       kfree(out_obj);
+       ACPI_FREE(out_obj);
 
        return ret;
 }
index d7d3f1669d4c0a7a94dc855740c1dabc17f2f8fb..4e816bb402f68cce61dd4272a3b57310515e1155 100644 (file)
@@ -308,7 +308,7 @@ static const struct dmi_system_id acpi_quirk_skip_dmi_ids[] = {
                                        ACPI_QUIRK_SKIP_ACPI_AC_AND_BATTERY),
        },
        {
-               /* Lenovo Yoga Tablet 1050F/L */
+               /* Lenovo Yoga Tablet 1050F/L */
                .matches = {
                        DMI_MATCH(DMI_SYS_VENDOR, "Intel Corp."),
                        DMI_MATCH(DMI_PRODUCT_NAME, "VALLEYVIEW C0 PLATFORM"),
@@ -319,6 +319,27 @@ static const struct dmi_system_id acpi_quirk_skip_dmi_ids[] = {
                .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 = {
@@ -348,6 +369,7 @@ static const struct acpi_device_id i2c_acpi_known_good_ids[] = {
        { "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 */
        {}
 };