Skip to content

Commit ee1a15e

Browse files
committed
Merge tag 'kvm-s390-master-4.16-2' of git://git.kernel.org/pub/scm/linux/kernel/git/kvms390/linux into HEAD
KVM: s390: fixes for multiple epoch facility We have certain cases where the multiple epoch facility is broken: - timer wakeup during epoch change - cpu hotplug - SCK instruction - stp sync checks Fix those.
2 parents 6c62cc4 + 0e7def5 commit ee1a15e

File tree

4 files changed

+71
-47
lines changed

4 files changed

+71
-47
lines changed

arch/s390/kvm/interrupt.c

Lines changed: 19 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -169,8 +169,15 @@ static int ckc_interrupts_enabled(struct kvm_vcpu *vcpu)
169169

170170
static int ckc_irq_pending(struct kvm_vcpu *vcpu)
171171
{
172-
if (vcpu->arch.sie_block->ckc >= kvm_s390_get_tod_clock_fast(vcpu->kvm))
172+
const u64 now = kvm_s390_get_tod_clock_fast(vcpu->kvm);
173+
const u64 ckc = vcpu->arch.sie_block->ckc;
174+
175+
if (vcpu->arch.sie_block->gcr[0] & 0x0020000000000000ul) {
176+
if ((s64)ckc >= (s64)now)
177+
return 0;
178+
} else if (ckc >= now) {
173179
return 0;
180+
}
174181
return ckc_interrupts_enabled(vcpu);
175182
}
176183

@@ -1047,13 +1054,19 @@ int kvm_cpu_has_pending_timer(struct kvm_vcpu *vcpu)
10471054

10481055
static u64 __calculate_sltime(struct kvm_vcpu *vcpu)
10491056
{
1050-
u64 now, cputm, sltime = 0;
1057+
const u64 now = kvm_s390_get_tod_clock_fast(vcpu->kvm);
1058+
const u64 ckc = vcpu->arch.sie_block->ckc;
1059+
u64 cputm, sltime = 0;
10511060

10521061
if (ckc_interrupts_enabled(vcpu)) {
1053-
now = kvm_s390_get_tod_clock_fast(vcpu->kvm);
1054-
sltime = tod_to_ns(vcpu->arch.sie_block->ckc - now);
1055-
/* already expired or overflow? */
1056-
if (!sltime || vcpu->arch.sie_block->ckc <= now)
1062+
if (vcpu->arch.sie_block->gcr[0] & 0x0020000000000000ul) {
1063+
if ((s64)now < (s64)ckc)
1064+
sltime = tod_to_ns((s64)ckc - (s64)now);
1065+
} else if (now < ckc) {
1066+
sltime = tod_to_ns(ckc - now);
1067+
}
1068+
/* already expired */
1069+
if (!sltime)
10571070
return 0;
10581071
if (cpu_timer_interrupts_enabled(vcpu)) {
10591072
cputm = kvm_s390_get_cpu_timer(vcpu);

arch/s390/kvm/kvm-s390.c

Lines changed: 45 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -179,6 +179,28 @@ int kvm_arch_hardware_enable(void)
179179
static void kvm_gmap_notifier(struct gmap *gmap, unsigned long start,
180180
unsigned long end);
181181

182+
static void kvm_clock_sync_scb(struct kvm_s390_sie_block *scb, u64 delta)
183+
{
184+
u8 delta_idx = 0;
185+
186+
/*
187+
* The TOD jumps by delta, we have to compensate this by adding
188+
* -delta to the epoch.
189+
*/
190+
delta = -delta;
191+
192+
/* sign-extension - we're adding to signed values below */
193+
if ((s64)delta < 0)
194+
delta_idx = -1;
195+
196+
scb->epoch += delta;
197+
if (scb->ecd & ECD_MEF) {
198+
scb->epdx += delta_idx;
199+
if (scb->epoch < delta)
200+
scb->epdx += 1;
201+
}
202+
}
203+
182204
/*
183205
* This callback is executed during stop_machine(). All CPUs are therefore
184206
* temporarily stopped. In order not to change guest behavior, we have to
@@ -194,13 +216,17 @@ static int kvm_clock_sync(struct notifier_block *notifier, unsigned long val,
194216
unsigned long long *delta = v;
195217

196218
list_for_each_entry(kvm, &vm_list, vm_list) {
197-
kvm->arch.epoch -= *delta;
198219
kvm_for_each_vcpu(i, vcpu, kvm) {
199-
vcpu->arch.sie_block->epoch -= *delta;
220+
kvm_clock_sync_scb(vcpu->arch.sie_block, *delta);
221+
if (i == 0) {
222+
kvm->arch.epoch = vcpu->arch.sie_block->epoch;
223+
kvm->arch.epdx = vcpu->arch.sie_block->epdx;
224+
}
200225
if (vcpu->arch.cputm_enabled)
201226
vcpu->arch.cputm_start += *delta;
202227
if (vcpu->arch.vsie_block)
203-
vcpu->arch.vsie_block->epoch -= *delta;
228+
kvm_clock_sync_scb(vcpu->arch.vsie_block,
229+
*delta);
204230
}
205231
}
206232
return NOTIFY_OK;
@@ -902,12 +928,9 @@ static int kvm_s390_set_tod_ext(struct kvm *kvm, struct kvm_device_attr *attr)
902928
if (copy_from_user(&gtod, (void __user *)attr->addr, sizeof(gtod)))
903929
return -EFAULT;
904930

905-
if (test_kvm_facility(kvm, 139))
906-
kvm_s390_set_tod_clock_ext(kvm, &gtod);
907-
else if (gtod.epoch_idx == 0)
908-
kvm_s390_set_tod_clock(kvm, gtod.tod);
909-
else
931+
if (!test_kvm_facility(kvm, 139) && gtod.epoch_idx)
910932
return -EINVAL;
933+
kvm_s390_set_tod_clock(kvm, &gtod);
911934

912935
VM_EVENT(kvm, 3, "SET: TOD extension: 0x%x, TOD base: 0x%llx",
913936
gtod.epoch_idx, gtod.tod);
@@ -932,13 +955,14 @@ static int kvm_s390_set_tod_high(struct kvm *kvm, struct kvm_device_attr *attr)
932955

933956
static int kvm_s390_set_tod_low(struct kvm *kvm, struct kvm_device_attr *attr)
934957
{
935-
u64 gtod;
958+
struct kvm_s390_vm_tod_clock gtod = { 0 };
936959

937-
if (copy_from_user(&gtod, (void __user *)attr->addr, sizeof(gtod)))
960+
if (copy_from_user(&gtod.tod, (void __user *)attr->addr,
961+
sizeof(gtod.tod)))
938962
return -EFAULT;
939963

940-
kvm_s390_set_tod_clock(kvm, gtod);
941-
VM_EVENT(kvm, 3, "SET: TOD base: 0x%llx", gtod);
964+
kvm_s390_set_tod_clock(kvm, &gtod);
965+
VM_EVENT(kvm, 3, "SET: TOD base: 0x%llx", gtod.tod);
942966
return 0;
943967
}
944968

@@ -2389,6 +2413,7 @@ void kvm_arch_vcpu_postcreate(struct kvm_vcpu *vcpu)
23892413
mutex_lock(&vcpu->kvm->lock);
23902414
preempt_disable();
23912415
vcpu->arch.sie_block->epoch = vcpu->kvm->arch.epoch;
2416+
vcpu->arch.sie_block->epdx = vcpu->kvm->arch.epdx;
23922417
preempt_enable();
23932418
mutex_unlock(&vcpu->kvm->lock);
23942419
if (!kvm_is_ucontrol(vcpu->kvm)) {
@@ -3021,8 +3046,8 @@ static int kvm_s390_handle_requests(struct kvm_vcpu *vcpu)
30213046
return 0;
30223047
}
30233048

3024-
void kvm_s390_set_tod_clock_ext(struct kvm *kvm,
3025-
const struct kvm_s390_vm_tod_clock *gtod)
3049+
void kvm_s390_set_tod_clock(struct kvm *kvm,
3050+
const struct kvm_s390_vm_tod_clock *gtod)
30263051
{
30273052
struct kvm_vcpu *vcpu;
30283053
struct kvm_s390_tod_clock_ext htod;
@@ -3034,10 +3059,12 @@ void kvm_s390_set_tod_clock_ext(struct kvm *kvm,
30343059
get_tod_clock_ext((char *)&htod);
30353060

30363061
kvm->arch.epoch = gtod->tod - htod.tod;
3037-
kvm->arch.epdx = gtod->epoch_idx - htod.epoch_idx;
3038-
3039-
if (kvm->arch.epoch > gtod->tod)
3040-
kvm->arch.epdx -= 1;
3062+
kvm->arch.epdx = 0;
3063+
if (test_kvm_facility(kvm, 139)) {
3064+
kvm->arch.epdx = gtod->epoch_idx - htod.epoch_idx;
3065+
if (kvm->arch.epoch > gtod->tod)
3066+
kvm->arch.epdx -= 1;
3067+
}
30413068

30423069
kvm_s390_vcpu_block_all(kvm);
30433070
kvm_for_each_vcpu(i, vcpu, kvm) {
@@ -3050,22 +3077,6 @@ void kvm_s390_set_tod_clock_ext(struct kvm *kvm,
30503077
mutex_unlock(&kvm->lock);
30513078
}
30523079

3053-
void kvm_s390_set_tod_clock(struct kvm *kvm, u64 tod)
3054-
{
3055-
struct kvm_vcpu *vcpu;
3056-
int i;
3057-
3058-
mutex_lock(&kvm->lock);
3059-
preempt_disable();
3060-
kvm->arch.epoch = tod - get_tod_clock();
3061-
kvm_s390_vcpu_block_all(kvm);
3062-
kvm_for_each_vcpu(i, vcpu, kvm)
3063-
vcpu->arch.sie_block->epoch = kvm->arch.epoch;
3064-
kvm_s390_vcpu_unblock_all(kvm);
3065-
preempt_enable();
3066-
mutex_unlock(&kvm->lock);
3067-
}
3068-
30693080
/**
30703081
* kvm_arch_fault_in_page - fault-in guest page if necessary
30713082
* @vcpu: The corresponding virtual cpu

arch/s390/kvm/kvm-s390.h

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -281,9 +281,8 @@ int kvm_s390_handle_sigp(struct kvm_vcpu *vcpu);
281281
int kvm_s390_handle_sigp_pei(struct kvm_vcpu *vcpu);
282282

283283
/* implemented in kvm-s390.c */
284-
void kvm_s390_set_tod_clock_ext(struct kvm *kvm,
285-
const struct kvm_s390_vm_tod_clock *gtod);
286-
void kvm_s390_set_tod_clock(struct kvm *kvm, u64 tod);
284+
void kvm_s390_set_tod_clock(struct kvm *kvm,
285+
const struct kvm_s390_vm_tod_clock *gtod);
287286
long kvm_arch_fault_in_page(struct kvm_vcpu *vcpu, gpa_t gpa, int writable);
288287
int kvm_s390_store_status_unloaded(struct kvm_vcpu *vcpu, unsigned long addr);
289288
int kvm_s390_vcpu_store_status(struct kvm_vcpu *vcpu, unsigned long addr);

arch/s390/kvm/priv.c

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -85,9 +85,10 @@ int kvm_s390_handle_e3(struct kvm_vcpu *vcpu)
8585
/* Handle SCK (SET CLOCK) interception */
8686
static int handle_set_clock(struct kvm_vcpu *vcpu)
8787
{
88+
struct kvm_s390_vm_tod_clock gtod = { 0 };
8889
int rc;
8990
u8 ar;
90-
u64 op2, val;
91+
u64 op2;
9192

9293
vcpu->stat.instruction_sck++;
9394

@@ -97,12 +98,12 @@ static int handle_set_clock(struct kvm_vcpu *vcpu)
9798
op2 = kvm_s390_get_base_disp_s(vcpu, &ar);
9899
if (op2 & 7) /* Operand must be on a doubleword boundary */
99100
return kvm_s390_inject_program_int(vcpu, PGM_SPECIFICATION);
100-
rc = read_guest(vcpu, op2, ar, &val, sizeof(val));
101+
rc = read_guest(vcpu, op2, ar, &gtod.tod, sizeof(gtod.tod));
101102
if (rc)
102103
return kvm_s390_inject_prog_cond(vcpu, rc);
103104

104-
VCPU_EVENT(vcpu, 3, "SCK: setting guest TOD to 0x%llx", val);
105-
kvm_s390_set_tod_clock(vcpu->kvm, val);
105+
VCPU_EVENT(vcpu, 3, "SCK: setting guest TOD to 0x%llx", gtod.tod);
106+
kvm_s390_set_tod_clock(vcpu->kvm, &gtod);
106107

107108
kvm_s390_set_psw_cc(vcpu, 0);
108109
return 0;

0 commit comments

Comments
 (0)