* [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