* [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; 9+ 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] 9+ 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-16 7:49 ` sashiko-bot 2026-09-19 6:19 ` kernel test robot 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, 2 replies; 9+ 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] 9+ messages in thread
* Re: [RFC PATCH 1/1] cpufreq: virtio: add driver to report host frequency to guests 2026-09-16 7:37 ` [RFC PATCH 1/1] cpufreq: virtio: add driver to report host frequency to guests Baorui.Liu @ 2026-09-16 7:49 ` sashiko-bot 2026-09-19 6:19 ` kernel test robot 1 sibling, 0 replies; 9+ messages in thread From: sashiko-bot @ 2026-09-16 7:49 UTC (permalink / raw) To: Baorui.Liu; +Cc: Eugenio Perez, virtualization, Michael S. Tsirkin Thank you for your contribution! Sashiko AI review found 8 potential issue(s) to consider: - [Critical] core: stack use-after-free on virtqueue timeout - [High] virtio: overlapping IN/OUT scatterlist mappings cause DMA aliasing - [High] virtio: missing virtio_reset_device() before deleting virtqueues - [High] cpufreq: self-deadlock in .target_index due to double transition_begin - [High] virtio: concurrent virtqueue modification due to missing IRQ-safe locking - [Medium] cpufreq: global pointer clobbering breaks active device on probe failure - [Medium] virtio: premature allocation of unassigned device ID in UAPI - [Medium] cpufreq: mutex held across blocking timeout serializes all CPUs -- commit e9a0cf53064fc73cd4797ce5c61c255c2aef1ee7 Author: Baorui.Liu <baorliu@amd.com> cpufreq: virtio: add driver to report host frequency to guests 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. > diff --git a/drivers/cpufreq/virtio-cpufreq.c b/drivers/cpufreq/virtio-cpufreq.c > new file mode 100644 > index 0000000000000..23fde862a3137 > --- /dev/null > +++ b/drivers/cpufreq/virtio-cpufreq.c [ ... ] > +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); [Severity: High] Is it possible this leads to a self-deadlock? For drivers without the CPUFREQ_ASYNC_NOTIFICATION flag, the cpufreq core automatically invokes cpufreq_freq_transition_begin() on their behalf before calling the .target_index() callback. Calling it a second time here might trigger a double-invocation warning in the core and cause the current task to wait on itself forever. > + > + return 0; > +} [ ... ] > +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); [Severity: High] Does virtqueue_get_buf() need to be protected by an IRQ-safe spinlock? Since virtqueue_add_sgs() modifies the virtqueue from process context in virtio_cpufreq_get(), and this IRQ callback modifies the same state here, could they race and corrupt the virtqueue structures if an interrupt fires on the same or another CPU? > +} > + > +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)); [Severity: High] Does using the exact same buffer and size for both input and output scatterlists cause DMA aliasing? Since virtqueue_add_sgs() maps these simultaneously as DMA_TO_DEVICE and DMA_FROM_DEVICE, could this violate the DMA API? On systems relying on SWIOTLB or non-coherent caches, cache operations might trample each other and cause data corruption. Should the request fields be split into separate, non-overlapping buffers (for example, mapping &req->cpu_id for output and &req->freq_khz for input)? > + 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); [Severity: Medium] Does holding the mutex across the blocking wait_for_completion_timeout() call serialize all CPUs? By holding vc->lock while waiting for up to 1000ms, does this block all other CPUs from submitting frequency queries, potentially causing latency spikes globally across the system? [Severity: Critical] Can a timeout lead to a stack use-after-free? When wait_for_completion_timeout() times out, this function returns immediately and abandons the request in the virtqueue. Since the completion token is allocated on the local stack, could the hard IRQ callback virtio_cpufreq_vq_cb() write to deallocated stack memory when the device eventually completes the request? > + > + 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); [ ... ] > +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; [Severity: Medium] Could this unconditional assignment break an already active device if a second probe fails? If a second virtio-cpufreq device is probed, it overwrites the global virtio_cpufreq_dev pointer. When cpufreq_register_driver() fails with -EEXIST, the error path sets virtio_cpufreq_dev to NULL, destroying the global pointer for the first successfully probed device. [Severity: High] Does the device need to be stopped with virtio_reset_device(vdev) before deleting its virtqueues? Since virtio_device_ready(vdev) has already enabled the device, could deleting the queues without resetting the device first cause hypervisor crashes or memory corruption if the device is still attempting to access memory or raise IRQs? > + 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); [Severity: High] Is a call to virtio_reset_device(vdev) missing here before freeing the queues? The virtio specification and driver model generally require that the device is stopped prior to deleting its virtqueues. > +} [ ... ] > diff --git a/include/uapi/linux/virtio_ids.h b/include/uapi/linux/virtio_ids.h > index f9056af0c6223..f7e357f3bf946 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 */ [Severity: Medium] Is it premature to add an unallocated device ID into the UAPI headers? Since the commit message notes this ID is not yet allocated by the virtio specification, including it here might freeze the ABI and risk collisions if the standard assigns 42 to a different device type in the future. > #define VIRTIO_ID_SPI 45 /* virtio spi */ > #define VIRTIO_ID_MEDIA 48 /* virtio media */ -- Sashiko AI review · https://sashiko.dev/#/patchset/20260916073735.541-2-baorliu@amd.com?part=1 ^ permalink raw reply [flat|nested] 9+ messages in thread
* Re: [RFC PATCH 1/1] cpufreq: virtio: add driver to report host frequency to guests 2026-09-16 7:37 ` [RFC PATCH 1/1] cpufreq: virtio: add driver to report host frequency to guests Baorui.Liu 2026-09-16 7:49 ` sashiko-bot @ 2026-09-19 6:19 ` kernel test robot 1 sibling, 0 replies; 9+ messages in thread From: kernel test robot @ 2026-09-19 6:19 UTC (permalink / raw) To: Baorui.Liu; +Cc: oe-kbuild-all Hi Baorui.Liu, [This is a private test report for your RFC patch.] kernel test robot noticed the following build errors: [auto build test ERROR on rafael-pm/linux-next] [also build test ERROR on rafael-pm/bleeding-edge amd-pstate/linux-next amd-pstate/bleeding-edge mst-vhost/linux-next linus/master v7.3-rc3 next-20260918] [If your patch is applied to the wrong git tree, kindly drop us a note. And when submitting patch, we suggest to use '--base' as documented in https://git-scm.com/docs/git-format-patch#_base_tree_information] url: https://github.com/intel-lab-lkp/linux/commits/Baorui-Liu/cpufreq-virtio-add-driver-to-report-host-frequency-to-guests/20260916-153735 base: https://git.kernel.org/pub/scm/linux/kernel/git/rafael/linux-pm.git linux-next patch link: https://lore.kernel.org/r/20260916073735.541-2-baorliu%40amd.com patch subject: [RFC PATCH 1/1] cpufreq: virtio: add driver to report host frequency to guests config: sh-allmodconfig (https://download.01.org/0day-ci/archive/20260919/202609191437.OaXd0Blz-lkp@intel.com/config) compiler: sh4-linux-gcc (GCC) 16.1.0 reproduce (this is a W=1 build): (https://download.01.org/0day-ci/archive/20260919/202609191437.OaXd0Blz-lkp@intel.com/reproduce) If you fix the issue in a separate patch/commit (i.e. not just a new version of the same patch/commit), kindly add following tags | Reported-by: kernel test robot <lkp@intel.com> | Closes: https://lore.kernel.org/oe-kbuild-all/202609191437.OaXd0Blz-lkp@intel.com/ All errors (new ones prefixed by >>): >> drivers/cpufreq/virtio-cpufreq.c:165:27: error: 'cpufreq_generic_attr' undeclared here (not in a function); did you mean 'cpufreq_generic_get'? 165 | .attr = cpufreq_generic_attr, | ^~~~~~~~~~~~~~~~~~~~ | cpufreq_generic_get vim +165 drivers/cpufreq/virtio-cpufreq.c 157 158 static struct cpufreq_driver virtio_cpufreq_driver = { 159 .name = DRV_NAME, 160 .flags = CPUFREQ_CONST_LOOPS, 161 .init = virtio_cpufreq_init_policy, 162 .verify = cpufreq_generic_frequency_table_verify, 163 .target_index = virtio_cpufreq_target_index, 164 .get = virtio_cpufreq_get, > 165 .attr = cpufreq_generic_attr, 166 }; 167 -- 0-DAY CI Kernel Test Service https://github.com/intel/lkp-tests/wiki ^ permalink raw reply [flat|nested] 9+ 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; 9+ 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] 9+ 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; 9+ 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] 9+ 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-22 9:13 ` sashiko-bot 2026-09-24 7:18 ` Saravana Kannan 2 siblings, 2 replies; 9+ 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] 9+ 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-22 9:13 ` sashiko-bot 2026-09-24 7:18 ` Saravana Kannan 1 sibling, 0 replies; 9+ messages in thread From: sashiko-bot @ 2026-09-22 9:13 UTC (permalink / raw) To: Baorui.Liu; +Cc: virtualization, Michael S. Tsirkin, Eugenio Perez Thank you for your contribution! Sashiko AI review found 6 potential issue(s) to consider: - [Critical] virtio: stack use-after-free of completion struct on timeout - [High] virtio: DMA API violation by mapping same buffer for IN and OUT - [High] virtio: global virtio_cpufreq_dev clobbered on secondary device probe - [High] virtio: missing virtio_reset_device() before deleting virtqueues - [High] virtio: data race in virtqueue access between IRQ handler and get() - [High] cpufreq: explicit transition begin inside target_index causes deadlock -- commit e9d1547c4f482aad7a2452a1f8c4a163f33031fa Author: Baorui.Liu <baorliu@amd.com> cpufreq: virtio: add driver to report host frequency to guests Add a virtio frontend that queries the host for the frequency of the mapped pCPU and exposes it through the cpufreq .get() callback. Link: https://lore.kernel.org/oe-kbuild-all/202609191437.OaXd0Blz-lkp@intel.com/ > diff --git a/drivers/cpufreq/virtio-cpufreq.c b/drivers/cpufreq/virtio-cpufreq.c > new file mode 100644 > index 0000000000000..aa04fd7fd3436 > --- /dev/null > +++ b/drivers/cpufreq/virtio-cpufreq.c [ ... ] > +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); [Severity: High] Does this explicit call to cpufreq_freq_transition_begin() cause a deadlock? The cpufreq core already handles frequency transitions by setting policy->transition_ongoing and invoking cpufreq_freq_transition_begin() prior to calling .target_index(). Calling it again here will hit a true transition_ongoing flag and block forever on wait_event() inside the transition begin sequence. > + cpufreq_freq_transition_end(policy, &freqs, 0); > + > + return 0; > +} [ ... ] > +static void virtio_cpufreq_vq_cb(struct virtqueue *vq) > +{ > + struct completion *done; > + unsigned int len; > + > + while ((done = virtqueue_get_buf(vq, &len)) != NULL) [Severity: High] Can this lockless access to the virtqueue in the IRQ handler race with virtqueue_add_sgs() in virtio_cpufreq_get()? If a timeout occurs in virtio_cpufreq_get(), the mutex is dropped, allowing a new request to start. A delayed host reply can cause virtqueue_get_buf() to execute concurrently with a new virtqueue_add_sgs() request, corrupting the virtqueue free list. > + complete(done); [Severity: Critical] Does this write to a destroyed stack frame if a timeout occurred? When virtio_cpufreq_get() times out, it returns and destroys the stack-allocated struct completion done. However, the pointer remains queued in the virtqueue. When the host eventually responds, the IRQ handler extracts the dangling pointer and executes complete(done), causing stack memory corruption. > +} > + > +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)); [Severity: High] Is it a violation of the DMA API to map the exact same buffer (req) for both the OUT and IN scatterlists? This causes the underlying DMA API to map the same memory with DMA_TO_DEVICE and DMA_FROM_DEVICE concurrently, violating ownership rules and causing bounce buffer (SWIOTLB) cache corruption. It also directly violates the Virtio specification against descriptors containing both device-readable and device-writable memory. > + 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); [Severity: High] Does dropping the mutex on timeout allow virtio_cpufreq_vq_cb() to race with a new request? > + > + 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); [Severity: Critical] Does returning here abandon the stack-allocated struct completion done while it is still in the virtqueue? The function returns on timeout without removing the request from the virtqueue. When the host eventually responds, virtio_cpufreq_vq_cb() will call complete(done) on a destroyed stack frame. > + } [ ... ] > +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; [Severity: High] Does this unconditional assignment clobber the global pointer if a second device is probed? If a second device is probed, it overwrites the active virtio_cpufreq_dev pointer. Registration of the second cpufreq driver fails with -EEXIST, causing the error path to set the global pointer to NULL. This severs the first, still-active device permanently, causing all subsequent .get() callbacks to fail. > + virtio_device_ready(vdev); > + > + ret = cpufreq_register_driver(&virtio_cpufreq_driver); > + if (ret) { > + vdev->config->del_vqs(vdev); [Severity: High] Is it safe to delete virtqueues while the device is in the READY state without calling virtio_reset_device() first? Because virtio_reset_device() is not called first to stop the device, the host may still actively process requests or DMA into the rings, causing a use-after-free corruption of kernel heap memory. > + virtio_cpufreq_dev = NULL; [Severity: High] Does nulling the global pointer on a failed secondary probe break the primary active device? > + 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); [Severity: High] Are virtqueues being deleted without first calling virtio_reset_device()? Allowing the virtqueues to be freed while the device is fully active could lead to the host continuing to actively process requests or DMA into the rings, causing a use-after-free corruption. > +} -- Sashiko AI review · https://sashiko.dev/#/patchset/20260922090224.430-1-baorliu@amd.com?part=1 ^ permalink raw reply [flat|nested] 9+ 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-22 9:13 ` sashiko-bot @ 2026-09-24 7:18 ` Saravana Kannan 1 sibling, 0 replies; 9+ 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] 9+ messages in thread
end of thread, other threads:[~2026-09-24 7:19 UTC | newest] Thread overview: 9+ 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-16 7:49 ` sashiko-bot 2026-09-19 6:19 ` kernel test robot 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-22 9:13 ` sashiko-bot 2026-09-24 7:18 ` Saravana Kannan
This is an external index of several public inboxes, see mirroring instructions on how to clone and mirror all data and code used by this external index.