]>
Commit | Line | Data |
---|---|---|
1e0a84ea CL |
1 | /* |
2 | * vhost-vdpa.c | |
3 | * | |
4 | * Copyright(c) 2017-2018 Intel Corporation. | |
5 | * Copyright(c) 2020 Red Hat, Inc. | |
6 | * | |
7 | * This work is licensed under the terms of the GNU GPL, version 2 or later. | |
8 | * See the COPYING file in the top-level directory. | |
9 | * | |
10 | */ | |
11 | ||
12 | #include "qemu/osdep.h" | |
13 | #include "clients.h" | |
14 | #include "net/vhost_net.h" | |
15 | #include "net/vhost-vdpa.h" | |
16 | #include "hw/virtio/vhost-vdpa.h" | |
17 | #include "qemu/config-file.h" | |
18 | #include "qemu/error-report.h" | |
19 | #include "qemu/option.h" | |
20 | #include "qapi/error.h" | |
21 | #include <sys/ioctl.h> | |
22 | #include <err.h> | |
23 | #include "standard-headers/linux/virtio_net.h" | |
24 | #include "monitor/monitor.h" | |
25 | #include "hw/virtio/vhost.h" | |
26 | ||
27 | /* Todo:need to add the multiqueue support here */ | |
28 | typedef struct VhostVDPAState { | |
29 | NetClientState nc; | |
30 | struct vhost_vdpa vhost_vdpa; | |
31 | VHostNetState *vhost_net; | |
32 | uint64_t acked_features; | |
33 | bool started; | |
34 | } VhostVDPAState; | |
35 | ||
36 | const int vdpa_feature_bits[] = { | |
37 | VIRTIO_F_NOTIFY_ON_EMPTY, | |
38 | VIRTIO_RING_F_INDIRECT_DESC, | |
39 | VIRTIO_RING_F_EVENT_IDX, | |
40 | VIRTIO_F_ANY_LAYOUT, | |
41 | VIRTIO_F_VERSION_1, | |
42 | VIRTIO_NET_F_CSUM, | |
43 | VIRTIO_NET_F_GUEST_CSUM, | |
44 | VIRTIO_NET_F_GSO, | |
45 | VIRTIO_NET_F_GUEST_TSO4, | |
46 | VIRTIO_NET_F_GUEST_TSO6, | |
47 | VIRTIO_NET_F_GUEST_ECN, | |
48 | VIRTIO_NET_F_GUEST_UFO, | |
49 | VIRTIO_NET_F_HOST_TSO4, | |
50 | VIRTIO_NET_F_HOST_TSO6, | |
51 | VIRTIO_NET_F_HOST_ECN, | |
52 | VIRTIO_NET_F_HOST_UFO, | |
53 | VIRTIO_NET_F_MRG_RXBUF, | |
54 | VIRTIO_NET_F_MTU, | |
55 | VIRTIO_F_IOMMU_PLATFORM, | |
56 | VIRTIO_F_RING_PACKED, | |
57 | VIRTIO_NET_F_GUEST_ANNOUNCE, | |
9aa47edd | 58 | VIRTIO_NET_F_STATUS, |
1e0a84ea CL |
59 | VHOST_INVALID_FEATURE_BIT |
60 | }; | |
61 | ||
62 | VHostNetState *vhost_vdpa_get_vhost_net(NetClientState *nc) | |
63 | { | |
64 | VhostVDPAState *s = DO_UPCAST(VhostVDPAState, nc, nc); | |
65 | assert(nc->info->type == NET_CLIENT_DRIVER_VHOST_VDPA); | |
66 | return s->vhost_net; | |
67 | } | |
68 | ||
69 | uint64_t vhost_vdpa_get_acked_features(NetClientState *nc) | |
70 | { | |
71 | VhostVDPAState *s = DO_UPCAST(VhostVDPAState, nc, nc); | |
72 | assert(nc->info->type == NET_CLIENT_DRIVER_VHOST_VDPA); | |
73 | s->acked_features = vhost_net_get_acked_features(s->vhost_net); | |
74 | ||
75 | return s->acked_features; | |
76 | } | |
77 | ||
78 | static int vhost_vdpa_net_check_device_id(struct vhost_net *net) | |
79 | { | |
80 | uint32_t device_id; | |
81 | int ret; | |
82 | struct vhost_dev *hdev; | |
83 | ||
84 | hdev = (struct vhost_dev *)&net->dev; | |
85 | ret = hdev->vhost_ops->vhost_get_device_id(hdev, &device_id); | |
86 | if (device_id != VIRTIO_ID_NET) { | |
87 | return -ENOTSUP; | |
88 | } | |
89 | return ret; | |
90 | } | |
91 | ||
92 | static void vhost_vdpa_del(NetClientState *ncs) | |
93 | { | |
94 | VhostVDPAState *s; | |
95 | assert(ncs->info->type == NET_CLIENT_DRIVER_VHOST_VDPA); | |
96 | s = DO_UPCAST(VhostVDPAState, nc, ncs); | |
97 | if (s->vhost_net) { | |
98 | vhost_net_cleanup(s->vhost_net); | |
99 | } | |
100 | } | |
101 | ||
102 | static int vhost_vdpa_add(NetClientState *ncs, void *be) | |
103 | { | |
104 | VhostNetOptions options; | |
105 | struct vhost_net *net = NULL; | |
106 | VhostVDPAState *s; | |
107 | int ret; | |
108 | ||
109 | options.backend_type = VHOST_BACKEND_TYPE_VDPA; | |
110 | assert(ncs->info->type == NET_CLIENT_DRIVER_VHOST_VDPA); | |
111 | s = DO_UPCAST(VhostVDPAState, nc, ncs); | |
112 | options.net_backend = ncs; | |
113 | options.opaque = be; | |
114 | options.busyloop_timeout = 0; | |
115 | ||
116 | net = vhost_net_init(&options); | |
117 | if (!net) { | |
118 | error_report("failed to init vhost_net for queue"); | |
119 | goto err; | |
120 | } | |
121 | if (s->vhost_net) { | |
122 | vhost_net_cleanup(s->vhost_net); | |
123 | g_free(s->vhost_net); | |
124 | } | |
125 | s->vhost_net = net; | |
126 | ret = vhost_vdpa_net_check_device_id(net); | |
127 | if (ret) { | |
128 | goto err; | |
129 | } | |
130 | return 0; | |
131 | err: | |
132 | if (net) { | |
133 | vhost_net_cleanup(net); | |
134 | } | |
135 | vhost_vdpa_del(ncs); | |
136 | return -1; | |
137 | } | |
138 | ||
139 | static void vhost_vdpa_cleanup(NetClientState *nc) | |
140 | { | |
141 | VhostVDPAState *s = DO_UPCAST(VhostVDPAState, nc, nc); | |
142 | ||
143 | if (s->vhost_net) { | |
144 | vhost_net_cleanup(s->vhost_net); | |
145 | g_free(s->vhost_net); | |
146 | s->vhost_net = NULL; | |
147 | } | |
57b3a7d8 CL |
148 | if (s->vhost_vdpa.device_fd >= 0) { |
149 | qemu_close(s->vhost_vdpa.device_fd); | |
150 | s->vhost_vdpa.device_fd = -1; | |
151 | } | |
1e0a84ea CL |
152 | } |
153 | ||
154 | static bool vhost_vdpa_has_vnet_hdr(NetClientState *nc) | |
155 | { | |
156 | assert(nc->info->type == NET_CLIENT_DRIVER_VHOST_VDPA); | |
157 | ||
158 | return true; | |
159 | } | |
160 | ||
161 | static bool vhost_vdpa_has_ufo(NetClientState *nc) | |
162 | { | |
163 | assert(nc->info->type == NET_CLIENT_DRIVER_VHOST_VDPA); | |
164 | VhostVDPAState *s = DO_UPCAST(VhostVDPAState, nc, nc); | |
165 | uint64_t features = 0; | |
166 | features |= (1ULL << VIRTIO_NET_F_HOST_UFO); | |
167 | features = vhost_net_get_features(s->vhost_net, features); | |
168 | return !!(features & (1ULL << VIRTIO_NET_F_HOST_UFO)); | |
169 | ||
170 | } | |
171 | ||
172 | static NetClientInfo net_vhost_vdpa_info = { | |
173 | .type = NET_CLIENT_DRIVER_VHOST_VDPA, | |
174 | .size = sizeof(VhostVDPAState), | |
175 | .cleanup = vhost_vdpa_cleanup, | |
176 | .has_vnet_hdr = vhost_vdpa_has_vnet_hdr, | |
177 | .has_ufo = vhost_vdpa_has_ufo, | |
178 | }; | |
179 | ||
180 | static int net_vhost_vdpa_init(NetClientState *peer, const char *device, | |
181 | const char *name, const char *vhostdev) | |
182 | { | |
183 | NetClientState *nc = NULL; | |
184 | VhostVDPAState *s; | |
185 | int vdpa_device_fd = -1; | |
186 | int ret = 0; | |
187 | assert(name); | |
188 | nc = qemu_new_net_client(&net_vhost_vdpa_info, peer, device, name); | |
189 | snprintf(nc->info_str, sizeof(nc->info_str), TYPE_VHOST_VDPA); | |
190 | nc->queue_index = 0; | |
191 | s = DO_UPCAST(VhostVDPAState, nc, nc); | |
448058aa | 192 | vdpa_device_fd = qemu_open_old(vhostdev, O_RDWR); |
1e0a84ea CL |
193 | if (vdpa_device_fd == -1) { |
194 | return -errno; | |
195 | } | |
196 | s->vhost_vdpa.device_fd = vdpa_device_fd; | |
197 | ret = vhost_vdpa_add(nc, (void *)&s->vhost_vdpa); | |
198 | assert(s->vhost_net); | |
199 | return ret; | |
200 | } | |
201 | ||
202 | static int net_vhost_check_net(void *opaque, QemuOpts *opts, Error **errp) | |
203 | { | |
204 | const char *name = opaque; | |
205 | const char *driver, *netdev; | |
206 | ||
207 | driver = qemu_opt_get(opts, "driver"); | |
208 | netdev = qemu_opt_get(opts, "netdev"); | |
209 | if (!driver || !netdev) { | |
210 | return 0; | |
211 | } | |
212 | if (strcmp(netdev, name) == 0 && | |
213 | !g_str_has_prefix(driver, "virtio-net-")) { | |
214 | error_setg(errp, "vhost-vdpa requires frontend driver virtio-net-*"); | |
215 | return -1; | |
216 | } | |
217 | return 0; | |
218 | } | |
219 | ||
220 | int net_init_vhost_vdpa(const Netdev *netdev, const char *name, | |
221 | NetClientState *peer, Error **errp) | |
222 | { | |
223 | const NetdevVhostVDPAOptions *opts; | |
224 | ||
225 | assert(netdev->type == NET_CLIENT_DRIVER_VHOST_VDPA); | |
226 | opts = &netdev->u.vhost_vdpa; | |
227 | /* verify net frontend */ | |
228 | if (qemu_opts_foreach(qemu_find_opts("device"), net_vhost_check_net, | |
229 | (char *)name, errp)) { | |
230 | return -1; | |
231 | } | |
232 | return net_vhost_vdpa_init(peer, TYPE_VHOST_VDPA, name, opts->vhostdev); | |
233 | } |