diff options
author | Cornelia Huck <cornelia.huck@de.ibm.com> | 2013-07-15 07:36:01 -0400 |
---|---|---|
committer | Cornelia Huck <cornelia.huck@de.ibm.com> | 2014-03-21 08:43:00 -0400 |
commit | 84223598778ba08041f4297fda485df83414d57e (patch) | |
tree | bd93b83a13cc5cd1f6781bf681161fb3982548c5 /arch/s390/kvm | |
parent | 841b91c584b6d1e2a2cb508bd2d0236cd37e1750 (diff) |
KVM: s390: irq routing for adapter interrupts.
Introduce a new interrupt class for s390 adapter interrupts and enable
irqfds for s390.
This is depending on a new s390 specific vm capability, KVM_CAP_S390_IRQCHIP,
that needs to be enabled by userspace.
Acked-by: Christian Borntraeger <borntraeger@de.ibm.com>
Signed-off-by: Cornelia Huck <cornelia.huck@de.ibm.com>
Diffstat (limited to 'arch/s390/kvm')
-rw-r--r-- | arch/s390/kvm/Kconfig | 2 | ||||
-rw-r--r-- | arch/s390/kvm/Makefile | 2 | ||||
-rw-r--r-- | arch/s390/kvm/interrupt.c | 121 | ||||
-rw-r--r-- | arch/s390/kvm/irq.h | 22 | ||||
-rw-r--r-- | arch/s390/kvm/kvm-s390.c | 17 |
5 files changed, 163 insertions, 1 deletions
diff --git a/arch/s390/kvm/Kconfig b/arch/s390/kvm/Kconfig index c8bacbcd2e5b..10d529ac9821 100644 --- a/arch/s390/kvm/Kconfig +++ b/arch/s390/kvm/Kconfig | |||
@@ -25,6 +25,8 @@ config KVM | |||
25 | select HAVE_KVM_EVENTFD | 25 | select HAVE_KVM_EVENTFD |
26 | select KVM_ASYNC_PF | 26 | select KVM_ASYNC_PF |
27 | select KVM_ASYNC_PF_SYNC | 27 | select KVM_ASYNC_PF_SYNC |
28 | select HAVE_KVM_IRQCHIP | ||
29 | select HAVE_KVM_IRQ_ROUTING | ||
28 | ---help--- | 30 | ---help--- |
29 | Support hosting paravirtualized guest machines using the SIE | 31 | Support hosting paravirtualized guest machines using the SIE |
30 | virtualization capability on the mainframe. This should work | 32 | virtualization capability on the mainframe. This should work |
diff --git a/arch/s390/kvm/Makefile b/arch/s390/kvm/Makefile index a47d2c355f68..d3adb37e93a4 100644 --- a/arch/s390/kvm/Makefile +++ b/arch/s390/kvm/Makefile | |||
@@ -7,7 +7,7 @@ | |||
7 | # as published by the Free Software Foundation. | 7 | # as published by the Free Software Foundation. |
8 | 8 | ||
9 | KVM := ../../../virt/kvm | 9 | KVM := ../../../virt/kvm |
10 | common-objs = $(KVM)/kvm_main.o $(KVM)/eventfd.o $(KVM)/async_pf.o | 10 | common-objs = $(KVM)/kvm_main.o $(KVM)/eventfd.o $(KVM)/async_pf.o $(KVM)/irqchip.o |
11 | 11 | ||
12 | ccflags-y := -Ivirt/kvm -Iarch/s390/kvm | 12 | ccflags-y := -Ivirt/kvm -Iarch/s390/kvm |
13 | 13 | ||
diff --git a/arch/s390/kvm/interrupt.c b/arch/s390/kvm/interrupt.c index 7ecef5a18e25..2e2814eceb85 100644 --- a/arch/s390/kvm/interrupt.c +++ b/arch/s390/kvm/interrupt.c | |||
@@ -13,6 +13,7 @@ | |||
13 | #include <linux/interrupt.h> | 13 | #include <linux/interrupt.h> |
14 | #include <linux/kvm_host.h> | 14 | #include <linux/kvm_host.h> |
15 | #include <linux/hrtimer.h> | 15 | #include <linux/hrtimer.h> |
16 | #include <linux/mmu_context.h> | ||
16 | #include <linux/signal.h> | 17 | #include <linux/signal.h> |
17 | #include <linux/slab.h> | 18 | #include <linux/slab.h> |
18 | #include <asm/asm-offsets.h> | 19 | #include <asm/asm-offsets.h> |
@@ -1284,3 +1285,123 @@ struct kvm_device_ops kvm_flic_ops = { | |||
1284 | .create = flic_create, | 1285 | .create = flic_create, |
1285 | .destroy = flic_destroy, | 1286 | .destroy = flic_destroy, |
1286 | }; | 1287 | }; |
1288 | |||
1289 | static unsigned long get_ind_bit(__u64 addr, unsigned long bit_nr, bool swap) | ||
1290 | { | ||
1291 | unsigned long bit; | ||
1292 | |||
1293 | bit = bit_nr + (addr % PAGE_SIZE) * 8; | ||
1294 | |||
1295 | return swap ? (bit ^ (BITS_PER_LONG - 1)) : bit; | ||
1296 | } | ||
1297 | |||
1298 | static struct s390_map_info *get_map_info(struct s390_io_adapter *adapter, | ||
1299 | u64 addr) | ||
1300 | { | ||
1301 | struct s390_map_info *map; | ||
1302 | |||
1303 | if (!adapter) | ||
1304 | return NULL; | ||
1305 | |||
1306 | list_for_each_entry(map, &adapter->maps, list) { | ||
1307 | if (map->guest_addr == addr) | ||
1308 | return map; | ||
1309 | } | ||
1310 | return NULL; | ||
1311 | } | ||
1312 | |||
1313 | static int adapter_indicators_set(struct kvm *kvm, | ||
1314 | struct s390_io_adapter *adapter, | ||
1315 | struct kvm_s390_adapter_int *adapter_int) | ||
1316 | { | ||
1317 | unsigned long bit; | ||
1318 | int summary_set, idx; | ||
1319 | struct s390_map_info *info; | ||
1320 | void *map; | ||
1321 | |||
1322 | info = get_map_info(adapter, adapter_int->ind_addr); | ||
1323 | if (!info) | ||
1324 | return -1; | ||
1325 | map = page_address(info->page); | ||
1326 | bit = get_ind_bit(info->addr, adapter_int->ind_offset, adapter->swap); | ||
1327 | set_bit(bit, map); | ||
1328 | idx = srcu_read_lock(&kvm->srcu); | ||
1329 | mark_page_dirty(kvm, info->guest_addr >> PAGE_SHIFT); | ||
1330 | set_page_dirty_lock(info->page); | ||
1331 | info = get_map_info(adapter, adapter_int->summary_addr); | ||
1332 | if (!info) { | ||
1333 | srcu_read_unlock(&kvm->srcu, idx); | ||
1334 | return -1; | ||
1335 | } | ||
1336 | map = page_address(info->page); | ||
1337 | bit = get_ind_bit(info->addr, adapter_int->summary_offset, | ||
1338 | adapter->swap); | ||
1339 | summary_set = test_and_set_bit(bit, map); | ||
1340 | mark_page_dirty(kvm, info->guest_addr >> PAGE_SHIFT); | ||
1341 | set_page_dirty_lock(info->page); | ||
1342 | srcu_read_unlock(&kvm->srcu, idx); | ||
1343 | return summary_set ? 0 : 1; | ||
1344 | } | ||
1345 | |||
1346 | /* | ||
1347 | * < 0 - not injected due to error | ||
1348 | * = 0 - coalesced, summary indicator already active | ||
1349 | * > 0 - injected interrupt | ||
1350 | */ | ||
1351 | static int set_adapter_int(struct kvm_kernel_irq_routing_entry *e, | ||
1352 | struct kvm *kvm, int irq_source_id, int level, | ||
1353 | bool line_status) | ||
1354 | { | ||
1355 | int ret; | ||
1356 | struct s390_io_adapter *adapter; | ||
1357 | |||
1358 | /* We're only interested in the 0->1 transition. */ | ||
1359 | if (!level) | ||
1360 | return 0; | ||
1361 | adapter = get_io_adapter(kvm, e->adapter.adapter_id); | ||
1362 | if (!adapter) | ||
1363 | return -1; | ||
1364 | down_read(&adapter->maps_lock); | ||
1365 | ret = adapter_indicators_set(kvm, adapter, &e->adapter); | ||
1366 | up_read(&adapter->maps_lock); | ||
1367 | if ((ret > 0) && !adapter->masked) { | ||
1368 | struct kvm_s390_interrupt s390int = { | ||
1369 | .type = KVM_S390_INT_IO(1, 0, 0, 0), | ||
1370 | .parm = 0, | ||
1371 | .parm64 = (adapter->isc << 27) | 0x80000000, | ||
1372 | }; | ||
1373 | ret = kvm_s390_inject_vm(kvm, &s390int); | ||
1374 | if (ret == 0) | ||
1375 | ret = 1; | ||
1376 | } | ||
1377 | return ret; | ||
1378 | } | ||
1379 | |||
1380 | int kvm_set_routing_entry(struct kvm_irq_routing_table *rt, | ||
1381 | struct kvm_kernel_irq_routing_entry *e, | ||
1382 | const struct kvm_irq_routing_entry *ue) | ||
1383 | { | ||
1384 | int ret; | ||
1385 | |||
1386 | switch (ue->type) { | ||
1387 | case KVM_IRQ_ROUTING_S390_ADAPTER: | ||
1388 | e->set = set_adapter_int; | ||
1389 | e->adapter.summary_addr = ue->u.adapter.summary_addr; | ||
1390 | e->adapter.ind_addr = ue->u.adapter.ind_addr; | ||
1391 | e->adapter.summary_offset = ue->u.adapter.summary_offset; | ||
1392 | e->adapter.ind_offset = ue->u.adapter.ind_offset; | ||
1393 | e->adapter.adapter_id = ue->u.adapter.adapter_id; | ||
1394 | ret = 0; | ||
1395 | break; | ||
1396 | default: | ||
1397 | ret = -EINVAL; | ||
1398 | } | ||
1399 | |||
1400 | return ret; | ||
1401 | } | ||
1402 | |||
1403 | int kvm_set_msi(struct kvm_kernel_irq_routing_entry *e, struct kvm *kvm, | ||
1404 | int irq_source_id, int level, bool line_status) | ||
1405 | { | ||
1406 | return -EINVAL; | ||
1407 | } | ||
diff --git a/arch/s390/kvm/irq.h b/arch/s390/kvm/irq.h new file mode 100644 index 000000000000..d98e4159643d --- /dev/null +++ b/arch/s390/kvm/irq.h | |||
@@ -0,0 +1,22 @@ | |||
1 | /* | ||
2 | * s390 irqchip routines | ||
3 | * | ||
4 | * Copyright IBM Corp. 2014 | ||
5 | * | ||
6 | * This program is free software; you can redistribute it and/or modify | ||
7 | * it under the terms of the GNU General Public License (version 2 only) | ||
8 | * as published by the Free Software Foundation. | ||
9 | * | ||
10 | * Author(s): Cornelia Huck <cornelia.huck@de.ibm.com> | ||
11 | */ | ||
12 | #ifndef __KVM_IRQ_H | ||
13 | #define __KVM_IRQ_H | ||
14 | |||
15 | #include <linux/kvm_host.h> | ||
16 | |||
17 | static inline int irqchip_in_kernel(struct kvm *kvm) | ||
18 | { | ||
19 | return 1; | ||
20 | } | ||
21 | |||
22 | #endif | ||
diff --git a/arch/s390/kvm/kvm-s390.c b/arch/s390/kvm/kvm-s390.c index 2e6fbb0b4f68..ce5b659ec531 100644 --- a/arch/s390/kvm/kvm-s390.c +++ b/arch/s390/kvm/kvm-s390.c | |||
@@ -196,6 +196,10 @@ static int kvm_vm_ioctl_enable_cap(struct kvm *kvm, struct kvm_enable_cap *cap) | |||
196 | return -EINVAL; | 196 | return -EINVAL; |
197 | 197 | ||
198 | switch (cap->cap) { | 198 | switch (cap->cap) { |
199 | case KVM_CAP_S390_IRQCHIP: | ||
200 | kvm->arch.use_irqchip = 1; | ||
201 | r = 0; | ||
202 | break; | ||
199 | default: | 203 | default: |
200 | r = -EINVAL; | 204 | r = -EINVAL; |
201 | break; | 205 | break; |
@@ -228,6 +232,18 @@ long kvm_arch_vm_ioctl(struct file *filp, | |||
228 | r = kvm_vm_ioctl_enable_cap(kvm, &cap); | 232 | r = kvm_vm_ioctl_enable_cap(kvm, &cap); |
229 | break; | 233 | break; |
230 | } | 234 | } |
235 | case KVM_CREATE_IRQCHIP: { | ||
236 | struct kvm_irq_routing_entry routing; | ||
237 | |||
238 | r = -EINVAL; | ||
239 | if (kvm->arch.use_irqchip) { | ||
240 | /* Set up dummy routing. */ | ||
241 | memset(&routing, 0, sizeof(routing)); | ||
242 | kvm_set_irq_routing(kvm, &routing, 0, 0); | ||
243 | r = 0; | ||
244 | } | ||
245 | break; | ||
246 | } | ||
231 | default: | 247 | default: |
232 | r = -ENOTTY; | 248 | r = -ENOTTY; |
233 | } | 249 | } |
@@ -284,6 +300,7 @@ int kvm_arch_init_vm(struct kvm *kvm, unsigned long type) | |||
284 | } | 300 | } |
285 | 301 | ||
286 | kvm->arch.css_support = 0; | 302 | kvm->arch.css_support = 0; |
303 | kvm->arch.use_irqchip = 0; | ||
287 | 304 | ||
288 | return 0; | 305 | return 0; |
289 | out_nogmap: | 306 | out_nogmap: |