diff options
-rw-r--r-- | arch/s390/include/asm/irq.h | 1 | ||||
-rw-r--r-- | arch/s390/include/asm/isc.h | 1 | ||||
-rw-r--r-- | arch/s390/include/asm/kvm_host.h | 3 | ||||
-rw-r--r-- | arch/s390/kernel/irq.c | 1 | ||||
-rw-r--r-- | arch/s390/kvm/interrupt.c | 169 | ||||
-rw-r--r-- | arch/s390/kvm/kvm-s390.c | 2 |
6 files changed, 175 insertions, 2 deletions
diff --git a/arch/s390/include/asm/irq.h b/arch/s390/include/asm/irq.h index 2f7f27e5493f..afaf5e3c57fd 100644 --- a/arch/s390/include/asm/irq.h +++ b/arch/s390/include/asm/irq.h | |||
@@ -62,6 +62,7 @@ enum interruption_class { | |||
62 | IRQIO_MSI, | 62 | IRQIO_MSI, |
63 | IRQIO_VIR, | 63 | IRQIO_VIR, |
64 | IRQIO_VAI, | 64 | IRQIO_VAI, |
65 | IRQIO_GAL, | ||
65 | NMI_NMI, | 66 | NMI_NMI, |
66 | CPU_RST, | 67 | CPU_RST, |
67 | NR_ARCH_IRQS | 68 | NR_ARCH_IRQS |
diff --git a/arch/s390/include/asm/isc.h b/arch/s390/include/asm/isc.h index 6cb9e2ed05b6..b2cc1ec78d06 100644 --- a/arch/s390/include/asm/isc.h +++ b/arch/s390/include/asm/isc.h | |||
@@ -21,6 +21,7 @@ | |||
21 | /* Adapter interrupts. */ | 21 | /* Adapter interrupts. */ |
22 | #define QDIO_AIRQ_ISC IO_SCH_ISC /* I/O subchannel in qdio mode */ | 22 | #define QDIO_AIRQ_ISC IO_SCH_ISC /* I/O subchannel in qdio mode */ |
23 | #define PCI_ISC 2 /* PCI I/O subchannels */ | 23 | #define PCI_ISC 2 /* PCI I/O subchannels */ |
24 | #define GAL_ISC 5 /* GIB alert */ | ||
24 | #define AP_ISC 6 /* adjunct processor (crypto) devices */ | 25 | #define AP_ISC 6 /* adjunct processor (crypto) devices */ |
25 | 26 | ||
26 | /* Functions for registration of I/O interruption subclasses */ | 27 | /* Functions for registration of I/O interruption subclasses */ |
diff --git a/arch/s390/include/asm/kvm_host.h b/arch/s390/include/asm/kvm_host.h index 2cfff617cb21..c5f51566ecd6 100644 --- a/arch/s390/include/asm/kvm_host.h +++ b/arch/s390/include/asm/kvm_host.h | |||
@@ -825,6 +825,9 @@ struct kvm_s390_gisa_iam { | |||
825 | struct kvm_s390_gisa_interrupt { | 825 | struct kvm_s390_gisa_interrupt { |
826 | struct kvm_s390_gisa *origin; | 826 | struct kvm_s390_gisa *origin; |
827 | struct kvm_s390_gisa_iam alert; | 827 | struct kvm_s390_gisa_iam alert; |
828 | struct hrtimer timer; | ||
829 | u64 expires; | ||
830 | DECLARE_BITMAP(kicked_mask, KVM_MAX_VCPUS); | ||
828 | }; | 831 | }; |
829 | 832 | ||
830 | struct kvm_arch{ | 833 | struct kvm_arch{ |
diff --git a/arch/s390/kernel/irq.c b/arch/s390/kernel/irq.c index 0e8d68bac82c..0cd5a5f96729 100644 --- a/arch/s390/kernel/irq.c +++ b/arch/s390/kernel/irq.c | |||
@@ -88,6 +88,7 @@ static const struct irq_class irqclass_sub_desc[] = { | |||
88 | {.irq = IRQIO_MSI, .name = "MSI", .desc = "[I/O] MSI Interrupt" }, | 88 | {.irq = IRQIO_MSI, .name = "MSI", .desc = "[I/O] MSI Interrupt" }, |
89 | {.irq = IRQIO_VIR, .name = "VIR", .desc = "[I/O] Virtual I/O Devices"}, | 89 | {.irq = IRQIO_VIR, .name = "VIR", .desc = "[I/O] Virtual I/O Devices"}, |
90 | {.irq = IRQIO_VAI, .name = "VAI", .desc = "[I/O] Virtual I/O Devices AI"}, | 90 | {.irq = IRQIO_VAI, .name = "VAI", .desc = "[I/O] Virtual I/O Devices AI"}, |
91 | {.irq = IRQIO_GAL, .name = "GAL", .desc = "[I/O] GIB Alert"}, | ||
91 | {.irq = NMI_NMI, .name = "NMI", .desc = "[NMI] Machine Check"}, | 92 | {.irq = NMI_NMI, .name = "NMI", .desc = "[NMI] Machine Check"}, |
92 | {.irq = CPU_RST, .name = "RST", .desc = "[CPU] CPU Restart"}, | 93 | {.irq = CPU_RST, .name = "RST", .desc = "[CPU] CPU Restart"}, |
93 | }; | 94 | }; |
diff --git a/arch/s390/kvm/interrupt.c b/arch/s390/kvm/interrupt.c index 9faaa8f96fc3..dc446203e5a4 100644 --- a/arch/s390/kvm/interrupt.c +++ b/arch/s390/kvm/interrupt.c | |||
@@ -26,6 +26,7 @@ | |||
26 | #include <asm/gmap.h> | 26 | #include <asm/gmap.h> |
27 | #include <asm/switch_to.h> | 27 | #include <asm/switch_to.h> |
28 | #include <asm/nmi.h> | 28 | #include <asm/nmi.h> |
29 | #include <asm/airq.h> | ||
29 | #include "kvm-s390.h" | 30 | #include "kvm-s390.h" |
30 | #include "gaccess.h" | 31 | #include "gaccess.h" |
31 | #include "trace-s390.h" | 32 | #include "trace-s390.h" |
@@ -268,6 +269,38 @@ static inline void gisa_clear_ipm(struct kvm_s390_gisa *gisa) | |||
268 | } while (cmpxchg(&gisa->u64.word[0], word, _word) != word); | 269 | } while (cmpxchg(&gisa->u64.word[0], word, _word) != word); |
269 | } | 270 | } |
270 | 271 | ||
272 | /** | ||
273 | * gisa_get_ipm_or_restore_iam - return IPM or restore GISA IAM | ||
274 | * | ||
275 | * @gi: gisa interrupt struct to work on | ||
276 | * | ||
277 | * Atomically restores the interruption alert mask if none of the | ||
278 | * relevant ISCs are pending and return the IPM. | ||
279 | * | ||
280 | * Returns: the relevant pending ISCs | ||
281 | */ | ||
282 | static inline u8 gisa_get_ipm_or_restore_iam(struct kvm_s390_gisa_interrupt *gi) | ||
283 | { | ||
284 | u8 pending_mask, alert_mask; | ||
285 | u64 word, _word; | ||
286 | |||
287 | do { | ||
288 | word = READ_ONCE(gi->origin->u64.word[0]); | ||
289 | alert_mask = READ_ONCE(gi->alert.mask); | ||
290 | pending_mask = (u8)(word >> 24) & alert_mask; | ||
291 | if (pending_mask) | ||
292 | return pending_mask; | ||
293 | _word = (word & ~0xffUL) | alert_mask; | ||
294 | } while (cmpxchg(&gi->origin->u64.word[0], word, _word) != word); | ||
295 | |||
296 | return 0; | ||
297 | } | ||
298 | |||
299 | static inline int gisa_in_alert_list(struct kvm_s390_gisa *gisa) | ||
300 | { | ||
301 | return READ_ONCE(gisa->next_alert) != (u32)(u64)gisa; | ||
302 | } | ||
303 | |||
271 | static inline void gisa_set_ipm_gisc(struct kvm_s390_gisa *gisa, u32 gisc) | 304 | static inline void gisa_set_ipm_gisc(struct kvm_s390_gisa *gisa, u32 gisc) |
272 | { | 305 | { |
273 | set_bit_inv(IPM_BIT_OFFSET + gisc, (unsigned long *) gisa); | 306 | set_bit_inv(IPM_BIT_OFFSET + gisc, (unsigned long *) gisa); |
@@ -1141,6 +1174,7 @@ static u64 __calculate_sltime(struct kvm_vcpu *vcpu) | |||
1141 | 1174 | ||
1142 | int kvm_s390_handle_wait(struct kvm_vcpu *vcpu) | 1175 | int kvm_s390_handle_wait(struct kvm_vcpu *vcpu) |
1143 | { | 1176 | { |
1177 | struct kvm_s390_gisa_interrupt *gi = &vcpu->kvm->arch.gisa_int; | ||
1144 | u64 sltime; | 1178 | u64 sltime; |
1145 | 1179 | ||
1146 | vcpu->stat.exit_wait_state++; | 1180 | vcpu->stat.exit_wait_state++; |
@@ -1154,6 +1188,11 @@ int kvm_s390_handle_wait(struct kvm_vcpu *vcpu) | |||
1154 | return -EOPNOTSUPP; /* disabled wait */ | 1188 | return -EOPNOTSUPP; /* disabled wait */ |
1155 | } | 1189 | } |
1156 | 1190 | ||
1191 | if (gi->origin && | ||
1192 | (gisa_get_ipm_or_restore_iam(gi) & | ||
1193 | vcpu->arch.sie_block->gcr[6] >> 24)) | ||
1194 | return 0; | ||
1195 | |||
1157 | if (!ckc_interrupts_enabled(vcpu) && | 1196 | if (!ckc_interrupts_enabled(vcpu) && |
1158 | !cpu_timer_interrupts_enabled(vcpu)) { | 1197 | !cpu_timer_interrupts_enabled(vcpu)) { |
1159 | VCPU_EVENT(vcpu, 3, "%s", "enabled wait w/o timer"); | 1198 | VCPU_EVENT(vcpu, 3, "%s", "enabled wait w/o timer"); |
@@ -2939,6 +2978,93 @@ int kvm_s390_get_irq_state(struct kvm_vcpu *vcpu, __u8 __user *buf, int len) | |||
2939 | return n; | 2978 | return n; |
2940 | } | 2979 | } |
2941 | 2980 | ||
2981 | static void __airqs_kick_single_vcpu(struct kvm *kvm, u8 deliverable_mask) | ||
2982 | { | ||
2983 | int vcpu_id, online_vcpus = atomic_read(&kvm->online_vcpus); | ||
2984 | struct kvm_s390_gisa_interrupt *gi = &kvm->arch.gisa_int; | ||
2985 | struct kvm_vcpu *vcpu; | ||
2986 | |||
2987 | for_each_set_bit(vcpu_id, kvm->arch.idle_mask, online_vcpus) { | ||
2988 | vcpu = kvm_get_vcpu(kvm, vcpu_id); | ||
2989 | if (psw_ioint_disabled(vcpu)) | ||
2990 | continue; | ||
2991 | deliverable_mask &= (u8)(vcpu->arch.sie_block->gcr[6] >> 24); | ||
2992 | if (deliverable_mask) { | ||
2993 | /* lately kicked but not yet running */ | ||
2994 | if (test_and_set_bit(vcpu_id, gi->kicked_mask)) | ||
2995 | return; | ||
2996 | kvm_s390_vcpu_wakeup(vcpu); | ||
2997 | return; | ||
2998 | } | ||
2999 | } | ||
3000 | } | ||
3001 | |||
3002 | static enum hrtimer_restart gisa_vcpu_kicker(struct hrtimer *timer) | ||
3003 | { | ||
3004 | struct kvm_s390_gisa_interrupt *gi = | ||
3005 | container_of(timer, struct kvm_s390_gisa_interrupt, timer); | ||
3006 | struct kvm *kvm = | ||
3007 | container_of(gi->origin, struct sie_page2, gisa)->kvm; | ||
3008 | u8 pending_mask; | ||
3009 | |||
3010 | pending_mask = gisa_get_ipm_or_restore_iam(gi); | ||
3011 | if (pending_mask) { | ||
3012 | __airqs_kick_single_vcpu(kvm, pending_mask); | ||
3013 | hrtimer_forward_now(timer, ns_to_ktime(gi->expires)); | ||
3014 | return HRTIMER_RESTART; | ||
3015 | }; | ||
3016 | |||
3017 | return HRTIMER_NORESTART; | ||
3018 | } | ||
3019 | |||
3020 | #define NULL_GISA_ADDR 0x00000000UL | ||
3021 | #define NONE_GISA_ADDR 0x00000001UL | ||
3022 | #define GISA_ADDR_MASK 0xfffff000UL | ||
3023 | |||
3024 | static void process_gib_alert_list(void) | ||
3025 | { | ||
3026 | struct kvm_s390_gisa_interrupt *gi; | ||
3027 | struct kvm_s390_gisa *gisa; | ||
3028 | struct kvm *kvm; | ||
3029 | u32 final, origin = 0UL; | ||
3030 | |||
3031 | do { | ||
3032 | /* | ||
3033 | * If the NONE_GISA_ADDR is still stored in the alert list | ||
3034 | * origin, we will leave the outer loop. No further GISA has | ||
3035 | * been added to the alert list by millicode while processing | ||
3036 | * the current alert list. | ||
3037 | */ | ||
3038 | final = (origin & NONE_GISA_ADDR); | ||
3039 | /* | ||
3040 | * Cut off the alert list and store the NONE_GISA_ADDR in the | ||
3041 | * alert list origin to avoid further GAL interruptions. | ||
3042 | * A new alert list can be build up by millicode in parallel | ||
3043 | * for guests not in the yet cut-off alert list. When in the | ||
3044 | * final loop, store the NULL_GISA_ADDR instead. This will re- | ||
3045 | * enable GAL interruptions on the host again. | ||
3046 | */ | ||
3047 | origin = xchg(&gib->alert_list_origin, | ||
3048 | (!final) ? NONE_GISA_ADDR : NULL_GISA_ADDR); | ||
3049 | /* | ||
3050 | * Loop through the just cut-off alert list and start the | ||
3051 | * gisa timers to kick idle vcpus to consume the pending | ||
3052 | * interruptions asap. | ||
3053 | */ | ||
3054 | while (origin & GISA_ADDR_MASK) { | ||
3055 | gisa = (struct kvm_s390_gisa *)(u64)origin; | ||
3056 | origin = gisa->next_alert; | ||
3057 | gisa->next_alert = (u32)(u64)gisa; | ||
3058 | kvm = container_of(gisa, struct sie_page2, gisa)->kvm; | ||
3059 | gi = &kvm->arch.gisa_int; | ||
3060 | if (hrtimer_active(&gi->timer)) | ||
3061 | hrtimer_cancel(&gi->timer); | ||
3062 | hrtimer_start(&gi->timer, 0, HRTIMER_MODE_REL); | ||
3063 | } | ||
3064 | } while (!final); | ||
3065 | |||
3066 | } | ||
3067 | |||
2942 | void kvm_s390_gisa_clear(struct kvm *kvm) | 3068 | void kvm_s390_gisa_clear(struct kvm *kvm) |
2943 | { | 3069 | { |
2944 | struct kvm_s390_gisa_interrupt *gi = &kvm->arch.gisa_int; | 3070 | struct kvm_s390_gisa_interrupt *gi = &kvm->arch.gisa_int; |
@@ -2958,6 +3084,9 @@ void kvm_s390_gisa_init(struct kvm *kvm) | |||
2958 | gi->origin = &kvm->arch.sie_page2->gisa; | 3084 | gi->origin = &kvm->arch.sie_page2->gisa; |
2959 | gi->alert.mask = 0; | 3085 | gi->alert.mask = 0; |
2960 | spin_lock_init(&gi->alert.ref_lock); | 3086 | spin_lock_init(&gi->alert.ref_lock); |
3087 | gi->expires = 50 * 1000; /* 50 usec */ | ||
3088 | hrtimer_init(&gi->timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL); | ||
3089 | gi->timer.function = gisa_vcpu_kicker; | ||
2961 | memset(gi->origin, 0, sizeof(struct kvm_s390_gisa)); | 3090 | memset(gi->origin, 0, sizeof(struct kvm_s390_gisa)); |
2962 | gi->origin->next_alert = (u32)(u64)gi->origin; | 3091 | gi->origin->next_alert = (u32)(u64)gi->origin; |
2963 | VM_EVENT(kvm, 3, "gisa 0x%pK initialized", gi->origin); | 3092 | VM_EVENT(kvm, 3, "gisa 0x%pK initialized", gi->origin); |
@@ -2965,7 +3094,17 @@ void kvm_s390_gisa_init(struct kvm *kvm) | |||
2965 | 3094 | ||
2966 | void kvm_s390_gisa_destroy(struct kvm *kvm) | 3095 | void kvm_s390_gisa_destroy(struct kvm *kvm) |
2967 | { | 3096 | { |
2968 | kvm->arch.gisa_int.origin = NULL; | 3097 | struct kvm_s390_gisa_interrupt *gi = &kvm->arch.gisa_int; |
3098 | |||
3099 | if (!gi->origin) | ||
3100 | return; | ||
3101 | if (gi->alert.mask) | ||
3102 | KVM_EVENT(3, "vm 0x%pK has unexpected iam 0x%02x", | ||
3103 | kvm, gi->alert.mask); | ||
3104 | while (gisa_in_alert_list(gi->origin)) | ||
3105 | cpu_relax(); | ||
3106 | hrtimer_cancel(&gi->timer); | ||
3107 | gi->origin = NULL; | ||
2969 | } | 3108 | } |
2970 | 3109 | ||
2971 | /** | 3110 | /** |
@@ -3051,11 +3190,23 @@ out: | |||
3051 | } | 3190 | } |
3052 | EXPORT_SYMBOL_GPL(kvm_s390_gisc_unregister); | 3191 | EXPORT_SYMBOL_GPL(kvm_s390_gisc_unregister); |
3053 | 3192 | ||
3193 | static void gib_alert_irq_handler(struct airq_struct *airq) | ||
3194 | { | ||
3195 | inc_irq_stat(IRQIO_GAL); | ||
3196 | process_gib_alert_list(); | ||
3197 | } | ||
3198 | |||
3199 | static struct airq_struct gib_alert_irq = { | ||
3200 | .handler = gib_alert_irq_handler, | ||
3201 | .lsi_ptr = &gib_alert_irq.lsi_mask, | ||
3202 | }; | ||
3203 | |||
3054 | void kvm_s390_gib_destroy(void) | 3204 | void kvm_s390_gib_destroy(void) |
3055 | { | 3205 | { |
3056 | if (!gib) | 3206 | if (!gib) |
3057 | return; | 3207 | return; |
3058 | chsc_sgib(0); | 3208 | chsc_sgib(0); |
3209 | unregister_adapter_interrupt(&gib_alert_irq); | ||
3059 | free_page((unsigned long)gib); | 3210 | free_page((unsigned long)gib); |
3060 | gib = NULL; | 3211 | gib = NULL; |
3061 | } | 3212 | } |
@@ -3075,16 +3226,30 @@ int kvm_s390_gib_init(u8 nisc) | |||
3075 | goto out; | 3226 | goto out; |
3076 | } | 3227 | } |
3077 | 3228 | ||
3229 | gib_alert_irq.isc = nisc; | ||
3230 | if (register_adapter_interrupt(&gib_alert_irq)) { | ||
3231 | pr_err("Registering the GIB alert interruption handler failed\n"); | ||
3232 | rc = -EIO; | ||
3233 | goto out_free_gib; | ||
3234 | } | ||
3235 | |||
3078 | gib->nisc = nisc; | 3236 | gib->nisc = nisc; |
3079 | if (chsc_sgib((u32)(u64)gib)) { | 3237 | if (chsc_sgib((u32)(u64)gib)) { |
3080 | pr_err("Associating the GIB with the AIV facility failed\n"); | 3238 | pr_err("Associating the GIB with the AIV facility failed\n"); |
3081 | free_page((unsigned long)gib); | 3239 | free_page((unsigned long)gib); |
3082 | gib = NULL; | 3240 | gib = NULL; |
3083 | rc = -EIO; | 3241 | rc = -EIO; |
3084 | goto out; | 3242 | goto out_unreg_gal; |
3085 | } | 3243 | } |
3086 | 3244 | ||
3087 | KVM_EVENT(3, "gib 0x%pK (nisc=%d) initialized", gib, gib->nisc); | 3245 | KVM_EVENT(3, "gib 0x%pK (nisc=%d) initialized", gib, gib->nisc); |
3246 | goto out; | ||
3247 | |||
3248 | out_unreg_gal: | ||
3249 | unregister_adapter_interrupt(&gib_alert_irq); | ||
3250 | out_free_gib: | ||
3251 | free_page((unsigned long)gib); | ||
3252 | gib = NULL; | ||
3088 | out: | 3253 | out: |
3089 | return rc; | 3254 | return rc; |
3090 | } | 3255 | } |
diff --git a/arch/s390/kvm/kvm-s390.c b/arch/s390/kvm/kvm-s390.c index 0de062e989e2..0099fbda2e98 100644 --- a/arch/s390/kvm/kvm-s390.c +++ b/arch/s390/kvm/kvm-s390.c | |||
@@ -3460,6 +3460,8 @@ static int vcpu_pre_run(struct kvm_vcpu *vcpu) | |||
3460 | kvm_s390_patch_guest_per_regs(vcpu); | 3460 | kvm_s390_patch_guest_per_regs(vcpu); |
3461 | } | 3461 | } |
3462 | 3462 | ||
3463 | clear_bit(vcpu->vcpu_id, vcpu->kvm->arch.gisa_int.kicked_mask); | ||
3464 | |||
3463 | vcpu->arch.sie_block->icptcode = 0; | 3465 | vcpu->arch.sie_block->icptcode = 0; |
3464 | cpuflags = atomic_read(&vcpu->arch.sie_block->cpuflags); | 3466 | cpuflags = atomic_read(&vcpu->arch.sie_block->cpuflags); |
3465 | VCPU_EVENT(vcpu, 6, "entering sie flags %x", cpuflags); | 3467 | VCPU_EVENT(vcpu, 6, "entering sie flags %x", cpuflags); |