diff options
-rw-r--r-- | arch/s390/include/asm/airq.h | 14 | ||||
-rw-r--r-- | arch/s390/include/asm/irq.h | 1 | ||||
-rw-r--r-- | arch/s390/include/asm/kvm_host.h | 5 | ||||
-rw-r--r-- | arch/s390/include/uapi/asm/kvm.h | 2 | ||||
-rw-r--r-- | arch/s390/kernel/irq.c | 1 | ||||
-rw-r--r-- | arch/s390/kvm/interrupt.c | 6 | ||||
-rw-r--r-- | arch/s390/kvm/kvm-s390.c | 22 | ||||
-rw-r--r-- | arch/s390/kvm/priv.c | 7 | ||||
-rw-r--r-- | arch/s390/kvm/sigp.c | 148 | ||||
-rw-r--r-- | drivers/s390/cio/airq.c | 66 | ||||
-rw-r--r-- | drivers/s390/kvm/virtio_ccw.c | 313 |
11 files changed, 438 insertions, 147 deletions
diff --git a/arch/s390/include/asm/airq.h b/arch/s390/include/asm/airq.h index 4bbb5957ed1b..bd93ff6661b8 100644 --- a/arch/s390/include/asm/airq.h +++ b/arch/s390/include/asm/airq.h | |||
@@ -44,11 +44,21 @@ struct airq_iv { | |||
44 | 44 | ||
45 | struct airq_iv *airq_iv_create(unsigned long bits, unsigned long flags); | 45 | struct airq_iv *airq_iv_create(unsigned long bits, unsigned long flags); |
46 | void airq_iv_release(struct airq_iv *iv); | 46 | void airq_iv_release(struct airq_iv *iv); |
47 | unsigned long airq_iv_alloc_bit(struct airq_iv *iv); | 47 | unsigned long airq_iv_alloc(struct airq_iv *iv, unsigned long num); |
48 | void airq_iv_free_bit(struct airq_iv *iv, unsigned long bit); | 48 | void airq_iv_free(struct airq_iv *iv, unsigned long bit, unsigned long num); |
49 | unsigned long airq_iv_scan(struct airq_iv *iv, unsigned long start, | 49 | unsigned long airq_iv_scan(struct airq_iv *iv, unsigned long start, |
50 | unsigned long end); | 50 | unsigned long end); |
51 | 51 | ||
52 | static inline unsigned long airq_iv_alloc_bit(struct airq_iv *iv) | ||
53 | { | ||
54 | return airq_iv_alloc(iv, 1); | ||
55 | } | ||
56 | |||
57 | static inline void airq_iv_free_bit(struct airq_iv *iv, unsigned long bit) | ||
58 | { | ||
59 | airq_iv_free(iv, bit, 1); | ||
60 | } | ||
61 | |||
52 | static inline unsigned long airq_iv_end(struct airq_iv *iv) | 62 | static inline unsigned long airq_iv_end(struct airq_iv *iv) |
53 | { | 63 | { |
54 | return iv->end; | 64 | return iv->end; |
diff --git a/arch/s390/include/asm/irq.h b/arch/s390/include/asm/irq.h index 5f8bcc5fe423..35f0faab5361 100644 --- a/arch/s390/include/asm/irq.h +++ b/arch/s390/include/asm/irq.h | |||
@@ -53,6 +53,7 @@ enum interruption_class { | |||
53 | IRQIO_PCI, | 53 | IRQIO_PCI, |
54 | IRQIO_MSI, | 54 | IRQIO_MSI, |
55 | IRQIO_VIR, | 55 | IRQIO_VIR, |
56 | IRQIO_VAI, | ||
56 | NMI_NMI, | 57 | NMI_NMI, |
57 | CPU_RST, | 58 | CPU_RST, |
58 | NR_ARCH_IRQS | 59 | NR_ARCH_IRQS |
diff --git a/arch/s390/include/asm/kvm_host.h b/arch/s390/include/asm/kvm_host.h index 2c69ba285e81..734d302ba389 100644 --- a/arch/s390/include/asm/kvm_host.h +++ b/arch/s390/include/asm/kvm_host.h | |||
@@ -107,7 +107,9 @@ struct kvm_s390_sie_block { | |||
107 | __u64 gbea; /* 0x0180 */ | 107 | __u64 gbea; /* 0x0180 */ |
108 | __u8 reserved188[24]; /* 0x0188 */ | 108 | __u8 reserved188[24]; /* 0x0188 */ |
109 | __u32 fac; /* 0x01a0 */ | 109 | __u32 fac; /* 0x01a0 */ |
110 | __u8 reserved1a4[68]; /* 0x01a4 */ | 110 | __u8 reserved1a4[58]; /* 0x01a4 */ |
111 | __u64 pp; /* 0x01de */ | ||
112 | __u8 reserved1e6[2]; /* 0x01e6 */ | ||
111 | __u64 itdba; /* 0x01e8 */ | 113 | __u64 itdba; /* 0x01e8 */ |
112 | __u8 reserved1f0[16]; /* 0x01f0 */ | 114 | __u8 reserved1f0[16]; /* 0x01f0 */ |
113 | } __attribute__((packed)); | 115 | } __attribute__((packed)); |
@@ -213,7 +215,6 @@ struct kvm_s390_float_interrupt { | |||
213 | int next_rr_cpu; | 215 | int next_rr_cpu; |
214 | unsigned long idle_mask[(KVM_MAX_VCPUS + sizeof(long) - 1) | 216 | unsigned long idle_mask[(KVM_MAX_VCPUS + sizeof(long) - 1) |
215 | / sizeof(long)]; | 217 | / sizeof(long)]; |
216 | struct kvm_s390_local_interrupt *local_int[KVM_MAX_VCPUS]; | ||
217 | unsigned int irq_count; | 218 | unsigned int irq_count; |
218 | }; | 219 | }; |
219 | 220 | ||
diff --git a/arch/s390/include/uapi/asm/kvm.h b/arch/s390/include/uapi/asm/kvm.h index cb4c1eb8a0a5..2f0ade24f96a 100644 --- a/arch/s390/include/uapi/asm/kvm.h +++ b/arch/s390/include/uapi/asm/kvm.h | |||
@@ -76,4 +76,6 @@ struct kvm_sync_regs { | |||
76 | #define KVM_REG_S390_PFTOKEN (KVM_REG_S390 | KVM_REG_SIZE_U64 | 0x5) | 76 | #define KVM_REG_S390_PFTOKEN (KVM_REG_S390 | KVM_REG_SIZE_U64 | 0x5) |
77 | #define KVM_REG_S390_PFCOMPARE (KVM_REG_S390 | KVM_REG_SIZE_U64 | 0x6) | 77 | #define KVM_REG_S390_PFCOMPARE (KVM_REG_S390 | KVM_REG_SIZE_U64 | 0x6) |
78 | #define KVM_REG_S390_PFSELECT (KVM_REG_S390 | KVM_REG_SIZE_U64 | 0x7) | 78 | #define KVM_REG_S390_PFSELECT (KVM_REG_S390 | KVM_REG_SIZE_U64 | 0x7) |
79 | #define KVM_REG_S390_PP (KVM_REG_S390 | KVM_REG_SIZE_U64 | 0x8) | ||
80 | #define KVM_REG_S390_GBEA (KVM_REG_S390 | KVM_REG_SIZE_U64 | 0x9) | ||
79 | #endif | 81 | #endif |
diff --git a/arch/s390/kernel/irq.c b/arch/s390/kernel/irq.c index bb27a262c44a..c288ef7e47b4 100644 --- a/arch/s390/kernel/irq.c +++ b/arch/s390/kernel/irq.c | |||
@@ -84,6 +84,7 @@ static const struct irq_class irqclass_sub_desc[NR_ARCH_IRQS] = { | |||
84 | [IRQIO_PCI] = {.name = "PCI", .desc = "[I/O] PCI Interrupt" }, | 84 | [IRQIO_PCI] = {.name = "PCI", .desc = "[I/O] PCI Interrupt" }, |
85 | [IRQIO_MSI] = {.name = "MSI", .desc = "[I/O] MSI Interrupt" }, | 85 | [IRQIO_MSI] = {.name = "MSI", .desc = "[I/O] MSI Interrupt" }, |
86 | [IRQIO_VIR] = {.name = "VIR", .desc = "[I/O] Virtual I/O Devices"}, | 86 | [IRQIO_VIR] = {.name = "VIR", .desc = "[I/O] Virtual I/O Devices"}, |
87 | [IRQIO_VAI] = {.name = "VAI", .desc = "[I/O] Virtual I/O Devices AI"}, | ||
87 | [NMI_NMI] = {.name = "NMI", .desc = "[NMI] Machine Check"}, | 88 | [NMI_NMI] = {.name = "NMI", .desc = "[NMI] Machine Check"}, |
88 | [CPU_RST] = {.name = "RST", .desc = "[CPU] CPU Restart"}, | 89 | [CPU_RST] = {.name = "RST", .desc = "[CPU] CPU Restart"}, |
89 | }; | 90 | }; |
diff --git a/arch/s390/kvm/interrupt.c b/arch/s390/kvm/interrupt.c index fff070bd0159..1d0f9d532c0b 100644 --- a/arch/s390/kvm/interrupt.c +++ b/arch/s390/kvm/interrupt.c | |||
@@ -692,6 +692,7 @@ static int __inject_vm(struct kvm *kvm, struct kvm_s390_interrupt_info *inti) | |||
692 | struct kvm_s390_local_interrupt *li; | 692 | struct kvm_s390_local_interrupt *li; |
693 | struct kvm_s390_float_interrupt *fi; | 693 | struct kvm_s390_float_interrupt *fi; |
694 | struct kvm_s390_interrupt_info *iter; | 694 | struct kvm_s390_interrupt_info *iter; |
695 | struct kvm_vcpu *dst_vcpu = NULL; | ||
695 | int sigcpu; | 696 | int sigcpu; |
696 | int rc = 0; | 697 | int rc = 0; |
697 | 698 | ||
@@ -726,9 +727,10 @@ static int __inject_vm(struct kvm *kvm, struct kvm_s390_interrupt_info *inti) | |||
726 | sigcpu = fi->next_rr_cpu++; | 727 | sigcpu = fi->next_rr_cpu++; |
727 | if (sigcpu == KVM_MAX_VCPUS) | 728 | if (sigcpu == KVM_MAX_VCPUS) |
728 | sigcpu = fi->next_rr_cpu = 0; | 729 | sigcpu = fi->next_rr_cpu = 0; |
729 | } while (fi->local_int[sigcpu] == NULL); | 730 | } while (kvm_get_vcpu(kvm, sigcpu) == NULL); |
730 | } | 731 | } |
731 | li = fi->local_int[sigcpu]; | 732 | dst_vcpu = kvm_get_vcpu(kvm, sigcpu); |
733 | li = &dst_vcpu->arch.local_int; | ||
732 | spin_lock_bh(&li->lock); | 734 | spin_lock_bh(&li->lock); |
733 | atomic_set_mask(CPUSTAT_EXT_INT, li->cpuflags); | 735 | atomic_set_mask(CPUSTAT_EXT_INT, li->cpuflags); |
734 | if (waitqueue_active(li->wq)) | 736 | if (waitqueue_active(li->wq)) |
diff --git a/arch/s390/kvm/kvm-s390.c b/arch/s390/kvm/kvm-s390.c index 18959bb75c51..9136f8d40850 100644 --- a/arch/s390/kvm/kvm-s390.c +++ b/arch/s390/kvm/kvm-s390.c | |||
@@ -386,6 +386,7 @@ static void kvm_s390_vcpu_initial_reset(struct kvm_vcpu *vcpu) | |||
386 | vcpu->arch.guest_fpregs.fpc = 0; | 386 | vcpu->arch.guest_fpregs.fpc = 0; |
387 | asm volatile("lfpc %0" : : "Q" (vcpu->arch.guest_fpregs.fpc)); | 387 | asm volatile("lfpc %0" : : "Q" (vcpu->arch.guest_fpregs.fpc)); |
388 | vcpu->arch.sie_block->gbea = 1; | 388 | vcpu->arch.sie_block->gbea = 1; |
389 | vcpu->arch.sie_block->pp = 0; | ||
389 | vcpu->arch.pfault_token = KVM_S390_PFAULT_TOKEN_INVALID; | 390 | vcpu->arch.pfault_token = KVM_S390_PFAULT_TOKEN_INVALID; |
390 | kvm_clear_async_pf_completion_queue(vcpu); | 391 | kvm_clear_async_pf_completion_queue(vcpu); |
391 | atomic_set_mask(CPUSTAT_STOPPED, &vcpu->arch.sie_block->cpuflags); | 392 | atomic_set_mask(CPUSTAT_STOPPED, &vcpu->arch.sie_block->cpuflags); |
@@ -459,11 +460,8 @@ struct kvm_vcpu *kvm_arch_vcpu_create(struct kvm *kvm, | |||
459 | spin_lock_init(&vcpu->arch.local_int.lock); | 460 | spin_lock_init(&vcpu->arch.local_int.lock); |
460 | INIT_LIST_HEAD(&vcpu->arch.local_int.list); | 461 | INIT_LIST_HEAD(&vcpu->arch.local_int.list); |
461 | vcpu->arch.local_int.float_int = &kvm->arch.float_int; | 462 | vcpu->arch.local_int.float_int = &kvm->arch.float_int; |
462 | spin_lock(&kvm->arch.float_int.lock); | ||
463 | kvm->arch.float_int.local_int[id] = &vcpu->arch.local_int; | ||
464 | vcpu->arch.local_int.wq = &vcpu->wq; | 463 | vcpu->arch.local_int.wq = &vcpu->wq; |
465 | vcpu->arch.local_int.cpuflags = &vcpu->arch.sie_block->cpuflags; | 464 | vcpu->arch.local_int.cpuflags = &vcpu->arch.sie_block->cpuflags; |
466 | spin_unlock(&kvm->arch.float_int.lock); | ||
467 | 465 | ||
468 | rc = kvm_vcpu_init(vcpu, kvm, id); | 466 | rc = kvm_vcpu_init(vcpu, kvm, id); |
469 | if (rc) | 467 | if (rc) |
@@ -571,6 +569,14 @@ static int kvm_arch_vcpu_ioctl_get_one_reg(struct kvm_vcpu *vcpu, | |||
571 | r = put_user(vcpu->arch.pfault_select, | 569 | r = put_user(vcpu->arch.pfault_select, |
572 | (u64 __user *)reg->addr); | 570 | (u64 __user *)reg->addr); |
573 | break; | 571 | break; |
572 | case KVM_REG_S390_PP: | ||
573 | r = put_user(vcpu->arch.sie_block->pp, | ||
574 | (u64 __user *)reg->addr); | ||
575 | break; | ||
576 | case KVM_REG_S390_GBEA: | ||
577 | r = put_user(vcpu->arch.sie_block->gbea, | ||
578 | (u64 __user *)reg->addr); | ||
579 | break; | ||
574 | default: | 580 | default: |
575 | break; | 581 | break; |
576 | } | 582 | } |
@@ -612,6 +618,14 @@ static int kvm_arch_vcpu_ioctl_set_one_reg(struct kvm_vcpu *vcpu, | |||
612 | r = get_user(vcpu->arch.pfault_select, | 618 | r = get_user(vcpu->arch.pfault_select, |
613 | (u64 __user *)reg->addr); | 619 | (u64 __user *)reg->addr); |
614 | break; | 620 | break; |
621 | case KVM_REG_S390_PP: | ||
622 | r = get_user(vcpu->arch.sie_block->pp, | ||
623 | (u64 __user *)reg->addr); | ||
624 | break; | ||
625 | case KVM_REG_S390_GBEA: | ||
626 | r = get_user(vcpu->arch.sie_block->gbea, | ||
627 | (u64 __user *)reg->addr); | ||
628 | break; | ||
615 | default: | 629 | default: |
616 | break; | 630 | break; |
617 | } | 631 | } |
@@ -935,7 +949,7 @@ int kvm_arch_vcpu_ioctl_run(struct kvm_vcpu *vcpu, struct kvm_run *kvm_run) | |||
935 | 949 | ||
936 | atomic_clear_mask(CPUSTAT_STOPPED, &vcpu->arch.sie_block->cpuflags); | 950 | atomic_clear_mask(CPUSTAT_STOPPED, &vcpu->arch.sie_block->cpuflags); |
937 | 951 | ||
938 | BUG_ON(vcpu->kvm->arch.float_int.local_int[vcpu->vcpu_id] == NULL); | 952 | BUG_ON(kvm_get_vcpu(vcpu->kvm, vcpu->vcpu_id) == NULL); |
939 | 953 | ||
940 | switch (kvm_run->exit_reason) { | 954 | switch (kvm_run->exit_reason) { |
941 | case KVM_EXIT_S390_SIEIC: | 955 | case KVM_EXIT_S390_SIEIC: |
diff --git a/arch/s390/kvm/priv.c b/arch/s390/kvm/priv.c index 75beea632a10..ae9e8ee21557 100644 --- a/arch/s390/kvm/priv.c +++ b/arch/s390/kvm/priv.c | |||
@@ -396,15 +396,10 @@ static int handle_stidp(struct kvm_vcpu *vcpu) | |||
396 | 396 | ||
397 | static void handle_stsi_3_2_2(struct kvm_vcpu *vcpu, struct sysinfo_3_2_2 *mem) | 397 | static void handle_stsi_3_2_2(struct kvm_vcpu *vcpu, struct sysinfo_3_2_2 *mem) |
398 | { | 398 | { |
399 | struct kvm_s390_float_interrupt *fi = &vcpu->kvm->arch.float_int; | ||
400 | int cpus = 0; | 399 | int cpus = 0; |
401 | int n; | 400 | int n; |
402 | 401 | ||
403 | spin_lock(&fi->lock); | 402 | cpus = atomic_read(&vcpu->kvm->online_vcpus); |
404 | for (n = 0; n < KVM_MAX_VCPUS; n++) | ||
405 | if (fi->local_int[n]) | ||
406 | cpus++; | ||
407 | spin_unlock(&fi->lock); | ||
408 | 403 | ||
409 | /* deal with other level 3 hypervisors */ | 404 | /* deal with other level 3 hypervisors */ |
410 | if (stsi(mem, 3, 2, 2)) | 405 | if (stsi(mem, 3, 2, 2)) |
diff --git a/arch/s390/kvm/sigp.c b/arch/s390/kvm/sigp.c index fe9442d39f0e..3fe44c441609 100644 --- a/arch/s390/kvm/sigp.c +++ b/arch/s390/kvm/sigp.c | |||
@@ -23,29 +23,30 @@ | |||
23 | static int __sigp_sense(struct kvm_vcpu *vcpu, u16 cpu_addr, | 23 | static int __sigp_sense(struct kvm_vcpu *vcpu, u16 cpu_addr, |
24 | u64 *reg) | 24 | u64 *reg) |
25 | { | 25 | { |
26 | struct kvm_s390_float_interrupt *fi = &vcpu->kvm->arch.float_int; | 26 | struct kvm_s390_local_interrupt *li; |
27 | struct kvm_vcpu *dst_vcpu = NULL; | ||
28 | int cpuflags; | ||
27 | int rc; | 29 | int rc; |
28 | 30 | ||
29 | if (cpu_addr >= KVM_MAX_VCPUS) | 31 | if (cpu_addr >= KVM_MAX_VCPUS) |
30 | return SIGP_CC_NOT_OPERATIONAL; | 32 | return SIGP_CC_NOT_OPERATIONAL; |
31 | 33 | ||
32 | spin_lock(&fi->lock); | 34 | dst_vcpu = kvm_get_vcpu(vcpu->kvm, cpu_addr); |
33 | if (fi->local_int[cpu_addr] == NULL) | 35 | if (!dst_vcpu) |
34 | rc = SIGP_CC_NOT_OPERATIONAL; | 36 | return SIGP_CC_NOT_OPERATIONAL; |
35 | else if (!(atomic_read(fi->local_int[cpu_addr]->cpuflags) | 37 | li = &dst_vcpu->arch.local_int; |
36 | & (CPUSTAT_ECALL_PEND | CPUSTAT_STOPPED))) | 38 | |
39 | cpuflags = atomic_read(li->cpuflags); | ||
40 | if (!(cpuflags & (CPUSTAT_ECALL_PEND | CPUSTAT_STOPPED))) | ||
37 | rc = SIGP_CC_ORDER_CODE_ACCEPTED; | 41 | rc = SIGP_CC_ORDER_CODE_ACCEPTED; |
38 | else { | 42 | else { |
39 | *reg &= 0xffffffff00000000UL; | 43 | *reg &= 0xffffffff00000000UL; |
40 | if (atomic_read(fi->local_int[cpu_addr]->cpuflags) | 44 | if (cpuflags & CPUSTAT_ECALL_PEND) |
41 | & CPUSTAT_ECALL_PEND) | ||
42 | *reg |= SIGP_STATUS_EXT_CALL_PENDING; | 45 | *reg |= SIGP_STATUS_EXT_CALL_PENDING; |
43 | if (atomic_read(fi->local_int[cpu_addr]->cpuflags) | 46 | if (cpuflags & CPUSTAT_STOPPED) |
44 | & CPUSTAT_STOPPED) | ||
45 | *reg |= SIGP_STATUS_STOPPED; | 47 | *reg |= SIGP_STATUS_STOPPED; |
46 | rc = SIGP_CC_STATUS_STORED; | 48 | rc = SIGP_CC_STATUS_STORED; |
47 | } | 49 | } |
48 | spin_unlock(&fi->lock); | ||
49 | 50 | ||
50 | VCPU_EVENT(vcpu, 4, "sensed status of cpu %x rc %x", cpu_addr, rc); | 51 | VCPU_EVENT(vcpu, 4, "sensed status of cpu %x rc %x", cpu_addr, rc); |
51 | return rc; | 52 | return rc; |
@@ -53,10 +54,9 @@ static int __sigp_sense(struct kvm_vcpu *vcpu, u16 cpu_addr, | |||
53 | 54 | ||
54 | static int __sigp_emergency(struct kvm_vcpu *vcpu, u16 cpu_addr) | 55 | static int __sigp_emergency(struct kvm_vcpu *vcpu, u16 cpu_addr) |
55 | { | 56 | { |
56 | struct kvm_s390_float_interrupt *fi = &vcpu->kvm->arch.float_int; | ||
57 | struct kvm_s390_local_interrupt *li; | 57 | struct kvm_s390_local_interrupt *li; |
58 | struct kvm_s390_interrupt_info *inti; | 58 | struct kvm_s390_interrupt_info *inti; |
59 | int rc; | 59 | struct kvm_vcpu *dst_vcpu = NULL; |
60 | 60 | ||
61 | if (cpu_addr >= KVM_MAX_VCPUS) | 61 | if (cpu_addr >= KVM_MAX_VCPUS) |
62 | return SIGP_CC_NOT_OPERATIONAL; | 62 | return SIGP_CC_NOT_OPERATIONAL; |
@@ -68,13 +68,10 @@ static int __sigp_emergency(struct kvm_vcpu *vcpu, u16 cpu_addr) | |||
68 | inti->type = KVM_S390_INT_EMERGENCY; | 68 | inti->type = KVM_S390_INT_EMERGENCY; |
69 | inti->emerg.code = vcpu->vcpu_id; | 69 | inti->emerg.code = vcpu->vcpu_id; |
70 | 70 | ||
71 | spin_lock(&fi->lock); | 71 | dst_vcpu = kvm_get_vcpu(vcpu->kvm, cpu_addr); |
72 | li = fi->local_int[cpu_addr]; | 72 | if (!dst_vcpu) |
73 | if (li == NULL) { | 73 | return SIGP_CC_NOT_OPERATIONAL; |
74 | rc = SIGP_CC_NOT_OPERATIONAL; | 74 | li = &dst_vcpu->arch.local_int; |
75 | kfree(inti); | ||
76 | goto unlock; | ||
77 | } | ||
78 | spin_lock_bh(&li->lock); | 75 | spin_lock_bh(&li->lock); |
79 | list_add_tail(&inti->list, &li->list); | 76 | list_add_tail(&inti->list, &li->list); |
80 | atomic_set(&li->active, 1); | 77 | atomic_set(&li->active, 1); |
@@ -82,11 +79,9 @@ static int __sigp_emergency(struct kvm_vcpu *vcpu, u16 cpu_addr) | |||
82 | if (waitqueue_active(li->wq)) | 79 | if (waitqueue_active(li->wq)) |
83 | wake_up_interruptible(li->wq); | 80 | wake_up_interruptible(li->wq); |
84 | spin_unlock_bh(&li->lock); | 81 | spin_unlock_bh(&li->lock); |
85 | rc = SIGP_CC_ORDER_CODE_ACCEPTED; | ||
86 | VCPU_EVENT(vcpu, 4, "sent sigp emerg to cpu %x", cpu_addr); | 82 | VCPU_EVENT(vcpu, 4, "sent sigp emerg to cpu %x", cpu_addr); |
87 | unlock: | 83 | |
88 | spin_unlock(&fi->lock); | 84 | return SIGP_CC_ORDER_CODE_ACCEPTED; |
89 | return rc; | ||
90 | } | 85 | } |
91 | 86 | ||
92 | static int __sigp_conditional_emergency(struct kvm_vcpu *vcpu, u16 cpu_addr, | 87 | static int __sigp_conditional_emergency(struct kvm_vcpu *vcpu, u16 cpu_addr, |
@@ -122,10 +117,9 @@ static int __sigp_conditional_emergency(struct kvm_vcpu *vcpu, u16 cpu_addr, | |||
122 | 117 | ||
123 | static int __sigp_external_call(struct kvm_vcpu *vcpu, u16 cpu_addr) | 118 | static int __sigp_external_call(struct kvm_vcpu *vcpu, u16 cpu_addr) |
124 | { | 119 | { |
125 | struct kvm_s390_float_interrupt *fi = &vcpu->kvm->arch.float_int; | ||
126 | struct kvm_s390_local_interrupt *li; | 120 | struct kvm_s390_local_interrupt *li; |
127 | struct kvm_s390_interrupt_info *inti; | 121 | struct kvm_s390_interrupt_info *inti; |
128 | int rc; | 122 | struct kvm_vcpu *dst_vcpu = NULL; |
129 | 123 | ||
130 | if (cpu_addr >= KVM_MAX_VCPUS) | 124 | if (cpu_addr >= KVM_MAX_VCPUS) |
131 | return SIGP_CC_NOT_OPERATIONAL; | 125 | return SIGP_CC_NOT_OPERATIONAL; |
@@ -137,13 +131,10 @@ static int __sigp_external_call(struct kvm_vcpu *vcpu, u16 cpu_addr) | |||
137 | inti->type = KVM_S390_INT_EXTERNAL_CALL; | 131 | inti->type = KVM_S390_INT_EXTERNAL_CALL; |
138 | inti->extcall.code = vcpu->vcpu_id; | 132 | inti->extcall.code = vcpu->vcpu_id; |
139 | 133 | ||
140 | spin_lock(&fi->lock); | 134 | dst_vcpu = kvm_get_vcpu(vcpu->kvm, cpu_addr); |
141 | li = fi->local_int[cpu_addr]; | 135 | if (!dst_vcpu) |
142 | if (li == NULL) { | 136 | return SIGP_CC_NOT_OPERATIONAL; |
143 | rc = SIGP_CC_NOT_OPERATIONAL; | 137 | li = &dst_vcpu->arch.local_int; |
144 | kfree(inti); | ||
145 | goto unlock; | ||
146 | } | ||
147 | spin_lock_bh(&li->lock); | 138 | spin_lock_bh(&li->lock); |
148 | list_add_tail(&inti->list, &li->list); | 139 | list_add_tail(&inti->list, &li->list); |
149 | atomic_set(&li->active, 1); | 140 | atomic_set(&li->active, 1); |
@@ -151,11 +142,9 @@ static int __sigp_external_call(struct kvm_vcpu *vcpu, u16 cpu_addr) | |||
151 | if (waitqueue_active(li->wq)) | 142 | if (waitqueue_active(li->wq)) |
152 | wake_up_interruptible(li->wq); | 143 | wake_up_interruptible(li->wq); |
153 | spin_unlock_bh(&li->lock); | 144 | spin_unlock_bh(&li->lock); |
154 | rc = SIGP_CC_ORDER_CODE_ACCEPTED; | ||
155 | VCPU_EVENT(vcpu, 4, "sent sigp ext call to cpu %x", cpu_addr); | 145 | VCPU_EVENT(vcpu, 4, "sent sigp ext call to cpu %x", cpu_addr); |
156 | unlock: | 146 | |
157 | spin_unlock(&fi->lock); | 147 | return SIGP_CC_ORDER_CODE_ACCEPTED; |
158 | return rc; | ||
159 | } | 148 | } |
160 | 149 | ||
161 | static int __inject_sigp_stop(struct kvm_s390_local_interrupt *li, int action) | 150 | static int __inject_sigp_stop(struct kvm_s390_local_interrupt *li, int action) |
@@ -189,31 +178,26 @@ out: | |||
189 | 178 | ||
190 | static int __sigp_stop(struct kvm_vcpu *vcpu, u16 cpu_addr, int action) | 179 | static int __sigp_stop(struct kvm_vcpu *vcpu, u16 cpu_addr, int action) |
191 | { | 180 | { |
192 | struct kvm_s390_float_interrupt *fi = &vcpu->kvm->arch.float_int; | ||
193 | struct kvm_s390_local_interrupt *li; | 181 | struct kvm_s390_local_interrupt *li; |
182 | struct kvm_vcpu *dst_vcpu = NULL; | ||
194 | int rc; | 183 | int rc; |
195 | 184 | ||
196 | if (cpu_addr >= KVM_MAX_VCPUS) | 185 | if (cpu_addr >= KVM_MAX_VCPUS) |
197 | return SIGP_CC_NOT_OPERATIONAL; | 186 | return SIGP_CC_NOT_OPERATIONAL; |
198 | 187 | ||
199 | spin_lock(&fi->lock); | 188 | dst_vcpu = kvm_get_vcpu(vcpu->kvm, cpu_addr); |
200 | li = fi->local_int[cpu_addr]; | 189 | if (!dst_vcpu) |
201 | if (li == NULL) { | 190 | return SIGP_CC_NOT_OPERATIONAL; |
202 | rc = SIGP_CC_NOT_OPERATIONAL; | 191 | li = &dst_vcpu->arch.local_int; |
203 | goto unlock; | ||
204 | } | ||
205 | 192 | ||
206 | rc = __inject_sigp_stop(li, action); | 193 | rc = __inject_sigp_stop(li, action); |
207 | 194 | ||
208 | unlock: | ||
209 | spin_unlock(&fi->lock); | ||
210 | VCPU_EVENT(vcpu, 4, "sent sigp stop to cpu %x", cpu_addr); | 195 | VCPU_EVENT(vcpu, 4, "sent sigp stop to cpu %x", cpu_addr); |
211 | 196 | ||
212 | if ((action & ACTION_STORE_ON_STOP) != 0 && rc == -ESHUTDOWN) { | 197 | if ((action & ACTION_STORE_ON_STOP) != 0 && rc == -ESHUTDOWN) { |
213 | /* If the CPU has already been stopped, we still have | 198 | /* If the CPU has already been stopped, we still have |
214 | * to save the status when doing stop-and-store. This | 199 | * to save the status when doing stop-and-store. This |
215 | * has to be done after unlocking all spinlocks. */ | 200 | * has to be done after unlocking all spinlocks. */ |
216 | struct kvm_vcpu *dst_vcpu = kvm_get_vcpu(vcpu->kvm, cpu_addr); | ||
217 | rc = kvm_s390_store_status_unloaded(dst_vcpu, | 201 | rc = kvm_s390_store_status_unloaded(dst_vcpu, |
218 | KVM_S390_STORE_STATUS_NOADDR); | 202 | KVM_S390_STORE_STATUS_NOADDR); |
219 | } | 203 | } |
@@ -249,12 +233,18 @@ static int __sigp_set_arch(struct kvm_vcpu *vcpu, u32 parameter) | |||
249 | static int __sigp_set_prefix(struct kvm_vcpu *vcpu, u16 cpu_addr, u32 address, | 233 | static int __sigp_set_prefix(struct kvm_vcpu *vcpu, u16 cpu_addr, u32 address, |
250 | u64 *reg) | 234 | u64 *reg) |
251 | { | 235 | { |
252 | struct kvm_s390_float_interrupt *fi = &vcpu->kvm->arch.float_int; | 236 | struct kvm_s390_local_interrupt *li; |
253 | struct kvm_s390_local_interrupt *li = NULL; | 237 | struct kvm_vcpu *dst_vcpu = NULL; |
254 | struct kvm_s390_interrupt_info *inti; | 238 | struct kvm_s390_interrupt_info *inti; |
255 | int rc; | 239 | int rc; |
256 | u8 tmp; | 240 | u8 tmp; |
257 | 241 | ||
242 | if (cpu_addr < KVM_MAX_VCPUS) | ||
243 | dst_vcpu = kvm_get_vcpu(vcpu->kvm, cpu_addr); | ||
244 | if (!dst_vcpu) | ||
245 | return SIGP_CC_NOT_OPERATIONAL; | ||
246 | li = &dst_vcpu->arch.local_int; | ||
247 | |||
258 | /* make sure that the new value is valid memory */ | 248 | /* make sure that the new value is valid memory */ |
259 | address = address & 0x7fffe000u; | 249 | address = address & 0x7fffe000u; |
260 | if (copy_from_guest_absolute(vcpu, &tmp, address, 1) || | 250 | if (copy_from_guest_absolute(vcpu, &tmp, address, 1) || |
@@ -268,18 +258,6 @@ static int __sigp_set_prefix(struct kvm_vcpu *vcpu, u16 cpu_addr, u32 address, | |||
268 | if (!inti) | 258 | if (!inti) |
269 | return SIGP_CC_BUSY; | 259 | return SIGP_CC_BUSY; |
270 | 260 | ||
271 | spin_lock(&fi->lock); | ||
272 | if (cpu_addr < KVM_MAX_VCPUS) | ||
273 | li = fi->local_int[cpu_addr]; | ||
274 | |||
275 | if (li == NULL) { | ||
276 | *reg &= 0xffffffff00000000UL; | ||
277 | *reg |= SIGP_STATUS_INCORRECT_STATE; | ||
278 | rc = SIGP_CC_STATUS_STORED; | ||
279 | kfree(inti); | ||
280 | goto out_fi; | ||
281 | } | ||
282 | |||
283 | spin_lock_bh(&li->lock); | 261 | spin_lock_bh(&li->lock); |
284 | /* cpu must be in stopped state */ | 262 | /* cpu must be in stopped state */ |
285 | if (!(atomic_read(li->cpuflags) & CPUSTAT_STOPPED)) { | 263 | if (!(atomic_read(li->cpuflags) & CPUSTAT_STOPPED)) { |
@@ -302,8 +280,6 @@ static int __sigp_set_prefix(struct kvm_vcpu *vcpu, u16 cpu_addr, u32 address, | |||
302 | VCPU_EVENT(vcpu, 4, "set prefix of cpu %02x to %x", cpu_addr, address); | 280 | VCPU_EVENT(vcpu, 4, "set prefix of cpu %02x to %x", cpu_addr, address); |
303 | out_li: | 281 | out_li: |
304 | spin_unlock_bh(&li->lock); | 282 | spin_unlock_bh(&li->lock); |
305 | out_fi: | ||
306 | spin_unlock(&fi->lock); | ||
307 | return rc; | 283 | return rc; |
308 | } | 284 | } |
309 | 285 | ||
@@ -341,28 +317,26 @@ static int __sigp_store_status_at_addr(struct kvm_vcpu *vcpu, u16 cpu_id, | |||
341 | static int __sigp_sense_running(struct kvm_vcpu *vcpu, u16 cpu_addr, | 317 | static int __sigp_sense_running(struct kvm_vcpu *vcpu, u16 cpu_addr, |
342 | u64 *reg) | 318 | u64 *reg) |
343 | { | 319 | { |
320 | struct kvm_s390_local_interrupt *li; | ||
321 | struct kvm_vcpu *dst_vcpu = NULL; | ||
344 | int rc; | 322 | int rc; |
345 | struct kvm_s390_float_interrupt *fi = &vcpu->kvm->arch.float_int; | ||
346 | 323 | ||
347 | if (cpu_addr >= KVM_MAX_VCPUS) | 324 | if (cpu_addr >= KVM_MAX_VCPUS) |
348 | return SIGP_CC_NOT_OPERATIONAL; | 325 | return SIGP_CC_NOT_OPERATIONAL; |
349 | 326 | ||
350 | spin_lock(&fi->lock); | 327 | dst_vcpu = kvm_get_vcpu(vcpu->kvm, cpu_addr); |
351 | if (fi->local_int[cpu_addr] == NULL) | 328 | if (!dst_vcpu) |
352 | rc = SIGP_CC_NOT_OPERATIONAL; | 329 | return SIGP_CC_NOT_OPERATIONAL; |
353 | else { | 330 | li = &dst_vcpu->arch.local_int; |
354 | if (atomic_read(fi->local_int[cpu_addr]->cpuflags) | 331 | if (atomic_read(li->cpuflags) & CPUSTAT_RUNNING) { |
355 | & CPUSTAT_RUNNING) { | 332 | /* running */ |
356 | /* running */ | 333 | rc = SIGP_CC_ORDER_CODE_ACCEPTED; |
357 | rc = SIGP_CC_ORDER_CODE_ACCEPTED; | 334 | } else { |
358 | } else { | 335 | /* not running */ |
359 | /* not running */ | 336 | *reg &= 0xffffffff00000000UL; |
360 | *reg &= 0xffffffff00000000UL; | 337 | *reg |= SIGP_STATUS_NOT_RUNNING; |
361 | *reg |= SIGP_STATUS_NOT_RUNNING; | 338 | rc = SIGP_CC_STATUS_STORED; |
362 | rc = SIGP_CC_STATUS_STORED; | ||
363 | } | ||
364 | } | 339 | } |
365 | spin_unlock(&fi->lock); | ||
366 | 340 | ||
367 | VCPU_EVENT(vcpu, 4, "sensed running status of cpu %x rc %x", cpu_addr, | 341 | VCPU_EVENT(vcpu, 4, "sensed running status of cpu %x rc %x", cpu_addr, |
368 | rc); | 342 | rc); |
@@ -373,26 +347,22 @@ static int __sigp_sense_running(struct kvm_vcpu *vcpu, u16 cpu_addr, | |||
373 | /* Test whether the destination CPU is available and not busy */ | 347 | /* Test whether the destination CPU is available and not busy */ |
374 | static int sigp_check_callable(struct kvm_vcpu *vcpu, u16 cpu_addr) | 348 | static int sigp_check_callable(struct kvm_vcpu *vcpu, u16 cpu_addr) |
375 | { | 349 | { |
376 | struct kvm_s390_float_interrupt *fi = &vcpu->kvm->arch.float_int; | ||
377 | struct kvm_s390_local_interrupt *li; | 350 | struct kvm_s390_local_interrupt *li; |
378 | int rc = SIGP_CC_ORDER_CODE_ACCEPTED; | 351 | int rc = SIGP_CC_ORDER_CODE_ACCEPTED; |
352 | struct kvm_vcpu *dst_vcpu = NULL; | ||
379 | 353 | ||
380 | if (cpu_addr >= KVM_MAX_VCPUS) | 354 | if (cpu_addr >= KVM_MAX_VCPUS) |
381 | return SIGP_CC_NOT_OPERATIONAL; | 355 | return SIGP_CC_NOT_OPERATIONAL; |
382 | 356 | ||
383 | spin_lock(&fi->lock); | 357 | dst_vcpu = kvm_get_vcpu(vcpu->kvm, cpu_addr); |
384 | li = fi->local_int[cpu_addr]; | 358 | if (!dst_vcpu) |
385 | if (li == NULL) { | 359 | return SIGP_CC_NOT_OPERATIONAL; |
386 | rc = SIGP_CC_NOT_OPERATIONAL; | 360 | li = &dst_vcpu->arch.local_int; |
387 | goto out; | ||
388 | } | ||
389 | |||
390 | spin_lock_bh(&li->lock); | 361 | spin_lock_bh(&li->lock); |
391 | if (li->action_bits & ACTION_STOP_ON_STOP) | 362 | if (li->action_bits & ACTION_STOP_ON_STOP) |
392 | rc = SIGP_CC_BUSY; | 363 | rc = SIGP_CC_BUSY; |
393 | spin_unlock_bh(&li->lock); | 364 | spin_unlock_bh(&li->lock); |
394 | out: | 365 | |
395 | spin_unlock(&fi->lock); | ||
396 | return rc; | 366 | return rc; |
397 | } | 367 | } |
398 | 368 | ||
diff --git a/drivers/s390/cio/airq.c b/drivers/s390/cio/airq.c index f055df0b167f..445564c790f6 100644 --- a/drivers/s390/cio/airq.c +++ b/drivers/s390/cio/airq.c | |||
@@ -186,55 +186,71 @@ void airq_iv_release(struct airq_iv *iv) | |||
186 | EXPORT_SYMBOL(airq_iv_release); | 186 | EXPORT_SYMBOL(airq_iv_release); |
187 | 187 | ||
188 | /** | 188 | /** |
189 | * airq_iv_alloc_bit - allocate an irq bit from an interrupt vector | 189 | * airq_iv_alloc - allocate irq bits from an interrupt vector |
190 | * @iv: pointer to an interrupt vector structure | 190 | * @iv: pointer to an interrupt vector structure |
191 | * @num: number of consecutive irq bits to allocate | ||
191 | * | 192 | * |
192 | * Returns the bit number of the allocated irq, or -1UL if no bit | 193 | * Returns the bit number of the first irq in the allocated block of irqs, |
193 | * is available or the AIRQ_IV_ALLOC flag has not been specified | 194 | * or -1UL if no bit is available or the AIRQ_IV_ALLOC flag has not been |
195 | * specified | ||
194 | */ | 196 | */ |
195 | unsigned long airq_iv_alloc_bit(struct airq_iv *iv) | 197 | unsigned long airq_iv_alloc(struct airq_iv *iv, unsigned long num) |
196 | { | 198 | { |
197 | unsigned long bit; | 199 | unsigned long bit, i; |
198 | 200 | ||
199 | if (!iv->avail) | 201 | if (!iv->avail || num == 0) |
200 | return -1UL; | 202 | return -1UL; |
201 | spin_lock(&iv->lock); | 203 | spin_lock(&iv->lock); |
202 | bit = find_first_bit_inv(iv->avail, iv->bits); | 204 | bit = find_first_bit_inv(iv->avail, iv->bits); |
203 | if (bit < iv->bits) { | 205 | while (bit + num <= iv->bits) { |
204 | clear_bit_inv(bit, iv->avail); | 206 | for (i = 1; i < num; i++) |
205 | if (bit >= iv->end) | 207 | if (!test_bit_inv(bit + i, iv->avail)) |
206 | iv->end = bit + 1; | 208 | break; |
207 | } else | 209 | if (i >= num) { |
210 | /* Found a suitable block of irqs */ | ||
211 | for (i = 0; i < num; i++) | ||
212 | clear_bit_inv(bit + i, iv->avail); | ||
213 | if (bit + num >= iv->end) | ||
214 | iv->end = bit + num + 1; | ||
215 | break; | ||
216 | } | ||
217 | bit = find_next_bit_inv(iv->avail, iv->bits, bit + i + 1); | ||
218 | } | ||
219 | if (bit + num > iv->bits) | ||
208 | bit = -1UL; | 220 | bit = -1UL; |
209 | spin_unlock(&iv->lock); | 221 | spin_unlock(&iv->lock); |
210 | return bit; | 222 | return bit; |
211 | 223 | ||
212 | } | 224 | } |
213 | EXPORT_SYMBOL(airq_iv_alloc_bit); | 225 | EXPORT_SYMBOL(airq_iv_alloc); |
214 | 226 | ||
215 | /** | 227 | /** |
216 | * airq_iv_free_bit - free an irq bit of an interrupt vector | 228 | * airq_iv_free - free irq bits of an interrupt vector |
217 | * @iv: pointer to interrupt vector structure | 229 | * @iv: pointer to interrupt vector structure |
218 | * @bit: number of the irq bit to free | 230 | * @bit: number of the first irq bit to free |
231 | * @num: number of consecutive irq bits to free | ||
219 | */ | 232 | */ |
220 | void airq_iv_free_bit(struct airq_iv *iv, unsigned long bit) | 233 | void airq_iv_free(struct airq_iv *iv, unsigned long bit, unsigned long num) |
221 | { | 234 | { |
222 | if (!iv->avail) | 235 | unsigned long i; |
236 | |||
237 | if (!iv->avail || num == 0) | ||
223 | return; | 238 | return; |
224 | spin_lock(&iv->lock); | 239 | spin_lock(&iv->lock); |
225 | /* Clear (possibly left over) interrupt bit */ | 240 | for (i = 0; i < num; i++) { |
226 | clear_bit_inv(bit, iv->vector); | 241 | /* Clear (possibly left over) interrupt bit */ |
227 | /* Make the bit position available again */ | 242 | clear_bit_inv(bit + i, iv->vector); |
228 | set_bit_inv(bit, iv->avail); | 243 | /* Make the bit positions available again */ |
229 | if (bit == iv->end - 1) { | 244 | set_bit_inv(bit + i, iv->avail); |
245 | } | ||
246 | if (bit + num >= iv->end) { | ||
230 | /* Find new end of bit-field */ | 247 | /* Find new end of bit-field */ |
231 | while (--iv->end > 0) | 248 | while (iv->end > 0 && !test_bit_inv(iv->end - 1, iv->avail)) |
232 | if (!test_bit_inv(iv->end - 1, iv->avail)) | 249 | iv->end--; |
233 | break; | ||
234 | } | 250 | } |
235 | spin_unlock(&iv->lock); | 251 | spin_unlock(&iv->lock); |
236 | } | 252 | } |
237 | EXPORT_SYMBOL(airq_iv_free_bit); | 253 | EXPORT_SYMBOL(airq_iv_free); |
238 | 254 | ||
239 | /** | 255 | /** |
240 | * airq_iv_scan - scan interrupt vector for non-zero bits | 256 | * airq_iv_scan - scan interrupt vector for non-zero bits |
diff --git a/drivers/s390/kvm/virtio_ccw.c b/drivers/s390/kvm/virtio_ccw.c index 0fc584832001..6a2b5fdcd552 100644 --- a/drivers/s390/kvm/virtio_ccw.c +++ b/drivers/s390/kvm/virtio_ccw.c | |||
@@ -1,7 +1,7 @@ | |||
1 | /* | 1 | /* |
2 | * ccw based virtio transport | 2 | * ccw based virtio transport |
3 | * | 3 | * |
4 | * Copyright IBM Corp. 2012 | 4 | * Copyright IBM Corp. 2012, 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) |
@@ -32,6 +32,8 @@ | |||
32 | #include <asm/cio.h> | 32 | #include <asm/cio.h> |
33 | #include <asm/ccwdev.h> | 33 | #include <asm/ccwdev.h> |
34 | #include <asm/virtio-ccw.h> | 34 | #include <asm/virtio-ccw.h> |
35 | #include <asm/isc.h> | ||
36 | #include <asm/airq.h> | ||
35 | 37 | ||
36 | /* | 38 | /* |
37 | * virtio related functions | 39 | * virtio related functions |
@@ -58,6 +60,8 @@ struct virtio_ccw_device { | |||
58 | unsigned long indicators; | 60 | unsigned long indicators; |
59 | unsigned long indicators2; | 61 | unsigned long indicators2; |
60 | struct vq_config_block *config_block; | 62 | struct vq_config_block *config_block; |
63 | bool is_thinint; | ||
64 | void *airq_info; | ||
61 | }; | 65 | }; |
62 | 66 | ||
63 | struct vq_info_block { | 67 | struct vq_info_block { |
@@ -72,15 +76,38 @@ struct virtio_feature_desc { | |||
72 | __u8 index; | 76 | __u8 index; |
73 | } __packed; | 77 | } __packed; |
74 | 78 | ||
79 | struct virtio_thinint_area { | ||
80 | unsigned long summary_indicator; | ||
81 | unsigned long indicator; | ||
82 | u64 bit_nr; | ||
83 | u8 isc; | ||
84 | } __packed; | ||
85 | |||
75 | struct virtio_ccw_vq_info { | 86 | struct virtio_ccw_vq_info { |
76 | struct virtqueue *vq; | 87 | struct virtqueue *vq; |
77 | int num; | 88 | int num; |
78 | void *queue; | 89 | void *queue; |
79 | struct vq_info_block *info_block; | 90 | struct vq_info_block *info_block; |
91 | int bit_nr; | ||
80 | struct list_head node; | 92 | struct list_head node; |
81 | long cookie; | 93 | long cookie; |
82 | }; | 94 | }; |
83 | 95 | ||
96 | #define VIRTIO_AIRQ_ISC IO_SCH_ISC /* inherit from subchannel */ | ||
97 | |||
98 | #define VIRTIO_IV_BITS (L1_CACHE_BYTES * 8) | ||
99 | #define MAX_AIRQ_AREAS 20 | ||
100 | |||
101 | static int virtio_ccw_use_airq = 1; | ||
102 | |||
103 | struct airq_info { | ||
104 | rwlock_t lock; | ||
105 | u8 summary_indicator; | ||
106 | struct airq_struct airq; | ||
107 | struct airq_iv *aiv; | ||
108 | }; | ||
109 | static struct airq_info *airq_areas[MAX_AIRQ_AREAS]; | ||
110 | |||
84 | #define CCW_CMD_SET_VQ 0x13 | 111 | #define CCW_CMD_SET_VQ 0x13 |
85 | #define CCW_CMD_VDEV_RESET 0x33 | 112 | #define CCW_CMD_VDEV_RESET 0x33 |
86 | #define CCW_CMD_SET_IND 0x43 | 113 | #define CCW_CMD_SET_IND 0x43 |
@@ -91,6 +118,7 @@ struct virtio_ccw_vq_info { | |||
91 | #define CCW_CMD_WRITE_CONF 0x21 | 118 | #define CCW_CMD_WRITE_CONF 0x21 |
92 | #define CCW_CMD_WRITE_STATUS 0x31 | 119 | #define CCW_CMD_WRITE_STATUS 0x31 |
93 | #define CCW_CMD_READ_VQ_CONF 0x32 | 120 | #define CCW_CMD_READ_VQ_CONF 0x32 |
121 | #define CCW_CMD_SET_IND_ADAPTER 0x73 | ||
94 | 122 | ||
95 | #define VIRTIO_CCW_DOING_SET_VQ 0x00010000 | 123 | #define VIRTIO_CCW_DOING_SET_VQ 0x00010000 |
96 | #define VIRTIO_CCW_DOING_RESET 0x00040000 | 124 | #define VIRTIO_CCW_DOING_RESET 0x00040000 |
@@ -102,6 +130,7 @@ struct virtio_ccw_vq_info { | |||
102 | #define VIRTIO_CCW_DOING_SET_IND 0x01000000 | 130 | #define VIRTIO_CCW_DOING_SET_IND 0x01000000 |
103 | #define VIRTIO_CCW_DOING_READ_VQ_CONF 0x02000000 | 131 | #define VIRTIO_CCW_DOING_READ_VQ_CONF 0x02000000 |
104 | #define VIRTIO_CCW_DOING_SET_CONF_IND 0x04000000 | 132 | #define VIRTIO_CCW_DOING_SET_CONF_IND 0x04000000 |
133 | #define VIRTIO_CCW_DOING_SET_IND_ADAPTER 0x08000000 | ||
105 | #define VIRTIO_CCW_INTPARM_MASK 0xffff0000 | 134 | #define VIRTIO_CCW_INTPARM_MASK 0xffff0000 |
106 | 135 | ||
107 | static struct virtio_ccw_device *to_vc_device(struct virtio_device *vdev) | 136 | static struct virtio_ccw_device *to_vc_device(struct virtio_device *vdev) |
@@ -109,6 +138,125 @@ static struct virtio_ccw_device *to_vc_device(struct virtio_device *vdev) | |||
109 | return container_of(vdev, struct virtio_ccw_device, vdev); | 138 | return container_of(vdev, struct virtio_ccw_device, vdev); |
110 | } | 139 | } |
111 | 140 | ||
141 | static void drop_airq_indicator(struct virtqueue *vq, struct airq_info *info) | ||
142 | { | ||
143 | unsigned long i, flags; | ||
144 | |||
145 | write_lock_irqsave(&info->lock, flags); | ||
146 | for (i = 0; i < airq_iv_end(info->aiv); i++) { | ||
147 | if (vq == (void *)airq_iv_get_ptr(info->aiv, i)) { | ||
148 | airq_iv_free_bit(info->aiv, i); | ||
149 | airq_iv_set_ptr(info->aiv, i, 0); | ||
150 | break; | ||
151 | } | ||
152 | } | ||
153 | write_unlock_irqrestore(&info->lock, flags); | ||
154 | } | ||
155 | |||
156 | static void virtio_airq_handler(struct airq_struct *airq) | ||
157 | { | ||
158 | struct airq_info *info = container_of(airq, struct airq_info, airq); | ||
159 | unsigned long ai; | ||
160 | |||
161 | inc_irq_stat(IRQIO_VAI); | ||
162 | read_lock(&info->lock); | ||
163 | /* Walk through indicators field, summary indicator active. */ | ||
164 | for (ai = 0;;) { | ||
165 | ai = airq_iv_scan(info->aiv, ai, airq_iv_end(info->aiv)); | ||
166 | if (ai == -1UL) | ||
167 | break; | ||
168 | vring_interrupt(0, (void *)airq_iv_get_ptr(info->aiv, ai)); | ||
169 | } | ||
170 | info->summary_indicator = 0; | ||
171 | smp_wmb(); | ||
172 | /* Walk through indicators field, summary indicator not active. */ | ||
173 | for (ai = 0;;) { | ||
174 | ai = airq_iv_scan(info->aiv, ai, airq_iv_end(info->aiv)); | ||
175 | if (ai == -1UL) | ||
176 | break; | ||
177 | vring_interrupt(0, (void *)airq_iv_get_ptr(info->aiv, ai)); | ||
178 | } | ||
179 | read_unlock(&info->lock); | ||
180 | } | ||
181 | |||
182 | static struct airq_info *new_airq_info(void) | ||
183 | { | ||
184 | struct airq_info *info; | ||
185 | int rc; | ||
186 | |||
187 | info = kzalloc(sizeof(*info), GFP_KERNEL); | ||
188 | if (!info) | ||
189 | return NULL; | ||
190 | rwlock_init(&info->lock); | ||
191 | info->aiv = airq_iv_create(VIRTIO_IV_BITS, AIRQ_IV_ALLOC | AIRQ_IV_PTR); | ||
192 | if (!info->aiv) { | ||
193 | kfree(info); | ||
194 | return NULL; | ||
195 | } | ||
196 | info->airq.handler = virtio_airq_handler; | ||
197 | info->airq.lsi_ptr = &info->summary_indicator; | ||
198 | info->airq.lsi_mask = 0xff; | ||
199 | info->airq.isc = VIRTIO_AIRQ_ISC; | ||
200 | rc = register_adapter_interrupt(&info->airq); | ||
201 | if (rc) { | ||
202 | airq_iv_release(info->aiv); | ||
203 | kfree(info); | ||
204 | return NULL; | ||
205 | } | ||
206 | return info; | ||
207 | } | ||
208 | |||
209 | static void destroy_airq_info(struct airq_info *info) | ||
210 | { | ||
211 | if (!info) | ||
212 | return; | ||
213 | |||
214 | unregister_adapter_interrupt(&info->airq); | ||
215 | airq_iv_release(info->aiv); | ||
216 | kfree(info); | ||
217 | } | ||
218 | |||
219 | static unsigned long get_airq_indicator(struct virtqueue *vqs[], int nvqs, | ||
220 | u64 *first, void **airq_info) | ||
221 | { | ||
222 | int i, j; | ||
223 | struct airq_info *info; | ||
224 | unsigned long indicator_addr = 0; | ||
225 | unsigned long bit, flags; | ||
226 | |||
227 | for (i = 0; i < MAX_AIRQ_AREAS && !indicator_addr; i++) { | ||
228 | if (!airq_areas[i]) | ||
229 | airq_areas[i] = new_airq_info(); | ||
230 | info = airq_areas[i]; | ||
231 | if (!info) | ||
232 | return 0; | ||
233 | write_lock_irqsave(&info->lock, flags); | ||
234 | bit = airq_iv_alloc(info->aiv, nvqs); | ||
235 | if (bit == -1UL) { | ||
236 | /* Not enough vacancies. */ | ||
237 | write_unlock_irqrestore(&info->lock, flags); | ||
238 | continue; | ||
239 | } | ||
240 | *first = bit; | ||
241 | *airq_info = info; | ||
242 | indicator_addr = (unsigned long)info->aiv->vector; | ||
243 | for (j = 0; j < nvqs; j++) { | ||
244 | airq_iv_set_ptr(info->aiv, bit + j, | ||
245 | (unsigned long)vqs[j]); | ||
246 | } | ||
247 | write_unlock_irqrestore(&info->lock, flags); | ||
248 | } | ||
249 | return indicator_addr; | ||
250 | } | ||
251 | |||
252 | static void virtio_ccw_drop_indicators(struct virtio_ccw_device *vcdev) | ||
253 | { | ||
254 | struct virtio_ccw_vq_info *info; | ||
255 | |||
256 | list_for_each_entry(info, &vcdev->virtqueues, node) | ||
257 | drop_airq_indicator(info->vq, vcdev->airq_info); | ||
258 | } | ||
259 | |||
112 | static int doing_io(struct virtio_ccw_device *vcdev, __u32 flag) | 260 | static int doing_io(struct virtio_ccw_device *vcdev, __u32 flag) |
113 | { | 261 | { |
114 | unsigned long flags; | 262 | unsigned long flags; |
@@ -145,6 +293,51 @@ static int ccw_io_helper(struct virtio_ccw_device *vcdev, | |||
145 | return ret ? ret : vcdev->err; | 293 | return ret ? ret : vcdev->err; |
146 | } | 294 | } |
147 | 295 | ||
296 | static void virtio_ccw_drop_indicator(struct virtio_ccw_device *vcdev, | ||
297 | struct ccw1 *ccw) | ||
298 | { | ||
299 | int ret; | ||
300 | unsigned long *indicatorp = NULL; | ||
301 | struct virtio_thinint_area *thinint_area = NULL; | ||
302 | struct airq_info *airq_info = vcdev->airq_info; | ||
303 | |||
304 | if (vcdev->is_thinint) { | ||
305 | thinint_area = kzalloc(sizeof(*thinint_area), | ||
306 | GFP_DMA | GFP_KERNEL); | ||
307 | if (!thinint_area) | ||
308 | return; | ||
309 | thinint_area->summary_indicator = | ||
310 | (unsigned long) &airq_info->summary_indicator; | ||
311 | thinint_area->isc = VIRTIO_AIRQ_ISC; | ||
312 | ccw->cmd_code = CCW_CMD_SET_IND_ADAPTER; | ||
313 | ccw->count = sizeof(*thinint_area); | ||
314 | ccw->cda = (__u32)(unsigned long) thinint_area; | ||
315 | } else { | ||
316 | indicatorp = kmalloc(sizeof(&vcdev->indicators), | ||
317 | GFP_DMA | GFP_KERNEL); | ||
318 | if (!indicatorp) | ||
319 | return; | ||
320 | *indicatorp = 0; | ||
321 | ccw->cmd_code = CCW_CMD_SET_IND; | ||
322 | ccw->count = sizeof(vcdev->indicators); | ||
323 | ccw->cda = (__u32)(unsigned long) indicatorp; | ||
324 | } | ||
325 | /* Deregister indicators from host. */ | ||
326 | vcdev->indicators = 0; | ||
327 | ccw->flags = 0; | ||
328 | ret = ccw_io_helper(vcdev, ccw, | ||
329 | vcdev->is_thinint ? | ||
330 | VIRTIO_CCW_DOING_SET_IND_ADAPTER : | ||
331 | VIRTIO_CCW_DOING_SET_IND); | ||
332 | if (ret && (ret != -ENODEV)) | ||
333 | dev_info(&vcdev->cdev->dev, | ||
334 | "Failed to deregister indicators (%d)\n", ret); | ||
335 | else if (vcdev->is_thinint) | ||
336 | virtio_ccw_drop_indicators(vcdev); | ||
337 | kfree(indicatorp); | ||
338 | kfree(thinint_area); | ||
339 | } | ||
340 | |||
148 | static inline long do_kvm_notify(struct subchannel_id schid, | 341 | static inline long do_kvm_notify(struct subchannel_id schid, |
149 | unsigned long queue_index, | 342 | unsigned long queue_index, |
150 | long cookie) | 343 | long cookie) |
@@ -232,11 +425,13 @@ static void virtio_ccw_del_vqs(struct virtio_device *vdev) | |||
232 | { | 425 | { |
233 | struct virtqueue *vq, *n; | 426 | struct virtqueue *vq, *n; |
234 | struct ccw1 *ccw; | 427 | struct ccw1 *ccw; |
428 | struct virtio_ccw_device *vcdev = to_vc_device(vdev); | ||
235 | 429 | ||
236 | ccw = kzalloc(sizeof(*ccw), GFP_DMA | GFP_KERNEL); | 430 | ccw = kzalloc(sizeof(*ccw), GFP_DMA | GFP_KERNEL); |
237 | if (!ccw) | 431 | if (!ccw) |
238 | return; | 432 | return; |
239 | 433 | ||
434 | virtio_ccw_drop_indicator(vcdev, ccw); | ||
240 | 435 | ||
241 | list_for_each_entry_safe(vq, n, &vdev->vqs, list) | 436 | list_for_each_entry_safe(vq, n, &vdev->vqs, list) |
242 | virtio_ccw_del_vq(vq, ccw); | 437 | virtio_ccw_del_vq(vq, ccw); |
@@ -326,6 +521,54 @@ out_err: | |||
326 | return ERR_PTR(err); | 521 | return ERR_PTR(err); |
327 | } | 522 | } |
328 | 523 | ||
524 | static int virtio_ccw_register_adapter_ind(struct virtio_ccw_device *vcdev, | ||
525 | struct virtqueue *vqs[], int nvqs, | ||
526 | struct ccw1 *ccw) | ||
527 | { | ||
528 | int ret; | ||
529 | struct virtio_thinint_area *thinint_area = NULL; | ||
530 | struct airq_info *info; | ||
531 | |||
532 | thinint_area = kzalloc(sizeof(*thinint_area), GFP_DMA | GFP_KERNEL); | ||
533 | if (!thinint_area) { | ||
534 | ret = -ENOMEM; | ||
535 | goto out; | ||
536 | } | ||
537 | /* Try to get an indicator. */ | ||
538 | thinint_area->indicator = get_airq_indicator(vqs, nvqs, | ||
539 | &thinint_area->bit_nr, | ||
540 | &vcdev->airq_info); | ||
541 | if (!thinint_area->indicator) { | ||
542 | ret = -ENOSPC; | ||
543 | goto out; | ||
544 | } | ||
545 | info = vcdev->airq_info; | ||
546 | thinint_area->summary_indicator = | ||
547 | (unsigned long) &info->summary_indicator; | ||
548 | thinint_area->isc = VIRTIO_AIRQ_ISC; | ||
549 | ccw->cmd_code = CCW_CMD_SET_IND_ADAPTER; | ||
550 | ccw->flags = CCW_FLAG_SLI; | ||
551 | ccw->count = sizeof(*thinint_area); | ||
552 | ccw->cda = (__u32)(unsigned long)thinint_area; | ||
553 | ret = ccw_io_helper(vcdev, ccw, VIRTIO_CCW_DOING_SET_IND_ADAPTER); | ||
554 | if (ret) { | ||
555 | if (ret == -EOPNOTSUPP) { | ||
556 | /* | ||
557 | * The host does not support adapter interrupts | ||
558 | * for virtio-ccw, stop trying. | ||
559 | */ | ||
560 | virtio_ccw_use_airq = 0; | ||
561 | pr_info("Adapter interrupts unsupported on host\n"); | ||
562 | } else | ||
563 | dev_warn(&vcdev->cdev->dev, | ||
564 | "enabling adapter interrupts = %d\n", ret); | ||
565 | virtio_ccw_drop_indicators(vcdev); | ||
566 | } | ||
567 | out: | ||
568 | kfree(thinint_area); | ||
569 | return ret; | ||
570 | } | ||
571 | |||
329 | static int virtio_ccw_find_vqs(struct virtio_device *vdev, unsigned nvqs, | 572 | static int virtio_ccw_find_vqs(struct virtio_device *vdev, unsigned nvqs, |
330 | struct virtqueue *vqs[], | 573 | struct virtqueue *vqs[], |
331 | vq_callback_t *callbacks[], | 574 | vq_callback_t *callbacks[], |
@@ -355,15 +598,23 @@ static int virtio_ccw_find_vqs(struct virtio_device *vdev, unsigned nvqs, | |||
355 | if (!indicatorp) | 598 | if (!indicatorp) |
356 | goto out; | 599 | goto out; |
357 | *indicatorp = (unsigned long) &vcdev->indicators; | 600 | *indicatorp = (unsigned long) &vcdev->indicators; |
358 | /* Register queue indicators with host. */ | 601 | if (vcdev->is_thinint) { |
359 | vcdev->indicators = 0; | 602 | ret = virtio_ccw_register_adapter_ind(vcdev, vqs, nvqs, ccw); |
360 | ccw->cmd_code = CCW_CMD_SET_IND; | 603 | if (ret) |
361 | ccw->flags = 0; | 604 | /* no error, just fall back to legacy interrupts */ |
362 | ccw->count = sizeof(vcdev->indicators); | 605 | vcdev->is_thinint = 0; |
363 | ccw->cda = (__u32)(unsigned long) indicatorp; | 606 | } |
364 | ret = ccw_io_helper(vcdev, ccw, VIRTIO_CCW_DOING_SET_IND); | 607 | if (!vcdev->is_thinint) { |
365 | if (ret) | 608 | /* Register queue indicators with host. */ |
366 | goto out; | 609 | vcdev->indicators = 0; |
610 | ccw->cmd_code = CCW_CMD_SET_IND; | ||
611 | ccw->flags = 0; | ||
612 | ccw->count = sizeof(vcdev->indicators); | ||
613 | ccw->cda = (__u32)(unsigned long) indicatorp; | ||
614 | ret = ccw_io_helper(vcdev, ccw, VIRTIO_CCW_DOING_SET_IND); | ||
615 | if (ret) | ||
616 | goto out; | ||
617 | } | ||
367 | /* Register indicators2 with host for config changes */ | 618 | /* Register indicators2 with host for config changes */ |
368 | *indicatorp = (unsigned long) &vcdev->indicators2; | 619 | *indicatorp = (unsigned long) &vcdev->indicators2; |
369 | vcdev->indicators2 = 0; | 620 | vcdev->indicators2 = 0; |
@@ -636,6 +887,8 @@ static void virtio_ccw_int_handler(struct ccw_device *cdev, | |||
636 | struct virtqueue *vq; | 887 | struct virtqueue *vq; |
637 | struct virtio_driver *drv; | 888 | struct virtio_driver *drv; |
638 | 889 | ||
890 | if (!vcdev) | ||
891 | return; | ||
639 | /* Check if it's a notification from the host. */ | 892 | /* Check if it's a notification from the host. */ |
640 | if ((intparm == 0) && | 893 | if ((intparm == 0) && |
641 | (scsw_stctl(&irb->scsw) == | 894 | (scsw_stctl(&irb->scsw) == |
@@ -663,6 +916,7 @@ static void virtio_ccw_int_handler(struct ccw_device *cdev, | |||
663 | case VIRTIO_CCW_DOING_SET_CONF_IND: | 916 | case VIRTIO_CCW_DOING_SET_CONF_IND: |
664 | case VIRTIO_CCW_DOING_RESET: | 917 | case VIRTIO_CCW_DOING_RESET: |
665 | case VIRTIO_CCW_DOING_READ_VQ_CONF: | 918 | case VIRTIO_CCW_DOING_READ_VQ_CONF: |
919 | case VIRTIO_CCW_DOING_SET_IND_ADAPTER: | ||
666 | vcdev->curr_io &= ~activity; | 920 | vcdev->curr_io &= ~activity; |
667 | wake_up(&vcdev->wait_q); | 921 | wake_up(&vcdev->wait_q); |
668 | break; | 922 | break; |
@@ -734,23 +988,37 @@ static int virtio_ccw_probe(struct ccw_device *cdev) | |||
734 | return 0; | 988 | return 0; |
735 | } | 989 | } |
736 | 990 | ||
991 | static struct virtio_ccw_device *virtio_grab_drvdata(struct ccw_device *cdev) | ||
992 | { | ||
993 | unsigned long flags; | ||
994 | struct virtio_ccw_device *vcdev; | ||
995 | |||
996 | spin_lock_irqsave(get_ccwdev_lock(cdev), flags); | ||
997 | vcdev = dev_get_drvdata(&cdev->dev); | ||
998 | if (!vcdev) { | ||
999 | spin_unlock_irqrestore(get_ccwdev_lock(cdev), flags); | ||
1000 | return NULL; | ||
1001 | } | ||
1002 | dev_set_drvdata(&cdev->dev, NULL); | ||
1003 | spin_unlock_irqrestore(get_ccwdev_lock(cdev), flags); | ||
1004 | return vcdev; | ||
1005 | } | ||
1006 | |||
737 | static void virtio_ccw_remove(struct ccw_device *cdev) | 1007 | static void virtio_ccw_remove(struct ccw_device *cdev) |
738 | { | 1008 | { |
739 | struct virtio_ccw_device *vcdev = dev_get_drvdata(&cdev->dev); | 1009 | struct virtio_ccw_device *vcdev = virtio_grab_drvdata(cdev); |
740 | 1010 | ||
741 | if (cdev->online) { | 1011 | if (vcdev && cdev->online) |
742 | unregister_virtio_device(&vcdev->vdev); | 1012 | unregister_virtio_device(&vcdev->vdev); |
743 | dev_set_drvdata(&cdev->dev, NULL); | ||
744 | } | ||
745 | cdev->handler = NULL; | 1013 | cdev->handler = NULL; |
746 | } | 1014 | } |
747 | 1015 | ||
748 | static int virtio_ccw_offline(struct ccw_device *cdev) | 1016 | static int virtio_ccw_offline(struct ccw_device *cdev) |
749 | { | 1017 | { |
750 | struct virtio_ccw_device *vcdev = dev_get_drvdata(&cdev->dev); | 1018 | struct virtio_ccw_device *vcdev = virtio_grab_drvdata(cdev); |
751 | 1019 | ||
752 | unregister_virtio_device(&vcdev->vdev); | 1020 | if (vcdev) |
753 | dev_set_drvdata(&cdev->dev, NULL); | 1021 | unregister_virtio_device(&vcdev->vdev); |
754 | return 0; | 1022 | return 0; |
755 | } | 1023 | } |
756 | 1024 | ||
@@ -759,6 +1027,7 @@ static int virtio_ccw_online(struct ccw_device *cdev) | |||
759 | { | 1027 | { |
760 | int ret; | 1028 | int ret; |
761 | struct virtio_ccw_device *vcdev; | 1029 | struct virtio_ccw_device *vcdev; |
1030 | unsigned long flags; | ||
762 | 1031 | ||
763 | vcdev = kzalloc(sizeof(*vcdev), GFP_KERNEL); | 1032 | vcdev = kzalloc(sizeof(*vcdev), GFP_KERNEL); |
764 | if (!vcdev) { | 1033 | if (!vcdev) { |
@@ -778,6 +1047,8 @@ static int virtio_ccw_online(struct ccw_device *cdev) | |||
778 | goto out_free; | 1047 | goto out_free; |
779 | } | 1048 | } |
780 | 1049 | ||
1050 | vcdev->is_thinint = virtio_ccw_use_airq; /* at least try */ | ||
1051 | |||
781 | vcdev->vdev.dev.parent = &cdev->dev; | 1052 | vcdev->vdev.dev.parent = &cdev->dev; |
782 | vcdev->vdev.dev.release = virtio_ccw_release_dev; | 1053 | vcdev->vdev.dev.release = virtio_ccw_release_dev; |
783 | vcdev->vdev.config = &virtio_ccw_config_ops; | 1054 | vcdev->vdev.config = &virtio_ccw_config_ops; |
@@ -786,7 +1057,9 @@ static int virtio_ccw_online(struct ccw_device *cdev) | |||
786 | INIT_LIST_HEAD(&vcdev->virtqueues); | 1057 | INIT_LIST_HEAD(&vcdev->virtqueues); |
787 | spin_lock_init(&vcdev->lock); | 1058 | spin_lock_init(&vcdev->lock); |
788 | 1059 | ||
1060 | spin_lock_irqsave(get_ccwdev_lock(cdev), flags); | ||
789 | dev_set_drvdata(&cdev->dev, vcdev); | 1061 | dev_set_drvdata(&cdev->dev, vcdev); |
1062 | spin_unlock_irqrestore(get_ccwdev_lock(cdev), flags); | ||
790 | vcdev->vdev.id.vendor = cdev->id.cu_type; | 1063 | vcdev->vdev.id.vendor = cdev->id.cu_type; |
791 | vcdev->vdev.id.device = cdev->id.cu_model; | 1064 | vcdev->vdev.id.device = cdev->id.cu_model; |
792 | ret = register_virtio_device(&vcdev->vdev); | 1065 | ret = register_virtio_device(&vcdev->vdev); |
@@ -797,7 +1070,9 @@ static int virtio_ccw_online(struct ccw_device *cdev) | |||
797 | } | 1070 | } |
798 | return 0; | 1071 | return 0; |
799 | out_put: | 1072 | out_put: |
1073 | spin_lock_irqsave(get_ccwdev_lock(cdev), flags); | ||
800 | dev_set_drvdata(&cdev->dev, NULL); | 1074 | dev_set_drvdata(&cdev->dev, NULL); |
1075 | spin_unlock_irqrestore(get_ccwdev_lock(cdev), flags); | ||
801 | put_device(&vcdev->vdev.dev); | 1076 | put_device(&vcdev->vdev.dev); |
802 | return ret; | 1077 | return ret; |
803 | out_free: | 1078 | out_free: |
@@ -935,6 +1210,10 @@ module_init(virtio_ccw_init); | |||
935 | 1210 | ||
936 | static void __exit virtio_ccw_exit(void) | 1211 | static void __exit virtio_ccw_exit(void) |
937 | { | 1212 | { |
1213 | int i; | ||
1214 | |||
938 | ccw_driver_unregister(&virtio_ccw_driver); | 1215 | ccw_driver_unregister(&virtio_ccw_driver); |
1216 | for (i = 0; i < MAX_AIRQ_AREAS; i++) | ||
1217 | destroy_airq_info(airq_areas[i]); | ||
939 | } | 1218 | } |
940 | module_exit(virtio_ccw_exit); | 1219 | module_exit(virtio_ccw_exit); |