x86: KVM vdso and clock improvements
Subsystems (5)
Andy Lutomirski
maintainerv2 cover x86: KVM vdso and clock improvements
x86: KVM vdso and clock improvements
NB: patch 1 doesn't really belong here, but it makes this a lot easier for me to test. Patch 1, if it's okay at all, should go though the kvm tree. The rest should probably go through tip:x86/vdso once they're reviewed.
I'll do a followup to enable vdso pvclock on 32-bit guests. I'm not currently set up to test it. (The KVM people could also do it very easily on top of these patches.)
Changes from v1:
- Dropped patch 1
- Added Paolo's review and acks
- Fixed a build issue on some configs
Andy Lutomirski (4):
x86, vdso, pvclock: Simplify and speed up the vdso pvclock reader x86/vdso: Get pvclock data from the vvar VMA instead of the fixmap x86/vdso: Remove pvclock fixmap machinery
x86/vdso: Enable vdso pvclock access on all vdso variants
arch/x86/entry/vdso/vclock_gettime.c | 151 ++++++++++++++++------------------ arch/x86/entry/vdso/vdso-layout.lds.S | 3 +- arch/x86/entry/vdso/vdso2c.c | 3 + arch/x86/entry/vdso/vma.c | 14 ++++ arch/x86/include/asm/fixmap.h | 5 -- arch/x86/include/asm/pvclock.h | 14 ++-- arch/x86/include/asm/vdso.h | 1 + arch/x86/kernel/kvmclock.c | 11 ++- arch/x86/kernel/pvclock.c | 24 ------ 9 files changed, 107 insertions(+), 119 deletions(-)
Signature · 1 line
2.5.0
Andy Lutomirski
maintainerPATCH v2 1/4 x86, vdso, pvclock: Simplify and speed up the vdso pvclock reader
From: Andy Lutomirski <[email protected]>
The pvclock vdso code was too abstracted to understand easily and excessively paranoid. Simplify it for a huge speedup.
This opens the door for additional simplifications, as the vdso no longer accesses the pvti for any vcpu other than vcpu 0.
Before, vclock_gettime using kvm-clock took about 45ns on my machine. With this change, it takes 29ns, which is almost as fast as the pure TSC implementation.
--- arch/x86/entry/vdso/vclock_gettime.c | 81 ++++++++++++++++++++---------------- 1 file changed, 46 insertions(+), 35 deletions(-)
arch/x86/entry/vdso/vclock_gettime.c +46 -35
@@ -78,47 +78,58 @@ static notrace const struct pvclock_vsyscall_time_info *get_pvti(int cpu) static notrace cycle_t vread_pvclock(int *mode) { -const struct pvclock_vsyscall_time_info *pvti; +const struct pvclock_vcpu_time_info *pvti = &get_pvti (0 )->pvti;cycle_t ret; -u64 last; -u32 version; -u8 flags; -unsigned cpu, cpu1; - +u64 tsc, pvti_tsc; +u64 last, delta, pvti_system_time; +u32 version, pvti_tsc_to_system_mul, pvti_tsc_shift;/* -* Note: hypervisor must guarantee that: -* 1. cpu ID number maps 1:1 to per-CPU pvclock time info. -* 2. that per-CPU pvclock time info is updated if the -* underlying CPU changes. -* 3. that version is increased whenever underlying CPU -* changes. +* Note: The kernel and hypervisor must guarantee that cpu ID +* number maps 1:1 to per-CPU pvclock time info. +* +* Because the hypervisor is entirely unaware of guest userspace +* preemption, it cannot guarantee that per-CPU pvclock time +* info is updated if the underlying CPU changes or that that +* version is increased whenever underlying CPU changes. * +* On KVM, we are guaranteed that pvti updates for any vCPU are +* atomic as seen by *all* vCPUs. This is an even stronger +* guarantee than we get with a normal seqlock. +* +* On Xen, we don't appear to have that guarantee, but Xen still +* supplies a valid seqlock using the version field. + +* We only do pvclock vdso timing at all if +* PVCLOCK_TSC_STABLE_BIT is set, and we interpret that bit to +* mean that all vCPUs have matching pvti and that the TSC is +* synced, so we can just look at vCPU 0's pvti. */ -do { - cpu =__getcpu () &VGETCPU_CPU_MASK ; -/* TODO: We can put vcpu id into higher bits of pvti.version. -* This will save a couple of cycles by getting rid of -* __getcpu() calls (Gleb). -*/ - - pvti =get_pvti (cpu); - - version =__pvclock_read_cycles (&pvti->pvti, &ret, &flags); - -/* -* Test we're still on the cpu as well as the version. -* We could have been migrated just after the first -* vgetcpu but before fetching the version, so we -* wouldn't notice a version change. -*/ - cpu1 =__getcpu () &VGETCPU_CPU_MASK ; - }while (unlikely (cpu != cpu1 || - (pvti->pvti.version &1 ) || - pvti->pvti.version != version)); - -if (unlikely (!(flags &PVCLOCK_TSC_STABLE_BIT ))) + +if (unlikely (!(pvti->flags &PVCLOCK_TSC_STABLE_BIT ))) { *mode =VCLOCK_NONE ; +return 0 ; + } + +do { + version = pvti->version; + +/* This is also a read barrier, so we'll read version first. */ + tsc =rdtsc_ordered (); + + pvti_tsc_to_system_mul = pvti->tsc_to_system_mul; + pvti_tsc_shift = pvti->tsc_shift; + pvti_system_time = pvti->system_time; + pvti_tsc = pvti->tsc_timestamp; + +/* Make sure that the version double-check is last. */ +smp_rmb (); + }while (unlikely ((version &1 ) || version != pvti->version)); + + delta = tsc - pvti_tsc; + ret = pvti_system_time + +pvclock_scale_delta (delta, pvti_tsc_to_system_mul, + pvti_tsc_shift);/* refer to tsc.c read_tsc() comment for rationale */ last = gtod->cycle_last;
Signature · 1 line
2.5.0
Re: [PATCH v2 1/4] x86, vdso, pvclock: Simplify and speed up the vdso pvclock reader
Quoted 98 lines · Andy Lutomirski
From: Andy Lutomirski <[email protected]>
The pvclock vdso code was too abstracted to understand easily and excessively paranoid. Simplify it for a huge speedup.
This opens the door for additional simplifications, as the vdso no longer accesses the pvti for any vcpu other than vcpu 0.
Before,
vclock_gettimeusing kvm-clock took about 45ns on my machine. With this change, it takes 29ns, which is almost as fast as the pure TSC implementation.Reviewed-by Paolo Bonzini <[email protected]>Signed-off-by Andy Lutomirski <[email protected]>--- arch/x86/entry/vdso/vclock_gettime.c | 81 ++++++++++++++++++++---------------- 1 file changed, 46 insertions(+), 35 deletions(-)arch/x86/entry/vdso/vclock_gettime.c +31
-35@@ -78,47 +78,58 @@ static notrace const struct pvclock_vsyscall_time_info *get_pvti(int cpu) static notrace cycle_t vread_pvclock(int *mode) { -const struct pvclock_vsyscall_time_info *pvti; +const struct pvclock_vcpu_time_info *pvti = &get_pvti (0 )->pvti;cycle_t ret; -u64 last; -u32 version; -u8 flags; -unsigned cpu, cpu1; - +u64 tsc, pvti_tsc; +u64 last, delta, pvti_system_time; +u32 version, pvti_tsc_to_system_mul, pvti_tsc_shift;/* -* Note: hypervisor must guarantee that: -* 1. cpu ID number maps 1:1 to per-CPU pvclock time info. -* 2. that per-CPU pvclock time info is updated if the -* underlying CPU changes. -* 3. that version is increased whenever underlying CPU -* changes. +* Note: The kernel and hypervisor must guarantee that cpu ID +* number maps 1:1 to per-CPU pvclock time info. +* +* Because the hypervisor is entirely unaware of guest userspace +* preemption, it cannot guarantee that per-CPU pvclock time +* info is updated if the underlying CPU changes or that that +* version is increased whenever underlying CPU changes. * +* On KVM, we are guaranteed that pvti updates for any vCPU are +* atomic as seen by *all* vCPUs. This is an even stronger +* guarantee than we get with a normal seqlock. +* +* On Xen, we don't appear to have that guarantee, but Xen still +* supplies a valid seqlock using the version field. + +* We only do pvclock vdso timing at all if +* PVCLOCK_TSC_STABLE_BIT is set, and we interpret that bit to +* mean that all vCPUs have matching pvti and that the TSC is +* synced, so we can just look at vCPU 0's pvti. */ -do { - cpu =__getcpu () &VGETCPU_CPU_MASK ; -/* TODO: We can put vcpu id into higher bits of pvti.version. -* This will save a couple of cycles by getting rid of -* __getcpu() calls (Gleb). -*/ - - pvti =get_pvti (cpu); - - version =__pvclock_read_cycles (&pvti->pvti, &ret, &flags); - -/* -* Test we're still on the cpu as well as the version. -* We could have been migrated just after the first -* vgetcpu but before fetching the version, so we -* wouldn't notice a version change. -*/ - cpu1 =__getcpu () &VGETCPU_CPU_MASK ; - }while (unlikely (cpu != cpu1 || - (pvti->pvti.version &1 ) || - pvti->pvti.version != version)); - -if (unlikely (!(flags &PVCLOCK_TSC_STABLE_BIT ))) + +if (unlikely (!(pvti->flags &PVCLOCK_TSC_STABLE_BIT ))) { *mode =VCLOCK_NONE ; +return 0 ; + } + + do { + version = pvti->version; + + /* This is also a read barrier, so we'll read version first. */
arch/x86/entry/vdso/vclock_gettime.c static notrace const struct pvclock_vsyscall_time_info *get_pvti(int cpu) · line 127
Quoted 10 lines
arch/x86/entry/vdso/vclock_gettime.c +10
-0+ tsc =rdtsc_ordered (); + + pvti_tsc_to_system_mul = pvti->tsc_to_system_mul; + pvti_tsc_shift = pvti->tsc_shift; + pvti_system_time = pvti->system_time; + pvti_tsc = pvti->tsc_timestamp; + +/* Make sure that the version double-check is last. */ +smp_rmb (); + }while (unlikely ((version &1 ) || version != pvti->version));
Andy,
What happens if PVCLOCK_TSC_STABLE_BIT is disabled here?
Quoted 15 lines
arch/x86/entry/vdso/vclock_gettime.c +5
-0+ + delta = tsc - pvti_tsc; + ret = pvti_system_time + +pvclock_scale_delta (delta, pvti_tsc_to_system_mul, + pvti_tsc_shift);/* refer to tsc.c read_tsc() comment for rationale */ last = gtod->cycle_last;Signature · 6 lines
2.5.0 -- To unsubscribe from this list: send the line "unsubscribe kvm" in the body of a message to [email protected] More majordomo info at http://vger.kernel.org/majordomo-info.html
Re: [PATCH v2 1/4] x86, vdso, pvclock: Simplify and speed up the vdso pvclock reader
Quoted 112 lines · Marcelo Tosatti
Quoted 98 lines · Andy Lutomirski
From: Andy Lutomirski <[email protected]>
The pvclock vdso code was too abstracted to understand easily and excessively paranoid. Simplify it for a huge speedup.
This opens the door for additional simplifications, as the vdso no longer accesses the pvti for any vcpu other than vcpu 0.
Before,
vclock_gettimeusing kvm-clock took about 45ns on my machine. With this change, it takes 29ns, which is almost as fast as the pure TSC implementation.Reviewed-by Paolo Bonzini <[email protected]>Signed-off-by Andy Lutomirski <[email protected]>--- arch/x86/entry/vdso/vclock_gettime.c | 81 ++++++++++++++++++++---------------- 1 file changed, 46 insertions(+), 35 deletions(-)arch/x86/entry/vdso/vclock_gettime.c +31
-35@@ -78,47 +78,58 @@ static notrace const struct pvclock_vsyscall_time_info *get_pvti(int cpu) static notrace cycle_t vread_pvclock(int *mode) { -const struct pvclock_vsyscall_time_info *pvti; +const struct pvclock_vcpu_time_info *pvti = &get_pvti (0 )->pvti;cycle_t ret; -u64 last; -u32 version; -u8 flags; -unsigned cpu, cpu1; - +u64 tsc, pvti_tsc; +u64 last, delta, pvti_system_time; +u32 version, pvti_tsc_to_system_mul, pvti_tsc_shift;/* -* Note: hypervisor must guarantee that: -* 1. cpu ID number maps 1:1 to per-CPU pvclock time info. -* 2. that per-CPU pvclock time info is updated if the -* underlying CPU changes. -* 3. that version is increased whenever underlying CPU -* changes. +* Note: The kernel and hypervisor must guarantee that cpu ID +* number maps 1:1 to per-CPU pvclock time info. +* +* Because the hypervisor is entirely unaware of guest userspace +* preemption, it cannot guarantee that per-CPU pvclock time +* info is updated if the underlying CPU changes or that that +* version is increased whenever underlying CPU changes. * +* On KVM, we are guaranteed that pvti updates for any vCPU are +* atomic as seen by *all* vCPUs. This is an even stronger +* guarantee than we get with a normal seqlock. +* +* On Xen, we don't appear to have that guarantee, but Xen still +* supplies a valid seqlock using the version field. + +* We only do pvclock vdso timing at all if +* PVCLOCK_TSC_STABLE_BIT is set, and we interpret that bit to +* mean that all vCPUs have matching pvti and that the TSC is +* synced, so we can just look at vCPU 0's pvti. */ -do { - cpu =__getcpu () &VGETCPU_CPU_MASK ; -/* TODO: We can put vcpu id into higher bits of pvti.version. -* This will save a couple of cycles by getting rid of -* __getcpu() calls (Gleb). -*/ - - pvti =get_pvti (cpu); - - version =__pvclock_read_cycles (&pvti->pvti, &ret, &flags); - -/* -* Test we're still on the cpu as well as the version. -* We could have been migrated just after the first -* vgetcpu but before fetching the version, so we -* wouldn't notice a version change. -*/ - cpu1 =__getcpu () &VGETCPU_CPU_MASK ; - }while (unlikely (cpu != cpu1 || - (pvti->pvti.version &1 ) || - pvti->pvti.version != version)); - -if (unlikely (!(flags &PVCLOCK_TSC_STABLE_BIT ))) + +if (unlikely (!(pvti->flags &PVCLOCK_TSC_STABLE_BIT ))) { *mode =VCLOCK_NONE ; +return 0 ; + } + + do { + version = pvti->version; + + /* This is also a read barrier, so we'll read version first. */arch/x86/entry/vdso/vclock_gettime.c static notrace const struct pvclock_vsyscall_time_info *get_pvti(int cpu) · line 127
Quoted 10 lines
arch/x86/entry/vdso/vclock_gettime.c +10
-0+ tsc =rdtsc_ordered (); + + pvti_tsc_to_system_mul = pvti->tsc_to_system_mul; + pvti_tsc_shift = pvti->tsc_shift; + pvti_system_time = pvti->system_time; + pvti_tsc = pvti->tsc_timestamp; + +/* Make sure that the version double-check is last. */ +smp_rmb (); + }while (unlikely ((version &1 ) || version != pvti->version));Andy,
Quoted 1 line
What happens if
PVCLOCK_TSC_STABLE_BITis disabled here?
Do you mean what happens if it's disabled in the loop part after the first check? If that's actually possible, I'll do a follow-up to bail if that happens by moving the check into the loop.
--Andy
Re: [PATCH v2 1/4] x86, vdso, pvclock: Simplify and speed up the vdso pvclock reader
Quoted 113 lines · Andy Lutomirski
Quoted 112 lines · Marcelo Tosatti
Quoted 98 lines · Andy Lutomirski
From: Andy Lutomirski <[email protected]>
The pvclock vdso code was too abstracted to understand easily and excessively paranoid. Simplify it for a huge speedup.
This opens the door for additional simplifications, as the vdso no longer accesses the pvti for any vcpu other than vcpu 0.
Before,
vclock_gettimeusing kvm-clock took about 45ns on my machine. With this change, it takes 29ns, which is almost as fast as the pure TSC implementation.Reviewed-by Paolo Bonzini <[email protected]>Signed-off-by Andy Lutomirski <[email protected]>--- arch/x86/entry/vdso/vclock_gettime.c | 81 ++++++++++++++++++++---------------- 1 file changed, 46 insertions(+), 35 deletions(-)arch/x86/entry/vdso/vclock_gettime.c +31
-35@@ -78,47 +78,58 @@ static notrace const struct pvclock_vsyscall_time_info *get_pvti(int cpu) static notrace cycle_t vread_pvclock(int *mode) { -const struct pvclock_vsyscall_time_info *pvti; +const struct pvclock_vcpu_time_info *pvti = &get_pvti (0 )->pvti;cycle_t ret; -u64 last; -u32 version; -u8 flags; -unsigned cpu, cpu1; - +u64 tsc, pvti_tsc; +u64 last, delta, pvti_system_time; +u32 version, pvti_tsc_to_system_mul, pvti_tsc_shift;/* -* Note: hypervisor must guarantee that: -* 1. cpu ID number maps 1:1 to per-CPU pvclock time info. -* 2. that per-CPU pvclock time info is updated if the -* underlying CPU changes. -* 3. that version is increased whenever underlying CPU -* changes. +* Note: The kernel and hypervisor must guarantee that cpu ID +* number maps 1:1 to per-CPU pvclock time info. +* +* Because the hypervisor is entirely unaware of guest userspace +* preemption, it cannot guarantee that per-CPU pvclock time +* info is updated if the underlying CPU changes or that that +* version is increased whenever underlying CPU changes. * +* On KVM, we are guaranteed that pvti updates for any vCPU are +* atomic as seen by *all* vCPUs. This is an even stronger +* guarantee than we get with a normal seqlock. +* +* On Xen, we don't appear to have that guarantee, but Xen still +* supplies a valid seqlock using the version field. + +* We only do pvclock vdso timing at all if +* PVCLOCK_TSC_STABLE_BIT is set, and we interpret that bit to +* mean that all vCPUs have matching pvti and that the TSC is +* synced, so we can just look at vCPU 0's pvti. */ -do { - cpu =__getcpu () &VGETCPU_CPU_MASK ; -/* TODO: We can put vcpu id into higher bits of pvti.version. -* This will save a couple of cycles by getting rid of -* __getcpu() calls (Gleb). -*/ - - pvti =get_pvti (cpu); - - version =__pvclock_read_cycles (&pvti->pvti, &ret, &flags); - -/* -* Test we're still on the cpu as well as the version. -* We could have been migrated just after the first -* vgetcpu but before fetching the version, so we -* wouldn't notice a version change. -*/ - cpu1 =__getcpu () &VGETCPU_CPU_MASK ; - }while (unlikely (cpu != cpu1 || - (pvti->pvti.version &1 ) || - pvti->pvti.version != version)); - -if (unlikely (!(flags &PVCLOCK_TSC_STABLE_BIT ))) + +if (unlikely (!(pvti->flags &PVCLOCK_TSC_STABLE_BIT ))) { *mode =VCLOCK_NONE ; +return 0 ; + } + + do { + version = pvti->version; + + /* This is also a read barrier, so we'll read version first. */arch/x86/entry/vdso/vclock_gettime.c static notrace const struct pvclock_vsyscall_time_info *get_pvti(int cpu) · line 127
Quoted 10 lines
arch/x86/entry/vdso/vclock_gettime.c +10
-0+ tsc =rdtsc_ordered (); + + pvti_tsc_to_system_mul = pvti->tsc_to_system_mul; + pvti_tsc_shift = pvti->tsc_shift; + pvti_system_time = pvti->system_time; + pvti_tsc = pvti->tsc_timestamp; + +/* Make sure that the version double-check is last. */ +smp_rmb (); + }while (unlikely ((version &1 ) || version != pvti->version));Andy,
Quoted 7 lines
Quoted 1 line
What happens if
PVCLOCK_TSC_STABLE_BITis disabled here?Do you mean what happens if it's disabled in the loop part after the first check? If that's actually possible, I'll do a follow-up to bail if that happens by moving the check into the loop.
--Andy
It is possible.
Andy Lutomirski
maintainerPATCH 1/1 x86/vdso/pvclock: Protect STABLE check with the seqcount
If the clock becomes unstable while we're reading it, we need to bail. We can do this by simply moving the check into the seqcount loop.
---
Marcelo, how's this?
arch/x86/entry/vdso/vclock_gettime.c | 12 ++++++------
1 file changed, 6 insertions(+), 6 deletions(-)
arch/x86/entry/vdso/vclock_gettime.c +6 -6
@@ -126,23 +126,23 @@ static notrace cycle_t vread_pvclock(int *mode)* * On Xen, we don't appear to have that guarantee, but Xen still * supplies a valid seqlock using the version field. - +* * We only do pvclock vdso timing at all if * PVCLOCK_TSC_STABLE_BIT is set, and we interpret that bit to * mean that all vCPUs have matching pvti and that the TSC is * synced, so we can just look at vCPU 0's pvti. */ -if (unlikely (!(pvti->flags &PVCLOCK_TSC_STABLE_BIT ))) { - *mode =VCLOCK_NONE ; -return 0 ; - } - do { version = pvti->version; smp_rmb(); + if (unlikely(!(pvti->flags & PVCLOCK_TSC_STABLE_BIT))) { + *mode = VCLOCK_NONE; + return 0; + } + tsc = rdtsc_ordered(); pvti_tsc_to_system_mul = pvti->tsc_to_system_mul; pvti_tsc_shift = pvti->tsc_shift;
Signature · 1 line
2.4.3
Re: [PATCH] x86/vdso/pvclock: Protect STABLE check with the seqcount
Quoted 49 lines · Andy Lutomirski
If the clock becomes unstable while we're reading it, we need to bail. We can do this by simply moving the check into the seqcount loop.
Reported-by Marcelo Tosatti <[email protected]>Signed-off-by Andy Lutomirski <[email protected]>---
Marcelo, how's this?
arch/x86/entry/vdso/vclock_gettime.c| 12 ++++++------1 file changed, 6 insertions(+), 6 deletions(-)arch/x86/entry/vdso/vclock_gettime.c +6
-6@@ -126,23 +126,23 @@ static notrace cycle_t vread_pvclock(int *mode)* * On Xen, we don't appear to have that guarantee, but Xen still * supplies a valid seqlock using the version field. - +* * We only do pvclock vdso timing at all if * PVCLOCK_TSC_STABLE_BIT is set, and we interpret that bit to * mean that all vCPUs have matching pvti and that the TSC is * synced, so we can just look at vCPU 0's pvti. */ -if (unlikely (!(pvti->flags &PVCLOCK_TSC_STABLE_BIT ))) { - *mode =VCLOCK_NONE ; -return 0 ; - } - do { version = pvti->version; smp_rmb(); + if (unlikely(!(pvti->flags & PVCLOCK_TSC_STABLE_BIT))) { + *mode = VCLOCK_NONE; + return 0; + } + tsc = rdtsc_ordered(); pvti_tsc_to_system_mul = pvti->tsc_to_system_mul; pvti_tsc_shift = pvti->tsc_shift;Signature · 1 line
2.4.3
Check it before returning the value (once cleared, it can't be set back to 1), similarly to what was in place before.
Re: [PATCH] x86/vdso/pvclock: Protect STABLE check with the seqcount
Quoted 40 lines · Marcelo Tosatti
Quoted 39 lines · Andy Lutomirski
If the clock becomes unstable while we're reading it, we need to bail. We can do this by simply moving the check into the seqcount loop.
Reported-by Marcelo Tosatti <[email protected]>Signed-off-by Andy Lutomirski <[email protected]>---
Marcelo, how's this?
arch/x86/entry/vdso/vclock_gettime.c| 12 ++++++------1 file changed, 6 insertions(+), 6 deletions(-)arch/x86/entry/vdso/vclock_gettime.c +1
-6@@ -126,23 +126,23 @@ static notrace cycle_t vread_pvclock(int *mode)* * On Xen, we don't appear to have that guarantee, but Xen still * supplies a valid seqlock using the version field. - +* * We only do pvclock vdso timing at all if * PVCLOCK_TSC_STABLE_BIT is set, and we interpret that bit to * mean that all vCPUs have matching pvti and that the TSC is * synced, so we can just look at vCPU 0's pvti. */ -if (unlikely (!(pvti->flags &PVCLOCK_TSC_STABLE_BIT ))) { - *mode =VCLOCK_NONE ; -return 0 ; - } - do { version = pvti->version; smp_rmb();
Quoted 15 lines
Quoted 10 lines
arch/x86/entry/vdso/vclock_gettime.c +5
-1+if (unlikely (!(pvti->flags &PVCLOCK_TSC_STABLE_BIT ))) { + *mode =VCLOCK_NONE ; +return 0 ; + } + tsc =rdtsc_ordered (); pvti_tsc_to_system_mul = pvti->tsc_to_system_mul; pvti_tsc_shift = pvti->tsc_shift; --2.4.3
Check it before returning the value (once cleared, it can't be set back to 1), similarly to what was in place before.
I don't understand what you mean.
In the old code (4.3 and 4.4), the vdso checks STABLE_BIT at the end, which is correct as long as STABLE_BIT can never change from 0 to 1.
In the -tip code, it's clearly wrong.
In the code in this patch, it should be correct regardless of how STABLE_BIT changes as long as the seqcount works. Given that the performance cost of doing that is zero, I'd rather keep it that way. If we're really paranoid, we could move it after the rest of the pvti reads and add a barrier, but is there really any host on which that matters?
--Andy
Signature · 2 lines
Andy Lutomirski AMA Capital Management, LLC
Re: [PATCH] x86/vdso/pvclock: Protect STABLE check with the seqcount
Quoted 5 lines · Andy Lutomirski
I don't understand what you mean.
In the old code (4.3 and 4.4), the vdso checks
STABLE_BITat the end, which is correct as long asSTABLE_BITcan never change from 0 to 1.
Quoted 8 lines
In the -tip code, it's clearly wrong.
In the code in this patch, it should be correct regardless of how
STABLE_BITchanges as long as the seqcount works. Given that the performance cost of doing that is zero, I'd rather keep it that way. If we're really paranoid, we could move it after the rest of the pvti reads and add a barrier, but is there really any host on which that matters?
I agree that your patch is fine.
Paolo
Re: [PATCH] x86/vdso/pvclock: Protect STABLE check with the seqcount
In reply to Andy LutomirskiQuoted 64 lines · Andy Lutomirski
Quoted 40 lines · Marcelo Tosatti
Quoted 39 lines · Andy Lutomirski
If the clock becomes unstable while we're reading it, we need to bail. We can do this by simply moving the check into the seqcount loop.
Reported-by Marcelo Tosatti <[email protected]>Signed-off-by Andy Lutomirski <[email protected]>---
Marcelo, how's this?
arch/x86/entry/vdso/vclock_gettime.c| 12 ++++++------1 file changed, 6 insertions(+), 6 deletions(-)arch/x86/entry/vdso/vclock_gettime.c +1
-6@@ -126,23 +126,23 @@ static notrace cycle_t vread_pvclock(int *mode)* * On Xen, we don't appear to have that guarantee, but Xen still * supplies a valid seqlock using the version field. - +* * We only do pvclock vdso timing at all if * PVCLOCK_TSC_STABLE_BIT is set, and we interpret that bit to * mean that all vCPUs have matching pvti and that the TSC is * synced, so we can just look at vCPU 0's pvti. */ -if (unlikely (!(pvti->flags &PVCLOCK_TSC_STABLE_BIT ))) { - *mode =VCLOCK_NONE ; -return 0 ; - } - do { version = pvti->version; smp_rmb();Quoted 15 lines
Quoted 10 lines
arch/x86/entry/vdso/vclock_gettime.c +5
-1+if (unlikely (!(pvti->flags &PVCLOCK_TSC_STABLE_BIT ))) { + *mode =VCLOCK_NONE ; +return 0 ; + } + tsc =rdtsc_ordered (); pvti_tsc_to_system_mul = pvti->tsc_to_system_mul; pvti_tsc_shift = pvti->tsc_shift; --2.4.3
Check it before returning the value (once cleared, it can't be set back to 1), similarly to what was in place before.
I don't understand what you mean.
In the old code (4.3 and 4.4), the vdso checks
STABLE_BITat the end, which is correct as long asSTABLE_BITcan never change from 0 to 1.In the -tip code, it's clearly wrong.
Quoted 12 lines
In the code in this patch, it should be correct regardless of how
STABLE_BITchanges as long as the seqcount works. Given that the performance cost of doing that is zero, I'd rather keep it that way. If we're really paranoid, we could move it after the rest of the pvti reads and add a barrier, but is there really any host on which that matters?--Andy
Signature · 2 lines
Andy Lutomirski AMA Capital Management, LLC
Right, its OK due to version check, thanks.
Re: [PATCH] x86/vdso/pvclock: Protect STABLE check with the seqcount
Hi Ingo-
Can you apply this before the tip:x86/asm pull request goes out? It fixes a regression in tip:x86/asm.
--Andy
Quoted 80 lines · Marcelo Tosatti
Quoted 71 lines · Andy Lutomirski
Quoted 40 lines · Marcelo Tosatti
Quoted 39 lines · Andy Lutomirski
If the clock becomes unstable while we're reading it, we need to bail. We can do this by simply moving the check into the seqcount loop.
Reported-by Marcelo Tosatti <[email protected]>Signed-off-by Andy Lutomirski <[email protected]>---
Marcelo, how's this?
arch/x86/entry/vdso/vclock_gettime.c| 12 ++++++------1 file changed, 6 insertions(+), 6 deletions(-)arch/x86/entry/vdso/vclock_gettime.c +1
-6@@ -126,23 +126,23 @@ static notrace cycle_t vread_pvclock(int *mode)* * On Xen, we don't appear to have that guarantee, but Xen still * supplies a valid seqlock using the version field. - +* * We only do pvclock vdso timing at all if * PVCLOCK_TSC_STABLE_BIT is set, and we interpret that bit to * mean that all vCPUs have matching pvti and that the TSC is * synced, so we can just look at vCPU 0's pvti. */ -if (unlikely (!(pvti->flags &PVCLOCK_TSC_STABLE_BIT ))) { - *mode =VCLOCK_NONE ; -return 0 ; - } - do { version = pvti->version; smp_rmb();Quoted 15 lines
Quoted 10 lines
arch/x86/entry/vdso/vclock_gettime.c +5
-1+if (unlikely (!(pvti->flags &PVCLOCK_TSC_STABLE_BIT ))) { + *mode =VCLOCK_NONE ; +return 0 ; + } + tsc =rdtsc_ordered (); pvti_tsc_to_system_mul = pvti->tsc_to_system_mul; pvti_tsc_shift = pvti->tsc_shift; --2.4.3
Check it before returning the value (once cleared, it can't be set back to 1), similarly to what was in place before.
I don't understand what you mean.
In the old code (4.3 and 4.4), the vdso checks
STABLE_BITat the end, which is correct as long asSTABLE_BITcan never change from 0 to 1.In the -tip code, it's clearly wrong.
In the code in this patch, it should be correct regardless of how
STABLE_BITchanges as long as the seqcount works. Given that the performance cost of doing that is zero, I'd rather keep it that way. If we're really paranoid, we could move it after the rest of the pvti reads and add a barrier, but is there really any host on which that matters?Quoted 5 lines
--Andy
--
Andy Lutomirski
AMA Capital Management, LLCRight, its OK due to version check, thanks.
Signature · 2 lines
Andy Lutomirski AMA Capital Management, LLC
Re: [PATCH] x86/vdso/pvclock: Protect STABLE check with the seqcount
Quoted 4 lines · Andy Lutomirski
Hi Ingo-
Can you apply this before the tip:x86/asm pull request goes out? It fixes a regression in tip:x86/asm.
Ooops, saw this mail too late - I'll merge this up into x86/urgent right now and send all pending fixes to Linus.
Thanks,
Ingo
tip-bot for Andy Lutomirski
merge bot[tip:x86/urgent] x86/vdso/pvclock: Protect STABLE check with the seqcount
In reply to Andy Lutomirskix86/vdso/pvclock: Protect STABLE check with the seqcount
Commit-ID: 78fd8c7288e0a4bba3ad1d69caf9396a6b69cb00 Gitweb: http://git.kernel.org/tip/78fd8c7288e0a4bba3ad1d69caf9396a6b69cb00 Author: Andy Lutomirski <[email protected]> AuthorDate: Mon, 4 Jan 2016 15:14:28 -0800 Committer: Ingo Molnar <[email protected]> CommitDate: Wed, 13 Jan 2016 11:46:29 +0100
x86/vdso/pvclock: Protect STABLE check with the seqcount
If the clock becomes unstable while we're reading it, we need to bail. We can do this by simply moving the check into the seqcount loop.
--- arch/x86/entry/vdso/vclock_gettime.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-)
arch/x86/entry/vdso/vclock_gettime.c +6 -6
@@ -126,23 +126,23 @@ static notrace cycle_t vread_pvclock(int *mode)* * On Xen, we don't appear to have that guarantee, but Xen still * supplies a valid seqlock using the version field. - +* * We only do pvclock vdso timing at all if * PVCLOCK_TSC_STABLE_BIT is set, and we interpret that bit to * mean that all vCPUs have matching pvti and that the TSC is * synced, so we can just look at vCPU 0's pvti. */ -if (unlikely (!(pvti->flags &PVCLOCK_TSC_STABLE_BIT ))) { - *mode =VCLOCK_NONE ; -return 0 ; - } - do { version = pvti->version; smp_rmb(); + if (unlikely(!(pvti->flags & PVCLOCK_TSC_STABLE_BIT))) { + *mode = VCLOCK_NONE; + return 0; + } + tsc = rdtsc_ordered(); pvti_tsc_to_system_mul = pvti->tsc_to_system_mul; pvti_tsc_shift = pvti->tsc_shift;
Andy Lutomirski
maintainerPATCH v2 2/4 x86/vdso: Get pvclock data from the vvar VMA instead of the fixmap
In reply to Andy Lutomirski--- arch/x86/entry/vdso/vclock_gettime.c | 20 ++++++++------------ arch/x86/entry/vdso/vdso-layout.lds.S | 3 ++- arch/x86/entry/vdso/vdso2c.c | 3 +++ arch/x86/entry/vdso/vma.c | 13 +++++++++++++ arch/x86/include/asm/pvclock.h | 9 +++++++++ arch/x86/include/asm/vdso.h | 1 + arch/x86/kernel/kvmclock.c | 5 +++++ 7 files changed, 41 insertions(+), 13 deletions(-)
arch/x86/entry/vdso/vclock_gettime.c +8 -12
@@ -36,6 +36,11 @@ static notrace cycle_t vread_hpet(void) }#endif +#ifdef CONFIG_PARAVIRT_CLOCK +extern u8 pvclock_page + __attribute__((visibility("hidden"))); +#endif +#ifndef BUILD_VDSO32 #include <linux/kernel.h>
@@ -62,23 +67,14 @@ notrace static long vdso_fallback_gtod(struct timeval *tv, struct timezone *tz)#ifdef CONFIG_PARAVIRT_CLOCK -static notrace const struct pvclock_vsyscall_time_info *get_pvti(int cpu) +static notrace const struct pvclock_vsyscall_time_info *get_pvti0(void) { -const struct pvclock_vsyscall_time_info *pvti_base; -int idx = cpu / (PAGE_SIZE /PVTI_SIZE ); -int offset = cpu % (PAGE_SIZE /PVTI_SIZE ); - -BUG_ON (PVCLOCK_FIXMAP_BEGIN + idx >PVCLOCK_FIXMAP_END ); - - pvti_base = (struct pvclock_vsyscall_time_info *) -__fix_to_virt (PVCLOCK_FIXMAP_BEGIN +idx); - -return &pvti_base[offset]; +return (const struct pvclock_vsyscall_time_info *)&pvclock_page; } static notrace cycle_t vread_pvclock(int *mode) { -const struct pvclock_vcpu_time_info *pvti = &get_pvti (0 )->pvti; +const struct pvclock_vcpu_time_info *pvti = &get_pvti0 ()->pvti;cycle_t ret;u64 tsc, pvti_tsc;u64 last, delta, pvti_system_time;
arch/x86/entry/vdso/vdso-layout.lds.S +2 -1
@@ -25,7 +25,7 @@ SECTIONS * segment. */ - vvar_start = . - 2 * PAGE_SIZE; + vvar_start = . - 3 * PAGE_SIZE; vvar_page = vvar_start; /* Place all vvars at the offsets in asm/vvar.h. */
@@ -36,6 +36,7 @@ SECTIONS #undef EMIT_VVAR hpet_page = vvar_start + PAGE_SIZE; + pvclock_page = vvar_start + 2 * PAGE_SIZE; . = SIZEOF_HEADERS;
arch/x86/entry/vdso/vdso2c.c +3 -0
@@ -73,6 +73,7 @@ enum { sym_vvar_start, sym_vvar_page, sym_hpet_page, + sym_pvclock_page, sym_VDSO_FAKE_SECTION_TABLE_START, sym_VDSO_FAKE_SECTION_TABLE_END, };
@@ -80,6 +81,7 @@ enum {const int special_pages[] = { sym_vvar_page, sym_hpet_page, + sym_pvclock_page, };struct vdso_sym {
@@ -91,6 +93,7 @@ struct vdso_sym required_syms[] = { [sym_vvar_start] = {"vvar_start", true}, [sym_vvar_page] = {"vvar_page", true}, [sym_hpet_page] = {"hpet_page", true}, + [sym_pvclock_page] = {"pvclock_page", true}, [sym_VDSO_FAKE_SECTION_TABLE_START] = {"VDSO_FAKE_SECTION_TABLE_START" , false },
arch/x86/entry/vdso/vma.c +13 -0
@@ -100,6 +100,7 @@ static int map_vdso(const struct vdso_image *image, bool calculate_addr) .name = "[vvar]", .pages = no_pages, }; +struct pvclock_vsyscall_time_info *pvti;if (calculate_addr) { addr = vdso_addr(current->mm->start_stack,
@@ -169,6 +170,18 @@ static int map_vdso(const struct vdso_image *image, bool calculate_addr) }#endif + pvti =pvclock_pvti_cpu0_va (); +if (pvti && image->sym_pvclock_page) { + ret =remap_pfn_range (vma, + text_start + image->sym_pvclock_page, +__pa (pvti) >>PAGE_SHIFT , +PAGE_SIZE , +PAGE_READONLY ); + +if (ret) + goto up_fail; + } + up_fail:if (ret) current->mm->context.vdso =NULL ;
arch/x86/include/asm/pvclock.h +9 -0
@@ -4,6 +4,15 @@#include <linux/clocksource.h> #include <asm/pvclock-abi.h> +#ifdef CONFIG_KVM_GUEST +extern struct pvclock_vsyscall_time_info *pvclock_pvti_cpu0_va (void ); +#else +static inline struct pvclock_vsyscall_time_info *pvclock_pvti_cpu0_va (void ) +{ +return NULL ; +} +#endif +/* some helper functions for xen and kvm pv clock sources */ cycle_t pvclock_clocksource_read (struct pvclock_vcpu_time_info *src);u8 pvclock_read_flags (struct pvclock_vcpu_time_info *src);
arch/x86/include/asm/vdso.h +1 -0
@@ -22,6 +22,7 @@ struct vdso_image {long sym_vvar_page;long sym_hpet_page; +long sym_pvclock_page;long sym_VDSO32_NOTE_MASK;long sym___kernel_sigreturn;long sym___kernel_rt_sigreturn;
arch/x86/kernel/kvmclock.c +5 -0
@@ -45,6 +45,11 @@ early_param("no-kvmclock", parse_no_kvmclock);static struct pvclock_vsyscall_time_info *hv_clock;static struct pvclock_wall_clock wall_clock; +struct pvclock_vsyscall_time_info *pvclock_pvti_cpu0_va (void ) +{ +return hv_clock; +} +/* * The wallclock is the time of day when we booted. Since then, some time may * have elapsed since the hypervisor wrote the data. So we try to account for
Signature · 1 line
2.5.0
Andy Lutomirski
maintainerPATCH v2 3/4 x86/vdso: Remove pvclock fixmap machinery
In reply to Andy Lutomirski--- arch/x86/entry/vdso/vclock_gettime.c | 1 - arch/x86/entry/vdso/vma.c | 1 + arch/x86/include/asm/fixmap.h | 5 ----- arch/x86/include/asm/pvclock.h | 5 ----- arch/x86/kernel/kvmclock.c | 6 ------ arch/x86/kernel/pvclock.c | 24 ------------------------ 6 files changed, 1 insertion(+), 41 deletions(-)
arch/x86/entry/vdso/vclock_gettime.c +0 -1
@@ -45,7 +45,6 @@ extern u8 pvclock_page#include <linux/kernel.h> #include <asm/vsyscall.h> -#include <asm/fixmap.h> #include <asm/pvclock.h> notrace static long vdso_fallback_gettime(long clock, struct timespec *ts)
arch/x86/entry/vdso/vma.c +1 -0
@@ -12,6 +12,7 @@#include <linux/random.h> #include <linux/elf.h> #include <linux/cpu.h> +#include <asm/pvclock.h> #include <asm/vgtod.h> #include <asm/proto.h> #include <asm/vdso.h>
arch/x86/include/asm/fixmap.h +0 -5
@@ -19,7 +19,6 @@#include <asm/acpi.h> #include <asm/apicdef.h> #include <asm/page.h> -#include <asm/pvclock.h> #ifdef CONFIG_X86_32 #include <linux/threads.h> #include <asm/kmap_types.h>
@@ -72,10 +71,6 @@ enum fixed_addresses { #ifdef CONFIG_X86_VSYSCALL_EMULATION VSYSCALL_PAGE = (FIXADDR_TOP - VSYSCALL_ADDR) >> PAGE_SHIFT, #endif -#ifdef CONFIG_PARAVIRT_CLOCK - PVCLOCK_FIXMAP_BEGIN, - PVCLOCK_FIXMAP_END = PVCLOCK_FIXMAP_BEGIN+PVCLOCK_VSYSCALL_NR_PAGES-1, -#endif #endif FIX_DBGP_BASE, FIX_EARLYCON_MEM_BASE,
arch/x86/include/asm/pvclock.h +0 -5
@@ -100,10 +100,5 @@ struct pvclock_vsyscall_time_info { } __attribute__((__aligned__(SMP_CACHE_BYTES)));#define PVTI_SIZE sizeof(struct pvclock_vsyscall_time_info) -#define PVCLOCK_VSYSCALL_NR_PAGES (((NR_CPUS-1)/(PAGE_SIZE/PVTI_SIZE))+1) - -int __init pvclock_init_vsyscall (struct pvclock_vsyscall_time_info *i, -int size); -struct pvclock_vcpu_time_info *pvclock_get_vsyscall_time_info (int cpu);#endif /* _ASM_X86_PVCLOCK_H */
arch/x86/kernel/kvmclock.c +0 -6
@@ -310,7 +310,6 @@ int __init kvm_setup_vsyscall_timeinfo(void) {#ifdef CONFIG_X86_64 int cpu; -int ret;u8 flags;struct pvclock_vcpu_time_info *vcpu_time;unsigned int size;
@@ -330,11 +329,6 @@ int __init kvm_setup_vsyscall_timeinfo(void)return 1 ; } -if ((ret =pvclock_init_vsyscall (hv_clock, size))) { -put_cpu (); -return ret; - } -put_cpu (); kvm_clock.archdata.vclock_mode =VCLOCK_PVCLOCK ;
arch/x86/kernel/pvclock.c +0 -24
@@ -140,27 +140,3 @@ void pvclock_read_wallclock(struct pvclock_wall_clock *wall_clock,set_normalized_timespec (ts, now.tv_sec, now.tv_nsec); } - -#ifdef CONFIG_X86_64 -/* -* Initialize the generic pvclock vsyscall state. This will allocate -* a/some page(s) for the per-vcpu pvclock information, set up a -* fixmap mapping for the page(s) -*/ - -int __init pvclock_init_vsyscall (struct pvclock_vsyscall_time_info *i, -int size) -{ -int idx; - -WARN_ON (size !=PVCLOCK_VSYSCALL_NR_PAGES *PAGE_SIZE ); - -for (idx =0 ; idx <= (PVCLOCK_FIXMAP_END -PVCLOCK_FIXMAP_BEGIN ); idx++) { -__set_fixmap (PVCLOCK_FIXMAP_BEGIN + idx, -__pa (i) + (idx*PAGE_SIZE ), -PAGE_KERNEL_VVAR ); - } - -return 0 ; -} -#endif
Signature · 1 line
2.5.0
Andy Lutomirski
maintainerPATCH v2 4/4 x86/vdso: Enable vdso pvclock access on all vdso variants
In reply to Andy LutomirskiNow that pvclock doesn't require access to the fixmap, all vdso variants can use it.
The kernel side isn't wired up for 32-bit kernels yet, but this covers 32-bit and x32 userspace on 64-bit kernels.
--- arch/x86/entry/vdso/vclock_gettime.c | 91 ++++++++++++++++-------------------- 1 file changed, 40 insertions(+), 51 deletions(-)
arch/x86/entry/vdso/vclock_gettime.c +40 -51
@@ -17,8 +17,10 @@#include <asm/vvar.h> #include <asm/unistd.h> #include <asm/msr.h> +#include <asm/pvclock.h> #include <linux/math64.h> #include <linux/time.h> +#include <linux/kernel.h> #define gtod (&VVAR(vsyscall_gtod_data))
@@ -43,10 +45,6 @@ extern u8 pvclock_page#ifndef BUILD_VDSO32 -#include <linux/kernel.h> -#include <asm/vsyscall.h> -#include <asm/pvclock.h> - notrace static long vdso_fallback_gettime(long clock, struct timespec *ts) {long ret;
@@ -64,8 +62,42 @@ notrace static long vdso_fallback_gtod(struct timeval *tv, struct timezone *tz)return ret; } -#ifdef CONFIG_PARAVIRT_CLOCK +#else + +notrace static long vdso_fallback_gettime(long clock, struct timespec *ts) +{ +long ret; + + asm( +"mov %%ebx, %%edx \n" +"mov %2, %%ebx \n" +"call __kernel_vsyscall \n" +"mov %%edx, %%ebx \n" + :"=a" (ret) + :"0" (__NR_clock_gettime),"g" (clock),"c" (ts) + :"memory" ,"edx" ); +return ret; +} + +notrace static long vdso_fallback_gtod(struct timeval *tv, struct timezone *tz) +{ +long ret; + + asm( +"mov %%ebx, %%edx \n" +"mov %2, %%ebx \n" +"call __kernel_vsyscall \n" +"mov %%edx, %%ebx \n" + :"=a" (ret) + :"0" (__NR_gettimeofday),"g" (tv),"c" (tz) + :"memory" ,"edx" ); +return ret; +} + +#endif + +#ifdef CONFIG_PARAVIRT_CLOCK static notrace const struct pvclock_vsyscall_time_info *get_pvti0(void) {return (const struct pvclock_vsyscall_time_info *)&pvclock_page;
@@ -109,9 +141,9 @@ static notrace cycle_t vread_pvclock(int *mode) do { version = pvti->version; - /* This is also a read barrier, so we'll read version first. */ - tsc = rdtsc_ordered(); + smp_rmb(); + tsc = rdtsc_ordered(); pvti_tsc_to_system_mul = pvti->tsc_to_system_mul; pvti_tsc_shift = pvti->tsc_shift; pvti_system_time = pvti->system_time;
@@ -126,7 +158,7 @@ static notrace cycle_t vread_pvclock(int *mode)pvclock_scale_delta (delta, pvti_tsc_to_system_mul, pvti_tsc_shift); -/* refer to tsc.c read_tsc() comment for rationale */ +/* refer to vread_tsc() comment for rationale */ last = gtod->cycle_last;if (likely (ret >= last))
@@ -136,49 +168,6 @@ static notrace cycle_t vread_pvclock(int *mode) }#endif -#else - -notrace static long vdso_fallback_gettime(long clock, struct timespec *ts) -{ -long ret; - - asm( -"mov %%ebx, %%edx \n" -"mov %2, %%ebx \n" -"call __kernel_vsyscall \n" -"mov %%edx, %%ebx \n" - :"=a" (ret) - :"0" (__NR_clock_gettime),"g" (clock),"c" (ts) - :"memory" ,"edx" ); -return ret; -} - -notrace static long vdso_fallback_gtod(struct timeval *tv, struct timezone *tz) -{ -long ret; - - asm( -"mov %%ebx, %%edx \n" -"mov %2, %%ebx \n" -"call __kernel_vsyscall \n" -"mov %%edx, %%ebx \n" - :"=a" (ret) - :"0" (__NR_gettimeofday),"g" (tv),"c" (tz) - :"memory" ,"edx" ); -return ret; -} - -#ifdef CONFIG_PARAVIRT_CLOCK - -static notrace cycle_t vread_pvclock(int *mode) -{ - *mode =VCLOCK_NONE ; -return 0 ; -} -#endif - -#endif - notrace static cycle_t vread_tsc(void) {cycle_t ret = (cycle_t )rdtsc_ordered ();
Signature · 1 line
2.5.0