diff options
author | Cornelia Huck <cornelia.huck@de.ibm.com> | 2012-12-14 11:02:18 -0500 |
---|---|---|
committer | Gleb Natapov <gleb@redhat.com> | 2012-12-18 07:37:13 -0500 |
commit | 7e64e0597fd67c975bfa3e76401bfbcdd5ae0ff9 (patch) | |
tree | 8af50236372d9db8679e9cbf10fad40766fedf4e /drivers/s390 | |
parent | 0abbe448eddb2263db3fb776757f480b34accd88 (diff) |
KVM: s390: Add a channel I/O based virtio transport driver.
Add a driver for kvm guests that matches virtual ccw devices provided
by the host as virtio bridge devices.
These virtio-ccw devices use a special set of channel commands in order
to perform virtio functions.
Reviewed-by: Marcelo Tosatti <mtosatti@redhat.com>
Reviewed-by: Alexander Graf <agraf@suse.de>
Signed-off-by: Cornelia Huck <cornelia.huck@de.ibm.com>
Signed-off-by: Gleb Natapov <gleb@redhat.com>
Diffstat (limited to 'drivers/s390')
-rw-r--r-- | drivers/s390/kvm/Makefile | 2 | ||||
-rw-r--r-- | drivers/s390/kvm/virtio_ccw.c | 853 |
2 files changed, 854 insertions, 1 deletions
diff --git a/drivers/s390/kvm/Makefile b/drivers/s390/kvm/Makefile index 0815690ac1e0..241891a57caf 100644 --- a/drivers/s390/kvm/Makefile +++ b/drivers/s390/kvm/Makefile | |||
@@ -6,4 +6,4 @@ | |||
6 | # it under the terms of the GNU General Public License (version 2 only) | 6 | # it under the terms of the GNU General Public License (version 2 only) |
7 | # as published by the Free Software Foundation. | 7 | # as published by the Free Software Foundation. |
8 | 8 | ||
9 | obj-$(CONFIG_S390_GUEST) += kvm_virtio.o | 9 | obj-$(CONFIG_S390_GUEST) += kvm_virtio.o virtio_ccw.o |
diff --git a/drivers/s390/kvm/virtio_ccw.c b/drivers/s390/kvm/virtio_ccw.c new file mode 100644 index 000000000000..1a5aff31d752 --- /dev/null +++ b/drivers/s390/kvm/virtio_ccw.c | |||
@@ -0,0 +1,853 @@ | |||
1 | /* | ||
2 | * ccw based virtio transport | ||
3 | * | ||
4 | * Copyright IBM Corp. 2012 | ||
5 | * | ||
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) | ||
8 | * as published by the Free Software Foundation. | ||
9 | * | ||
10 | * Author(s): Cornelia Huck <cornelia.huck@de.ibm.com> | ||
11 | */ | ||
12 | |||
13 | #include <linux/kernel_stat.h> | ||
14 | #include <linux/init.h> | ||
15 | #include <linux/bootmem.h> | ||
16 | #include <linux/err.h> | ||
17 | #include <linux/virtio.h> | ||
18 | #include <linux/virtio_config.h> | ||
19 | #include <linux/slab.h> | ||
20 | #include <linux/interrupt.h> | ||
21 | #include <linux/virtio_ring.h> | ||
22 | #include <linux/pfn.h> | ||
23 | #include <linux/async.h> | ||
24 | #include <linux/wait.h> | ||
25 | #include <linux/list.h> | ||
26 | #include <linux/bitops.h> | ||
27 | #include <linux/module.h> | ||
28 | #include <linux/io.h> | ||
29 | #include <linux/kvm_para.h> | ||
30 | #include <asm/setup.h> | ||
31 | #include <asm/irq.h> | ||
32 | #include <asm/cio.h> | ||
33 | #include <asm/ccwdev.h> | ||
34 | |||
35 | /* | ||
36 | * virtio related functions | ||
37 | */ | ||
38 | |||
39 | struct vq_config_block { | ||
40 | __u16 index; | ||
41 | __u16 num; | ||
42 | } __packed; | ||
43 | |||
44 | #define VIRTIO_CCW_CONFIG_SIZE 0x100 | ||
45 | /* same as PCI config space size, should be enough for all drivers */ | ||
46 | |||
47 | struct virtio_ccw_device { | ||
48 | struct virtio_device vdev; | ||
49 | __u8 status; | ||
50 | __u8 config[VIRTIO_CCW_CONFIG_SIZE]; | ||
51 | struct ccw_device *cdev; | ||
52 | struct ccw1 *ccw; | ||
53 | __u32 area; | ||
54 | __u32 curr_io; | ||
55 | int err; | ||
56 | wait_queue_head_t wait_q; | ||
57 | spinlock_t lock; | ||
58 | struct list_head virtqueues; | ||
59 | unsigned long indicators; | ||
60 | unsigned long indicators2; | ||
61 | struct vq_config_block *config_block; | ||
62 | }; | ||
63 | |||
64 | struct vq_info_block { | ||
65 | __u64 queue; | ||
66 | __u32 align; | ||
67 | __u16 index; | ||
68 | __u16 num; | ||
69 | } __packed; | ||
70 | |||
71 | struct virtio_feature_desc { | ||
72 | __u32 features; | ||
73 | __u8 index; | ||
74 | } __packed; | ||
75 | |||
76 | struct virtio_ccw_vq_info { | ||
77 | struct virtqueue *vq; | ||
78 | int num; | ||
79 | void *queue; | ||
80 | struct vq_info_block *info_block; | ||
81 | struct list_head node; | ||
82 | }; | ||
83 | |||
84 | #define KVM_VIRTIO_CCW_RING_ALIGN 4096 | ||
85 | |||
86 | #define KVM_S390_VIRTIO_CCW_NOTIFY 3 | ||
87 | |||
88 | #define CCW_CMD_SET_VQ 0x13 | ||
89 | #define CCW_CMD_VDEV_RESET 0x33 | ||
90 | #define CCW_CMD_SET_IND 0x43 | ||
91 | #define CCW_CMD_SET_CONF_IND 0x53 | ||
92 | #define CCW_CMD_READ_FEAT 0x12 | ||
93 | #define CCW_CMD_WRITE_FEAT 0x11 | ||
94 | #define CCW_CMD_READ_CONF 0x22 | ||
95 | #define CCW_CMD_WRITE_CONF 0x21 | ||
96 | #define CCW_CMD_WRITE_STATUS 0x31 | ||
97 | #define CCW_CMD_READ_VQ_CONF 0x32 | ||
98 | |||
99 | #define VIRTIO_CCW_DOING_SET_VQ 0x00010000 | ||
100 | #define VIRTIO_CCW_DOING_RESET 0x00040000 | ||
101 | #define VIRTIO_CCW_DOING_READ_FEAT 0x00080000 | ||
102 | #define VIRTIO_CCW_DOING_WRITE_FEAT 0x00100000 | ||
103 | #define VIRTIO_CCW_DOING_READ_CONFIG 0x00200000 | ||
104 | #define VIRTIO_CCW_DOING_WRITE_CONFIG 0x00400000 | ||
105 | #define VIRTIO_CCW_DOING_WRITE_STATUS 0x00800000 | ||
106 | #define VIRTIO_CCW_DOING_SET_IND 0x01000000 | ||
107 | #define VIRTIO_CCW_DOING_READ_VQ_CONF 0x02000000 | ||
108 | #define VIRTIO_CCW_DOING_SET_CONF_IND 0x04000000 | ||
109 | #define VIRTIO_CCW_INTPARM_MASK 0xffff0000 | ||
110 | |||
111 | static struct virtio_ccw_device *to_vc_device(struct virtio_device *vdev) | ||
112 | { | ||
113 | return container_of(vdev, struct virtio_ccw_device, vdev); | ||
114 | } | ||
115 | |||
116 | static int doing_io(struct virtio_ccw_device *vcdev, __u32 flag) | ||
117 | { | ||
118 | unsigned long flags; | ||
119 | __u32 ret; | ||
120 | |||
121 | spin_lock_irqsave(get_ccwdev_lock(vcdev->cdev), flags); | ||
122 | if (vcdev->err) | ||
123 | ret = 0; | ||
124 | else | ||
125 | ret = vcdev->curr_io & flag; | ||
126 | spin_unlock_irqrestore(get_ccwdev_lock(vcdev->cdev), flags); | ||
127 | return ret; | ||
128 | } | ||
129 | |||
130 | static int ccw_io_helper(struct virtio_ccw_device *vcdev, __u32 intparm) | ||
131 | { | ||
132 | int ret; | ||
133 | unsigned long flags; | ||
134 | int flag = intparm & VIRTIO_CCW_INTPARM_MASK; | ||
135 | |||
136 | spin_lock_irqsave(get_ccwdev_lock(vcdev->cdev), flags); | ||
137 | ret = ccw_device_start(vcdev->cdev, vcdev->ccw, intparm, 0, 0); | ||
138 | if (!ret) | ||
139 | vcdev->curr_io |= flag; | ||
140 | spin_unlock_irqrestore(get_ccwdev_lock(vcdev->cdev), flags); | ||
141 | wait_event(vcdev->wait_q, doing_io(vcdev, flag) == 0); | ||
142 | return ret ? ret : vcdev->err; | ||
143 | } | ||
144 | |||
145 | static inline long do_kvm_notify(struct subchannel_id schid, | ||
146 | unsigned long queue_index) | ||
147 | { | ||
148 | register unsigned long __nr asm("1") = KVM_S390_VIRTIO_CCW_NOTIFY; | ||
149 | register struct subchannel_id __schid asm("2") = schid; | ||
150 | register unsigned long __index asm("3") = queue_index; | ||
151 | register long __rc asm("2"); | ||
152 | |||
153 | asm volatile ("diag 2,4,0x500\n" | ||
154 | : "=d" (__rc) : "d" (__nr), "d" (__schid), "d" (__index) | ||
155 | : "memory", "cc"); | ||
156 | return __rc; | ||
157 | } | ||
158 | |||
159 | static void virtio_ccw_kvm_notify(struct virtqueue *vq) | ||
160 | { | ||
161 | struct virtio_ccw_vq_info *info = vq->priv; | ||
162 | struct virtio_ccw_device *vcdev; | ||
163 | struct subchannel_id schid; | ||
164 | |||
165 | vcdev = to_vc_device(info->vq->vdev); | ||
166 | ccw_device_get_schid(vcdev->cdev, &schid); | ||
167 | do_kvm_notify(schid, virtqueue_get_queue_index(vq)); | ||
168 | } | ||
169 | |||
170 | static int virtio_ccw_read_vq_conf(struct virtio_ccw_device *vcdev, int index) | ||
171 | { | ||
172 | vcdev->config_block->index = index; | ||
173 | vcdev->ccw->cmd_code = CCW_CMD_READ_VQ_CONF; | ||
174 | vcdev->ccw->flags = 0; | ||
175 | vcdev->ccw->count = sizeof(struct vq_config_block); | ||
176 | vcdev->ccw->cda = (__u32)(unsigned long)(vcdev->config_block); | ||
177 | ccw_io_helper(vcdev, VIRTIO_CCW_DOING_READ_VQ_CONF); | ||
178 | return vcdev->config_block->num; | ||
179 | } | ||
180 | |||
181 | static void virtio_ccw_del_vq(struct virtqueue *vq) | ||
182 | { | ||
183 | struct virtio_ccw_device *vcdev = to_vc_device(vq->vdev); | ||
184 | struct virtio_ccw_vq_info *info = vq->priv; | ||
185 | unsigned long flags; | ||
186 | unsigned long size; | ||
187 | int ret; | ||
188 | unsigned int index = virtqueue_get_queue_index(vq); | ||
189 | |||
190 | /* Remove from our list. */ | ||
191 | spin_lock_irqsave(&vcdev->lock, flags); | ||
192 | list_del(&info->node); | ||
193 | spin_unlock_irqrestore(&vcdev->lock, flags); | ||
194 | |||
195 | /* Release from host. */ | ||
196 | info->info_block->queue = 0; | ||
197 | info->info_block->align = 0; | ||
198 | info->info_block->index = index; | ||
199 | info->info_block->num = 0; | ||
200 | vcdev->ccw->cmd_code = CCW_CMD_SET_VQ; | ||
201 | vcdev->ccw->flags = 0; | ||
202 | vcdev->ccw->count = sizeof(*info->info_block); | ||
203 | vcdev->ccw->cda = (__u32)(unsigned long)(info->info_block); | ||
204 | ret = ccw_io_helper(vcdev, VIRTIO_CCW_DOING_SET_VQ | index); | ||
205 | /* | ||
206 | * -ENODEV isn't considered an error: The device is gone anyway. | ||
207 | * This may happen on device detach. | ||
208 | */ | ||
209 | if (ret && (ret != -ENODEV)) | ||
210 | dev_warn(&vq->vdev->dev, "Error %d while deleting queue %d", | ||
211 | ret, index); | ||
212 | |||
213 | vring_del_virtqueue(vq); | ||
214 | size = PAGE_ALIGN(vring_size(info->num, KVM_VIRTIO_CCW_RING_ALIGN)); | ||
215 | free_pages_exact(info->queue, size); | ||
216 | kfree(info->info_block); | ||
217 | kfree(info); | ||
218 | } | ||
219 | |||
220 | static void virtio_ccw_del_vqs(struct virtio_device *vdev) | ||
221 | { | ||
222 | struct virtqueue *vq, *n; | ||
223 | |||
224 | list_for_each_entry_safe(vq, n, &vdev->vqs, list) | ||
225 | virtio_ccw_del_vq(vq); | ||
226 | } | ||
227 | |||
228 | static struct virtqueue *virtio_ccw_setup_vq(struct virtio_device *vdev, | ||
229 | int i, vq_callback_t *callback, | ||
230 | const char *name) | ||
231 | { | ||
232 | struct virtio_ccw_device *vcdev = to_vc_device(vdev); | ||
233 | int err; | ||
234 | struct virtqueue *vq; | ||
235 | struct virtio_ccw_vq_info *info; | ||
236 | unsigned long size; | ||
237 | unsigned long flags; | ||
238 | |||
239 | /* Allocate queue. */ | ||
240 | info = kzalloc(sizeof(struct virtio_ccw_vq_info), GFP_KERNEL); | ||
241 | if (!info) { | ||
242 | dev_warn(&vcdev->cdev->dev, "no info\n"); | ||
243 | err = -ENOMEM; | ||
244 | goto out_err; | ||
245 | } | ||
246 | info->info_block = kzalloc(sizeof(*info->info_block), | ||
247 | GFP_DMA | GFP_KERNEL); | ||
248 | if (!info->info_block) { | ||
249 | dev_warn(&vcdev->cdev->dev, "no info block\n"); | ||
250 | err = -ENOMEM; | ||
251 | goto out_err; | ||
252 | } | ||
253 | info->num = virtio_ccw_read_vq_conf(vcdev, i); | ||
254 | size = PAGE_ALIGN(vring_size(info->num, KVM_VIRTIO_CCW_RING_ALIGN)); | ||
255 | info->queue = alloc_pages_exact(size, GFP_KERNEL | __GFP_ZERO); | ||
256 | if (info->queue == NULL) { | ||
257 | dev_warn(&vcdev->cdev->dev, "no queue\n"); | ||
258 | err = -ENOMEM; | ||
259 | goto out_err; | ||
260 | } | ||
261 | |||
262 | vq = vring_new_virtqueue(i, info->num, KVM_VIRTIO_CCW_RING_ALIGN, vdev, | ||
263 | true, info->queue, virtio_ccw_kvm_notify, | ||
264 | callback, name); | ||
265 | if (!vq) { | ||
266 | /* For now, we fail if we can't get the requested size. */ | ||
267 | dev_warn(&vcdev->cdev->dev, "no vq\n"); | ||
268 | err = -ENOMEM; | ||
269 | free_pages_exact(info->queue, size); | ||
270 | goto out_err; | ||
271 | } | ||
272 | info->vq = vq; | ||
273 | vq->priv = info; | ||
274 | |||
275 | /* Register it with the host. */ | ||
276 | info->info_block->queue = (__u64)info->queue; | ||
277 | info->info_block->align = KVM_VIRTIO_CCW_RING_ALIGN; | ||
278 | info->info_block->index = i; | ||
279 | info->info_block->num = info->num; | ||
280 | vcdev->ccw->cmd_code = CCW_CMD_SET_VQ; | ||
281 | vcdev->ccw->flags = 0; | ||
282 | vcdev->ccw->count = sizeof(*info->info_block); | ||
283 | vcdev->ccw->cda = (__u32)(unsigned long)(info->info_block); | ||
284 | err = ccw_io_helper(vcdev, VIRTIO_CCW_DOING_SET_VQ | i); | ||
285 | if (err) { | ||
286 | dev_warn(&vcdev->cdev->dev, "SET_VQ failed\n"); | ||
287 | free_pages_exact(info->queue, size); | ||
288 | info->vq = NULL; | ||
289 | vq->priv = NULL; | ||
290 | goto out_err; | ||
291 | } | ||
292 | |||
293 | /* Save it to our list. */ | ||
294 | spin_lock_irqsave(&vcdev->lock, flags); | ||
295 | list_add(&info->node, &vcdev->virtqueues); | ||
296 | spin_unlock_irqrestore(&vcdev->lock, flags); | ||
297 | |||
298 | return vq; | ||
299 | |||
300 | out_err: | ||
301 | if (info) | ||
302 | kfree(info->info_block); | ||
303 | kfree(info); | ||
304 | return ERR_PTR(err); | ||
305 | } | ||
306 | |||
307 | static int virtio_ccw_find_vqs(struct virtio_device *vdev, unsigned nvqs, | ||
308 | struct virtqueue *vqs[], | ||
309 | vq_callback_t *callbacks[], | ||
310 | const char *names[]) | ||
311 | { | ||
312 | struct virtio_ccw_device *vcdev = to_vc_device(vdev); | ||
313 | unsigned long *indicatorp = NULL; | ||
314 | int ret, i; | ||
315 | |||
316 | for (i = 0; i < nvqs; ++i) { | ||
317 | vqs[i] = virtio_ccw_setup_vq(vdev, i, callbacks[i], names[i]); | ||
318 | if (IS_ERR(vqs[i])) { | ||
319 | ret = PTR_ERR(vqs[i]); | ||
320 | vqs[i] = NULL; | ||
321 | goto out; | ||
322 | } | ||
323 | } | ||
324 | ret = -ENOMEM; | ||
325 | /* We need a data area under 2G to communicate. */ | ||
326 | indicatorp = kmalloc(sizeof(&vcdev->indicators), GFP_DMA | GFP_KERNEL); | ||
327 | if (!indicatorp) | ||
328 | goto out; | ||
329 | *indicatorp = (unsigned long) &vcdev->indicators; | ||
330 | /* Register queue indicators with host. */ | ||
331 | vcdev->indicators = 0; | ||
332 | vcdev->ccw->cmd_code = CCW_CMD_SET_IND; | ||
333 | vcdev->ccw->flags = 0; | ||
334 | vcdev->ccw->count = sizeof(vcdev->indicators); | ||
335 | vcdev->ccw->cda = (__u32)(unsigned long) indicatorp; | ||
336 | ret = ccw_io_helper(vcdev, VIRTIO_CCW_DOING_SET_IND); | ||
337 | if (ret) | ||
338 | goto out; | ||
339 | /* Register indicators2 with host for config changes */ | ||
340 | *indicatorp = (unsigned long) &vcdev->indicators2; | ||
341 | vcdev->indicators2 = 0; | ||
342 | vcdev->ccw->cmd_code = CCW_CMD_SET_CONF_IND; | ||
343 | vcdev->ccw->flags = 0; | ||
344 | vcdev->ccw->count = sizeof(vcdev->indicators2); | ||
345 | vcdev->ccw->cda = (__u32)(unsigned long) indicatorp; | ||
346 | ret = ccw_io_helper(vcdev, VIRTIO_CCW_DOING_SET_CONF_IND); | ||
347 | if (ret) | ||
348 | goto out; | ||
349 | |||
350 | kfree(indicatorp); | ||
351 | return 0; | ||
352 | out: | ||
353 | kfree(indicatorp); | ||
354 | virtio_ccw_del_vqs(vdev); | ||
355 | return ret; | ||
356 | } | ||
357 | |||
358 | static void virtio_ccw_reset(struct virtio_device *vdev) | ||
359 | { | ||
360 | struct virtio_ccw_device *vcdev = to_vc_device(vdev); | ||
361 | |||
362 | /* Zero status bits. */ | ||
363 | vcdev->status = 0; | ||
364 | |||
365 | /* Send a reset ccw on device. */ | ||
366 | vcdev->ccw->cmd_code = CCW_CMD_VDEV_RESET; | ||
367 | vcdev->ccw->flags = 0; | ||
368 | vcdev->ccw->count = 0; | ||
369 | vcdev->ccw->cda = 0; | ||
370 | ccw_io_helper(vcdev, VIRTIO_CCW_DOING_RESET); | ||
371 | } | ||
372 | |||
373 | static u32 virtio_ccw_get_features(struct virtio_device *vdev) | ||
374 | { | ||
375 | struct virtio_ccw_device *vcdev = to_vc_device(vdev); | ||
376 | struct virtio_feature_desc features; | ||
377 | int ret; | ||
378 | |||
379 | /* Read the feature bits from the host. */ | ||
380 | /* TODO: Features > 32 bits */ | ||
381 | features.index = 0; | ||
382 | vcdev->ccw->cmd_code = CCW_CMD_READ_FEAT; | ||
383 | vcdev->ccw->flags = 0; | ||
384 | vcdev->ccw->count = sizeof(features); | ||
385 | vcdev->ccw->cda = vcdev->area; | ||
386 | ret = ccw_io_helper(vcdev, VIRTIO_CCW_DOING_READ_FEAT); | ||
387 | if (ret) | ||
388 | return 0; | ||
389 | |||
390 | memcpy(&features, (void *)(unsigned long)vcdev->area, | ||
391 | sizeof(features)); | ||
392 | return le32_to_cpu(features.features); | ||
393 | } | ||
394 | |||
395 | static void virtio_ccw_finalize_features(struct virtio_device *vdev) | ||
396 | { | ||
397 | struct virtio_ccw_device *vcdev = to_vc_device(vdev); | ||
398 | struct virtio_feature_desc features; | ||
399 | int i; | ||
400 | |||
401 | /* Give virtio_ring a chance to accept features. */ | ||
402 | vring_transport_features(vdev); | ||
403 | |||
404 | for (i = 0; i < sizeof(*vdev->features) / sizeof(features.features); | ||
405 | i++) { | ||
406 | int highbits = i % 2 ? 32 : 0; | ||
407 | features.index = i; | ||
408 | features.features = cpu_to_le32(vdev->features[i / 2] | ||
409 | >> highbits); | ||
410 | memcpy((void *)(unsigned long)vcdev->area, &features, | ||
411 | sizeof(features)); | ||
412 | /* Write the feature bits to the host. */ | ||
413 | vcdev->ccw->cmd_code = CCW_CMD_WRITE_FEAT; | ||
414 | vcdev->ccw->flags = 0; | ||
415 | vcdev->ccw->count = sizeof(features); | ||
416 | vcdev->ccw->cda = vcdev->area; | ||
417 | ccw_io_helper(vcdev, VIRTIO_CCW_DOING_WRITE_FEAT); | ||
418 | } | ||
419 | } | ||
420 | |||
421 | static void virtio_ccw_get_config(struct virtio_device *vdev, | ||
422 | unsigned int offset, void *buf, unsigned len) | ||
423 | { | ||
424 | struct virtio_ccw_device *vcdev = to_vc_device(vdev); | ||
425 | int ret; | ||
426 | |||
427 | /* Read the config area from the host. */ | ||
428 | vcdev->ccw->cmd_code = CCW_CMD_READ_CONF; | ||
429 | vcdev->ccw->flags = 0; | ||
430 | vcdev->ccw->count = offset + len; | ||
431 | vcdev->ccw->cda = vcdev->area; | ||
432 | ret = ccw_io_helper(vcdev, VIRTIO_CCW_DOING_READ_CONFIG); | ||
433 | if (ret) | ||
434 | return; | ||
435 | |||
436 | memcpy(vcdev->config, (void *)(unsigned long)vcdev->area, | ||
437 | sizeof(vcdev->config)); | ||
438 | memcpy(buf, &vcdev->config[offset], len); | ||
439 | } | ||
440 | |||
441 | static void virtio_ccw_set_config(struct virtio_device *vdev, | ||
442 | unsigned int offset, const void *buf, | ||
443 | unsigned len) | ||
444 | { | ||
445 | struct virtio_ccw_device *vcdev = to_vc_device(vdev); | ||
446 | |||
447 | memcpy(&vcdev->config[offset], buf, len); | ||
448 | /* Write the config area to the host. */ | ||
449 | memcpy((void *)(unsigned long)vcdev->area, vcdev->config, | ||
450 | sizeof(vcdev->config)); | ||
451 | vcdev->ccw->cmd_code = CCW_CMD_WRITE_CONF; | ||
452 | vcdev->ccw->flags = 0; | ||
453 | vcdev->ccw->count = offset + len; | ||
454 | vcdev->ccw->cda = vcdev->area; | ||
455 | ccw_io_helper(vcdev, VIRTIO_CCW_DOING_WRITE_CONFIG); | ||
456 | } | ||
457 | |||
458 | static u8 virtio_ccw_get_status(struct virtio_device *vdev) | ||
459 | { | ||
460 | struct virtio_ccw_device *vcdev = to_vc_device(vdev); | ||
461 | |||
462 | return vcdev->status; | ||
463 | } | ||
464 | |||
465 | static void virtio_ccw_set_status(struct virtio_device *vdev, u8 status) | ||
466 | { | ||
467 | struct virtio_ccw_device *vcdev = to_vc_device(vdev); | ||
468 | |||
469 | /* Write the status to the host. */ | ||
470 | vcdev->status = status; | ||
471 | memcpy((void *)(unsigned long)vcdev->area, &status, sizeof(status)); | ||
472 | vcdev->ccw->cmd_code = CCW_CMD_WRITE_STATUS; | ||
473 | vcdev->ccw->flags = 0; | ||
474 | vcdev->ccw->count = sizeof(status); | ||
475 | vcdev->ccw->cda = vcdev->area; | ||
476 | ccw_io_helper(vcdev, VIRTIO_CCW_DOING_WRITE_STATUS); | ||
477 | } | ||
478 | |||
479 | static struct virtio_config_ops virtio_ccw_config_ops = { | ||
480 | .get_features = virtio_ccw_get_features, | ||
481 | .finalize_features = virtio_ccw_finalize_features, | ||
482 | .get = virtio_ccw_get_config, | ||
483 | .set = virtio_ccw_set_config, | ||
484 | .get_status = virtio_ccw_get_status, | ||
485 | .set_status = virtio_ccw_set_status, | ||
486 | .reset = virtio_ccw_reset, | ||
487 | .find_vqs = virtio_ccw_find_vqs, | ||
488 | .del_vqs = virtio_ccw_del_vqs, | ||
489 | }; | ||
490 | |||
491 | |||
492 | /* | ||
493 | * ccw bus driver related functions | ||
494 | */ | ||
495 | |||
496 | static void virtio_ccw_release_dev(struct device *_d) | ||
497 | { | ||
498 | struct virtio_device *dev = container_of(_d, struct virtio_device, | ||
499 | dev); | ||
500 | struct virtio_ccw_device *vcdev = to_vc_device(dev); | ||
501 | |||
502 | kfree((void *)(unsigned long)vcdev->area); | ||
503 | kfree(vcdev->config_block); | ||
504 | kfree(vcdev->ccw); | ||
505 | kfree(vcdev); | ||
506 | } | ||
507 | |||
508 | static int irb_is_error(struct irb *irb) | ||
509 | { | ||
510 | if (scsw_cstat(&irb->scsw) != 0) | ||
511 | return 1; | ||
512 | if (scsw_dstat(&irb->scsw) & ~(DEV_STAT_CHN_END | DEV_STAT_DEV_END)) | ||
513 | return 1; | ||
514 | if (scsw_cc(&irb->scsw) != 0) | ||
515 | return 1; | ||
516 | return 0; | ||
517 | } | ||
518 | |||
519 | static struct virtqueue *virtio_ccw_vq_by_ind(struct virtio_ccw_device *vcdev, | ||
520 | int index) | ||
521 | { | ||
522 | struct virtio_ccw_vq_info *info; | ||
523 | unsigned long flags; | ||
524 | struct virtqueue *vq; | ||
525 | |||
526 | vq = NULL; | ||
527 | spin_lock_irqsave(&vcdev->lock, flags); | ||
528 | list_for_each_entry(info, &vcdev->virtqueues, node) { | ||
529 | if (virtqueue_get_queue_index(info->vq) == index) { | ||
530 | vq = info->vq; | ||
531 | break; | ||
532 | } | ||
533 | } | ||
534 | spin_unlock_irqrestore(&vcdev->lock, flags); | ||
535 | return vq; | ||
536 | } | ||
537 | |||
538 | static void virtio_ccw_int_handler(struct ccw_device *cdev, | ||
539 | unsigned long intparm, | ||
540 | struct irb *irb) | ||
541 | { | ||
542 | __u32 activity = intparm & VIRTIO_CCW_INTPARM_MASK; | ||
543 | struct virtio_ccw_device *vcdev = dev_get_drvdata(&cdev->dev); | ||
544 | int i; | ||
545 | struct virtqueue *vq; | ||
546 | struct virtio_driver *drv; | ||
547 | |||
548 | /* Check if it's a notification from the host. */ | ||
549 | if ((intparm == 0) && | ||
550 | (scsw_stctl(&irb->scsw) == | ||
551 | (SCSW_STCTL_ALERT_STATUS | SCSW_STCTL_STATUS_PEND))) { | ||
552 | /* OK */ | ||
553 | } | ||
554 | if (irb_is_error(irb)) | ||
555 | vcdev->err = -EIO; /* XXX - use real error */ | ||
556 | if (vcdev->curr_io & activity) { | ||
557 | switch (activity) { | ||
558 | case VIRTIO_CCW_DOING_READ_FEAT: | ||
559 | case VIRTIO_CCW_DOING_WRITE_FEAT: | ||
560 | case VIRTIO_CCW_DOING_READ_CONFIG: | ||
561 | case VIRTIO_CCW_DOING_WRITE_CONFIG: | ||
562 | case VIRTIO_CCW_DOING_WRITE_STATUS: | ||
563 | case VIRTIO_CCW_DOING_SET_VQ: | ||
564 | case VIRTIO_CCW_DOING_SET_IND: | ||
565 | case VIRTIO_CCW_DOING_SET_CONF_IND: | ||
566 | case VIRTIO_CCW_DOING_RESET: | ||
567 | case VIRTIO_CCW_DOING_READ_VQ_CONF: | ||
568 | vcdev->curr_io &= ~activity; | ||
569 | wake_up(&vcdev->wait_q); | ||
570 | break; | ||
571 | default: | ||
572 | /* don't know what to do... */ | ||
573 | dev_warn(&cdev->dev, "Suspicious activity '%08x'\n", | ||
574 | activity); | ||
575 | WARN_ON(1); | ||
576 | break; | ||
577 | } | ||
578 | } | ||
579 | for_each_set_bit(i, &vcdev->indicators, | ||
580 | sizeof(vcdev->indicators) * BITS_PER_BYTE) { | ||
581 | /* The bit clear must happen before the vring kick. */ | ||
582 | clear_bit(i, &vcdev->indicators); | ||
583 | barrier(); | ||
584 | vq = virtio_ccw_vq_by_ind(vcdev, i); | ||
585 | vring_interrupt(0, vq); | ||
586 | } | ||
587 | if (test_bit(0, &vcdev->indicators2)) { | ||
588 | drv = container_of(vcdev->vdev.dev.driver, | ||
589 | struct virtio_driver, driver); | ||
590 | |||
591 | if (drv && drv->config_changed) | ||
592 | drv->config_changed(&vcdev->vdev); | ||
593 | clear_bit(0, &vcdev->indicators2); | ||
594 | } | ||
595 | } | ||
596 | |||
597 | /* | ||
598 | * We usually want to autoonline all devices, but give the admin | ||
599 | * a way to exempt devices from this. | ||
600 | */ | ||
601 | #define __DEV_WORDS ((__MAX_SUBCHANNEL + (8*sizeof(long) - 1)) / \ | ||
602 | (8*sizeof(long))) | ||
603 | static unsigned long devs_no_auto[__MAX_SSID + 1][__DEV_WORDS]; | ||
604 | |||
605 | static char *no_auto = ""; | ||
606 | |||
607 | module_param(no_auto, charp, 0444); | ||
608 | MODULE_PARM_DESC(no_auto, "list of ccw bus id ranges not to be auto-onlined"); | ||
609 | |||
610 | static int virtio_ccw_check_autoonline(struct ccw_device *cdev) | ||
611 | { | ||
612 | struct ccw_dev_id id; | ||
613 | |||
614 | ccw_device_get_id(cdev, &id); | ||
615 | if (test_bit(id.devno, devs_no_auto[id.ssid])) | ||
616 | return 0; | ||
617 | return 1; | ||
618 | } | ||
619 | |||
620 | static void virtio_ccw_auto_online(void *data, async_cookie_t cookie) | ||
621 | { | ||
622 | struct ccw_device *cdev = data; | ||
623 | int ret; | ||
624 | |||
625 | ret = ccw_device_set_online(cdev); | ||
626 | if (ret) | ||
627 | dev_warn(&cdev->dev, "Failed to set online: %d\n", ret); | ||
628 | } | ||
629 | |||
630 | static int virtio_ccw_probe(struct ccw_device *cdev) | ||
631 | { | ||
632 | cdev->handler = virtio_ccw_int_handler; | ||
633 | |||
634 | if (virtio_ccw_check_autoonline(cdev)) | ||
635 | async_schedule(virtio_ccw_auto_online, cdev); | ||
636 | return 0; | ||
637 | } | ||
638 | |||
639 | static void virtio_ccw_remove(struct ccw_device *cdev) | ||
640 | { | ||
641 | struct virtio_ccw_device *vcdev = dev_get_drvdata(&cdev->dev); | ||
642 | |||
643 | if (cdev->online) { | ||
644 | unregister_virtio_device(&vcdev->vdev); | ||
645 | dev_set_drvdata(&cdev->dev, NULL); | ||
646 | } | ||
647 | cdev->handler = NULL; | ||
648 | } | ||
649 | |||
650 | static int virtio_ccw_offline(struct ccw_device *cdev) | ||
651 | { | ||
652 | struct virtio_ccw_device *vcdev = dev_get_drvdata(&cdev->dev); | ||
653 | |||
654 | unregister_virtio_device(&vcdev->vdev); | ||
655 | dev_set_drvdata(&cdev->dev, NULL); | ||
656 | return 0; | ||
657 | } | ||
658 | |||
659 | |||
660 | /* Area needs to be big enough to fit status, features or configuration. */ | ||
661 | #define VIRTIO_AREA_SIZE VIRTIO_CCW_CONFIG_SIZE /* biggest possible use */ | ||
662 | |||
663 | static int virtio_ccw_online(struct ccw_device *cdev) | ||
664 | { | ||
665 | int ret; | ||
666 | struct virtio_ccw_device *vcdev; | ||
667 | |||
668 | vcdev = kzalloc(sizeof(*vcdev), GFP_KERNEL); | ||
669 | if (!vcdev) { | ||
670 | dev_warn(&cdev->dev, "Could not get memory for virtio\n"); | ||
671 | ret = -ENOMEM; | ||
672 | goto out_free; | ||
673 | } | ||
674 | vcdev->area = (__u32)(unsigned long)kzalloc(VIRTIO_AREA_SIZE, | ||
675 | GFP_DMA | GFP_KERNEL); | ||
676 | if (!vcdev->area) { | ||
677 | dev_warn(&cdev->dev, "Cound not get memory for virtio\n"); | ||
678 | ret = -ENOMEM; | ||
679 | goto out_free; | ||
680 | } | ||
681 | vcdev->config_block = kzalloc(sizeof(*vcdev->config_block), | ||
682 | GFP_DMA | GFP_KERNEL); | ||
683 | if (!vcdev->config_block) { | ||
684 | ret = -ENOMEM; | ||
685 | goto out_free; | ||
686 | } | ||
687 | vcdev->ccw = kzalloc(sizeof(*vcdev->ccw), GFP_DMA | GFP_KERNEL); | ||
688 | if (!vcdev->ccw) { | ||
689 | ret = -ENOMEM; | ||
690 | goto out_free; | ||
691 | } | ||
692 | |||
693 | vcdev->vdev.dev.parent = &cdev->dev; | ||
694 | vcdev->vdev.dev.release = virtio_ccw_release_dev; | ||
695 | vcdev->vdev.config = &virtio_ccw_config_ops; | ||
696 | vcdev->cdev = cdev; | ||
697 | init_waitqueue_head(&vcdev->wait_q); | ||
698 | INIT_LIST_HEAD(&vcdev->virtqueues); | ||
699 | spin_lock_init(&vcdev->lock); | ||
700 | |||
701 | dev_set_drvdata(&cdev->dev, vcdev); | ||
702 | vcdev->vdev.id.vendor = cdev->id.cu_type; | ||
703 | vcdev->vdev.id.device = cdev->id.cu_model; | ||
704 | ret = register_virtio_device(&vcdev->vdev); | ||
705 | if (ret) { | ||
706 | dev_warn(&cdev->dev, "Failed to register virtio device: %d\n", | ||
707 | ret); | ||
708 | goto out_put; | ||
709 | } | ||
710 | return 0; | ||
711 | out_put: | ||
712 | dev_set_drvdata(&cdev->dev, NULL); | ||
713 | put_device(&vcdev->vdev.dev); | ||
714 | return ret; | ||
715 | out_free: | ||
716 | if (vcdev) { | ||
717 | kfree((void *)(unsigned long)vcdev->area); | ||
718 | kfree(vcdev->config_block); | ||
719 | kfree(vcdev->ccw); | ||
720 | } | ||
721 | kfree(vcdev); | ||
722 | return ret; | ||
723 | } | ||
724 | |||
725 | static int virtio_ccw_cio_notify(struct ccw_device *cdev, int event) | ||
726 | { | ||
727 | /* TODO: Check whether we need special handling here. */ | ||
728 | return 0; | ||
729 | } | ||
730 | |||
731 | static struct ccw_device_id virtio_ids[] = { | ||
732 | { CCW_DEVICE(0x3832, 0) }, | ||
733 | {}, | ||
734 | }; | ||
735 | MODULE_DEVICE_TABLE(ccw, virtio_ids); | ||
736 | |||
737 | static struct ccw_driver virtio_ccw_driver = { | ||
738 | .driver = { | ||
739 | .owner = THIS_MODULE, | ||
740 | .name = "virtio_ccw", | ||
741 | }, | ||
742 | .ids = virtio_ids, | ||
743 | .probe = virtio_ccw_probe, | ||
744 | .remove = virtio_ccw_remove, | ||
745 | .set_offline = virtio_ccw_offline, | ||
746 | .set_online = virtio_ccw_online, | ||
747 | .notify = virtio_ccw_cio_notify, | ||
748 | .int_class = IOINT_VIR, | ||
749 | }; | ||
750 | |||
751 | static int __init pure_hex(char **cp, unsigned int *val, int min_digit, | ||
752 | int max_digit, int max_val) | ||
753 | { | ||
754 | int diff; | ||
755 | |||
756 | diff = 0; | ||
757 | *val = 0; | ||
758 | |||
759 | while (diff <= max_digit) { | ||
760 | int value = hex_to_bin(**cp); | ||
761 | |||
762 | if (value < 0) | ||
763 | break; | ||
764 | *val = *val * 16 + value; | ||
765 | (*cp)++; | ||
766 | diff++; | ||
767 | } | ||
768 | |||
769 | if ((diff < min_digit) || (diff > max_digit) || (*val > max_val)) | ||
770 | return 1; | ||
771 | |||
772 | return 0; | ||
773 | } | ||
774 | |||
775 | static int __init parse_busid(char *str, unsigned int *cssid, | ||
776 | unsigned int *ssid, unsigned int *devno) | ||
777 | { | ||
778 | char *str_work; | ||
779 | int rc, ret; | ||
780 | |||
781 | rc = 1; | ||
782 | |||
783 | if (*str == '\0') | ||
784 | goto out; | ||
785 | |||
786 | str_work = str; | ||
787 | ret = pure_hex(&str_work, cssid, 1, 2, __MAX_CSSID); | ||
788 | if (ret || (str_work[0] != '.')) | ||
789 | goto out; | ||
790 | str_work++; | ||
791 | ret = pure_hex(&str_work, ssid, 1, 1, __MAX_SSID); | ||
792 | if (ret || (str_work[0] != '.')) | ||
793 | goto out; | ||
794 | str_work++; | ||
795 | ret = pure_hex(&str_work, devno, 4, 4, __MAX_SUBCHANNEL); | ||
796 | if (ret || (str_work[0] != '\0')) | ||
797 | goto out; | ||
798 | |||
799 | rc = 0; | ||
800 | out: | ||
801 | return rc; | ||
802 | } | ||
803 | |||
804 | static void __init no_auto_parse(void) | ||
805 | { | ||
806 | unsigned int from_cssid, to_cssid, from_ssid, to_ssid, from, to; | ||
807 | char *parm, *str; | ||
808 | int rc; | ||
809 | |||
810 | str = no_auto; | ||
811 | while ((parm = strsep(&str, ","))) { | ||
812 | rc = parse_busid(strsep(&parm, "-"), &from_cssid, | ||
813 | &from_ssid, &from); | ||
814 | if (rc) | ||
815 | continue; | ||
816 | if (parm != NULL) { | ||
817 | rc = parse_busid(parm, &to_cssid, | ||
818 | &to_ssid, &to); | ||
819 | if ((from_ssid > to_ssid) || | ||
820 | ((from_ssid == to_ssid) && (from > to))) | ||
821 | rc = -EINVAL; | ||
822 | } else { | ||
823 | to_cssid = from_cssid; | ||
824 | to_ssid = from_ssid; | ||
825 | to = from; | ||
826 | } | ||
827 | if (rc) | ||
828 | continue; | ||
829 | while ((from_ssid < to_ssid) || | ||
830 | ((from_ssid == to_ssid) && (from <= to))) { | ||
831 | set_bit(from, devs_no_auto[from_ssid]); | ||
832 | from++; | ||
833 | if (from > __MAX_SUBCHANNEL) { | ||
834 | from_ssid++; | ||
835 | from = 0; | ||
836 | } | ||
837 | } | ||
838 | } | ||
839 | } | ||
840 | |||
841 | static int __init virtio_ccw_init(void) | ||
842 | { | ||
843 | /* parse no_auto string before we do anything further */ | ||
844 | no_auto_parse(); | ||
845 | return ccw_driver_register(&virtio_ccw_driver); | ||
846 | } | ||
847 | module_init(virtio_ccw_init); | ||
848 | |||
849 | static void __exit virtio_ccw_exit(void) | ||
850 | { | ||
851 | ccw_driver_unregister(&virtio_ccw_driver); | ||
852 | } | ||
853 | module_exit(virtio_ccw_exit); | ||