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:42:49 -0400 |
commit | 841b91c584b6d1e2a2cb508bd2d0236cd37e1750 (patch) | |
tree | 0a2f1b2cfa471cefa22bc9c12628361ed2c0ac4c /arch/s390/kvm | |
parent | d938dc55225a7212e7f31c5a8571da304cc3de16 (diff) |
KVM: s390: adapter interrupt sources
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.
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/interrupt.c | 173 | ||||
-rw-r--r-- | arch/s390/kvm/kvm-s390.c | 1 | ||||
-rw-r--r-- | arch/s390/kvm/kvm-s390.h | 2 |
3 files changed, 175 insertions, 1 deletions
diff --git a/arch/s390/kvm/interrupt.c b/arch/s390/kvm/interrupt.c index 79d2e4fa9f9c..7ecef5a18e25 100644 --- a/arch/s390/kvm/interrupt.c +++ b/arch/s390/kvm/interrupt.c | |||
@@ -1,7 +1,7 @@ | |||
1 | /* | 1 | /* |
2 | * handling kvm guest interrupts | 2 | * handling kvm guest interrupts |
3 | * | 3 | * |
4 | * Copyright IBM Corp. 2008 | 4 | * Copyright IBM Corp. 2008,2014 |
5 | * | 5 | * |
6 | * This program is free software; you can redistribute it and/or modify | 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) | 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, | |||
1054 | return r; | 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 | static int flic_set_attr(struct kvm_device *dev, struct kvm_device_attr *attr) | 1222 | static int flic_set_attr(struct kvm_device *dev, struct kvm_device_attr *attr) |
1058 | { | 1223 | { |
1059 | int r = 0; | 1224 | int r = 0; |
@@ -1082,6 +1247,12 @@ static int flic_set_attr(struct kvm_device *dev, struct kvm_device_attr *attr) | |||
1082 | kvm_for_each_vcpu(i, vcpu, dev->kvm) | 1247 | kvm_for_each_vcpu(i, vcpu, dev->kvm) |
1083 | kvm_clear_async_pf_completion_queue(vcpu); | 1248 | kvm_clear_async_pf_completion_queue(vcpu); |
1084 | break; | 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 | default: | 1256 | default: |
1086 | r = -EINVAL; | 1257 | r = -EINVAL; |
1087 | } | 1258 | } |
diff --git a/arch/s390/kvm/kvm-s390.c b/arch/s390/kvm/kvm-s390.c index 9f1e99f12d4f..2e6fbb0b4f68 100644 --- a/arch/s390/kvm/kvm-s390.c +++ b/arch/s390/kvm/kvm-s390.c | |||
@@ -343,6 +343,7 @@ void kvm_arch_destroy_vm(struct kvm *kvm) | |||
343 | debug_unregister(kvm->arch.dbf); | 343 | debug_unregister(kvm->arch.dbf); |
344 | if (!kvm_is_ucontrol(kvm)) | 344 | if (!kvm_is_ucontrol(kvm)) |
345 | gmap_free(kvm->arch.gmap); | 345 | gmap_free(kvm->arch.gmap); |
346 | kvm_s390_destroy_adapters(kvm); | ||
346 | } | 347 | } |
347 | 348 | ||
348 | /* Section: vcpu related */ | 349 | /* Section: vcpu related */ |
diff --git a/arch/s390/kvm/kvm-s390.h b/arch/s390/kvm/kvm-s390.h index ed4750a5bc3c..5502cc951868 100644 --- a/arch/s390/kvm/kvm-s390.h +++ b/arch/s390/kvm/kvm-s390.h | |||
@@ -136,6 +136,7 @@ int __must_check kvm_s390_inject_vcpu(struct kvm_vcpu *vcpu, | |||
136 | int __must_check kvm_s390_inject_program_int(struct kvm_vcpu *vcpu, u16 code); | 136 | int __must_check kvm_s390_inject_program_int(struct kvm_vcpu *vcpu, u16 code); |
137 | struct kvm_s390_interrupt_info *kvm_s390_get_io_int(struct kvm *kvm, | 137 | struct kvm_s390_interrupt_info *kvm_s390_get_io_int(struct kvm *kvm, |
138 | u64 cr6, u64 schid); | 138 | u64 cr6, u64 schid); |
139 | int kvm_s390_mask_adapter(struct kvm *kvm, unsigned int id, bool masked); | ||
139 | 140 | ||
140 | /* implemented in priv.c */ | 141 | /* implemented in priv.c */ |
141 | int kvm_s390_handle_b2(struct kvm_vcpu *vcpu); | 142 | int kvm_s390_handle_b2(struct kvm_vcpu *vcpu); |
@@ -162,5 +163,6 @@ int kvm_s390_handle_diag(struct kvm_vcpu *vcpu); | |||
162 | /* implemented in interrupt.c */ | 163 | /* implemented in interrupt.c */ |
163 | int kvm_cpu_has_interrupt(struct kvm_vcpu *vcpu); | 164 | int kvm_cpu_has_interrupt(struct kvm_vcpu *vcpu); |
164 | int psw_extint_disabled(struct kvm_vcpu *vcpu); | 165 | int psw_extint_disabled(struct kvm_vcpu *vcpu); |
166 | void kvm_s390_destroy_adapters(struct kvm *kvm); | ||
165 | 167 | ||
166 | #endif | 168 | #endif |