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