Linux Power Management development
 help / color / mirror / Atom feed
* [RFC PATCH 0/1] cpufreq: virtio frontend to report host CPU frequency
@ 2026-09-16  7:37 Baorui.Liu
  2026-09-16  7:37 ` [RFC PATCH 1/1] cpufreq: virtio: add driver to report host frequency to guests Baorui.Liu
                   ` (2 more replies)
  0 siblings, 3 replies; 6+ messages in thread
From: Baorui.Liu @ 2026-09-16  7:37 UTC (permalink / raw)
  To: linux-pm
  Cc: rafael, viresh.kumar, saravanak, mst, jasowangio, eperezma,
	xuanzhuo, virtualization, Baorui.Liu

This RFC adds a guest virtio-cpufreq frontend so a VM can read the
host frequency of the pCPU that currently runs a given vCPU.

Context
=======
Android batterystats needs cpufreq together with power_profile.xml.
A Xen Android guest cannot read host sysfs, so QEMU already has a
backend (virtio device id 42) that returns xc_get_cpufreq_avgfreq()
for the pinned pCPU. This patch is the Linux frontend.

Why not virtual-cpufreq.c
=========================
drivers/cpufreq/virtual-cpufreq.c is an MMIO guest driver. This
stack is virtio + Xen/QEMU, so a separate frontend is used. Feedback
on folding into or sharing infrastructure with virtual-cpufreq is
welcome.

Known RFC issues
================
- Virtio device id 42 is not allocated by the OASIS virtio spec.
- The OPP table is hardcoded (1400/1700/3000 kHz) for bring-up.
- The QEMU backend is a separate series (not this patch).
- .get() waits on a virtqueue and may sleep.

Please comment on the id allocation, protocol, and whether this
belongs in linux-pm as RFC before a formal PATCH.

Baorui.Liu (1):
  cpufreq: virtio: add driver to report host frequency to guests

 drivers/cpufreq/Kconfig          |  14 ++
 drivers/cpufreq/Makefile         |   1 +
 drivers/cpufreq/virtio-cpufreq.c | 235 +++++++++++++++++++++++++++++++
 include/uapi/linux/virtio_ids.h  |   1 +
 4 files changed, 251 insertions(+)
 create mode 100644 drivers/cpufreq/virtio-cpufreq.c

-- 
2.34.1


^ permalink raw reply	[flat|nested] 6+ messages in thread

* [RFC PATCH 1/1] cpufreq: virtio: add driver to report host frequency to guests
  2026-09-16  7:37 [RFC PATCH 0/1] cpufreq: virtio frontend to report host CPU frequency Baorui.Liu
