aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/s390/kvm/virtio_ccw.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/s390/kvm/virtio_ccw.c')
-rw-r--r--drivers/s390/kvm/virtio_ccw.c323
1 files changed, 306 insertions, 17 deletions
diff --git a/drivers/s390/kvm/virtio_ccw.c b/drivers/s390/kvm/virtio_ccw.c
index 0fc584832001..1e1fc671f89a 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,9 @@ 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 bool going_away;
65 void *airq_info;
61}; 66};
62 67
63struct vq_info_block { 68struct vq_info_block {
@@ -72,15 +77,38 @@ struct virtio_feature_desc {
72 __u8 index; 77 __u8 index;
73} __packed; 78} __packed;
74 79
80struct virtio_thinint_area {
81 unsigned long summary_indicator;
82 unsigned long indicator;
83 u64 bit_nr;
84 u8 isc;
85} __packed;
86
75struct virtio_ccw_vq_info { 87struct virtio_ccw_vq_info {
76 struct virtqueue *vq; 88 struct virtqueue *vq;
77 int num; 89 int num;
78 void *queue; 90 void *queue;
79 struct vq_info_block *info_block; 91 struct vq_info_block *info_block;
92 int bit_nr;
80 struct list_head node; 93 struct list_head node;
81 long cookie; 94 long cookie;
82}; 95};
83 96
97#define VIRTIO_AIRQ_ISC IO_SCH_ISC /* inherit from subchannel */
98
99#define VIRTIO_IV_BITS (L1_CACHE_BYTES * 8)
100#define MAX_AIRQ_AREAS 20
101
102static int virtio_ccw_use_airq = 1;
103
104struct airq_info {
105 rwlock_t lock;
106 u8 summary_indicator;
107 struct airq_struct airq;
108 struct airq_iv *aiv;
109};
110static struct airq_info *airq_areas[MAX_AIRQ_AREAS];
111
84#define CCW_CMD_SET_VQ 0x13 112#define CCW_CMD_SET_VQ 0x13
85#define CCW_CMD_VDEV_RESET 0x33 113#define CCW_CMD_VDEV_RESET 0x33
86#define CCW_CMD_SET_IND 0x43 114#define CCW_CMD_SET_IND 0x43
@@ -91,6 +119,7 @@ struct virtio_ccw_vq_info {
91#define CCW_CMD_WRITE_CONF 0x21 119#define CCW_CMD_WRITE_CONF 0x21
92#define CCW_CMD_WRITE_STATUS 0x31 120#define CCW_CMD_WRITE_STATUS 0x31
93#define CCW_CMD_READ_VQ_CONF 0x32 121#define CCW_CMD_READ_VQ_CONF 0x32
122#define CCW_CMD_SET_IND_ADAPTER 0x73
94 123
95#define VIRTIO_CCW_DOING_SET_VQ 0x00010000 124#define VIRTIO_CCW_DOING_SET_VQ 0x00010000
96#define VIRTIO_CCW_DOING_RESET 0x00040000 125#define VIRTIO_CCW_DOING_RESET 0x00040000
@@ -102,6 +131,7 @@ struct virtio_ccw_vq_info {
102#define VIRTIO_CCW_DOING_SET_IND 0x01000000 131#define VIRTIO_CCW_DOING_SET_IND 0x01000000
103#define VIRTIO_CCW_DOING_READ_VQ_CONF 0x02000000 132#define VIRTIO_CCW_DOING_READ_VQ_CONF 0x02000000
104#define VIRTIO_CCW_DOING_SET_CONF_IND 0x04000000 133#define VIRTIO_CCW_DOING_SET_CONF_IND 0x04000000
134#define VIRTIO_CCW_DOING_SET_IND_ADAPTER 0x08000000
105#define VIRTIO_CCW_INTPARM_MASK 0xffff0000 135#define VIRTIO_CCW_INTPARM_MASK 0xffff0000
106 136
107static struct virtio_ccw_device *to_vc_device(struct virtio_device *vdev) 137static struct virtio_ccw_device *to_vc_device(struct virtio_device *vdev)
@@ -109,6 +139,125 @@ static struct virtio_ccw_device *to_vc_device(struct virtio_device *vdev)
109 return container_of(vdev, struct virtio_ccw_device, vdev); 139 return container_of(vdev, struct virtio_ccw_device, vdev);
110} 140}
111 141
142static void drop_airq_indicator(struct virtqueue *vq, struct airq_info *info)
143{
144 unsigned long i, flags;
145
146 write_lock_irqsave(&info->lock, flags);
147 for (i = 0; i < airq_iv_end(info->aiv); i++) {
148 if (vq == (void *)airq_iv_get_ptr(info->aiv, i)) {
149 airq_iv_free_bit(info->aiv, i);
150 airq_iv_set_ptr(info->aiv, i, 0);
151 break;
152 }
153 }
154 write_unlock_irqrestore(&info->lock, flags);
155}
156
157static void virtio_airq_handler(struct airq_struct *airq)
158{
159 struct airq_info *info = container_of(airq, struct airq_info, airq);
160 unsigned long ai;
161
162 inc_irq_stat(IRQIO_VAI);
163 read_lock(&info->lock);
164 /* Walk through indicators field, summary indicator active. */
165 for (ai = 0;;) {
166 ai = airq_iv_scan(info->aiv, ai, airq_iv_end(info->aiv));
167 if (ai == -1UL)
168 break;
169 vring_interrupt(0, (void *)airq_iv_get_ptr(info->aiv, ai));
170 }
171 info->summary_indicator = 0;
172 smp_wmb();
173 /* Walk through indicators field, summary indicator not active. */
174 for (ai = 0;;) {
175 ai = airq_iv_scan(info->aiv, ai, airq_iv_end(info->aiv));
176 if (ai == -1UL)
177 break;
178 vring_interrupt(0, (void *)airq_iv_get_ptr(info->aiv, ai));
179 }
180 read_unlock(&info->lock);
181}
182
183static struct airq_info *new_airq_info(void)
184{
185 struct airq_info *info;
186 int rc;
187
188 info = kzalloc(sizeof(*info), GFP_KERNEL);
189 if (!info)
190 return NULL;
191 rwlock_init(&info->lock);
192 info->aiv = airq_iv_create(VIRTIO_IV_BITS, AIRQ_IV_ALLOC | AIRQ_IV_PTR);
193 if (!info->aiv) {
194 kfree(info);
195 return NULL;
196 }
197 info->airq.handler = virtio_airq_handler;
198 info->airq.lsi_ptr = &info->summary_indicator;
199 info->airq.lsi_mask = 0xff;
200 info->airq.isc = VIRTIO_AIRQ_ISC;
201 rc = register_adapter_interrupt(&info->airq);
202 if (rc) {
203 airq_iv_release(info->aiv);
204 kfree(info);
205 return NULL;
206 }
207 return info;
208}
209
210static void destroy_airq_info(struct airq_info *info)
211{
212 if (!info)
213 return;
214
215 unregister_adapter_interrupt(&info->airq);
216 airq_iv_release(info->aiv);
217 kfree(info);
218}
219
220static unsigned long get_airq_indicator(struct virtqueue *vqs[], int nvqs,
221 u64 *first, void **airq_info)
222{
223 int i, j;
224 struct airq_info *info;
225 unsigned long indicator_addr = 0;
226 unsigned long bit, flags;
227
228 for (i = 0; i < MAX_AIRQ_AREAS && !indicator_addr; i++) {
229 if (!airq_areas[i])
230 airq_areas[i] = new_airq_info();
231 info = airq_areas[i];
232 if (!info)
233 return 0;
234 write_lock_irqsave(&info->lock, flags);
235 bit = airq_iv_alloc(info->aiv, nvqs);
236 if (bit == -1UL) {
237 /* Not enough vacancies. */
238 write_unlock_irqrestore(&info->lock, flags);
239 continue;
240 }
241 *first = bit;
242 *airq_info = info;
243 indicator_addr = (unsigned long)info->aiv->vector;
244 for (j = 0; j < nvqs; j++) {
245 airq_iv_set_ptr(info->aiv, bit + j,
246 (unsigned long)vqs[j]);
247 }
248 write_unlock_irqrestore(&info->lock, flags);
249 }
250 return indicator_addr;
251}
252
253static void virtio_ccw_drop_indicators(struct virtio_ccw_device *vcdev)
254{
255 struct virtio_ccw_vq_info *info;
256
257 list_for_each_entry(info, &vcdev->virtqueues, node)
258 drop_airq_indicator(info->vq, vcdev->airq_info);
259}
260
112static int doing_io(struct virtio_ccw_device *vcdev, __u32 flag) 261static int doing_io(struct virtio_ccw_device *vcdev, __u32 flag)
113{ 262{
114 unsigned long flags; 263 unsigned long flags;
@@ -145,6 +294,51 @@ static int ccw_io_helper(struct virtio_ccw_device *vcdev,
145 return ret ? ret : vcdev->err; 294 return ret ? ret : vcdev->err;
146} 295}
147 296
297static void virtio_ccw_drop_indicator(struct virtio_ccw_device *vcdev,
298 struct ccw1 *ccw)
299{
300 int ret;
301 unsigned long *indicatorp = NULL;
302 struct virtio_thinint_area *thinint_area = NULL;
303 struct airq_info *airq_info = vcdev->airq_info;
304
305 if (vcdev->is_thinint) {
306 thinint_area = kzalloc(sizeof(*thinint_area),
307 GFP_DMA | GFP_KERNEL);
308 if (!thinint_area)
309 return;
310 thinint_area->summary_indicator =
311 (unsigned long) &airq_info->summary_indicator;
312 thinint_area->isc = VIRTIO_AIRQ_ISC;
313 ccw->cmd_code = CCW_CMD_SET_IND_ADAPTER;
314 ccw->count = sizeof(*thinint_area);
315 ccw->cda = (__u32)(unsigned long) thinint_area;
316 } else {
317 indicatorp = kmalloc(sizeof(&vcdev->indicators),
318 GFP_DMA | GFP_KERNEL);
319 if (!indicatorp)
320 return;
321 *indicatorp = 0;
322 ccw->cmd_code = CCW_CMD_SET_IND;
323 ccw->count = sizeof(vcdev->indicators);
324 ccw->cda = (__u32)(unsigned long) indicatorp;
325 }
326 /* Deregister indicators from host. */
327 vcdev->indicators = 0;
328 ccw->flags = 0;
329 ret = ccw_io_helper(vcdev, ccw,
330 vcdev->is_thinint ?
331 VIRTIO_CCW_DOING_SET_IND_ADAPTER :
332 VIRTIO_CCW_DOING_SET_IND);
333 if (ret && (ret != -ENODEV))
334 dev_info(&vcdev->cdev->dev,
335 "Failed to deregister indicators (%d)\n", ret);
336 else if (vcdev->is_thinint)
337 virtio_ccw_drop_indicators(vcdev);
338 kfree(indicatorp);
339 kfree(thinint_area);
340}
341
148static inline long do_kvm_notify(struct subchannel_id schid, 342static inline long do_kvm_notify(struct subchannel_id schid,
149 unsigned long queue_index, 343 unsigned long queue_index,
150 long cookie) 344 long cookie)
@@ -232,11 +426,13 @@ static void virtio_ccw_del_vqs(struct virtio_device *vdev)
232{ 426{
233 struct virtqueue *vq, *n; 427 struct virtqueue *vq, *n;
234 struct ccw1 *ccw; 428 struct ccw1 *ccw;
429 struct virtio_ccw_device *vcdev = to_vc_device(vdev);
235 430
236 ccw = kzalloc(sizeof(*ccw), GFP_DMA | GFP_KERNEL); 431 ccw = kzalloc(sizeof(*ccw), GFP_DMA | GFP_KERNEL);
237 if (!ccw) 432 if (!ccw)
238 return; 433 return;
239 434
435 virtio_ccw_drop_indicator(vcdev, ccw);
240 436
241 list_for_each_entry_safe(vq, n, &vdev->vqs, list) 437 list_for_each_entry_safe(vq, n, &vdev->vqs, list)
242 virtio_ccw_del_vq(vq, ccw); 438 virtio_ccw_del_vq(vq, ccw);
@@ -326,6 +522,54 @@ out_err:
326 return ERR_PTR(err); 522 return ERR_PTR(err);
327} 523}
328 524
525static int virtio_ccw_register_adapter_ind(struct virtio_ccw_device *vcdev,
526 struct virtqueue *vqs[], int nvqs,
527 struct ccw1 *ccw)
528{
529 int ret;
530 struct virtio_thinint_area *thinint_area = NULL;
531 struct airq_info *info;
532
533 thinint_area = kzalloc(sizeof(*thinint_area), GFP_DMA | GFP_KERNEL);
534 if (!thinint_area) {
535 ret = -ENOMEM;
536 goto out;
537 }
538 /* Try to get an indicator. */
539 thinint_area->indicator = get_airq_indicator(vqs, nvqs,
540 &thinint_area->bit_nr,
541 &vcdev->airq_info);
542 if (!thinint_area->indicator) {
543 ret = -ENOSPC;
544 goto out;
545 }
546 info = vcdev->airq_info;
547 thinint_area->summary_indicator =
548 (unsigned long) &info->summary_indicator;
549 thinint_area->isc = VIRTIO_AIRQ_ISC;
550 ccw->cmd_code = CCW_CMD_SET_IND_ADAPTER;
551 ccw->flags = CCW_FLAG_SLI;
552 ccw->count = sizeof(*thinint_area);
553 ccw->cda = (__u32)(unsigned long)thinint_area;
554 ret = ccw_io_helper(vcdev, ccw, VIRTIO_CCW_DOING_SET_IND_ADAPTER);
555 if (ret) {
556 if (ret == -EOPNOTSUPP) {
557 /*
558 * The host does not support adapter interrupts
559 * for virtio-ccw, stop trying.
560 */
561 virtio_ccw_use_airq = 0;
562 pr_info("Adapter interrupts unsupported on host\n");
563 } else
564 dev_warn(&vcdev->cdev->dev,
565 "enabling adapter interrupts = %d\n", ret);
566 virtio_ccw_drop_indicators(vcdev);
567 }
568out:
569 kfree(thinint_area);
570 return ret;
571}
572
329static int virtio_ccw_find_vqs(struct virtio_device *vdev, unsigned nvqs, 573static int virtio_ccw_find_vqs(struct virtio_device *vdev, unsigned nvqs,
330 struct virtqueue *vqs[], 574 struct virtqueue *vqs[],
331 vq_callback_t *callbacks[], 575 vq_callback_t *callbacks[],
@@ -355,15 +599,23 @@ static int virtio_ccw_find_vqs(struct virtio_device *vdev, unsigned nvqs,
355 if (!indicatorp) 599 if (!indicatorp)
356 goto out; 600 goto out;
357 *indicatorp = (unsigned long) &vcdev->indicators; 601 *indicatorp = (unsigned long) &vcdev->indicators;
358 /* Register queue indicators with host. */ 602 if (vcdev->is_thinint) {
359 vcdev->indicators = 0; 603 ret = virtio_ccw_register_adapter_ind(vcdev, vqs, nvqs, ccw);
360 ccw->cmd_code = CCW_CMD_SET_IND; 604 if (ret)
361 ccw->flags = 0; 605 /* no error, just fall back to legacy interrupts */
362 ccw->count = sizeof(vcdev->indicators); 606 vcdev->is_thinint = 0;
363 ccw->cda = (__u32)(unsigned long) indicatorp; 607 }
364 ret = ccw_io_helper(vcdev, ccw, VIRTIO_CCW_DOING_SET_IND); 608 if (!vcdev->is_thinint) {
365 if (ret) 609 /* Register queue indicators with host. */
366 goto out; 610 vcdev->indicators = 0;
611 ccw->cmd_code = CCW_CMD_SET_IND;
612 ccw->flags = 0;
613 ccw->count = sizeof(vcdev->indicators);
614 ccw->cda = (__u32)(unsigned long) indicatorp;
615 ret = ccw_io_helper(vcdev, ccw, VIRTIO_CCW_DOING_SET_IND);
616 if (ret)
617 goto out;
618 }
367 /* Register indicators2 with host for config changes */ 619 /* Register indicators2 with host for config changes */
368 *indicatorp = (unsigned long) &vcdev->indicators2; 620 *indicatorp = (unsigned long) &vcdev->indicators2;
369 vcdev->indicators2 = 0; 621 vcdev->indicators2 = 0;
@@ -636,6 +888,8 @@ static void virtio_ccw_int_handler(struct ccw_device *cdev,
636 struct virtqueue *vq; 888 struct virtqueue *vq;
637 struct virtio_driver *drv; 889 struct virtio_driver *drv;
638 890
891 if (!vcdev)
892 return;
639 /* Check if it's a notification from the host. */ 893 /* Check if it's a notification from the host. */
640 if ((intparm == 0) && 894 if ((intparm == 0) &&
641 (scsw_stctl(&irb->scsw) == 895 (scsw_stctl(&irb->scsw) ==
@@ -663,6 +917,7 @@ static void virtio_ccw_int_handler(struct ccw_device *cdev,
663 case VIRTIO_CCW_DOING_SET_CONF_IND: 917 case VIRTIO_CCW_DOING_SET_CONF_IND:
664 case VIRTIO_CCW_DOING_RESET: 918 case VIRTIO_CCW_DOING_RESET:
665 case VIRTIO_CCW_DOING_READ_VQ_CONF: 919 case VIRTIO_CCW_DOING_READ_VQ_CONF:
920 case VIRTIO_CCW_DOING_SET_IND_ADAPTER:
666 vcdev->curr_io &= ~activity; 921 vcdev->curr_io &= ~activity;
667 wake_up(&vcdev->wait_q); 922 wake_up(&vcdev->wait_q);
668 break; 923 break;
@@ -734,23 +989,46 @@ static int virtio_ccw_probe(struct ccw_device *cdev)
734 return 0; 989 return 0;
735} 990}
736 991
992static struct virtio_ccw_device *virtio_grab_drvdata(struct ccw_device *cdev)
993{
994 unsigned long flags;
995 struct virtio_ccw_device *vcdev;
996
997 spin_lock_irqsave(get_ccwdev_lock(cdev), flags);
998 vcdev = dev_get_drvdata(&cdev->dev);
999 if (!vcdev || vcdev->going_away) {
1000 spin_unlock_irqrestore(get_ccwdev_lock(cdev), flags);
1001 return NULL;
1002 }
1003 vcdev->going_away = true;
1004 spin_unlock_irqrestore(get_ccwdev_lock(cdev), flags);
1005 return vcdev;
1006}
1007
737static void virtio_ccw_remove(struct ccw_device *cdev) 1008static void virtio_ccw_remove(struct ccw_device *cdev)
738{ 1009{
739 struct virtio_ccw_device *vcdev = dev_get_drvdata(&cdev->dev); 1010 unsigned long flags;
1011 struct virtio_ccw_device *vcdev = virtio_grab_drvdata(cdev);
740 1012
741 if (cdev->online) { 1013 if (vcdev && cdev->online)
742 unregister_virtio_device(&vcdev->vdev); 1014 unregister_virtio_device(&vcdev->vdev);
743 dev_set_drvdata(&cdev->dev, NULL); 1015 spin_lock_irqsave(get_ccwdev_lock(cdev), flags);
744 } 1016 dev_set_drvdata(&cdev->dev, NULL);
1017 spin_unlock_irqrestore(get_ccwdev_lock(cdev), flags);
745 cdev->handler = NULL; 1018 cdev->handler = NULL;
746} 1019}
747 1020
748static int virtio_ccw_offline(struct ccw_device *cdev) 1021static int virtio_ccw_offline(struct ccw_device *cdev)
749{ 1022{
750 struct virtio_ccw_device *vcdev = dev_get_drvdata(&cdev->dev); 1023 unsigned long flags;
1024 struct virtio_ccw_device *vcdev = virtio_grab_drvdata(cdev);
751 1025
752 unregister_virtio_device(&vcdev->vdev); 1026 if (vcdev) {
753 dev_set_drvdata(&cdev->dev, NULL); 1027 unregister_virtio_device(&vcdev->vdev);
1028 spin_lock_irqsave(get_ccwdev_lock(cdev), flags);
1029 dev_set_drvdata(&cdev->dev, NULL);
1030 spin_unlock_irqrestore(get_ccwdev_lock(cdev), flags);
1031 }
754 return 0; 1032 return 0;
755} 1033}
756 1034
@@ -759,6 +1037,7 @@ static int virtio_ccw_online(struct ccw_device *cdev)
759{ 1037{
760 int ret; 1038 int ret;
761 struct virtio_ccw_device *vcdev; 1039 struct virtio_ccw_device *vcdev;
1040 unsigned long flags;
762 1041
763 vcdev = kzalloc(sizeof(*vcdev), GFP_KERNEL); 1042 vcdev = kzalloc(sizeof(*vcdev), GFP_KERNEL);
764 if (!vcdev) { 1043 if (!vcdev) {
@@ -778,6 +1057,8 @@ static int virtio_ccw_online(struct ccw_device *cdev)
778 goto out_free; 1057 goto out_free;
779 } 1058 }
780 1059
1060 vcdev->is_thinint = virtio_ccw_use_airq; /* at least try */
1061
781 vcdev->vdev.dev.parent = &cdev->dev; 1062 vcdev->vdev.dev.parent = &cdev->dev;
782 vcdev->vdev.dev.release = virtio_ccw_release_dev; 1063 vcdev->vdev.dev.release = virtio_ccw_release_dev;
783 vcdev->vdev.config = &virtio_ccw_config_ops; 1064 vcdev->vdev.config = &virtio_ccw_config_ops;
@@ -786,7 +1067,9 @@ static int virtio_ccw_online(struct ccw_device *cdev)
786 INIT_LIST_HEAD(&vcdev->virtqueues); 1067 INIT_LIST_HEAD(&vcdev->virtqueues);
787 spin_lock_init(&vcdev->lock); 1068 spin_lock_init(&vcdev->lock);
788 1069
1070 spin_lock_irqsave(get_ccwdev_lock(cdev), flags);
789 dev_set_drvdata(&cdev->dev, vcdev); 1071 dev_set_drvdata(&cdev->dev, vcdev);
1072 spin_unlock_irqrestore(get_ccwdev_lock(cdev), flags);
790 vcdev->vdev.id.vendor = cdev->id.cu_type; 1073 vcdev->vdev.id.vendor = cdev->id.cu_type;
791 vcdev->vdev.id.device = cdev->id.cu_model; 1074 vcdev->vdev.id.device = cdev->id.cu_model;
792 ret = register_virtio_device(&vcdev->vdev); 1075 ret = register_virtio_device(&vcdev->vdev);
@@ -797,7 +1080,9 @@ static int virtio_ccw_online(struct ccw_device *cdev)
797 } 1080 }
798 return 0; 1081 return 0;
799out_put: 1082out_put:
1083 spin_lock_irqsave(get_ccwdev_lock(cdev), flags);
800 dev_set_drvdata(&cdev->dev, NULL); 1084 dev_set_drvdata(&cdev->dev, NULL);
1085 spin_unlock_irqrestore(get_ccwdev_lock(cdev), flags);
801 put_device(&vcdev->vdev.dev); 1086 put_device(&vcdev->vdev.dev);
802 return ret; 1087 return ret;
803out_free: 1088out_free:
@@ -935,6 +1220,10 @@ module_init(virtio_ccw_init);
935 1220
936static void __exit virtio_ccw_exit(void) 1221static void __exit virtio_ccw_exit(void)
937{ 1222{
1223 int i;
1224
938 ccw_driver_unregister(&virtio_ccw_driver); 1225 ccw_driver_unregister(&virtio_ccw_driver);
1226 for (i = 0; i < MAX_AIRQ_AREAS; i++)
1227 destroy_airq_info(airq_areas[i]);
939} 1228}
940module_exit(virtio_ccw_exit); 1229module_exit(virtio_ccw_exit);