[PATCH] xen: Expose time_offset in struct arch_shared_info

Tu Dinh posted 1 patch 6 days, 17 hours ago
Patches applied successfully (tree, apply log)
git fetch https://gitlab.com/xen-project/patchew/xen tags/patchew/20251112070815.545-1-ngoc-tu.dinh@vates.tech
tools/include/xen-foreign/reference.size | 5 ++---
xen/common/kernel.c                      | 3 ++-
xen/common/time.c                        | 5 +++++
xen/include/public/arch-arm.h            | 2 ++
xen/include/public/arch-ppc.h            | 3 +++
xen/include/public/arch-riscv.h          | 3 ++-
xen/include/public/arch-x86/xen.h        | 3 +++
xen/include/public/features.h            | 5 +++++
8 files changed, 24 insertions(+), 5 deletions(-)
[PATCH] xen: Expose time_offset in struct arch_shared_info
Posted by Tu Dinh 6 days, 17 hours ago
time_offset is currently always added to wc_sec. This means that without
the actual value of time_offset, guests have no way of knowing what's
the actual host clock. Once the guest clock drifts beyond 1 second,
updates to the guest RTC would themselves change time_offset and make it
impossible to resync guest time to host time.

Since there's no way to add more fields to struct shared_info, the
addition has to be done through struct arch_shared_info instead. Add two
fields in arch_shared_info representing time_offset's low and high
32-bit halves.

Provide a new feature bit XENFEAT_shared_info_time_offset for this
functionality.

Signed-off-by: Tu Dinh <ngoc-tu.dinh@vates.tech>
---
 tools/include/xen-foreign/reference.size | 5 ++---
 xen/common/kernel.c                      | 3 ++-
 xen/common/time.c                        | 5 +++++
 xen/include/public/arch-arm.h            | 2 ++
 xen/include/public/arch-ppc.h            | 3 +++
 xen/include/public/arch-riscv.h          | 3 ++-
 xen/include/public/arch-x86/xen.h        | 3 +++
 xen/include/public/features.h            | 5 +++++
 8 files changed, 24 insertions(+), 5 deletions(-)

diff --git a/tools/include/xen-foreign/reference.size b/tools/include/xen-foreign/reference.size
index 11a06a7a43..38e799617a 100644
--- a/tools/include/xen-foreign/reference.size
+++ b/tools/include/xen-foreign/reference.size
@@ -9,6 +9,5 @@ vcpu_guest_context        |     352     352    2800    5168
 arch_vcpu_info            |       0       0      24      16
 vcpu_time_info            |      32      32      32      32
 vcpu_info                 |      48      48      64      64
-arch_shared_info          |       0       0      28      48
-shared_info               |    1088    1088    2344    3136
-
+arch_shared_info          |       8       8      36      56
+shared_info               |    1096    1096    2352    3144
diff --git a/xen/common/kernel.c b/xen/common/kernel.c
index e6979352e1..dbd2cf9c76 100644
--- a/xen/common/kernel.c
+++ b/xen/common/kernel.c
@@ -676,7 +676,8 @@ long do_xen_version(int cmd, XEN_GUEST_HANDLE_PARAM(void) arg)
 #ifdef CONFIG_X86
                         (1U << XENFEAT_vcpu_time_phys_area) |
 #endif
-                        (1U << XENFEAT_runstate_phys_area);
+                        (1U << XENFEAT_runstate_phys_area) |
+                        (1U << XENFEAT_shared_info_time_offset);
             if ( VM_ASSIST(d, pae_extended_cr3) )
                 fi.submap |= (1U << XENFEAT_pae_pgdir_above_4gb);
             if ( paging_mode_translate(d) )
diff --git a/xen/common/time.c b/xen/common/time.c
index c873b5731b..0f38b1342d 100644
--- a/xen/common/time.c
+++ b/xen/common/time.c
@@ -118,6 +118,11 @@ void update_domain_wallclock_time(struct domain *d)
     shared_info(d, wc_sec_hi) = sec >> 32;
 #endif
 
+    shared_info(d, arch.time_offset) =
+        (uint32_t)(uint64_t)d->time_offset.seconds;
+    shared_info(d, arch.time_offset_hi) =
+        (uint32_t)((uint64_t)d->time_offset.seconds >> 32);
+
     smp_wmb();
     *wc_version = version_update_end(*wc_version);
 
diff --git a/xen/include/public/arch-arm.h b/xen/include/public/arch-arm.h
index b31324f8d4..2f34fb409b 100644
--- a/xen/include/public/arch-arm.h
+++ b/xen/include/public/arch-arm.h
@@ -363,6 +363,8 @@ struct arch_vcpu_info {
 typedef struct arch_vcpu_info arch_vcpu_info_t;
 
 struct arch_shared_info {
+    uint32_t time_offset;
+    uint32_t time_offset_hi;
 };
 typedef struct arch_shared_info arch_shared_info_t;
 typedef uint64_t xen_callback_t;
diff --git a/xen/include/public/arch-ppc.h b/xen/include/public/arch-ppc.h
index 4ca453a284..5bb47e09d3 100644
--- a/xen/include/public/arch-ppc.h
+++ b/xen/include/public/arch-ppc.h
@@ -92,6 +92,9 @@ DEFINE_XEN_GUEST_HANDLE(vcpu_guest_context_t);
 
 struct arch_shared_info {
     uint64_t boot_timebase;
+
+    uint32_t time_offset;
+    uint32_t time_offset_hi;
 };
 
 struct arch_vcpu_info {
diff --git a/xen/include/public/arch-riscv.h b/xen/include/public/arch-riscv.h
index 168263b920..e502aade99 100644
--- a/xen/include/public/arch-riscv.h
+++ b/xen/include/public/arch-riscv.h
@@ -65,8 +65,9 @@ struct arch_vcpu_info {
 };
 typedef struct arch_vcpu_info arch_vcpu_info_t;
 
-/* TODO:  add a placeholder entry if no real ones surface */
 struct arch_shared_info {
+    uint32_t time_offset;
+    uint32_t time_offset_hi;
 };
 typedef struct arch_shared_info arch_shared_info_t;
 
diff --git a/xen/include/public/arch-x86/xen.h b/xen/include/public/arch-x86/xen.h
index b99a691706..598af31e4e 100644
--- a/xen/include/public/arch-x86/xen.h
+++ b/xen/include/public/arch-x86/xen.h
@@ -263,6 +263,9 @@ struct arch_shared_info {
     /* There's no room for this field in the generic structure. */
     uint32_t wc_sec_hi;
 #endif
+
+    uint32_t time_offset;
+    uint32_t time_offset_hi;
 };
 typedef struct arch_shared_info arch_shared_info_t;
 
diff --git a/xen/include/public/features.h b/xen/include/public/features.h
index 8801930947..b48591c17a 100644
--- a/xen/include/public/features.h
+++ b/xen/include/public/features.h
@@ -128,6 +128,11 @@
  */
 #define XENFEAT_dm_msix_all_writes        20
 
+/*
+ * arch_shared_info provides time_offset and time_offset_hi
+ */
+#define XENFEAT_shared_info_time_offset   21
+
 #define XENFEAT_NR_SUBMAPS 1
 
 #endif /* __XEN_PUBLIC_FEATURES_H__ */
-- 
2.43.0



--
Ngoc Tu Dinh | Vates XCP-ng Developer

XCP-ng & Xen Orchestra - Vates solutions

web: https://vates.tech
Re: [PATCH] xen: Expose time_offset in struct arch_shared_info
Posted by Jan Beulich 5 days, 12 hours ago
On 12.11.2025 08:08, Tu Dinh wrote:
> time_offset is currently always added to wc_sec. This means that without
> the actual value of time_offset, guests have no way of knowing what's
> the actual host clock. Once the guest clock drifts beyond 1 second,
> updates to the guest RTC would themselves change time_offset and make it
> impossible to resync guest time to host time.

I guess I don't understand what the problem is, or why it would need a
change in the hypervisor (interface). If the guest updates the vRTC, it is
(implicitly) asking for a change of time offset, isn't it? And whatever
adjustments it makes, it could track and accumulate them?

> Since there's no way to add more fields to struct shared_info, the
> addition has to be done through struct arch_shared_info instead. Add two
> fields in arch_shared_info representing time_offset's low and high
> 32-bit halves.

Any why in two halves? Without that, ...

> --- a/xen/common/time.c
> +++ b/xen/common/time.c
> @@ -118,6 +118,11 @@ void update_domain_wallclock_time(struct domain *d)
>      shared_info(d, wc_sec_hi) = sec >> 32;
>  #endif
>  
> +    shared_info(d, arch.time_offset) =
> +        (uint32_t)(uint64_t)d->time_offset.seconds;
> +    shared_info(d, arch.time_offset_hi) =
> +        (uint32_t)((uint64_t)d->time_offset.seconds >> 32);

... no casting at all would be needed here. (Even when there are two halves,
some of the casting can be dropped.)

Another question is - why unsigned? struct domain's time_offset.seconds is
signed for a reason, aiui.

Jan
Re: [PATCH] xen: Expose time_offset in struct arch_shared_info
Posted by Tu Dinh 5 days, 12 hours ago
On 13/11/2025 13:09, Jan Beulich wrote:
> On 12.11.2025 08:08, Tu Dinh wrote:
>> time_offset is currently always added to wc_sec. This means that without
>> the actual value of time_offset, guests have no way of knowing what's
>> the actual host clock. Once the guest clock drifts beyond 1 second,
>> updates to the guest RTC would themselves change time_offset and make it
>> impossible to resync guest time to host time.
> 
> I guess I don't understand what the problem is, or why it would need a
> change in the hypervisor (interface). If the guest updates the vRTC, it is
> (implicitly) asking for a change of time offset, isn't it? And whatever
> adjustments it makes, it could track and accumulate them?

vRTC drift can happen for other reasons. For example, Windows can write 
to the RTC at any time; if a guest clock drift has already happened 
(e.g. after a migration), an unfortunately-timed RTC write will make it 
permanent. Windows time providers don't have the ability to control when 
Windows writes to RTC either. Thus the "real" host clock time is needed 
to help the VM adjust to the correct time.

IOW, it's the distinction between "keeping track of already correct 
time" versus "correcting wrong time by adjusting the offset"; the latter 
is what I'm looking for.

> 
>> Since there's no way to add more fields to struct shared_info, the
>> addition has to be done through struct arch_shared_info instead. Add two
>> fields in arch_shared_info representing time_offset's low and high
>> 32-bit halves.
> 
> Any why in two halves? Without that, ...
> 
>> --- a/xen/common/time.c
>> +++ b/xen/common/time.c
>> @@ -118,6 +118,11 @@ void update_domain_wallclock_time(struct domain *d)
>>       shared_info(d, wc_sec_hi) = sec >> 32;
>>   #endif
>>   
>> +    shared_info(d, arch.time_offset) =
>> +        (uint32_t)(uint64_t)d->time_offset.seconds;
>> +    shared_info(d, arch.time_offset_hi) =
>> +        (uint32_t)((uint64_t)d->time_offset.seconds >> 32);
> 
> ... no casting at all would be needed here. (Even when there are two halves,
> some of the casting can be dropped.)
> 
> Another question is - why unsigned? struct domain's time_offset.seconds is
> signed for a reason, aiui.

Both are just for easy consumption of the time offset on 32-bit guests. 
Unsigned is particularly because these are only parts of an int64_t (and 
therefore have no signedness themselves) and I prefer to let the 
conversion happen after reading the two fields.

> 
> Jan



--
Ngoc Tu Dinh | Vates XCP-ng Developer

XCP-ng & Xen Orchestra - Vates solutions

web: https://vates.tech