@ 2026-09-16  7:37 ` Baorui.Liu
  2026-09-22  9:02 ` [RFC PATCH v2 0/1] cpufreq: virtio frontend to report host CPU frequency Baorui.Liu
  2026-09-22  9:02 ` [RFC PATCH v2 1/1] cpufreq: virtio: add driver to report host frequency to guests Baorui.Liu
  2 siblings, 0 replies; 6+ messages in thread
From: Baorui.Liu @ 2026-09-16  7:37 UTC (permalink / raw)
  To: linux-pm
  Cc: rafael, viresh.kumar, saravanak, mst, jasowangio, eperezma,
	xuanzhuo, virtualization, Baorui.Liu

Some virtualization stacks pin each vCPU to a pCPU. Guest software
still needs the current host frequency for energy accounting, but
the guest cannot access host sysfs.

Add a virtio frontend that queries the host for the frequency of
the mapped pCPU and exposes it through the cpufreq .get() callback.
Frequency tables are placeholder OPPs; the driver does not change
the physical frequency.

RFC: virtio device id 42 is not allocated by the virtio spec yet.
Signed-off-by: Baorui.Liu <baorliu@amd.com>
---
 drivers/cpufreq/Kconfig          |  14 ++
 drivers/cpufreq/Makefile         |   1 +
 drivers/cpufreq/virtio-cpufreq.c | 235 +++++++++++++++++++++++++++++++
 include/uapi/linux/virtio_ids.h  |   1 +
 4 files changed, 251 insertions(+)
 create mode 100644 drivers/cpufreq/virtio-cpufreq.c

diff --git a/drivers/cpufreq/Kconfig b/drivers/cpufreq/Kconfig
index db83f3365698..ba0e38f5dce5 100644
--- a/drivers/cpufreq/Kconfig
+++ b/drivers/cpufreq/Kconfig
@@ -242,6 +242,20 @@ config CPUFREQ_VIRT
 
 	  If in doubt, say N.
 
+config CPUFREQ_VIRTIO
+	tristate "Virtio CPU frequency driver"
+	depends on VIRTIO
+	help
+	  This option adds a virtio cpufreq frontend. The guest sends a
+	  vCPU id to the host backend and receives the mapped physical
+	  CPU frequency in kHz through the cpufreq .get() callback.
+
+	  This is intended for virtualization stacks (for example Xen)
+	  that pin each vCPU to a pCPU and expose host frequency to the
+	  guest. It does not change the host frequency.
+
+	  If in doubt, say N.
+
 config CPUFREQ_DT_PLATDEV
 	bool "Generic DT based cpufreq platdev driver"
 	depends on OF
diff --git a/drivers/cpufreq/Makefile b/drivers/cpufreq/Makefile
index 6c7a39b7f8d2..9d9bb7ea1300 100644
--- a/drivers/cpufreq/Makefile
+++ b/drivers/cpufreq/Makefile
@@ -18,6 +18,7 @@ obj-$(CONFIG_CPUFREQ_DT)		+= cpufreq-dt.o
 obj-$(CONFIG_CPUFREQ_DT_RUST)		+= rcpufreq_dt.o
 obj-$(CONFIG_CPUFREQ_DT_PLATDEV)	+= cpufreq-dt-platdev.o
 obj-$(CONFIG_CPUFREQ_VIRT)		+= virtual-cpufreq.o
+obj-$(CONFIG_CPUFREQ_VIRTIO)		+= virtio-cpufreq.o
 
 # Traces
 CFLAGS_amd-pstate-trace.o               := -I$(src)
diff --git a/drivers/cpufreq/virtio-cpufreq.c b/drivers/cpufreq/virtio-cpufreq.c
new file mode 100644
index 000000000000..23fde862a313
--- /dev/null
+++ b/drivers/cpufreq/virtio-cpufreq.c
@@ -0,0 +1,235 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Virtio cpufreq frontend
+ *
+ * Query the host for the frequency of the pCPU that currently runs a
+ * given vCPU. Used when the guest cannot read host cpufreq sysfs, for
+ * example a Xen domain that needs the value for energy accounting.
+ *
+ * The on-wire protocol is a packed { cpu_id, freq_khz } request that
+ * must match the QEMU virtio-cpufreq backend.
+ */
+
+#include <linux/completion.h>
+#include <linux/cpufreq.h>
+#include <linux/cpumask.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/scatterlist.h>
+#include <linux/slab.h>
+#include <linux/virtio.h>
+#include <linux/virtio_config.h>
+#include <linux/virtio_ids.h>
+
+#define DRV_NAME "virtio-cpufreq"
+
+struct virtio_cpufreq_req {
+	__le32 cpu_id;
+	__le32 freq_khz;
+} __packed;
+
+struct virtio_cpufreq {
+	struct virtio_device *vdev;
+	struct virtqueue *vq;
+	struct mutex lock; /* serializes virtqueue requests */
+};
+
+static struct virtio_cpufreq *virtio_cpufreq_dev;
+
+/*
+ * Placeholder OPPs so cpufreq has a table. Real platforms should
+ * populate this from the host or from firmware. Values match the
+ * existing QEMU/Android backend used for bring-up.
+ */
+static struct cpufreq_frequency_table virtio_freq_table[] = {
+	{ .frequency = 1400000 },
+	{ .frequency = 1700000 },
+	{ .frequency = 3000000 },
+	{ .frequency = CPUFREQ_TABLE_END },
+};
+
+static int virtio_cpufreq_init_policy(struct cpufreq_policy *policy)
+{
+	cpumask_clear(policy->cpus);
+	cpumask_set_cpu(policy->cpu, policy->cpus);
+
+	policy->freq_table = virtio_freq_table;
+	policy->cpuinfo.min_freq = 1400000;
+	policy->cpuinfo.max_freq = 3000000;
+	policy->cpuinfo.transition_latency = 0;
+
+	return 0;
+}
+
+static int virtio_cpufreq_target_index(struct cpufreq_policy *policy,
+				       unsigned int index)
+{
+	struct cpufreq_freqs freqs;
+
+	freqs.old = policy->cur;
+	freqs.new = policy->freq_table[index].frequency;
+
+	/*
+	 * This frontend does not change host frequency. It only keeps
+	 * the cpufreq core in sync so userspace can observe values.
+	 */
+	cpufreq_freq_transition_begin(policy, &freqs);
+	cpufreq_freq_transition_end(policy, &freqs, 0);
+
+	return 0;
+}
+
+static unsigned int virtio_cpufreq_fallback(unsigned int cpu)
+{
+	struct cpufreq_policy *policy;
+	unsigned int freq = 0;
+
+	policy = cpufreq_cpu_get(cpu);
+	if (policy) {
+		freq = policy->cur;
+		cpufreq_cpu_put(policy);
+	}
+
+	return freq;
+}
+
+static void virtio_cpufreq_vq_cb(struct virtqueue *vq)
+{
+	struct completion *done;
+	unsigned int len;
+
+	while ((done = virtqueue_get_buf(vq, &len)) != NULL)
+		complete(done);
+}
+
+static unsigned int virtio_cpufreq_get(unsigned int cpu)
+{
+	struct virtio_cpufreq *vc = virtio_cpufreq_dev;
+	struct virtio_cpufreq_req *req;
+	struct scatterlist out_sg, in_sg, *sgs[2];
+	struct completion done;
+	unsigned int freq_khz;
+	int ret;
+
+	if (!vc || !vc->vq)
+		return virtio_cpufreq_fallback(cpu);
+
+	req = kzalloc_obj(*req, GFP_KERNEL);
+	if (!req)
+		return virtio_cpufreq_fallback(cpu);
+
+	req->cpu_id = cpu_to_le32(cpu);
+
+	init_completion(&done);
+	sg_init_one(&out_sg, req, sizeof(*req));
+	sg_init_one(&in_sg, req, sizeof(*req));
+	sgs[0] = &out_sg;
+	sgs[1] = &in_sg;
+
+	mutex_lock(&vc->lock);
+	ret = virtqueue_add_sgs(vc->vq, sgs, 1, 1, &done, GFP_KERNEL);
+	if (ret) {
+		mutex_unlock(&vc->lock);
+		kfree(req);
+		return virtio_cpufreq_fallback(cpu);
+	}
+
+	virtqueue_kick(vc->vq);
+	ret = wait_for_completion_timeout(&done, msecs_to_jiffies(1000));
+	mutex_unlock(&vc->lock);
+
+	if (!ret) {
+		/*
+		 * The buffer may still be on the virtqueue. Leak it
+		 * rather than freeing while the host may still write.
+		 */
+		return virtio_cpufreq_fallback(cpu);
+	}
+
+	freq_khz = le32_to_cpu(req->freq_khz);
+	kfree(req);
+
+	if (!freq_khz)
+		return virtio_cpufreq_fallback(cpu);
+
+	return freq_khz;
+}
+
+static struct cpufreq_driver virtio_cpufreq_driver = {
+	.name		= DRV_NAME,
+	.flags		= CPUFREQ_CONST_LOOPS,
+	.init		= virtio_cpufreq_init_policy,
+	.verify		= cpufreq_generic_frequency_table_verify,
+	.target_index	= virtio_cpufreq_target_index,
+	.get		= virtio_cpufreq_get,
+	.attr		= cpufreq_generic_attr,
+};
+
+static int virtio_cpufreq_probe(struct virtio_device *vdev)
+{
+	struct virtio_cpufreq *vc;
+	struct virtqueue *vq;
+	int ret;
+
+	vc = devm_kzalloc(&vdev->dev, sizeof(*vc), GFP_KERNEL);
+	if (!vc)
+		return -ENOMEM;
+
+	mutex_init(&vc->lock);
+	vc->vdev = vdev;
+
+	vq = virtio_find_single_vq(vdev, virtio_cpufreq_vq_cb, "requests");
+	if (IS_ERR(vq))
+		return PTR_ERR(vq);
+
+	vc->vq = vq;
+	vdev->priv = vc;
+	virtio_cpufreq_dev = vc;
+	virtio_device_ready(vdev);
+
+	ret = cpufreq_register_driver(&virtio_cpufreq_driver);
+	if (ret) {
+		vdev->config->del_vqs(vdev);
+		virtio_cpufreq_dev = NULL;
+		return ret;
+	}
+
+	return 0;
+}
+
+static void virtio_cpufreq_remove(struct virtio_device *vdev)
+{
+	cpufreq_unregister_driver(&virtio_cpufreq_driver);
+	virtio_cpufreq_dev = NULL;
+	vdev->config->del_vqs(vdev);
+}
+
+static const struct virtio_device_id id_table[] = {
+	{ VIRTIO_ID_CPUFREQ, VIRTIO_DEV_ANY_ID },
+	{ 0 },
+};
+
+static struct virtio_driver virtio_cpufreq_virtio_driver = {
+	.driver.name	= DRV_NAME,
+	.driver.owner	= THIS_MODULE,
+	.id_table	= id_table,
+	.probe		= virtio_cpufreq_probe,
+	.remove		= virtio_cpufreq_remove,
+};
+
+static int __init virtio_cpufreq_mod_init(void)
+{
+	return register_virtio_driver(&virtio_cpufreq_virtio_driver);
+}
+
+static void __exit virtio_cpufreq_mod_exit(void)
+{
+	unregister_virtio_driver(&virtio_cpufreq_virtio_driver);
+}
+
+module_init(virtio_cpufreq_mod_init);
+module_exit(virtio_cpufreq_mod_exit);
+
+MODULE_DEVICE_TABLE(virtio, id_table);
+MODULE_DESCRIPTION("Virtio cpufreq frontend");
+MODULE_LICENSE("GPL");
diff --git a/include/uapi/linux/virtio_ids.h b/include/uapi/linux/virtio_ids.h
index f9056af0c622..f7e357f3bf94 100644
--- a/include/uapi/linux/virtio_ids.h
+++ b/include/uapi/linux/virtio_ids.h
@@ -68,6 +68,7 @@
 #define VIRTIO_ID_AUDIO_POLICY		39 /* virtio audio policy */
 #define VIRTIO_ID_BT			40 /* virtio bluetooth */
 #define VIRTIO_ID_GPIO			41 /* virtio gpio */
+#define VIRTIO_ID_CPUFREQ		42 /* virtio cpufreq */
 #define VIRTIO_ID_SPI			45 /* virtio spi */
 #define VIRTIO_ID_MEDIA			48 /* virtio media */
 
-- 
2.34.1


^ permalink raw reply related	[flat|nested] 6+ messages in thread

* [RFC PATCH v2 0/1] cpufreq: virtio frontend to report host CPU frequency
  2026-09-16  7:37 [RFC PATCH 0/1] cpufreq: virtio frontend to report host CPU frequency Baorui.Liu
  2026-09-16  7:37 ` [RFC PATCH 1/1] cpufreq: virtio: add driver to report host frequency to guests Baorui.Liu
