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