diff options
Diffstat (limited to 'drivers/vhost/vsock.c')
-rw-r--r-- | drivers/vhost/vsock.c | 719 |
1 files changed, 719 insertions, 0 deletions
diff --git a/drivers/vhost/vsock.c b/drivers/vhost/vsock.c new file mode 100644 index 000000000000..0ddf3a2dbfc4 --- /dev/null +++ b/drivers/vhost/vsock.c | |||
@@ -0,0 +1,719 @@ | |||
1 | /* | ||
2 | * vhost transport for vsock | ||
3 | * | ||
4 | * Copyright (C) 2013-2015 Red Hat, Inc. | ||
5 | * Author: Asias He <asias@redhat.com> | ||
6 | * Stefan Hajnoczi <stefanha@redhat.com> | ||
7 | * | ||
8 | * This work is licensed under the terms of the GNU GPL, version 2. | ||
9 | */ | ||
10 | #include <linux/miscdevice.h> | ||
11 | #include <linux/atomic.h> | ||
12 | #include <linux/module.h> | ||
13 | #include <linux/mutex.h> | ||
14 | #include <linux/vmalloc.h> | ||
15 | #include <net/sock.h> | ||
16 | #include <linux/virtio_vsock.h> | ||
17 | #include <linux/vhost.h> | ||
18 | |||
19 | #include <net/af_vsock.h> | ||
20 | #include "vhost.h" | ||
21 | |||
22 | #define VHOST_VSOCK_DEFAULT_HOST_CID 2 | ||
23 | |||
24 | enum { | ||
25 | VHOST_VSOCK_FEATURES = VHOST_FEATURES, | ||
26 | }; | ||
27 | |||
28 | /* Used to track all the vhost_vsock instances on the system. */ | ||
29 | static DEFINE_SPINLOCK(vhost_vsock_lock); | ||
30 | static LIST_HEAD(vhost_vsock_list); | ||
31 | |||
32 | struct vhost_vsock { | ||
33 | struct vhost_dev dev; | ||
34 | struct vhost_virtqueue vqs[2]; | ||
35 | |||
36 | /* Link to global vhost_vsock_list, protected by vhost_vsock_lock */ | ||
37 | struct list_head list; | ||
38 | |||
39 | struct vhost_work send_pkt_work; | ||
40 | spinlock_t send_pkt_list_lock; | ||
41 | struct list_head send_pkt_list; /* host->guest pending packets */ | ||
42 | |||
43 | atomic_t queued_replies; | ||
44 | |||
45 | u32 guest_cid; | ||
46 | }; | ||
47 | |||
48 | static u32 vhost_transport_get_local_cid(void) | ||
49 | { | ||
50 | return VHOST_VSOCK_DEFAULT_HOST_CID; | ||
51 | } | ||
52 | |||
53 | static struct vhost_vsock *vhost_vsock_get(u32 guest_cid) | ||
54 | { | ||
55 | struct vhost_vsock *vsock; | ||
56 | |||
57 | spin_lock_bh(&vhost_vsock_lock); | ||
58 | list_for_each_entry(vsock, &vhost_vsock_list, list) { | ||
59 | u32 other_cid = vsock->guest_cid; | ||
60 | |||
61 | /* Skip instances that have no CID yet */ | ||
62 | if (other_cid == 0) | ||
63 | continue; | ||
64 | |||
65 | if (other_cid == guest_cid) { | ||
66 | spin_unlock_bh(&vhost_vsock_lock); | ||
67 | return vsock; | ||
68 | } | ||
69 | } | ||
70 | spin_unlock_bh(&vhost_vsock_lock); | ||
71 | |||
72 | return NULL; | ||
73 | } | ||
74 | |||
75 | static void | ||
76 | vhost_transport_do_send_pkt(struct vhost_vsock *vsock, | ||
77 | struct vhost_virtqueue *vq) | ||
78 | { | ||
79 | struct vhost_virtqueue *tx_vq = &vsock->vqs[VSOCK_VQ_TX]; | ||
80 | bool added = false; | ||
81 | bool restart_tx = false; | ||
82 | |||
83 | mutex_lock(&vq->mutex); | ||
84 | |||
85 | if (!vq->private_data) | ||
86 | goto out; | ||
87 | |||
88 | /* Avoid further vmexits, we're already processing the virtqueue */ | ||
89 | vhost_disable_notify(&vsock->dev, vq); | ||
90 | |||
91 | for (;;) { | ||
92 | struct virtio_vsock_pkt *pkt; | ||
93 | struct iov_iter iov_iter; | ||
94 | unsigned out, in; | ||
95 | size_t nbytes; | ||
96 | size_t len; | ||
97 | int head; | ||
98 | |||
99 | spin_lock_bh(&vsock->send_pkt_list_lock); | ||
100 | if (list_empty(&vsock->send_pkt_list)) { | ||
101 | spin_unlock_bh(&vsock->send_pkt_list_lock); | ||
102 | vhost_enable_notify(&vsock->dev, vq); | ||
103 | break; | ||
104 | } | ||
105 | |||
106 | pkt = list_first_entry(&vsock->send_pkt_list, | ||
107 | struct virtio_vsock_pkt, list); | ||
108 | list_del_init(&pkt->list); | ||
109 | spin_unlock_bh(&vsock->send_pkt_list_lock); | ||
110 | |||
111 | head = vhost_get_vq_desc(vq, vq->iov, ARRAY_SIZE(vq->iov), | ||
112 | &out, &in, NULL, NULL); | ||
113 | if (head < 0) { | ||
114 | spin_lock_bh(&vsock->send_pkt_list_lock); | ||
115 | list_add(&pkt->list, &vsock->send_pkt_list); | ||
116 | spin_unlock_bh(&vsock->send_pkt_list_lock); | ||
117 | break; | ||
118 | } | ||
119 | |||
120 | if (head == vq->num) { | ||
121 | spin_lock_bh(&vsock->send_pkt_list_lock); | ||
122 | list_add(&pkt->list, &vsock->send_pkt_list); | ||
123 | spin_unlock_bh(&vsock->send_pkt_list_lock); | ||
124 | |||
125 | /* We cannot finish yet if more buffers snuck in while | ||
126 | * re-enabling notify. | ||
127 | */ | ||
128 | if (unlikely(vhost_enable_notify(&vsock->dev, vq))) { | ||
129 | vhost_disable_notify(&vsock->dev, vq); | ||
130 | continue; | ||
131 | } | ||
132 | break; | ||
133 | } | ||
134 | |||
135 | if (out) { | ||
136 | virtio_transport_free_pkt(pkt); | ||
137 | vq_err(vq, "Expected 0 output buffers, got %u\n", out); | ||
138 | break; | ||
139 | } | ||
140 | |||
141 | len = iov_length(&vq->iov[out], in); | ||
142 | iov_iter_init(&iov_iter, READ, &vq->iov[out], in, len); | ||
143 | |||
144 | nbytes = copy_to_iter(&pkt->hdr, sizeof(pkt->hdr), &iov_iter); | ||
145 | if (nbytes != sizeof(pkt->hdr)) { | ||
146 | virtio_transport_free_pkt(pkt); | ||
147 | vq_err(vq, "Faulted on copying pkt hdr\n"); | ||
148 | break; | ||
149 | } | ||
150 | |||
151 | nbytes = copy_to_iter(pkt->buf, pkt->len, &iov_iter); | ||
152 | if (nbytes != pkt->len) { | ||
153 | virtio_transport_free_pkt(pkt); | ||
154 | vq_err(vq, "Faulted on copying pkt buf\n"); | ||
155 | break; | ||
156 | } | ||
157 | |||
158 | vhost_add_used(vq, head, sizeof(pkt->hdr) + pkt->len); | ||
159 | added = true; | ||
160 | |||
161 | if (pkt->reply) { | ||
162 | int val; | ||
163 | |||
164 | val = atomic_dec_return(&vsock->queued_replies); | ||
165 | |||
166 | /* Do we have resources to resume tx processing? */ | ||
167 | if (val + 1 == tx_vq->num) | ||
168 | restart_tx = true; | ||
169 | } | ||
170 | |||
171 | virtio_transport_free_pkt(pkt); | ||
172 | } | ||
173 | if (added) | ||
174 | vhost_signal(&vsock->dev, vq); | ||
175 | |||
176 | out: | ||
177 | mutex_unlock(&vq->mutex); | ||
178 | |||
179 | if (restart_tx) | ||
180 | vhost_poll_queue(&tx_vq->poll); | ||
181 | } | ||
182 | |||
183 | static void vhost_transport_send_pkt_work(struct vhost_work *work) | ||
184 | { | ||
185 | struct vhost_virtqueue *vq; | ||
186 | struct vhost_vsock *vsock; | ||
187 | |||
188 | vsock = container_of(work, struct vhost_vsock, send_pkt_work); | ||
189 | vq = &vsock->vqs[VSOCK_VQ_RX]; | ||
190 | |||
191 | vhost_transport_do_send_pkt(vsock, vq); | ||
192 | } | ||
193 | |||
194 | static int | ||
195 | vhost_transport_send_pkt(struct virtio_vsock_pkt *pkt) | ||
196 | { | ||
197 | struct vhost_vsock *vsock; | ||
198 | struct vhost_virtqueue *vq; | ||
199 | int len = pkt->len; | ||
200 | |||
201 | /* Find the vhost_vsock according to guest context id */ | ||
202 | vsock = vhost_vsock_get(le64_to_cpu(pkt->hdr.dst_cid)); | ||
203 | if (!vsock) { | ||
204 | virtio_transport_free_pkt(pkt); | ||
205 | return -ENODEV; | ||
206 | } | ||
207 | |||
208 | vq = &vsock->vqs[VSOCK_VQ_RX]; | ||
209 | |||
210 | if (pkt->reply) | ||
211 | atomic_inc(&vsock->queued_replies); | ||
212 | |||
213 | spin_lock_bh(&vsock->send_pkt_list_lock); | ||
214 | list_add_tail(&pkt->list, &vsock->send_pkt_list); | ||
215 | spin_unlock_bh(&vsock->send_pkt_list_lock); | ||
216 | |||
217 | vhost_work_queue(&vsock->dev, &vsock->send_pkt_work); | ||
218 | return len; | ||
219 | } | ||
220 | |||
221 | static struct virtio_vsock_pkt * | ||
222 | vhost_vsock_alloc_pkt(struct vhost_virtqueue *vq, | ||
223 | unsigned int out, unsigned int in) | ||
224 | { | ||
225 | struct virtio_vsock_pkt *pkt; | ||
226 | struct iov_iter iov_iter; | ||
227 | size_t nbytes; | ||
228 | size_t len; | ||
229 | |||
230 | if (in != 0) { | ||
231 | vq_err(vq, "Expected 0 input buffers, got %u\n", in); | ||
232 | return NULL; | ||
233 | } | ||
234 | |||
235 | pkt = kzalloc(sizeof(*pkt), GFP_KERNEL); | ||
236 | if (!pkt) | ||
237 | return NULL; | ||
238 | |||
239 | len = iov_length(vq->iov, out); | ||
240 | iov_iter_init(&iov_iter, WRITE, vq->iov, out, len); | ||
241 | |||
242 | nbytes = copy_from_iter(&pkt->hdr, sizeof(pkt->hdr), &iov_iter); | ||
243 | if (nbytes != sizeof(pkt->hdr)) { | ||
244 | vq_err(vq, "Expected %zu bytes for pkt->hdr, got %zu bytes\n", | ||
245 | sizeof(pkt->hdr), nbytes); | ||
246 | kfree(pkt); | ||
247 | return NULL; | ||
248 | } | ||
249 | |||
250 | if (le16_to_cpu(pkt->hdr.type) == VIRTIO_VSOCK_TYPE_STREAM) | ||
251 | pkt->len = le32_to_cpu(pkt->hdr.len); | ||
252 | |||
253 | /* No payload */ | ||
254 | if (!pkt->len) | ||
255 | return pkt; | ||
256 | |||
257 | /* The pkt is too big */ | ||
258 | if (pkt->len > VIRTIO_VSOCK_MAX_PKT_BUF_SIZE) { | ||
259 | kfree(pkt); | ||
260 | return NULL; | ||
261 | } | ||
262 | |||
263 | pkt->buf = kmalloc(pkt->len, GFP_KERNEL); | ||
264 | if (!pkt->buf) { | ||
265 | kfree(pkt); | ||
266 | return NULL; | ||
267 | } | ||
268 | |||
269 | nbytes = copy_from_iter(pkt->buf, pkt->len, &iov_iter); | ||
270 | if (nbytes != pkt->len) { | ||
271 | vq_err(vq, "Expected %u byte payload, got %zu bytes\n", | ||
272 | pkt->len, nbytes); | ||
273 | virtio_transport_free_pkt(pkt); | ||
274 | return NULL; | ||
275 | } | ||
276 | |||
277 | return pkt; | ||
278 | } | ||
279 | |||
280 | /* Is there space left for replies to rx packets? */ | ||
281 | static bool vhost_vsock_more_replies(struct vhost_vsock *vsock) | ||
282 | { | ||
283 | struct vhost_virtqueue *vq = &vsock->vqs[VSOCK_VQ_TX]; | ||
284 | int val; | ||
285 | |||
286 | smp_rmb(); /* paired with atomic_inc() and atomic_dec_return() */ | ||
287 | val = atomic_read(&vsock->queued_replies); | ||
288 | |||
289 | return val < vq->num; | ||
290 | } | ||
291 | |||
292 | static void vhost_vsock_handle_tx_kick(struct vhost_work *work) | ||
293 | { | ||
294 | struct vhost_virtqueue *vq = container_of(work, struct vhost_virtqueue, | ||
295 | poll.work); | ||
296 | struct vhost_vsock *vsock = container_of(vq->dev, struct vhost_vsock, | ||
297 | dev); | ||
298 | struct virtio_vsock_pkt *pkt; | ||
299 | int head; | ||
300 | unsigned int out, in; | ||
301 | bool added = false; | ||
302 | |||
303 | mutex_lock(&vq->mutex); | ||
304 | |||
305 | if (!vq->private_data) | ||
306 | goto out; | ||
307 | |||
308 | vhost_disable_notify(&vsock->dev, vq); | ||
309 | for (;;) { | ||
310 | if (!vhost_vsock_more_replies(vsock)) { | ||
311 | /* Stop tx until the device processes already | ||
312 | * pending replies. Leave tx virtqueue | ||
313 | * callbacks disabled. | ||
314 | */ | ||
315 | goto no_more_replies; | ||
316 | } | ||
317 | |||
318 | head = vhost_get_vq_desc(vq, vq->iov, ARRAY_SIZE(vq->iov), | ||
319 | &out, &in, NULL, NULL); | ||
320 | if (head < 0) | ||
321 | break; | ||
322 | |||
323 | if (head == vq->num) { | ||
324 | if (unlikely(vhost_enable_notify(&vsock->dev, vq))) { | ||
325 | vhost_disable_notify(&vsock->dev, vq); | ||
326 | continue; | ||
327 | } | ||
328 | break; | ||
329 | } | ||
330 | |||
331 | pkt = vhost_vsock_alloc_pkt(vq, out, in); | ||
332 | if (!pkt) { | ||
333 | vq_err(vq, "Faulted on pkt\n"); | ||
334 | continue; | ||
335 | } | ||
336 | |||
337 | /* Only accept correctly addressed packets */ | ||
338 | if (le64_to_cpu(pkt->hdr.src_cid) == vsock->guest_cid) | ||
339 | virtio_transport_recv_pkt(pkt); | ||
340 | else | ||
341 | virtio_transport_free_pkt(pkt); | ||
342 | |||
343 | vhost_add_used(vq, head, sizeof(pkt->hdr) + pkt->len); | ||
344 | added = true; | ||
345 | } | ||
346 | |||
347 | no_more_replies: | ||
348 | if (added) | ||
349 | vhost_signal(&vsock->dev, vq); | ||
350 | |||
351 | out: | ||
352 | mutex_unlock(&vq->mutex); | ||
353 | } | ||
354 | |||
355 | static void vhost_vsock_handle_rx_kick(struct vhost_work *work) | ||
356 | { | ||
357 | struct vhost_virtqueue *vq = container_of(work, struct vhost_virtqueue, | ||
358 | poll.work); | ||
359 | struct vhost_vsock *vsock = container_of(vq->dev, struct vhost_vsock, | ||
360 | dev); | ||
361 | |||
362 | vhost_transport_do_send_pkt(vsock, vq); | ||
363 | } | ||
364 | |||
365 | static int vhost_vsock_start(struct vhost_vsock *vsock) | ||
366 | { | ||
367 | size_t i; | ||
368 | int ret; | ||
369 | |||
370 | mutex_lock(&vsock->dev.mutex); | ||
371 | |||
372 | ret = vhost_dev_check_owner(&vsock->dev); | ||
373 | if (ret) | ||
374 | goto err; | ||
375 | |||
376 | for (i = 0; i < ARRAY_SIZE(vsock->vqs); i++) { | ||
377 | struct vhost_virtqueue *vq = &vsock->vqs[i]; | ||
378 | |||
379 | mutex_lock(&vq->mutex); | ||
380 | |||
381 | if (!vhost_vq_access_ok(vq)) { | ||
382 | ret = -EFAULT; | ||
383 | mutex_unlock(&vq->mutex); | ||
384 | goto err_vq; | ||
385 | } | ||
386 | |||
387 | if (!vq->private_data) { | ||
388 | vq->private_data = vsock; | ||
389 | vhost_vq_init_access(vq); | ||
390 | } | ||
391 | |||
392 | mutex_unlock(&vq->mutex); | ||
393 | } | ||
394 | |||
395 | mutex_unlock(&vsock->dev.mutex); | ||
396 | return 0; | ||
397 | |||
398 | err_vq: | ||
399 | for (i = 0; i < ARRAY_SIZE(vsock->vqs); i++) { | ||
400 | struct vhost_virtqueue *vq = &vsock->vqs[i]; | ||
401 | |||
402 | mutex_lock(&vq->mutex); | ||
403 | vq->private_data = NULL; | ||
404 | mutex_unlock(&vq->mutex); | ||
405 | } | ||
406 | err: | ||
407 | mutex_unlock(&vsock->dev.mutex); | ||
408 | return ret; | ||
409 | } | ||
410 | |||
411 | static int vhost_vsock_stop(struct vhost_vsock *vsock) | ||
412 | { | ||
413 | size_t i; | ||
414 | int ret; | ||
415 | |||
416 | mutex_lock(&vsock->dev.mutex); | ||
417 | |||
418 | ret = vhost_dev_check_owner(&vsock->dev); | ||
419 | if (ret) | ||
420 | goto err; | ||
421 | |||
422 | for (i = 0; i < ARRAY_SIZE(vsock->vqs); i++) { | ||
423 | struct vhost_virtqueue *vq = &vsock->vqs[i]; | ||
424 | |||
425 | mutex_lock(&vq->mutex); | ||
426 | vq->private_data = NULL; | ||
427 | mutex_unlock(&vq->mutex); | ||
428 | } | ||
429 | |||
430 | err: | ||
431 | mutex_unlock(&vsock->dev.mutex); | ||
432 | return ret; | ||
433 | } | ||
434 | |||
435 | static void vhost_vsock_free(struct vhost_vsock *vsock) | ||
436 | { | ||
437 | kvfree(vsock); | ||
438 | } | ||
439 | |||
440 | static int vhost_vsock_dev_open(struct inode *inode, struct file *file) | ||
441 | { | ||
442 | struct vhost_virtqueue **vqs; | ||
443 | struct vhost_vsock *vsock; | ||
444 | int ret; | ||
445 | |||
446 | /* This struct is large and allocation could fail, fall back to vmalloc | ||
447 | * if there is no other way. | ||
448 | */ | ||
449 | vsock = kzalloc(sizeof(*vsock), GFP_KERNEL | __GFP_NOWARN | __GFP_REPEAT); | ||
450 | if (!vsock) { | ||
451 | vsock = vmalloc(sizeof(*vsock)); | ||
452 | if (!vsock) | ||
453 | return -ENOMEM; | ||
454 | } | ||
455 | |||
456 | vqs = kmalloc_array(ARRAY_SIZE(vsock->vqs), sizeof(*vqs), GFP_KERNEL); | ||
457 | if (!vqs) { | ||
458 | ret = -ENOMEM; | ||
459 | goto out; | ||
460 | } | ||
461 | |||
462 | atomic_set(&vsock->queued_replies, 0); | ||
463 | |||
464 | vqs[VSOCK_VQ_TX] = &vsock->vqs[VSOCK_VQ_TX]; | ||
465 | vqs[VSOCK_VQ_RX] = &vsock->vqs[VSOCK_VQ_RX]; | ||
466 | vsock->vqs[VSOCK_VQ_TX].handle_kick = vhost_vsock_handle_tx_kick; | ||
467 | vsock->vqs[VSOCK_VQ_RX].handle_kick = vhost_vsock_handle_rx_kick; | ||
468 | |||
469 | vhost_dev_init(&vsock->dev, vqs, ARRAY_SIZE(vsock->vqs)); | ||
470 | |||
471 | file->private_data = vsock; | ||
472 | spin_lock_init(&vsock->send_pkt_list_lock); | ||
473 | INIT_LIST_HEAD(&vsock->send_pkt_list); | ||
474 | vhost_work_init(&vsock->send_pkt_work, vhost_transport_send_pkt_work); | ||
475 | |||
476 | spin_lock_bh(&vhost_vsock_lock); | ||
477 | list_add_tail(&vsock->list, &vhost_vsock_list); | ||
478 | spin_unlock_bh(&vhost_vsock_lock); | ||
479 | return 0; | ||
480 | |||
481 | out: | ||
482 | vhost_vsock_free(vsock); | ||
483 | return ret; | ||
484 | } | ||
485 | |||
486 | static void vhost_vsock_flush(struct vhost_vsock *vsock) | ||
487 | { | ||
488 | int i; | ||
489 | |||
490 | for (i = 0; i < ARRAY_SIZE(vsock->vqs); i++) | ||
491 | if (vsock->vqs[i].handle_kick) | ||
492 | vhost_poll_flush(&vsock->vqs[i].poll); | ||
493 | vhost_work_flush(&vsock->dev, &vsock->send_pkt_work); | ||
494 | } | ||
495 | |||
496 | static void vhost_vsock_reset_orphans(struct sock *sk) | ||
497 | { | ||
498 | struct vsock_sock *vsk = vsock_sk(sk); | ||
499 | |||
500 | /* vmci_transport.c doesn't take sk_lock here either. At least we're | ||
501 | * under vsock_table_lock so the sock cannot disappear while we're | ||
502 | * executing. | ||
503 | */ | ||
504 | |||
505 | if (!vhost_vsock_get(vsk->local_addr.svm_cid)) { | ||
506 | sock_set_flag(sk, SOCK_DONE); | ||
507 | vsk->peer_shutdown = SHUTDOWN_MASK; | ||
508 | sk->sk_state = SS_UNCONNECTED; | ||
509 | sk->sk_err = ECONNRESET; | ||
510 | sk->sk_error_report(sk); | ||
511 | } | ||
512 | } | ||
513 | |||
514 | static int vhost_vsock_dev_release(struct inode *inode, struct file *file) | ||
515 | { | ||
516 | struct vhost_vsock *vsock = file->private_data; | ||
517 | |||
518 | spin_lock_bh(&vhost_vsock_lock); | ||
519 | list_del(&vsock->list); | ||
520 | spin_unlock_bh(&vhost_vsock_lock); | ||
521 | |||
522 | /* Iterating over all connections for all CIDs to find orphans is | ||
523 | * inefficient. Room for improvement here. */ | ||
524 | vsock_for_each_connected_socket(vhost_vsock_reset_orphans); | ||
525 | |||
526 | vhost_vsock_stop(vsock); | ||
527 | vhost_vsock_flush(vsock); | ||
528 | vhost_dev_stop(&vsock->dev); | ||
529 | |||
530 | spin_lock_bh(&vsock->send_pkt_list_lock); | ||
531 | while (!list_empty(&vsock->send_pkt_list)) { | ||
532 | struct virtio_vsock_pkt *pkt; | ||
533 | |||
534 | pkt = list_first_entry(&vsock->send_pkt_list, | ||
535 | struct virtio_vsock_pkt, list); | ||
536 | list_del_init(&pkt->list); | ||
537 | virtio_transport_free_pkt(pkt); | ||
538 | } | ||
539 | spin_unlock_bh(&vsock->send_pkt_list_lock); | ||
540 | |||
541 | vhost_dev_cleanup(&vsock->dev, false); | ||
542 | kfree(vsock->dev.vqs); | ||
543 | vhost_vsock_free(vsock); | ||
544 | return 0; | ||
545 | } | ||
546 | |||
547 | static int vhost_vsock_set_cid(struct vhost_vsock *vsock, u64 guest_cid) | ||
548 | { | ||
549 | struct vhost_vsock *other; | ||
550 | |||
551 | /* Refuse reserved CIDs */ | ||
552 | if (guest_cid <= VMADDR_CID_HOST || | ||
553 | guest_cid == U32_MAX) | ||
554 | return -EINVAL; | ||
555 | |||
556 | /* 64-bit CIDs are not yet supported */ | ||
557 | if (guest_cid > U32_MAX) | ||
558 | return -EINVAL; | ||
559 | |||
560 | /* Refuse if CID is already in use */ | ||
561 | other = vhost_vsock_get(guest_cid); | ||
562 | if (other && other != vsock) | ||
563 | return -EADDRINUSE; | ||
564 | |||
565 | spin_lock_bh(&vhost_vsock_lock); | ||
566 | vsock->guest_cid = guest_cid; | ||
567 | spin_unlock_bh(&vhost_vsock_lock); | ||
568 | |||
569 | return 0; | ||
570 | } | ||
571 | |||
572 | static int vhost_vsock_set_features(struct vhost_vsock *vsock, u64 features) | ||
573 | { | ||
574 | struct vhost_virtqueue *vq; | ||
575 | int i; | ||
576 | |||
577 | if (features & ~VHOST_VSOCK_FEATURES) | ||
578 | return -EOPNOTSUPP; | ||
579 | |||
580 | mutex_lock(&vsock->dev.mutex); | ||
581 | if ((features & (1 << VHOST_F_LOG_ALL)) && | ||
582 | !vhost_log_access_ok(&vsock->dev)) { | ||
583 | mutex_unlock(&vsock->dev.mutex); | ||
584 | return -EFAULT; | ||
585 | } | ||
586 | |||
587 | for (i = 0; i < ARRAY_SIZE(vsock->vqs); i++) { | ||
588 | vq = &vsock->vqs[i]; | ||
589 | mutex_lock(&vq->mutex); | ||
590 | vq->acked_features = features; | ||
591 | mutex_unlock(&vq->mutex); | ||
592 | } | ||
593 | mutex_unlock(&vsock->dev.mutex); | ||
594 | return 0; | ||
595 | } | ||
596 | |||
597 | static long vhost_vsock_dev_ioctl(struct file *f, unsigned int ioctl, | ||
598 | unsigned long arg) | ||
599 | { | ||
600 | struct vhost_vsock *vsock = f->private_data; | ||
601 | void __user *argp = (void __user *)arg; | ||
602 | u64 guest_cid; | ||
603 | u64 features; | ||
604 | int start; | ||
605 | int r; | ||
606 | |||
607 | switch (ioctl) { | ||
608 | case VHOST_VSOCK_SET_GUEST_CID: | ||
609 | if (copy_from_user(&guest_cid, argp, sizeof(guest_cid))) | ||
610 | return -EFAULT; | ||
611 | return vhost_vsock_set_cid(vsock, guest_cid); | ||
612 | case VHOST_VSOCK_SET_RUNNING: | ||
613 | if (copy_from_user(&start, argp, sizeof(start))) | ||
614 | return -EFAULT; | ||
615 | if (start) | ||
616 | return vhost_vsock_start(vsock); | ||
617 | else | ||
618 | return vhost_vsock_stop(vsock); | ||
619 | case VHOST_GET_FEATURES: | ||
620 | features = VHOST_VSOCK_FEATURES; | ||
621 | if (copy_to_user(argp, &features, sizeof(features))) | ||
622 | return -EFAULT; | ||
623 | return 0; | ||
624 | case VHOST_SET_FEATURES: | ||
625 | if (copy_from_user(&features, argp, sizeof(features))) | ||
626 | return -EFAULT; | ||
627 | return vhost_vsock_set_features(vsock, features); | ||
628 | default: | ||
629 | mutex_lock(&vsock->dev.mutex); | ||
630 | r = vhost_dev_ioctl(&vsock->dev, ioctl, argp); | ||
631 | if (r == -ENOIOCTLCMD) | ||
632 | r = vhost_vring_ioctl(&vsock->dev, ioctl, argp); | ||
633 | else | ||
634 | vhost_vsock_flush(vsock); | ||
635 | mutex_unlock(&vsock->dev.mutex); | ||
636 | return r; | ||
637 | } | ||
638 | } | ||
639 | |||
640 | static const struct file_operations vhost_vsock_fops = { | ||
641 | .owner = THIS_MODULE, | ||
642 | .open = vhost_vsock_dev_open, | ||
643 | .release = vhost_vsock_dev_release, | ||
644 | .llseek = noop_llseek, | ||
645 | .unlocked_ioctl = vhost_vsock_dev_ioctl, | ||
646 | }; | ||
647 | |||
648 | static struct miscdevice vhost_vsock_misc = { | ||
649 | .minor = MISC_DYNAMIC_MINOR, | ||
650 | .name = "vhost-vsock", | ||
651 | .fops = &vhost_vsock_fops, | ||
652 | }; | ||
653 | |||
654 | static struct virtio_transport vhost_transport = { | ||
655 | .transport = { | ||
656 | .get_local_cid = vhost_transport_get_local_cid, | ||
657 | |||
658 | .init = virtio_transport_do_socket_init, | ||
659 | .destruct = virtio_transport_destruct, | ||
660 | .release = virtio_transport_release, | ||
661 | .connect = virtio_transport_connect, | ||
662 | .shutdown = virtio_transport_shutdown, | ||
663 | |||
664 | .dgram_enqueue = virtio_transport_dgram_enqueue, | ||
665 | .dgram_dequeue = virtio_transport_dgram_dequeue, | ||
666 | .dgram_bind = virtio_transport_dgram_bind, | ||
667 | .dgram_allow = virtio_transport_dgram_allow, | ||
668 | |||
669 | .stream_enqueue = virtio_transport_stream_enqueue, | ||
670 | .stream_dequeue = virtio_transport_stream_dequeue, | ||
671 | .stream_has_data = virtio_transport_stream_has_data, | ||
672 | .stream_has_space = virtio_transport_stream_has_space, | ||
673 | .stream_rcvhiwat = virtio_transport_stream_rcvhiwat, | ||
674 | .stream_is_active = virtio_transport_stream_is_active, | ||
675 | .stream_allow = virtio_transport_stream_allow, | ||
676 | |||
677 | .notify_poll_in = virtio_transport_notify_poll_in, | ||
678 | .notify_poll_out = virtio_transport_notify_poll_out, | ||
679 | .notify_recv_init = virtio_transport_notify_recv_init, | ||
680 | .notify_recv_pre_block = virtio_transport_notify_recv_pre_block, | ||
681 | .notify_recv_pre_dequeue = virtio_transport_notify_recv_pre_dequeue, | ||
682 | .notify_recv_post_dequeue = virtio_transport_notify_recv_post_dequeue, | ||
683 | .notify_send_init = virtio_transport_notify_send_init, | ||
684 | .notify_send_pre_block = virtio_transport_notify_send_pre_block, | ||
685 | .notify_send_pre_enqueue = virtio_transport_notify_send_pre_enqueue, | ||
686 | .notify_send_post_enqueue = virtio_transport_notify_send_post_enqueue, | ||
687 | |||
688 | .set_buffer_size = virtio_transport_set_buffer_size, | ||
689 | .set_min_buffer_size = virtio_transport_set_min_buffer_size, | ||
690 | .set_max_buffer_size = virtio_transport_set_max_buffer_size, | ||
691 | .get_buffer_size = virtio_transport_get_buffer_size, | ||
692 | .get_min_buffer_size = virtio_transport_get_min_buffer_size, | ||
693 | .get_max_buffer_size = virtio_transport_get_max_buffer_size, | ||
694 | }, | ||
695 | |||
696 | .send_pkt = vhost_transport_send_pkt, | ||
697 | }; | ||
698 | |||
699 | static int __init vhost_vsock_init(void) | ||
700 | { | ||
701 | int ret; | ||
702 | |||
703 | ret = vsock_core_init(&vhost_transport.transport); | ||
704 | if (ret < 0) | ||
705 | return ret; | ||
706 | return misc_register(&vhost_vsock_misc); | ||
707 | }; | ||
708 | |||
709 | static void __exit vhost_vsock_exit(void) | ||
710 | { | ||
711 | misc_deregister(&vhost_vsock_misc); | ||
712 | vsock_core_exit(); | ||
713 | }; | ||
714 | |||
715 | module_init(vhost_vsock_init); | ||
716 | module_exit(vhost_vsock_exit); | ||
717 | MODULE_LICENSE("GPL v2"); | ||
718 | MODULE_AUTHOR("Asias He"); | ||
719 | MODULE_DESCRIPTION("vhost transport for vsock "); | ||