@ 2026-09-22  9:02 ` Baorui.Liu
  2026-09-22  9:22   ` Michael S. Tsirkin
  2026-09-22  9:02 ` [RFC PATCH v2 1/1] cpufreq: virtio: add driver to report host frequency to guests Baorui.Liu
  2 siblings, 1 reply; 6+ messages in thread
From: Baorui.Liu @ 2026-09-22  9:02 UTC (permalink / raw)
  To: linux-pm
  Cc: rafael, viresh.kumar, saravanak, mst, jasowangio, eperezma,
	xuanzhuo, virtualization, Baorui.Liu

This is RFC v2 of the guest virtio-cpufreq frontend.

Changes since RFC v1
====================
- Fix 0-day kbuild error: cpufreq_generic_attr is gone on current
  linux-pm; use cpufreq_freq_attr_scaling_available_freqs.
  Reported-by: kernel test robot <lkp@intel.com>
  Closes: https://lore.kernel.org/oe-kbuild-all/202609191437.OaXd0Blz-lkp@intel.com/

RFC notes unchanged: experimental virtio ID 42, hardcoded OPPs,
sleeping .get(), QEMU backend is a separate series.

v1: https://lore.kernel.org/r/20260916073735.541-1-baorliu@amd.com

Baorui.Liu (1):
  cpufreq: virtio: add driver to report host frequency to guests

 drivers/cpufreq/Kconfig          |  14 ++
 drivers/cpufreq/Makefile         |   1 +
 drivers/cpufreq/virtio-cpufreq.c | 240 +++++++++++++++++++++++++++++++
 include/uapi/linux/virtio_ids.h  |   1 +
 4 files changed, 256 insertions(+)
 create mode 100644 drivers/cpufreq/virtio-cpufreq.c

-- 
2.34.1


^ permalink raw reply	[flat|nested] 6+ messages in thread

* [RFC PATCH v2 1/1] cpufreq: virtio: add driver to report host frequency to guests
  2026-09-16  7:37 [RFC PATCH 0/1] cpufreq: virtio frontend to report host CPU frequency Baorui.Liu
  2026-09-16  7:37 ` [RFC PATCH 1/1] cpufreq: virtio: add driver to report host frequency to guests Baorui.Liu
  2026-09-22  9:02 ` [RFC PATCH v2 0/1] cpufreq: virtio frontend to report host CPU frequency Baorui.Liu
@ 2026-09-22  9:02 ` Baorui.Liu
  2026-09-24  7:18   ` Saravana Kannan
  2 siblings, 1 reply; 6+ messages in thread
From: Baorui.Liu @ 2026-09-22  9:02 UTC (permalink / raw)
  To: linux-pm
  Cc: rafael, viresh.kumar, saravanak, mst, jasowangio, eperezma,
	xuanzhuo, virtualization, Baorui.Liu, kernel test robot

Some virtualization stacks pin each vCPU to a pCPU. Guest software
still needs the current host frequency for energy accounting, but
the guest cannot access host sysfs.

Add a virtio frontend that queries the host for the frequency of
the mapped pCPU and exposes it through the cpufreq .get() callback.

Fix build on current linux-pm: cpufreq_generic_attr no longer
exists; use cpufreq_freq_attr_scaling_available_freqs instead.

Reported-by: kernel test robot <lkp@intel.com>
Closes: https://lore.kernel.org/oe-kbuild-all/202609191437.OaXd0Blz-lkp@intel.com/
Signed-off-by: Baorui.Liu <baorliu@amd.com>
---
 drivers/cpufreq/Kconfig          |  14 ++
 drivers/cpufreq/Makefile         |   1 +
 drivers/cpufreq/virtio-cpufreq.c | 240 +++++++++++++++++++++++++++++++
 include/uapi/linux/virtio_ids.h  |   1 +
 4 files changed, 256 insertions(+)
 create mode 100644 drivers/cpufreq/virtio-cpufreq.c

diff --git a/drivers/cpufreq/Kconfig b/drivers/cpufreq/Kconfig
index db83f3365698..ba0e38f5dce5 100644
--- a/drivers/cpufreq/Kconfig
+++ b/drivers/cpufreq/Kconfig
@@ -242,6 +242,20 @@ config CPUFREQ_VIRT
 
 	  If in doubt, say N.
 
+config CPUFREQ_VIRTIO
+	tristate "Virtio CPU frequency driver"
+	depends on VIRTIO
+	help
+	  This option adds a virtio cpufreq frontend. The guest sends a
+	  vCPU id to the host backend and receives the mapped physical
+	  CPU frequency in kHz through the cpufreq .get() callback.
+
+	  This is intended for virtualization stacks (for example Xen)
+	  that pin each vCPU to a pCPU and expose host frequency to the
+	  guest. It does not change the host frequency.
+
+	  If in doubt, say N.
+
 config CPUFREQ_DT_PLATDEV
 	bool "Generic DT based cpufreq platdev driver"
 	depends on OF
diff --git a/drivers/cpufreq/Makefile b/drivers/cpufreq/Makefile
index 6c7a39b7f8d2..9d9bb7ea1300 100644
--- a/drivers/cpufreq/Makefile
+++ b/drivers/cpufreq/Makefile
@@ -18,6 +18,7 @@ obj-$(CONFIG_CPUFREQ_DT)		+= cpufreq-dt.o
 obj-$(CONFIG_CPUFREQ_DT_RUST)		+= rcpufreq_dt.o
 obj-$(CONFIG_CPUFREQ_DT_PLATDEV)	+= cpufreq-dt-platdev.o
 obj-$(CONFIG_CPUFREQ_VIRT)		+= virtual-cpufreq.o
+obj-$(CONFIG_CPUFREQ_VIRTIO)		+= virtio-cpufreq.o
 
 # Traces
 CFLAGS_amd-pstate-trace.o               := -I$(src)
diff --git a/drivers/cpufreq/virtio-cpufreq.c b/drivers/cpufreq/virtio-cpufreq.c
new file mode 100644
index 000000000000..aa04fd7fd343
--- /dev/null
+++ b/drivers/cpufreq/virtio-cpufreq.c
@@ -0,0 +1,240 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Virtio cpufreq frontend
+ *
+ * Query the host for the frequency of the pCPU that currently runs a
+ * given vCPU. Used when the guest cannot read host cpufreq sysfs, for
+ * example a Xen domain that needs the value for energy accounting.
+ *
+ * The on-wire protocol is a packed { cpu_id, freq_khz } request that
+ * must match the QEMU virtio-cpufreq backend.
+ */
+
+#include <linux/completion.h>
+#include <linux/cpufreq.h>
+#include <linux/cpumask.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/scatterlist.h>
+#include <linux/slab.h>
+#include <linux/virtio.h>
+#include <linux/virtio_config.h>
+#include <linux/virtio_ids.h>
+
+#define DRV_NAME "virtio-cpufreq"
+
+struct virtio_cpufreq_req {
+	__le32 cpu_id;
+	__le32 freq_khz;
+} __packed;
+
+struct virtio_cpufreq {
+	struct virtio_device *vdev;
+	struct virtqueue *vq;
+	struct mutex lock; /* serializes virtqueue requests */
+};
+
+static struct virtio_cpufreq *virtio_cpufreq_dev;
+
+/*
+ * Placeholder OPPs so cpufreq has a table. Real platforms should
+ * populate this from the host or from firmware. Values match the
+ * existing QEMU/Android backend used for bring-up.
+ */
+static struct cpufreq_frequency_table virtio_freq_table[] = {
+	{ .frequency = 1400000 },
+	{ .frequency = 1700000 },
+	{ .frequency = 3000000 },
+	{ .frequency = CPUFREQ_TABLE_END },
+};
+
+static int virtio_cpufreq_init_policy(struct cpufreq_policy *policy)
+{
+	cpumask_clear(policy->cpus);
+	cpumask_set_cpu(policy->cpu, policy->cpus);
+
+	policy->freq_table = virtio_freq_table;
+	policy->cpuinfo.min_freq = 1400000;
+	policy->cpuinfo.max_freq = 3000000;
+	policy->cpuinfo.transition_latency = 0;
+
+	return 0;
+}
+
+static int virtio_cpufreq_target_index(struct cpufreq_policy *policy,
+				       unsigned int index)
+{
+	struct cpufreq_freqs freqs;
+
+	freqs.old = policy->cur;
+	freqs.new = policy->freq_table[index].frequency;
+
+	/*
+	 * This frontend does not change host frequency. It only keeps
+	 * the cpufreq core in sync so userspace can observe values.
+	 */
+	cpufreq_freq_transition_begin(policy, &freqs);
+	cpufreq_freq_transition_end(policy, &freqs, 0);
+
+	return 0;
+}
+
+static unsigned int virtio_cpufreq_fallback(unsigned int cpu)
+{
+	struct cpufreq_policy *policy;
+	unsigned int freq = 0;
+
+	policy = cpufreq_cpu_get(cpu);
+	if (policy) {
+		freq = policy->cur;
+		cpufreq_cpu_put(policy);
+	}
+
+	return freq;
+}
+
+static void virtio_cpufreq_vq_cb(struct virtqueue *vq)
+{
+	struct completion *done;
+	unsigned int len;
+
+	while ((done = virtqueue_get_buf(vq, &len)) != NULL)
+		complete(done);
+}
+
+static unsigned int virtio_cpufreq_get(unsigned int cpu)
+{
+	struct virtio_cpufreq *vc = virtio_cpufreq_dev;
+	struct virtio_cpufreq_req *req;
+	struct scatterlist out_sg, in_sg, *sgs[2];
+	struct completion done;
+	unsigned int freq_khz;
+	int ret;
+
+	if (!vc || !vc->vq)
+		return virtio_cpufreq_fallback(cpu);
+
+	req = kzalloc_obj(*req, GFP_KERNEL);
+	if (!req)
+		return virtio_cpufreq_fallback(cpu);
+
+	req->cpu_id = cpu_to_le32(cpu);
+
+	init_completion(&done);
+	sg_init_one(&out_sg, req, sizeof(*req));
+	sg_init_one(&in_sg, req, sizeof(*req));
+	sgs[0] = &out_sg;
+	sgs[1] = &in_sg;
+
+	mutex_lock(&vc->lock);
+	ret = virtqueue_add_sgs(vc->vq, sgs, 1, 1, &done, GFP_KERNEL);
+	if (ret) {
+		mutex_unlock(&vc->lock);
+		kfree(req);
+		return virtio_cpufreq_fallback(cpu);
+	}
+
+	virtqueue_kick(vc->vq);
+	ret = wait_for_completion_timeout(&done, msecs_to_jiffies(1000));
+	mutex_unlock(&vc->lock);
+
+	if (!ret) {
+		/*
+		 * The buffer may still be on the virtqueue. Leak it
+		 * rather than freeing while the host may still write.
+		 */
+		return virtio_cpufreq_fallback(cpu);
+	}
+
+	freq_khz = le32_to_cpu(req->freq_khz);
+	kfree(req);
+
+	if (!freq_khz)
+		return virtio_cpufreq_fallback(cpu);
+
+	return freq_khz;
+}
+
+static struct freq_attr *virtio_cpufreq_attr[] = {
+	&cpufreq_freq_attr_scaling_available_freqs,
+	NULL,
+};
+
+static struct cpufreq_driver virtio_cpufreq_driver = {
+	.name		= DRV_NAME,
+	.flags		= CPUFREQ_CONST_LOOPS,
+	.init		= virtio_cpufreq_init_policy,
+	.verify		= cpufreq_generic_frequency_table_verify,
+	.target_index	= virtio_cpufreq_target_index,
+	.get		= virtio_cpufreq_get,
+	.attr		= virtio_cpufreq_attr,
+};
+
+static int virtio_cpufreq_probe(struct virtio_device *vdev)
+{
+	struct virtio_cpufreq *vc;
+	struct virtqueue *vq;
+	int ret;
+
+	vc = devm_kzalloc(&vdev->dev, sizeof(*vc), GFP_KERNEL);
+	if (!vc)
+		return -ENOMEM;
+
+	mutex_init(&vc->lock);
+	vc->vdev = vdev;
+
+	vq = virtio_find_single_vq(vdev, virtio_cpufreq_vq_cb, "requests");
+	if (IS_ERR(vq))
+		return PTR_ERR(vq);
+
+	vc->vq = vq;
+	vdev->priv = vc;
+	virtio_cpufreq_dev = vc;
+	virtio_device_ready(vdev);
+
+	ret = cpufreq_register_driver(&virtio_cpufreq_driver);
+	if (ret) {
+		vdev->config->del_vqs(vdev);
+		virtio_cpufreq_dev = NULL;
+		return ret;
+	}
+
+	return 0;
+}
+
+static void virtio_cpufreq_remove(struct virtio_device *vdev)
+{
+	cpufreq_unregister_driver(&virtio_cpufreq_driver);
+	virtio_cpufreq_dev = NULL;
+	vdev->config->del_vqs(vdev);
+}
+
+static const struct virtio_device_id id_table[] = {
+	{ VIRTIO_ID_CPUFREQ, VIRTIO_DEV_ANY_ID },
+	{ 0 },
+};
+
+static struct virtio_driver virtio_cpufreq_virtio_driver = {
+	.driver.name	= DRV_NAME,
+	.driver.owner	= THIS_MODULE,
+	.id_table	= id_table,
+	.probe		= virtio_cpufreq_probe,
+	.remove		= virtio_cpufreq_remove,
+};
+
+static int __init virtio_cpufreq_mod_init(void)
+{
+	return register_virtio_driver(&virtio_cpufreq_virtio_driver);
+}
+
+static void __exit virtio_cpufreq_mod_exit(void)
+{
+	unregister_virtio_driver(&virtio_cpufreq_virtio_driver);
+}
+
+module_init(virtio_cpufreq_mod_init);
+module_exit(virtio_cpufreq_mod_exit);
+
+MODULE_DEVICE_TABLE(virtio, id_table);
+MODULE_DESCRIPTION("Virtio cpufreq frontend");
+MODULE_LICENSE("GPL");
diff --git a/include/uapi/linux/virtio_ids.h b/include/uapi/linux/virtio_ids.h
index f9056af0c622..f7e357f3bf94 100644
--- a/include/uapi/linux/virtio_ids.h
+++ b/include/uapi/linux/virtio_ids.h
@@ -68,6 +68,7 @@
 #define VIRTIO_ID_AUDIO_POLICY		39 /* virtio audio policy */
 #define VIRTIO_ID_BT			40 /* virtio bluetooth */
 #define VIRTIO_ID_GPIO			41 /* virtio gpio */
+#define VIRTIO_ID_CPUFREQ		42 /* virtio cpufreq */
 #define VIRTIO_ID_SPI			45 /* virtio spi */
 #define VIRTIO_ID_MEDIA			48 /* virtio media */
 
-- 
2.34.1


^ permalink raw reply related	[flat|nested] 6+ messages in thread

* Re: [RFC PATCH v2 0/1] cpufreq: virtio frontend to report host CPU frequency
  2026-09-22  9:02 ` [RFC PATCH v2 0/1] cpufreq: virtio frontend to report host CPU frequency Baorui.Liu
