]>
Commit | Line | Data |
---|---|---|
077263fb SG |
1 | // SPDX-License-Identifier: GPL-2.0-only |
2 | /* loopback transport for vsock using virtio_transport_common APIs | |
3 | * | |
4 | * Copyright (C) 2013-2019 Red Hat, Inc. | |
5 | * Authors: Asias He <asias@redhat.com> | |
6 | * Stefan Hajnoczi <stefanha@redhat.com> | |
7 | * Stefano Garzarella <sgarzare@redhat.com> | |
8 | * | |
9 | */ | |
10 | #include <linux/spinlock.h> | |
11 | #include <linux/module.h> | |
12 | #include <linux/list.h> | |
13 | #include <linux/virtio_vsock.h> | |
14 | ||
15 | struct vsock_loopback { | |
16 | struct workqueue_struct *workqueue; | |
17 | ||
18 | spinlock_t pkt_list_lock; /* protects pkt_list */ | |
19 | struct list_head pkt_list; | |
20 | struct work_struct pkt_work; | |
21 | }; | |
22 | ||
23 | static struct vsock_loopback the_vsock_loopback; | |
24 | ||
25 | static u32 vsock_loopback_get_local_cid(void) | |
26 | { | |
27 | return VMADDR_CID_LOCAL; | |
28 | } | |
29 | ||
30 | static int vsock_loopback_send_pkt(struct virtio_vsock_pkt *pkt) | |
31 | { | |
32 | struct vsock_loopback *vsock = &the_vsock_loopback; | |
33 | int len = pkt->len; | |
34 | ||
35 | spin_lock_bh(&vsock->pkt_list_lock); | |
36 | list_add_tail(&pkt->list, &vsock->pkt_list); | |
37 | spin_unlock_bh(&vsock->pkt_list_lock); | |
38 | ||
39 | queue_work(vsock->workqueue, &vsock->pkt_work); | |
40 | ||
41 | return len; | |
42 | } | |
43 | ||
44 | static int vsock_loopback_cancel_pkt(struct vsock_sock *vsk) | |
45 | { | |
46 | struct vsock_loopback *vsock = &the_vsock_loopback; | |
47 | struct virtio_vsock_pkt *pkt, *n; | |
48 | LIST_HEAD(freeme); | |
49 | ||
50 | spin_lock_bh(&vsock->pkt_list_lock); | |
51 | list_for_each_entry_safe(pkt, n, &vsock->pkt_list, list) { | |
52 | if (pkt->vsk != vsk) | |
53 | continue; | |
54 | list_move(&pkt->list, &freeme); | |
55 | } | |
56 | spin_unlock_bh(&vsock->pkt_list_lock); | |
57 | ||
58 | list_for_each_entry_safe(pkt, n, &freeme, list) { | |
59 | list_del(&pkt->list); | |
60 | virtio_transport_free_pkt(pkt); | |
61 | } | |
62 | ||
63 | return 0; | |
64 | } | |
65 | ||
66 | static struct virtio_transport loopback_transport = { | |
67 | .transport = { | |
68 | .module = THIS_MODULE, | |
69 | ||
70 | .get_local_cid = vsock_loopback_get_local_cid, | |
71 | ||
72 | .init = virtio_transport_do_socket_init, | |
73 | .destruct = virtio_transport_destruct, | |
74 | .release = virtio_transport_release, | |
75 | .connect = virtio_transport_connect, | |
76 | .shutdown = virtio_transport_shutdown, | |
77 | .cancel_pkt = vsock_loopback_cancel_pkt, | |
78 | ||
79 | .dgram_bind = virtio_transport_dgram_bind, | |
80 | .dgram_dequeue = virtio_transport_dgram_dequeue, | |
81 | .dgram_enqueue = virtio_transport_dgram_enqueue, | |
82 | .dgram_allow = virtio_transport_dgram_allow, | |
83 | ||
84 | .stream_dequeue = virtio_transport_stream_dequeue, | |
85 | .stream_enqueue = virtio_transport_stream_enqueue, | |
86 | .stream_has_data = virtio_transport_stream_has_data, | |
87 | .stream_has_space = virtio_transport_stream_has_space, | |
88 | .stream_rcvhiwat = virtio_transport_stream_rcvhiwat, | |
89 | .stream_is_active = virtio_transport_stream_is_active, | |
90 | .stream_allow = virtio_transport_stream_allow, | |
91 | ||
92 | .notify_poll_in = virtio_transport_notify_poll_in, | |
93 | .notify_poll_out = virtio_transport_notify_poll_out, | |
94 | .notify_recv_init = virtio_transport_notify_recv_init, | |
95 | .notify_recv_pre_block = virtio_transport_notify_recv_pre_block, | |
96 | .notify_recv_pre_dequeue = virtio_transport_notify_recv_pre_dequeue, | |
97 | .notify_recv_post_dequeue = virtio_transport_notify_recv_post_dequeue, | |
98 | .notify_send_init = virtio_transport_notify_send_init, | |
99 | .notify_send_pre_block = virtio_transport_notify_send_pre_block, | |
100 | .notify_send_pre_enqueue = virtio_transport_notify_send_pre_enqueue, | |
101 | .notify_send_post_enqueue = virtio_transport_notify_send_post_enqueue, | |
102 | .notify_buffer_size = virtio_transport_notify_buffer_size, | |
103 | }, | |
104 | ||
105 | .send_pkt = vsock_loopback_send_pkt, | |
106 | }; | |
107 | ||
108 | static void vsock_loopback_work(struct work_struct *work) | |
109 | { | |
110 | struct vsock_loopback *vsock = | |
111 | container_of(work, struct vsock_loopback, pkt_work); | |
112 | LIST_HEAD(pkts); | |
113 | ||
114 | spin_lock_bh(&vsock->pkt_list_lock); | |
115 | list_splice_init(&vsock->pkt_list, &pkts); | |
116 | spin_unlock_bh(&vsock->pkt_list_lock); | |
117 | ||
118 | while (!list_empty(&pkts)) { | |
119 | struct virtio_vsock_pkt *pkt; | |
120 | ||
121 | pkt = list_first_entry(&pkts, struct virtio_vsock_pkt, list); | |
122 | list_del_init(&pkt->list); | |
123 | ||
124 | virtio_transport_deliver_tap_pkt(pkt); | |
125 | virtio_transport_recv_pkt(&loopback_transport, pkt); | |
126 | } | |
127 | } | |
128 | ||
129 | static int __init vsock_loopback_init(void) | |
130 | { | |
131 | struct vsock_loopback *vsock = &the_vsock_loopback; | |
132 | int ret; | |
133 | ||
134 | vsock->workqueue = alloc_workqueue("vsock-loopback", 0, 0); | |
135 | if (!vsock->workqueue) | |
136 | return -ENOMEM; | |
137 | ||
138 | spin_lock_init(&vsock->pkt_list_lock); | |
139 | INIT_LIST_HEAD(&vsock->pkt_list); | |
140 | INIT_WORK(&vsock->pkt_work, vsock_loopback_work); | |
141 | ||
142 | ret = vsock_core_register(&loopback_transport.transport, | |
143 | VSOCK_TRANSPORT_F_LOCAL); | |
144 | if (ret) | |
145 | goto out_wq; | |
146 | ||
147 | return 0; | |
148 | ||
149 | out_wq: | |
150 | destroy_workqueue(vsock->workqueue); | |
151 | return ret; | |
152 | } | |
153 | ||
154 | static void __exit vsock_loopback_exit(void) | |
155 | { | |
156 | struct vsock_loopback *vsock = &the_vsock_loopback; | |
157 | struct virtio_vsock_pkt *pkt; | |
158 | ||
159 | vsock_core_unregister(&loopback_transport.transport); | |
160 | ||
161 | flush_work(&vsock->pkt_work); | |
162 | ||
163 | spin_lock_bh(&vsock->pkt_list_lock); | |
164 | while (!list_empty(&vsock->pkt_list)) { | |
165 | pkt = list_first_entry(&vsock->pkt_list, | |
166 | struct virtio_vsock_pkt, list); | |
167 | list_del(&pkt->list); | |
168 | virtio_transport_free_pkt(pkt); | |
169 | } | |
170 | spin_unlock_bh(&vsock->pkt_list_lock); | |
171 | ||
172 | destroy_workqueue(vsock->workqueue); | |
173 | } | |
174 | ||
175 | module_init(vsock_loopback_init); | |
176 | module_exit(vsock_loopback_exit); | |
177 | MODULE_LICENSE("GPL v2"); | |
178 | MODULE_AUTHOR("Stefano Garzarella <sgarzare@redhat.com>"); | |
179 | MODULE_DESCRIPTION("loopback transport for vsock"); | |
180 | MODULE_ALIAS_NETPROTO(PF_VSOCK); |