Commit a4e6512a authored by Ulf Hansson's avatar Ulf Hansson Committed by Rafael J. Wysocki
Browse files

PM: QoS: Introduce a CPU system wakeup QoS limit



Some platforms supports multiple low power states for CPUs that can be used
when entering system-wide suspend. Currently we are always selecting the
deepest possible state for the CPUs, which can break the system wakeup
latency constraint that may be required for a use case.

Let's take the first step towards addressing this problem, by introducing
an interface for user space, that allows us to specify the CPU system
wakeup QoS limit. Subsequent changes will start taking into account the new
QoS limit.

Reviewed-by: default avatarDhruva Gole <d-gole@ti.com>
Reviewed-by: default avatarKevin Hilman (TI) <khilman@baylibre.com>
Tested-by: default avatarKevin Hilman (TI) <khilman@baylibre.com>
Signed-off-by: default avatarUlf Hansson <ulf.hansson@linaro.org>
Link: https://patch.msgid.link/20251125112650.329269-2-ulf.hansson@linaro.org


Signed-off-by: default avatarRafael J. Wysocki <rafael.j.wysocki@intel.com>
parent ac3fd01e
Loading
Loading
Loading
Loading
+9 −0
Original line number Diff line number Diff line
@@ -162,6 +162,15 @@ static inline void cpu_latency_qos_update_request(struct pm_qos_request *req,
static inline void cpu_latency_qos_remove_request(struct pm_qos_request *req) {}
#endif

#ifdef CONFIG_PM_QOS_CPU_SYSTEM_WAKEUP
s32 cpu_wakeup_latency_qos_limit(void);
#else
static inline s32 cpu_wakeup_latency_qos_limit(void)
{
	return PM_QOS_RESUME_LATENCY_NO_CONSTRAINT;
}
#endif

#ifdef CONFIG_PM
enum pm_qos_flags_status __dev_pm_qos_flags(struct device *dev, s32 mask);
enum pm_qos_flags_status dev_pm_qos_flags(struct device *dev, s32 mask);
+11 −0
Original line number Diff line number Diff line
@@ -202,6 +202,17 @@ config PM_WAKELOCKS_GC
	depends on PM_WAKELOCKS
	default y

config PM_QOS_CPU_SYSTEM_WAKEUP
	bool "User space interface for CPU system wakeup QoS"
	depends on CPU_IDLE
	help
	  Enable this to allow user space via the cpu_wakeup_latency file to
	  specify a CPU system wakeup latency limit.

	  This may be particularly useful for platforms supporting multiple low
	  power states for CPUs during system-wide suspend and s2idle in
	  particular.

config PM
	bool "Device power management core functionality"
	help
+106 −0
Original line number Diff line number Diff line
@@ -415,6 +415,105 @@ static struct miscdevice cpu_latency_qos_miscdev = {
	.fops = &cpu_latency_qos_fops,
};

#ifdef CONFIG_PM_QOS_CPU_SYSTEM_WAKEUP
/* The CPU system wakeup latency QoS. */
static struct pm_qos_constraints cpu_wakeup_latency_constraints = {
	.list = PLIST_HEAD_INIT(cpu_wakeup_latency_constraints.list),
	.target_value = PM_QOS_RESUME_LATENCY_NO_CONSTRAINT,
	.default_value = PM_QOS_RESUME_LATENCY_NO_CONSTRAINT,
	.no_constraint_value = PM_QOS_RESUME_LATENCY_NO_CONSTRAINT,
	.type = PM_QOS_MIN,
};

/**
 * cpu_wakeup_latency_qos_limit - Current CPU system wakeup latency QoS limit.
 *
 * Returns the current CPU system wakeup latency QoS limit that may have been
 * requested by user space.
 */
s32 cpu_wakeup_latency_qos_limit(void)
{
	return pm_qos_read_value(&cpu_wakeup_latency_constraints);
}

static int cpu_wakeup_latency_qos_open(struct inode *inode, struct file *filp)
{
	struct pm_qos_request *req;

	req = kzalloc(sizeof(*req), GFP_KERNEL);
	if (!req)
		return -ENOMEM;

	req->qos = &cpu_wakeup_latency_constraints;
	pm_qos_update_target(req->qos, &req->node, PM_QOS_ADD_REQ,
			     PM_QOS_RESUME_LATENCY_NO_CONSTRAINT);
	filp->private_data = req;

	return 0;
}

static int cpu_wakeup_latency_qos_release(struct inode *inode,
					  struct file *filp)
{
	struct pm_qos_request *req = filp->private_data;

	filp->private_data = NULL;
	pm_qos_update_target(req->qos, &req->node, PM_QOS_REMOVE_REQ,
			     PM_QOS_RESUME_LATENCY_NO_CONSTRAINT);
	kfree(req);

	return 0;
}

static ssize_t cpu_wakeup_latency_qos_read(struct file *filp, char __user *buf,
					   size_t count, loff_t *f_pos)
{
	s32 value = pm_qos_read_value(&cpu_wakeup_latency_constraints);

	return simple_read_from_buffer(buf, count, f_pos, &value, sizeof(s32));
}

static ssize_t cpu_wakeup_latency_qos_write(struct file *filp,
					    const char __user *buf,
					    size_t count, loff_t *f_pos)
{
	struct pm_qos_request *req = filp->private_data;
	s32 value;

	if (count == sizeof(s32)) {
		if (copy_from_user(&value, buf, sizeof(s32)))
			return -EFAULT;
	} else {
		int ret;

		ret = kstrtos32_from_user(buf, count, 16, &value);
		if (ret)
			return ret;
	}

	if (value < 0)
		return -EINVAL;

	pm_qos_update_target(req->qos, &req->node, PM_QOS_UPDATE_REQ, value);

	return count;
}

static const struct file_operations cpu_wakeup_latency_qos_fops = {
	.open = cpu_wakeup_latency_qos_open,
	.release = cpu_wakeup_latency_qos_release,
	.read = cpu_wakeup_latency_qos_read,
	.write = cpu_wakeup_latency_qos_write,
	.llseek = noop_llseek,
};

static struct miscdevice cpu_wakeup_latency_qos_miscdev = {
	.minor = MISC_DYNAMIC_MINOR,
	.name = "cpu_wakeup_latency",
	.fops = &cpu_wakeup_latency_qos_fops,
};
#endif /* CONFIG_PM_QOS_CPU_SYSTEM_WAKEUP */

static int __init cpu_latency_qos_init(void)
{
	int ret;
@@ -424,6 +523,13 @@ static int __init cpu_latency_qos_init(void)
		pr_err("%s: %s setup failed\n", __func__,
		       cpu_latency_qos_miscdev.name);

#ifdef CONFIG_PM_QOS_CPU_SYSTEM_WAKEUP
	ret = misc_register(&cpu_wakeup_latency_qos_miscdev);
	if (ret < 0)
		pr_err("%s: %s setup failed\n", __func__,
		       cpu_wakeup_latency_qos_miscdev.name);
#endif

	return ret;
}
late_initcall(cpu_latency_qos_init);