@ 2026-09-22  9:22   ` Michael S. Tsirkin
  0 siblings, 0 replies; 6+ messages in thread
From: Michael S. Tsirkin @ 2026-09-22  9:22 UTC (permalink / raw)
  To: Baorui.Liu
  Cc: linux-pm, rafael, viresh.kumar, saravanak, jasowangio, eperezma,
	xuanzhuo, virtualization

On Tue, Sep 22, 2026 at 05:02:23PM +0800, Baorui.Liu wrote:
> This is RFC v2 of the guest virtio-cpufreq frontend.
> 
> Changes since RFC v1
> ====================
> - Fix 0-day kbuild error: cpufreq_generic_attr is gone on current
>   linux-pm; use cpufreq_freq_attr_scaling_available_freqs.
>   Reported-by: kernel test robot <lkp@intel.com>
>   Closes: https://lore.kernel.org/oe-kbuild-all/202609191437.OaXd0Blz-lkp@intel.com/
> 
> RFC notes unchanged: experimental virtio ID 42, hardcoded OPPs,
> sleeping .get(), QEMU backend is a separate series.
> 
> v1: https://lore.kernel.org/r/20260916073735.541-1-baorliu@amd.com

could you link to qemu and spec series, please?


> Baorui.Liu (1):
>   cpufreq: virtio: add driver to report host frequency to guests
> 
>  drivers/cpufreq/Kconfig          |  14 ++
>  drivers/cpufreq/Makefile         |   1 +
>  drivers/cpufreq/virtio-cpufreq.c | 240 +++++++++++++++++++++++++++++++
>  include/uapi/linux/virtio_ids.h  |   1 +
>  4 files changed, 256 insertions(+)
>  create mode 100644 drivers/cpufreq/virtio-cpufreq.c
> 
> -- 
> 2.34.1


^ permalink raw reply	[flat|nested] 6+ messages in thread

* Re: [RFC PATCH v2 1/1] cpufreq: virtio: add driver to report host frequency to guests
  2026-09-22  9:02 ` [RFC PATCH v2 1/1] cpufreq: virtio: add driver to report host frequency to guests Baorui.Liu
