diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2013-02-24 16:07:18 -0500 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2013-02-24 16:07:18 -0500 |
commit | 89f883372fa60f604d136924baf3e89ff1870e9e (patch) | |
tree | cb69b0a14957945ba00d3d392bf9ccbbef56f3b8 /drivers/s390 | |
parent | 9e2d59ad580d590134285f361a0e80f0e98c0207 (diff) | |
parent | 6b73a96065e89dc9fa75ba4f78b1aa3a3bbd0470 (diff) |
Merge tag 'kvm-3.9-1' of git://git.kernel.org/pub/scm/virt/kvm/kvm
Pull KVM updates from Marcelo Tosatti:
"KVM updates for the 3.9 merge window, including x86 real mode
emulation fixes, stronger memory slot interface restrictions, mmu_lock
spinlock hold time reduction, improved handling of large page faults
on shadow, initial APICv HW acceleration support, s390 channel IO
based virtio, amongst others"
* tag 'kvm-3.9-1' of git://git.kernel.org/pub/scm/virt/kvm/kvm: (143 commits)
Revert "KVM: MMU: lazily drop large spte"
x86: pvclock kvm: align allocation size to page size
KVM: nVMX: Remove redundant get_vmcs12 from nested_vmx_exit_handled_msr
x86 emulator: fix parity calculation for AAD instruction
KVM: PPC: BookE: Handle alignment interrupts
booke: Added DBCR4 SPR number
KVM: PPC: booke: Allow multiple exception types
KVM: PPC: booke: use vcpu reference from thread_struct
KVM: Remove user_alloc from struct kvm_memory_slot
KVM: VMX: disable apicv by default
KVM: s390: Fix handling of iscs.
KVM: MMU: cleanup __direct_map
KVM: MMU: remove pt_access in mmu_set_spte
KVM: MMU: cleanup mapping-level
KVM: MMU: lazily drop large spte
KVM: VMX: cleanup vmx_set_cr0().
KVM: VMX: add missing exit names to VMX_EXIT_REASONS array
KVM: VMX: disable SMEP feature when guest is in non-paging mode
KVM: Remove duplicate text in api.txt
Revert "KVM: MMU: split kvm_mmu_free_page"
...
Diffstat (limited to 'drivers/s390')
-rw-r--r-- | drivers/s390/kvm/Makefile | 2 | ||||
-rw-r--r-- | drivers/s390/kvm/kvm_virtio.c | 38 | ||||
-rw-r--r-- | drivers/s390/kvm/virtio_ccw.c | 926 |
3 files changed, 957 insertions, 9 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/kvm_virtio.c b/drivers/s390/kvm/kvm_virtio.c index 8491111aec12..03a15e016778 100644 --- a/drivers/s390/kvm/kvm_virtio.c +++ b/drivers/s390/kvm/kvm_virtio.c | |||
@@ -422,6 +422,26 @@ static void kvm_extint_handler(struct ext_code ext_code, | |||
422 | } | 422 | } |
423 | 423 | ||
424 | /* | 424 | /* |
425 | * For s390-virtio, we expect a page above main storage containing | ||
426 | * the virtio configuration. Try to actually load from this area | ||
427 | * in order to figure out if the host provides this page. | ||
428 | */ | ||
429 | static int __init test_devices_support(unsigned long addr) | ||
430 | { | ||
431 | int ret = -EIO; | ||
432 | |||
433 | asm volatile( | ||
434 | "0: lura 0,%1\n" | ||
435 | "1: xgr %0,%0\n" | ||
436 | "2:\n" | ||
437 | EX_TABLE(0b,2b) | ||
438 | EX_TABLE(1b,2b) | ||
439 | : "+d" (ret) | ||
440 | : "a" (addr) | ||
441 | : "0", "cc"); | ||
442 | return ret; | ||
443 | } | ||
444 | /* | ||
425 | * Init function for virtio | 445 | * Init function for virtio |
426 | * devices are in a single page above top of "normal" mem | 446 | * devices are in a single page above top of "normal" mem |
427 | */ | 447 | */ |
@@ -432,21 +452,23 @@ static int __init kvm_devices_init(void) | |||
432 | if (!MACHINE_IS_KVM) | 452 | if (!MACHINE_IS_KVM) |
433 | return -ENODEV; | 453 | return -ENODEV; |
434 | 454 | ||
455 | if (test_devices_support(real_memory_size) < 0) | ||
456 | return -ENODEV; | ||
457 | |||
458 | rc = vmem_add_mapping(real_memory_size, PAGE_SIZE); | ||
459 | if (rc) | ||
460 | return rc; | ||
461 | |||
462 | kvm_devices = (void *) real_memory_size; | ||
463 | |||
435 | kvm_root = root_device_register("kvm_s390"); | 464 | kvm_root = root_device_register("kvm_s390"); |
436 | if (IS_ERR(kvm_root)) { | 465 | if (IS_ERR(kvm_root)) { |
437 | rc = PTR_ERR(kvm_root); | 466 | rc = PTR_ERR(kvm_root); |
438 | printk(KERN_ERR "Could not register kvm_s390 root device"); | 467 | printk(KERN_ERR "Could not register kvm_s390 root device"); |
468 | vmem_remove_mapping(real_memory_size, PAGE_SIZE); | ||
439 | return rc; | 469 | return rc; |
440 | } | 470 | } |
441 | 471 | ||
442 | rc = vmem_add_mapping(real_memory_size, PAGE_SIZE); | ||
443 | if (rc) { | ||
444 | root_device_unregister(kvm_root); | ||
445 | return rc; | ||
446 | } | ||
447 | |||
448 | kvm_devices = (void *) real_memory_size; | ||
449 | |||
450 | INIT_WORK(&hotplug_work, hotplug_devices); | 472 | INIT_WORK(&hotplug_work, hotplug_devices); |
451 | 473 | ||
452 | service_subclass_irq_register(); | 474 | service_subclass_irq_register(); |
diff --git a/drivers/s390/kvm/virtio_ccw.c b/drivers/s390/kvm/virtio_ccw.c new file mode 100644 index 000000000000..2029b6caa595 --- /dev/null +++ b/drivers/s390/kvm/virtio_ccw.c | |||
@@ -0,0 +1,926 @@ | |||
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 | __u32 curr_io; | ||
53 | int err; | ||
54 | wait_queue_head_t wait_q; | ||
55 | spinlock_t lock; | ||
56 | struct list_head virtqueues; | ||
57 | unsigned long indicators; | ||
58 | unsigned long indicators2; | ||
59 | struct vq_config_block *config_block; | ||
60 | }; | ||
61 | |||
62 | struct vq_info_block { | ||
63 | __u64 queue; | ||
64 | __u32 align; | ||
65 | __u16 index; | ||
66 | __u16 num; | ||
67 | } __packed; | ||
68 | |||
69 | struct virtio_feature_desc { | ||
70 | __u32 features; | ||
71 | __u8 index; | ||
72 | } __packed; | ||
73 | |||
74 | struct virtio_ccw_vq_info { | ||
75 | struct virtqueue *vq; | ||
76 | int num; | ||
77 | void *queue; | ||
78 | struct vq_info_block *info_block; | ||
79 | struct list_head node; | ||
80 | }; | ||
81 | |||
82 | #define KVM_VIRTIO_CCW_RING_ALIGN 4096 | ||
83 | |||
84 | #define KVM_S390_VIRTIO_CCW_NOTIFY 3 | ||
85 | |||
86 | #define CCW_CMD_SET_VQ 0x13 | ||
87 | #define CCW_CMD_VDEV_RESET 0x33 | ||
88 | #define CCW_CMD_SET_IND 0x43 | ||
89 | #define CCW_CMD_SET_CONF_IND 0x53 | ||
90 | #define CCW_CMD_READ_FEAT 0x12 | ||
91 | #define CCW_CMD_WRITE_FEAT 0x11 | ||
92 | #define CCW_CMD_READ_CONF 0x22 | ||
93 | #define CCW_CMD_WRITE_CONF 0x21 | ||
94 | #define CCW_CMD_WRITE_STATUS 0x31 | ||
95 | #define CCW_CMD_READ_VQ_CONF 0x32 | ||
96 | |||
97 | #define VIRTIO_CCW_DOING_SET_VQ 0x00010000 | ||
98 | #define VIRTIO_CCW_DOING_RESET 0x00040000 | ||
99 | #define VIRTIO_CCW_DOING_READ_FEAT 0x00080000 | ||
100 | #define VIRTIO_CCW_DOING_WRITE_FEAT 0x00100000 | ||
101 | #define VIRTIO_CCW_DOING_READ_CONFIG 0x00200000 | ||
102 | #define VIRTIO_CCW_DOING_WRITE_CONFIG 0x00400000 | ||
103 | #define VIRTIO_CCW_DOING_WRITE_STATUS 0x00800000 | ||
104 | #define VIRTIO_CCW_DOING_SET_IND 0x01000000 | ||
105 | #define VIRTIO_CCW_DOING_READ_VQ_CONF 0x02000000 | ||
106 | #define VIRTIO_CCW_DOING_SET_CONF_IND 0x04000000 | ||
107 | #define VIRTIO_CCW_INTPARM_MASK 0xffff0000 | ||
108 | |||
109 | static struct virtio_ccw_device *to_vc_device(struct virtio_device *vdev) | ||
110 | { | ||
111 | return container_of(vdev, struct virtio_ccw_device, vdev); | ||
112 | } | ||
113 | |||
114 | static int doing_io(struct virtio_ccw_device *vcdev, __u32 flag) | ||
115 | { | ||
116 | unsigned long flags; | ||
117 | __u32 ret; | ||
118 | |||
119 | spin_lock_irqsave(get_ccwdev_lock(vcdev->cdev), flags); | ||
120 | if (vcdev->err) | ||
121 | ret = 0; | ||
122 | else | ||
123 | ret = vcdev->curr_io & flag; | ||
124 | spin_unlock_irqrestore(get_ccwdev_lock(vcdev->cdev), flags); | ||
125 | return ret; | ||
126 | } | ||
127 | |||
128 | static int ccw_io_helper(struct virtio_ccw_device *vcdev, | ||
129 | struct ccw1 *ccw, __u32 intparm) | ||
130 | { | ||
131 | int ret; | ||
132 | unsigned long flags; | ||
133 | int flag = intparm & VIRTIO_CCW_INTPARM_MASK; | ||
134 | |||
135 | do { | ||
136 | spin_lock_irqsave(get_ccwdev_lock(vcdev->cdev), flags); | ||
137 | ret = ccw_device_start(vcdev->cdev, ccw, intparm, 0, 0); | ||
138 | if (!ret) | ||
139 | vcdev->curr_io |= flag; | ||
140 | spin_unlock_irqrestore(get_ccwdev_lock(vcdev->cdev), flags); | ||
141 | cpu_relax(); | ||
142 | } while (ret == -EBUSY); | ||
143 | wait_event(vcdev->wait_q, doing_io(vcdev, flag) == 0); | ||
144 | return ret ? ret : vcdev->err; | ||
145 | } | ||
146 | |||
147 | static inline long do_kvm_notify(struct subchannel_id schid, | ||
148 | unsigned long queue_index) | ||
149 | { | ||
150 | register unsigned long __nr asm("1") = KVM_S390_VIRTIO_CCW_NOTIFY; | ||
151 | register struct subchannel_id __schid asm("2") = schid; | ||
152 | register unsigned long __index asm("3") = queue_index; | ||
153 | register long __rc asm("2"); | ||
154 | |||
155 | asm volatile ("diag 2,4,0x500\n" | ||
156 | : "=d" (__rc) : "d" (__nr), "d" (__schid), "d" (__index) | ||
157 | : "memory", "cc"); | ||
158 | return __rc; | ||
159 | } | ||
160 | |||
161 | static void virtio_ccw_kvm_notify(struct virtqueue *vq) | ||
162 | { | ||
163 | struct virtio_ccw_vq_info *info = vq->priv; | ||
164 | struct virtio_ccw_device *vcdev; | ||
165 | struct subchannel_id schid; | ||
166 | |||
167 | vcdev = to_vc_device(info->vq->vdev); | ||
168 | ccw_device_get_schid(vcdev->cdev, &schid); | ||
169 | do_kvm_notify(schid, virtqueue_get_queue_index(vq)); | ||
170 | } | ||
171 | |||
172 | static int virtio_ccw_read_vq_conf(struct virtio_ccw_device *vcdev, | ||
173 | struct ccw1 *ccw, int index) | ||
174 | { | ||
175 | vcdev->config_block->index = index; | ||
176 | ccw->cmd_code = CCW_CMD_READ_VQ_CONF; | ||
177 | ccw->flags = 0; | ||
178 | ccw->count = sizeof(struct vq_config_block); | ||
179 | ccw->cda = (__u32)(unsigned long)(vcdev->config_block); | ||
180 | ccw_io_helper(vcdev, ccw, VIRTIO_CCW_DOING_READ_VQ_CONF); | ||
181 | return vcdev->config_block->num; | ||
182 | } | ||
183 | |||
184 | static void virtio_ccw_del_vq(struct virtqueue *vq, struct ccw1 *ccw) | ||
185 | { | ||
186 | struct virtio_ccw_device *vcdev = to_vc_device(vq->vdev); | ||
187 | struct virtio_ccw_vq_info *info = vq->priv; | ||
188 | unsigned long flags; | ||
189 | unsigned long size; | ||
190 | int ret; | ||
191 | unsigned int index = virtqueue_get_queue_index(vq); | ||
192 | |||
193 | /* Remove from our list. */ | ||
194 | spin_lock_irqsave(&vcdev->lock, flags); | ||
195 | list_del(&info->node); | ||
196 | spin_unlock_irqrestore(&vcdev->lock, flags); | ||
197 | |||
198 | /* Release from host. */ | ||
199 | info->info_block->queue = 0; | ||
200 | info->info_block->align = 0; | ||
201 | info->info_block->index = index; | ||
202 | info->info_block->num = 0; | ||
203 | ccw->cmd_code = CCW_CMD_SET_VQ; | ||
204 | ccw->flags = 0; | ||
205 | ccw->count = sizeof(*info->info_block); | ||
206 | ccw->cda = (__u32)(unsigned long)(info->info_block); | ||
207 | ret = ccw_io_helper(vcdev, ccw, | ||
208 | VIRTIO_CCW_DOING_SET_VQ | index); | ||
209 | /* | ||
210 | * -ENODEV isn't considered an error: The device is gone anyway. | ||
211 | * This may happen on device detach. | ||
212 | */ | ||
213 | if (ret && (ret != -ENODEV)) | ||
214 | dev_warn(&vq->vdev->dev, "Error %d while deleting queue %d", | ||
215 | ret, index); | ||
216 | |||
217 | vring_del_virtqueue(vq); | ||
218 | size = PAGE_ALIGN(vring_size(info->num, KVM_VIRTIO_CCW_RING_ALIGN)); | ||
219 | free_pages_exact(info->queue, size); | ||
220 | kfree(info->info_block); | ||
221 | kfree(info); | ||
222 | } | ||
223 | |||
224 | static void virtio_ccw_del_vqs(struct virtio_device *vdev) | ||
225 | { | ||
226 | struct virtqueue *vq, *n; | ||
227 | struct ccw1 *ccw; | ||
228 | |||
229 | ccw = kzalloc(sizeof(*ccw), GFP_DMA | GFP_KERNEL); | ||
230 | if (!ccw) | ||
231 | return; | ||
232 | |||
233 | |||
234 | list_for_each_entry_safe(vq, n, &vdev->vqs, list) | ||
235 | virtio_ccw_del_vq(vq, ccw); | ||
236 | |||
237 | kfree(ccw); | ||
238 | } | ||
239 | |||
240 | static struct virtqueue *virtio_ccw_setup_vq(struct virtio_device *vdev, | ||
241 | int i, vq_callback_t *callback, | ||
242 | const char *name, | ||
243 | struct ccw1 *ccw) | ||
244 | { | ||
245 | struct virtio_ccw_device *vcdev = to_vc_device(vdev); | ||
246 | int err; | ||
247 | struct virtqueue *vq = NULL; | ||
248 | struct virtio_ccw_vq_info *info; | ||
249 | unsigned long size = 0; /* silence the compiler */ | ||
250 | unsigned long flags; | ||
251 | |||
252 | /* Allocate queue. */ | ||
253 | info = kzalloc(sizeof(struct virtio_ccw_vq_info), GFP_KERNEL); | ||
254 | if (!info) { | ||
255 | dev_warn(&vcdev->cdev->dev, "no info\n"); | ||
256 | err = -ENOMEM; | ||
257 | goto out_err; | ||
258 | } | ||
259 | info->info_block = kzalloc(sizeof(*info->info_block), | ||
260 | GFP_DMA | GFP_KERNEL); | ||
261 | if (!info->info_block) { | ||
262 | dev_warn(&vcdev->cdev->dev, "no info block\n"); | ||
263 | err = -ENOMEM; | ||
264 | goto out_err; | ||
265 | } | ||
266 | info->num = virtio_ccw_read_vq_conf(vcdev, ccw, i); | ||
267 | size = PAGE_ALIGN(vring_size(info->num, KVM_VIRTIO_CCW_RING_ALIGN)); | ||
268 | info->queue = alloc_pages_exact(size, GFP_KERNEL | __GFP_ZERO); | ||
269 | if (info->queue == NULL) { | ||
270 | dev_warn(&vcdev->cdev->dev, "no queue\n"); | ||
271 | err = -ENOMEM; | ||
272 | goto out_err; | ||
273 | } | ||
274 | |||
275 | vq = vring_new_virtqueue(i, info->num, KVM_VIRTIO_CCW_RING_ALIGN, vdev, | ||
276 | true, info->queue, virtio_ccw_kvm_notify, | ||
277 | callback, name); | ||
278 | if (!vq) { | ||
279 | /* For now, we fail if we can't get the requested size. */ | ||
280 | dev_warn(&vcdev->cdev->dev, "no vq\n"); | ||
281 | err = -ENOMEM; | ||
282 | goto out_err; | ||
283 | } | ||
284 | |||
285 | /* Register it with the host. */ | ||
286 | info->info_block->queue = (__u64)info->queue; | ||
287 | info->info_block->align = KVM_VIRTIO_CCW_RING_ALIGN; | ||
288 | info->info_block->index = i; | ||
289 | info->info_block->num = info->num; | ||
290 | ccw->cmd_code = CCW_CMD_SET_VQ; | ||
291 | ccw->flags = 0; | ||
292 | ccw->count = sizeof(*info->info_block); | ||
293 | ccw->cda = (__u32)(unsigned long)(info->info_block); | ||
294 | err = ccw_io_helper(vcdev, ccw, VIRTIO_CCW_DOING_SET_VQ | i); | ||
295 | if (err) { | ||
296 | dev_warn(&vcdev->cdev->dev, "SET_VQ failed\n"); | ||
297 | goto out_err; | ||
298 | } | ||
299 | |||
300 | info->vq = vq; | ||
301 | vq->priv = info; | ||
302 | |||
303 | /* Save it to our list. */ | ||
304 | spin_lock_irqsave(&vcdev->lock, flags); | ||
305 | list_add(&info->node, &vcdev->virtqueues); | ||
306 | spin_unlock_irqrestore(&vcdev->lock, flags); | ||
307 | |||
308 | return vq; | ||
309 | |||
310 | out_err: | ||
311 | if (vq) | ||
312 | vring_del_virtqueue(vq); | ||
313 | if (info) { | ||
314 | if (info->queue) | ||
315 | free_pages_exact(info->queue, size); | ||
316 | kfree(info->info_block); | ||
317 | } | ||
318 | kfree(info); | ||
319 | return ERR_PTR(err); | ||
320 | } | ||
321 | |||
322 | static int virtio_ccw_find_vqs(struct virtio_device *vdev, unsigned nvqs, | ||
323 | struct virtqueue *vqs[], | ||
324 | vq_callback_t *callbacks[], | ||
325 | const char *names[]) | ||
326 | { | ||
327 | struct virtio_ccw_device *vcdev = to_vc_device(vdev); | ||
328 | unsigned long *indicatorp = NULL; | ||
329 | int ret, i; | ||
330 | struct ccw1 *ccw; | ||
331 | |||
332 | ccw = kzalloc(sizeof(*ccw), GFP_DMA | GFP_KERNEL); | ||
333 | if (!ccw) | ||
334 | return -ENOMEM; | ||
335 | |||
336 | for (i = 0; i < nvqs; ++i) { | ||
337 | vqs[i] = virtio_ccw_setup_vq(vdev, i, callbacks[i], names[i], | ||
338 | ccw); | ||
339 | if (IS_ERR(vqs[i])) { | ||
340 | ret = PTR_ERR(vqs[i]); | ||
341 | vqs[i] = NULL; | ||
342 | goto out; | ||
343 | } | ||
344 | } | ||
345 | ret = -ENOMEM; | ||
346 | /* We need a data area under 2G to communicate. */ | ||
347 | indicatorp = kmalloc(sizeof(&vcdev->indicators), GFP_DMA | GFP_KERNEL); | ||
348 | if (!indicatorp) | ||
349 | goto out; | ||
350 | *indicatorp = (unsigned long) &vcdev->indicators; | ||
351 | /* Register queue indicators with host. */ | ||
352 | vcdev->indicators = 0; | ||
353 | ccw->cmd_code = CCW_CMD_SET_IND; | ||
354 | ccw->flags = 0; | ||
355 | ccw->count = sizeof(vcdev->indicators); | ||
356 | ccw->cda = (__u32)(unsigned long) indicatorp; | ||
357 | ret = ccw_io_helper(vcdev, ccw, VIRTIO_CCW_DOING_SET_IND); | ||
358 | if (ret) | ||
359 | goto out; | ||
360 | /* Register indicators2 with host for config changes */ | ||
361 | *indicatorp = (unsigned long) &vcdev->indicators2; | ||
362 | vcdev->indicators2 = 0; | ||
363 | ccw->cmd_code = CCW_CMD_SET_CONF_IND; | ||
364 | ccw->flags = 0; | ||
365 | ccw->count = sizeof(vcdev->indicators2); | ||
366 | ccw->cda = (__u32)(unsigned long) indicatorp; | ||
367 | ret = ccw_io_helper(vcdev, ccw, VIRTIO_CCW_DOING_SET_CONF_IND); | ||
368 | if (ret) | ||
369 | goto out; | ||
370 | |||
371 | kfree(indicatorp); | ||
372 | kfree(ccw); | ||
373 | return 0; | ||
374 | out: | ||
375 | kfree(indicatorp); | ||
376 | kfree(ccw); | ||
377 | virtio_ccw_del_vqs(vdev); | ||
378 | return ret; | ||
379 | } | ||
380 | |||
381 | static void virtio_ccw_reset(struct virtio_device *vdev) | ||
382 | { | ||
383 | struct virtio_ccw_device *vcdev = to_vc_device(vdev); | ||
384 | struct ccw1 *ccw; | ||
385 | |||
386 | ccw = kzalloc(sizeof(*ccw), GFP_DMA | GFP_KERNEL); | ||
387 | if (!ccw) | ||
388 | return; | ||
389 | |||
390 | /* Zero status bits. */ | ||
391 | *vcdev->status = 0; | ||
392 | |||
393 | /* Send a reset ccw on device. */ | ||
394 | ccw->cmd_code = CCW_CMD_VDEV_RESET; | ||
395 | ccw->flags = 0; | ||
396 | ccw->count = 0; | ||
397 | ccw->cda = 0; | ||
398 | ccw_io_helper(vcdev, ccw, VIRTIO_CCW_DOING_RESET); | ||
399 | kfree(ccw); | ||
400 | } | ||
401 | |||
402 | static u32 virtio_ccw_get_features(struct virtio_device *vdev) | ||
403 | { | ||
404 | struct virtio_ccw_device *vcdev = to_vc_device(vdev); | ||
405 | struct virtio_feature_desc *features; | ||
406 | int ret, rc; | ||
407 | struct ccw1 *ccw; | ||
408 | |||
409 | ccw = kzalloc(sizeof(*ccw), GFP_DMA | GFP_KERNEL); | ||
410 | if (!ccw) | ||
411 | return 0; | ||
412 | |||
413 | features = kzalloc(sizeof(*features), GFP_DMA | GFP_KERNEL); | ||
414 | if (!features) { | ||
415 | rc = 0; | ||
416 | goto out_free; | ||
417 | } | ||
418 | /* Read the feature bits from the host. */ | ||
419 | /* TODO: Features > 32 bits */ | ||
420 | features->index = 0; | ||
421 | ccw->cmd_code = CCW_CMD_READ_FEAT; | ||
422 | ccw->flags = 0; | ||
423 | ccw->count = sizeof(*features); | ||
424 | ccw->cda = (__u32)(unsigned long)features; | ||
425 | ret = ccw_io_helper(vcdev, ccw, VIRTIO_CCW_DOING_READ_FEAT); | ||
426 | if (ret) { | ||
427 | rc = 0; | ||
428 | goto out_free; | ||
429 | } | ||
430 | |||
431 | rc = le32_to_cpu(features->features); | ||
432 | |||
433 | out_free: | ||
434 | kfree(features); | ||
435 | kfree(ccw); | ||
436 | return rc; | ||
437 | } | ||
438 | |||
439 | static void virtio_ccw_finalize_features(struct virtio_device *vdev) | ||
440 | { | ||
441 | struct virtio_ccw_device *vcdev = to_vc_device(vdev); | ||
442 | struct virtio_feature_desc *features; | ||
443 | int i; | ||
444 | struct ccw1 *ccw; | ||
445 | |||
446 | ccw = kzalloc(sizeof(*ccw), GFP_DMA | GFP_KERNEL); | ||
447 | if (!ccw) | ||
448 | return; | ||
449 | |||
450 | features = kzalloc(sizeof(*features), GFP_DMA | GFP_KERNEL); | ||
451 | if (!features) | ||
452 | goto out_free; | ||
453 | |||
454 | /* Give virtio_ring a chance to accept features. */ | ||
455 | vring_transport_features(vdev); | ||
456 | |||
457 | for (i = 0; i < sizeof(*vdev->features) / sizeof(features->features); | ||
458 | i++) { | ||
459 | int highbits = i % 2 ? 32 : 0; | ||
460 | features->index = i; | ||
461 | features->features = cpu_to_le32(vdev->features[i / 2] | ||
462 | >> highbits); | ||
463 | /* Write the feature bits to the host. */ | ||
464 | ccw->cmd_code = CCW_CMD_WRITE_FEAT; | ||
465 | ccw->flags = 0; | ||
466 | ccw->count = sizeof(*features); | ||
467 | ccw->cda = (__u32)(unsigned long)features; | ||
468 | ccw_io_helper(vcdev, ccw, VIRTIO_CCW_DOING_WRITE_FEAT); | ||
469 | } | ||
470 | out_free: | ||
471 | kfree(features); | ||
472 | kfree(ccw); | ||
473 | } | ||
474 | |||
475 | static void virtio_ccw_get_config(struct virtio_device *vdev, | ||
476 | unsigned int offset, void *buf, unsigned len) | ||
477 | { | ||
478 | struct virtio_ccw_device *vcdev = to_vc_device(vdev); | ||
479 | int ret; | ||
480 | struct ccw1 *ccw; | ||
481 | void *config_area; | ||
482 | |||
483 | ccw = kzalloc(sizeof(*ccw), GFP_DMA | GFP_KERNEL); | ||
484 | if (!ccw) | ||
485 | return; | ||
486 | |||
487 | config_area = kzalloc(VIRTIO_CCW_CONFIG_SIZE, GFP_DMA | GFP_KERNEL); | ||
488 | if (!config_area) | ||
489 | goto out_free; | ||
490 | |||
491 | /* Read the config area from the host. */ | ||
492 | ccw->cmd_code = CCW_CMD_READ_CONF; | ||
493 | ccw->flags = 0; | ||
494 | ccw->count = offset + len; | ||
495 | ccw->cda = (__u32)(unsigned long)config_area; | ||
496 | ret = ccw_io_helper(vcdev, ccw, VIRTIO_CCW_DOING_READ_CONFIG); | ||
497 | if (ret) | ||
498 | goto out_free; | ||
499 | |||
500 | memcpy(vcdev->config, config_area, sizeof(vcdev->config)); | ||
501 | memcpy(buf, &vcdev->config[offset], len); | ||
502 | |||
503 | out_free: | ||
504 | kfree(config_area); | ||
505 | kfree(ccw); | ||
506 | } | ||
507 | |||
508 | static void virtio_ccw_set_config(struct virtio_device *vdev, | ||
509 | unsigned int offset, const void *buf, | ||
510 | unsigned len) | ||
511 | { | ||
512 | struct virtio_ccw_device *vcdev = to_vc_device(vdev); | ||
513 | struct ccw1 *ccw; | ||
514 | void *config_area; | ||
515 | |||
516 | ccw = kzalloc(sizeof(*ccw), GFP_DMA | GFP_KERNEL); | ||
517 | if (!ccw) | ||
518 | return; | ||
519 | |||
520 | config_area = kzalloc(VIRTIO_CCW_CONFIG_SIZE, GFP_DMA | GFP_KERNEL); | ||
521 | if (!config_area) | ||
522 | goto out_free; | ||
523 | |||
524 | memcpy(&vcdev->config[offset], buf, len); | ||
525 | /* Write the config area to the host. */ | ||
526 | memcpy(config_area, vcdev->config, sizeof(vcdev->config)); | ||
527 | ccw->cmd_code = CCW_CMD_WRITE_CONF; | ||
528 | ccw->flags = 0; | ||
529 | ccw->count = offset + len; | ||
530 | ccw->cda = (__u32)(unsigned long)config_area; | ||
531 | ccw_io_helper(vcdev, ccw, VIRTIO_CCW_DOING_WRITE_CONFIG); | ||
532 | |||
533 | out_free: | ||
534 | kfree(config_area); | ||
535 | kfree(ccw); | ||
536 | } | ||
537 | |||
538 | static u8 virtio_ccw_get_status(struct virtio_device *vdev) | ||
539 | { | ||
540 | struct virtio_ccw_device *vcdev = to_vc_device(vdev); | ||
541 | |||
542 | return *vcdev->status; | ||
543 | } | ||
544 | |||
545 | static void virtio_ccw_set_status(struct virtio_device *vdev, u8 status) | ||
546 | { | ||
547 | struct virtio_ccw_device *vcdev = to_vc_device(vdev); | ||
548 | struct ccw1 *ccw; | ||
549 | |||
550 | ccw = kzalloc(sizeof(*ccw), GFP_DMA | GFP_KERNEL); | ||
551 | if (!ccw) | ||
552 | return; | ||
553 | |||
554 | /* Write the status to the host. */ | ||
555 | *vcdev->status = status; | ||
556 | ccw->cmd_code = CCW_CMD_WRITE_STATUS; | ||
557 | ccw->flags = 0; | ||
558 | ccw->count = sizeof(status); | ||
559 | ccw->cda = (__u32)(unsigned long)vcdev->status; | ||
560 | ccw_io_helper(vcdev, ccw, VIRTIO_CCW_DOING_WRITE_STATUS); | ||
561 | kfree(ccw); | ||
562 | } | ||
563 | |||
564 | static struct virtio_config_ops virtio_ccw_config_ops = { | ||
565 | .get_features = virtio_ccw_get_features, | ||
566 | .finalize_features = virtio_ccw_finalize_features, | ||
567 | .get = virtio_ccw_get_config, | ||
568 | .set = virtio_ccw_set_config, | ||
569 | .get_status = virtio_ccw_get_status, | ||
570 | .set_status = virtio_ccw_set_status, | ||
571 | .reset = virtio_ccw_reset, | ||
572 | .find_vqs = virtio_ccw_find_vqs, | ||
573 | .del_vqs = virtio_ccw_del_vqs, | ||
574 | }; | ||
575 | |||
576 | |||
577 | /* | ||
578 | * ccw bus driver related functions | ||
579 | */ | ||
580 | |||
581 | static void virtio_ccw_release_dev(struct device *_d) | ||
582 | { | ||
583 | struct virtio_device *dev = container_of(_d, struct virtio_device, | ||
584 | dev); | ||
585 | struct virtio_ccw_device *vcdev = to_vc_device(dev); | ||
586 | |||
587 | kfree(vcdev->status); | ||
588 | kfree(vcdev->config_block); | ||
589 | kfree(vcdev); | ||
590 | } | ||
591 | |||
592 | static int irb_is_error(struct irb *irb) | ||
593 | { | ||
594 | if (scsw_cstat(&irb->scsw) != 0) | ||
595 | return 1; | ||
596 | if (scsw_dstat(&irb->scsw) & ~(DEV_STAT_CHN_END | DEV_STAT_DEV_END)) | ||
597 | return 1; | ||
598 | if (scsw_cc(&irb->scsw) != 0) | ||
599 | return 1; | ||
600 | return 0; | ||
601 | } | ||
602 | |||
603 | static struct virtqueue *virtio_ccw_vq_by_ind(struct virtio_ccw_device *vcdev, | ||
604 | int index) | ||
605 | { | ||
606 | struct virtio_ccw_vq_info *info; | ||
607 | unsigned long flags; | ||
608 | struct virtqueue *vq; | ||
609 | |||
610 | vq = NULL; | ||
611 | spin_lock_irqsave(&vcdev->lock, flags); | ||
612 | list_for_each_entry(info, &vcdev->virtqueues, node) { | ||
613 | if (virtqueue_get_queue_index(info->vq) == index) { | ||
614 | vq = info->vq; | ||
615 | break; | ||
616 | } | ||
617 | } | ||
618 | spin_unlock_irqrestore(&vcdev->lock, flags); | ||
619 | return vq; | ||
620 | } | ||
621 | |||
622 | static void virtio_ccw_int_handler(struct ccw_device *cdev, | ||
623 | unsigned long intparm, | ||
624 | struct irb *irb) | ||
625 | { | ||
626 | __u32 activity = intparm & VIRTIO_CCW_INTPARM_MASK; | ||
627 | struct virtio_ccw_device *vcdev = dev_get_drvdata(&cdev->dev); | ||
628 | int i; | ||
629 | struct virtqueue *vq; | ||
630 | struct virtio_driver *drv; | ||
631 | |||
632 | /* Check if it's a notification from the host. */ | ||
633 | if ((intparm == 0) && | ||
634 | (scsw_stctl(&irb->scsw) == | ||
635 | (SCSW_STCTL_ALERT_STATUS | SCSW_STCTL_STATUS_PEND))) { | ||
636 | /* OK */ | ||
637 | } | ||
638 | if (irb_is_error(irb)) | ||
639 | vcdev->err = -EIO; /* XXX - use real error */ | ||
640 | if (vcdev->curr_io & activity) { | ||
641 | switch (activity) { | ||
642 | case VIRTIO_CCW_DOING_READ_FEAT: | ||
643 | case VIRTIO_CCW_DOING_WRITE_FEAT: | ||
644 | case VIRTIO_CCW_DOING_READ_CONFIG: | ||
645 | case VIRTIO_CCW_DOING_WRITE_CONFIG: | ||
646 | case VIRTIO_CCW_DOING_WRITE_STATUS: | ||
647 | case VIRTIO_CCW_DOING_SET_VQ: | ||
648 | case VIRTIO_CCW_DOING_SET_IND: | ||
649 | case VIRTIO_CCW_DOING_SET_CONF_IND: | ||
650 | case VIRTIO_CCW_DOING_RESET: | ||
651 | case VIRTIO_CCW_DOING_READ_VQ_CONF: | ||
652 | vcdev->curr_io &= ~activity; | ||
653 | wake_up(&vcdev->wait_q); | ||
654 | break; | ||
655 | default: | ||
656 | /* don't know what to do... */ | ||
657 | dev_warn(&cdev->dev, "Suspicious activity '%08x'\n", | ||
658 | activity); | ||
659 | WARN_ON(1); | ||
660 | break; | ||
661 | } | ||
662 | } | ||
663 | for_each_set_bit(i, &vcdev->indicators, | ||
664 | sizeof(vcdev->indicators) * BITS_PER_BYTE) { | ||
665 | /* The bit clear must happen before the vring kick. */ | ||
666 | clear_bit(i, &vcdev->indicators); | ||
667 | barrier(); | ||
668 | vq = virtio_ccw_vq_by_ind(vcdev, i); | ||
669 | vring_interrupt(0, vq); | ||
670 | } | ||
671 | if (test_bit(0, &vcdev->indicators2)) { | ||
672 | drv = container_of(vcdev->vdev.dev.driver, | ||
673 | struct virtio_driver, driver); | ||
674 | |||
675 | if (drv && drv->config_changed) | ||
676 | drv->config_changed(&vcdev->vdev); | ||
677 | clear_bit(0, &vcdev->indicators2); | ||
678 | } | ||
679 | } | ||
680 | |||
681 | /* | ||
682 | * We usually want to autoonline all devices, but give the admin | ||
683 | * a way to exempt devices from this. | ||
684 | */ | ||
685 | #define __DEV_WORDS ((__MAX_SUBCHANNEL + (8*sizeof(long) - 1)) / \ | ||
686 | (8*sizeof(long))) | ||
687 | static unsigned long devs_no_auto[__MAX_SSID + 1][__DEV_WORDS]; | ||
688 | |||
689 | static char *no_auto = ""; | ||
690 | |||
691 | module_param(no_auto, charp, 0444); | ||
692 | MODULE_PARM_DESC(no_auto, "list of ccw bus id ranges not to be auto-onlined"); | ||
693 | |||
694 | static int virtio_ccw_check_autoonline(struct ccw_device *cdev) | ||
695 | { | ||
696 | struct ccw_dev_id id; | ||
697 | |||
698 | ccw_device_get_id(cdev, &id); | ||
699 | if (test_bit(id.devno, devs_no_auto[id.ssid])) | ||
700 | return 0; | ||
701 | return 1; | ||
702 | } | ||
703 | |||
704 | static void virtio_ccw_auto_online(void *data, async_cookie_t cookie) | ||
705 | { | ||
706 | struct ccw_device *cdev = data; | ||
707 | int ret; | ||
708 | |||
709 | ret = ccw_device_set_online(cdev); | ||
710 | if (ret) | ||
711 | dev_warn(&cdev->dev, "Failed to set online: %d\n", ret); | ||
712 | } | ||
713 | |||
714 | static int virtio_ccw_probe(struct ccw_device *cdev) | ||
715 | { | ||
716 | cdev->handler = virtio_ccw_int_handler; | ||
717 | |||
718 | if (virtio_ccw_check_autoonline(cdev)) | ||
719 | async_schedule(virtio_ccw_auto_online, cdev); | ||
720 | return 0; | ||
721 | } | ||
722 | |||
723 | static void virtio_ccw_remove(struct ccw_device *cdev) | ||
724 | { | ||
725 | struct virtio_ccw_device *vcdev = dev_get_drvdata(&cdev->dev); | ||
726 | |||
727 | if (cdev->online) { | ||
728 | unregister_virtio_device(&vcdev->vdev); | ||
729 | dev_set_drvdata(&cdev->dev, NULL); | ||
730 | } | ||
731 | cdev->handler = NULL; | ||
732 | } | ||
733 | |||
734 | static int virtio_ccw_offline(struct ccw_device *cdev) | ||
735 | { | ||
736 | struct virtio_ccw_device *vcdev = dev_get_drvdata(&cdev->dev); | ||
737 | |||
738 | unregister_virtio_device(&vcdev->vdev); | ||
739 | dev_set_drvdata(&cdev->dev, NULL); | ||
740 | return 0; | ||
741 | } | ||
742 | |||
743 | |||
744 | static int virtio_ccw_online(struct ccw_device *cdev) | ||
745 | { | ||
746 | int ret; | ||
747 | struct virtio_ccw_device *vcdev; | ||
748 | |||
749 | vcdev = kzalloc(sizeof(*vcdev), GFP_KERNEL); | ||
750 | if (!vcdev) { | ||
751 | dev_warn(&cdev->dev, "Could not get memory for virtio\n"); | ||
752 | ret = -ENOMEM; | ||
753 | goto out_free; | ||
754 | } | ||
755 | vcdev->config_block = kzalloc(sizeof(*vcdev->config_block), | ||
756 | GFP_DMA | GFP_KERNEL); | ||
757 | if (!vcdev->config_block) { | ||
758 | ret = -ENOMEM; | ||
759 | goto out_free; | ||
760 | } | ||
761 | vcdev->status = kzalloc(sizeof(*vcdev->status), GFP_DMA | GFP_KERNEL); | ||
762 | if (!vcdev->status) { | ||
763 | ret = -ENOMEM; | ||
764 | goto out_free; | ||
765 | } | ||
766 | |||
767 | vcdev->vdev.dev.parent = &cdev->dev; | ||
768 | vcdev->vdev.dev.release = virtio_ccw_release_dev; | ||
769 | vcdev->vdev.config = &virtio_ccw_config_ops; | ||
770 | vcdev->cdev = cdev; | ||
771 | init_waitqueue_head(&vcdev->wait_q); | ||
772 | INIT_LIST_HEAD(&vcdev->virtqueues); | ||
773 | spin_lock_init(&vcdev->lock); | ||
774 | |||
775 | dev_set_drvdata(&cdev->dev, vcdev); | ||
776 | vcdev->vdev.id.vendor = cdev->id.cu_type; | ||
777 | vcdev->vdev.id.device = cdev->id.cu_model; | ||
778 | ret = register_virtio_device(&vcdev->vdev); | ||
779 | if (ret) { | ||
780 | dev_warn(&cdev->dev, "Failed to register virtio device: %d\n", | ||
781 | ret); | ||
782 | goto out_put; | ||
783 | } | ||
784 | return 0; | ||
785 | out_put: | ||
786 | dev_set_drvdata(&cdev->dev, NULL); | ||
787 | put_device(&vcdev->vdev.dev); | ||
788 | return ret; | ||
789 | out_free: | ||
790 | if (vcdev) { | ||
791 | kfree(vcdev->status); | ||
792 | kfree(vcdev->config_block); | ||
793 | } | ||
794 | kfree(vcdev); | ||
795 | return ret; | ||
796 | } | ||
797 | |||
798 | static int virtio_ccw_cio_notify(struct ccw_device *cdev, int event) | ||
799 | { | ||
800 | /* TODO: Check whether we need special handling here. */ | ||
801 | return 0; | ||
802 | } | ||
803 | |||
804 | static struct ccw_device_id virtio_ids[] = { | ||
805 | { CCW_DEVICE(0x3832, 0) }, | ||
806 | {}, | ||
807 | }; | ||
808 | MODULE_DEVICE_TABLE(ccw, virtio_ids); | ||
809 | |||
810 | static struct ccw_driver virtio_ccw_driver = { | ||
811 | .driver = { | ||
812 | .owner = THIS_MODULE, | ||
813 | .name = "virtio_ccw", | ||
814 | }, | ||
815 | .ids = virtio_ids, | ||
816 | .probe = virtio_ccw_probe, | ||
817 | .remove = virtio_ccw_remove, | ||
818 | .set_offline = virtio_ccw_offline, | ||
819 | .set_online = virtio_ccw_online, | ||
820 | .notify = virtio_ccw_cio_notify, | ||
821 | .int_class = IRQIO_VIR, | ||
822 | }; | ||
823 | |||
824 | static int __init pure_hex(char **cp, unsigned int *val, int min_digit, | ||
825 | int max_digit, int max_val) | ||
826 | { | ||
827 | int diff; | ||
828 | |||
829 | diff = 0; | ||
830 | *val = 0; | ||
831 | |||
832 | while (diff <= max_digit) { | ||
833 | int value = hex_to_bin(**cp); | ||
834 | |||
835 | if (value < 0) | ||
836 | break; | ||
837 | *val = *val * 16 + value; | ||
838 | (*cp)++; | ||
839 | diff++; | ||
840 | } | ||
841 | |||
842 | if ((diff < min_digit) || (diff > max_digit) || (*val > max_val)) | ||
843 | return 1; | ||
844 | |||
845 | return 0; | ||
846 | } | ||
847 | |||
848 | static int __init parse_busid(char *str, unsigned int *cssid, | ||
849 | unsigned int *ssid, unsigned int *devno) | ||
850 | { | ||
851 | char *str_work; | ||
852 | int rc, ret; | ||
853 | |||
854 | rc = 1; | ||
855 | |||
856 | if (*str == '\0') | ||
857 | goto out; | ||
858 | |||
859 | str_work = str; | ||
860 | ret = pure_hex(&str_work, cssid, 1, 2, __MAX_CSSID); | ||
861 | if (ret || (str_work[0] != '.')) | ||
862 | goto out; | ||
863 | str_work++; | ||
864 | ret = pure_hex(&str_work, ssid, 1, 1, __MAX_SSID); | ||
865 | if (ret || (str_work[0] != '.')) | ||
866 | goto out; | ||
867 | str_work++; | ||
868 | ret = pure_hex(&str_work, devno, 4, 4, __MAX_SUBCHANNEL); | ||
869 | if (ret || (str_work[0] != '\0')) | ||
870 | goto out; | ||
871 | |||
872 | rc = 0; | ||
873 | out: | ||
874 | return rc; | ||
875 | } | ||
876 | |||
877 | static void __init no_auto_parse(void) | ||
878 | { | ||
879 | unsigned int from_cssid, to_cssid, from_ssid, to_ssid, from, to; | ||
880 | char *parm, *str; | ||
881 | int rc; | ||
882 | |||
883 | str = no_auto; | ||
884 | while ((parm = strsep(&str, ","))) { | ||
885 | rc = parse_busid(strsep(&parm, "-"), &from_cssid, | ||
886 | &from_ssid, &from); | ||
887 | if (rc) | ||
888 | continue; | ||
889 | if (parm != NULL) { | ||
890 | rc = parse_busid(parm, &to_cssid, | ||
891 | &to_ssid, &to); | ||
892 | if ((from_ssid > to_ssid) || | ||
893 | ((from_ssid == to_ssid) && (from > to))) | ||
894 | rc = -EINVAL; | ||
895 | } else { | ||
896 | to_cssid = from_cssid; | ||
897 | to_ssid = from_ssid; | ||
898 | to = from; | ||
899 | } | ||
900 | if (rc) | ||
901 | continue; | ||
902 | while ((from_ssid < to_ssid) || | ||
903 | ((from_ssid == to_ssid) && (from <= to))) { | ||
904 | set_bit(from, devs_no_auto[from_ssid]); | ||
905 | from++; | ||
906 | if (from > __MAX_SUBCHANNEL) { | ||
907 | from_ssid++; | ||
908 | from = 0; | ||
909 | } | ||
910 | } | ||
911 | } | ||
912 | } | ||
913 | |||
914 | static int __init virtio_ccw_init(void) | ||
915 | { | ||
916 | /* parse no_auto string before we do anything further */ | ||
917 | no_auto_parse(); | ||
918 | return ccw_driver_register(&virtio_ccw_driver); | ||
919 | } | ||
920 | module_init(virtio_ccw_init); | ||
921 | |||
922 | static void __exit virtio_ccw_exit(void) | ||
923 | { | ||
924 | ccw_driver_unregister(&virtio_ccw_driver); | ||
925 | } | ||
926 | module_exit(virtio_ccw_exit); | ||