]>
Commit | Line | Data |
---|---|---|
fc0b9b0e SH |
1 | /* |
2 | * Virtio vsock device | |
3 | * | |
4 | * Copyright 2015 Red Hat, Inc. | |
5 | * | |
6 | * Authors: | |
7 | * Stefan Hajnoczi <stefanha@redhat.com> | |
8 | * | |
9 | * This work is licensed under the terms of the GNU GPL, version 2 or | |
10 | * (at your option) any later version. See the COPYING file in the | |
11 | * top-level directory. | |
12 | */ | |
13 | ||
fc0b9b0e SH |
14 | #include "qemu/osdep.h" |
15 | #include "standard-headers/linux/virtio_vsock.h" | |
16 | #include "qapi/error.h" | |
fc0b9b0e | 17 | #include "hw/virtio/virtio-access.h" |
fc0b9b0e | 18 | #include "qemu/error-report.h" |
384c2561 | 19 | #include "qemu/sockets.h" |
a27bd6c7 | 20 | #include "hw/qdev-properties.h" |
fc0b9b0e | 21 | #include "hw/virtio/vhost-vsock.h" |
fc0b9b0e SH |
22 | #include "monitor/monitor.h" |
23 | ||
fc0b9b0e SH |
24 | static void vhost_vsock_get_config(VirtIODevice *vdev, uint8_t *config) |
25 | { | |
26 | VHostVSock *vsock = VHOST_VSOCK(vdev); | |
27 | struct virtio_vsock_config vsockcfg = {}; | |
28 | ||
29 | virtio_stq_p(vdev, &vsockcfg.guest_cid, vsock->conf.guest_cid); | |
30 | memcpy(config, &vsockcfg, sizeof(vsockcfg)); | |
31 | } | |
32 | ||
c6136ec0 | 33 | static int vhost_vsock_set_guest_cid(VirtIODevice *vdev) |
fc0b9b0e | 34 | { |
c6136ec0 SG |
35 | VHostVSockCommon *vvc = VHOST_VSOCK_COMMON(vdev); |
36 | VHostVSock *vsock = VHOST_VSOCK(vdev); | |
37 | const VhostOps *vhost_ops = vvc->vhost_dev.vhost_ops; | |
fc0b9b0e SH |
38 | int ret; |
39 | ||
40 | if (!vhost_ops->vhost_vsock_set_guest_cid) { | |
41 | return -ENOSYS; | |
42 | } | |
43 | ||
c6136ec0 | 44 | ret = vhost_ops->vhost_vsock_set_guest_cid(&vvc->vhost_dev, |
fc0b9b0e SH |
45 | vsock->conf.guest_cid); |
46 | if (ret < 0) { | |
47 | return -errno; | |
48 | } | |
49 | return 0; | |
50 | } | |
51 | ||
c6136ec0 | 52 | static int vhost_vsock_set_running(VirtIODevice *vdev, int start) |
fc0b9b0e | 53 | { |
c6136ec0 SG |
54 | VHostVSockCommon *vvc = VHOST_VSOCK_COMMON(vdev); |
55 | const VhostOps *vhost_ops = vvc->vhost_dev.vhost_ops; | |
fc0b9b0e SH |
56 | int ret; |
57 | ||
58 | if (!vhost_ops->vhost_vsock_set_running) { | |
59 | return -ENOSYS; | |
60 | } | |
61 | ||
c6136ec0 | 62 | ret = vhost_ops->vhost_vsock_set_running(&vvc->vhost_dev, start); |
fc0b9b0e SH |
63 | if (ret < 0) { |
64 | return -errno; | |
65 | } | |
66 | return 0; | |
67 | } | |
68 | ||
fc0b9b0e SH |
69 | |
70 | static void vhost_vsock_set_status(VirtIODevice *vdev, uint8_t status) | |
71 | { | |
c6136ec0 | 72 | VHostVSockCommon *vvc = VHOST_VSOCK_COMMON(vdev); |
259d69c0 | 73 | bool should_start = virtio_device_should_start(vdev, status); |
c6136ec0 | 74 | int ret; |
fc0b9b0e | 75 | |
b8f3e6a1 | 76 | if (vhost_dev_is_started(&vvc->vhost_dev) == should_start) { |
fc0b9b0e SH |
77 | return; |
78 | } | |
79 | ||
80 | if (should_start) { | |
c6136ec0 SG |
81 | ret = vhost_vsock_common_start(vdev); |
82 | if (ret < 0) { | |
83 | return; | |
84 | } | |
85 | ||
86 | ret = vhost_vsock_set_running(vdev, 1); | |
87 | if (ret < 0) { | |
88 | vhost_vsock_common_stop(vdev); | |
89 | error_report("Error starting vhost vsock: %d", -ret); | |
90 | return; | |
91 | } | |
fc0b9b0e | 92 | } else { |
c6136ec0 SG |
93 | ret = vhost_vsock_set_running(vdev, 0); |
94 | if (ret < 0) { | |
95 | error_report("vhost vsock set running failed: %d", ret); | |
96 | return; | |
97 | } | |
98 | ||
99 | vhost_vsock_common_stop(vdev); | |
fc0b9b0e SH |
100 | } |
101 | } | |
102 | ||
103 | static uint64_t vhost_vsock_get_features(VirtIODevice *vdev, | |
104 | uint64_t requested_features, | |
105 | Error **errp) | |
106 | { | |
46ce0171 | 107 | return vhost_vsock_common_get_features(vdev, requested_features, errp); |
fc0b9b0e SH |
108 | } |
109 | ||
81cc8a65 HP |
110 | static const VMStateDescription vmstate_virtio_vhost_vsock = { |
111 | .name = "virtio-vhost_vsock", | |
112 | .minimum_version_id = VHOST_VSOCK_SAVEVM_VERSION, | |
113 | .version_id = VHOST_VSOCK_SAVEVM_VERSION, | |
ca02a170 | 114 | .fields = (const VMStateField[]) { |
81cc8a65 HP |
115 | VMSTATE_VIRTIO_DEVICE, |
116 | VMSTATE_END_OF_LIST() | |
117 | }, | |
c6136ec0 SG |
118 | .pre_save = vhost_vsock_common_pre_save, |
119 | .post_load = vhost_vsock_common_post_load, | |
81cc8a65 | 120 | }; |
fc0b9b0e SH |
121 | |
122 | static void vhost_vsock_device_realize(DeviceState *dev, Error **errp) | |
123 | { | |
c6136ec0 | 124 | VHostVSockCommon *vvc = VHOST_VSOCK_COMMON(dev); |
fc0b9b0e SH |
125 | VirtIODevice *vdev = VIRTIO_DEVICE(dev); |
126 | VHostVSock *vsock = VHOST_VSOCK(dev); | |
127 | int vhostfd; | |
128 | int ret; | |
129 | ||
130 | /* Refuse to use reserved CID numbers */ | |
131 | if (vsock->conf.guest_cid <= 2) { | |
132 | error_setg(errp, "guest-cid property must be greater than 2"); | |
133 | return; | |
134 | } | |
135 | ||
136 | if (vsock->conf.guest_cid > UINT32_MAX) { | |
137 | error_setg(errp, "guest-cid property must be a 32-bit number"); | |
138 | return; | |
139 | } | |
140 | ||
141 | if (vsock->conf.vhostfd) { | |
947e4744 | 142 | vhostfd = monitor_fd_param(monitor_cur(), vsock->conf.vhostfd, errp); |
fc0b9b0e SH |
143 | if (vhostfd == -1) { |
144 | error_prepend(errp, "vhost-vsock: unable to parse vhostfd: "); | |
145 | return; | |
146 | } | |
384c2561 | 147 | |
701544cf MAL |
148 | if (!g_unix_set_fd_nonblocking(vhostfd, true, NULL)) { |
149 | error_setg_errno(errp, errno, | |
384c2561 SG |
150 | "vhost-vsock: unable to set non-blocking mode"); |
151 | return; | |
152 | } | |
fc0b9b0e SH |
153 | } else { |
154 | vhostfd = open("/dev/vhost-vsock", O_RDWR); | |
155 | if (vhostfd < 0) { | |
f1e92c3d | 156 | error_setg_errno(errp, errno, |
fc0b9b0e SH |
157 | "vhost-vsock: failed to open vhost device"); |
158 | return; | |
159 | } | |
384c2561 | 160 | |
701544cf MAL |
161 | if (!g_unix_set_fd_nonblocking(vhostfd, true, NULL)) { |
162 | error_setg_errno(errp, errno, | |
163 | "Failed to set FD nonblocking"); | |
164 | return; | |
165 | } | |
fc0b9b0e SH |
166 | } |
167 | ||
3857cd5c | 168 | vhost_vsock_common_realize(vdev); |
fc0b9b0e | 169 | |
c6136ec0 | 170 | ret = vhost_dev_init(&vvc->vhost_dev, (void *)(uintptr_t)vhostfd, |
a6945f22 | 171 | VHOST_BACKEND_TYPE_KERNEL, 0, errp); |
fc0b9b0e | 172 | if (ret < 0) { |
d731ab31 DT |
173 | /* |
174 | * vhostfd is closed by vhost_dev_cleanup, which is called | |
175 | * by vhost_dev_init on initialization error. | |
176 | */ | |
fc0b9b0e SH |
177 | goto err_virtio; |
178 | } | |
179 | ||
c6136ec0 | 180 | ret = vhost_vsock_set_guest_cid(vdev); |
fc0b9b0e SH |
181 | if (ret < 0) { |
182 | error_setg_errno(errp, -ret, "vhost-vsock: unable to set guest cid"); | |
183 | goto err_vhost_dev; | |
184 | } | |
185 | ||
fc0b9b0e SH |
186 | return; |
187 | ||
188 | err_vhost_dev: | |
e82cdba3 | 189 | /* vhost_dev_cleanup() closes the vhostfd passed to vhost_dev_init() */ |
d731ab31 | 190 | vhost_dev_cleanup(&vvc->vhost_dev); |
fc0b9b0e | 191 | err_virtio: |
c6136ec0 | 192 | vhost_vsock_common_unrealize(vdev); |
fc0b9b0e SH |
193 | } |
194 | ||
b69c3c21 | 195 | static void vhost_vsock_device_unrealize(DeviceState *dev) |
fc0b9b0e | 196 | { |
c6136ec0 | 197 | VHostVSockCommon *vvc = VHOST_VSOCK_COMMON(dev); |
fc0b9b0e | 198 | VirtIODevice *vdev = VIRTIO_DEVICE(dev); |
fc0b9b0e SH |
199 | |
200 | /* This will stop vhost backend if appropriate. */ | |
201 | vhost_vsock_set_status(vdev, 0); | |
202 | ||
c6136ec0 SG |
203 | vhost_dev_cleanup(&vvc->vhost_dev); |
204 | vhost_vsock_common_unrealize(vdev); | |
fc0b9b0e SH |
205 | } |
206 | ||
207 | static Property vhost_vsock_properties[] = { | |
208 | DEFINE_PROP_UINT64("guest-cid", VHostVSock, conf.guest_cid, 0), | |
209 | DEFINE_PROP_STRING("vhostfd", VHostVSock, conf.vhostfd), | |
210 | DEFINE_PROP_END_OF_LIST(), | |
211 | }; | |
212 | ||
213 | static void vhost_vsock_class_init(ObjectClass *klass, void *data) | |
214 | { | |
215 | DeviceClass *dc = DEVICE_CLASS(klass); | |
216 | VirtioDeviceClass *vdc = VIRTIO_DEVICE_CLASS(klass); | |
217 | ||
4f67d30b | 218 | device_class_set_props(dc, vhost_vsock_properties); |
fc0b9b0e | 219 | dc->vmsd = &vmstate_virtio_vhost_vsock; |
fc0b9b0e SH |
220 | vdc->realize = vhost_vsock_device_realize; |
221 | vdc->unrealize = vhost_vsock_device_unrealize; | |
222 | vdc->get_features = vhost_vsock_get_features; | |
223 | vdc->get_config = vhost_vsock_get_config; | |
224 | vdc->set_status = vhost_vsock_set_status; | |
fc0b9b0e SH |
225 | } |
226 | ||
227 | static const TypeInfo vhost_vsock_info = { | |
228 | .name = TYPE_VHOST_VSOCK, | |
c6136ec0 | 229 | .parent = TYPE_VHOST_VSOCK_COMMON, |
fc0b9b0e SH |
230 | .instance_size = sizeof(VHostVSock), |
231 | .class_init = vhost_vsock_class_init, | |
232 | }; | |
233 | ||
234 | static void vhost_vsock_register_types(void) | |
235 | { | |
236 | type_register_static(&vhost_vsock_info); | |
237 | } | |
238 | ||
239 | type_init(vhost_vsock_register_types) |