Add a new interface to register/deregister sources of adapter interrupts identified by an unique id via the flic. Adapters may also be maskable and carry a list of pinned pages. These adapters will be used by irq routing later.
Unified · Split
| @@ -12,6 +12,7 @@ FLIC provides support to · 11 unmodified lines | ||
| 12 | 12 | - inspect currently pending interrupts (KVM_FLIC_GET_ALL_IRQS) |
| 13 | 13 | - purge all pending floating interrupts (KVM_DEV_FLIC_CLEAR_IRQS) |
| 14 | 14 | - enable/disable for the guest transparent async page faults |
| 15 | +- register and modify adapter interrupt sources (KVM_DEV_FLIC_ADAPTER_*) | |
| 15 | 16 | |
| 16 | 17 | Groups: |
| 17 | 18 | KVM_DEV_FLIC_ENQUEUE |
| @@ -44,3 +45,47 @@ Groups: · 26 unmodified lines | ||
| 44 | 45 | Disables async page faults for the guest and waits until already pending |
| 45 | 46 | async page faults are done. This is necessary to trigger a completion interrupt |
| 46 | 47 | for every init interrupt before migrating the interrupt list. |
| 48 | + | |
| 49 | + KVM_DEV_FLIC_ADAPTER_REGISTER | |
| 50 | + Register an I/O adapter interrupt source. Takes a kvm_s390_io_adapter | |
| 51 | + describing the adapter to register: | |
| 52 | + | |
| 53 | +struct kvm_s390_io_adapter { | |
| 54 | + __u32 id; | |
| 55 | + __u8 isc; | |
| 56 | + __u8 maskable; | |
| 57 | + __u8 swap; | |
| 58 | + __u8 pad; | |
| 59 | +}; | |
| 60 | + | |
| 61 | + id contains the unique id for the adapter, isc the I/O interruption subclass | |
| 62 | + to use, maskable whether this adapter may be masked (interrupts turned off) | |
| 63 | + and swap whether the indicators need to be byte swapped. | |
| 64 | + | |
| 65 | + | |
| 66 | + KVM_DEV_FLIC_ADAPTER_MODIFY | |
| 67 | + Modifies attributes of an existing I/O adapter interrupt source. Takes | |
| 68 | + a kvm_s390_io_adapter_req specifiying the adapter and the operation: | |
| 69 | + | |
| 70 | +struct kvm_s390_io_adapter_req { | |
| 71 | + __u32 id; | |
| 72 | + __u8 type; | |
| 73 | + __u8 mask; | |
| 74 | + __u16 pad0; | |
| 75 | + __u64 addr; | |
| 76 | +}; | |
| 77 | + | |
| 78 | + id specifies the adapter and type the operation. The supported operations | |
| 79 | + are: | |
| 80 | + | |
| 81 | + KVM_S390_IO_ADAPTER_MASK | |
| 82 | + mask or unmask the adapter, as specified in mask | |
| 83 | + | |
| 84 | + KVM_S390_IO_ADAPTER_MAP | |
| 85 | + perform a gmap translation for the guest address provided in addr, | |
| 86 | + pin a userspace page for the translated address and add it to the | |
| 87 | + list of mappings | |
| 88 | + | |
| 89 | + KVM_S390_IO_ADAPTER_UNMAP | |
| 90 | + release a userspace page for the translated address specified in addr | |
| 91 | + from the list of mappings | |
| @@ -19,6 +19,7 @@ · 18 unmodified lines | ||
| 19 | 19 | #include <linux/kvm.h> |
| 20 | 20 | #include <asm/debug.h> |
| 21 | 21 | #include <asm/cpu.h> |
| 22 | +#include <asm/isc.h> | |
| 22 | 23 | |
| 23 | 24 | #define KVM_MAX_VCPUS 64 |
| 24 | 25 | #define KVM_USER_MEM_SLOTS 32 |
| @@ -245,6 +246,27 @@ struct kvm_vm_stat { · 220 unmodified lines | ||
| 245 | 246 | struct kvm_arch_memory_slot { |
| 246 | 247 | }; |
| 247 | 248 | |
| 249 | +struct s390_map_info { | |
| 250 | + struct list_head list; | |
| 251 | + __u64 guest_addr; | |
| 252 | + __u64 addr; | |
| 253 | + struct page *page; | |
| 254 | +}; | |
| 255 | + | |
| 256 | +struct s390_io_adapter { | |
| 257 | + unsigned int id; | |
| 258 | + int isc; | |
| 259 | + bool maskable; | |
| 260 | + bool masked; | |
| 261 | + bool swap; | |
| 262 | + struct rw_semaphore maps_lock; | |
| 263 | + struct list_head maps; | |
| 264 | + atomic_t nr_maps; | |
| 265 | +}; | |
| 266 | + | |
| 267 | +#define MAX_S390_IO_ADAPTERS ((MAX_ISC + 1) * 8) | |
| 268 | +#define MAX_S390_ADAPTER_MAPS 256 | |
| 269 | + | |
| 248 | 270 | struct kvm_arch{ |
| 249 | 271 | struct sca_block *sca; |
| 250 | 272 | debug_info_t *dbf; |
| @@ -252,6 +274,7 @@ struct kvm_arch{ · 1 unmodified line | ||
| 252 | 274 | struct kvm_device *flic; |
| 253 | 275 | struct gmap *gmap; |
| 254 | 276 | int css_support; |
| 277 | + struct s390_io_adapter *adapters[MAX_S390_IO_ADAPTERS]; | |
| 255 | 278 | }; |
| 256 | 279 | |
| 257 | 280 | #define KVM_HVA_ERR_BAD (-1UL) |
| @@ -22,6 +22,8 @@ · 21 unmodified lines | ||
| 22 | 22 | #define KVM_DEV_FLIC_CLEAR_IRQS 3 |
| 23 | 23 | #define KVM_DEV_FLIC_APF_ENABLE 4 |
| 24 | 24 | #define KVM_DEV_FLIC_APF_DISABLE_WAIT 5 |
| 25 | +#define KVM_DEV_FLIC_ADAPTER_REGISTER 6 | |
| 26 | +#define KVM_DEV_FLIC_ADAPTER_MODIFY 7 | |
| 25 | 27 | /* |
| 26 | 28 | * We can have up to 4*64k pending subchannels + 8 adapter interrupts, |
| 27 | 29 | * as well as up to ASYNC_PF_PER_VCPU*KVM_MAX_VCPUS pfault done interrupts. |
| @@ -32,6 +34,26 @@ · 4 unmodified lines | ||
| 32 | 34 | #define KVM_S390_MAX_FLOAT_IRQS 266250 |
| 33 | 35 | #define KVM_S390_FLIC_MAX_BUFFER 0x2000000 |
| 34 | 36 | |
| 37 | +struct kvm_s390_io_adapter { | |
| 38 | + __u32 id; | |
| 39 | + __u8 isc; | |
| 40 | + __u8 maskable; | |
| 41 | + __u8 swap; | |
| 42 | + __u8 pad; | |
| 43 | +}; | |
| 44 | + | |
| 45 | +#define KVM_S390_IO_ADAPTER_MASK 1 | |
| 46 | +#define KVM_S390_IO_ADAPTER_MAP 2 | |
| 47 | +#define KVM_S390_IO_ADAPTER_UNMAP 3 | |
| 48 | + | |
| 49 | +struct kvm_s390_io_adapter_req { | |
| 50 | + __u32 id; | |
| 51 | + __u8 type; | |
| 52 | + __u8 mask; | |
| 53 | + __u16 pad0; | |
| 54 | + __u64 addr; | |
| 55 | +}; | |
| 56 | + | |
| 35 | 57 | /* for KVM_GET_REGS and KVM_SET_REGS */ |
| 36 | 58 | struct kvm_regs { |
| 37 | 59 | /* general purpose regs for s390 */ |
| @@ -1,7 +1,7 @@ | ||
| 1 | 1 | /* |
| 2 | 2 | * handling kvm guest interrupts |
| 3 | 3 | * |
| 4 | − * Copyright IBM Corp. 2008 | |
| 4 | + * Copyright IBM Corp. 2008,2014 | |
| 5 | 5 | * |
| 6 | 6 | * This program is free software; you can redistribute it and/or modify |
| 7 | 7 | * it under the terms of the GNU General Public License (version 2 only) |
| @@ -1054,6 +1054,171 @@ static int enqueue_floating_irq(struct kvm_device *dev, · 1046 unmodified lines | ||
| 1054 | 1054 | return r; |
| 1055 | 1055 | } |
| 1056 | 1056 | |
| 1057 | +static struct s390_io_adapter *get_io_adapter(struct kvm *kvm, unsigned int id) | |
| 1058 | +{ | |
| 1059 | + if (id >= MAX_S390_IO_ADAPTERS) | |
| 1060 | + return NULL; | |
| 1061 | + return kvm->arch.adapters[id]; | |
| 1062 | +} | |
| 1063 | + | |
| 1064 | +static int register_io_adapter(struct kvm_device *dev, | |
| 1065 | + struct kvm_device_attr *attr) | |
| 1066 | +{ | |
| 1067 | + struct s390_io_adapter *adapter; | |
| 1068 | + struct kvm_s390_io_adapter adapter_info; | |
| 1069 | + | |
| 1070 | + if (copy_from_user(&adapter_info, | |
| 1071 | + (void __user *)attr->addr, sizeof(adapter_info))) | |
| 1072 | + return -EFAULT; | |
| 1073 | + | |
| 1074 | + if ((adapter_info.id >= MAX_S390_IO_ADAPTERS) || | |
| 1075 | + (dev->kvm->arch.adapters[adapter_info.id] != NULL)) | |
| 1076 | + return -EINVAL; | |
| 1077 | + | |
| 1078 | + adapter = kzalloc(sizeof(*adapter), GFP_KERNEL); | |
| 1079 | + if (!adapter) | |
| 1080 | + return -ENOMEM; | |
| 1081 | + | |
| 1082 | + INIT_LIST_HEAD(&adapter->maps); | |
| 1083 | + init_rwsem(&adapter->maps_lock); | |
| 1084 | + atomic_set(&adapter->nr_maps, 0); | |
| 1085 | + adapter->id = adapter_info.id; | |
| 1086 | + adapter->isc = adapter_info.isc; | |
| 1087 | + adapter->maskable = adapter_info.maskable; | |
| 1088 | + adapter->masked = false; | |
| 1089 | + adapter->swap = adapter_info.swap; | |
| 1090 | + dev->kvm->arch.adapters[adapter->id] = adapter; | |
| 1091 | + | |
| 1092 | + return 0; | |
| 1093 | +} | |
| 1094 | + | |
| 1095 | +int kvm_s390_mask_adapter(struct kvm *kvm, unsigned int id, bool masked) | |
| 1096 | +{ | |
| 1097 | + int ret; | |
| 1098 | + struct s390_io_adapter *adapter = get_io_adapter(kvm, id); | |
| 1099 | + | |
| 1100 | + if (!adapter || !adapter->maskable) | |
| 1101 | + return -EINVAL; | |
| 1102 | + ret = adapter->masked; | |
| 1103 | + adapter->masked = masked; | |
| 1104 | + return ret; | |
| 1105 | +} | |
| 1106 | + | |
| 1107 | +static int kvm_s390_adapter_map(struct kvm *kvm, unsigned int id, __u64 addr) | |
| 1108 | +{ | |
| 1109 | + struct s390_io_adapter *adapter = get_io_adapter(kvm, id); | |
| 1110 | + struct s390_map_info *map; | |
| 1111 | + int ret; | |
| 1112 | + | |
| 1113 | + if (!adapter || !addr) | |
| 1114 | + return -EINVAL; | |
| 1115 | + | |
| 1116 | + map = kzalloc(sizeof(*map), GFP_KERNEL); | |
| 1117 | + if (!map) { | |
| 1118 | + ret = -ENOMEM; | |
| 1119 | + goto out; | |
| 1120 | + } | |
| 1121 | + INIT_LIST_HEAD(&map->list); | |
| 1122 | + map->guest_addr = addr; | |
| 1123 | + map->addr = gmap_translate(addr, kvm->arch.gmap); | |
| 1124 | + if (map->addr == -EFAULT) { | |
| 1125 | + ret = -EFAULT; | |
| 1126 | + goto out; | |
| 1127 | + } | |
| 1128 | + ret = get_user_pages_fast(map->addr, 1, 1, &map->page); | |
| 1129 | + if (ret < 0) | |
| 1130 | + goto out; | |
| 1131 | + BUG_ON(ret != 1); | |
| 1132 | + down_write(&adapter->maps_lock); | |
| 1133 | + if (atomic_inc_return(&adapter->nr_maps) < MAX_S390_ADAPTER_MAPS) { | |
| 1134 | + list_add_tail(&map->list, &adapter->maps); | |
| 1135 | + ret = 0; | |
| 1136 | + } else { | |
| 1137 | + put_page(map->page); | |
| 1138 | + ret = -EINVAL; | |
| 1139 | + } | |
| 1140 | + up_write(&adapter->maps_lock); | |
| 1141 | +out: | |
| 1142 | + if (ret) | |
| 1143 | + kfree(map); | |
| 1144 | + return ret; | |
| 1145 | +} | |
| 1146 | + | |
| 1147 | +static int kvm_s390_adapter_unmap(struct kvm *kvm, unsigned int id, __u64 addr) | |
| 1148 | +{ | |
| 1149 | + struct s390_io_adapter *adapter = get_io_adapter(kvm, id); | |
| 1150 | + struct s390_map_info *map, *tmp; | |
| 1151 | + int found = 0; | |
| 1152 | + | |
| 1153 | + if (!adapter || !addr) | |
| 1154 | + return -EINVAL; | |
| 1155 | + | |
| 1156 | + down_write(&adapter->maps_lock); | |
| 1157 | + list_for_each_entry_safe(map, tmp, &adapter->maps, list) { | |
| 1158 | + if (map->guest_addr == addr) { | |
| 1159 | + found = 1; | |
| 1160 | + atomic_dec(&adapter->nr_maps); | |
| 1161 | + list_del(&map->list); | |
| 1162 | + put_page(map->page); | |
| 1163 | + kfree(map); | |
| 1164 | + break; | |
| 1165 | + } | |
| 1166 | + } | |
| 1167 | + up_write(&adapter->maps_lock); | |
| 1168 | + | |
| 1169 | + return found ? 0 : -EINVAL; | |
| 1170 | +} | |
| 1171 | + | |
| 1172 | +void kvm_s390_destroy_adapters(struct kvm *kvm) | |
| 1173 | +{ | |
| 1174 | + int i; | |
| 1175 | + struct s390_map_info *map, *tmp; | |
| 1176 | + | |
| 1177 | + for (i = 0; i < MAX_S390_IO_ADAPTERS; i++) { | |
| 1178 | + if (!kvm->arch.adapters[i]) | |
| 1179 | + continue; | |
| 1180 | + list_for_each_entry_safe(map, tmp, | |
| 1181 | + &kvm->arch.adapters[i]->maps, list) { | |
| 1182 | + list_del(&map->list); | |
| 1183 | + put_page(map->page); | |
| 1184 | + kfree(map); | |
| 1185 | + } | |
| 1186 | + kfree(kvm->arch.adapters[i]); | |
| 1187 | + } | |
| 1188 | +} | |
| 1189 | + | |
| 1190 | +static int modify_io_adapter(struct kvm_device *dev, | |
| 1191 | + struct kvm_device_attr *attr) | |
| 1192 | +{ | |
| 1193 | + struct kvm_s390_io_adapter_req req; | |
| 1194 | + struct s390_io_adapter *adapter; | |
| 1195 | + int ret; | |
| 1196 | + | |
| 1197 | + if (copy_from_user(&req, (void __user *)attr->addr, sizeof(req))) | |
| 1198 | + return -EFAULT; | |
| 1199 | + | |
| 1200 | + adapter = get_io_adapter(dev->kvm, req.id); | |
| 1201 | + if (!adapter) | |
| 1202 | + return -EINVAL; | |
| 1203 | + switch (req.type) { | |
| 1204 | + case KVM_S390_IO_ADAPTER_MASK: | |
| 1205 | + ret = kvm_s390_mask_adapter(dev->kvm, req.id, req.mask); | |
| 1206 | + if (ret > 0) | |
| 1207 | + ret = 0; | |
| 1208 | + break; | |
| 1209 | + case KVM_S390_IO_ADAPTER_MAP: | |
| 1210 | + ret = kvm_s390_adapter_map(dev->kvm, req.id, req.addr); | |
| 1211 | + break; | |
| 1212 | + case KVM_S390_IO_ADAPTER_UNMAP: | |
| 1213 | + ret = kvm_s390_adapter_unmap(dev->kvm, req.id, req.addr); | |
| 1214 | + break; | |
| 1215 | + default: | |
| 1216 | + ret = -EINVAL; | |
| 1217 | + } | |
| 1218 | + | |
| 1219 | + return ret; | |
| 1220 | +} | |
| 1221 | + | |
| 1057 | 1222 | static int flic_set_attr(struct kvm_device *dev, struct kvm_device_attr *attr) |
| 1058 | 1223 | { |
| 1059 | 1224 | int r = 0; |
| @@ -1082,6 +1247,12 @@ static int flic_set_attr(struct kvm_device *dev, struct kvm_device_attr *attr) · 22 unmodified lines | ||
| 1082 | 1247 | kvm_for_each_vcpu(i, vcpu, dev->kvm) |
| 1083 | 1248 | kvm_clear_async_pf_completion_queue(vcpu); |
| 1084 | 1249 | break; |
| 1250 | + case KVM_DEV_FLIC_ADAPTER_REGISTER: | |
| 1251 | + r = register_io_adapter(dev, attr); | |
| 1252 | + break; | |
| 1253 | + case KVM_DEV_FLIC_ADAPTER_MODIFY: | |
| 1254 | + r = modify_io_adapter(dev, attr); | |
| 1255 | + break; | |
| 1085 | 1256 | default: |
| 1086 | 1257 | r = -EINVAL; |
| 1087 | 1258 | } |
| @@ -343,6 +343,7 @@ void kvm_arch_destroy_vm(struct kvm *kvm) · 342 unmodified lines | ||
| 343 | 343 | debug_unregister(kvm->arch.dbf); |
| 344 | 344 | if (!kvm_is_ucontrol(kvm)) |
| 345 | 345 | gmap_free(kvm->arch.gmap); |
| 346 | + kvm_s390_destroy_adapters(kvm); | |
| 346 | 347 | } |
| 347 | 348 | |
| 348 | 349 | /* Section: vcpu related */ |
| @@ -136,6 +136,7 @@ int __must_check kvm_s390_inject_vcpu(struct kvm_vcpu *vcpu, · 135 unmodified lines | ||
| 136 | 136 | int __must_check kvm_s390_inject_program_int(struct kvm_vcpu *vcpu, u16 code); |
| 137 | 137 | struct kvm_s390_interrupt_info *kvm_s390_get_io_int(struct kvm *kvm, |
| 138 | 138 | u64 cr6, u64 schid); |
| 139 | +int kvm_s390_mask_adapter(struct kvm *kvm, unsigned int id, bool masked); | |
| 139 | 140 | |
| 140 | 141 | /* implemented in priv.c */ |
| 141 | 142 | int kvm_s390_handle_b2(struct kvm_vcpu *vcpu); |
| @@ -162,5 +163,6 @@ int kvm_s390_handle_diag(struct kvm_vcpu *vcpu); · 20 unmodified lines | ||
| 162 | 163 | /* implemented in interrupt.c */ |
| 163 | 164 | int kvm_cpu_has_interrupt(struct kvm_vcpu *vcpu); |
| 164 | 165 | int psw_extint_disabled(struct kvm_vcpu *vcpu); |
| 166 | +void kvm_s390_destroy_adapters(struct kvm *kvm); | |
| 165 | 167 | |
| 166 | 168 | #endif |