Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions arch/x86/include/asm/mshyperv.h
Original file line number Diff line number Diff line change
Expand Up @@ -241,6 +241,8 @@ void hyperv_setup_mmu_ops(void);
void set_hv_tscchange_cb(void (*cb)(void));
void clear_hv_tscchange_cb(void);
void hyperv_stop_tsc_emulation(void);
void hv_save_sched_clock_state(void);
void hv_restore_sched_clock_state(void);
int hyperv_flush_guest_mapping(u64 as);
int hyperv_flush_guest_mapping_range(u64 as,
hyperv_fill_flush_list_func fill_func, void *data);
Expand Down
4 changes: 2 additions & 2 deletions arch/x86/kernel/cpu/mshyperv.c
Original file line number Diff line number Diff line change
Expand Up @@ -266,13 +266,13 @@ static void restore_hv_clock_tsc_state(void)
* suspend-resume and the offset used to measure time needs to be
* corrected, post resume.
*/
static void hv_save_sched_clock_state(void)
void hv_save_sched_clock_state(void)
{
old_save_sched_clock_state();
save_hv_clock_tsc_state();
}

static void hv_restore_sched_clock_state(void)
void hv_restore_sched_clock_state(void)
{
restore_hv_clock_tsc_state();
old_restore_sched_clock_state();
Expand Down
60 changes: 60 additions & 0 deletions drivers/hv/mshv_vtl_main.c
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@

#ifdef CONFIG_X86_64
#include <linux/cleanup.h>
#include <linux/stop_machine.h>

#include <asm/apic.h>
#include <uapi/asm/mtrr.h>
Expand All @@ -42,6 +43,7 @@
#include <asm/vmx.h>

#include "../../kernel/fpu/legacy.h"
#include "../../kernel/time/timekeeping.h"
Copy link

Copilot AI Jan 30, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Inclusion of internal kernel header: The file includes "../../kernel/time/timekeeping.h" using a relative path. This is an internal kernel header that is not part of the public kernel API. Driver code should not include internal headers using relative paths, as this breaks modularity and may cause build issues. The functions from this header (timekeeping_suspend, timekeeping_resume, sched_clock_suspend, sched_clock_resume) are also not exported symbols, which means they cannot be used from driver code that could potentially be built as a module.

Suggested change
#include "../../kernel/time/timekeeping.h"
#include <linux/timekeeping.h>

Copilot uses AI. Check for mistakes.

#endif

Expand Down Expand Up @@ -865,6 +867,57 @@ static int mshv_vtl_ioctl_add_vtl0_mem(struct mshv_vtl *vtl, void __user *arg)
return 0;
}

#ifdef CONFIG_X86_64
static int restore_partition_time_with_cpus_stopped(void *data)
{
struct mshv_partition_time *partition_time = data;
struct hv_input_restore_partition_time *input;
int result = 0;
u64 status;

/* Save current clock state. No other CPUs are running so no locks are taken. */
sched_clock_suspend();
timekeeping_suspend();
hv_save_sched_clock_state();

/* Interrupts are disabled, make the hypercall to update the TSC. */
input = *this_cpu_ptr(hyperv_pcpu_input_arg);
input->partition_id = HV_PARTITION_ID_SELF;
input->tsc_sequence = partition_time->tsc_sequence;
input->reserved = 0;
input->reference_time_in_100_ns = partition_time->reference_time_in_100_ns;
input->tsc = partition_time->tsc;
status = hv_do_hypercall(HVCALL_RESTORE_PARTITION_TIME, input, NULL);
if (!hv_result_success(status)) {
pr_err("HVCALL_RESTORE_PARTITION_TIME failed with %#llx\n", status);
result = -EINVAL;
}

/* Restore clock state using current TSC value. */
hv_restore_sched_clock_state();
timekeeping_resume();
sched_clock_resume();

return result;
}

static int mshv_restore_partition_time(void __user *arg)
{
unsigned long irq_flags;
struct mshv_partition_time partition_time;
int ret;

if (copy_from_user(&partition_time, arg, sizeof(partition_time)))
return -EFAULT;

/* Stop other CPUs, using the current one to restore partition time. */
local_irq_save(irq_flags);
ret = stop_machine(restore_partition_time_with_cpus_stopped, &partition_time, cpumask_of(smp_processor_id()));
Copy link

Copilot AI Jan 30, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Unclear stop_machine usage: The code calls stop_machine(restore_partition_time_with_cpus_stopped, &partition_time, cpumask_of(smp_processor_id())), which stops all CPUs but only runs the callback on the current CPU. While this ensures no other CPUs are running during the TSC update (as indicated by the comment on line 878), it's worth verifying that making the HVCALL_RESTORE_PARTITION_TIME hypercall from only one CPU is sufficient to update the TSC for the entire system. If the TSC update is system-wide and a single hypercall is sufficient, the current implementation is correct. However, if each CPU needs to update its TSC state, the callback should run on all CPUs by passing NULL or cpu_online_mask instead.

Copilot uses AI. Check for mistakes.
local_irq_restore(irq_flags);
Comment on lines +914 to +916
Copy link

Copilot AI Jan 30, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Unnecessary interrupt handling: The local_irq_save and local_irq_restore calls around stop_machine are redundant. The stop_machine function already handles interrupt disabling and restoration as part of its implementation. Calling local_irq_save before stop_machine is unnecessary and could potentially interfere with stop_machine's own interrupt handling. Consider removing these calls.

Copilot uses AI. Check for mistakes.
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

smp_processor_id() requires pre-emption to be disabled

return ret;
}
#endif

