Path: blob/main/e2e_tests/guest_under_test/kernel/patches/virtio_pvclock.patch
5394 views
From 7f8eba774852bad453f9015ca408612337acc86d Mon Sep 17 00:00:00 20011From: Hikaru Nishida <[email protected]>2Date: Wed, 24 Jan 2024 14:23:40 +09003Subject: [PATCH] CHROMIUM: virtio_pvclock: port driver impl from Android45Initial virtio_pvclock device driver implementation from Android.67This is a squash of aosp/1959549, aosp/1962079, aosp/2395934:8- ANDROID: virtio: virtio_pvclock: initial driver impl9- ANDROID: virtio: virtio_pvclock: call vclocks_set_used10- ANDROID: virtio: virtio_pvclock: fix rating decl1112BUG=b:271057959, b:29525664113TEST=make O=../v5.10-arcvm_build x86_64_arcvm_defconfig14TEST=make -j`nproc` O=../v5.10-arcvm_build bzImage15TEST=tast run ${DUT} arc.PlayStore.vm arc.Suspend.*16UPSTREAM-TASK=b:3216182821718Change-Id: I068da510e17283b13791e3ae51542b74d460197519Signed-off-by: Hikaru Nishida <[email protected]>20---21arch/x86/entry/vdso/vma.c | 1 +22arch/x86/kernel/pvclock.c | 2 +23drivers/virtio/Kconfig | 36 +++24drivers/virtio/Makefile | 1 +25drivers/virtio/virtio_pvclock.c | 345 ++++++++++++++++++++++++++++26include/uapi/linux/virtio_ids.h | 3 +27include/uapi/linux/virtio_pvclock.h | 74 ++++++28kernel/time/timekeeping.c | 4 +298 files changed, 466 insertions(+)30create mode 100644 drivers/virtio/virtio_pvclock.c31create mode 100644 include/uapi/linux/virtio_pvclock.h3233diff --git a/arch/x86/entry/vdso/vma.c b/arch/x86/entry/vdso/vma.c34index 128866139..51520db4a 10064435--- a/arch/x86/entry/vdso/vma.c36+++ b/arch/x86/entry/vdso/vma.c37@@ -39,6 +39,7 @@ struct vdso_data *arch_get_vdso_data(void *vvar_page)38#undef EMIT_VVAR3940unsigned int vclocks_used __read_mostly;41+EXPORT_SYMBOL_GPL(vclocks_used);4243#if defined(CONFIG_X86_64)44unsigned int __read_mostly vdso64_enabled = 1;45diff --git a/arch/x86/kernel/pvclock.c b/arch/x86/kernel/pvclock.c46index eda37df01..54b41d759 10064447--- a/arch/x86/kernel/pvclock.c48+++ b/arch/x86/kernel/pvclock.c49@@ -109,6 +109,7 @@ u64 pvclock_clocksource_read(struct pvclock_vcpu_time_info *src)5051return ret;52}53+EXPORT_SYMBOL_GPL(pvclock_clocksource_read);5455void pvclock_read_wallclock(struct pvclock_wall_clock *wall_clock,56struct pvclock_vcpu_time_info *vcpu_time,57@@ -148,6 +149,7 @@ void pvclock_set_pvti_cpu0_va(struct pvclock_vsyscall_time_info *pvti)58WARN_ON(vclock_was_used(VDSO_CLOCKMODE_PVCLOCK));59pvti_cpu0_va = pvti;60}61+EXPORT_SYMBOL_GPL(pvclock_set_pvti_cpu0_va);6263struct pvclock_vsyscall_time_info *pvclock_get_pvti_cpu0_va(void)64{65diff --git a/drivers/virtio/Kconfig b/drivers/virtio/Kconfig66index 0a53a6123..72921084e 10064467--- a/drivers/virtio/Kconfig68+++ b/drivers/virtio/Kconfig69@@ -173,4 +173,40 @@ config VIRTIO_DMA_SHARED_BUFFER70This option adds a flavor of dma buffers that are backed by71virtio resources.7273+config VIRTIO_PVCLOCK74+ tristate "Virtio pvclock driver"75+ depends on VIRTIO76+ depends on X8677+ select PARAVIRT_CLOCK78+ help79+ This driver supports virtio pvclock devices.80+ It helps emulating CLOCK_BOOTTIME behavior around host's suspend / resume81+ without actually suspends the guest with the hypervisor's support.82+83+ If unsure, say M.84+85+config VIRTIO_PVCLOCK86+ tristate "Virtio pvclock driver"87+ depends on VIRTIO88+ depends on X8689+ select PARAVIRT_CLOCK90+ help91+ This driver supports virtio pvclock devices.92+ It helps emulating CLOCK_BOOTTIME behavior around host's suspend / resume93+ without actually suspends the guest with the hypervisor's support.94+95+ If unsure, say M.96+97+config VIRTIO_PVCLOCK98+ tristate "Virtio pvclock driver"99+ depends on VIRTIO100+ depends on X86101+ select PARAVIRT_CLOCK102+ help103+ This driver supports virtio pvclock devices.104+ It helps emulating CLOCK_BOOTTIME behavior around host's suspend / resume105+ without actually suspends the guest with the hypervisor's support.106+107+ If unsure, say M.108+109endif # VIRTIO_MENU110diff --git a/drivers/virtio/Makefile b/drivers/virtio/Makefile111index 8e98d2491..79e6dea7c 100644112--- a/drivers/virtio/Makefile113+++ b/drivers/virtio/Makefile114@@ -12,3 +12,4 @@ obj-$(CONFIG_VIRTIO_INPUT) += virtio_input.o115obj-$(CONFIG_VIRTIO_VDPA) += virtio_vdpa.o116obj-$(CONFIG_VIRTIO_MEM) += virtio_mem.o117obj-$(CONFIG_VIRTIO_DMA_SHARED_BUFFER) += virtio_dma_buf.o118+obj-$(CONFIG_VIRTIO_PVCLOCK) += virtio_pvclock.o119diff --git a/drivers/virtio/virtio_pvclock.c b/drivers/virtio/virtio_pvclock.c120new file mode 100644121index 000000000..7d6fd0b52122--- /dev/null123+++ b/drivers/virtio/virtio_pvclock.c124@@ -0,0 +1,345 @@125+// SPDX-License-Identifier: GPL-2.0-or-later126+/*127+ * Virtio pvclock implementation.128+ *129+ * Copyright (C) 2021 Google, Inc.130+ */131+132+#include <linux/clocksource.h>133+#include <linux/dma-mapping.h>134+#include <linux/module.h>135+#include <linux/slab.h>136+#include <linux/virtio.h>137+#include <linux/virtio_pvclock.h>138+#include <linux/workqueue.h>139+#include <asm/pvclock.h>140+141+enum virtio_pvclock_vq {142+ VIRTIO_PVCLOCK_VQ_SET_PVCLOCK_PAGE,143+ VIRTIO_PVCLOCK_VQ_MAX144+};145+146+struct virtio_pvclock {147+ struct virtio_device *vdev;148+ struct virtqueue *set_pvclock_page_vq;149+ struct virtio_pvclock_set_pvclock_page_req set_page_request;150+151+ /* Updating the suspend time happens via scheduled work. */152+ struct work_struct update_suspend_time_work;153+ /* Creating the clocksource happens via scheduled work. */154+ struct work_struct create_clocksource_work;155+156+ /* Synchronize access/update to injected_suspend_ns. */157+ struct mutex inject_suspend_lock;158+ /* Total ns injected as sleep time. */159+ u64 injected_suspend_ns;160+161+ /* DMA address of virtio_pvclock_page. */162+ dma_addr_t pvclock_page_dma_addr;163+};164+165+/* CPU accessible pointer to pvclock page. */166+static struct pvclock_vsyscall_time_info *virtio_pvclock_page;167+168+static struct virtio_device_id id_table[] = {169+ { VIRTIO_ID_PVCLOCK, VIRTIO_DEV_ANY_ID },170+ { 0 },171+};172+173+void update_suspend_time(struct work_struct *work)174+{175+ u64 suspend_ns, suspend_time_delta = 0;176+ struct timespec64 inject_time;177+ struct virtio_pvclock *vp;178+179+ vp = container_of(work, struct virtio_pvclock,180+ update_suspend_time_work);181+182+ virtio_cread(vp->vdev, struct virtio_pvclock_config, suspend_time_ns,183+ &suspend_ns);184+185+ mutex_lock(&vp->inject_suspend_lock);186+ if (suspend_ns > vp->injected_suspend_ns) {187+ suspend_time_delta = suspend_ns - vp->injected_suspend_ns;188+ vp->injected_suspend_ns = suspend_ns;189+ }190+ mutex_unlock(&vp->inject_suspend_lock);191+192+ if (suspend_time_delta == 0) {193+ dev_err(&vp->vdev->dev,194+ "%s: suspend_time_ns is less than injected_suspend_ns\n",195+ __func__);196+ return;197+ }198+199+ inject_time = ns_to_timespec64(suspend_time_delta);200+201+ timekeeping_inject_sleeptime64(&inject_time);202+203+ dev_info(&vp->vdev->dev, "injected sleeptime: %llu ns\n",204+ suspend_time_delta);205+}206+207+static u64 virtio_pvclock_clocksource_read(struct clocksource *cs)208+{209+ u64 ret;210+211+ preempt_disable_notrace();212+ ret = pvclock_clocksource_read(&virtio_pvclock_page->pvti);213+ preempt_enable_notrace();214+ return ret;215+}216+217+static int virtio_pvclock_cs_enable(struct clocksource *cs)218+{219+ if (cs->vdso_clock_mode == VDSO_CLOCKMODE_PVCLOCK)220+ vclocks_set_used(VDSO_CLOCKMODE_PVCLOCK);221+ return 0;222+}223+224+static struct clocksource virtio_pvclock_clocksource = {225+ .name = "virtio-pvclock",226+ .rating = 200, /* default rating, updated by virtpvclock_validate */227+ .read = virtio_pvclock_clocksource_read,228+ .mask = CLOCKSOURCE_MASK(64),229+ .flags = CLOCK_SOURCE_IS_CONTINUOUS,230+ .enable = virtio_pvclock_cs_enable,231+};232+233+static void set_pvclock_page_callback(struct virtqueue *vq)234+{235+ struct virtio_pvclock *vp = vq->vdev->priv;236+237+ if (vp->set_page_request.status != VIRTIO_PVCLOCK_S_OK) {238+ dev_err(&vq->vdev->dev,239+ "%s: set_pvclock_page req status is %u\n", __func__,240+ vp->set_page_request.status);241+ return;242+ }243+244+ /*245+ * Create the actual clocksource via a work queue because we're in an246+ * interrupt handler right now.247+ */248+ schedule_work(&vp->create_clocksource_work);249+}250+251+static void create_clocksource(struct work_struct *work)252+{253+ struct virtio_pvclock *vp;254+255+ vp = container_of(work, struct virtio_pvclock, create_clocksource_work);256+257+ /*258+ * VDSO pvclock can only be used if the TSCs are stable. The device also259+ * must set PVCLOCK_TSC_STABLE_BIT in the pvclock flags field.260+ */261+ if (virtio_has_feature(vp->vdev, VIRTIO_PVCLOCK_F_TSC_STABLE)) {262+ pvclock_set_pvti_cpu0_va(virtio_pvclock_page);263+ virtio_pvclock_clocksource.vdso_clock_mode =264+ VDSO_CLOCKMODE_PVCLOCK;265+ }266+267+ clocksource_register_hz(&virtio_pvclock_clocksource, NSEC_PER_SEC);268+269+ dev_info(&vp->vdev->dev, "registered clocksource\n");270+}271+272+static void virtpvclock_changed(struct virtio_device *vdev)273+{274+ struct virtio_pvclock *vp = vdev->priv;275+276+ schedule_work(&vp->update_suspend_time_work);277+}278+279+static int set_pvclock_page(struct virtio_pvclock *vp)280+{281+ struct scatterlist sg;282+ int err;283+284+ vp->set_page_request.pvclock_page_pa = vp->pvclock_page_dma_addr;285+ vp->set_page_request.system_time = ktime_get();286+ vp->set_page_request.tsc_timestamp = rdtsc_ordered();287+288+ sg_init_one(&sg, &vp->set_page_request, sizeof(vp->set_page_request));289+ err = virtqueue_add_outbuf(vp->set_pvclock_page_vq, &sg, 1, vp,290+ GFP_KERNEL);291+292+ if (err) {293+ dev_err(&vp->vdev->dev, "%s: failed to add output\n", __func__);294+ return err;295+ }296+ virtqueue_kick(vp->set_pvclock_page_vq);297+298+ return 0;299+}300+301+static int init_vqs(struct virtio_pvclock *vp)302+{303+ vq_callback_t *callbacks[VIRTIO_PVCLOCK_VQ_MAX];304+ struct virtqueue *vqs[VIRTIO_PVCLOCK_VQ_MAX];305+ const char *names[VIRTIO_PVCLOCK_VQ_MAX];306+ int err;307+308+ callbacks[VIRTIO_PVCLOCK_VQ_SET_PVCLOCK_PAGE] =309+ set_pvclock_page_callback;310+ names[VIRTIO_PVCLOCK_VQ_SET_PVCLOCK_PAGE] = "set_pvclock_page";311+312+ err = vp->vdev->config->find_vqs(vp->vdev, VIRTIO_PVCLOCK_VQ_MAX, vqs,313+ callbacks, names, NULL, NULL);314+ if (err)315+ return err;316+317+ vp->set_pvclock_page_vq = vqs[VIRTIO_PVCLOCK_VQ_SET_PVCLOCK_PAGE];318+319+ return set_pvclock_page(vp);320+}321+322+static int virtpvclock_probe(struct virtio_device *vdev)323+{324+ struct virtio_pvclock *vp;325+ int err;326+327+ if (!vdev->config->get) {328+ dev_err(&vdev->dev, "%s: config access disabled\n", __func__);329+ return -EINVAL;330+ }331+332+ vp = kzalloc(sizeof(*vp), GFP_KERNEL);333+ if (!vp) {334+ err = -ENOMEM;335+ goto out;336+ }337+338+ virtio_pvclock_page =339+ dma_alloc_coherent(vdev->dev.parent,340+ sizeof(*virtio_pvclock_page),341+ &vp->pvclock_page_dma_addr, GFP_KERNEL);342+343+ if (!virtio_pvclock_page) {344+ err = -ENOMEM;345+ goto out_free_vp;346+ }347+348+ INIT_WORK(&vp->update_suspend_time_work, update_suspend_time);349+ INIT_WORK(&vp->create_clocksource_work, create_clocksource);350+ mutex_init(&vp->inject_suspend_lock);351+352+ vp->vdev = vdev;353+ vdev->priv = vp;354+355+ err = init_vqs(vp);356+ if (err)357+ goto out_free_pvclock_page;358+359+ virtio_device_ready(vdev);360+361+ return 0;362+363+out_free_pvclock_page:364+ dma_free_coherent(vdev->dev.parent, sizeof(*virtio_pvclock_page),365+ virtio_pvclock_page, vp->pvclock_page_dma_addr);366+367+out_free_vp:368+ kfree(vp);369+out:370+ return err;371+}372+373+static void remove_common(struct virtio_pvclock *vp)374+{375+ /* Now we reset the device so we can clean up the queues. */376+ vp->vdev->config->reset(vp->vdev);377+378+ vp->vdev->config->del_vqs(vp->vdev);379+}380+381+static void virtpvclock_remove(struct virtio_device *vdev)382+{383+ struct virtio_pvclock *vp = vdev->priv;384+385+ remove_common(vp);386+387+ dma_free_coherent(vdev->dev.parent, sizeof(*virtio_pvclock_page),388+ virtio_pvclock_page, vp->pvclock_page_dma_addr);389+390+ kfree(vp);391+}392+393+#ifdef CONFIG_PM_SLEEP394+static int virtpvclock_freeze(struct virtio_device *vdev)395+{396+ struct virtio_pvclock *vp = vdev->priv;397+398+ /*399+ * The workqueue is already frozen by the PM core before this400+ * function is called.401+ */402+ remove_common(vp);403+ return 0;404+}405+406+static int virtpvclock_restore(struct virtio_device *vdev)407+{408+ int ret;409+410+ ret = init_vqs(vdev->priv);411+ if (ret)412+ return ret;413+414+ virtio_device_ready(vdev);415+416+ return 0;417+}418+#endif419+420+#define MAX_CLOCKSOURCE_RATING 450421+422+static int virtpvclock_validate(struct virtio_device *vdev)423+{424+ uint32_t rating;425+426+ if (!virtio_has_feature(vdev, VIRTIO_PVCLOCK_F_CLOCKSOURCE_RATING))427+ return 0;428+429+ rating = virtio_cread32(vdev, offsetof(struct virtio_pvclock_config,430+ clocksource_rating));431+ if (rating > MAX_CLOCKSOURCE_RATING) {432+ dev_warn(433+ &vdev->dev,434+ "device clocksource rating too high: %u, using max rating: %u\n",435+ rating, MAX_CLOCKSOURCE_RATING);436+ __virtio_clear_bit(vdev, VIRTIO_PVCLOCK_F_CLOCKSOURCE_RATING);437+ virtio_pvclock_clocksource.rating = (int)MAX_CLOCKSOURCE_RATING;438+ } else {439+ dev_info(&vdev->dev, "clocksource rating set to %u\n", rating);440+ virtio_pvclock_clocksource.rating = (int)rating;441+ }442+443+ return 0;444+}445+446+static unsigned int features[] = { VIRTIO_PVCLOCK_F_TSC_STABLE,447+ VIRTIO_PVCLOCK_F_INJECT_SLEEP,448+ VIRTIO_PVCLOCK_F_CLOCKSOURCE_RATING };449+450+static struct virtio_driver virtio_pvclock_driver = {451+ .feature_table = features,452+ .feature_table_size = ARRAY_SIZE(features),453+ .driver.name = KBUILD_MODNAME,454+ .driver.owner = THIS_MODULE,455+ .id_table = id_table,456+ .validate = virtpvclock_validate,457+ .probe = virtpvclock_probe,458+ .remove = virtpvclock_remove,459+ .config_changed = virtpvclock_changed,460+#ifdef CONFIG_PM_SLEEP461+ .freeze = virtpvclock_freeze,462+ .restore = virtpvclock_restore,463+#endif464+};465+466+module_virtio_driver(virtio_pvclock_driver);467+MODULE_DEVICE_TABLE(virtio, id_table);468+MODULE_DESCRIPTION("Virtio pvclock driver");469+MODULE_LICENSE("GPL");470diff --git a/include/uapi/linux/virtio_ids.h b/include/uapi/linux/virtio_ids.h471index 7aa2eb766..c4ce86b44 100644472--- a/include/uapi/linux/virtio_ids.h473+++ b/include/uapi/linux/virtio_ids.h474@@ -69,6 +69,9 @@475#define VIRTIO_ID_BT 40 /* virtio bluetooth */476#define VIRTIO_ID_GPIO 41 /* virtio gpio */477478+/* Chrome OS-specific devices */479+#define VIRTIO_ID_PVCLOCK 61 /* virtio pvclock (experimental id) */480+481/*482* Virtio Transitional IDs483*/484diff --git a/include/uapi/linux/virtio_pvclock.h b/include/uapi/linux/virtio_pvclock.h485new file mode 100644486index 000000000..808d47b21487--- /dev/null488+++ b/include/uapi/linux/virtio_pvclock.h489@@ -0,0 +1,74 @@490+/* SPDX-License-Identifier: (GPL-2.0 WITH Linux-syscall-note) OR BSD-3-Clause */491+/* This header is BSD licensed so anyone can use the definitions to implement492+ * compatible drivers/servers.493+ *494+ * Redistribution and use in source and binary forms, with or without495+ * modification, are permitted provided that the following conditions496+ * are met:497+ * 1. Redistributions of source code must retain the above copyright498+ * notice, this list of conditions and the following disclaimer.499+ * 2. Redistributions in binary form must reproduce the above copyright500+ * notice, this list of conditions and the following disclaimer in the501+ * documentation and/or other materials provided with the distribution.502+ * 3. Neither the name of IBM nor the names of its contributors503+ * may be used to endorse or promote products derived from this software504+ * without specific prior written permission.505+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS IS'' AND506+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE507+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE508+ * ARE DISCLAIMED. IN NO EVENT SHALL IBM OR CONTRIBUTORS BE LIABLE509+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL510+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS511+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)512+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT513+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY514+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF515+ * SUCH DAMAGE.516+ */517+518+#ifndef _LINUX_VIRTIO_PVCLOCK_H519+#define _LINUX_VIRTIO_PVCLOCK_H520+521+#include <linux/types.h>522+#include <linux/virtio_types.h>523+#include <linux/virtio_ids.h>524+#include <linux/virtio_config.h>525+526+/* The feature bitmap for virtio pvclock */527+/* TSC is stable */528+#define VIRTIO_PVCLOCK_F_TSC_STABLE 0529+/* Inject sleep for suspend */530+#define VIRTIO_PVCLOCK_F_INJECT_SLEEP 1531+/* Use device clocksource rating */532+#define VIRTIO_PVCLOCK_F_CLOCKSOURCE_RATING 2533+534+struct virtio_pvclock_config {535+ /* Number of ns the VM has been suspended without guest suspension. */536+ __u64 suspend_time_ns;537+ /* Device-suggested rating of the pvclock clocksource. */538+ __u32 clocksource_rating;539+ __u32 padding;540+};541+542+/* Status values for a virtio_pvclock request. */543+#define VIRTIO_PVCLOCK_S_OK 0544+#define VIRTIO_PVCLOCK_S_IOERR 1545+#define VIRTIO_PVCLOCK_S_UNSUPP 2546+547+/*548+ * Virtio pvclock set pvclock page request. Sets up the shared memory549+ * pvclock_vsyscall_time_info struct.550+ */551+struct virtio_pvclock_set_pvclock_page_req {552+ /* Physical address of pvclock_vsyscall_time_info. */553+ __u64 pvclock_page_pa;554+ /* Current system time. */555+ __u64 system_time;556+ /* Current tsc value. */557+ __u64 tsc_timestamp;558+ /* Status of this request, one of VIRTIO_PVCLOCK_S_*. */559+ __u8 status;560+ __u8 padding[7];561+};562+563+#endif /* _LINUX_VIRTIO_PVCLOCK_H */564diff --git a/kernel/time/timekeeping.c b/kernel/time/timekeeping.c565index 221c8c404..3fd9bb166 100644566--- a/kernel/time/timekeeping.c567+++ b/kernel/time/timekeeping.c568@@ -1735,7 +1735,10 @@ bool timekeeping_rtc_skipsuspend(void)569{570return persistent_clock_exists;571}572+#endif573574+#if (defined(CONFIG_PM_SLEEP) && defined(CONFIG_RTC_HCTOSYS_DEVICE)) || \575+ defined(CONFIG_VIRTIO_PVCLOCK)576/**577* timekeeping_inject_sleeptime64 - Adds suspend interval to timeekeeping values578* @delta: pointer to a timespec64 delta value579@@ -1769,6 +1772,7 @@ void timekeeping_inject_sleeptime64(const struct timespec64 *delta)580/* Signal hrtimers about time change */581clock_was_set(CLOCK_SET_WALL | CLOCK_SET_BOOT);582}583+EXPORT_SYMBOL_GPL(timekeeping_inject_sleeptime64);584#endif585586/**587--5882.43.0.429.g432eaa2c6b-goog589590591592