diff options
Diffstat (limited to 'drivers/s390')
-rw-r--r-- | drivers/s390/kvm/virtio_ccw.c | 323 |
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 | ||
63 | struct vq_info_block { | 68 | struct vq_info_block { |
@@ -72,15 +77,38 @@ struct virtio_feature_desc { | |||
72 | __u8 index; | 77 | __u8 index; |
73 | } __packed; | 78 | } __packed; |
74 | 79 | ||
80 | struct virtio_thinint_area { | ||
81 | unsigned long summary_indicator; | ||
82 | unsigned long indicator; | ||
83 | u64 bit_nr; | ||
84 | u8 isc; | ||
85 | } __packed; | ||
86 | |||
75 | struct virtio_ccw_vq_info { | 87 | struct 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 | |||
102 | static int virtio_ccw_use_airq = 1; | ||
103 | |||
104 | struct airq_info { | ||
105 | rwlock_t lock; | ||
106 | u8 summary_indicator; | ||
107 | struct airq_struct airq; | ||
108 | struct airq_iv *aiv; | ||
109 | }; | ||
110 | static 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 | ||
107 | static struct virtio_ccw_device *to_vc_device(struct virtio_device *vdev) | 137 | static 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 | ||
142 | static 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 | |||
157 | static 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 | |||
183 | static 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 | |||
210 | static 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 | |||
220 | static 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 | |||
253 | static 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 | |||
112 | static int doing_io(struct virtio_ccw_device *vcdev, __u32 flag) | 261 | static 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 | ||
297 | static 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 | |||
148 | static inline long do_kvm_notify(struct subchannel_id schid, | 342 | static 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 | ||
525 | static 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 | } | ||
568 | out: | ||
569 | kfree(thinint_area); | ||
570 | return ret; | ||
571 | } | ||
572 | |||
329 | static int virtio_ccw_find_vqs(struct virtio_device *vdev, unsigned nvqs, | 573 | static 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 | ||
992 | static 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 | |||
737 | static void virtio_ccw_remove(struct ccw_device *cdev) | 1008 | static 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 | ||
748 | static int virtio_ccw_offline(struct ccw_device *cdev) | 1021 | static 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; |
799 | out_put: | 1082 | out_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; |
803 | out_free: | 1088 | out_free: |
@@ -935,6 +1220,10 @@ module_init(virtio_ccw_init); | |||
935 | 1220 | ||
936 | static void __exit virtio_ccw_exit(void) | 1221 | static 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 | } |
940 | module_exit(virtio_ccw_exit); | 1229 | module_exit(virtio_ccw_exit); |