static void mshv_vtl_cancel(int cpu)
{
int here = get_cpu();
Expand Down Expand Up @@ -2596,6 +2649,13 @@ mshv_vtl_ioctl(struct file *filp, unsigned int ioctl, unsigned long arg)
case MSHV_VTL_ADD_VTL0_MEMORY:
ret = mshv_vtl_ioctl_add_vtl0_mem(vtl, (void __user *)arg);
break;

#if defined(CONFIG_X86_64)
case MSHV_RESTORE_PARTITION_TIME:
ret = mshv_restore_partition_time((void __user *)arg);
break;
#endif

#if defined(CONFIG_X86_64) && defined(CONFIG_INTEL_TDX_GUEST)
case MSHV_VTL_TDCALL:
ret = mshv_vtl_ioctl_tdcall((void __user *)arg);
Expand Down
9 changes: 9 additions & 0 deletions include/asm-generic/hyperv-tlfs.h
Original file line number Diff line number Diff line change
Expand Up @@ -197,6 +197,7 @@ union hv_reference_tsc_msr {
#define HVCALL_FLUSH_GUEST_PHYSICAL_ADDRESS_SPACE 0x00af
#define HVCALL_FLUSH_GUEST_PHYSICAL_ADDRESS_LIST 0x00b0
#define HVCALL_MODIFY_SPARSE_GPA_PAGE_HOST_VISIBILITY 0x00db
#define HVCALL_RESTORE_PARTITION_TIME 0x0103
#define HVCALL_MMIO_READ 0x0106
#define HVCALL_MMIO_WRITE 0x0107

Expand Down Expand Up @@ -1002,4 +1003,12 @@ struct hv_enable_vp_vtl {
u16 mbz1;
struct hv_init_vp_context vp_context;
} __packed;

struct hv_input_restore_partition_time {
u64 partition_id;
u32 tsc_sequence;
u32 reserved;
u64 reference_time_in_100_ns;
u64 tsc;
} __packed;
Comment on lines +1007 to +1013
Copy link

Copilot AI Jan 30, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Duplicate struct definition: struct hv_input_restore_partition_time is defined in both include/uapi/hyperv/hvgdk_mini.h (lines 1316-1322 with __u64/__u32 types) and include/asm-generic/hyperv-tlfs.h (lines 1007-1013 with u64/u32 types). This duplication could lead to compilation issues or confusion. Since hvgdk_mini.h is a UAPI header and uses UAPI types (__u64, __u32), while hyperv-tlfs.h is a kernel header using kernel types (u64, u32), only one definition should exist. Consider keeping the definition in the appropriate header based on where it's primarily used.

Suggested change
struct hv_input_restore_partition_time {
u64 partition_id;
u32 tsc_sequence;
u32 reserved;
u64 reference_time_in_100_ns;
u64 tsc;
} __packed;

Copilot uses AI. Check for mistakes.
#endif
12 changes: 12 additions & 0 deletions include/uapi/hyperv/hvgdk_mini.h
Original file line number Diff line number Diff line change
Expand Up @@ -261,6 +261,8 @@ union hv_hypervisor_version_info {
#define HVCALL_GET_VP_CPUID_VALUES 0x00f4
#define HVCALL_START_VP 0x0099
#define HVCALL_GET_VP_INDEX_FROM_APIC_ID 0x009a
#define HVCALL_RESTORE_PARTITION_TIME 0x0103


/*
* Some macros - i.e. GENMASK_ULL and BIT_ULL - are not currently supported by
Expand Down Expand Up @@ -1310,4 +1312,14 @@ struct hv_input_install_intercept {
union hv_intercept_parameters intercept_parameter;
} __packed;

#if defined(__x86_64__)
struct hv_input_restore_partition_time {
__u64 partition_id;
__u32 tsc_sequence;
__u32 reserved;
__u64 reference_time_in_100_ns;
__u64 tsc;
} __packed;
#endif

#endif /* _UAPI_HV_HVGDK_MINI_H */
8 changes: 8 additions & 0 deletions include/uapi/linux/mshv.h
Original file line number Diff line number Diff line change
Expand Up @@ -306,6 +306,13 @@ struct mshv_map_device_intr {
__u8 padding[7];
} __packed;

struct mshv_partition_time {
__u32 tsc_sequence;
__u32 reserved;
__u64 reference_time_in_100_ns;
__u64 tsc;
} __packed;

#define MSHV_KICK_CPUS_FLAG_WAIT_FOR_CPUS (1 << 0)
#define MSHV_KICK_CPUS_FLAG_CANCEL_CPU_RUN (1 << 1)

Expand Down Expand Up @@ -371,6 +378,7 @@ struct mshv_map_device_intr {
struct mshv_map_device_intr)

/* For x86-64 only */
#define MSHV_RESTORE_PARTITION_TIME _IOW(MSHV_IOCTL, 0x13, struct mshv_partition_time)
#define MSHV_VTL_GUEST_VSM_VMSA_PFN _IOWR(MSHV_IOCTL, 0x34, __u64)

/* For x86-64 SEV-SNP only */
Expand Down