Commit 016decc0 authored by Linus Torvalds's avatar Linus Torvalds
Browse files
Pull ACPI fixes from Rafael Wysocki:
 "These fix the handling of notifications in the ACPI thermal driver and
  address a device enumeration issue leading to the presence of multiple
  'MODALIAS=' entries in one uevent file in sysfs in some cases.

  Specifics:

   - Modify the ACPI thermal driver to avoid evaluating _TMP directly in
     its Notify () handler callback and running too many thermal checks
     for one thermal zone at the same time so as to address a work item
     accumulation issue observed on some systems that fail to shut down
     as a result of it (Rafael Wysocki)

   - Modify the ACPI uevent file creation code to avoid putting multiple
     'MODALIAS=' entries in one uevent file in sysfs which breaks
     systemd-udevd (Kai-Heng Feng)"

* tag 'acpi-5.11-rc6' of git://git.kernel.org/pub/scm/linux/kernel/git/rafael/linux-pm:
  ACPI: thermal: Do not call acpi_thermal_check() directly
  ACPI: sysfs: Prefer "compatible" modalias
parents 6305d15e b584b7e9
Loading
Loading
Loading
Loading
+6 −14
Original line number Diff line number Diff line
@@ -251,20 +251,12 @@ int __acpi_device_uevent_modalias(struct acpi_device *adev,
	if (add_uevent_var(env, "MODALIAS="))
		return -ENOMEM;

	len = create_pnp_modalias(adev, &env->buf[env->buflen - 1],
				  sizeof(env->buf) - env->buflen);
	if (len < 0)
		return len;

	env->buflen += len;
	if (!adev->data.of_compatible)
		return 0;

	if (len > 0 && add_uevent_var(env, "MODALIAS="))
		return -ENOMEM;

	if (adev->data.of_compatible)
		len = create_of_modalias(adev, &env->buf[env->buflen - 1],
					 sizeof(env->buf) - env->buflen);
	else
		len = create_pnp_modalias(adev, &env->buf[env->buflen - 1],
					  sizeof(env->buf) - env->buflen);
	if (len < 0)
		return len;

+33 −13
Original line number Diff line number Diff line
@@ -174,6 +174,8 @@ struct acpi_thermal {
	struct thermal_zone_device *thermal_zone;
	int kelvin_offset;	/* in millidegrees */
	struct work_struct thermal_check_work;
	struct mutex thermal_check_lock;
	refcount_t thermal_check_count;
};

/* --------------------------------------------------------------------------
@@ -495,14 +497,6 @@ static int acpi_thermal_get_trip_points(struct acpi_thermal *tz)
	return 0;
}

static void acpi_thermal_check(void *data)
{
	struct acpi_thermal *tz = data;

	thermal_zone_device_update(tz->thermal_zone,
				   THERMAL_EVENT_UNSPECIFIED);
}

/* sys I/F for generic thermal sysfs support */

static int thermal_get_temp(struct thermal_zone_device *thermal, int *temp)
@@ -900,6 +894,12 @@ static void acpi_thermal_unregister_thermal_zone(struct acpi_thermal *tz)
                                 Driver Interface
   -------------------------------------------------------------------------- */

static void acpi_queue_thermal_check(struct acpi_thermal *tz)
{
	if (!work_pending(&tz->thermal_check_work))
		queue_work(acpi_thermal_pm_queue, &tz->thermal_check_work);
}

static void acpi_thermal_notify(struct acpi_device *device, u32 event)
{
	struct acpi_thermal *tz = acpi_driver_data(device);
@@ -910,17 +910,17 @@ static void acpi_thermal_notify(struct acpi_device *device, u32 event)

	switch (event) {
	case ACPI_THERMAL_NOTIFY_TEMPERATURE:
		acpi_thermal_check(tz);
		acpi_queue_thermal_check(tz);
		break;
	case ACPI_THERMAL_NOTIFY_THRESHOLDS:
		acpi_thermal_trips_update(tz, ACPI_TRIPS_REFRESH_THRESHOLDS);
		acpi_thermal_check(tz);
		acpi_queue_thermal_check(tz);
		acpi_bus_generate_netlink_event(device->pnp.device_class,
						  dev_name(&device->dev), event, 0);
		break;
	case ACPI_THERMAL_NOTIFY_DEVICES:
		acpi_thermal_trips_update(tz, ACPI_TRIPS_REFRESH_DEVICES);
		acpi_thermal_check(tz);
		acpi_queue_thermal_check(tz);
		acpi_bus_generate_netlink_event(device->pnp.device_class,
						  dev_name(&device->dev), event, 0);
		break;
@@ -1020,7 +1020,25 @@ static void acpi_thermal_check_fn(struct work_struct *work)
{
	struct acpi_thermal *tz = container_of(work, struct acpi_thermal,
					       thermal_check_work);
	acpi_thermal_check(tz);

	/*
	 * In general, it is not sufficient to check the pending bit, because
	 * subsequent instances of this function may be queued after one of them
	 * has started running (e.g. if _TMP sleeps).  Avoid bailing out if just
	 * one of them is running, though, because it may have done the actual
	 * check some time ago, so allow at least one of them to block on the
	 * mutex while another one is running the update.
	 */
	if (!refcount_dec_not_one(&tz->thermal_check_count))
		return;

	mutex_lock(&tz->thermal_check_lock);

	thermal_zone_device_update(tz->thermal_zone, THERMAL_EVENT_UNSPECIFIED);

	refcount_inc(&tz->thermal_check_count);

	mutex_unlock(&tz->thermal_check_lock);
}

static int acpi_thermal_add(struct acpi_device *device)
@@ -1052,6 +1070,8 @@ static int acpi_thermal_add(struct acpi_device *device)
	if (result)
		goto free_memory;

	refcount_set(&tz->thermal_check_count, 3);
	mutex_init(&tz->thermal_check_lock);
	INIT_WORK(&tz->thermal_check_work, acpi_thermal_check_fn);

	pr_info(PREFIX "%s [%s] (%ld C)\n", acpi_device_name(device),
@@ -1117,7 +1137,7 @@ static int acpi_thermal_resume(struct device *dev)
		tz->state.active |= tz->trips.active[i].flags.enabled;
	}

	queue_work(acpi_thermal_pm_queue, &tz->thermal_check_work);
	acpi_queue_thermal_check(tz);

	return AE_OK;
}