diff options
author | Heiko Carstens <heiko.carstens@de.ibm.com> | 2014-01-10 08:33:28 -0500 |
---|---|---|
committer | Christian Borntraeger <borntraeger@de.ibm.com> | 2014-04-22 07:24:39 -0400 |
commit | 8a242234b4bfed37f7fbd9b0b16f8088f31ca140 (patch) | |
tree | b7dbea23f97a08e2e45512f20d546521dfcd6fb9 | |
parent | 217a440683b51463f53e397cfdda27d7e92bf275 (diff) |
KVM: s390: make use of ipte lock
Signed-off-by: Heiko Carstens <heiko.carstens@de.ibm.com>
Reviewed-by: Thomas Huth <thuth@linux.vnet.ibm.com>
Signed-off-by: Christian Borntraeger <borntraeger@de.ibm.com>
-rw-r--r-- | arch/s390/include/asm/kvm_host.h | 12 | ||||
-rw-r--r-- | arch/s390/kvm/gaccess.c | 109 | ||||
-rw-r--r-- | arch/s390/kvm/gaccess.h | 2 | ||||
-rw-r--r-- | arch/s390/kvm/kvm-s390.c | 2 | ||||
-rw-r--r-- | arch/s390/kvm/priv.c | 18 |
5 files changed, 142 insertions, 1 deletions
diff --git a/arch/s390/include/asm/kvm_host.h b/arch/s390/include/asm/kvm_host.h index c290d443d2c1..f1ed7bdba733 100644 --- a/arch/s390/include/asm/kvm_host.h +++ b/arch/s390/include/asm/kvm_host.h | |||
@@ -39,9 +39,17 @@ struct sca_entry { | |||
39 | __u64 reserved2[2]; | 39 | __u64 reserved2[2]; |
40 | } __attribute__((packed)); | 40 | } __attribute__((packed)); |
41 | 41 | ||
42 | union ipte_control { | ||
43 | unsigned long val; | ||
44 | struct { | ||
45 | unsigned long k : 1; | ||
46 | unsigned long kh : 31; | ||
47 | unsigned long kg : 32; | ||
48 | }; | ||
49 | }; | ||
42 | 50 | ||
43 | struct sca_block { | 51 | struct sca_block { |
44 | __u64 ipte_control; | 52 | union ipte_control ipte_control; |
45 | __u64 reserved[5]; | 53 | __u64 reserved[5]; |
46 | __u64 mcn; | 54 | __u64 mcn; |
47 | __u64 reserved2; | 55 | __u64 reserved2; |
@@ -167,6 +175,7 @@ struct kvm_vcpu_stat { | |||
167 | u32 instruction_stpx; | 175 | u32 instruction_stpx; |
168 | u32 instruction_stap; | 176 | u32 instruction_stap; |
169 | u32 instruction_storage_key; | 177 | u32 instruction_storage_key; |
178 | u32 instruction_ipte_interlock; | ||
170 | u32 instruction_stsch; | 179 | u32 instruction_stsch; |
171 | u32 instruction_chsc; | 180 | u32 instruction_chsc; |
172 | u32 instruction_stsi; | 181 | u32 instruction_stsi; |
@@ -336,6 +345,7 @@ struct kvm_arch{ | |||
336 | int use_irqchip; | 345 | int use_irqchip; |
337 | int use_cmma; | 346 | int use_cmma; |
338 | struct s390_io_adapter *adapters[MAX_S390_IO_ADAPTERS]; | 347 | struct s390_io_adapter *adapters[MAX_S390_IO_ADAPTERS]; |
348 | wait_queue_head_t ipte_wq; | ||
339 | }; | 349 | }; |
340 | 350 | ||
341 | #define KVM_HVA_ERR_BAD (-1UL) | 351 | #define KVM_HVA_ERR_BAD (-1UL) |
diff --git a/arch/s390/kvm/gaccess.c b/arch/s390/kvm/gaccess.c index 916e1ee1f8c9..691fdb776c90 100644 --- a/arch/s390/kvm/gaccess.c +++ b/arch/s390/kvm/gaccess.c | |||
@@ -207,6 +207,107 @@ union raddress { | |||
207 | unsigned long pfra : 52; /* Page-Frame Real Address */ | 207 | unsigned long pfra : 52; /* Page-Frame Real Address */ |
208 | }; | 208 | }; |
209 | 209 | ||
210 | static int ipte_lock_count; | ||
211 | static DEFINE_MUTEX(ipte_mutex); | ||
212 | |||
213 | int ipte_lock_held(struct kvm_vcpu *vcpu) | ||
214 | { | ||
215 | union ipte_control *ic = &vcpu->kvm->arch.sca->ipte_control; | ||
216 | |||
217 | if (vcpu->arch.sie_block->eca & 1) | ||
218 | return ic->kh != 0; | ||
219 | return ipte_lock_count != 0; | ||
220 | } | ||
221 | |||
222 | static void ipte_lock_simple(struct kvm_vcpu *vcpu) | ||
223 | { | ||
224 | union ipte_control old, new, *ic; | ||
225 | |||
226 | mutex_lock(&ipte_mutex); | ||
227 | ipte_lock_count++; | ||
228 | if (ipte_lock_count > 1) | ||
229 | goto out; | ||
230 | ic = &vcpu->kvm->arch.sca->ipte_control; | ||
231 | do { | ||
232 | old = ACCESS_ONCE(*ic); | ||
233 | while (old.k) { | ||
234 | cond_resched(); | ||
235 | old = ACCESS_ONCE(*ic); | ||
236 | } | ||
237 | new = old; | ||
238 | new.k = 1; | ||
239 | } while (cmpxchg(&ic->val, old.val, new.val) != old.val); | ||
240 | out: | ||
241 | mutex_unlock(&ipte_mutex); | ||
242 | } | ||
243 | |||
244 | static void ipte_unlock_simple(struct kvm_vcpu *vcpu) | ||
245 | { | ||
246 | union ipte_control old, new, *ic; | ||
247 | |||
248 | mutex_lock(&ipte_mutex); | ||
249 | ipte_lock_count--; | ||
250 | if (ipte_lock_count) | ||
251 | goto out; | ||
252 | ic = &vcpu->kvm->arch.sca->ipte_control; | ||
253 | do { | ||
254 | new = old = ACCESS_ONCE(*ic); | ||
255 | new.k = 0; | ||
256 | } while (cmpxchg(&ic->val, old.val, new.val) != old.val); | ||
257 | if (!ipte_lock_count) | ||
258 | wake_up(&vcpu->kvm->arch.ipte_wq); | ||
259 | out: | ||
260 | mutex_unlock(&ipte_mutex); | ||
261 | } | ||
262 | |||
263 | static void ipte_lock_siif(struct kvm_vcpu *vcpu) | ||
264 | { | ||
265 | union ipte_control old, new, *ic; | ||
266 | |||
267 | ic = &vcpu->kvm->arch.sca->ipte_control; | ||
268 | do { | ||
269 | old = ACCESS_ONCE(*ic); | ||
270 | while (old.kg) { | ||
271 | cond_resched(); | ||
272 | old = ACCESS_ONCE(*ic); | ||
273 | } | ||
274 | new = old; | ||
275 | new.k = 1; | ||
276 | new.kh++; | ||
277 | } while (cmpxchg(&ic->val, old.val, new.val) != old.val); | ||
278 | } | ||
279 | |||
280 | static void ipte_unlock_siif(struct kvm_vcpu *vcpu) | ||
281 | { | ||
282 | union ipte_control old, new, *ic; | ||
283 | |||
284 | ic = &vcpu->kvm->arch.sca->ipte_control; | ||
285 | do { | ||
286 | new = old = ACCESS_ONCE(*ic); | ||
287 | new.kh--; | ||
288 | if (!new.kh) | ||
289 | new.k = 0; | ||
290 | } while (cmpxchg(&ic->val, old.val, new.val) != old.val); | ||
291 | if (!new.kh) | ||
292 | wake_up(&vcpu->kvm->arch.ipte_wq); | ||
293 | } | ||
294 | |||
295 | static void ipte_lock(struct kvm_vcpu *vcpu) | ||
296 | { | ||
297 | if (vcpu->arch.sie_block->eca & 1) | ||
298 | ipte_lock_siif(vcpu); | ||
299 | else | ||
300 | ipte_lock_simple(vcpu); | ||
301 | } | ||
302 | |||
303 | static void ipte_unlock(struct kvm_vcpu *vcpu) | ||
304 | { | ||
305 | if (vcpu->arch.sie_block->eca & 1) | ||
306 | ipte_unlock_siif(vcpu); | ||
307 | else | ||
308 | ipte_unlock_simple(vcpu); | ||
309 | } | ||
310 | |||
210 | static unsigned long get_vcpu_asce(struct kvm_vcpu *vcpu) | 311 | static unsigned long get_vcpu_asce(struct kvm_vcpu *vcpu) |
211 | { | 312 | { |
212 | switch (psw_bits(vcpu->arch.sie_block->gpsw).as) { | 313 | switch (psw_bits(vcpu->arch.sie_block->gpsw).as) { |
@@ -485,6 +586,8 @@ int access_guest(struct kvm_vcpu *vcpu, unsigned long ga, void *data, | |||
485 | unsigned long _len, nr_pages, gpa, idx; | 586 | unsigned long _len, nr_pages, gpa, idx; |
486 | unsigned long pages_array[2]; | 587 | unsigned long pages_array[2]; |
487 | unsigned long *pages; | 588 | unsigned long *pages; |
589 | int need_ipte_lock; | ||
590 | union asce asce; | ||
488 | int rc; | 591 | int rc; |
489 | 592 | ||
490 | if (!len) | 593 | if (!len) |
@@ -498,6 +601,10 @@ int access_guest(struct kvm_vcpu *vcpu, unsigned long ga, void *data, | |||
498 | pages = vmalloc(nr_pages * sizeof(unsigned long)); | 601 | pages = vmalloc(nr_pages * sizeof(unsigned long)); |
499 | if (!pages) | 602 | if (!pages) |
500 | return -ENOMEM; | 603 | return -ENOMEM; |
604 | asce.val = get_vcpu_asce(vcpu); | ||
605 | need_ipte_lock = psw_bits(*psw).t && !asce.r; | ||
606 | if (need_ipte_lock) | ||
607 | ipte_lock(vcpu); | ||
501 | rc = guest_page_range(vcpu, ga, pages, nr_pages, write); | 608 | rc = guest_page_range(vcpu, ga, pages, nr_pages, write); |
502 | for (idx = 0; idx < nr_pages && !rc; idx++) { | 609 | for (idx = 0; idx < nr_pages && !rc; idx++) { |
503 | gpa = *(pages + idx) + (ga & ~PAGE_MASK); | 610 | gpa = *(pages + idx) + (ga & ~PAGE_MASK); |
@@ -510,6 +617,8 @@ int access_guest(struct kvm_vcpu *vcpu, unsigned long ga, void *data, | |||
510 | ga += _len; | 617 | ga += _len; |
511 | data += _len; | 618 | data += _len; |
512 | } | 619 | } |
620 | if (need_ipte_lock) | ||
621 | ipte_unlock(vcpu); | ||
513 | if (nr_pages > ARRAY_SIZE(pages_array)) | 622 | if (nr_pages > ARRAY_SIZE(pages_array)) |
514 | vfree(pages); | 623 | vfree(pages); |
515 | return rc; | 624 | return rc; |
diff --git a/arch/s390/kvm/gaccess.h b/arch/s390/kvm/gaccess.h index 21ee62cd948e..f46e764c5b43 100644 --- a/arch/s390/kvm/gaccess.h +++ b/arch/s390/kvm/gaccess.h | |||
@@ -397,4 +397,6 @@ int read_guest_real(struct kvm_vcpu *vcpu, unsigned long gra, void *data, | |||
397 | return access_guest_real(vcpu, gra, data, len, 0); | 397 | return access_guest_real(vcpu, gra, data, len, 0); |
398 | } | 398 | } |
399 | 399 | ||
400 | int ipte_lock_held(struct kvm_vcpu *vcpu); | ||
401 | |||
400 | #endif /* __KVM_S390_GACCESS_H */ | 402 | #endif /* __KVM_S390_GACCESS_H */ |
diff --git a/arch/s390/kvm/kvm-s390.c b/arch/s390/kvm/kvm-s390.c index 927ba7361da9..e1dfe2461d4b 100644 --- a/arch/s390/kvm/kvm-s390.c +++ b/arch/s390/kvm/kvm-s390.c | |||
@@ -67,6 +67,7 @@ struct kvm_stats_debugfs_item debugfs_entries[] = { | |||
67 | { "instruction_stpx", VCPU_STAT(instruction_stpx) }, | 67 | { "instruction_stpx", VCPU_STAT(instruction_stpx) }, |
68 | { "instruction_stap", VCPU_STAT(instruction_stap) }, | 68 | { "instruction_stap", VCPU_STAT(instruction_stap) }, |
69 | { "instruction_storage_key", VCPU_STAT(instruction_storage_key) }, | 69 | { "instruction_storage_key", VCPU_STAT(instruction_storage_key) }, |
70 | { "instruction_ipte_interlock", VCPU_STAT(instruction_ipte_interlock) }, | ||
70 | { "instruction_stsch", VCPU_STAT(instruction_stsch) }, | 71 | { "instruction_stsch", VCPU_STAT(instruction_stsch) }, |
71 | { "instruction_chsc", VCPU_STAT(instruction_chsc) }, | 72 | { "instruction_chsc", VCPU_STAT(instruction_chsc) }, |
72 | { "instruction_essa", VCPU_STAT(instruction_essa) }, | 73 | { "instruction_essa", VCPU_STAT(instruction_essa) }, |
@@ -437,6 +438,7 @@ int kvm_arch_init_vm(struct kvm *kvm, unsigned long type) | |||
437 | 438 | ||
438 | spin_lock_init(&kvm->arch.float_int.lock); | 439 | spin_lock_init(&kvm->arch.float_int.lock); |
439 | INIT_LIST_HEAD(&kvm->arch.float_int.list); | 440 | INIT_LIST_HEAD(&kvm->arch.float_int.list); |
441 | init_waitqueue_head(&kvm->arch.ipte_wq); | ||
440 | 442 | ||
441 | debug_register_view(kvm->arch.dbf, &debug_sprintf_view); | 443 | debug_register_view(kvm->arch.dbf, &debug_sprintf_view); |
442 | VM_EVENT(kvm, 3, "%s", "vm created"); | 444 | VM_EVENT(kvm, 3, "%s", "vm created"); |
diff --git a/arch/s390/kvm/priv.c b/arch/s390/kvm/priv.c index 9a04d74c5fb4..4792f1df921a 100644 --- a/arch/s390/kvm/priv.c +++ b/arch/s390/kvm/priv.c | |||
@@ -173,6 +173,19 @@ static int handle_skey(struct kvm_vcpu *vcpu) | |||
173 | return 0; | 173 | return 0; |
174 | } | 174 | } |
175 | 175 | ||
176 | static int handle_ipte_interlock(struct kvm_vcpu *vcpu) | ||
177 | { | ||
178 | psw_t *psw = &vcpu->arch.sie_block->gpsw; | ||
179 | |||
180 | vcpu->stat.instruction_ipte_interlock++; | ||
181 | if (psw_bits(*psw).p) | ||
182 | return kvm_s390_inject_program_int(vcpu, PGM_PRIVILEGED_OP); | ||
183 | wait_event(vcpu->kvm->arch.ipte_wq, !ipte_lock_held(vcpu)); | ||
184 | psw->addr = __rewind_psw(*psw, 4); | ||
185 | VCPU_EVENT(vcpu, 4, "%s", "retrying ipte interlock operation"); | ||
186 | return 0; | ||
187 | } | ||
188 | |||
176 | static int handle_test_block(struct kvm_vcpu *vcpu) | 189 | static int handle_test_block(struct kvm_vcpu *vcpu) |
177 | { | 190 | { |
178 | unsigned long hva; | 191 | unsigned long hva; |
@@ -509,6 +522,7 @@ static const intercept_handler_t b2_handlers[256] = { | |||
509 | [0x10] = handle_set_prefix, | 522 | [0x10] = handle_set_prefix, |
510 | [0x11] = handle_store_prefix, | 523 | [0x11] = handle_store_prefix, |
511 | [0x12] = handle_store_cpu_address, | 524 | [0x12] = handle_store_cpu_address, |
525 | [0x21] = handle_ipte_interlock, | ||
512 | [0x29] = handle_skey, | 526 | [0x29] = handle_skey, |
513 | [0x2a] = handle_skey, | 527 | [0x2a] = handle_skey, |
514 | [0x2b] = handle_skey, | 528 | [0x2b] = handle_skey, |
@@ -526,6 +540,7 @@ static const intercept_handler_t b2_handlers[256] = { | |||
526 | [0x3a] = handle_io_inst, | 540 | [0x3a] = handle_io_inst, |
527 | [0x3b] = handle_io_inst, | 541 | [0x3b] = handle_io_inst, |
528 | [0x3c] = handle_io_inst, | 542 | [0x3c] = handle_io_inst, |
543 | [0x50] = handle_ipte_interlock, | ||
529 | [0x5f] = handle_io_inst, | 544 | [0x5f] = handle_io_inst, |
530 | [0x74] = handle_io_inst, | 545 | [0x74] = handle_io_inst, |
531 | [0x76] = handle_io_inst, | 546 | [0x76] = handle_io_inst, |
@@ -686,7 +701,10 @@ static int handle_essa(struct kvm_vcpu *vcpu) | |||
686 | } | 701 | } |
687 | 702 | ||
688 | static const intercept_handler_t b9_handlers[256] = { | 703 | static const intercept_handler_t b9_handlers[256] = { |
704 | [0x8a] = handle_ipte_interlock, | ||
689 | [0x8d] = handle_epsw, | 705 | [0x8d] = handle_epsw, |
706 | [0x8e] = handle_ipte_interlock, | ||
707 | [0x8f] = handle_ipte_interlock, | ||
690 | [0xab] = handle_essa, | 708 | [0xab] = handle_essa, |
691 | [0xaf] = handle_pfmf, | 709 | [0xaf] = handle_pfmf, |
692 | }; | 710 | }; |