diff options
author | Jens Freimann <jfrei@linux.vnet.ibm.com> | 2014-11-24 11:13:46 -0500 |
---|---|---|
committer | Christian Borntraeger <borntraeger@de.ibm.com> | 2015-03-31 15:07:31 -0400 |
commit | 816c7667ea97c61884e014cfeedaede5b67b0e58 (patch) | |
tree | 0be000b61900a44f9dabe81405b145cbb87b761d /arch/s390/kvm | |
parent | 79e87a103de1eda0cb4d726cd8581798e2d38f3e (diff) |
KVM: s390: migrate vcpu interrupt state
This patch adds support to migrate vcpu interrupts. Two new vcpu ioctls
are added which get/set the complete status of pending interrupts in one
go. The ioctls are marked as available with the new capability
KVM_CAP_S390_IRQ_STATE.
We can not use a ONEREG, as the number of pending local interrupts is not
constant and depends on the number of CPUs.
To retrieve the interrupt state we add an ioctl KVM_S390_GET_IRQ_STATE.
Its input parameter is a pointer to a struct kvm_s390_irq_state which
has a buffer and length. For all currently pending interrupts, we copy
a struct kvm_s390_irq into the buffer and pass it to userspace.
To store interrupt state into a buffer provided by userspace, we add an
ioctl KVM_S390_SET_IRQ_STATE. It passes a struct kvm_s390_irq_state into
the kernel and injects all interrupts contained in the buffer.
Signed-off-by: Jens Freimann <jfrei@linux.vnet.ibm.com>
Signed-off-by: Christian Borntraeger <borntraeger@de.ibm.com>
Acked-by: Cornelia Huck <cornelia.huck@de.ibm.com>
Diffstat (limited to 'arch/s390/kvm')
-rw-r--r-- | arch/s390/kvm/interrupt.c | 140 | ||||
-rw-r--r-- | arch/s390/kvm/kvm-s390.c | 36 | ||||
-rw-r--r-- | arch/s390/kvm/kvm-s390.h | 4 |
3 files changed, 180 insertions, 0 deletions
diff --git a/arch/s390/kvm/interrupt.c b/arch/s390/kvm/interrupt.c index bc0988093c5b..9de47265ef73 100644 --- a/arch/s390/kvm/interrupt.c +++ b/arch/s390/kvm/interrupt.c | |||
@@ -2123,3 +2123,143 @@ int kvm_set_msi(struct kvm_kernel_irq_routing_entry *e, struct kvm *kvm, | |||
2123 | { | 2123 | { |
2124 | return -EINVAL; | 2124 | return -EINVAL; |
2125 | } | 2125 | } |
2126 | |||
2127 | int kvm_s390_set_irq_state(struct kvm_vcpu *vcpu, void __user *irqstate, int len) | ||
2128 | { | ||
2129 | struct kvm_s390_local_interrupt *li = &vcpu->arch.local_int; | ||
2130 | struct kvm_s390_irq *buf; | ||
2131 | int r = 0; | ||
2132 | int n; | ||
2133 | |||
2134 | buf = vmalloc(len); | ||
2135 | if (!buf) | ||
2136 | return -ENOMEM; | ||
2137 | |||
2138 | if (copy_from_user((void *) buf, irqstate, len)) { | ||
2139 | r = -EFAULT; | ||
2140 | goto out_free; | ||
2141 | } | ||
2142 | |||
2143 | /* | ||
2144 | * Don't allow setting the interrupt state | ||
2145 | * when there are already interrupts pending | ||
2146 | */ | ||
2147 | spin_lock(&li->lock); | ||
2148 | if (li->pending_irqs) { | ||
2149 | r = -EBUSY; | ||
2150 | goto out_unlock; | ||
2151 | } | ||
2152 | |||
2153 | for (n = 0; n < len / sizeof(*buf); n++) { | ||
2154 | r = do_inject_vcpu(vcpu, &buf[n]); | ||
2155 | if (r) | ||
2156 | break; | ||
2157 | } | ||
2158 | |||
2159 | out_unlock: | ||
2160 | spin_unlock(&li->lock); | ||
2161 | out_free: | ||
2162 | vfree(buf); | ||
2163 | |||
2164 | return r; | ||
2165 | } | ||
2166 | |||
2167 | static void store_local_irq(struct kvm_s390_local_interrupt *li, | ||
2168 | struct kvm_s390_irq *irq, | ||
2169 | unsigned long irq_type) | ||
2170 | { | ||
2171 | switch (irq_type) { | ||
2172 | case IRQ_PEND_MCHK_EX: | ||
2173 | case IRQ_PEND_MCHK_REP: | ||
2174 | irq->type = KVM_S390_MCHK; | ||
2175 | irq->u.mchk = li->irq.mchk; | ||
2176 | break; | ||
2177 | case IRQ_PEND_PROG: | ||
2178 | irq->type = KVM_S390_PROGRAM_INT; | ||
2179 | irq->u.pgm = li->irq.pgm; | ||
2180 | break; | ||
2181 | case IRQ_PEND_PFAULT_INIT: | ||
2182 | irq->type = KVM_S390_INT_PFAULT_INIT; | ||
2183 | irq->u.ext = li->irq.ext; | ||
2184 | break; | ||
2185 | case IRQ_PEND_EXT_EXTERNAL: | ||
2186 | irq->type = KVM_S390_INT_EXTERNAL_CALL; | ||
2187 | irq->u.extcall = li->irq.extcall; | ||
2188 | break; | ||
2189 | case IRQ_PEND_EXT_CLOCK_COMP: | ||
2190 | irq->type = KVM_S390_INT_CLOCK_COMP; | ||
2191 | break; | ||
2192 | case IRQ_PEND_EXT_CPU_TIMER: | ||
2193 | irq->type = KVM_S390_INT_CPU_TIMER; | ||
2194 | break; | ||
2195 | case IRQ_PEND_SIGP_STOP: | ||
2196 | irq->type = KVM_S390_SIGP_STOP; | ||
2197 | irq->u.stop = li->irq.stop; | ||
2198 | break; | ||
2199 | case IRQ_PEND_RESTART: | ||
2200 | irq->type = KVM_S390_RESTART; | ||
2201 | break; | ||
2202 | case IRQ_PEND_SET_PREFIX: | ||
2203 | irq->type = KVM_S390_SIGP_SET_PREFIX; | ||
2204 | irq->u.prefix = li->irq.prefix; | ||
2205 | break; | ||
2206 | } | ||
2207 | } | ||
2208 | |||
2209 | int kvm_s390_get_irq_state(struct kvm_vcpu *vcpu, __u8 __user *buf, int len) | ||
2210 | { | ||
2211 | uint8_t sigp_ctrl = vcpu->kvm->arch.sca->cpu[vcpu->vcpu_id].sigp_ctrl; | ||
2212 | unsigned long sigp_emerg_pending[BITS_TO_LONGS(KVM_MAX_VCPUS)]; | ||
2213 | struct kvm_s390_local_interrupt *li = &vcpu->arch.local_int; | ||
2214 | unsigned long pending_irqs; | ||
2215 | struct kvm_s390_irq irq; | ||
2216 | unsigned long irq_type; | ||
2217 | int cpuaddr; | ||
2218 | int n = 0; | ||
2219 | |||
2220 | spin_lock(&li->lock); | ||
2221 | pending_irqs = li->pending_irqs; | ||
2222 | memcpy(&sigp_emerg_pending, &li->sigp_emerg_pending, | ||
2223 | sizeof(sigp_emerg_pending)); | ||
2224 | spin_unlock(&li->lock); | ||
2225 | |||
2226 | for_each_set_bit(irq_type, &pending_irqs, IRQ_PEND_COUNT) { | ||
2227 | memset(&irq, 0, sizeof(irq)); | ||
2228 | if (irq_type == IRQ_PEND_EXT_EMERGENCY) | ||
2229 | continue; | ||
2230 | if (n + sizeof(irq) > len) | ||
2231 | return -ENOBUFS; | ||
2232 | store_local_irq(&vcpu->arch.local_int, &irq, irq_type); | ||
2233 | if (copy_to_user(&buf[n], &irq, sizeof(irq))) | ||
2234 | return -EFAULT; | ||
2235 | n += sizeof(irq); | ||
2236 | } | ||
2237 | |||
2238 | if (test_bit(IRQ_PEND_EXT_EMERGENCY, &pending_irqs)) { | ||
2239 | for_each_set_bit(cpuaddr, sigp_emerg_pending, KVM_MAX_VCPUS) { | ||
2240 | memset(&irq, 0, sizeof(irq)); | ||
2241 | if (n + sizeof(irq) > len) | ||
2242 | return -ENOBUFS; | ||
2243 | irq.type = KVM_S390_INT_EMERGENCY; | ||
2244 | irq.u.emerg.code = cpuaddr; | ||
2245 | if (copy_to_user(&buf[n], &irq, sizeof(irq))) | ||
2246 | return -EFAULT; | ||
2247 | n += sizeof(irq); | ||
2248 | } | ||
2249 | } | ||
2250 | |||
2251 | if ((sigp_ctrl & SIGP_CTRL_C) && | ||
2252 | (atomic_read(&vcpu->arch.sie_block->cpuflags) & | ||
2253 | CPUSTAT_ECALL_PEND)) { | ||
2254 | if (n + sizeof(irq) > len) | ||
2255 | return -ENOBUFS; | ||
2256 | memset(&irq, 0, sizeof(irq)); | ||
2257 | irq.type = KVM_S390_INT_EXTERNAL_CALL; | ||
2258 | irq.u.extcall.code = sigp_ctrl & SIGP_CTRL_SCN_MASK; | ||
2259 | if (copy_to_user(&buf[n], &irq, sizeof(irq))) | ||
2260 | return -EFAULT; | ||
2261 | n += sizeof(irq); | ||
2262 | } | ||
2263 | |||
2264 | return n; | ||
2265 | } | ||
diff --git a/arch/s390/kvm/kvm-s390.c b/arch/s390/kvm/kvm-s390.c index 8bc25d405edf..3040b14751b8 100644 --- a/arch/s390/kvm/kvm-s390.c +++ b/arch/s390/kvm/kvm-s390.c | |||
@@ -41,6 +41,9 @@ | |||
41 | #include "trace-s390.h" | 41 | #include "trace-s390.h" |
42 | 42 | ||
43 | #define MEM_OP_MAX_SIZE 65536 /* Maximum transfer size for KVM_S390_MEM_OP */ | 43 | #define MEM_OP_MAX_SIZE 65536 /* Maximum transfer size for KVM_S390_MEM_OP */ |
44 | #define LOCAL_IRQS 32 | ||
45 | #define VCPU_IRQS_MAX_BUF (sizeof(struct kvm_s390_irq) * \ | ||
46 | (KVM_MAX_VCPUS + LOCAL_IRQS)) | ||
44 | 47 | ||
45 | #define VCPU_STAT(x) offsetof(struct kvm_vcpu, stat.x), KVM_STAT_VCPU | 48 | #define VCPU_STAT(x) offsetof(struct kvm_vcpu, stat.x), KVM_STAT_VCPU |
46 | 49 | ||
@@ -181,6 +184,7 @@ int kvm_vm_ioctl_check_extension(struct kvm *kvm, long ext) | |||
181 | case KVM_CAP_S390_USER_SIGP: | 184 | case KVM_CAP_S390_USER_SIGP: |
182 | case KVM_CAP_S390_USER_STSI: | 185 | case KVM_CAP_S390_USER_STSI: |
183 | case KVM_CAP_S390_SKEYS: | 186 | case KVM_CAP_S390_SKEYS: |
187 | case KVM_CAP_S390_IRQ_STATE: | ||
184 | r = 1; | 188 | r = 1; |
185 | break; | 189 | break; |
186 | case KVM_CAP_S390_MEM_OP: | 190 | case KVM_CAP_S390_MEM_OP: |
@@ -2500,6 +2504,38 @@ long kvm_arch_vcpu_ioctl(struct file *filp, | |||
2500 | r = -EFAULT; | 2504 | r = -EFAULT; |
2501 | break; | 2505 | break; |
2502 | } | 2506 | } |
2507 | case KVM_S390_SET_IRQ_STATE: { | ||
2508 | struct kvm_s390_irq_state irq_state; | ||
2509 | |||
2510 | r = -EFAULT; | ||
2511 | if (copy_from_user(&irq_state, argp, sizeof(irq_state))) | ||
2512 | break; | ||
2513 | if (irq_state.len > VCPU_IRQS_MAX_BUF || | ||
2514 | irq_state.len == 0 || | ||
2515 | irq_state.len % sizeof(struct kvm_s390_irq) > 0) { | ||
2516 | r = -EINVAL; | ||
2517 | break; | ||
2518 | } | ||
2519 | r = kvm_s390_set_irq_state(vcpu, | ||
2520 | (void __user *) irq_state.buf, | ||
2521 | irq_state.len); | ||
2522 | break; | ||
2523 | } | ||
2524 | case KVM_S390_GET_IRQ_STATE: { | ||
2525 | struct kvm_s390_irq_state irq_state; | ||
2526 | |||
2527 | r = -EFAULT; | ||
2528 | if (copy_from_user(&irq_state, argp, sizeof(irq_state))) | ||
2529 | break; | ||
2530 | if (irq_state.len == 0) { | ||
2531 | r = -EINVAL; | ||
2532 | break; | ||
2533 | } | ||
2534 | r = kvm_s390_get_irq_state(vcpu, | ||
2535 | (__u8 __user *) irq_state.buf, | ||
2536 | irq_state.len); | ||
2537 | break; | ||
2538 | } | ||
2503 | default: | 2539 | default: |
2504 | r = -ENOTTY; | 2540 | r = -ENOTTY; |
2505 | } | 2541 | } |
diff --git a/arch/s390/kvm/kvm-s390.h b/arch/s390/kvm/kvm-s390.h index 343644a59392..ca108b90ae56 100644 --- a/arch/s390/kvm/kvm-s390.h +++ b/arch/s390/kvm/kvm-s390.h | |||
@@ -272,6 +272,10 @@ int kvm_s390_ext_call_pending(struct kvm_vcpu *vcpu); | |||
272 | extern struct kvm_device_ops kvm_flic_ops; | 272 | extern struct kvm_device_ops kvm_flic_ops; |
273 | int kvm_s390_is_stop_irq_pending(struct kvm_vcpu *vcpu); | 273 | int kvm_s390_is_stop_irq_pending(struct kvm_vcpu *vcpu); |
274 | void kvm_s390_clear_stop_irq(struct kvm_vcpu *vcpu); | 274 | void kvm_s390_clear_stop_irq(struct kvm_vcpu *vcpu); |
275 | int kvm_s390_set_irq_state(struct kvm_vcpu *vcpu, | ||
276 | void __user *buf, int len); | ||
277 | int kvm_s390_get_irq_state(struct kvm_vcpu *vcpu, | ||
278 | __u8 __user *buf, int len); | ||
275 | 279 | ||
276 | /* implemented in guestdbg.c */ | 280 | /* implemented in guestdbg.c */ |
277 | void kvm_s390_backup_guest_per_regs(struct kvm_vcpu *vcpu); | 281 | void kvm_s390_backup_guest_per_regs(struct kvm_vcpu *vcpu); |