hulk inclusion category: bugfix bugzilla: https://atomgit.com/src-openeuler/kernel/issues/20607 ----------------------------------------------- The parallelization of hbm on and off can cause problems, such as the abnormal state of acpi device. Force paralleize the routine of power on/off the hbm device, which can fix various the abnormal state of acpi devices. Fixes: 039ec3f73992 ("soc: hisilicon: hisi_hbmdev: Add power domain control methods") Signed-off-by: Zhang Zekun <zhangzekun11@huawei.com> Conflicts: drivers/soc/hisilicon/hisi_hbmdev.c [context conflict: e6bdbcc764af822ff7172a4a78d437dc433a0c74] Signed-off-by: Ziming Du <duziming2@huawei.com> --- drivers/acpi/device_sysfs.c | 8 +++ drivers/acpi/osl.c | 5 ++ drivers/soc/hisilicon/hisi_hbmdev.c | 90 ++++++++++++++++++++--------- include/linux/acpi.h | 2 + 4 files changed, 77 insertions(+), 28 deletions(-) diff --git a/drivers/acpi/device_sysfs.c b/drivers/acpi/device_sysfs.c index eae6016ad2825..40db928064e62 100644 --- a/drivers/acpi/device_sysfs.c +++ b/drivers/acpi/device_sysfs.c @@ -366,6 +366,11 @@ static ssize_t power_state_show(struct device *dev, static DEVICE_ATTR_RO(power_state); +static struct acpi_platform_list no_eject_plat_info[] = { + {"HISI ", "HIP11 ", 0, ACPI_SIG_IORT, all_versions, NULL, 0}, + { } +}; + static ssize_t eject_store(struct device *d, struct device_attribute *attr, const char *buf, size_t count) @@ -385,6 +390,9 @@ eject_store(struct device *d, struct device_attribute *attr, if (ACPI_FAILURE(status) || !acpi_device->flags.ejectable) return -ENODEV; + if (acpi_match_platform_list(no_eject_plat_info) >= 0) + return -ENODEV; + acpi_dev_get(acpi_device); status = acpi_hotplug_schedule(acpi_device, ACPI_OST_EC_OSPM_EJECT); if (ACPI_SUCCESS(status)) diff --git a/drivers/acpi/osl.c b/drivers/acpi/osl.c index 28527d246fc36..851af01a00cc3 100644 --- a/drivers/acpi/osl.c +++ b/drivers/acpi/osl.c @@ -1196,6 +1196,11 @@ bool acpi_queue_hotplug_work(struct work_struct *work) return queue_work(kacpi_hotplug_wq, work); } +void flush_hotplug_workqueue(void) +{ + flush_workqueue(kacpi_hotplug_wq); +} + acpi_status acpi_os_create_semaphore(u32 max_units, u32 initial_units, acpi_handle * handle) { diff --git a/drivers/soc/hisilicon/hisi_hbmdev.c b/drivers/soc/hisilicon/hisi_hbmdev.c index 778a9cc1450a8..73f691902466f 100644 --- a/drivers/soc/hisilicon/hisi_hbmdev.c +++ b/drivers/soc/hisilicon/hisi_hbmdev.c @@ -33,6 +33,7 @@ struct memory_dev { nodemask_t cluster_cpumask[MAX_NUMNODES]; }; +static struct mutex dev_lock; static struct memory_dev *mdev; static ssize_t memory_locality_show(struct kobject *kobj, @@ -250,21 +251,6 @@ static ssize_t pxms_show(struct device *dev, } static DEVICE_ATTR_RO(pxms); -static int memdev_power_on(struct acpi_device *adev) -{ - acpi_handle handle = adev->handle; - acpi_status status; - - /* Power on and online the devices */ - status = acpi_evaluate_object(handle, "_ON", NULL, NULL); - if (ACPI_FAILURE(status)) { - acpi_handle_warn(handle, "Power on failed (0x%x)\n", status); - return -ENODEV; - } - - return 0; -} - static int hbmdev_check(struct acpi_device *adev, void *arg) { const char *hid = acpi_device_hid(adev); @@ -275,34 +261,71 @@ static int hbmdev_check(struct acpi_device *adev, void *arg) *found = true; return -1; } + } - /* There might be devices have not attached */ - if (!adev->handler) - return 0; + return 0; +} - adev->handler->hotplug.demand_offline = true; - } +static int check_memdev_status(struct acpi_device *child, void *data) +{ + unsigned long long sta = 0; + acpi_status status; + + status = acpi_evaluate_integer(child->handle, "_STA", NULL, &sta); + if (ACPI_FAILURE(status) || (sta & ACPI_STA_DEVICE_ENABLED)) + return -EINVAL; return 0; } -static int memdev_power_off(struct acpi_device *adev) +static int memdev_power_on(struct acpi_device *adev) { acpi_handle handle = adev->handle; acpi_status status; + int ret; - acpi_scan_lock_acquire(); - acpi_dev_for_each_child(adev, hbmdev_check, NULL); - acpi_scan_lock_release(); + ret = acpi_dev_for_each_child(adev, check_memdev_status, NULL); + if (ret) + return ret; - /* Eject the devices and power off */ - status = acpi_evaluate_object(handle, "_OFF", NULL, NULL); + /* Power on and online the devices */ + status = acpi_evaluate_object(handle, "_ON", NULL, NULL); if (ACPI_FAILURE(status)) { + acpi_handle_warn(handle, "Power on failed (0x%x)\n", status); return -ENODEV; } + return 0; } +static int schedule_memdev_offline(struct acpi_device *child, void *data) +{ + const char *hid = acpi_device_hid(child); + acpi_status status; + + if (strcmp(hid, ACPI_MEMORY_DEVICE_HID)) + return 0; + + if (!child->handler) + return 0; + + child->handler->hotplug.demand_offline = true; + + get_device(&child->dev); + status = acpi_hotplug_schedule(child, ACPI_OST_EC_OSPM_EJECT); + if (ACPI_SUCCESS(status)) + return 0; + + put_device(&child->dev); + + return 0; +} + +static void memdev_power_off(struct acpi_device *adev) +{ + acpi_dev_for_each_child(adev, schedule_memdev_offline, NULL); +} + static ssize_t state_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { @@ -310,23 +333,33 @@ static ssize_t state_store(struct device *dev, struct device_attribute *attr, const int type = online_type_from_str(buf); int ret; + if (!mutex_trylock(&dev_lock)) + return -EBUSY; + /* Disallow pending on the mutex to avoid potential hung task*/ ret = lock_device_hotplug_sysfs(); - if (ret) + if (ret) { + mutex_unlock(&dev_lock); return ret; + } switch (type) { case STATE_ONLINE: ret = memdev_power_on(adev); break; case STATE_OFFLINE: - ret = memdev_power_off(adev); + memdev_power_off(adev); break; default: break; } + unlock_device_hotplug(); + acpi_os_wait_events_complete(); + flush_hotplug_workqueue(); + + mutex_unlock(&dev_lock); if (ret) return ret; @@ -412,6 +445,7 @@ static int __init mdev_init(void) mdev = kzalloc(sizeof(struct memory_dev), GFP_KERNEL); if (!mdev) return -ENOMEM; + mutex_init(&dev_lock); ret = container_init(); if (ret) { diff --git a/include/linux/acpi.h b/include/linux/acpi.h index acd1a9241829c..eb12ffc5e3cf8 100644 --- a/include/linux/acpi.h +++ b/include/linux/acpi.h @@ -1578,10 +1578,12 @@ static inline void acpi_init_ffh(void) { } #ifdef CONFIG_ACPI extern void acpi_device_notify(struct device *dev); extern void acpi_device_notify_remove(struct device *dev); +extern void flush_hotplug_workqueue(void); extern acpi_status acpi_hotplug_schedule(struct acpi_device *adev, u32 src); #else static inline void acpi_device_notify(struct device *dev) { } static inline void acpi_device_notify_remove(struct device *dev) { } +static inline void flush_hotplug_workqueue(void) {} #endif #ifdef CONFIG_ACPI_APEI_MEMORY_FAILURE int register_apei_page_offline_notifier(struct notifier_block *nb); -- 2.43.0