aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDavid Hildenbrand <dahi@linux.vnet.ibm.com>2016-02-17 15:53:33 -0500
committerChristian Borntraeger <borntraeger@de.ibm.com>2016-03-08 07:57:53 -0500
commit9c23a1318eb12fcf76d9f663d2c3d88598e62a55 (patch)
tree54451983c32ccdc6e28bfa58f69f0d5ebd1438f6
parentdb0758b29709815d93a963e31e2ec87ecf74f8bd (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.h8
-rw-r--r--arch/s390/kvm/kvm-s390.c30
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)
1435static void __start_cpu_timer_accounting(struct kvm_vcpu *vcpu) 1435static 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 */
1442static void __stop_cpu_timer_accounting(struct kvm_vcpu *vcpu) 1444static 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)
1480void kvm_s390_set_cpu_timer(struct kvm_vcpu *vcpu, __u64 cputm) 1484void 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)