xref: /openbmc/qemu/hw/virtio/vhost-vsock.c (revision 45e576c7)
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 
14 #include "qemu/osdep.h"
15 #include "standard-headers/linux/virtio_vsock.h"
16 #include "qapi/error.h"
17 #include "hw/virtio/virtio-access.h"
18 #include "qemu/error-report.h"
19 #include "qemu/sockets.h"
20 #include "hw/qdev-properties.h"
21 #include "hw/virtio/vhost-vsock.h"
22 #include "monitor/monitor.h"
23 
24 const int feature_bits[] = {
25     VIRTIO_VSOCK_F_SEQPACKET,
26     VHOST_INVALID_FEATURE_BIT
27 };
28 
29 static void vhost_vsock_get_config(VirtIODevice *vdev, uint8_t *config)
30 {
31     VHostVSock *vsock = VHOST_VSOCK(vdev);
32     struct virtio_vsock_config vsockcfg = {};
33 
34     virtio_stq_p(vdev, &vsockcfg.guest_cid, vsock->conf.guest_cid);
35     memcpy(config, &vsockcfg, sizeof(vsockcfg));
36 }
37 
38 static int vhost_vsock_set_guest_cid(VirtIODevice *vdev)
39 {
40     VHostVSockCommon *vvc = VHOST_VSOCK_COMMON(vdev);
41     VHostVSock *vsock = VHOST_VSOCK(vdev);
42     const VhostOps *vhost_ops = vvc->vhost_dev.vhost_ops;
43     int ret;
44 
45     if (!vhost_ops->vhost_vsock_set_guest_cid) {
46         return -ENOSYS;
47     }
48 
49     ret = vhost_ops->vhost_vsock_set_guest_cid(&vvc->vhost_dev,
50                                                vsock->conf.guest_cid);
51     if (ret < 0) {
52         return -errno;
53     }
54     return 0;
55 }
56 
57 static int vhost_vsock_set_running(VirtIODevice *vdev, int start)
58 {
59     VHostVSockCommon *vvc = VHOST_VSOCK_COMMON(vdev);
60     const VhostOps *vhost_ops = vvc->vhost_dev.vhost_ops;
61     int ret;
62 
63     if (!vhost_ops->vhost_vsock_set_running) {
64         return -ENOSYS;
65     }
66 
67     ret = vhost_ops->vhost_vsock_set_running(&vvc->vhost_dev, start);
68     if (ret < 0) {
69         return -errno;
70     }
71     return 0;
72 }
73 
74 
75 static void vhost_vsock_set_status(VirtIODevice *vdev, uint8_t status)
76 {
77     VHostVSockCommon *vvc = VHOST_VSOCK_COMMON(vdev);
78     bool should_start = status & VIRTIO_CONFIG_S_DRIVER_OK;
79     int ret;
80 
81     if (!vdev->vm_running) {
82         should_start = false;
83     }
84 
85     if (vvc->vhost_dev.started == should_start) {
86         return;
87     }
88 
89     if (should_start) {
90         ret = vhost_vsock_common_start(vdev);
91         if (ret < 0) {
92             return;
93         }
94 
95         ret = vhost_vsock_set_running(vdev, 1);
96         if (ret < 0) {
97             vhost_vsock_common_stop(vdev);
98             error_report("Error starting vhost vsock: %d", -ret);
99             return;
100         }
101     } else {
102         ret = vhost_vsock_set_running(vdev, 0);
103         if (ret < 0) {
104             error_report("vhost vsock set running failed: %d", ret);
105             return;
106         }
107 
108         vhost_vsock_common_stop(vdev);
109     }
110 }
111 
112 static uint64_t vhost_vsock_get_features(VirtIODevice *vdev,
113                                          uint64_t requested_features,
114                                          Error **errp)
115 {
116     VHostVSockCommon *vvc = VHOST_VSOCK_COMMON(vdev);
117 
118     virtio_add_feature(&requested_features, VIRTIO_VSOCK_F_SEQPACKET);
119     return vhost_get_features(&vvc->vhost_dev, feature_bits,
120                                 requested_features);
121 }
122 
123 static const VMStateDescription vmstate_virtio_vhost_vsock = {
124     .name = "virtio-vhost_vsock",
125     .minimum_version_id = VHOST_VSOCK_SAVEVM_VERSION,
126     .version_id = VHOST_VSOCK_SAVEVM_VERSION,
127     .fields = (VMStateField[]) {
128         VMSTATE_VIRTIO_DEVICE,
129         VMSTATE_END_OF_LIST()
130     },
131     .pre_save = vhost_vsock_common_pre_save,
132     .post_load = vhost_vsock_common_post_load,
133 };
134 
135 static void vhost_vsock_device_realize(DeviceState *dev, Error **errp)
136 {
137     VHostVSockCommon *vvc = VHOST_VSOCK_COMMON(dev);
138     VirtIODevice *vdev = VIRTIO_DEVICE(dev);
139     VHostVSock *vsock = VHOST_VSOCK(dev);
140     int vhostfd;
141     int ret;
142 
143     /* Refuse to use reserved CID numbers */
144     if (vsock->conf.guest_cid <= 2) {
145         error_setg(errp, "guest-cid property must be greater than 2");
146         return;
147     }
148 
149     if (vsock->conf.guest_cid > UINT32_MAX) {
150         error_setg(errp, "guest-cid property must be a 32-bit number");
151         return;
152     }
153 
154     if (vsock->conf.vhostfd) {
155         vhostfd = monitor_fd_param(monitor_cur(), vsock->conf.vhostfd, errp);
156         if (vhostfd == -1) {
157             error_prepend(errp, "vhost-vsock: unable to parse vhostfd: ");
158             return;
159         }
160 
161         ret = qemu_try_set_nonblock(vhostfd);
162         if (ret < 0) {
163             error_setg_errno(errp, -ret,
164                              "vhost-vsock: unable to set non-blocking mode");
165             return;
166         }
167     } else {
168         vhostfd = open("/dev/vhost-vsock", O_RDWR);
169         if (vhostfd < 0) {
170             error_setg_errno(errp, errno,
171                              "vhost-vsock: failed to open vhost device");
172             return;
173         }
174 
175         qemu_set_nonblock(vhostfd);
176     }
177 
178     vhost_vsock_common_realize(vdev, "vhost-vsock");
179 
180     ret = vhost_dev_init(&vvc->vhost_dev, (void *)(uintptr_t)vhostfd,
181                          VHOST_BACKEND_TYPE_KERNEL, 0, errp);
182     if (ret < 0) {
183         goto err_virtio;
184     }
185 
186     ret = vhost_vsock_set_guest_cid(vdev);
187     if (ret < 0) {
188         error_setg_errno(errp, -ret, "vhost-vsock: unable to set guest cid");
189         goto err_vhost_dev;
190     }
191 
192     return;
193 
194 err_vhost_dev:
195     vhost_dev_cleanup(&vvc->vhost_dev);
196     /* vhost_dev_cleanup() closes the vhostfd passed to vhost_dev_init() */
197     vhostfd = -1;
198 err_virtio:
199     vhost_vsock_common_unrealize(vdev);
200     if (vhostfd >= 0) {
201         close(vhostfd);
202     }
203     return;
204 }
205 
206 static void vhost_vsock_device_unrealize(DeviceState *dev)
207 {
208     VHostVSockCommon *vvc = VHOST_VSOCK_COMMON(dev);
209     VirtIODevice *vdev = VIRTIO_DEVICE(dev);
210 
211     /* This will stop vhost backend if appropriate. */
212     vhost_vsock_set_status(vdev, 0);
213 
214     vhost_dev_cleanup(&vvc->vhost_dev);
215     vhost_vsock_common_unrealize(vdev);
216 }
217 
218 static Property vhost_vsock_properties[] = {
219     DEFINE_PROP_UINT64("guest-cid", VHostVSock, conf.guest_cid, 0),
220     DEFINE_PROP_STRING("vhostfd", VHostVSock, conf.vhostfd),
221     DEFINE_PROP_END_OF_LIST(),
222 };
223 
224 static void vhost_vsock_class_init(ObjectClass *klass, void *data)
225 {
226     DeviceClass *dc = DEVICE_CLASS(klass);
227     VirtioDeviceClass *vdc = VIRTIO_DEVICE_CLASS(klass);
228 
229     device_class_set_props(dc, vhost_vsock_properties);
230     dc->vmsd = &vmstate_virtio_vhost_vsock;
231     vdc->realize = vhost_vsock_device_realize;
232     vdc->unrealize = vhost_vsock_device_unrealize;
233     vdc->get_features = vhost_vsock_get_features;
234     vdc->get_config = vhost_vsock_get_config;
235     vdc->set_status = vhost_vsock_set_status;
236 }
237 
238 static const TypeInfo vhost_vsock_info = {
239     .name = TYPE_VHOST_VSOCK,
240     .parent = TYPE_VHOST_VSOCK_COMMON,
241     .instance_size = sizeof(VHostVSock),
242     .class_init = vhost_vsock_class_init,
243 };
244 
245 static void vhost_vsock_register_types(void)
246 {
247     type_register_static(&vhost_vsock_info);
248 }
249 
250 type_init(vhost_vsock_register_types)
251