* [PATCH v2] Drivers: hv: util: Cosmetic changes for hv_utils_transport.c
From: Abhishek Tiwari @ 2025-09-01 12:00 UTC (permalink / raw)
To: abhitiwari, kys, haiyangz, wei.liu, decui, linux-hyperv,
linux-kernel, ssengar
Cc: Abhishek Tiwari
Fix issues reported by checkpatch.pl script for hv_utils_transport.c file
- Update pr_warn() calls to use __func__ for consistent logging context.
- else should follow close brace '}'
No functional changes intended.
Signed-off-by: Abhishek Tiwari <abhitiwari@linux.microsoft.com>
Reviewed-by: Naman Jain <namjain@linux.microsoft.com>
---
Changes in v2:
- Replace "x86/hyperv:" with "Drivers: hv: util:" in patch title.
---
drivers/hv/hv_utils_transport.c | 10 +++++-----
1 file changed, 5 insertions(+), 5 deletions(-)
diff --git a/drivers/hv/hv_utils_transport.c b/drivers/hv/hv_utils_transport.c
index 832885198643..b3de35ff6334 100644
--- a/drivers/hv/hv_utils_transport.c
+++ b/drivers/hv/hv_utils_transport.c
@@ -129,8 +129,7 @@ static int hvt_op_open(struct inode *inode, struct file *file)
* device gets released.
*/
hvt->mode = HVUTIL_TRANSPORT_CHARDEV;
- }
- else if (hvt->mode == HVUTIL_TRANSPORT_NETLINK) {
+ } else if (hvt->mode == HVUTIL_TRANSPORT_NETLINK) {
/*
* We're switching from netlink communication to using char
* device. Issue the reset first.
@@ -195,7 +194,7 @@ static void hvt_cn_callback(struct cn_msg *msg, struct netlink_skb_parms *nsp)
}
spin_unlock(&hvt_list_lock);
if (!hvt_found) {
- pr_warn("hvt_cn_callback: spurious message received!\n");
+ pr_warn("%s: spurious message received!\n", __func__);
return;
}
@@ -210,7 +209,7 @@ static void hvt_cn_callback(struct cn_msg *msg, struct netlink_skb_parms *nsp)
if (hvt->mode == HVUTIL_TRANSPORT_NETLINK)
hvt_found->on_msg(msg->data, msg->len);
else
- pr_warn("hvt_cn_callback: unexpected netlink message!\n");
+ pr_warn("%s: unexpected netlink message!\n", __func__);
mutex_unlock(&hvt->lock);
}
@@ -260,8 +259,9 @@ int hvutil_transport_send(struct hvutil_transport *hvt, void *msg, int len,
hvt->outmsg_len = len;
hvt->on_read = on_read_cb;
wake_up_interruptible(&hvt->outmsg_q);
- } else
+ } else {
ret = -ENOMEM;
+ }
out_unlock:
mutex_unlock(&hvt->lock);
return ret;
--
2.43.0
^ permalink raw reply related
* [PATCH v2 4/4] drm/hypervdrm: Use vblank timer
From: Thomas Zimmermann @ 2025-09-01 11:07 UTC (permalink / raw)
To: louis.chauvet, drawat.floss, hamohammed.sa, melissa.srw, mhklinux,
simona, airlied, maarten.lankhorst
Cc: dri-devel, linux-hyperv, Thomas Zimmermann
In-Reply-To: <20250901111241.233875-1-tzimmermann@suse.de>
HyperV's virtual hardware does not provide vblank interrupts. Use a
vblank timer to simulate the interrupt. Rate-limits the display's
update frequency to the display-mode settings. Avoids excessive CPU
overhead with compositors that do not rate-limit their output.
Signed-off-by: Thomas Zimmermann <tzimmermann@suse.de>
---
drivers/gpu/drm/hyperv/hyperv_drm_modeset.c | 11 +++++++++++
1 file changed, 11 insertions(+)
diff --git a/drivers/gpu/drm/hyperv/hyperv_drm_modeset.c b/drivers/gpu/drm/hyperv/hyperv_drm_modeset.c
index 945b9482bcb3..6e6eb1c12a68 100644
--- a/drivers/gpu/drm/hyperv/hyperv_drm_modeset.c
+++ b/drivers/gpu/drm/hyperv/hyperv_drm_modeset.c
@@ -19,6 +19,8 @@
#include <drm/drm_probe_helper.h>
#include <drm/drm_panic.h>
#include <drm/drm_plane.h>
+#include <drm/drm_vblank.h>
+#include <drm/drm_vblank_helper.h>
#include "hyperv_drm.h"
@@ -111,11 +113,15 @@ static void hyperv_crtc_helper_atomic_enable(struct drm_crtc *crtc,
crtc_state->mode.hdisplay,
crtc_state->mode.vdisplay,
plane_state->fb->pitches[0]);
+
+ drm_crtc_vblank_on(crtc);
}
static const struct drm_crtc_helper_funcs hyperv_crtc_helper_funcs = {
.atomic_check = drm_crtc_helper_atomic_check,
+ .atomic_flush = drm_crtc_vblank_atomic_flush,
.atomic_enable = hyperv_crtc_helper_atomic_enable,
+ .atomic_disable = drm_crtc_vblank_atomic_disable,
};
static const struct drm_crtc_funcs hyperv_crtc_funcs = {
@@ -125,6 +131,7 @@ static const struct drm_crtc_funcs hyperv_crtc_funcs = {
.page_flip = drm_atomic_helper_page_flip,
.atomic_duplicate_state = drm_atomic_helper_crtc_duplicate_state,
.atomic_destroy_state = drm_atomic_helper_crtc_destroy_state,
+ DRM_CRTC_VBLANK_TIMER_FUNCS,
};
static int hyperv_plane_atomic_check(struct drm_plane *plane,
@@ -321,6 +328,10 @@ int hyperv_mode_config_init(struct hyperv_drm_device *hv)
return ret;
}
+ ret = drm_vblank_init(dev, 1);
+ if (ret)
+ return ret;
+
drm_mode_config_reset(dev);
return 0;
--
2.50.1
^ permalink raw reply related
* [PATCH v2 1/4] drm/vblank: Add vblank timer
From: Thomas Zimmermann @ 2025-09-01 11:06 UTC (permalink / raw)
To: louis.chauvet, drawat.floss, hamohammed.sa, melissa.srw, mhklinux,
simona, airlied, maarten.lankhorst
Cc: dri-devel, linux-hyperv, Thomas Zimmermann
In-Reply-To: <20250901111241.233875-1-tzimmermann@suse.de>
The vblank timer simulates a vblank interrupt for hardware without
support. Rate-limits the display update frequency.
DRM drivers for hardware without vblank support apply display updates
ASAP. A vblank event informs DRM clients of the completed update.
Userspace compositors immediately schedule the next update, which
creates significant load on virtualization outputs. Display updates
are usually fast on virtualization outputs, as their framebuffers are
in regular system memory and there's no hardware vblank interrupt to
throttle the update rate.
The vblank timer is a HR timer that signals the vblank in software.
It limits the update frequency of a DRM driver similar to a hardware
vblank interrupt. The timer is not synchronized to the actual vblank
interval of the display.
The code has been adopted from vkms, which added the funtionality
in commit 3a0709928b17 ("drm/vkms: Add vblank events simulated by
hrtimers").
The new implementation is part of the existing vblank support,
which sets up the timer automatically. Drivers only have to start
and cancel the vblank timer as part of enabling and disabling the
CRTC. The new vblank helper library provides callbacks for struct
drm_crtc_funcs.
The standard way for handling vblank is to call drm_crtc_handle_vblank().
Drivers that require additional processing, such as vkms, can init
handle_vblank_timeout in struct drm_crtc_helper_funcs to refer to
their timeout handler.
v2:
- implement vblank timer entirely in vblank helpers
- downgrade overrun warning to debug
- fix docs
Signed-off-by: Thomas Zimmermann <tzimmermann@suse.de>
Tested-by: Louis Chauvet <louis.chauvet@bootlin.com>
Reviewed-by: Louis Chauvet <louis.chauvet@bootlin.com>
---
Documentation/gpu/drm-kms-helpers.rst | 12 +++
drivers/gpu/drm/Makefile | 3 +-
drivers/gpu/drm/drm_vblank.c | 122 ++++++++++++++++++++++-
drivers/gpu/drm/drm_vblank_helper.c | 96 ++++++++++++++++++
include/drm/drm_modeset_helper_vtables.h | 12 +++
include/drm/drm_vblank.h | 28 ++++++
include/drm/drm_vblank_helper.h | 33 ++++++
7 files changed, 303 insertions(+), 3 deletions(-)
create mode 100644 drivers/gpu/drm/drm_vblank_helper.c
create mode 100644 include/drm/drm_vblank_helper.h
diff --git a/Documentation/gpu/drm-kms-helpers.rst b/Documentation/gpu/drm-kms-helpers.rst
index 5139705089f2..781129f78b06 100644
--- a/Documentation/gpu/drm-kms-helpers.rst
+++ b/Documentation/gpu/drm-kms-helpers.rst
@@ -92,6 +92,18 @@ GEM Atomic Helper Reference
.. kernel-doc:: drivers/gpu/drm/drm_gem_atomic_helper.c
:export:
+VBLANK Helper Reference
+-----------------------
+
+.. kernel-doc:: drivers/gpu/drm/drm_vblank_helper.c
+ :doc: overview
+
+.. kernel-doc:: include/drm/drm_vblank_helper.h
+ :internal:
+
+.. kernel-doc:: drivers/gpu/drm/drm_vblank_helper.c
+ :export:
+
Simple KMS Helper Reference
===========================
diff --git a/drivers/gpu/drm/Makefile b/drivers/gpu/drm/Makefile
index 4dafbdc8f86a..5ba4ffdb8055 100644
--- a/drivers/gpu/drm/Makefile
+++ b/drivers/gpu/drm/Makefile
@@ -150,7 +150,8 @@ drm_kms_helper-y := \
drm_plane_helper.o \
drm_probe_helper.o \
drm_self_refresh_helper.o \
- drm_simple_kms_helper.o
+ drm_simple_kms_helper.o \
+ drm_vblank_helper.o
drm_kms_helper-$(CONFIG_DRM_PANEL_BRIDGE) += bridge/panel.o
drm_kms_helper-$(CONFIG_DRM_FBDEV_EMULATION) += drm_fb_helper.o
obj-$(CONFIG_DRM_KMS_HELPER) += drm_kms_helper.o
diff --git a/drivers/gpu/drm/drm_vblank.c b/drivers/gpu/drm/drm_vblank.c
index 46f59883183d..2a4ee41e2fcf 100644
--- a/drivers/gpu/drm/drm_vblank.c
+++ b/drivers/gpu/drm/drm_vblank.c
@@ -136,8 +136,17 @@
* vblanks after a timer has expired, which can be configured through the
* ``vblankoffdelay`` module parameter.
*
- * Drivers for hardware without support for vertical-blanking interrupts
- * must not call drm_vblank_init(). For such drivers, atomic helpers will
+ * Drivers for hardware without support for vertical-blanking interrupts can
+ * use DRM vblank timers to send vblank events at the rate of the current
+ * display mode's refresh. While not synchronized to the hardware's
+ * vertical-blanking regions, the timer helps DRM clients and compositors to
+ * adapt their update cycle to the display output. Drivers should set up
+ * vblanking as usual, but call drm_crtc_vblank_start_timer() and
+ * drm_crtc_vblank_cancel_timer() as part of their atomic mode setting.
+ * See also DRM vblank helpers for more information.
+ *
+ * Drivers without support for vertical-blanking interrupts nor timers must
+ * not call drm_vblank_init(). For these drivers, atomic helpers will
* automatically generate fake vblank events as part of the display update.
* This functionality also can be controlled by the driver by enabling and
* disabling struct drm_crtc_state.no_vblank.
@@ -2162,3 +2171,112 @@ int drm_crtc_queue_sequence_ioctl(struct drm_device *dev, void *data,
return ret;
}
+/*
+ * VBLANK timer
+ */
+
+static enum hrtimer_restart drm_vblank_timer_function(struct hrtimer *timer)
+{
+ struct drm_vblank_crtc_timer *vtimer =
+ container_of(timer, struct drm_vblank_crtc_timer, timer);
+ struct drm_crtc *crtc = vtimer->crtc;
+ const struct drm_crtc_helper_funcs *crtc_funcs = crtc->helper_private;
+ struct drm_device *dev = crtc->dev;
+ u64 ret_overrun;
+ bool succ;
+
+ ret_overrun = hrtimer_forward_now(&vtimer->timer, vtimer->interval);
+ if (ret_overrun != 1)
+ drm_dbg_vbl(dev, "vblank timer overrun\n");
+
+ if (crtc_funcs->handle_vblank_timeout)
+ succ = crtc_funcs->handle_vblank_timeout(crtc);
+ else
+ succ = drm_crtc_handle_vblank(crtc);
+ if (!succ)
+ return HRTIMER_NORESTART;
+
+ return HRTIMER_RESTART;
+}
+
+/**
+ * drm_crtc_vblank_start_timer - Starts the vblank timer on the given CRTC
+ * @crtc: the CRTC
+ *
+ * Drivers should call this function from their CRTC's enable_vblank
+ * function to start a vblank timer. The timer will fire after the duration
+ * of a full frame. drm_crtc_vblank_cancel_timer() disables a running timer.
+ *
+ * Returns:
+ * 0 on success, or a negative errno code otherwise.
+ */
+int drm_crtc_vblank_start_timer(struct drm_crtc *crtc)
+{
+ struct drm_vblank_crtc *vblank = drm_crtc_vblank_crtc(crtc);
+ struct drm_vblank_crtc_timer *vtimer = &vblank->vblank_timer;
+
+ if (!vtimer->crtc) {
+ vtimer->crtc = crtc;
+ hrtimer_setup(&vtimer->timer, drm_vblank_timer_function,
+ CLOCK_MONOTONIC, HRTIMER_MODE_REL);
+ }
+
+ drm_calc_timestamping_constants(crtc, &crtc->mode);
+
+ vtimer->interval = ktime_set(0, vblank->framedur_ns);
+ hrtimer_start(&vtimer->timer, vtimer->interval, HRTIMER_MODE_REL);
+
+ return 0;
+}
+EXPORT_SYMBOL(drm_crtc_vblank_start_timer);
+
+/**
+ * drm_crtc_vblank_start_timer - Cancels the given CRTC's vblank timer
+ * @crtc: the CRTC
+ *
+ * Drivers should call this function from their CRTC's disable_vblank
+ * function to stop a vblank timer.
+ */
+void drm_crtc_vblank_cancel_timer(struct drm_crtc *crtc)
+{
+ struct drm_vblank_crtc *vblank = drm_crtc_vblank_crtc(crtc);
+ struct drm_vblank_crtc_timer *vtimer = &vblank->vblank_timer;
+
+ hrtimer_cancel(&vtimer->timer);
+}
+EXPORT_SYMBOL(drm_crtc_vblank_cancel_timer);
+
+/**
+ * drm_crtc_vblank_get_vblank_timeout - Returns the vblank timeout
+ * @crtc: The CRTC
+ * @vblank_time: Returns the next vblank timestamp
+ *
+ * The helper drm_crtc_vblank_get_vblank_timeout() returns the next vblank
+ * timestamp of the CRTC's vblank timer according to the timer's expiry
+ * time.
+ */
+void drm_crtc_vblank_get_vblank_timeout(struct drm_crtc *crtc, ktime_t *vblank_time)
+{
+ struct drm_vblank_crtc *vblank = drm_crtc_vblank_crtc(crtc);
+ struct drm_vblank_crtc_timer *vtimer = &vblank->vblank_timer;
+
+ if (!READ_ONCE(vblank->enabled)) {
+ *vblank_time = ktime_get();
+ return;
+ }
+
+ *vblank_time = READ_ONCE(vtimer->timer.node.expires);
+
+ if (drm_WARN_ON(crtc->dev, !ktime_compare(*vblank_time, vblank->time)))
+ return; /* Already expired */
+
+ /*
+ * To prevent races we roll the hrtimer forward before we do any
+ * interrupt processing - this is how real hw works (the interrupt
+ * is only generated after all the vblank registers are updated)
+ * and what the vblank core expects. Therefore we need to always
+ * correct the timestamp by one frame.
+ */
+ *vblank_time = ktime_sub(*vblank_time, vtimer->interval);
+}
+EXPORT_SYMBOL(drm_crtc_vblank_get_vblank_timeout);
diff --git a/drivers/gpu/drm/drm_vblank_helper.c b/drivers/gpu/drm/drm_vblank_helper.c
new file mode 100644
index 000000000000..f94d1e706191
--- /dev/null
+++ b/drivers/gpu/drm/drm_vblank_helper.c
@@ -0,0 +1,96 @@
+// SPDX-License-Identifier: MIT
+
+#include <drm/drm_crtc.h>
+#include <drm/drm_managed.h>
+#include <drm/drm_modeset_helper_vtables.h>
+#include <drm/drm_print.h>
+#include <drm/drm_vblank.h>
+#include <drm/drm_vblank_helper.h>
+
+/**
+ * DOC: overview
+ *
+ * The vblank helper library provides functions for supporting vertical
+ * blanking in DRM drivers.
+ *
+ * For vblank timers, several callback implementations are available.
+ * Drivers enable support for vblank timers by setting the vblank callbacks
+ * in struct &drm_crtc_funcs to the helpers provided by this library. The
+ * initializer macro DRM_CRTC_VBLANK_TIMER_FUNCS does this conveniently.
+ *
+ * Once the driver enables vblank support with drm_vblank_init(), each
+ * CRTC's vblank timer fires according to the programmed display mode. By
+ * default, the vblank timer invokes drm_crtc_handle_vblank(). Drivers with
+ * more specific requirements can set their own handler function in
+ * struct &drm_crtc_helper_funcs.handle_vblank_timeout.
+ */
+
+/*
+ * VBLANK timer
+ */
+
+/**
+ * drm_crtc_vblank_helper_enable_vblank_timer - Implements struct &drm_crtc_funcs.enable_vblank
+ * @crtc: The CRTC
+ *
+ * The helper drm_crtc_vblank_helper_enable_vblank_timer() implements
+ * enable_vblank of struct drm_crtc_helper_funcs for CRTCs that require
+ * a VBLANK timer. It sets up the timer on the first invocation. The
+ * started timer expires after the current frame duration. See struct
+ * &drm_vblank_crtc.framedur_ns.
+ *
+ * See also struct &drm_crtc_helper_funcs.enable_vblank.
+ *
+ * Returns:
+ * 0 on success, or a negative errno code otherwise.
+ */
+int drm_crtc_vblank_helper_enable_vblank_timer(struct drm_crtc *crtc)
+{
+ return drm_crtc_vblank_start_timer(crtc);
+}
+EXPORT_SYMBOL(drm_crtc_vblank_helper_enable_vblank_timer);
+
+/**
+ * drm_crtc_vblank_helper_disable_vblank_timer - Implements struct &drm_crtc_funcs.disable_vblank
+ * @crtc: The CRTC
+ *
+ * The helper drm_crtc_vblank_helper_disable_vblank_timer() implements
+ * disable_vblank of struct drm_crtc_funcs for CRTCs that require a
+ * VBLANK timer.
+ *
+ * See also struct &drm_crtc_helper_funcs.disable_vblank.
+ */
+void drm_crtc_vblank_helper_disable_vblank_timer(struct drm_crtc *crtc)
+{
+ drm_crtc_vblank_cancel_timer(crtc);
+}
+EXPORT_SYMBOL(drm_crtc_vblank_helper_disable_vblank_timer);
+
+/**
+ * drm_crtc_vblank_helper_get_vblank_timestamp_from_timer -
+ * Implements struct &drm_crtc_funcs.get_vblank_timestamp
+ * @crtc: The CRTC
+ * @max_error: Maximum acceptable error
+ * @vblank_time: Returns the next vblank timestamp
+ * @in_vblank_irq: True is called from drm_crtc_handle_vblank()
+ *
+ * The helper drm_crtc_helper_get_vblank_timestamp_from_timer() implements
+ * get_vblank_timestamp of struct drm_crtc_funcs for CRTCs that require a
+ * VBLANK timer. It returns the timestamp according to the timer's expiry
+ * time.
+ *
+ * See also struct &drm_crtc_funcs.get_vblank_timestamp.
+ *
+ * Returns:
+ * True on success, or false otherwise.
+ */
+bool drm_crtc_vblank_helper_get_vblank_timestamp_from_timer(struct drm_crtc *crtc,
+ int *max_error,
+ ktime_t *vblank_time,
+ bool in_vblank_irq)
+{
+ drm_crtc_vblank_get_vblank_timeout(crtc, vblank_time);
+
+ return true;
+}
+EXPORT_SYMBOL(drm_crtc_vblank_helper_get_vblank_timestamp_from_timer);
diff --git a/include/drm/drm_modeset_helper_vtables.h b/include/drm/drm_modeset_helper_vtables.h
index ce7c7aeac887..fe32854b7ffe 100644
--- a/include/drm/drm_modeset_helper_vtables.h
+++ b/include/drm/drm_modeset_helper_vtables.h
@@ -490,6 +490,18 @@ struct drm_crtc_helper_funcs {
bool in_vblank_irq, int *vpos, int *hpos,
ktime_t *stime, ktime_t *etime,
const struct drm_display_mode *mode);
+
+ /**
+ * @handle_vblank_timeout: Handles timeouts of the vblank timer.
+ *
+ * Called by CRTC's the vblank timer on each timeout. Semantics is
+ * equivalient to drm_crtc_handle_vblank(). Implementations should
+ * invoke drm_crtc_handle_vblank() as part of processing the timeout.
+ *
+ * This callback is optional. If unset, the vblank timer invokes
+ * drm_crtc_handle_vblank() directly.
+ */
+ bool (*handle_vblank_timeout)(struct drm_crtc *crtc);
};
/**
diff --git a/include/drm/drm_vblank.h b/include/drm/drm_vblank.h
index 151ab1e85b1b..f020415abd20 100644
--- a/include/drm/drm_vblank.h
+++ b/include/drm/drm_vblank.h
@@ -25,6 +25,7 @@
#define _DRM_VBLANK_H_
#include <linux/seqlock.h>
+#include <linux/hrtimer.h>
#include <linux/idr.h>
#include <linux/poll.h>
#include <linux/kthread.h>
@@ -103,6 +104,24 @@ struct drm_vblank_crtc_config {
bool disable_immediate;
};
+/**
+ * struct drm_vblank_crtc_timer - vblank timer for a CRTC
+ */
+struct drm_vblank_crtc_timer {
+ /**
+ * @timer: The vblank's high-resolution timer
+ */
+ struct hrtimer timer;
+ /**
+ * @interval: Duration between two vblanks
+ */
+ ktime_t interval;
+ /**
+ * @crtc: The timer's CRTC
+ */
+ struct drm_crtc *crtc;
+};
+
/**
* struct drm_vblank_crtc - vblank tracking for a CRTC
*
@@ -254,6 +273,11 @@ struct drm_vblank_crtc {
* cancelled.
*/
wait_queue_head_t work_wait_queue;
+
+ /**
+ * @vblank_timer: Holds the state of the vblank timer
+ */
+ struct drm_vblank_crtc_timer vblank_timer;
};
struct drm_vblank_crtc *drm_crtc_vblank_crtc(struct drm_crtc *crtc);
@@ -290,6 +314,10 @@ wait_queue_head_t *drm_crtc_vblank_waitqueue(struct drm_crtc *crtc);
void drm_crtc_set_max_vblank_count(struct drm_crtc *crtc,
u32 max_vblank_count);
+int drm_crtc_vblank_start_timer(struct drm_crtc *crtc);
+void drm_crtc_vblank_cancel_timer(struct drm_crtc *crtc);
+void drm_crtc_vblank_get_vblank_timeout(struct drm_crtc *crtc, ktime_t *vblank_time);
+
/*
* Helpers for struct drm_crtc_funcs
*/
diff --git a/include/drm/drm_vblank_helper.h b/include/drm/drm_vblank_helper.h
new file mode 100644
index 000000000000..74a971d0cfba
--- /dev/null
+++ b/include/drm/drm_vblank_helper.h
@@ -0,0 +1,33 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
+
+#ifndef _DRM_VBLANK_HELPER_H_
+#define _DRM_VBLANK_HELPER_H_
+
+#include <linux/hrtimer_types.h>
+#include <linux/types.h>
+
+struct drm_crtc;
+
+/*
+ * VBLANK timer
+ */
+
+int drm_crtc_vblank_helper_enable_vblank_timer(struct drm_crtc *crtc);
+void drm_crtc_vblank_helper_disable_vblank_timer(struct drm_crtc *crtc);
+bool drm_crtc_vblank_helper_get_vblank_timestamp_from_timer(struct drm_crtc *crtc,
+ int *max_error,
+ ktime_t *vblank_time,
+ bool in_vblank_irq);
+
+/**
+ * DRM_CRTC_VBLANK_TIMER_FUNCS - Default implementation for VBLANK timers
+ *
+ * This macro initializes struct &drm_crtc_funcs to default helpers for
+ * VBLANK timers.
+ */
+#define DRM_CRTC_VBLANK_TIMER_FUNCS \
+ .enable_vblank = drm_crtc_vblank_helper_enable_vblank_timer, \
+ .disable_vblank = drm_crtc_vblank_helper_disable_vblank_timer, \
+ .get_vblank_timestamp = drm_crtc_vblank_helper_get_vblank_timestamp_from_timer
+
+#endif
--
2.50.1
^ permalink raw reply related
* [PATCH v2 3/4] drm/vkms: Convert to DRM's vblank timer
From: Thomas Zimmermann @ 2025-09-01 11:07 UTC (permalink / raw)
To: louis.chauvet, drawat.floss, hamohammed.sa, melissa.srw, mhklinux,
simona, airlied, maarten.lankhorst
Cc: dri-devel, linux-hyperv, Thomas Zimmermann
In-Reply-To: <20250901111241.233875-1-tzimmermann@suse.de>
Replace vkms' vblank timer with the DRM implementation. The DRM
code is identical in concept, but differs in implementation.
Vblank timers are covered in vblank helpers and initializer macros,
so remove the corresponding hrtimer in struct vkms_output. The
vblank timer calls vkms' custom timeout code via handle_vblank_timeout
in struct drm_crtc_helper_funcs.
Signed-off-by: Thomas Zimmermann <tzimmermann@suse.de>
Tested-by: Louis Chauvet <louis.chauvet@bootlin.com>
Reviewed-by: Louis Chauvet <louis.chauvet@bootlin.com>
---
drivers/gpu/drm/vkms/vkms_crtc.c | 83 +++-----------------------------
drivers/gpu/drm/vkms/vkms_drv.h | 2 -
2 files changed, 7 insertions(+), 78 deletions(-)
diff --git a/drivers/gpu/drm/vkms/vkms_crtc.c b/drivers/gpu/drm/vkms/vkms_crtc.c
index e60573e0f3e9..bd79f24686dc 100644
--- a/drivers/gpu/drm/vkms/vkms_crtc.c
+++ b/drivers/gpu/drm/vkms/vkms_crtc.c
@@ -7,25 +7,18 @@
#include <drm/drm_managed.h>
#include <drm/drm_probe_helper.h>
#include <drm/drm_vblank.h>
+#include <drm/drm_vblank_helper.h>
#include "vkms_drv.h"
-static enum hrtimer_restart vkms_vblank_simulate(struct hrtimer *timer)
+static bool vkms_crtc_handle_vblank_timeout(struct drm_crtc *crtc)
{
- struct vkms_output *output = container_of(timer, struct vkms_output,
- vblank_hrtimer);
- struct drm_crtc *crtc = &output->crtc;
+ struct vkms_output *output = drm_crtc_to_vkms_output(crtc);
struct vkms_crtc_state *state;
- u64 ret_overrun;
bool ret, fence_cookie;
fence_cookie = dma_fence_begin_signalling();
- ret_overrun = hrtimer_forward_now(&output->vblank_hrtimer,
- output->period_ns);
- if (ret_overrun != 1)
- pr_warn("%s: vblank timer overrun\n", __func__);
-
spin_lock(&output->lock);
ret = drm_crtc_handle_vblank(crtc);
if (!ret)
@@ -57,55 +50,6 @@ static enum hrtimer_restart vkms_vblank_simulate(struct hrtimer *timer)
dma_fence_end_signalling(fence_cookie);
- return HRTIMER_RESTART;
-}
-
-static int vkms_enable_vblank(struct drm_crtc *crtc)
-{
- struct drm_vblank_crtc *vblank = drm_crtc_vblank_crtc(crtc);
- struct vkms_output *out = drm_crtc_to_vkms_output(crtc);
-
- hrtimer_setup(&out->vblank_hrtimer, &vkms_vblank_simulate, CLOCK_MONOTONIC,
- HRTIMER_MODE_REL);
- out->period_ns = ktime_set(0, vblank->framedur_ns);
- hrtimer_start(&out->vblank_hrtimer, out->period_ns, HRTIMER_MODE_REL);
-
- return 0;
-}
-
-static void vkms_disable_vblank(struct drm_crtc *crtc)
-{
- struct vkms_output *out = drm_crtc_to_vkms_output(crtc);
-
- hrtimer_cancel(&out->vblank_hrtimer);
-}
-
-static bool vkms_get_vblank_timestamp(struct drm_crtc *crtc,
- int *max_error, ktime_t *vblank_time,
- bool in_vblank_irq)
-{
- struct vkms_output *output = drm_crtc_to_vkms_output(crtc);
- struct drm_vblank_crtc *vblank = drm_crtc_vblank_crtc(crtc);
-
- if (!READ_ONCE(vblank->enabled)) {
- *vblank_time = ktime_get();
- return true;
- }
-
- *vblank_time = READ_ONCE(output->vblank_hrtimer.node.expires);
-
- if (WARN_ON(*vblank_time == vblank->time))
- return true;
-
- /*
- * To prevent races we roll the hrtimer forward before we do any
- * interrupt processing - this is how real hw works (the interrupt is
- * only generated after all the vblank registers are updated) and what
- * the vblank core expects. Therefore we need to always correct the
- * timestampe by one frame.
- */
- *vblank_time -= output->period_ns;
-
return true;
}
@@ -159,9 +103,7 @@ static const struct drm_crtc_funcs vkms_crtc_funcs = {
.reset = vkms_atomic_crtc_reset,
.atomic_duplicate_state = vkms_atomic_crtc_duplicate_state,
.atomic_destroy_state = vkms_atomic_crtc_destroy_state,
- .enable_vblank = vkms_enable_vblank,
- .disable_vblank = vkms_disable_vblank,
- .get_vblank_timestamp = vkms_get_vblank_timestamp,
+ DRM_CRTC_VBLANK_TIMER_FUNCS,
.get_crc_sources = vkms_get_crc_sources,
.set_crc_source = vkms_set_crc_source,
.verify_crc_source = vkms_verify_crc_source,
@@ -213,18 +155,6 @@ static int vkms_crtc_atomic_check(struct drm_crtc *crtc,
return 0;
}
-static void vkms_crtc_atomic_enable(struct drm_crtc *crtc,
- struct drm_atomic_state *state)
-{
- drm_crtc_vblank_on(crtc);
-}
-
-static void vkms_crtc_atomic_disable(struct drm_crtc *crtc,
- struct drm_atomic_state *state)
-{
- drm_crtc_vblank_off(crtc);
-}
-
static void vkms_crtc_atomic_begin(struct drm_crtc *crtc,
struct drm_atomic_state *state)
__acquires(&vkms_output->lock)
@@ -265,8 +195,9 @@ static const struct drm_crtc_helper_funcs vkms_crtc_helper_funcs = {
.atomic_check = vkms_crtc_atomic_check,
.atomic_begin = vkms_crtc_atomic_begin,
.atomic_flush = vkms_crtc_atomic_flush,
- .atomic_enable = vkms_crtc_atomic_enable,
- .atomic_disable = vkms_crtc_atomic_disable,
+ .atomic_enable = drm_crtc_vblank_atomic_enable,
+ .atomic_disable = drm_crtc_vblank_atomic_disable,
+ .handle_vblank_timeout = vkms_crtc_handle_vblank_timeout,
};
struct vkms_output *vkms_crtc_init(struct drm_device *dev, struct drm_plane *primary,
diff --git a/drivers/gpu/drm/vkms/vkms_drv.h b/drivers/gpu/drm/vkms/vkms_drv.h
index 8013c31efe3b..fb9711e1c6fb 100644
--- a/drivers/gpu/drm/vkms/vkms_drv.h
+++ b/drivers/gpu/drm/vkms/vkms_drv.h
@@ -215,8 +215,6 @@ struct vkms_output {
struct drm_crtc crtc;
struct drm_writeback_connector wb_connector;
struct drm_encoder wb_encoder;
- struct hrtimer vblank_hrtimer;
- ktime_t period_ns;
struct workqueue_struct *composer_workq;
spinlock_t lock;
--
2.50.1
^ permalink raw reply related
* [PATCH v2 2/4] drm/vblank: Add CRTC helpers for simple use cases
From: Thomas Zimmermann @ 2025-09-01 11:06 UTC (permalink / raw)
To: louis.chauvet, drawat.floss, hamohammed.sa, melissa.srw, mhklinux,
simona, airlied, maarten.lankhorst
Cc: dri-devel, linux-hyperv, Thomas Zimmermann
In-Reply-To: <20250901111241.233875-1-tzimmermann@suse.de>
Implement atomic_flush, atomic_enable and atomic_disable of struct
drm_crtc_helper_funcs for vblank handling. Driver with no further
requirements can use these functions instead of adding their own.
Also simplifies the use of vblank timers.
v2:
- fix docs
Signed-off-by: Thomas Zimmermann <tzimmermann@suse.de>
---
drivers/gpu/drm/drm_vblank_helper.c | 80 +++++++++++++++++++++++++++++
include/drm/drm_vblank_helper.h | 23 +++++++++
2 files changed, 103 insertions(+)
diff --git a/drivers/gpu/drm/drm_vblank_helper.c b/drivers/gpu/drm/drm_vblank_helper.c
index f94d1e706191..a04a6ba1b0ca 100644
--- a/drivers/gpu/drm/drm_vblank_helper.c
+++ b/drivers/gpu/drm/drm_vblank_helper.c
@@ -1,5 +1,6 @@
// SPDX-License-Identifier: MIT
+#include <drm/drm_atomic.h>
#include <drm/drm_crtc.h>
#include <drm/drm_managed.h>
#include <drm/drm_modeset_helper_vtables.h>
@@ -17,6 +18,12 @@
* Drivers enable support for vblank timers by setting the vblank callbacks
* in struct &drm_crtc_funcs to the helpers provided by this library. The
* initializer macro DRM_CRTC_VBLANK_TIMER_FUNCS does this conveniently.
+ * The driver further has to send the VBLANK event from its atomic_flush
+ * callback and control vblank from the CRTC's atomic_enable and atomic_disable
+ * callbacks. The callbacks are located in struct &drm_crtc_helper_funcs.
+ * The vblank helper library provides implementations of these callbacks
+ * for drivers without further requirements. The initializer macro
+ * DRM_CRTC_HELPER_VBLANK_FUNCS sets them coveniently.
*
* Once the driver enables vblank support with drm_vblank_init(), each
* CRTC's vblank timer fires according to the programmed display mode. By
@@ -25,6 +32,79 @@
* struct &drm_crtc_helper_funcs.handle_vblank_timeout.
*/
+/*
+ * VBLANK helpers
+ */
+
+/**
+ * drm_crtc_vblank_atomic_flush -
+ * Implements struct &drm_crtc_helper_funcs.atomic_flush
+ * @crtc: The CRTC
+ * @state: The atomic state to apply
+ *
+ * The helper drm_crtc_vblank_atomic_flush() implements atomic_flush of
+ * struct drm_crtc_helper_funcs for CRTCs that only need to send out a
+ * VBLANK event.
+ *
+ * See also struct &drm_crtc_helper_funcs.atomic_flush.
+ */
+void drm_crtc_vblank_atomic_flush(struct drm_crtc *crtc,
+ struct drm_atomic_state *state)
+{
+ struct drm_device *dev = crtc->dev;
+ struct drm_crtc_state *crtc_state = drm_atomic_get_new_crtc_state(state, crtc);
+ struct drm_pending_vblank_event *event;
+
+ spin_lock_irq(&dev->event_lock);
+
+ event = crtc_state->event;
+ crtc_state->event = NULL;
+
+ if (event) {
+ if (drm_crtc_vblank_get(crtc) == 0)
+ drm_crtc_arm_vblank_event(crtc, event);
+ else
+ drm_crtc_send_vblank_event(crtc, event);
+ }
+
+ spin_unlock_irq(&dev->event_lock);
+}
+EXPORT_SYMBOL(drm_crtc_vblank_atomic_flush);
+
+/**
+ * drm_crtc_vblank_atomic_enable - Implements struct &drm_crtc_helper_funcs.atomic_enable
+ * @crtc: The CRTC
+ * @state: The atomic state
+ *
+ * The helper drm_crtc_vblank_atomic_enable() implements atomic_enable
+ * of struct drm_crtc_helper_funcs for CRTCs the only need to enable VBLANKs.
+ *
+ * See also struct &drm_crtc_helper_funcs.atomic_enable.
+ */
+void drm_crtc_vblank_atomic_enable(struct drm_crtc *crtc,
+ struct drm_atomic_state *state)
+{
+ drm_crtc_vblank_on(crtc);
+}
+EXPORT_SYMBOL(drm_crtc_vblank_atomic_enable);
+
+/**
+ * drm_crtc_vblank_atomic_disable - Implements struct &drm_crtc_helper_funcs.atomic_disable
+ * @crtc: The CRTC
+ * @state: The atomic state
+ *
+ * The helper drm_crtc_vblank_atomic_disable() implements atomic_disable
+ * of struct drm_crtc_helper_funcs for CRTCs the only need to disable VBLANKs.
+ *
+ * See also struct &drm_crtc_funcs.atomic_disable.
+ */
+void drm_crtc_vblank_atomic_disable(struct drm_crtc *crtc,
+ struct drm_atomic_state *state)
+{
+ drm_crtc_vblank_off(crtc);
+}
+EXPORT_SYMBOL(drm_crtc_vblank_atomic_disable);
+
/*
* VBLANK timer
*/
diff --git a/include/drm/drm_vblank_helper.h b/include/drm/drm_vblank_helper.h
index 74a971d0cfba..fcd8a9b35846 100644
--- a/include/drm/drm_vblank_helper.h
+++ b/include/drm/drm_vblank_helper.h
@@ -6,8 +6,31 @@
#include <linux/hrtimer_types.h>
#include <linux/types.h>
+struct drm_atomic_state;
struct drm_crtc;
+/*
+ * VBLANK helpers
+ */
+
+void drm_crtc_vblank_atomic_flush(struct drm_crtc *crtc,
+ struct drm_atomic_state *state);
+void drm_crtc_vblank_atomic_enable(struct drm_crtc *crtc,
+ struct drm_atomic_state *state);
+void drm_crtc_vblank_atomic_disable(struct drm_crtc *crtc,
+ struct drm_atomic_state *crtc_state);
+
+/**
+ * DRM_CRTC_HELPER_VBLANK_FUNCS - Default implementation for VBLANK helpers
+ *
+ * This macro initializes struct &drm_crtc_helper_funcs to default helpers
+ * for VBLANK handling.
+ */
+#define DRM_CRTC_HELPER_VBLANK_FUNCS \
+ .atomic_flush = drm_crtc_vblank_atomic_flush, \
+ .atomic_enable = drm_crtc_vblank_atomic_enable, \
+ .atomic_disable = drm_crtc_vblank_atomic_disable
+
/*
* VBLANK timer
*/
--
2.50.1
^ permalink raw reply related
* [PATCH v2 0/4] drm: Add vblank timers for devices without interrupts
From: Thomas Zimmermann @ 2025-09-01 11:06 UTC (permalink / raw)
To: louis.chauvet, drawat.floss, hamohammed.sa, melissa.srw, mhklinux,
simona, airlied, maarten.lankhorst
Cc: dri-devel, linux-hyperv, Thomas Zimmermann
Compositors often depend on vblanks to limit their display-update
rate. Without, they see vblank events ASAP, which breaks the rate-
limit feature. This creates high CPU overhead. It is especially a
problem with virtual devices with fast framebuffer access.
The series moves vkms' vblank timer to DRM and converts the hyperv
DRM driver. An earlier version of this series contains examples of
other updated drivers. In principle, any DRM driver without vblank
hardware can use the timer.
The series has been motivated by a recent discussion about hypervdrm [1]
and other long-standing bug reports. [2][3]
[1] https://lore.kernel.org/dri-devel/20250523161522.409504-1-mhklinux@outlook.com/T/#ma2ebb52b60bfb0325879349377738fadcd7cb7ef
[2] https://bugzilla.suse.com/show_bug.cgi?id=1189174
[3] https://invent.kde.org/plasma/kwin/-/merge_requests/1229#note_284606
Thomas Zimmermann (4):
drm/vblank: Add vblank timer
drm/vblank: Add CRTC helpers for simple use cases
drm/vkms: Convert to DRM's vblank timer
drm/hypervdrm: Use vblank timer
Documentation/gpu/drm-kms-helpers.rst | 12 ++
drivers/gpu/drm/Makefile | 3 +-
drivers/gpu/drm/drm_vblank.c | 122 +++++++++++++-
drivers/gpu/drm/drm_vblank_helper.c | 176 ++++++++++++++++++++
drivers/gpu/drm/hyperv/hyperv_drm_modeset.c | 11 ++
drivers/gpu/drm/vkms/vkms_crtc.c | 83 +--------
drivers/gpu/drm/vkms/vkms_drv.h | 2 -
include/drm/drm_modeset_helper_vtables.h | 12 ++
include/drm/drm_vblank.h | 28 ++++
include/drm/drm_vblank_helper.h | 56 +++++++
10 files changed, 424 insertions(+), 81 deletions(-)
create mode 100644 drivers/gpu/drm/drm_vblank_helper.c
create mode 100644 include/drm/drm_vblank_helper.h
--
2.50.1
^ permalink raw reply
* Re: [PATCH 1/1] Drivers: hv: Simplify data structures for VMBus channel close message
From: Tianyu Lan @ 2025-08-31 17:56 UTC (permalink / raw)
To: mhklinux
Cc: haiyangz, wei.liu, decui, kys, linux-kernel, linux-hyperv,
gustavoars
In-Reply-To: <20250831160406.958974-1-mhklinux@outlook.com>
On Mon, Sep 1, 2025 at 12:06 AM <mhkelley58@gmail.com> wrote:
>
> From: Michael Kelley <mhklinux@outlook.com>
>
> struct vmbus_close_msg is used for sending the VMBus channel close
> message. It contains a struct vmbus_channel_msginfo, which has a
> flex array member at the end. The latter's presence in the middle
> of struct vmbus_close_msg causes warnings when built with
> -Wflex-array-member-not-at-end.
>
> But the struct vmbus_channel_msginfo is unused because the Hyper-V host
> does not send a response to the channel close message. So remove the
> struct vmbus_channel_msginfo. Then, since the only remaining field is
> struct vmbus_channel_close_channel, also remove the containing struct
> vmbus_close_msg and directly use struct vmbus_channel_close_channel.
> Besides eliminating unnecessary complexity, these changes resolve the
> -Wflex-array-member-not-at-end warnings.
>
> Signed-off-by: Michael Kelley <mhklinux@outlook.com>
> ---
Reviewed-by: Tianyu Lan <tiala@microsoft.com>
--
Thanks
Tianyu Lan
^ permalink raw reply
* Re: [PATCH v4] x86/hyperv: Fix kdump on Azure CVMs
From: Tianyu Lan @ 2025-08-31 17:37 UTC (permalink / raw)
To: Michael Kelley
Cc: Vitaly Kuznetsov, linux-hyperv@vger.kernel.org,
linux-coco@lists.linux.dev, K. Y. Srinivasan, Haiyang Zhang,
Wei Liu, Dexuan Cui, x86@kernel.org, linux-kernel@vger.kernel.org,
Nuno Das Neves, Tianyu Lan, Li Tian, Philipp Rudo
In-Reply-To: <SN6PR02MB41571C1A6DE8756D72176E93D43AA@SN6PR02MB4157.namprd02.prod.outlook.com>
On Sat, Aug 30, 2025 at 1:03 AM Michael Kelley <mhklinux@outlook.com> wrote:
>
> From: Vitaly Kuznetsov <vkuznets@redhat.com> Sent: Thursday, August 28, 2025 2:16 AM
> >
> > Azure CVM instance types featuring a paravisor hang upon kdump. The
> > investigation shows that makedumpfile causes a hang when it steps on a page
> > which was previously share with the host
> > (HVCALL_MODIFY_SPARSE_GPA_PAGE_HOST_VISIBILITY). The new kernel has no
> > knowledge of these 'special' regions (which are Vmbus connection pages,
> > GPADL buffers, ...). There are several ways to approach the issue:
> > - Convey the knowledge about these regions to the new kernel somehow.
> > - Unshare these regions before accessing in the new kernel (it is unclear
> > if there's a way to query the status for a given GPA range).
> > - Unshare these regions before jumping to the new kernel (which this patch
> > implements).
> >
> > To make the procedure as robust as possible, store PFN ranges of shared
> > regions in a linked list instead of storing GVAs and re-using
> > hv_vtom_set_host_visibility(). This also allows to avoid memory allocation
> > on the kdump/kexec path.
> >
> > Signed-off-by: Vitaly Kuznetsov <vkuznets@redhat.com>
>
> Looks good!
>
> Adding Confidential Computing mailing list (linux-coco@lists.linux.dev)
> for visibility.
>
> Reviewed-by: Michael Kelley <mhklinux@outlook.com>
>
> > ---
> > Changes since v3 [Michael Kelley]:
> > - Employ x86_platform.guest.enc_kexec_{begin,finish} hooks.
> > - Don't use spinlock in what's now hv_vtom_kexec_finish().
> > - Handle possible hypercall failures in hv_mark_gpa_visibility()
> > symmetrically; change hv_list_enc_remove() to return -ENOMEM as well.
> > - Rebase to the latest hyperv/next.
> > ---
> > arch/x86/hyperv/ivm.c | 211 +++++++++++++++++++++++++++++++++++++++++-
> > 1 file changed, 210 insertions(+), 1 deletion(-)
> >
> > diff --git a/arch/x86/hyperv/ivm.c b/arch/x86/hyperv/ivm.c
> > index ade6c665c97e..a4615b889f3e 100644
> > --- a/arch/x86/hyperv/ivm.c
> > +++ b/arch/x86/hyperv/ivm.c
> > @@ -462,6 +462,195 @@ void hv_ivm_msr_read(u64 msr, u64 *value)
> > hv_ghcb_msr_read(msr, value);
> > }
> >
> > +/*
> > + * Keep track of the PFN regions which were shared with the host. The access
> > + * must be revoked upon kexec/kdump (see hv_ivm_clear_host_access()).
> > + */
> > +struct hv_enc_pfn_region {
> > + struct list_head list;
> > + u64 pfn;
> > + int count;
> > +};
> > +
> > +static LIST_HEAD(hv_list_enc);
> > +static DEFINE_RAW_SPINLOCK(hv_list_enc_lock);
> > +
> > +static int hv_list_enc_add(const u64 *pfn_list, int count)
> > +{
> > + struct hv_enc_pfn_region *ent;
> > + unsigned long flags;
> > + u64 pfn;
> > + int i;
> > +
> > + for (i = 0; i < count; i++) {
> > + pfn = pfn_list[i];
> > +
> > + raw_spin_lock_irqsave(&hv_list_enc_lock, flags);
> > + /* Check if the PFN already exists in some region first */
> > + list_for_each_entry(ent, &hv_list_enc, list) {
> > + if ((ent->pfn <= pfn) && (ent->pfn + ent->count - 1 >= pfn))
> > + /* Nothing to do - pfn is already in the list */
> > + goto unlock_done;
> > + }
> > +
> > + /*
> > + * Check if the PFN is adjacent to an existing region. Growing
> > + * a region can make it adjacent to another one but merging is
> > + * not (yet) implemented for simplicity. A PFN cannot be added
> > + * to two regions to keep the logic in hv_list_enc_remove()
> > + * correct.
> > + */
> > + list_for_each_entry(ent, &hv_list_enc, list) {
> > + if (ent->pfn + ent->count == pfn) {
> > + /* Grow existing region up */
> > + ent->count++;
> > + goto unlock_done;
> > + } else if (pfn + 1 == ent->pfn) {
> > + /* Grow existing region down */
> > + ent->pfn--;
> > + ent->count++;
> > + goto unlock_done;
> > + }
> > + }
> > + raw_spin_unlock_irqrestore(&hv_list_enc_lock, flags);
> > +
> > + /* No adjacent region found -- create a new one */
> > + ent = kzalloc(sizeof(struct hv_enc_pfn_region), GFP_KERNEL);
> > + if (!ent)
> > + return -ENOMEM;
> > +
> > + ent->pfn = pfn;
> > + ent->count = 1;
> > +
> > + raw_spin_lock_irqsave(&hv_list_enc_lock, flags);
> > + list_add(&ent->list, &hv_list_enc);
> > +
> > +unlock_done:
> > + raw_spin_unlock_irqrestore(&hv_list_enc_lock, flags);
> > + }
> > +
> > + return 0;
> > +}
> > +
> > +static int hv_list_enc_remove(const u64 *pfn_list, int count)
> > +{
> > + struct hv_enc_pfn_region *ent, *t;
> > + struct hv_enc_pfn_region new_region;
> > + unsigned long flags;
> > + u64 pfn;
> > + int i;
> > +
> > + for (i = 0; i < count; i++) {
> > + pfn = pfn_list[i];
> > +
> > + raw_spin_lock_irqsave(&hv_list_enc_lock, flags);
> > + list_for_each_entry_safe(ent, t, &hv_list_enc, list) {
> > + if (pfn == ent->pfn + ent->count - 1) {
> > + /* Removing tail pfn */
> > + ent->count--;
> > + if (!ent->count) {
> > + list_del(&ent->list);
> > + kfree(ent);
> > + }
> > + goto unlock_done;
> > + } else if (pfn == ent->pfn) {
> > + /* Removing head pfn */
> > + ent->count--;
> > + ent->pfn++;
> > + if (!ent->count) {
> > + list_del(&ent->list);
> > + kfree(ent);
> > + }
> > + goto unlock_done;
> > + } else if (pfn > ent->pfn && pfn < ent->pfn + ent->count - 1) {
> > + /*
> > + * Removing a pfn in the middle. Cut off the tail
> > + * of the existing region and create a template for
> > + * the new one.
> > + */
> > + new_region.pfn = pfn + 1;
> > + new_region.count = ent->count - (pfn - ent->pfn + 1);
> > + ent->count = pfn - ent->pfn;
> > + goto unlock_split;
> > + }
> > +
> > + }
> > +unlock_done:
> > + raw_spin_unlock_irqrestore(&hv_list_enc_lock, flags);
> > + continue;
> > +
> > +unlock_split:
> > + raw_spin_unlock_irqrestore(&hv_list_enc_lock, flags);
> > +
> > + ent = kzalloc(sizeof(struct hv_enc_pfn_region), GFP_KERNEL);
> > + if (!ent)
> > + return -ENOMEM;
> > +
> > + ent->pfn = new_region.pfn;
> > + ent->count = new_region.count;
> > +
> > + raw_spin_lock_irqsave(&hv_list_enc_lock, flags);
> > + list_add(&ent->list, &hv_list_enc);
> > + raw_spin_unlock_irqrestore(&hv_list_enc_lock, flags);
> > + }
> > +
> > + return 0;
> > +}
> > +
> > +/* Stop new private<->shared conversions */
> > +static void hv_vtom_kexec_begin(void)
> > +{
> > + if (!IS_ENABLED(CONFIG_KEXEC_CORE))
> > + return;
> > +
> > + /*
> > + * Crash kernel reaches here with interrupts disabled: can't wait for
> > + * conversions to finish.
> > + *
> > + * If race happened, just report and proceed.
> > + */
> > + if (!set_memory_enc_stop_conversion())
> > + pr_warn("Failed to stop shared<->private conversions\n");
> > +}
> > +
> > +static void hv_vtom_kexec_finish(void)
> > +{
> > + struct hv_gpa_range_for_visibility *input;
> > + struct hv_enc_pfn_region *ent;
> > + unsigned long flags;
> > + u64 hv_status;
> > + int cur, i;
> > +
> > + local_irq_save(flags);
> > + input = *this_cpu_ptr(hyperv_pcpu_input_arg);
> > +
> > + if (unlikely(!input))
> > + goto out;
> > +
> > + list_for_each_entry(ent, &hv_list_enc, list) {
> > + for (i = 0, cur = 0; i < ent->count; i++) {
> > + input->gpa_page_list[cur] = ent->pfn + i;
> > + cur++;
> > +
> > + if (cur == HV_MAX_MODIFY_GPA_REP_COUNT || i == ent->count - 1) {
> > + input->partition_id = HV_PARTITION_ID_SELF;
> > + input->host_visibility = VMBUS_PAGE_NOT_VISIBLE;
> > + input->reserved0 = 0;
> > + input->reserved1 = 0;
> > + hv_status = hv_do_rep_hypercall(
> > + HVCALL_MODIFY_SPARSE_GPA_PAGE_HOST_VISIBILITY,
> > + cur, 0, input, NULL);
> > + WARN_ON_ONCE(!hv_result_success(hv_status));
> > + cur = 0;
> > + }
> > + }
> > +
> > + }
> > +
> > +out:
> > + local_irq_restore(flags);
> > +}
> > +
> > /*
> > * hv_mark_gpa_visibility - Set pages visible to host via hvcall.
> > *
> > @@ -475,6 +664,7 @@ static int hv_mark_gpa_visibility(u16 count, const u64 pfn[],
> > struct hv_gpa_range_for_visibility *input;
> > u64 hv_status;
> > unsigned long flags;
> > + int ret;
> >
> > /* no-op if partition isolation is not enabled */
> > if (!hv_is_isolation_supported())
> > @@ -486,6 +676,13 @@ static int hv_mark_gpa_visibility(u16 count, const u64 pfn[],
> > return -EINVAL;
> > }
> >
> > + if (visibility == VMBUS_PAGE_NOT_VISIBLE)
> > + ret = hv_list_enc_remove(pfn, count);
> > + else
> > + ret = hv_list_enc_add(pfn, count);
> > + if (ret)
> > + return ret;
> > +
> > local_irq_save(flags);
> > input = *this_cpu_ptr(hyperv_pcpu_input_arg);
> >
> > @@ -506,8 +703,18 @@ static int hv_mark_gpa_visibility(u16 count, const u64 pfn[],
> >
> > if (hv_result_success(hv_status))
> > return 0;
> > +
> > + if (visibility == VMBUS_PAGE_NOT_VISIBLE)
> > + ret = hv_list_enc_add(pfn, count);
> > else
> > - return -EFAULT;
> > + ret = hv_list_enc_remove(pfn, count);
> > + /*
> > + * There's no good way to recover from -ENOMEM here, the accounting is
> > + * wrong either way.
> > + */
> > + WARN_ON_ONCE(ret);
> > +
> > + return -EFAULT;
> > }
> >
> > /*
> > @@ -669,6 +876,8 @@ void __init hv_vtom_init(void)
> > x86_platform.guest.enc_tlb_flush_required = hv_vtom_tlb_flush_required;
> > x86_platform.guest.enc_status_change_prepare = hv_vtom_clear_present;
> > x86_platform.guest.enc_status_change_finish = hv_vtom_set_host_visibility;
> > + x86_platform.guest.enc_kexec_begin = hv_vtom_kexec_begin;
> > + x86_platform.guest.enc_kexec_finish = hv_vtom_kexec_finish;
> >
> > /* Set WB as the default cache mode. */
> > guest_force_mtrr_state(NULL, 0, MTRR_TYPE_WRBACK);
> > --
> > 2.50.1
>
>
Reviewed-by: Tianyu Lan <tiala@microsoft.com>
--
Thanks
Tianyu Lan
^ permalink raw reply
* Re: [PATCH v2] uio_hv_generic: Let userspace take care of interrupt mask
From: Tianyu Lan @ 2025-08-31 17:30 UTC (permalink / raw)
To: Naman Jain
Cc: Greg Kroah-Hartman, K . Y . Srinivasan, Haiyang Zhang, Wei Liu,
Dexuan Cui, Stephen Hemminger, linux-hyperv, linux-kernel,
Michael Kelley, Long Li, stable
In-Reply-To: <20250828044200.492030-1-namjain@linux.microsoft.com>
On Thu, Aug 28, 2025 at 12:42 PM Naman Jain <namjain@linux.microsoft.com> wrote:
>
> Remove the logic to set interrupt mask by default in uio_hv_generic
> driver as the interrupt mask value is supposed to be controlled
> completely by the user space. If the mask bit gets changed
> by the driver, concurrently with user mode operating on the ring,
> the mask bit may be set when it is supposed to be clear, and the
> user-mode driver will miss an interrupt which will cause a hang.
>
> For eg- when the driver sets inbound ring buffer interrupt mask to 1,
> the host does not interrupt the guest on the UIO VMBus channel.
> However, setting the mask does not prevent the host from putting a
> message in the inbound ring buffer. So let’s assume that happens,
> the host puts a message into the ring buffer but does not interrupt.
>
> Subsequently, the user space code in the guest sets the inbound ring
> buffer interrupt mask to 0, saying “Hey, I’m ready for interrupts”.
> User space code then calls pread() to wait for an interrupt.
> Then one of two things happens:
>
> * The host never sends another message. So the pread() waits forever.
> * The host does send another message. But because there’s already a
> message in the ring buffer, it doesn’t generate an interrupt.
> This is the correct behavior, because the host should only send an
> interrupt when the inbound ring buffer transitions from empty to
> not-empty. Adding an additional message to a ring buffer that is not
> empty is not supposed to generate an interrupt on the guest.
> Since the guest is waiting in pread() and not removing messages from
> the ring buffer, the pread() waits forever.
>
> This could be easily reproduced in hv_fcopy_uio_daemon if we delay
> setting interrupt mask to 0.
>
> Similarly if hv_uio_channel_cb() sets the interrupt_mask to 1,
> there’s a race condition. Once user space empties the inbound ring
> buffer, but before user space sets interrupt_mask to 0, the host could
> put another message in the ring buffer but it wouldn’t interrupt.
> Then the next pread() would hang.
>
> Fix these by removing all instances where interrupt_mask is changed,
> while keeping the one in set_event() unchanged to enable userspace
> control the interrupt mask by writing 0/1 to /dev/uioX.
>
> Fixes: 95096f2fbd10 ("uio-hv-generic: new userspace i/o driver for VMBus")
> Suggested-by: John Starks <jostarks@microsoft.com>
> Signed-off-by: Naman Jain <namjain@linux.microsoft.com>
> Cc: <stable@vger.kernel.org>
> ---
> Changes since v1:
> https://lore.kernel.org/all/20250818064846.271294-1-namjain@linux.microsoft.com/
> * Added Fixes and Cc stable tags.
> ---
> drivers/uio/uio_hv_generic.c | 7 +------
> 1 file changed, 1 insertion(+), 6 deletions(-)
>
> diff --git a/drivers/uio/uio_hv_generic.c b/drivers/uio/uio_hv_generic.c
> index f19efad4d6f8..3f8e2e27697f 100644
> --- a/drivers/uio/uio_hv_generic.c
> +++ b/drivers/uio/uio_hv_generic.c
> @@ -111,7 +111,6 @@ static void hv_uio_channel_cb(void *context)
> struct hv_device *hv_dev;
> struct hv_uio_private_data *pdata;
>
> - chan->inbound.ring_buffer->interrupt_mask = 1;
> virt_mb();
>
> /*
> @@ -183,8 +182,6 @@ hv_uio_new_channel(struct vmbus_channel *new_sc)
> return;
> }
>
> - /* Disable interrupts on sub channel */
> - new_sc->inbound.ring_buffer->interrupt_mask = 1;
> set_channel_read_mode(new_sc, HV_CALL_ISR);
> ret = hv_create_ring_sysfs(new_sc, hv_uio_ring_mmap);
> if (ret) {
> @@ -227,9 +224,7 @@ hv_uio_open(struct uio_info *info, struct inode *inode)
>
> ret = vmbus_connect_ring(dev->channel,
> hv_uio_channel_cb, dev->channel);
> - if (ret == 0)
> - dev->channel->inbound.ring_buffer->interrupt_mask = 1;
> - else
> + if (ret)
> atomic_dec(&pdata->refcnt);
>
> return ret;
> --
> 2.34.1
>
>
Reviewed-by: Tianyu Lan <tiala@microsoft.com>
Tested-by: Tianyu Lan <tiala@microsoft.com>
--
Thanks
Tianyu Lan
^ permalink raw reply
* RE: [PATCH v2] uio_hv_generic: Let userspace take care of interrupt mask
From: Michael Kelley @ 2025-08-31 16:12 UTC (permalink / raw)
To: Naman Jain, Greg Kroah-Hartman, K . Y . Srinivasan, Haiyang Zhang,
Wei Liu, Dexuan Cui, Stephen Hemminger
Cc: linux-hyperv@vger.kernel.org, linux-kernel@vger.kernel.org,
Long Li, stable@vger.kernel.org
In-Reply-To: <20250828044200.492030-1-namjain@linux.microsoft.com>
From: Naman Jain <namjain@linux.microsoft.com> Sent: Wednesday, August 27, 2025 9:42 PM
>
> Remove the logic to set interrupt mask by default in uio_hv_generic
> driver as the interrupt mask value is supposed to be controlled
> completely by the user space. If the mask bit gets changed
> by the driver, concurrently with user mode operating on the ring,
> the mask bit may be set when it is supposed to be clear, and the
> user-mode driver will miss an interrupt which will cause a hang.
>
> For eg- when the driver sets inbound ring buffer interrupt mask to 1,
> the host does not interrupt the guest on the UIO VMBus channel.
> However, setting the mask does not prevent the host from putting a
> message in the inbound ring buffer. So let's assume that happens,
> the host puts a message into the ring buffer but does not interrupt.
>
> Subsequently, the user space code in the guest sets the inbound ring
> buffer interrupt mask to 0, saying "Hey, I'm ready for interrupts".
> User space code then calls pread() to wait for an interrupt.
> Then one of two things happens:
>
> * The host never sends another message. So the pread() waits forever.
> * The host does send another message. But because there's already a
> message in the ring buffer, it doesn't generate an interrupt.
> This is the correct behavior, because the host should only send an
> interrupt when the inbound ring buffer transitions from empty to
> not-empty. Adding an additional message to a ring buffer that is not
> empty is not supposed to generate an interrupt on the guest.
> Since the guest is waiting in pread() and not removing messages from
> the ring buffer, the pread() waits forever.
>
> This could be easily reproduced in hv_fcopy_uio_daemon if we delay
> setting interrupt mask to 0.
>
> Similarly if hv_uio_channel_cb() sets the interrupt_mask to 1,
> there's a race condition. Once user space empties the inbound ring
> buffer, but before user space sets interrupt_mask to 0, the host could
> put another message in the ring buffer but it wouldn't interrupt.
> Then the next pread() would hang.
>
> Fix these by removing all instances where interrupt_mask is changed,
> while keeping the one in set_event() unchanged to enable userspace
> control the interrupt mask by writing 0/1 to /dev/uioX.
>
> Fixes: 95096f2fbd10 ("uio-hv-generic: new userspace i/o driver for VMBus")
> Suggested-by: John Starks <jostarks@microsoft.com>
> Signed-off-by: Naman Jain <namjain@linux.microsoft.com>
> Cc: <stable@vger.kernel.org>
Reviewed-by: Michael Kelley <mhklinux@outlook.com>
> ---
> Changes since v1:
> https://lore.kernel.org/all/20250818064846.271294-1-namjain@linux.microsoft.com/
> * Added Fixes and Cc stable tags.
> ---
> drivers/uio/uio_hv_generic.c | 7 +------
> 1 file changed, 1 insertion(+), 6 deletions(-)
>
> diff --git a/drivers/uio/uio_hv_generic.c b/drivers/uio/uio_hv_generic.c
> index f19efad4d6f8..3f8e2e27697f 100644
> --- a/drivers/uio/uio_hv_generic.c
> +++ b/drivers/uio/uio_hv_generic.c
> @@ -111,7 +111,6 @@ static void hv_uio_channel_cb(void *context)
> struct hv_device *hv_dev;
> struct hv_uio_private_data *pdata;
>
> - chan->inbound.ring_buffer->interrupt_mask = 1;
> virt_mb();
>
> /*
> @@ -183,8 +182,6 @@ hv_uio_new_channel(struct vmbus_channel *new_sc)
> return;
> }
>
> - /* Disable interrupts on sub channel */
> - new_sc->inbound.ring_buffer->interrupt_mask = 1;
> set_channel_read_mode(new_sc, HV_CALL_ISR);
> ret = hv_create_ring_sysfs(new_sc, hv_uio_ring_mmap);
> if (ret) {
> @@ -227,9 +224,7 @@ hv_uio_open(struct uio_info *info, struct inode *inode)
>
> ret = vmbus_connect_ring(dev->channel,
> hv_uio_channel_cb, dev->channel);
> - if (ret == 0)
> - dev->channel->inbound.ring_buffer->interrupt_mask = 1;
> - else
> + if (ret)
> atomic_dec(&pdata->refcnt);
>
> return ret;
> --
> 2.34.1
^ permalink raw reply
* [PATCH 1/1] Drivers: hv: Simplify data structures for VMBus channel close message
From: mhkelley58 @ 2025-08-31 16:04 UTC (permalink / raw)
To: haiyangz, wei.liu, decui, kys, linux-kernel, linux-hyperv; +Cc: gustavoars
From: Michael Kelley <mhklinux@outlook.com>
struct vmbus_close_msg is used for sending the VMBus channel close
message. It contains a struct vmbus_channel_msginfo, which has a
flex array member at the end. The latter's presence in the middle
of struct vmbus_close_msg causes warnings when built with
-Wflex-array-member-not-at-end.
But the struct vmbus_channel_msginfo is unused because the Hyper-V host
does not send a response to the channel close message. So remove the
struct vmbus_channel_msginfo. Then, since the only remaining field is
struct vmbus_channel_close_channel, also remove the containing struct
vmbus_close_msg and directly use struct vmbus_channel_close_channel.
Besides eliminating unnecessary complexity, these changes resolve the
-Wflex-array-member-not-at-end warnings.
Signed-off-by: Michael Kelley <mhklinux@outlook.com>
---
drivers/hv/channel.c | 2 +-
include/linux/hyperv.h | 7 +------
2 files changed, 2 insertions(+), 7 deletions(-)
diff --git a/drivers/hv/channel.c b/drivers/hv/channel.c
index 7c7c66e0dc3f..162d6aeece7b 100644
--- a/drivers/hv/channel.c
+++ b/drivers/hv/channel.c
@@ -925,7 +925,7 @@ static int vmbus_close_internal(struct vmbus_channel *channel)
/* Send a closing message */
- msg = &channel->close_msg.msg;
+ msg = &channel->close_msg;
msg->header.msgtype = CHANNELMSG_CLOSECHANNEL;
msg->child_relid = channel->offermsg.child_relid;
diff --git a/include/linux/hyperv.h b/include/linux/hyperv.h
index a59c5c3e95fb..59826c89171c 100644
--- a/include/linux/hyperv.h
+++ b/include/linux/hyperv.h
@@ -707,11 +707,6 @@ struct vmbus_channel_msginfo {
unsigned char msg[];
};
-struct vmbus_close_msg {
- struct vmbus_channel_msginfo info;
- struct vmbus_channel_close_channel msg;
-};
-
enum vmbus_device_type {
HV_IDE = 0,
HV_SCSI,
@@ -800,7 +795,7 @@ struct vmbus_channel {
struct hv_ring_buffer_info outbound; /* send to parent */
struct hv_ring_buffer_info inbound; /* receive from parent */
- struct vmbus_close_msg close_msg;
+ struct vmbus_channel_close_channel close_msg;
/* Statistics */
u64 interrupts; /* Host to Guest interrupts */
--
2.25.1
^ permalink raw reply related
* RE: [PATCH] fixup! mshv: Add support for a new parent partition configuration
From: Michael Kelley @ 2025-08-31 15:14 UTC (permalink / raw)
To: Nuno Das Neves, linux-hyperv@vger.kernel.org,
linux-kernel@vger.kernel.org
Cc: kys@microsoft.com, haiyangz@microsoft.com, wei.liu@kernel.org,
decui@microsoft.com
In-Reply-To: <1756498672-17603-1-git-send-email-nunodasneves@linux.microsoft.com>
From: Nuno Das Neves <nunodasneves@linux.microsoft.com> Sent: Friday, August 29, 2025 1:18 PM
>
> ---
> drivers/hv/hv_common.c | 2 +-
> 1 file changed, 1 insertion(+), 1 deletion(-)
>
> diff --git a/drivers/hv/hv_common.c b/drivers/hv/hv_common.c
> index 8836cf9fad40..e109a620c83f 100644
> --- a/drivers/hv/hv_common.c
> +++ b/drivers/hv/hv_common.c
> @@ -257,7 +257,7 @@ static void hv_kmsg_dump_register(void)
>
> static inline bool hv_output_page_exists(void)
> {
> - return hv_root_partition() || IS_ENABLED(CONFIG_HYPERV_VTL_MODE);
> + return hv_parent_partition() || IS_ENABLED(CONFIG_HYPERV_VTL_MODE);
> }
>
> void __init hv_get_partition_id(void)
> --
> 2.34.1
>
Reviewed-by: Michael Kelley <mhklinux@outlook.com>
^ permalink raw reply
* RE: [PATCH] mshv: Add support for a new parent partition configuration
From: Michael Kelley @ 2025-08-31 15:12 UTC (permalink / raw)
To: Nuno Das Neves, linux-hyperv@vger.kernel.org,
linux-kernel@vger.kernel.org, linux-arch@vger.kernel.org
Cc: kys@microsoft.com, haiyangz@microsoft.com, wei.liu@kernel.org,
decui@microsoft.com, arnd@arndb.de
In-Reply-To: <1755588559-29629-1-git-send-email-nunodasneves@linux.microsoft.com>
From: Nuno Das Neves <nunodasneves@linux.microsoft.com> Sent: Tuesday, August 19, 2025 12:29 AM
>
> Detect booting as an "L1VH" partition. This is a new scenario very
> similar to root partition where the mshv_root driver can be used to
> create and manage guest partitions.
>
> It mostly works the same as root partition, but there are some
> differences in how various features are handled. hv_l1vh_partition()
> is introduced to handle these cases. Add hv_parent_partition()
> which returns true for either case, replacing some hv_root_partition()
> checks.
>
> Signed-off-by: Nuno Das Neves <nunodasneves@linux.microsoft.com>
> ---
> drivers/hv/hv_common.c | 20 ++++++++++++--------
> drivers/hv/mshv_root_main.c | 22 ++++++++++++++--------
> include/asm-generic/mshyperv.h | 11 +++++++++++
> 3 files changed, 37 insertions(+), 16 deletions(-)
>
> diff --git a/drivers/hv/hv_common.c b/drivers/hv/hv_common.c
> index cbe4a954ad46..a6839593ca31 100644
> --- a/drivers/hv/hv_common.c
> +++ b/drivers/hv/hv_common.c
> @@ -357,7 +357,7 @@ int __init hv_common_init(void)
> hyperv_pcpu_arg = alloc_percpu(void *);
> BUG_ON(!hyperv_pcpu_arg);
>
> - if (hv_root_partition()) {
> + if (hv_parent_partition()) {
> hv_synic_eventring_tail = alloc_percpu(u8 *);
> BUG_ON(!hv_synic_eventring_tail);
> }
> @@ -506,7 +506,7 @@ int hv_common_cpu_init(unsigned int cpu)
> if (msr_vp_index > hv_max_vp_index)
> hv_max_vp_index = msr_vp_index;
>
> - if (hv_root_partition()) {
> + if (hv_parent_partition()) {
> synic_eventring_tail = (u8 **)this_cpu_ptr(hv_synic_eventring_tail);
> *synic_eventring_tail = kcalloc(HV_SYNIC_SINT_COUNT,
> sizeof(u8), flags);
> @@ -532,7 +532,7 @@ int hv_common_cpu_die(unsigned int cpu)
> * originally allocated memory is reused in hv_common_cpu_init().
> */
>
> - if (hv_root_partition()) {
> + if (hv_parent_partition()) {
> synic_eventring_tail = this_cpu_ptr(hv_synic_eventring_tail);
> kfree(*synic_eventring_tail);
> *synic_eventring_tail = NULL;
> @@ -703,13 +703,17 @@ void hv_identify_partition_type(void)
> * the root partition setting if also a Confidential VM.
> */
> if ((ms_hyperv.priv_high & HV_CREATE_PARTITIONS) &&
> - (ms_hyperv.priv_high & HV_CPU_MANAGEMENT) &&
> !(ms_hyperv.priv_high & HV_ISOLATION)) {
> - pr_info("Hyper-V: running as root partition\n");
> - if (IS_ENABLED(CONFIG_MSHV_ROOT))
> - hv_curr_partition_type = HV_PARTITION_TYPE_ROOT;
> - else
> +
> + if (!IS_ENABLED(CONFIG_MSHV_ROOT)) {
> pr_crit("Hyper-V: CONFIG_MSHV_ROOT not enabled!\n");
> + } else if (ms_hyperv.priv_high & HV_CPU_MANAGEMENT) {
> + pr_info("Hyper-V: running as root partition\n");
> + hv_curr_partition_type = HV_PARTITION_TYPE_ROOT;
> + } else {
> + pr_info("Hyper-V: running as L1VH partition\n");
> + hv_curr_partition_type = HV_PARTITION_TYPE_L1VH;
> + }
> }
> }
>
> diff --git a/drivers/hv/mshv_root_main.c b/drivers/hv/mshv_root_main.c
> index aca3331ad516..7c710703cd96 100644
> --- a/drivers/hv/mshv_root_main.c
> +++ b/drivers/hv/mshv_root_main.c
> @@ -37,12 +37,6 @@ MODULE_AUTHOR("Microsoft");
> MODULE_LICENSE("GPL");
> MODULE_DESCRIPTION("Microsoft Hyper-V root partition VMM interface /dev/mshv");
>
> -/* TODO move this to mshyperv.h when needed outside driver */
> -static inline bool hv_parent_partition(void)
> -{
> - return hv_root_partition();
> -}
> -
> /* TODO move this to another file when debugfs code is added */
> enum hv_stats_vp_counters { /* HV_THREAD_COUNTER */
> #if defined(CONFIG_X86)
> @@ -2190,6 +2184,15 @@ struct notifier_block mshv_reboot_nb = {
> .notifier_call = mshv_reboot_notify,
> };
>
> +static int __init mshv_l1vh_partition_init(struct device *dev)
> +{
> + hv_scheduler_type = HV_SCHEDULER_TYPE_CORE_SMT;
> + dev_info(dev, "Hypervisor using %s\n",
> + scheduler_type_to_string(hv_scheduler_type));
> +
> + return 0;
> +}
I'm a bit late reviewing this patch, but I have a suggestion. With this
function added, setting hv_scheduler_type and outputting the message
is now in two places: here and in mshv_retrieve_scheduler_type().
Instead, check for root vs. L1VH in mshv_retrieve_scheduler_type(),
and either call hv_retrieve_scheduler_type(), or force to
HV_SCHEDULER_TYPE_CORE_SMT. Then the rest of the code in
mshv_retrieve_scheduler_type(), including outputting the message,
just works. That puts all the logic about the scheduler type in one
place.
Then move the call to mshv_retrieve_scheduler_type() directly
into mshv_parent_partition_init() just before
if (hv_root_partition())
ret = mshv_root_partition_init(dev);
mshv_l1vh_partition_init() is no longer needed.
Independent of the suggestion, everything else looks good.
Reviewed-by: Michael Kelley <mhklinux@outlook.com>
> +
> static void mshv_root_partition_exit(void)
> {
> unregister_reboot_notifier(&mshv_reboot_nb);
> @@ -2224,7 +2227,7 @@ static int __init mshv_parent_partition_init(void)
> struct device *dev;
> union hv_hypervisor_version_info version_info;
>
> - if (!hv_root_partition() || is_kdump_kernel())
> + if (!hv_parent_partition() || is_kdump_kernel())
> return -ENODEV;
>
> if (hv_get_hypervisor_version(&version_info))
> @@ -2261,7 +2264,10 @@ static int __init mshv_parent_partition_init(void)
>
> mshv_cpuhp_online = ret;
>
> - ret = mshv_root_partition_init(dev);
> + if (hv_root_partition())
> + ret = mshv_root_partition_init(dev);
> + else
> + ret = mshv_l1vh_partition_init(dev);
> if (ret)
> goto remove_cpu_state;
>
> diff --git a/include/asm-generic/mshyperv.h b/include/asm-generic/mshyperv.h
> index dbbacd47ca35..f0f0eacb2eef 100644
> --- a/include/asm-generic/mshyperv.h
> +++ b/include/asm-generic/mshyperv.h
> @@ -31,6 +31,7 @@
> enum hv_partition_type {
> HV_PARTITION_TYPE_GUEST,
> HV_PARTITION_TYPE_ROOT,
> + HV_PARTITION_TYPE_L1VH,
> };
>
> struct ms_hyperv_info {
> @@ -457,12 +458,22 @@ static inline bool hv_root_partition(void)
> {
> return hv_curr_partition_type == HV_PARTITION_TYPE_ROOT;
> }
> +static inline bool hv_l1vh_partition(void)
> +{
> + return hv_curr_partition_type == HV_PARTITION_TYPE_L1VH;
> +}
> +static inline bool hv_parent_partition(void)
> +{
> + return hv_root_partition() || hv_l1vh_partition();
> +}
> int hv_call_deposit_pages(int node, u64 partition_id, u32 num_pages);
> int hv_call_add_logical_proc(int node, u32 lp_index, u32 acpi_id);
> int hv_call_create_vp(int node, u64 partition_id, u32 vp_index, u32 flags);
>
> #else /* CONFIG_MSHV_ROOT */
> static inline bool hv_root_partition(void) { return false; }
> +static inline bool hv_l1vh_partition(void) { return false; }
> +static inline bool hv_parent_partition(void) { return false; }
> static inline int hv_call_deposit_pages(int node, u64 partition_id, u32 num_pages)
> {
> return -EOPNOTSUPP;
> --
> 2.34.1
^ permalink raw reply
* Re: [PATCH V0 1/2] hyper-v: Add CONFIG_HYPERV_VMBUS option
From: Mukesh R @ 2025-08-29 20:59 UTC (permalink / raw)
To: Nuno Das Neves, dri-devel, linux-kernel, linux-input,
linux-hyperv, netdev, linux-pci, linux-scsi, linux-fbdev,
linux-arch, virtualization
Cc: maarten.lankhorst, mripard, tzimmermann, airlied, simona, jikos,
bentiss, kys, haiyangz, wei.liu, decui, dmitry.torokhov,
andrew+netdev, davem, edumazet, kuba, pabeni, bhelgaas,
James.Bottomley, martin.petersen, gregkh, deller, arnd, sgarzare,
horms
In-Reply-To: <5003d5e8-a025-4827-b8a0-6fe11877421b@linux.microsoft.com>
On 8/28/25 17:29, Nuno Das Neves wrote:
> On 8/27/2025 5:59 PM, Mukesh Rathor wrote:
>> Somehow vmbus driver is hinged on CONFIG_HYPERV. It appears this is initial
>> code that did not get addressed when the scope of CONFIG_HYPERV went beyond
>> vmbus. This commit creates a fine grained HYPERV_VMBUS option and updates
>> drivers that depend on VMBUS.
>>
>
> The commit message can be improved. The docs are helpful here:
> https://docs.kernel.org/process/submitting-patches.html#describe-your-changes
>
> In particular, some clearer reasons for the change.
> e.g.
> - CONFIG_HYPERV encompasses too much right now. It's not always clear what
> depends on builtin hyperv code and what depends on vmbus.
>
> - Since there is so much builtin hyperv code, building CONFIG_HYPERV as a
> module doesn't make intuitive sense. Building vmbus support as a module does.
>
> - There are actually some real scenarios someone may want to compile with
> CONFIG_HYPERV but without vmbus, like baremetal root partition.
>
> FWIW I think it's a good idea, interested to hear what others think.
Sorry, you had mentioned it and I expanded the cover letter and forgot the
commit message here. You said it better than I could above, so I can just use
that in V1 next week if no other comments.
Thanks,
-Mukesh
> Nuno
>
>> Signed-off-by: Mukesh Rathor <mrathor@linux.microsoft.com>
>> ---
>> drivers/gpu/drm/Kconfig | 2 +-
>> drivers/hid/Kconfig | 2 +-
>> drivers/hv/Kconfig | 12 +++++++++---
>> drivers/hv/Makefile | 2 +-
>> drivers/input/serio/Kconfig | 4 ++--
>> drivers/net/hyperv/Kconfig | 2 +-
>> drivers/pci/Kconfig | 2 +-
>> drivers/scsi/Kconfig | 2 +-
>> drivers/uio/Kconfig | 2 +-
>> drivers/video/fbdev/Kconfig | 2 +-
>> include/asm-generic/mshyperv.h | 8 +++++---
>> net/vmw_vsock/Kconfig | 2 +-
>> 12 files changed, 25 insertions(+), 17 deletions(-)
>>
>> diff --git a/drivers/gpu/drm/Kconfig b/drivers/gpu/drm/Kconfig
>> index f7ea8e895c0c..58f34da061c6 100644
>> --- a/drivers/gpu/drm/Kconfig
>> +++ b/drivers/gpu/drm/Kconfig
>> @@ -398,7 +398,7 @@ source "drivers/gpu/drm/imagination/Kconfig"
>>
>> config DRM_HYPERV
>> tristate "DRM Support for Hyper-V synthetic video device"
>> - depends on DRM && PCI && HYPERV
>> + depends on DRM && PCI && HYPERV_VMBUS
>> select DRM_CLIENT_SELECTION
>> select DRM_KMS_HELPER
>> select DRM_GEM_SHMEM_HELPER
>> diff --git a/drivers/hid/Kconfig b/drivers/hid/Kconfig
>> index a57901203aeb..fe3dc8c0db99 100644
>> --- a/drivers/hid/Kconfig
>> +++ b/drivers/hid/Kconfig
>> @@ -1162,7 +1162,7 @@ config GREENASIA_FF
>>
>> config HID_HYPERV_MOUSE
>> tristate "Microsoft Hyper-V mouse driver"
>> - depends on HYPERV
>> + depends on HYPERV_VMBUS
>> help
>> Select this option to enable the Hyper-V mouse driver.
>>
>> diff --git a/drivers/hv/Kconfig b/drivers/hv/Kconfig
>> index 2e8df09db599..08c4ed005137 100644
>> --- a/drivers/hv/Kconfig
>> +++ b/drivers/hv/Kconfig
>> @@ -44,18 +44,24 @@ config HYPERV_TIMER
>>
>> config HYPERV_UTILS
>> tristate "Microsoft Hyper-V Utilities driver"
>> - depends on HYPERV && CONNECTOR && NLS
>> + depends on HYPERV_VMBUS && CONNECTOR && NLS
>> depends on PTP_1588_CLOCK_OPTIONAL
>> help
>> Select this option to enable the Hyper-V Utilities.
>>
>> config HYPERV_BALLOON
>> tristate "Microsoft Hyper-V Balloon driver"
>> - depends on HYPERV
>> + depends on HYPERV_VMBUS
>> select PAGE_REPORTING
>> help
>> Select this option to enable Hyper-V Balloon driver.
>>
>> +config HYPERV_VMBUS
>> + tristate "Microsoft Hyper-V Vmbus driver"
>> + depends on HYPERV
>> + help
>> + Select this option to enable Hyper-V Vmbus driver.
>> +
>> config MSHV_ROOT
>> tristate "Microsoft Hyper-V root partition support"
>> depends on HYPERV && (X86_64 || ARM64)
>> @@ -75,7 +81,7 @@ config MSHV_ROOT
>>
>> config MSHV_VTL
>> tristate "Microsoft Hyper-V VTL driver"
>> - depends on X86_64 && HYPERV_VTL_MODE
>> + depends on X86_64 && HYPERV_VTL_MODE && HYPERV_VMBUS
>> # Mapping VTL0 memory to a userspace process in VTL2 is supported in OpenHCL.
>> # VTL2 for OpenHCL makes use of Huge Pages to improve performance on VMs,
>> # specially with large memory requirements.
>> diff --git a/drivers/hv/Makefile b/drivers/hv/Makefile
>> index c53a0df746b7..050517756a82 100644
>> --- a/drivers/hv/Makefile
>> +++ b/drivers/hv/Makefile
>> @@ -1,5 +1,5 @@
>> # SPDX-License-Identifier: GPL-2.0
>> -obj-$(CONFIG_HYPERV) += hv_vmbus.o
>> +obj-$(CONFIG_HYPERV_VMBUS) += hv_vmbus.o
>> obj-$(CONFIG_HYPERV_UTILS) += hv_utils.o
>> obj-$(CONFIG_HYPERV_BALLOON) += hv_balloon.o
>> obj-$(CONFIG_MSHV_ROOT) += mshv_root.o
>> diff --git a/drivers/input/serio/Kconfig b/drivers/input/serio/Kconfig
>> index 17edc1597446..c7ef347a4dff 100644
>> --- a/drivers/input/serio/Kconfig
>> +++ b/drivers/input/serio/Kconfig
>> @@ -276,8 +276,8 @@ config SERIO_OLPC_APSP
>>
>> config HYPERV_KEYBOARD
>> tristate "Microsoft Synthetic Keyboard driver"
>> - depends on HYPERV
>> - default HYPERV
>> + depends on HYPERV_VMBUS
>> + default HYPERV_VMBUS
>> help
>> Select this option to enable the Hyper-V Keyboard driver.
>>
>> diff --git a/drivers/net/hyperv/Kconfig b/drivers/net/hyperv/Kconfig
>> index c8cbd85adcf9..982964c1a9fb 100644
>> --- a/drivers/net/hyperv/Kconfig
>> +++ b/drivers/net/hyperv/Kconfig
>> @@ -1,7 +1,7 @@
>> # SPDX-License-Identifier: GPL-2.0-only
>> config HYPERV_NET
>> tristate "Microsoft Hyper-V virtual network driver"
>> - depends on HYPERV
>> + depends on HYPERV_VMBUS
>> select UCS2_STRING
>> select NLS
>> help
>> diff --git a/drivers/pci/Kconfig b/drivers/pci/Kconfig
>> index 9a249c65aedc..7065a8e5f9b1 100644
>> --- a/drivers/pci/Kconfig
>> +++ b/drivers/pci/Kconfig
>> @@ -221,7 +221,7 @@ config PCI_LABEL
>>
>> config PCI_HYPERV
>> tristate "Hyper-V PCI Frontend"
>> - depends on ((X86 && X86_64) || ARM64) && HYPERV && PCI_MSI && SYSFS
>> + depends on ((X86 && X86_64) || ARM64) && HYPERV_VMBUS && PCI_MSI && SYSFS
>> select PCI_HYPERV_INTERFACE
>> select IRQ_MSI_LIB
>> help
>> diff --git a/drivers/scsi/Kconfig b/drivers/scsi/Kconfig
>> index 5522310bab8d..19d0884479a2 100644
>> --- a/drivers/scsi/Kconfig
>> +++ b/drivers/scsi/Kconfig
>> @@ -589,7 +589,7 @@ config XEN_SCSI_FRONTEND
>>
>> config HYPERV_STORAGE
>> tristate "Microsoft Hyper-V virtual storage driver"
>> - depends on SCSI && HYPERV
>> + depends on SCSI && HYPERV_VMBUS
>> depends on m || SCSI_FC_ATTRS != m
>> default HYPERV
>> help
>> diff --git a/drivers/uio/Kconfig b/drivers/uio/Kconfig
>> index b060dcd7c635..6f86a61231e6 100644
>> --- a/drivers/uio/Kconfig
>> +++ b/drivers/uio/Kconfig
>> @@ -140,7 +140,7 @@ config UIO_MF624
>>
>> config UIO_HV_GENERIC
>> tristate "Generic driver for Hyper-V VMBus"
>> - depends on HYPERV
>> + depends on HYPERV_VMBUS
>> help
>> Generic driver that you can bind, dynamically, to any
>> Hyper-V VMBus device. It is useful to provide direct access
>> diff --git a/drivers/video/fbdev/Kconfig b/drivers/video/fbdev/Kconfig
>> index c21484d15f0c..72c63eaeb983 100644
>> --- a/drivers/video/fbdev/Kconfig
>> +++ b/drivers/video/fbdev/Kconfig
>> @@ -1774,7 +1774,7 @@ config FB_BROADSHEET
>>
>> config FB_HYPERV
>> tristate "Microsoft Hyper-V Synthetic Video support"
>> - depends on FB && HYPERV
>> + depends on FB && HYPERV_VMBUS
>> select DMA_CMA if HAVE_DMA_CONTIGUOUS && CMA
>> select FB_IOMEM_HELPERS_DEFERRED
>> help
>> diff --git a/include/asm-generic/mshyperv.h b/include/asm-generic/mshyperv.h
>> index 1d2ad1304ad4..66c58c91b530 100644
>> --- a/include/asm-generic/mshyperv.h
>> +++ b/include/asm-generic/mshyperv.h
>> @@ -165,6 +165,7 @@ static inline u64 hv_generate_guest_id(u64 kernel_version)
>>
>> void __init hv_mark_resources(void);
>>
>> +#if IS_ENABLED(CONFIG_HYPERV_VMBUS)
>> /* Free the message slot and signal end-of-message if required */
>> static inline void vmbus_signal_eom(struct hv_message *msg, u32 old_msg_type)
>> {
>> @@ -200,6 +201,10 @@ static inline void vmbus_signal_eom(struct hv_message *msg, u32 old_msg_type)
>> }
>> }
>>
>> +extern int vmbus_interrupt;
>> +extern int vmbus_irq;
>> +#endif /* CONFIG_HYPERV_VMBUS */
>> +
>> int hv_get_hypervisor_version(union hv_hypervisor_version_info *info);
>>
>> void hv_setup_vmbus_handler(void (*handler)(void));
>> @@ -213,9 +218,6 @@ void hv_setup_crash_handler(void (*handler)(struct pt_regs *regs));
>> void hv_remove_crash_handler(void);
>> void hv_setup_mshv_handler(void (*handler)(void));
>>
>> -extern int vmbus_interrupt;
>> -extern int vmbus_irq;
>> -
>> #if IS_ENABLED(CONFIG_HYPERV)
>> /*
>> * Hypervisor's notion of virtual processor ID is different from
>> diff --git a/net/vmw_vsock/Kconfig b/net/vmw_vsock/Kconfig
>> index 56356d2980c8..8e803c4828c4 100644
>> --- a/net/vmw_vsock/Kconfig
>> +++ b/net/vmw_vsock/Kconfig
>> @@ -72,7 +72,7 @@ config VIRTIO_VSOCKETS_COMMON
>>
>> config HYPERV_VSOCKETS
>> tristate "Hyper-V transport for Virtual Sockets"
>> - depends on VSOCKETS && HYPERV
>> + depends on VSOCKETS && HYPERV_VMBUS
>> help
>> This module implements a Hyper-V transport for Virtual Sockets.
>>
^ permalink raw reply
* [PATCH] fixup! mshv: Add support for a new parent partition configuration
From: Nuno Das Neves @ 2025-08-29 20:17 UTC (permalink / raw)
To: linux-hyperv, linux-kernel; +Cc: kys, haiyangz, wei.liu, decui, Nuno Das Neves
---
drivers/hv/hv_common.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/drivers/hv/hv_common.c b/drivers/hv/hv_common.c
index 8836cf9fad40..e109a620c83f 100644
--- a/drivers/hv/hv_common.c
+++ b/drivers/hv/hv_common.c
@@ -257,7 +257,7 @@ static void hv_kmsg_dump_register(void)
static inline bool hv_output_page_exists(void)
{
- return hv_root_partition() || IS_ENABLED(CONFIG_HYPERV_VTL_MODE);
+ return hv_parent_partition() || IS_ENABLED(CONFIG_HYPERV_VTL_MODE);
}
void __init hv_get_partition_id(void)
--
2.34.1
^ permalink raw reply related
* Re: [PATCH v2 2/7] Drivers: hv: Disentangle VTL return cancellation from SIGPENDING
From: Wei Liu @ 2025-08-29 18:38 UTC (permalink / raw)
To: Sean Christopherson
Cc: Marc Zyngier, Oliver Upton, Catalin Marinas, Will Deacon,
Tianrui Zhao, Bibo Mao, Huacai Chen, Anup Patel, Paul Walmsley,
Palmer Dabbelt, Albert Ou, Paolo Bonzini, Thomas Gleixner,
Ingo Molnar, Borislav Petkov, Dave Hansen, x86, K. Y. Srinivasan,
Haiyang Zhang, Wei Liu, Dexuan Cui, Peter Zijlstra,
Andy Lutomirski, Paul E. McKenney, Frederic Weisbecker,
Neeraj Upadhyay, Joel Fernandes, Josh Triplett, Boqun Feng,
Uladzislau Rezki, linux-kernel, linux-arm-kernel, kvmarm, kvm,
loongarch, kvm-riscv, linux-riscv, linux-hyperv, rcu,
Nuno Das Neves, Mukesh R, ssengar, namjain
In-Reply-To: <20250828000156.23389-3-seanjc@google.com>
On Wed, Aug 27, 2025 at 05:01:51PM -0700, Sean Christopherson wrote:
> Check for return to a lower VTL being cancelled separately from handling
> pending TIF-based work, as there is no need to immediately process pending
> work; the kernel will immediately exit to userspace (ignoring preemption)
> and handle the pending work at that time.
>
> Disentangling cancellation from the TIF-based work will allow switching to
> common virtualization APIs for detecting and processing pending work.
>
> Signed-off-by: Sean Christopherson <seanjc@google.com>
Thanks for this patch, Sean.
My current plan is to drop this driver from my tree (hence it will
disappear from linux-next soon) because Peter has an objection to the
ABI it introduces. I just have not gotten around to it yet.
I won't apply this patch, the next one and the last one. I have CC'ed
the owner of that driver to this patch here so that your suggestion can
be incorporated in future submissions.
CC Saurabh and Naman.
Thanks,
Wei
> ---
> drivers/hv/mshv_vtl_main.c | 12 +++++++-----
> 1 file changed, 7 insertions(+), 5 deletions(-)
>
> diff --git a/drivers/hv/mshv_vtl_main.c b/drivers/hv/mshv_vtl_main.c
> index 12f5e77b7095..aa09a76f0eff 100644
> --- a/drivers/hv/mshv_vtl_main.c
> +++ b/drivers/hv/mshv_vtl_main.c
> @@ -731,19 +731,21 @@ static int mshv_vtl_ioctl_return_to_lower_vtl(void)
> _TIF_NOTIFY_RESUME | _TIF_NOTIFY_SIGNAL |
> _TIF_NEED_RESCHED_LAZY;
> unsigned long ti_work;
> - u32 cancel;
> unsigned long irq_flags;
> struct hv_vp_assist_page *hvp;
> int ret;
>
> local_irq_save(irq_flags);
> + if (READ_ONCE(mshv_vtl_this_run()->cancel)) {
> + local_irq_restore(irq_flags);
> + preempt_enable();
> + return -EINTR;
> + }
> +
> ti_work = READ_ONCE(current_thread_info()->flags);
> - cancel = READ_ONCE(mshv_vtl_this_run()->cancel);
> - if (unlikely((ti_work & VTL0_WORK) || cancel)) {
> + if (unlikely(ti_work & VTL0_WORK)) {
> local_irq_restore(irq_flags);
> preempt_enable();
> - if (cancel)
> - ti_work |= _TIF_SIGPENDING;
> ret = mshv_do_pre_guest_mode_work(ti_work);
> if (ret)
> return ret;
> --
> 2.51.0.268.g9569e192d0-goog
>
>
^ permalink raw reply
* RE: [PATCH v4] x86/hyperv: Fix kdump on Azure CVMs
From: Michael Kelley @ 2025-08-29 17:03 UTC (permalink / raw)
To: Vitaly Kuznetsov, linux-hyperv@vger.kernel.org,
linux-coco@lists.linux.dev
Cc: K. Y. Srinivasan, Haiyang Zhang, Wei Liu, Dexuan Cui,
x86@kernel.org, linux-kernel@vger.kernel.org, Nuno Das Neves,
Tianyu Lan, Li Tian, Philipp Rudo
In-Reply-To: <20250828091618.884950-1-vkuznets@redhat.com>
From: Vitaly Kuznetsov <vkuznets@redhat.com> Sent: Thursday, August 28, 2025 2:16 AM
>
> Azure CVM instance types featuring a paravisor hang upon kdump. The
> investigation shows that makedumpfile causes a hang when it steps on a page
> which was previously share with the host
> (HVCALL_MODIFY_SPARSE_GPA_PAGE_HOST_VISIBILITY). The new kernel has no
> knowledge of these 'special' regions (which are Vmbus connection pages,
> GPADL buffers, ...). There are several ways to approach the issue:
> - Convey the knowledge about these regions to the new kernel somehow.
> - Unshare these regions before accessing in the new kernel (it is unclear
> if there's a way to query the status for a given GPA range).
> - Unshare these regions before jumping to the new kernel (which this patch
> implements).
>
> To make the procedure as robust as possible, store PFN ranges of shared
> regions in a linked list instead of storing GVAs and re-using
> hv_vtom_set_host_visibility(). This also allows to avoid memory allocation
> on the kdump/kexec path.
>
> Signed-off-by: Vitaly Kuznetsov <vkuznets@redhat.com>
Looks good!
Adding Confidential Computing mailing list (linux-coco@lists.linux.dev)
for visibility.
Reviewed-by: Michael Kelley <mhklinux@outlook.com>
> ---
> Changes since v3 [Michael Kelley]:
> - Employ x86_platform.guest.enc_kexec_{begin,finish} hooks.
> - Don't use spinlock in what's now hv_vtom_kexec_finish().
> - Handle possible hypercall failures in hv_mark_gpa_visibility()
> symmetrically; change hv_list_enc_remove() to return -ENOMEM as well.
> - Rebase to the latest hyperv/next.
> ---
> arch/x86/hyperv/ivm.c | 211 +++++++++++++++++++++++++++++++++++++++++-
> 1 file changed, 210 insertions(+), 1 deletion(-)
>
> diff --git a/arch/x86/hyperv/ivm.c b/arch/x86/hyperv/ivm.c
> index ade6c665c97e..a4615b889f3e 100644
> --- a/arch/x86/hyperv/ivm.c
> +++ b/arch/x86/hyperv/ivm.c
> @@ -462,6 +462,195 @@ void hv_ivm_msr_read(u64 msr, u64 *value)
> hv_ghcb_msr_read(msr, value);
> }
>
> +/*
> + * Keep track of the PFN regions which were shared with the host. The access
> + * must be revoked upon kexec/kdump (see hv_ivm_clear_host_access()).
> + */
> +struct hv_enc_pfn_region {
> + struct list_head list;
> + u64 pfn;
> + int count;
> +};
> +
> +static LIST_HEAD(hv_list_enc);
> +static DEFINE_RAW_SPINLOCK(hv_list_enc_lock);
> +
> +static int hv_list_enc_add(const u64 *pfn_list, int count)
> +{
> + struct hv_enc_pfn_region *ent;
> + unsigned long flags;
> + u64 pfn;
> + int i;
> +
> + for (i = 0; i < count; i++) {
> + pfn = pfn_list[i];
> +
> + raw_spin_lock_irqsave(&hv_list_enc_lock, flags);
> + /* Check if the PFN already exists in some region first */
> + list_for_each_entry(ent, &hv_list_enc, list) {
> + if ((ent->pfn <= pfn) && (ent->pfn + ent->count - 1 >= pfn))
> + /* Nothing to do - pfn is already in the list */
> + goto unlock_done;
> + }
> +
> + /*
> + * Check if the PFN is adjacent to an existing region. Growing
> + * a region can make it adjacent to another one but merging is
> + * not (yet) implemented for simplicity. A PFN cannot be added
> + * to two regions to keep the logic in hv_list_enc_remove()
> + * correct.
> + */
> + list_for_each_entry(ent, &hv_list_enc, list) {
> + if (ent->pfn + ent->count == pfn) {
> + /* Grow existing region up */
> + ent->count++;
> + goto unlock_done;
> + } else if (pfn + 1 == ent->pfn) {
> + /* Grow existing region down */
> + ent->pfn--;
> + ent->count++;
> + goto unlock_done;
> + }
> + }
> + raw_spin_unlock_irqrestore(&hv_list_enc_lock, flags);
> +
> + /* No adjacent region found -- create a new one */
> + ent = kzalloc(sizeof(struct hv_enc_pfn_region), GFP_KERNEL);
> + if (!ent)
> + return -ENOMEM;
> +
> + ent->pfn = pfn;
> + ent->count = 1;
> +
> + raw_spin_lock_irqsave(&hv_list_enc_lock, flags);
> + list_add(&ent->list, &hv_list_enc);
> +
> +unlock_done:
> + raw_spin_unlock_irqrestore(&hv_list_enc_lock, flags);
> + }
> +
> + return 0;
> +}
> +
> +static int hv_list_enc_remove(const u64 *pfn_list, int count)
> +{
> + struct hv_enc_pfn_region *ent, *t;
> + struct hv_enc_pfn_region new_region;
> + unsigned long flags;
> + u64 pfn;
> + int i;
> +
> + for (i = 0; i < count; i++) {
> + pfn = pfn_list[i];
> +
> + raw_spin_lock_irqsave(&hv_list_enc_lock, flags);
> + list_for_each_entry_safe(ent, t, &hv_list_enc, list) {
> + if (pfn == ent->pfn + ent->count - 1) {
> + /* Removing tail pfn */
> + ent->count--;
> + if (!ent->count) {
> + list_del(&ent->list);
> + kfree(ent);
> + }
> + goto unlock_done;
> + } else if (pfn == ent->pfn) {
> + /* Removing head pfn */
> + ent->count--;
> + ent->pfn++;
> + if (!ent->count) {
> + list_del(&ent->list);
> + kfree(ent);
> + }
> + goto unlock_done;
> + } else if (pfn > ent->pfn && pfn < ent->pfn + ent->count - 1) {
> + /*
> + * Removing a pfn in the middle. Cut off the tail
> + * of the existing region and create a template for
> + * the new one.
> + */
> + new_region.pfn = pfn + 1;
> + new_region.count = ent->count - (pfn - ent->pfn + 1);
> + ent->count = pfn - ent->pfn;
> + goto unlock_split;
> + }
> +
> + }
> +unlock_done:
> + raw_spin_unlock_irqrestore(&hv_list_enc_lock, flags);
> + continue;
> +
> +unlock_split:
> + raw_spin_unlock_irqrestore(&hv_list_enc_lock, flags);
> +
> + ent = kzalloc(sizeof(struct hv_enc_pfn_region), GFP_KERNEL);
> + if (!ent)
> + return -ENOMEM;
> +
> + ent->pfn = new_region.pfn;
> + ent->count = new_region.count;
> +
> + raw_spin_lock_irqsave(&hv_list_enc_lock, flags);
> + list_add(&ent->list, &hv_list_enc);
> + raw_spin_unlock_irqrestore(&hv_list_enc_lock, flags);
> + }
> +
> + return 0;
> +}
> +
> +/* Stop new private<->shared conversions */
> +static void hv_vtom_kexec_begin(void)
> +{
> + if (!IS_ENABLED(CONFIG_KEXEC_CORE))
> + return;
> +
> + /*
> + * Crash kernel reaches here with interrupts disabled: can't wait for
> + * conversions to finish.
> + *
> + * If race happened, just report and proceed.
> + */
> + if (!set_memory_enc_stop_conversion())
> + pr_warn("Failed to stop shared<->private conversions\n");
> +}
> +
> +static void hv_vtom_kexec_finish(void)
> +{
> + struct hv_gpa_range_for_visibility *input;
> + struct hv_enc_pfn_region *ent;
> + unsigned long flags;
> + u64 hv_status;
> + int cur, i;
> +
> + local_irq_save(flags);
> + input = *this_cpu_ptr(hyperv_pcpu_input_arg);
> +
> + if (unlikely(!input))
> + goto out;
> +
> + list_for_each_entry(ent, &hv_list_enc, list) {
> + for (i = 0, cur = 0; i < ent->count; i++) {
> + input->gpa_page_list[cur] = ent->pfn + i;
> + cur++;
> +
> + if (cur == HV_MAX_MODIFY_GPA_REP_COUNT || i == ent->count - 1) {
> + input->partition_id = HV_PARTITION_ID_SELF;
> + input->host_visibility = VMBUS_PAGE_NOT_VISIBLE;
> + input->reserved0 = 0;
> + input->reserved1 = 0;
> + hv_status = hv_do_rep_hypercall(
> + HVCALL_MODIFY_SPARSE_GPA_PAGE_HOST_VISIBILITY,
> + cur, 0, input, NULL);
> + WARN_ON_ONCE(!hv_result_success(hv_status));
> + cur = 0;
> + }
> + }
> +
> + }
> +
> +out:
> + local_irq_restore(flags);
> +}
> +
> /*
> * hv_mark_gpa_visibility - Set pages visible to host via hvcall.
> *
> @@ -475,6 +664,7 @@ static int hv_mark_gpa_visibility(u16 count, const u64 pfn[],
> struct hv_gpa_range_for_visibility *input;
> u64 hv_status;
> unsigned long flags;
> + int ret;
>
> /* no-op if partition isolation is not enabled */
> if (!hv_is_isolation_supported())
> @@ -486,6 +676,13 @@ static int hv_mark_gpa_visibility(u16 count, const u64 pfn[],
> return -EINVAL;
> }
>
> + if (visibility == VMBUS_PAGE_NOT_VISIBLE)
> + ret = hv_list_enc_remove(pfn, count);
> + else
> + ret = hv_list_enc_add(pfn, count);
> + if (ret)
> + return ret;
> +
> local_irq_save(flags);
> input = *this_cpu_ptr(hyperv_pcpu_input_arg);
>
> @@ -506,8 +703,18 @@ static int hv_mark_gpa_visibility(u16 count, const u64 pfn[],
>
> if (hv_result_success(hv_status))
> return 0;
> +
> + if (visibility == VMBUS_PAGE_NOT_VISIBLE)
> + ret = hv_list_enc_add(pfn, count);
> else
> - return -EFAULT;
> + ret = hv_list_enc_remove(pfn, count);
> + /*
> + * There's no good way to recover from -ENOMEM here, the accounting is
> + * wrong either way.
> + */
> + WARN_ON_ONCE(ret);
> +
> + return -EFAULT;
> }
>
> /*
> @@ -669,6 +876,8 @@ void __init hv_vtom_init(void)
> x86_platform.guest.enc_tlb_flush_required = hv_vtom_tlb_flush_required;
> x86_platform.guest.enc_status_change_prepare = hv_vtom_clear_present;
> x86_platform.guest.enc_status_change_finish = hv_vtom_set_host_visibility;
> + x86_platform.guest.enc_kexec_begin = hv_vtom_kexec_begin;
> + x86_platform.guest.enc_kexec_finish = hv_vtom_kexec_finish;
>
> /* Set WB as the default cache mode. */
> guest_force_mtrr_state(NULL, 0, MTRR_TYPE_WRBACK);
> --
> 2.50.1
^ permalink raw reply
* Re: [PATCH v2][next] hyperv: Avoid a hundred -Wflex-array-member-not-at-end warnings
From: Gustavo A. R. Silva @ 2025-08-29 16:17 UTC (permalink / raw)
To: Michael Kelley, Gustavo A. R. Silva, K. Y. Srinivasan,
Haiyang Zhang, Wei Liu, Dexuan Cui
Cc: linux-hyperv@vger.kernel.org, linux-kernel@vger.kernel.org,
linux-hardening@vger.kernel.org
In-Reply-To: <SN6PR02MB4157D4C3A66E0563A9071DF1D43AA@SN6PR02MB4157.namprd02.prod.outlook.com>
On 8/29/25 18:07, Michael Kelley wrote:
> From: Gustavo A. R. Silva <gustavoars@kernel.org> Sent: Friday, August 29, 2025 4:44 AM
>>
>> -Wflex-array-member-not-at-end was introduced in GCC-14, and we are
>> getting ready to enable it, globally.
>>
>> Use the new TRAILING_OVERLAP() helper to fix 159 of the following type
>> of warnings:
>>
>> 159 ./include/linux/hyperv.h:711:38: warning: structure containing a flexible array
>> member is not at the end of another structure [-Wflex-array-member-not-at-end]
>>
>> This helper creates a union between a flexible-array member (FAM)
>> and a set of members that would otherwise follow it. This overlays
>> the trailing members onto the FAM while preserving the original
>> memory layout.
>>
>> Also, move `struct vmbus_close_msg close_msg;` at the end of
>> `struct vmbus_channel`, as `struct vmbus_channel_msginfo,` ends
>> in a flexible array member.
>>
>> Signed-off-by: Gustavo A. R. Silva <gustavoars@kernel.org>
>> --
>> Changes in v2:
>> - Fix subject line.
>>
>> v1:
>> - Link: https://lore.kernel.org/linux-hardening/aLGSDpi4xDjUUYVm@kspp/
>>
>> include/linux/hyperv.h | 10 ++++++----
>> 1 file changed, 6 insertions(+), 4 deletions(-)
>>
>> diff --git a/include/linux/hyperv.h b/include/linux/hyperv.h
>> index a59c5c3e95fb..efdd570669fa 100644
>> --- a/include/linux/hyperv.h
>> +++ b/include/linux/hyperv.h
>> @@ -708,8 +708,9 @@ struct vmbus_channel_msginfo {
>> };
>>
>> struct vmbus_close_msg {
>> - struct vmbus_channel_msginfo info;
>
> It turns out that this field of struct vmbus_close_msg is never used.
> It dates back to 2011, so maybe somewhere along the way it stopped
> being used, but struct vmbus_close_msg was left unchanged.
>
> So a better solution to the "flex-array-member-not-at-end" issue is
> to eliminate this structure entirely, and use struct
> vmbus_channel_close_channel directly in the one place where
> struct vmbus_close_msg is currently used. I've done a quick test of
> this change and I don't see any problems.
>
> I'll submit a separate patch with my proposed change, and this
> patch can be dropped. Does that work?
Yes, please. Go for it. :)
Thanks!
-Gustavo
^ permalink raw reply
* RE: [PATCH v2][next] hyperv: Avoid a hundred -Wflex-array-member-not-at-end warnings
From: Michael Kelley @ 2025-08-29 16:07 UTC (permalink / raw)
To: Gustavo A. R. Silva, K. Y. Srinivasan, Haiyang Zhang, Wei Liu,
Dexuan Cui
Cc: linux-hyperv@vger.kernel.org, linux-kernel@vger.kernel.org,
linux-hardening@vger.kernel.org
In-Reply-To: <aLGSlaa6Llqz7jkJ@kspp>
From: Gustavo A. R. Silva <gustavoars@kernel.org> Sent: Friday, August 29, 2025 4:44 AM
>
> -Wflex-array-member-not-at-end was introduced in GCC-14, and we are
> getting ready to enable it, globally.
>
> Use the new TRAILING_OVERLAP() helper to fix 159 of the following type
> of warnings:
>
> 159 ./include/linux/hyperv.h:711:38: warning: structure containing a flexible array
> member is not at the end of another structure [-Wflex-array-member-not-at-end]
>
> This helper creates a union between a flexible-array member (FAM)
> and a set of members that would otherwise follow it. This overlays
> the trailing members onto the FAM while preserving the original
> memory layout.
>
> Also, move `struct vmbus_close_msg close_msg;` at the end of
> `struct vmbus_channel`, as `struct vmbus_channel_msginfo,` ends
> in a flexible array member.
>
> Signed-off-by: Gustavo A. R. Silva <gustavoars@kernel.org>
> --
> Changes in v2:
> - Fix subject line.
>
> v1:
> - Link: https://lore.kernel.org/linux-hardening/aLGSDpi4xDjUUYVm@kspp/
>
> include/linux/hyperv.h | 10 ++++++----
> 1 file changed, 6 insertions(+), 4 deletions(-)
>
> diff --git a/include/linux/hyperv.h b/include/linux/hyperv.h
> index a59c5c3e95fb..efdd570669fa 100644
> --- a/include/linux/hyperv.h
> +++ b/include/linux/hyperv.h
> @@ -708,8 +708,9 @@ struct vmbus_channel_msginfo {
> };
>
> struct vmbus_close_msg {
> - struct vmbus_channel_msginfo info;
It turns out that this field of struct vmbus_close_msg is never used.
It dates back to 2011, so maybe somewhere along the way it stopped
being used, but struct vmbus_close_msg was left unchanged.
So a better solution to the "flex-array-member-not-at-end" issue is
to eliminate this structure entirely, and use struct
vmbus_channel_close_channel directly in the one place where
struct vmbus_close_msg is currently used. I've done a quick test of
this change and I don't see any problems.
I'll submit a separate patch with my proposed change, and this
patch can be dropped. Does that work?
Michael
> - struct vmbus_channel_close_channel msg;
> + TRAILING_OVERLAP(struct vmbus_channel_msginfo, info, msg,
> + struct vmbus_channel_close_channel msg;
> + );
> };
>
> enum vmbus_device_type {
> @@ -800,8 +801,6 @@ struct vmbus_channel {
> struct hv_ring_buffer_info outbound; /* send to parent */
> struct hv_ring_buffer_info inbound; /* receive from parent */
>
> - struct vmbus_close_msg close_msg;
> -
> /* Statistics */
> u64 interrupts; /* Host to Guest interrupts */
> u64 sig_events; /* Guest to Host events */
> @@ -1008,6 +1007,9 @@ struct vmbus_channel {
>
> /* boolean to control visibility of sysfs for ring buffer */
> bool ring_sysfs_visible;
> +
> + /* Must be last --ends in a flexible-array member. */
> + struct vmbus_close_msg close_msg;
> };
>
> #define lock_requestor(channel, flags) \
> --
> 2.43.0
>
^ permalink raw reply
* RE: [PATCH v2] uio_hv_generic: Let userspace take care of interrupt mask
From: Long Li @ 2025-08-29 14:17 UTC (permalink / raw)
To: Naman Jain, Greg Kroah-Hartman, KY Srinivasan, Haiyang Zhang,
Wei Liu, Dexuan Cui, Stephen Hemminger
Cc: linux-hyperv@vger.kernel.org, linux-kernel@vger.kernel.org,
Michael Kelley, stable@vger.kernel.org
In-Reply-To: <20250828044200.492030-1-namjain@linux.microsoft.com>
> Subject: [PATCH v2] uio_hv_generic: Let userspace take care of interrupt mask
>
> Remove the logic to set interrupt mask by default in uio_hv_generic driver as
> the interrupt mask value is supposed to be controlled completely by the user
> space. If the mask bit gets changed by the driver, concurrently with user mode
> operating on the ring, the mask bit may be set when it is supposed to be clear,
> and the user-mode driver will miss an interrupt which will cause a hang.
>
> For eg- when the driver sets inbound ring buffer interrupt mask to 1, the host
> does not interrupt the guest on the UIO VMBus channel.
> However, setting the mask does not prevent the host from putting a message
> in the inbound ring buffer. So let's assume that happens, the host puts a
> message into the ring buffer but does not interrupt.
>
> Subsequently, the user space code in the guest sets the inbound ring buffer
> interrupt mask to 0, saying "Hey, I'm ready for interrupts".
> User space code then calls pread() to wait for an interrupt.
> Then one of two things happens:
>
> * The host never sends another message. So the pread() waits forever.
> * The host does send another message. But because there's already a
> message in the ring buffer, it doesn't generate an interrupt.
> This is the correct behavior, because the host should only send an
> interrupt when the inbound ring buffer transitions from empty to
> not-empty. Adding an additional message to a ring buffer that is not
> empty is not supposed to generate an interrupt on the guest.
> Since the guest is waiting in pread() and not removing messages from
> the ring buffer, the pread() waits forever.
>
> This could be easily reproduced in hv_fcopy_uio_daemon if we delay setting
> interrupt mask to 0.
>
> Similarly if hv_uio_channel_cb() sets the interrupt_mask to 1, there's a race
> condition. Once user space empties the inbound ring buffer, but before user
> space sets interrupt_mask to 0, the host could put another message in the ring
> buffer but it wouldn't interrupt.
> Then the next pread() would hang.
>
> Fix these by removing all instances where interrupt_mask is changed, while
> keeping the one in set_event() unchanged to enable userspace control the
> interrupt mask by writing 0/1 to /dev/uioX.
>
> Fixes: 95096f2fbd10 ("uio-hv-generic: new userspace i/o driver for VMBus")
> Suggested-by: John Starks <jostarks@microsoft.com>
> Signed-off-by: Naman Jain <namjain@linux.microsoft.com>
> Cc: <stable@vger.kernel.org>
Reviewed-by: Long Li <longli@microsoft.com>
> ---
> Changes since v1:
> https://nam06.safelinks.protection.outlook.com/?url=https%3A%2F%2Flore.
> kernel.org%2Fall%2F20250818064846.271294-1-
> namjain%40linux.microsoft.com%2F&data=05%7C02%7Clongli%40microsoft
> .com%7Cd254da4dfccd4050923f08dde5ed4153%7C72f988bf86f141af91a
> b2d7cd011db47%7C1%7C0%7C638919529361971491%7CUnknown%7CT
> WFpbGZsb3d8eyJFbXB0eU1hcGkiOnRydWUsIlYiOiIwLjAuMDAwMCIsIlAiOiJX
> aW4zMiIsIkFOIjoiTWFpbCIsIldUIjoyfQ%3D%3D%7C0%7C%7C%7C&sdata=75
> A%2BJu5gaUZhYuBXDZEyKBRgJlsnaUenzL3wFOngMnU%3D&reserved=0
> * Added Fixes and Cc stable tags.
> ---
> drivers/uio/uio_hv_generic.c | 7 +------
> 1 file changed, 1 insertion(+), 6 deletions(-)
>
> diff --git a/drivers/uio/uio_hv_generic.c b/drivers/uio/uio_hv_generic.c index
> f19efad4d6f8..3f8e2e27697f 100644
> --- a/drivers/uio/uio_hv_generic.c
> +++ b/drivers/uio/uio_hv_generic.c
> @@ -111,7 +111,6 @@ static void hv_uio_channel_cb(void *context)
> struct hv_device *hv_dev;
> struct hv_uio_private_data *pdata;
>
> - chan->inbound.ring_buffer->interrupt_mask = 1;
> virt_mb();
>
> /*
> @@ -183,8 +182,6 @@ hv_uio_new_channel(struct vmbus_channel
> *new_sc)
> return;
> }
>
> - /* Disable interrupts on sub channel */
> - new_sc->inbound.ring_buffer->interrupt_mask = 1;
> set_channel_read_mode(new_sc, HV_CALL_ISR);
> ret = hv_create_ring_sysfs(new_sc, hv_uio_ring_mmap);
> if (ret) {
> @@ -227,9 +224,7 @@ hv_uio_open(struct uio_info *info, struct inode
> *inode)
>
> ret = vmbus_connect_ring(dev->channel,
> hv_uio_channel_cb, dev->channel);
> - if (ret == 0)
> - dev->channel->inbound.ring_buffer->interrupt_mask = 1;
> - else
> + if (ret)
> atomic_dec(&pdata->refcnt);
>
> return ret;
> --
> 2.34.1
^ permalink raw reply
* [PATCH v2][next] hyperv: Avoid a hundred -Wflex-array-member-not-at-end warnings
From: Gustavo A. R. Silva @ 2025-08-29 11:44 UTC (permalink / raw)
To: K. Y. Srinivasan, Haiyang Zhang, Wei Liu, Dexuan Cui
Cc: linux-hyperv, linux-kernel, Gustavo A. R. Silva, linux-hardening
-Wflex-array-member-not-at-end was introduced in GCC-14, and we are
getting ready to enable it, globally.
Use the new TRAILING_OVERLAP() helper to fix 159 of the following type
of warnings:
159 ./include/linux/hyperv.h:711:38: warning: structure containing a flexible array member is not at the end of another structure [-Wflex-array-member-not-at-end]
This helper creates a union between a flexible-array member (FAM)
and a set of members that would otherwise follow it. This overlays
the trailing members onto the FAM while preserving the original
memory layout.
Also, move `struct vmbus_close_msg close_msg;` at the end of
`struct vmbus_channel`, as `struct vmbus_channel_msginfo,` ends
in a flexible array member.
Signed-off-by: Gustavo A. R. Silva <gustavoars@kernel.org>
--
Changes in v2:
- Fix subject line.
v1:
- Link: https://lore.kernel.org/linux-hardening/aLGSDpi4xDjUUYVm@kspp/
include/linux/hyperv.h | 10 ++++++----
1 file changed, 6 insertions(+), 4 deletions(-)
diff --git a/include/linux/hyperv.h b/include/linux/hyperv.h
index a59c5c3e95fb..efdd570669fa 100644
--- a/include/linux/hyperv.h
+++ b/include/linux/hyperv.h
@@ -708,8 +708,9 @@ struct vmbus_channel_msginfo {
};
struct vmbus_close_msg {
- struct vmbus_channel_msginfo info;
- struct vmbus_channel_close_channel msg;
+ TRAILING_OVERLAP(struct vmbus_channel_msginfo, info, msg,
+ struct vmbus_channel_close_channel msg;
+ );
};
enum vmbus_device_type {
@@ -800,8 +801,6 @@ struct vmbus_channel {
struct hv_ring_buffer_info outbound; /* send to parent */
struct hv_ring_buffer_info inbound; /* receive from parent */
- struct vmbus_close_msg close_msg;
-
/* Statistics */
u64 interrupts; /* Host to Guest interrupts */
u64 sig_events; /* Guest to Host events */
@@ -1008,6 +1007,9 @@ struct vmbus_channel {
/* boolean to control visibility of sysfs for ring buffer */
bool ring_sysfs_visible;
+
+ /* Must be last --ends in a flexible-array member. */
+ struct vmbus_close_msg close_msg;
};
#define lock_requestor(channel, flags) \
--
2.43.0
^ permalink raw reply related
* [PATCH][next] hyperv: Avoid a hundred -Wflex-array-member-at-end warnings
From: Gustavo A. R. Silva @ 2025-08-29 11:42 UTC (permalink / raw)
To: K. Y. Srinivasan, Haiyang Zhang, Wei Liu, Dexuan Cui
Cc: linux-hyperv, linux-kernel, Gustavo A. R. Silva, linux-hardening
-Wflex-array-member-not-at-end was introduced in GCC-14, and we are
getting ready to enable it, globally.
Use the new TRAILING_OVERLAP() helper to fix 159 of the following type
of warnings:
159 ./include/linux/hyperv.h:711:38: warning: structure containing a flexible array member is not at the end of another structure [-Wflex-array-member-not-at-end]
This helper creates a union between a flexible-array member (FAM)
and a set of members that would otherwise follow it. This overlays
the trailing members onto the FAM while preserving the original
memory layout.
Also, move `struct vmbus_close_msg close_msg;` at the end of
`struct vmbus_channel`, as `struct vmbus_channel_msginfo,` ends
in a flexible array member.
Signed-off-by: Gustavo A. R. Silva <gustavoars@kernel.org>
---
include/linux/hyperv.h | 10 ++++++----
1 file changed, 6 insertions(+), 4 deletions(-)
diff --git a/include/linux/hyperv.h b/include/linux/hyperv.h
index a59c5c3e95fb..efdd570669fa 100644
--- a/include/linux/hyperv.h
+++ b/include/linux/hyperv.h
@@ -708,8 +708,9 @@ struct vmbus_channel_msginfo {
};
struct vmbus_close_msg {
- struct vmbus_channel_msginfo info;
- struct vmbus_channel_close_channel msg;
+ TRAILING_OVERLAP(struct vmbus_channel_msginfo, info, msg,
+ struct vmbus_channel_close_channel msg;
+ );
};
enum vmbus_device_type {
@@ -800,8 +801,6 @@ struct vmbus_channel {
struct hv_ring_buffer_info outbound; /* send to parent */
struct hv_ring_buffer_info inbound; /* receive from parent */
- struct vmbus_close_msg close_msg;
-
/* Statistics */
u64 interrupts; /* Host to Guest interrupts */
u64 sig_events; /* Guest to Host events */
@@ -1008,6 +1007,9 @@ struct vmbus_channel {
/* boolean to control visibility of sysfs for ring buffer */
bool ring_sysfs_visible;
+
+ /* Must be last --ends in a flexible-array member. */
+ struct vmbus_close_msg close_msg;
};
#define lock_requestor(channel, flags) \
--
2.43.0
^ permalink raw reply related
* Re: [PATCH v2] uio_hv_generic: Let userspace take care of interrupt mask
From: Naman Jain @ 2025-08-29 5:52 UTC (permalink / raw)
To: Stephen Hemminger
Cc: Greg Kroah-Hartman, K . Y . Srinivasan, Haiyang Zhang, Wei Liu,
Dexuan Cui, linux-hyperv, linux-kernel, Michael Kelley, Long Li,
stable
In-Reply-To: <20250828080207.6d9d3426@hermes.local>
On 8/28/2025 8:32 PM, Stephen Hemminger wrote:
> On Thu, 28 Aug 2025 10:12:00 +0530
> Naman Jain <namjain@linux.microsoft.com> wrote:
>
>> Remove the logic to set interrupt mask by default in uio_hv_generic
>> driver as the interrupt mask value is supposed to be controlled
>> completely by the user space. If the mask bit gets changed
>> by the driver, concurrently with user mode operating on the ring,
>> the mask bit may be set when it is supposed to be clear, and the
>> user-mode driver will miss an interrupt which will cause a hang.
>>
>> For eg- when the driver sets inbound ring buffer interrupt mask to 1,
>> the host does not interrupt the guest on the UIO VMBus channel.
>> However, setting the mask does not prevent the host from putting a
>> message in the inbound ring buffer. So let’s assume that happens,
>> the host puts a message into the ring buffer but does not interrupt.
>>
>> Subsequently, the user space code in the guest sets the inbound ring
>> buffer interrupt mask to 0, saying “Hey, I’m ready for interrupts”.
>> User space code then calls pread() to wait for an interrupt.
>> Then one of two things happens:
>>
>> * The host never sends another message. So the pread() waits forever.
>> * The host does send another message. But because there’s already a
>> message in the ring buffer, it doesn’t generate an interrupt.
>> This is the correct behavior, because the host should only send an
>> interrupt when the inbound ring buffer transitions from empty to
>> not-empty. Adding an additional message to a ring buffer that is not
>> empty is not supposed to generate an interrupt on the guest.
>> Since the guest is waiting in pread() and not removing messages from
>> the ring buffer, the pread() waits forever.
>>
>> This could be easily reproduced in hv_fcopy_uio_daemon if we delay
>> setting interrupt mask to 0.
>>
>> Similarly if hv_uio_channel_cb() sets the interrupt_mask to 1,
>> there’s a race condition. Once user space empties the inbound ring
>> buffer, but before user space sets interrupt_mask to 0, the host could
>> put another message in the ring buffer but it wouldn’t interrupt.
>> Then the next pread() would hang.
>>
>> Fix these by removing all instances where interrupt_mask is changed,
>> while keeping the one in set_event() unchanged to enable userspace
>> control the interrupt mask by writing 0/1 to /dev/uioX.
>>
>> Fixes: 95096f2fbd10 ("uio-hv-generic: new userspace i/o driver for VMBus")
>> Suggested-by: John Starks <jostarks@microsoft.com>
>> Signed-off-by: Naman Jain <namjain@linux.microsoft.com>
>> Cc: <stable@vger.kernel.org>
>
> Makes sense. I think the logic got carried over from uio.
> Does it need to make sure interrupt is masked by default to avoid
> races at startup?
No, initially I also figured that this would be required, and that's why
this was added in the first place. But my experiments with userspace
told me otherwise and I don't think this is required.
Thanks.
Regards,
Naman
^ permalink raw reply
* [PATCH 6/6] mshv: Introduce new hypercall to map stats page for L1VH partitions
From: Nuno Das Neves @ 2025-08-29 0:43 UTC (permalink / raw)
To: linux-hyperv, linux-kernel
Cc: kys, haiyangz, wei.liu, mhklinux, decui, paekkaladevi,
Jinank Jain, Nuno Das Neves
In-Reply-To: <1756428230-3599-1-git-send-email-nunodasneves@linux.microsoft.com>
From: Jinank Jain <jinankjain@linux.microsoft.com>
Introduce HVCALL_MAP_STATS_PAGE2 which provides a map location (GPFN)
to map the stats to. This hypercall is required for L1VH partitions,
depending on the hypervisor version. This uses the same check as the
state page map location; mshv_use_overlay_gpfn().
Add mshv_map_vp_state_page() helpers to use this new hypercall or the
old one depending on availability.
For unmapping, the original HVCALL_UNMAP_STATS_PAGE works for both
cases.
Signed-off-by: Jinank Jain <jinankjain@linux.microsoft.com>
Signed-off-by: Nuno Das Neves <nunodasneves@linux.microsoft.com>
---
drivers/hv/mshv_root.h | 10 ++--
drivers/hv/mshv_root_hv_call.c | 92 ++++++++++++++++++++++++++++++++--
drivers/hv/mshv_root_main.c | 25 +++++----
include/hyperv/hvgdk_mini.h | 1 +
include/hyperv/hvhdk_mini.h | 7 +++
5 files changed, 115 insertions(+), 20 deletions(-)
diff --git a/drivers/hv/mshv_root.h b/drivers/hv/mshv_root.h
index d7c9520ef788..d16a020ae0ee 100644
--- a/drivers/hv/mshv_root.h
+++ b/drivers/hv/mshv_root.h
@@ -297,11 +297,11 @@ int hv_call_connect_port(u64 port_partition_id, union hv_port_id port_id,
int hv_call_disconnect_port(u64 connection_partition_id,
union hv_connection_id connection_id);
int hv_call_notify_port_ring_empty(u32 sint_index);
-int hv_call_map_stat_page(enum hv_stats_object_type type,
- const union hv_stats_object_identity *identity,
- void **addr);
-int hv_call_unmap_stat_page(enum hv_stats_object_type type,
- const union hv_stats_object_identity *identity);
+int hv_map_stats_page(enum hv_stats_object_type type,
+ const union hv_stats_object_identity *identity,
+ void **addr);
+int hv_unmap_stats_page(enum hv_stats_object_type type, void *page_addr,
+ const union hv_stats_object_identity *identity);
int hv_call_modify_spa_host_access(u64 partition_id, struct page **pages,
u64 page_struct_count, u32 host_access,
u32 flags, u8 acquire);
diff --git a/drivers/hv/mshv_root_hv_call.c b/drivers/hv/mshv_root_hv_call.c
index 1882cc90f2f5..44013751cfc1 100644
--- a/drivers/hv/mshv_root_hv_call.c
+++ b/drivers/hv/mshv_root_hv_call.c
@@ -804,6 +804,45 @@ hv_call_notify_port_ring_empty(u32 sint_index)
return hv_result_to_errno(status);
}
+static int
+hv_call_map_stats_page2(enum hv_stats_object_type type,
+ const union hv_stats_object_identity *identity,
+ u64 map_location)
+{
+ unsigned long flags;
+ struct hv_input_map_stats_page2 *input;
+ u64 status;
+ int ret;
+
+ if (!map_location || !mshv_use_overlay_gpfn())
+ return -EINVAL;
+
+ do {
+ local_irq_save(flags);
+ input = *this_cpu_ptr(hyperv_pcpu_input_arg);
+
+ memset(input, 0, sizeof(*input));
+ input->type = type;
+ input->identity = *identity;
+ input->map_location = map_location;
+
+ status = hv_do_hypercall(HVCALL_MAP_STATS_PAGE2, input, NULL);
+
+ local_irq_restore(flags);
+ if (hv_result(status) != HV_STATUS_INSUFFICIENT_MEMORY) {
+ if (hv_result_success(status))
+ break;
+ hv_status_debug(status, "\n");
+ return hv_result_to_errno(status);
+ }
+
+ ret = hv_call_deposit_pages(NUMA_NO_NODE,
+ hv_current_partition_id, 1);
+ } while (!ret);
+
+ return ret;
+}
+
static int
hv_stats_get_area_type(enum hv_stats_object_type type,
const union hv_stats_object_identity *identity)
@@ -822,9 +861,10 @@ hv_stats_get_area_type(enum hv_stats_object_type type,
return -EINVAL;
}
-int hv_call_map_stat_page(enum hv_stats_object_type type,
- const union hv_stats_object_identity *identity,
- void **addr)
+static int
+hv_call_map_stats_page(enum hv_stats_object_type type,
+ const union hv_stats_object_identity *identity,
+ void **addr)
{
unsigned long flags;
struct hv_input_map_stats_page *input;
@@ -880,8 +920,37 @@ int hv_call_map_stat_page(enum hv_stats_object_type type,
return ret;
}
-int hv_call_unmap_stat_page(enum hv_stats_object_type type,
- const union hv_stats_object_identity *identity)
+int hv_map_stats_page(enum hv_stats_object_type type,
+ const union hv_stats_object_identity *identity,
+ void **addr)
+{
+ int ret;
+ struct page *allocated_page = NULL;
+
+ if (!addr)
+ return -EINVAL;
+
+ if (mshv_use_overlay_gpfn()) {
+ allocated_page = alloc_page(GFP_KERNEL);
+ if (!allocated_page)
+ return -ENOMEM;
+
+ ret = hv_call_map_stats_page2(type, identity,
+ page_to_pfn(allocated_page));
+ *addr = page_address(allocated_page);
+ } else {
+ ret = hv_call_map_stats_page(type, identity, addr);
+ }
+
+ if (ret && allocated_page)
+ __free_page(allocated_page);
+
+ return ret;
+}
+
+static int
+hv_call_unmap_stats_page(enum hv_stats_object_type type,
+ const union hv_stats_object_identity *identity)
{
unsigned long flags;
struct hv_input_unmap_stats_page *input;
@@ -900,6 +969,19 @@ int hv_call_unmap_stat_page(enum hv_stats_object_type type,
return hv_result_to_errno(status);
}
+int hv_unmap_stats_page(enum hv_stats_object_type type, void *page_addr,
+ const union hv_stats_object_identity *identity)
+{
+ int ret;
+
+ ret = hv_call_unmap_stats_page(type, identity);
+
+ if (mshv_use_overlay_gpfn() && page_addr)
+ __free_page(virt_to_page(page_addr));
+
+ return ret;
+}
+
int hv_call_modify_spa_host_access(u64 partition_id, struct page **pages,
u64 page_struct_count, u32 host_access,
u32 flags, u8 acquire)
diff --git a/drivers/hv/mshv_root_main.c b/drivers/hv/mshv_root_main.c
index f91880cc9e29..1699423cc524 100644
--- a/drivers/hv/mshv_root_main.c
+++ b/drivers/hv/mshv_root_main.c
@@ -894,7 +894,8 @@ mshv_vp_release(struct inode *inode, struct file *filp)
return 0;
}
-static void mshv_vp_stats_unmap(u64 partition_id, u32 vp_index)
+static void mshv_vp_stats_unmap(u64 partition_id, u32 vp_index,
+ void *stats_pages[])
{
union hv_stats_object_identity identity = {
.vp.partition_id = partition_id,
@@ -902,10 +903,13 @@ static void mshv_vp_stats_unmap(u64 partition_id, u32 vp_index)
};
identity.vp.stats_area_type = HV_STATS_AREA_SELF;
- hv_call_unmap_stat_page(HV_STATS_OBJECT_VP, &identity);
+ hv_unmap_stats_page(HV_STATS_OBJECT_VP, NULL, &identity);
+
+ if (stats_pages[HV_STATS_AREA_PARENT] == stats_pages[HV_STATS_AREA_SELF])
+ return;
identity.vp.stats_area_type = HV_STATS_AREA_PARENT;
- hv_call_unmap_stat_page(HV_STATS_OBJECT_VP, &identity);
+ hv_unmap_stats_page(HV_STATS_OBJECT_VP, NULL, &identity);
}
static int mshv_vp_stats_map(u64 partition_id, u32 vp_index,
@@ -918,14 +922,14 @@ static int mshv_vp_stats_map(u64 partition_id, u32 vp_index,
int err;
identity.vp.stats_area_type = HV_STATS_AREA_SELF;
- err = hv_call_map_stat_page(HV_STATS_OBJECT_VP, &identity,
- &stats_pages[HV_STATS_AREA_SELF]);
+ err = hv_map_stats_page(HV_STATS_OBJECT_VP, &identity,
+ &stats_pages[HV_STATS_AREA_SELF]);
if (err)
return err;
identity.vp.stats_area_type = HV_STATS_AREA_PARENT;
- err = hv_call_map_stat_page(HV_STATS_OBJECT_VP, &identity,
- &stats_pages[HV_STATS_AREA_PARENT]);
+ err = hv_map_stats_page(HV_STATS_OBJECT_VP, &identity,
+ &stats_pages[HV_STATS_AREA_PARENT]);
if (err)
goto unmap_self;
@@ -936,7 +940,7 @@ static int mshv_vp_stats_map(u64 partition_id, u32 vp_index,
unmap_self:
identity.vp.stats_area_type = HV_STATS_AREA_SELF;
- hv_call_unmap_stat_page(HV_STATS_OBJECT_VP, &identity);
+ hv_unmap_stats_page(HV_STATS_OBJECT_VP, NULL, &identity);
return err;
}
@@ -1044,7 +1048,7 @@ mshv_partition_ioctl_create_vp(struct mshv_partition *partition,
kfree(vp);
unmap_stats_pages:
if (hv_scheduler_type == HV_SCHEDULER_TYPE_ROOT)
- mshv_vp_stats_unmap(partition->pt_id, args.vp_index);
+ mshv_vp_stats_unmap(partition->pt_id, args.vp_index, stats_pages);
unmap_ghcb_page:
if (mshv_partition_encrypted(partition) && is_ghcb_mapping_available())
hv_unmap_vp_state_page(partition->pt_id, args.vp_index,
@@ -1796,7 +1800,8 @@ static void destroy_partition(struct mshv_partition *partition)
continue;
if (hv_scheduler_type == HV_SCHEDULER_TYPE_ROOT)
- mshv_vp_stats_unmap(partition->pt_id, vp->vp_index);
+ mshv_vp_stats_unmap(partition->pt_id, vp->vp_index,
+ (void **)vp->vp_stats_pages);
if (vp->vp_register_page) {
(void)hv_unmap_vp_state_page(partition->pt_id,
diff --git a/include/hyperv/hvgdk_mini.h b/include/hyperv/hvgdk_mini.h
index 1bde0aa102ec..27652785e3f2 100644
--- a/include/hyperv/hvgdk_mini.h
+++ b/include/hyperv/hvgdk_mini.h
@@ -493,6 +493,7 @@ union hv_vp_assist_msr_contents { /* HV_REGISTER_VP_ASSIST_PAGE */
#define HVCALL_GET_PARTITION_PROPERTY_EX 0x0101
#define HVCALL_MMIO_READ 0x0106
#define HVCALL_MMIO_WRITE 0x0107
+#define HVCALL_MAP_STATS_PAGE2 0x0131
/* HV_HYPERCALL_INPUT */
#define HV_HYPERCALL_RESULT_MASK GENMASK_ULL(15, 0)
diff --git a/include/hyperv/hvhdk_mini.h b/include/hyperv/hvhdk_mini.h
index bf2ce27dfcc5..064bf735cab6 100644
--- a/include/hyperv/hvhdk_mini.h
+++ b/include/hyperv/hvhdk_mini.h
@@ -177,6 +177,13 @@ struct hv_input_map_stats_page {
union hv_stats_object_identity identity;
} __packed;
+struct hv_input_map_stats_page2 {
+ u32 type; /* enum hv_stats_object_type */
+ u32 padding;
+ union hv_stats_object_identity identity;
+ u64 map_location;
+} __packed;
+
struct hv_output_map_stats_page {
u64 map_location;
} __packed;
--
2.34.1
^ permalink raw reply related
* [PATCH 5/6] mshv: Allocate vp state page for HVCALL_MAP_VP_STATE_PAGE on L1VH
From: Nuno Das Neves @ 2025-08-29 0:43 UTC (permalink / raw)
To: linux-hyperv, linux-kernel
Cc: kys, haiyangz, wei.liu, mhklinux, decui, paekkaladevi,
Jinank Jain, Nuno Das Neves
In-Reply-To: <1756428230-3599-1-git-send-email-nunodasneves@linux.microsoft.com>
From: Jinank Jain <jinankjain@linux.microsoft.com>
Introduce mshv_use_overlay_gpfn() to check if a page needs to be
allocated and passed to the hypervisor to map VP state pages. This is
only needed on L1VH, and only on some (newer) versions of the
hypervisor, hence the need to check vmm_capabilities.
Introduce functions hv_map/unmap_vp_state_page() to handle the
allocation and freeing.
Signed-off-by: Jinank Jain <jinankjain@linux.microsoft.com>
Signed-off-by: Nuno Das Neves <nunodasneves@linux.microsoft.com>
---
drivers/hv/mshv_root.h | 11 +++---
drivers/hv/mshv_root_hv_call.c | 61 +++++++++++++++++++++++++---
drivers/hv/mshv_root_main.c | 72 +++++++++++++++++-----------------
3 files changed, 96 insertions(+), 48 deletions(-)
diff --git a/drivers/hv/mshv_root.h b/drivers/hv/mshv_root.h
index 0cb1e2589fe1..d7c9520ef788 100644
--- a/drivers/hv/mshv_root.h
+++ b/drivers/hv/mshv_root.h
@@ -279,11 +279,12 @@ int hv_call_set_vp_state(u32 vp_index, u64 partition_id,
/* Choose between pages and bytes */
struct hv_vp_state_data state_data, u64 page_count,
struct page **pages, u32 num_bytes, u8 *bytes);
-int hv_call_map_vp_state_page(u64 partition_id, u32 vp_index, u32 type,
- union hv_input_vtl input_vtl,
- struct page **state_page);
-int hv_call_unmap_vp_state_page(u64 partition_id, u32 vp_index, u32 type,
- union hv_input_vtl input_vtl);
+int hv_map_vp_state_page(u64 partition_id, u32 vp_index, u32 type,
+ union hv_input_vtl input_vtl,
+ struct page **state_page);
+int hv_unmap_vp_state_page(u64 partition_id, u32 vp_index, u32 type,
+ void *page_addr,
+ union hv_input_vtl input_vtl);
int hv_call_create_port(u64 port_partition_id, union hv_port_id port_id,
u64 connection_partition_id, struct hv_port_info *port_info,
u8 port_vtl, u8 min_connection_vtl, int node);
diff --git a/drivers/hv/mshv_root_hv_call.c b/drivers/hv/mshv_root_hv_call.c
index 7589b1ff3515..1882cc90f2f5 100644
--- a/drivers/hv/mshv_root_hv_call.c
+++ b/drivers/hv/mshv_root_hv_call.c
@@ -526,9 +526,9 @@ int hv_call_set_vp_state(u32 vp_index, u64 partition_id,
return ret;
}
-int hv_call_map_vp_state_page(u64 partition_id, u32 vp_index, u32 type,
- union hv_input_vtl input_vtl,
- struct page **state_page)
+static int hv_call_map_vp_state_page(u64 partition_id, u32 vp_index, u32 type,
+ union hv_input_vtl input_vtl,
+ struct page **state_page)
{
struct hv_input_map_vp_state_page *input;
struct hv_output_map_vp_state_page *output;
@@ -547,7 +547,14 @@ int hv_call_map_vp_state_page(u64 partition_id, u32 vp_index, u32 type,
input->type = type;
input->input_vtl = input_vtl;
- status = hv_do_hypercall(HVCALL_MAP_VP_STATE_PAGE, input, output);
+ if (*state_page) {
+ input->flags.map_location_provided = 1;
+ input->requested_map_location =
+ page_to_pfn(*state_page);
+ }
+
+ status = hv_do_hypercall(HVCALL_MAP_VP_STATE_PAGE, input,
+ output);
if (hv_result(status) != HV_STATUS_INSUFFICIENT_MEMORY) {
if (hv_result_success(status))
@@ -565,8 +572,39 @@ int hv_call_map_vp_state_page(u64 partition_id, u32 vp_index, u32 type,
return ret;
}
-int hv_call_unmap_vp_state_page(u64 partition_id, u32 vp_index, u32 type,
- union hv_input_vtl input_vtl)
+static bool mshv_use_overlay_gpfn(void)
+{
+ return hv_l1vh_partition() &&
+ mshv_root.vmm_caps.vmm_can_provide_overlay_gpfn;
+}
+
+int hv_map_vp_state_page(u64 partition_id, u32 vp_index, u32 type,
+ union hv_input_vtl input_vtl,
+ struct page **state_page)
+{
+ int ret = 0;
+ struct page *allocated_page = NULL;
+
+ if (mshv_use_overlay_gpfn()) {
+ allocated_page = alloc_page(GFP_KERNEL);
+ if (!allocated_page)
+ return -ENOMEM;
+ *state_page = allocated_page;
+ } else {
+ *state_page = NULL;
+ }
+
+ ret = hv_call_map_vp_state_page(partition_id, vp_index, type, input_vtl,
+ state_page);
+
+ if (ret && allocated_page)
+ __free_page(allocated_page);
+
+ return ret;
+}
+
+static int hv_call_unmap_vp_state_page(u64 partition_id, u32 vp_index, u32 type,
+ union hv_input_vtl input_vtl)
{
unsigned long flags;
u64 status;
@@ -590,6 +628,17 @@ int hv_call_unmap_vp_state_page(u64 partition_id, u32 vp_index, u32 type,
return hv_result_to_errno(status);
}
+int hv_unmap_vp_state_page(u64 partition_id, u32 vp_index, u32 type,
+ void *page_addr, union hv_input_vtl input_vtl)
+{
+ int ret = hv_call_unmap_vp_state_page(partition_id, vp_index, type, input_vtl);
+
+ if (mshv_use_overlay_gpfn() && page_addr)
+ __free_page(virt_to_page(page_addr));
+
+ return ret;
+}
+
int
hv_call_get_partition_property_ex(u64 partition_id, u64 property_code, u64 arg,
void *property_value, size_t property_value_sz)
diff --git a/drivers/hv/mshv_root_main.c b/drivers/hv/mshv_root_main.c
index 29f61ecc9771..f91880cc9e29 100644
--- a/drivers/hv/mshv_root_main.c
+++ b/drivers/hv/mshv_root_main.c
@@ -964,28 +964,25 @@ mshv_partition_ioctl_create_vp(struct mshv_partition *partition,
if (ret)
return ret;
- ret = hv_call_map_vp_state_page(partition->pt_id, args.vp_index,
- HV_VP_STATE_PAGE_INTERCEPT_MESSAGE,
- input_vtl_zero,
- &intercept_message_page);
+ ret = hv_map_vp_state_page(partition->pt_id, args.vp_index,
+ HV_VP_STATE_PAGE_INTERCEPT_MESSAGE,
+ input_vtl_zero, &intercept_message_page);
if (ret)
goto destroy_vp;
if (!mshv_partition_encrypted(partition)) {
- ret = hv_call_map_vp_state_page(partition->pt_id, args.vp_index,
- HV_VP_STATE_PAGE_REGISTERS,
- input_vtl_zero,
- ®ister_page);
+ ret = hv_map_vp_state_page(partition->pt_id, args.vp_index,
+ HV_VP_STATE_PAGE_REGISTERS,
+ input_vtl_zero, ®ister_page);
if (ret)
goto unmap_intercept_message_page;
}
if (mshv_partition_encrypted(partition) &&
is_ghcb_mapping_available()) {
- ret = hv_call_map_vp_state_page(partition->pt_id, args.vp_index,
- HV_VP_STATE_PAGE_GHCB,
- input_vtl_normal,
- &ghcb_page);
+ ret = hv_map_vp_state_page(partition->pt_id, args.vp_index,
+ HV_VP_STATE_PAGE_GHCB,
+ input_vtl_normal, &ghcb_page);
if (ret)
goto unmap_register_page;
}
@@ -1049,21 +1046,19 @@ mshv_partition_ioctl_create_vp(struct mshv_partition *partition,
if (hv_scheduler_type == HV_SCHEDULER_TYPE_ROOT)
mshv_vp_stats_unmap(partition->pt_id, args.vp_index);
unmap_ghcb_page:
- if (mshv_partition_encrypted(partition) && is_ghcb_mapping_available()) {
- hv_call_unmap_vp_state_page(partition->pt_id, args.vp_index,
- HV_VP_STATE_PAGE_GHCB,
- input_vtl_normal);
- }
+ if (mshv_partition_encrypted(partition) && is_ghcb_mapping_available())
+ hv_unmap_vp_state_page(partition->pt_id, args.vp_index,
+ HV_VP_STATE_PAGE_GHCB, vp->vp_ghcb_page,
+ input_vtl_normal);
unmap_register_page:
- if (!mshv_partition_encrypted(partition)) {
- hv_call_unmap_vp_state_page(partition->pt_id, args.vp_index,
- HV_VP_STATE_PAGE_REGISTERS,
- input_vtl_zero);
- }
+ if (!mshv_partition_encrypted(partition))
+ hv_unmap_vp_state_page(partition->pt_id, args.vp_index,
+ HV_VP_STATE_PAGE_REGISTERS,
+ vp->vp_register_page, input_vtl_zero);
unmap_intercept_message_page:
- hv_call_unmap_vp_state_page(partition->pt_id, args.vp_index,
- HV_VP_STATE_PAGE_INTERCEPT_MESSAGE,
- input_vtl_zero);
+ hv_unmap_vp_state_page(partition->pt_id, args.vp_index,
+ HV_VP_STATE_PAGE_INTERCEPT_MESSAGE,
+ vp->vp_intercept_msg_page, input_vtl_zero);
destroy_vp:
hv_call_delete_vp(partition->pt_id, args.vp_index);
return ret;
@@ -1804,24 +1799,27 @@ static void destroy_partition(struct mshv_partition *partition)
mshv_vp_stats_unmap(partition->pt_id, vp->vp_index);
if (vp->vp_register_page) {
- (void)hv_call_unmap_vp_state_page(partition->pt_id,
- vp->vp_index,
- HV_VP_STATE_PAGE_REGISTERS,
- input_vtl_zero);
+ (void)hv_unmap_vp_state_page(partition->pt_id,
+ vp->vp_index,
+ HV_VP_STATE_PAGE_REGISTERS,
+ vp->vp_register_page,
+ input_vtl_zero);
vp->vp_register_page = NULL;
}
- (void)hv_call_unmap_vp_state_page(partition->pt_id,
- vp->vp_index,
- HV_VP_STATE_PAGE_INTERCEPT_MESSAGE,
- input_vtl_zero);
+ (void)hv_unmap_vp_state_page(partition->pt_id,
+ vp->vp_index,
+ HV_VP_STATE_PAGE_INTERCEPT_MESSAGE,
+ vp->vp_intercept_msg_page,
+ input_vtl_zero);
vp->vp_intercept_msg_page = NULL;
if (vp->vp_ghcb_page) {
- (void)hv_call_unmap_vp_state_page(partition->pt_id,
- vp->vp_index,
- HV_VP_STATE_PAGE_GHCB,
- input_vtl_normal);
+ (void)hv_unmap_vp_state_page(partition->pt_id,
+ vp->vp_index,
+ HV_VP_STATE_PAGE_GHCB,
+ vp->vp_ghcb_page,
+ input_vtl_normal);
vp->vp_ghcb_page = NULL;
}
--
2.34.1
^ permalink raw reply related
page: next (older) | prev (newer) | latest
- recent:[subjects (threaded)|topics (new)|topics (active)]
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox