diff options
author | David Hildenbrand <dahi@linux.vnet.ibm.com> | 2016-02-17 15:53:33 -0500 |
---|---|---|
committer | Christian Borntraeger <borntraeger@de.ibm.com> | 2016-03-08 07:57:53 -0500 |
commit | 9c23a1318eb12fcf76d9f663d2c3d88598e62a55 (patch) | |
tree | 54451983c32ccdc6e28bfa58f69f0d5ebd1438f6 | |
parent | db0758b29709815d93a963e31e2ec87ecf74f8bd (diff) |
KVM: s390: protect VCPU cpu timer with a seqcount
For now, only the owning VCPU thread (that has loaded the VCPU) can get a
consistent cpu timer value when calculating the delta. However, other
threads might also be interested in a more recent, consistent value. Of
special interest will be the timer callback of a VCPU that executes without
having the VCPU loaded and could run in parallel with the VCPU thread.
The cpu timer has a nice property: it is only updated by the owning VCPU
thread. And speaking about accounting, a consistent value can only be
calculated by looking at cputm_start and the cpu timer itself in
one shot, otherwise the result might be wrong.
As we only have one writing thread at a time (owning VCPU thread), we can
use a seqcount instead of a seqlock and retry if the VCPU refreshed its
cpu timer. This avoids any heavy locking and only introduces a counter
update/check plus a handful of smp_wmb().
The owning VCPU thread should never have to retry on reads, and also for
other threads this might be a very rare scenario.
Please note that we have to use the raw_* variants for locking the seqcount
as lockdep will produce false warnings otherwise. The rq->lock held during
vcpu_load/put is also acquired from hardirq context. Lockdep cannot know
that we avoid potential deadlocks by disabling preemption and thereby
disable concurrent write locking attempts (via vcpu_put/load).
Reviewed-by: Christian Borntraeger <borntraeger@de.ibm.com>
Signed-off-by: David Hildenbrand <dahi@linux.vnet.ibm.com>
Signed-off-by: Christian Borntraeger <borntraeger@de.ibm.com>
-rw-r--r-- | arch/s390/include/asm/kvm_host.h | 8 | ||||
-rw-r--r-- | arch/s390/kvm/kvm-s390.c | 30 |
2 files changed, 30 insertions, 8 deletions
diff --git a/arch/s390/include/asm/kvm_host.h b/arch/s390/include/asm/kvm_host.h index 91796dd2a8ec..d61e64555938 100644 --- a/arch/s390/include/asm/kvm_host.h +++ b/arch/s390/include/asm/kvm_host.h | |||
@@ -20,6 +20,7 @@ | |||
20 | #include <linux/kvm_types.h> | 20 | #include <linux/kvm_types.h> |
21 | #include <linux/kvm_host.h> | 21 | #include <linux/kvm_host.h> |
22 | #include <linux/kvm.h> | 22 | #include <linux/kvm.h> |
23 | #include <linux/seqlock.h> | ||
23 | #include <asm/debug.h> | 24 | #include <asm/debug.h> |
24 | #include <asm/cpu.h> | 25 | #include <asm/cpu.h> |
25 | #include <asm/fpu/api.h> | 26 | #include <asm/fpu/api.h> |
@@ -553,6 +554,13 @@ struct kvm_vcpu_arch { | |||
553 | unsigned long pfault_select; | 554 | unsigned long pfault_select; |
554 | unsigned long pfault_compare; | 555 | unsigned long pfault_compare; |
555 | bool cputm_enabled; | 556 | bool cputm_enabled; |
557 | /* | ||
558 | * The seqcount protects updates to cputm_start and sie_block.cputm, | ||
559 | * this way we can have non-blocking reads with consistent values. | ||
560 | * Only the owning VCPU thread (vcpu->cpu) is allowed to change these | ||
561 | * values and to start/stop/enable/disable cpu timer accounting. | ||
562 | */ | ||
563 | seqcount_t cputm_seqcount; | ||
556 | __u64 cputm_start; | 564 | __u64 cputm_start; |
557 | }; | 565 | }; |
558 | 566 | ||
diff --git a/arch/s390/kvm/kvm-s390.c b/arch/s390/kvm/kvm-s390.c index 76b99149dc65..38223c4603c7 100644 --- a/arch/s390/kvm/kvm-s390.c +++ b/arch/s390/kvm/kvm-s390.c | |||
@@ -1435,15 +1435,19 @@ int kvm_arch_vcpu_init(struct kvm_vcpu *vcpu) | |||
1435 | static void __start_cpu_timer_accounting(struct kvm_vcpu *vcpu) | 1435 | static void __start_cpu_timer_accounting(struct kvm_vcpu *vcpu) |
1436 | { | 1436 | { |
1437 | WARN_ON_ONCE(vcpu->arch.cputm_start != 0); | 1437 | WARN_ON_ONCE(vcpu->arch.cputm_start != 0); |
1438 | raw_write_seqcount_begin(&vcpu->arch.cputm_seqcount); | ||
1438 | vcpu->arch.cputm_start = get_tod_clock_fast(); | 1439 | vcpu->arch.cputm_start = get_tod_clock_fast(); |
1440 | raw_write_seqcount_end(&vcpu->arch.cputm_seqcount); | ||
1439 | } | 1441 | } |
1440 | 1442 | ||
1441 | /* needs disabled preemption to protect from TOD sync and vcpu_load/put */ | 1443 | /* needs disabled preemption to protect from TOD sync and vcpu_load/put */ |
1442 | static void __stop_cpu_timer_accounting(struct kvm_vcpu *vcpu) | 1444 | static void __stop_cpu_timer_accounting(struct kvm_vcpu *vcpu) |
1443 | { | 1445 | { |
1444 | WARN_ON_ONCE(vcpu->arch.cputm_start == 0); | 1446 | WARN_ON_ONCE(vcpu->arch.cputm_start == 0); |
1447 | raw_write_seqcount_begin(&vcpu->arch.cputm_seqcount); | ||
1445 | vcpu->arch.sie_block->cputm -= get_tod_clock_fast() - vcpu->arch.cputm_start; | 1448 | vcpu->arch.sie_block->cputm -= get_tod_clock_fast() - vcpu->arch.cputm_start; |
1446 | vcpu->arch.cputm_start = 0; | 1449 | vcpu->arch.cputm_start = 0; |
1450 | raw_write_seqcount_end(&vcpu->arch.cputm_seqcount); | ||
1447 | } | 1451 | } |
1448 | 1452 | ||
1449 | /* needs disabled preemption to protect from TOD sync and vcpu_load/put */ | 1453 | /* needs disabled preemption to protect from TOD sync and vcpu_load/put */ |
@@ -1480,28 +1484,37 @@ static void disable_cpu_timer_accounting(struct kvm_vcpu *vcpu) | |||
1480 | void kvm_s390_set_cpu_timer(struct kvm_vcpu *vcpu, __u64 cputm) | 1484 | void kvm_s390_set_cpu_timer(struct kvm_vcpu *vcpu, __u64 cputm) |
1481 | { | 1485 | { |
1482 | preempt_disable(); /* protect from TOD sync and vcpu_load/put */ | 1486 | preempt_disable(); /* protect from TOD sync and vcpu_load/put */ |
1487 | raw_write_seqcount_begin(&vcpu->arch.cputm_seqcount); | ||
1483 | if (vcpu->arch.cputm_enabled) | 1488 | if (vcpu->arch.cputm_enabled) |
1484 | vcpu->arch.cputm_start = get_tod_clock_fast(); | 1489 | vcpu->arch.cputm_start = get_tod_clock_fast(); |
1485 | vcpu->arch.sie_block->cputm = cputm; | 1490 | vcpu->arch.sie_block->cputm = cputm; |
1491 | raw_write_seqcount_end(&vcpu->arch.cputm_seqcount); | ||
1486 | preempt_enable(); | 1492 | preempt_enable(); |
1487 | } | 1493 | } |
1488 | 1494 | ||
1489 | /* update and get the cpu timer - can also be called from other VCPU threads */ | 1495 | /* update and get the cpu timer - can also be called from other VCPU threads */ |
1490 | __u64 kvm_s390_get_cpu_timer(struct kvm_vcpu *vcpu) | 1496 | __u64 kvm_s390_get_cpu_timer(struct kvm_vcpu *vcpu) |
1491 | { | 1497 | { |
1498 | unsigned int seq; | ||
1492 | __u64 value; | 1499 | __u64 value; |
1493 | int me; | ||
1494 | 1500 | ||
1495 | if (unlikely(!vcpu->arch.cputm_enabled)) | 1501 | if (unlikely(!vcpu->arch.cputm_enabled)) |
1496 | return vcpu->arch.sie_block->cputm; | 1502 | return vcpu->arch.sie_block->cputm; |
1497 | 1503 | ||
1498 | me = get_cpu(); /* also protects from TOD sync and vcpu_load/put */ | 1504 | preempt_disable(); /* protect from TOD sync and vcpu_load/put */ |
1499 | value = vcpu->arch.sie_block->cputm; | 1505 | do { |
1500 | if (likely(me == vcpu->cpu)) { | 1506 | seq = raw_read_seqcount(&vcpu->arch.cputm_seqcount); |
1501 | /* the VCPU itself will always read consistent values */ | 1507 | /* |
1502 | value -= get_tod_clock_fast() - vcpu->arch.cputm_start; | 1508 | * If the writer would ever execute a read in the critical |
1503 | } | 1509 | * section, e.g. in irq context, we have a deadlock. |
1504 | put_cpu(); | 1510 | */ |
1511 | WARN_ON_ONCE((seq & 1) && smp_processor_id() == vcpu->cpu); | ||
1512 | value = vcpu->arch.sie_block->cputm; | ||
1513 | /* if cputm_start is 0, accounting is being started/stopped */ | ||
1514 | if (likely(vcpu->arch.cputm_start)) | ||
1515 | value -= get_tod_clock_fast() - vcpu->arch.cputm_start; | ||
1516 | } while (read_seqcount_retry(&vcpu->arch.cputm_seqcount, seq & ~1)); | ||
1517 | preempt_enable(); | ||
1505 | return value; | 1518 | return value; |
1506 | } | 1519 | } |
1507 | 1520 | ||
@@ -1704,6 +1717,7 @@ struct kvm_vcpu *kvm_arch_vcpu_create(struct kvm *kvm, | |||
1704 | vcpu->arch.local_int.float_int = &kvm->arch.float_int; | 1717 | vcpu->arch.local_int.float_int = &kvm->arch.float_int; |
1705 | vcpu->arch.local_int.wq = &vcpu->wq; | 1718 | vcpu->arch.local_int.wq = &vcpu->wq; |
1706 | vcpu->arch.local_int.cpuflags = &vcpu->arch.sie_block->cpuflags; | 1719 | vcpu->arch.local_int.cpuflags = &vcpu->arch.sie_block->cpuflags; |
1720 | seqcount_init(&vcpu->arch.cputm_seqcount); | ||
1707 | 1721 | ||
1708 | rc = kvm_vcpu_init(vcpu, kvm, id); | 1722 | rc = kvm_vcpu_init(vcpu, kvm, id); |
1709 | if (rc) | 1723 | if (rc) |