@ 2026-09-24  7:18   ` Saravana Kannan
  0 siblings, 0 replies; 6+ messages in thread
From: Saravana Kannan @ 2026-09-24  7:18 UTC (permalink / raw)
  To: Baorui.Liu
  Cc: linux-pm, rafael, viresh.kumar, saravanak, mst, jasowangio,
	eperezma, xuanzhuo, virtualization, kernel test robot

On Tue, Sep 22, 2026 at 2:03 AM Baorui.Liu <baorliu@amd.com> wrote:
>

As you stated in the v1 cover letter, there's already a MMIO based
driver. Why can't we use that? Why not add a MMIO CPUfreq device?

It's been a while, so I don't remember all the details. But we did
consider virtio for this but then decided against it for a few
reasons:
1. The overhead is much higher than MMIO
2. Doesn't allow the fastswitch mode that's super useful with
schedutil. IIRC AMD prefers schedutil.
3. Didn't want to add a vitrio device just for this.
4. Since Virtio is built on top of PCI (IIRC/IIUC), it was also adding
a limitation on how early we can have this driver working and also how
it'd affect the suspend/resume path if we wanted to change the cpufreq
after PCI is suspended inside the VM.

I'd really really prefer if you went with the MMIO option.


-Saravana



> Some virtualization stacks pin each vCPU to a pCPU. Guest software
> still needs the current host frequency for energy accounting, but
> the guest cannot access host sysfs.
>
> Add a virtio frontend that queries the host for the frequency of
> the mapped pCPU and exposes it through the cpufreq .get() callback.
>
> Fix build on current linux-pm: cpufreq_generic_attr no longer
> exists; use cpufreq_freq_attr_scaling_available_freqs instead.
>
> Reported-by: kernel test robot <lkp@intel.com>
> Closes: https://lore.kernel.org/oe-kbuild-all/202609191437.OaXd0Blz-lkp@intel.com/
> Signed-off-by: Baorui.Liu <baorliu@amd.com>
> ---
>  drivers/cpufreq/Kconfig          |  14 ++
>  drivers/cpufreq/Makefile         |   1 +
>  drivers/cpufreq/virtio-cpufreq.c | 240 +++++++++++++++++++++++++++++++
>  include/uapi/linux/virtio_ids.h  |   1 +
>  4 files changed, 256 insertions(+)
>  create mode 100644 drivers/cpufreq/virtio-cpufreq.c
>
> diff --git a/drivers/cpufreq/Kconfig b/drivers/cpufreq/Kconfig
> index db83f3365698..ba0e38f5dce5 100644
> --- a/drivers/cpufreq/Kconfig
> +++ b/drivers/cpufreq/Kconfig
> @@ -242,6 +242,20 @@ config CPUFREQ_VIRT
>
>           If in doubt, say N.
>
> +config CPUFREQ_VIRTIO
> +       tristate "Virtio CPU frequency driver"
> +       depends on VIRTIO
> +       help
> +         This option adds a virtio cpufreq frontend. The guest sends a
> +         vCPU id to the host backend and receives the mapped physical
> +         CPU frequency in kHz through the cpufreq .get() callback.
> +
> +         This is intended for virtualization stacks (for example Xen)
> +         that pin each vCPU to a pCPU and expose host frequency to the
> +         guest. It does not change the host frequency.
> +
> +         If in doubt, say N.
> +
>  config CPUFREQ_DT_PLATDEV
>         bool "Generic DT based cpufreq platdev driver"
>         depends on OF
> diff --git a/drivers/cpufreq/Makefile b/drivers/cpufreq/Makefile
> index 6c7a39b7f8d2..9d9bb7ea1300 100644
> --- a/drivers/cpufreq/Makefile
> +++ b/drivers/cpufreq/Makefile
> @@ -18,6 +18,7 @@ obj-$(CONFIG_CPUFREQ_DT)              += cpufreq-dt.o
>  obj-$(CONFIG_CPUFREQ_DT_RUST)          += rcpufreq_dt.o
>  obj-$(CONFIG_CPUFREQ_DT_PLATDEV)       += cpufreq-dt-platdev.o
>  obj-$(CONFIG_CPUFREQ_VIRT)             += virtual-cpufreq.o
> +obj-$(CONFIG_CPUFREQ_VIRTIO)           += virtio-cpufreq.o
>
>  # Traces
>  CFLAGS_amd-pstate-trace.o               := -I$(src)
> diff --git a/drivers/cpufreq/virtio-cpufreq.c b/drivers/cpufreq/virtio-cpufreq.c
> new file mode 100644
> index 000000000000..aa04fd7fd343
> --- /dev/null
> +++ b/drivers/cpufreq/virtio-cpufreq.c
> @@ -0,0 +1,240 @@
> +// SPDX-License-Identifier: GPL-2.0-only
> +/*
> + * Virtio cpufreq frontend
> + *
> + * Query the host for the frequency of the pCPU that currently runs a
> + * given vCPU. Used when the guest cannot read host cpufreq sysfs, for
> + * example a Xen domain that needs the value for energy accounting.
> + *
> + * The on-wire protocol is a packed { cpu_id, freq_khz } request that
> + * must match the QEMU virtio-cpufreq backend.
> + */
> +
> +#include <linux/completion.h>
> +#include <linux/cpufreq.h>
> +#include <linux/cpumask.h>
> +#include <linux/module.h>
> +#include <linux/mutex.h>
> +#include <linux/scatterlist.h>
> +#include <linux/slab.h>
> +#include <linux/virtio.h>
> +#include <linux/virtio_config.h>
> +#include <linux/virtio_ids.h>
> +
> +#define DRV_NAME "virtio-cpufreq"
> +
> +struct virtio_cpufreq_req {
> +       __le32 cpu_id;
> +       __le32 freq_khz;
> +} __packed;
> +
> +struct virtio_cpufreq {
> +       struct virtio_device *vdev;
> +       struct virtqueue *vq;
> +       struct mutex lock; /* serializes virtqueue requests */
> +};
> +
> +static struct virtio_cpufreq *virtio_cpufreq_dev;
> +
> +/*
> + * Placeholder OPPs so cpufreq has a table. Real platforms should
> + * populate this from the host or from firmware. Values match the
> + * existing QEMU/Android backend used for bring-up.
> + */
> +static struct cpufreq_frequency_table virtio_freq_table[] = {
> +       { .frequency = 1400000 },
> +       { .frequency = 1700000 },
> +       { .frequency = 3000000 },
> +       { .frequency = CPUFREQ_TABLE_END },
> +};
> +
> +static int virtio_cpufreq_init_policy(struct cpufreq_policy *policy)
> +{
> +       cpumask_clear(policy->cpus);
> +       cpumask_set_cpu(policy->cpu, policy->cpus);
> +
> +       policy->freq_table = virtio_freq_table;
> +       policy->cpuinfo.min_freq = 1400000;
> +       policy->cpuinfo.max_freq = 3000000;
> +       policy->cpuinfo.transition_latency = 0;
> +
> +       return 0;
> +}
> +
> +static int virtio_cpufreq_target_index(struct cpufreq_policy *policy,
> +                                      unsigned int index)
> +{
> +       struct cpufreq_freqs freqs;
> +
> +       freqs.old = policy->cur;
> +       freqs.new = policy->freq_table[index].frequency;
> +
> +       /*
> +        * This frontend does not change host frequency. It only keeps
> +        * the cpufreq core in sync so userspace can observe values.
> +        */
> +       cpufreq_freq_transition_begin(policy, &freqs);
> +       cpufreq_freq_transition_end(policy, &freqs, 0);
> +
> +       return 0;
> +}
> +
> +static unsigned int virtio_cpufreq_fallback(unsigned int cpu)
> +{
> +       struct cpufreq_policy *policy;
> +       unsigned int freq = 0;
> +
> +       policy = cpufreq_cpu_get(cpu);
> +       if (policy) {
> +               freq = policy->cur;
> +               cpufreq_cpu_put(policy);
> +       }
> +
> +       return freq;
> +}
> +
> +static void virtio_cpufreq_vq_cb(struct virtqueue *vq)
> +{
> +       struct completion *done;
> +       unsigned int len;
> +
> +       while ((done = virtqueue_get_buf(vq, &len)) != NULL)
> +               complete(done);
> +}
> +
> +static unsigned int virtio_cpufreq_get(unsigned int cpu)
> +{
> +       struct virtio_cpufreq *vc = virtio_cpufreq_dev;
> +       struct virtio_cpufreq_req *req;
> +       struct scatterlist out_sg, in_sg, *sgs[2];
> +       struct completion done;
> +       unsigned int freq_khz;
> +       int ret;
> +
> +       if (!vc || !vc->vq)
> +               return virtio_cpufreq_fallback(cpu);
> +
> +       req = kzalloc_obj(*req, GFP_KERNEL);
> +       if (!req)
> +               return virtio_cpufreq_fallback(cpu);
> +
> +       req->cpu_id = cpu_to_le32(cpu);
> +
> +       init_completion(&done);
> +       sg_init_one(&out_sg, req, sizeof(*req));
> +       sg_init_one(&in_sg, req, sizeof(*req));
> +       sgs[0] = &out_sg;
> +       sgs[1] = &in_sg;
> +
> +       mutex_lock(&vc->lock);
> +       ret = virtqueue_add_sgs(vc->vq, sgs, 1, 1, &done, GFP_KERNEL);
> +       if (ret) {
> +               mutex_unlock(&vc->lock);
> +               kfree(req);
> +               return virtio_cpufreq_fallback(cpu);
> +       }
> +
> +       virtqueue_kick(vc->vq);
> +       ret = wait_for_completion_timeout(&done, msecs_to_jiffies(1000));
> +       mutex_unlock(&vc->lock);
> +
> +       if (!ret) {
> +               /*
> +                * The buffer may still be on the virtqueue. Leak it
> +                * rather than freeing while the host may still write.
> +                */
> +               return virtio_cpufreq_fallback(cpu);
> +       }
> +
> +       freq_khz = le32_to_cpu(req->freq_khz);
> +       kfree(req);
> +
> +       if (!freq_khz)
> +               return virtio_cpufreq_fallback(cpu);
> +
> +       return freq_khz;
> +}
> +
> +static struct freq_attr *virtio_cpufreq_attr[] = {
> +       &cpufreq_freq_attr_scaling_available_freqs,
> +       NULL,
> +};
> +
> +static struct cpufreq_driver virtio_cpufreq_driver = {
> +       .name           = DRV_NAME,
> +       .flags          = CPUFREQ_CONST_LOOPS,
> +       .init           = virtio_cpufreq_init_policy,
> +       .verify         = cpufreq_generic_frequency_table_verify,
> +       .target_index   = virtio_cpufreq_target_index,
> +       .get            = virtio_cpufreq_get,
> +       .attr           = virtio_cpufreq_attr,
> +};
> +
> +static int virtio_cpufreq_probe(struct virtio_device *vdev)
> +{
> +       struct virtio_cpufreq *vc;
> +       struct virtqueue *vq;
> +       int ret;
> +
> +       vc = devm_kzalloc(&vdev->dev, sizeof(*vc), GFP_KERNEL);
> +       if (!vc)
> +               return -ENOMEM;
> +
> +       mutex_init(&vc->lock);
> +       vc->vdev = vdev;
> +
> +       vq = virtio_find_single_vq(vdev, virtio_cpufreq_vq_cb, "requests");
> +       if (IS_ERR(vq))
> +               return PTR_ERR(vq);
> +
> +       vc->vq = vq;
> +       vdev->priv = vc;
> +       virtio_cpufreq_dev = vc;
> +       virtio_device_ready(vdev);
> +
> +       ret = cpufreq_register_driver(&virtio_cpufreq_driver);
> +       if (ret) {
> +               vdev->config->del_vqs(vdev);
> +               virtio_cpufreq_dev = NULL;
> +               return ret;
> +       }
> +
> +       return 0;
> +}
> +
> +static void virtio_cpufreq_remove(struct virtio_device *vdev)
> +{
> +       cpufreq_unregister_driver(&virtio_cpufreq_driver);
> +       virtio_cpufreq_dev = NULL;
> +       vdev->config->del_vqs(vdev);
> +}
> +
> +static const struct virtio_device_id id_table[] = {
> +       { VIRTIO_ID_CPUFREQ, VIRTIO_DEV_ANY_ID },
> +       { 0 },
> +};
> +
> +static struct virtio_driver virtio_cpufreq_virtio_driver = {
> +       .driver.name    = DRV_NAME,
> +       .driver.owner   = THIS_MODULE,
> +       .id_table       = id_table,
> +       .probe          = virtio_cpufreq_probe,
> +       .remove         = virtio_cpufreq_remove,
> +};
> +
> +static int __init virtio_cpufreq_mod_init(void)
> +{
> +       return register_virtio_driver(&virtio_cpufreq_virtio_driver);
> +}
> +
> +static void __exit virtio_cpufreq_mod_exit(void)
> +{
> +       unregister_virtio_driver(&virtio_cpufreq_virtio_driver);
> +}
> +
> +module_init(virtio_cpufreq_mod_init);
> +module_exit(virtio_cpufreq_mod_exit);
> +
> +MODULE_DEVICE_TABLE(virtio, id_table);
> +MODULE_DESCRIPTION("Virtio cpufreq frontend");
> +MODULE_LICENSE("GPL");
> diff --git a/include/uapi/linux/virtio_ids.h b/include/uapi/linux/virtio_ids.h
> index f9056af0c622..f7e357f3bf94 100644
> --- a/include/uapi/linux/virtio_ids.h
> +++ b/include/uapi/linux/virtio_ids.h
> @@ -68,6 +68,7 @@
>  #define VIRTIO_ID_AUDIO_POLICY         39 /* virtio audio policy */
>  #define VIRTIO_ID_BT                   40 /* virtio bluetooth */
>  #define VIRTIO_ID_GPIO                 41 /* virtio gpio */
> +#define VIRTIO_ID_CPUFREQ              42 /* virtio cpufreq */
>  #define VIRTIO_ID_SPI                  45 /* virtio spi */
>  #define VIRTIO_ID_MEDIA                        48 /* virtio media */
>
> --
> 2.34.1
>

^ permalink raw reply	[flat|nested] 6+ messages in thread

end of thread, other threads:[~2026-09-24  7:19 UTC | newest]

Thread overview: 6+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-09-16  7:37 [RFC PATCH 0/1] cpufreq: virtio frontend to report host CPU frequency Baorui.Liu
2026-09-16  7:37 ` [RFC PATCH 1/1] cpufreq: virtio: add driver to report host frequency to guests Baorui.Liu
2026-09-22  9:02 ` [RFC PATCH v2 0/1] cpufreq: virtio frontend to report host CPU frequency Baorui.Liu
2026-09-22  9:22   ` Michael S. Tsirkin
2026-09-22  9:02 ` [RFC PATCH v2 1/1] cpufreq: virtio: add driver to report host frequency to guests Baorui.Liu
2026-09-24  7:18   ` Saravana Kannan

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox