new config option vtsc_tolerance_khz to avoid TSC emulation
PATCH v6 1/1 new config option vtsc_tolerance_khz to avoid TSC emulation
Add an option to control when vTSC emulation will be activated for a domU with tsc_mode=default. Without such option each TSC access from domU will be emulated, which causes a significant perfomance drop for workloads that make use of rdtsc.
One option to avoid the TSC option is to run domUs with tsc_mode=native. This has the drawback that migrating a domU from a "2.3GHz" class host to a "2.4GHz" class host may change the rate at wich the TSC counter increases, the domU may not be prepared for that.
With the new option the host admin can decide how a domU should behave when it is migrated across systems of the same class. Since there is always some jitter when Xen calibrates the cpu_khz value, all hosts of the same class will most likely have slightly different values. As a result vTSC emulation is unavoidable. Data collected during the incident which triggered this change showed a jitter of up to 200 KHz across systems of the same class.
Existing padding fields are reused to store vtsc_khz_tolerance as u16.
v6:
- mention default value in xl.cfg
- tsc_set_info: remove usage of __func__, use %d for domid
- tsc_set_info: use ABS to calculate khz_diff
v5:
- reduce functionality to allow setting of the tolerance value only at initial domU startup
v4:
- add missing copyback in XEN_DOMCTL_set_vtsc_tolerance_khz
v3:
- rename vtsc_khz_tolerance to vtsc_tolerance_khz
- separate domctls to adjust values
- more docs
- update libxl.h
- update python tests
- flask check bound to tsc permissions
- not runtime tested due to dlsym() build errors in staging
--- docs/man/xen-tscmode.pod.7 | 16 ++++++++++++++++ docs/man/xl.cfg.pod.5.in | 10 ++++++++++ docs/specs/libxc-migration-stream.pandoc | 6 ++++-- tools/libxc/include/xenctrl.h | 2 ++ tools/libxc/xc_domain.c | 4 ++++ tools/libxc/xc_sr_common_x86.c | 6 ++++-- tools/libxc/xc_sr_stream_format.h | 3 ++- tools/libxl/libxl.h | 6 ++++++ tools/libxl/libxl_types.idl | 1 + tools/libxl/libxl_x86.c | 3 ++- tools/python/xen/lowlevel/xc/xc.c | 2 +- tools/xl/xl_parse.c | 3 +++ xen/arch/x86/domain.c | 2 +- xen/arch/x86/domctl.c | 2 ++ xen/arch/x86/time.c | 30 +++++++++++++++++++++++++++--- xen/include/asm-x86/domain.h | 1 + xen/include/asm-x86/time.h | 6 ++++-- xen/include/public/domctl.h | 3 ++- 18 files changed, 92 insertions(+), 14 deletions(-)
docs/man/xen-tscmode.pod.7 +16 -0
@@ -99,6 +99,9 @@ whether or not the VM has been saved/restored/migrated =back +If the tsc_mode is set to "default" the decision to emulate TSC can be +tweaked further with the "vtsc_tolerance_khz" option. + To understand this in more detail, the rest of this document must be read.
@@ -211,6 +214,19 @@ is emulated. Note that, though emulated, the "apparent" TSC frequency will be the TSC frequency of the initial physical machine, even after migration. +Since the calibration of the TSC frequency may not be 100% accurate, the +exact value of the frequency can change even across reboots. This means +also several otherwise identical systems can have a slightly different +TSC frequency. As a result TSC access will be emulated if a domU is +migrated from one host to another, identical host. To avoid the +performance impact of TSC emulation a certain tolerance of the measured +host TSC frequency can be specified with "vtsc_tolerance_khz". If the +measured "cpu_khz" value is within the tolerance range, TSC access +remains native. Otherwise it will be emulated. This allows to migrate +domUs between identical hardware. If the domU will be migrated to a +different kind of hardware, say from a "2.3GHz" to a "2.5GHz" system, +TSC will be emualted to maintain the TSC frequency expected by the domU. + For environments where both TSC-safeness AND highest performance even across migration is a requirement, application code can be specially modified to use an algorithm explicitly designed into Xen for this purpose.
docs/man/xl.cfg.pod.5.in +10 -0
@@ -1891,6 +1891,16 @@ determined in a similar way to that of B<default> TSC mode. Please see B<xen-tscmode(7)> for more information on this option. +=item B<vtsc_tolerance_khz="KHZ"> + +B<(x86 only, relevant only for tsc_mode=default)> +When a domU is started, the CPU frequency of the host is used by the domU for +TSC related time measurement. Once the domU is either migrated or +saved/restored on another host that CPU frequency has to be emulated to avoid +timedrift. To avoid the performance penalty of the TSC emulation, allow a +certain amount of jitter of the measured CPU frequency on the hosts the domU +is supposed to run on. Default value is 0, i.e. no tolerance. + =item B<localtime=BOOLEAN> Set the real time clock to local time or to UTC. False (0) by default,
docs/specs/libxc-migration-stream.pandoc +4 -2
@@ -3,7 +3,7 @@ Andrew Cooper <<[email protected]>> Wen Congyang <<[email protected]>> Yang Hongyang <<[email protected]>> -% Revision 2 +% Revision 3 Introduction ============
@@ -472,7 +472,7 @@ XEN\_DOMCTL\_{get,set}tscinfo hypercall sub-ops. +------------------------+------------------------+ | nsec | +------------------------+------------------------+ - | incarnation | (reserved) | + | incarnation | tolerance | (reserved) | +------------------------+------------------------+ --------------------------------------------------------------------
@@ -485,6 +485,8 @@ khz TSC frequency, in kHz. nsec Elapsed time, in nanoseconds. incarnation Incarnation. + +tolerance Amount of Jitter the domU can handle after migration -------------------------------------------------------------------- \clearpage
tools/libxc/include/xenctrl.h +2 -0
@@ -1360,6 +1360,7 @@ int xc_domain_set_tsc_info(xc_interface *xch,uint32_t tsc_mode, uint64_t elapsed_nsec, uint32_t gtsc_khz, + uint16_t vtsc_tolerance_khz, uint32_t incarnation); int xc_domain_get_tsc_info(xc_interface *xch,
@@ -1367,6 +1368,7 @@ int xc_domain_get_tsc_info(xc_interface *xch,uint32_t *tsc_mode, uint64_t *elapsed_nsec, uint32_t *gtsc_khz, + uint16_t *vtsc_tolerance_khz, uint32_t *incarnation);int xc_domain_disable_migrate (xc_interface *xch,uint32_t domid);
tools/libxc/xc_domain.c +4 -0
@@ -852,6 +852,7 @@ int xc_domain_set_tsc_info(xc_interface *xch,uint32_t tsc_mode, uint64_t elapsed_nsec, uint32_t gtsc_khz, + uint16_t vtsc_tolerance_khz, uint32_t incarnation) {DECLARE_DOMCTL ;
@@ -860,6 +861,7 @@ int xc_domain_set_tsc_info(xc_interface *xch, domctl.u.tsc_info.tsc_mode = tsc_mode; domctl.u.tsc_info.elapsed_nsec = elapsed_nsec; domctl.u.tsc_info.gtsc_khz = gtsc_khz; + domctl.u.tsc_info.vtsc_tolerance_khz = vtsc_tolerance_khz; domctl.u.tsc_info.incarnation = incarnation;return do_domctl (xch, &domctl); }
@@ -869,6 +871,7 @@ int xc_domain_get_tsc_info(xc_interface *xch,uint32_t *tsc_mode, uint64_t *elapsed_nsec, uint32_t *gtsc_khz, + uint16_t *vtsc_tolerance_khz, uint32_t *incarnation) { int rc;
@@ -882,6 +885,7 @@ int xc_domain_get_tsc_info(xc_interface *xch, *tsc_mode = domctl.u.tsc_info.tsc_mode; *elapsed_nsec = domctl.u.tsc_info.elapsed_nsec; *gtsc_khz = domctl.u.tsc_info.gtsc_khz; + *vtsc_tolerance_khz = domctl.u.tsc_info.vtsc_tolerance_khz; *incarnation = domctl.u.tsc_info.incarnation; }return rc;
tools/libxc/xc_sr_common_x86.c +4 -2
@@ -12,7 +12,8 @@ int write_tsc_info(struct xc_sr_context *ctx) };if (xc_domain_get_tsc_info (xch, ctx->domid, &tsc.mode, - &tsc.nsec, &tsc.khz, &tsc.incarnation) <0 ) + &tsc.nsec, &tsc.khz, &tsc.vtsc_tolerance, + &tsc.incarnation) <0 ) {PERROR ("Unable to obtain TSC information" );return -1 ;
@@ -34,7 +35,8 @@ int handle_tsc_info(struct xc_sr_context *ctx, struct xc_sr_record *rec) }if (xc_domain_set_tsc_info (xch, ctx->domid, tsc->mode, - tsc->nsec, tsc->khz, tsc->incarnation) ) + tsc->nsec, tsc->khz, tsc->vtsc_tolerance, + tsc->incarnation) ) {PERROR ("Unable to set TSC information" );return -1 ;
tools/libxc/xc_sr_stream_format.h +2 -1
@@ -121,7 +121,8 @@ struct xc_sr_rec_tsc_infouint32_t khz;uint64_t nsec;uint32_t incarnation; -uint32_t _res1; +uint16_t vtsc_tolerance; +uint16_t _res1; };/* HVM_PARAMS */
tools/libxl/libxl.h +6 -0
@@ -354,6 +354,12 @@#define LIBXL_HAVE_BUILDINFO_BOOTLOADER 1#define LIBXL_HAVE_BUILDINFO_BOOTLOADER_ARGS 1 +/* +* LIBXL_HAVE_VTSC_TOLERANCE_KHZ indicates that libxl_domain_build_info +* has the vtsc_tolerance_khz field. +*/ +#define LIBXL_HAVE_VTSC_TOLERANCE_KHZ 1 +/* * libxl ABI compatibility *
tools/libxl/libxl_types.idl +1 -0
@@ -466,6 +466,7 @@ libxl_domain_build_info = Struct("domain_build_info",[ ("vcpu_soft_affinity", Array(libxl_bitmap, "num_vcpu_soft_affinity")), ("numa_placement", libxl_defbool), ("tsc_mode", libxl_tsc_mode), + ("vtsc_tolerance_khz", uint32), ("max_memkb", MemKB), ("target_memkb", MemKB), ("video_memkb", MemKB),
tools/libxl/libxl_x86.c +2 -1
@@ -313,7 +313,8 @@ int libxl__arch_domain_create(libxl__gc *gc, libxl_domain_config *d_config,default :abort (); } -xc_domain_set_tsc_info (ctx->xch, domid, tsc_mode,0 ,0 ,0 ); +xc_domain_set_tsc_info (ctx->xch, domid, tsc_mode,0 ,0 , + d_config->b_info.vtsc_tolerance_khz,0 );if (libxl_defbool_val (d_config->b_info.disable_migrate))xc_domain_disable_migrate (ctx->xch, domid); rtc_timeoffset = d_config->b_info.rtc_timeoffset;
tools/python/xen/lowlevel/xc/xc.c +1 -1
@@ -1522,7 +1522,7 @@ static PyObject *pyxc_domain_set_tsc_info(XcObject *self, PyObject *args)if (!PyArg_ParseTuple (args,"ii" , &dom, &tsc_mode))return NULL ; -if (xc_domain_set_tsc_info (self->xc_handle, dom, tsc_mode,0 ,0 ,0 ) !=0 ) +if (xc_domain_set_tsc_info (self->xc_handle, dom, tsc_mode,0 ,0 ,0 ,0 ) !=0 )return pyxc_error_to_exception (self->xc_handle);Py_INCREF (zero);
tools/xl/xl_parse.c +3 -0
@@ -1126,6 +1126,9 @@ void parse_config_data(const char *config_source, } } +if (!xlu_cfg_get_long (config,"vtsc_tolerance_khz" , &l,0 )) + b_info->vtsc_tolerance_khz = l <0 || l >UINT16_MAX ?UINT16_MAX : l; +if (!xlu_cfg_get_long (config,"rtc_timeoffset" , &l,0 )) b_info->rtc_timeoffset = l;
xen/arch/x86/domain.c +1 -1
@@ -561,7 +561,7 @@ int arch_domain_create(struct domain *d,ASSERT_UNREACHABLE ();/* Not HVM and not PV? */ /* initialize default tsc behavior in case tools don't */ -tsc_set_info (d,TSC_MODE_DEFAULT ,0UL ,0 ,0 ); +tsc_set_info (d,TSC_MODE_DEFAULT ,0UL ,0 ,0 ,0 );/* PV/PVH guests get an emulated PIT too for video BIOSes to use. */ pit_init (d, cpu_khz);
xen/arch/x86/domctl.c +2 -0
@@ -939,6 +939,7 @@ long arch_do_domctl(tsc_get_info (d, &domctl->u.tsc_info.tsc_mode, &domctl->u.tsc_info.elapsed_nsec, &domctl->u.tsc_info.gtsc_khz, + &domctl->u.tsc_info.vtsc_tolerance_khz, &domctl->u.tsc_info.incarnation);domain_unpause (d); copyback = true;
@@ -954,6 +955,7 @@ long arch_do_domctl(tsc_set_info (d, domctl->u.tsc_info.tsc_mode, domctl->u.tsc_info.elapsed_nsec, domctl->u.tsc_info.gtsc_khz, + domctl->u.tsc_info.vtsc_tolerance_khz, domctl->u.tsc_info.incarnation);domain_unpause (d); }
xen/arch/x86/time.c +27 -3
@@ -2064,7 +2064,7 @@ int host_tsc_is_safe(void)*/ void tsc_get_info (struct domain *d,uint32_t *tsc_mode,uint64_t *elapsed_nsec,uint32_t *gtsc_khz, -uint32_t *incarnation) +uint16_t *vtsc_tolerance_khz,uint32_t *incarnation) {bool enable_tsc_scaling =is_hvm_domain (d) && hvm_tsc_scaling_supported && !d->arch.vtsc;
@@ -2080,6 +2080,7 @@ void tsc_get_info(struct domain *d, uint32_t *tsc_mode, *elapsed_nsec = *gtsc_khz =0 ;break ;case TSC_MODE_DEFAULT : + *vtsc_tolerance_khz = d->arch.vtsc_tolerance_khz;if ( d->arch.vtsc ) {case TSC_MODE_ALWAYS_EMULATE :
@@ -2122,7 +2123,8 @@ void tsc_get_info(struct domain *d, uint32_t *tsc_mode,*/ void tsc_set_info (struct domain *d,uint32_t tsc_mode,uint64_t elapsed_nsec, -uint32_t gtsc_khz,uint32_t incarnation) +uint32_t gtsc_khz,uint16_t vtsc_tolerance_khz, +uint32_t incarnation) {ASSERT (!is_system_domain (d));
@@ -2134,9 +2136,12 @@ void tsc_set_info(struct domain *d,switch ( d->arch.tsc_mode = tsc_mode ) { +bool disable_vtsc;bool enable_tsc_scaling;case TSC_MODE_DEFAULT : + d->arch.vtsc_tolerance_khz = vtsc_tolerance_khz; +/* Fallthrough. */ case TSC_MODE_ALWAYS_EMULATE : d->arch.vtsc_offset =get_s_time () - elapsed_nsec; d->arch.tsc_khz = gtsc_khz ?: cpu_khz;
@@ -2149,8 +2154,25 @@ void tsc_set_info(struct domain *d,* When a guest is created, gtsc_khz is passed in as zero, making * d->arch.tsc_khz == cpu_khz. Thus no need to check incarnation. */ + disable_vtsc = d->arch.tsc_khz == cpu_khz; + +if ( tsc_mode ==TSC_MODE_DEFAULT && gtsc_khz && + d->arch.vtsc_tolerance_khz ) + { +long khz_diff; + + khz_diff =ABS ((long )(cpu_khz - gtsc_khz)); + disable_vtsc = khz_diff <= d->arch.vtsc_tolerance_khz; + +printk (XENLOG_G_INFO "d%d: host has %lu kHz," +" domU expects %u kHz," +" difference of %ld is %s tolerance of %u\n" , + d->domain_id, cpu_khz, gtsc_khz, khz_diff, + disable_vtsc ?"within" :"outside" , + d->arch.vtsc_tolerance_khz); + }if ( tsc_mode ==TSC_MODE_DEFAULT &&host_tsc_is_safe () && - (d->arch.tsc_khz == cpu_khz || + (disable_vtsc || (is_hvm_domain (d) &&hvm_get_tsc_scaling_ratio (d->arch.tsc_khz))) ) {
@@ -2239,6 +2261,8 @@ static void dump_softtsc(unsigned char key)printk (",ofs=%#" PRIx64, d->arch.vtsc_offset);if ( d->arch.tsc_khz )printk (",khz=%" PRIu32, d->arch.tsc_khz); +if ( d->arch.vtsc_tolerance_khz ) +printk (",tol=%" PRIu16, d->arch.vtsc_tolerance_khz);if ( d->arch.incarnation )printk (",inc=%" PRIu32, d->arch.incarnation);#if !defined(NDEBUG ) || defined(CONFIG_PERF_COUNTERS )
xen/include/asm-x86/domain.h +1 -0
@@ -374,6 +374,7 @@ struct arch_domainuint64_t vtsc_offset;/* adjustment for save/restore/migrate */ uint32_t tsc_khz;/* cached guest khz for certain emulated or hardware TSC scaling cases */ +uint32_t vtsc_tolerance_khz;/* domU handles that much jitter in cpu_khz */ struct time_scale vtsc_to_ns;/* scaling for certain emulated or hardware TSC scaling cases */ struct time_scale ns_to_vtsc;/* scaling for certain emulated or
xen/include/asm-x86/time.h +4 -2
@@ -61,10 +61,12 @@ u64 gtime_to_gtsc(struct domain *d, u64 time);u64 gtsc_to_gtime (struct domain *d,u64 tsc);void tsc_set_info (struct domain *d,uint32_t tsc_mode,uint64_t elapsed_nsec, -uint32_t gtsc_khz,uint32_t incarnation); +uint32_t gtsc_khz,uint16_t vtsc_tolerance_khz, +uint32_t incarnation);void tsc_get_info (struct domain *d,uint32_t *tsc_mode,uint64_t *elapsed_nsec, -uint32_t *gtsc_khz,uint32_t *incarnation); +uint32_t *gtsc_khz,uint16_t *vtsc_tolerance_khz, +uint32_t *incarnation);void force_update_vcpu_system_time (struct vcpu *v);
xen/include/public/domctl.h +2 -1
@@ -702,7 +702,8 @@ struct xen_domctl_tsc_info {uint32_t tsc_mode;uint32_t gtsc_khz;uint32_t incarnation; -uint32_t pad; +uint16_t vtsc_tolerance_khz; +uint16_t pad;uint64_aligned_t elapsed_nsec; };
_______________________________________________
Xen-devel mailing list
[email protected]
https://lists.xenproject.org/mailman/listinfo/xen-devel
tools/libxl/libxl_types.idl libxl_domain_build_info = Struct("domain_build_info",[ · line 469
Quoted 9 lines · Olaf Hering
tools/libxl/libxl_types.idl +1
-0@@ -466,6 +466,7 @@ libxl_domain_build_info = Struct("domain_build_info",[ ("vcpu_soft_affinity", Array(libxl_bitmap, "num_vcpu_soft_affinity")), ("numa_placement", libxl_defbool), ("tsc_mode", libxl_tsc_mode), + ("vtsc_tolerance_khz", uint32),
Given the difference in types here, you need to check for truncation where the value is assigned from uint32_t to uint16_t.
But why don't you use uint16_t here instead? Is there future plan to widen it to uint32_t in the hypervisor?
The rest looks correct to me.
Wei.
_______________________________________________
Xen-devel mailing list
[email protected]
https://lists.xenproject.org/mailman/listinfo/xen-devel