]>
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; | |
1e0a84ea CL |
32 | bool started; |
33 | } VhostVDPAState; | |
34 | ||
35 | const int vdpa_feature_bits[] = { | |
36 | VIRTIO_F_NOTIFY_ON_EMPTY, | |
37 | VIRTIO_RING_F_INDIRECT_DESC, | |
38 | VIRTIO_RING_F_EVENT_IDX, | |
39 | VIRTIO_F_ANY_LAYOUT, | |
40 | VIRTIO_F_VERSION_1, | |
41 | VIRTIO_NET_F_CSUM, | |
42 | VIRTIO_NET_F_GUEST_CSUM, | |
43 | VIRTIO_NET_F_GSO, | |
44 | VIRTIO_NET_F_GUEST_TSO4, | |
45 | VIRTIO_NET_F_GUEST_TSO6, | |
46 | VIRTIO_NET_F_GUEST_ECN, | |
47 | VIRTIO_NET_F_GUEST_UFO, | |
48 | VIRTIO_NET_F_HOST_TSO4, | |
49 | VIRTIO_NET_F_HOST_TSO6, | |
50 | VIRTIO_NET_F_HOST_ECN, | |
51 | VIRTIO_NET_F_HOST_UFO, | |
52 | VIRTIO_NET_F_MRG_RXBUF, | |
53 | VIRTIO_NET_F_MTU, | |
54 | VIRTIO_F_IOMMU_PLATFORM, | |
55 | VIRTIO_F_RING_PACKED, | |
0145c393 AM |
56 | VIRTIO_NET_F_RSS, |
57 | VIRTIO_NET_F_HASH_REPORT, | |
1e0a84ea | 58 | VIRTIO_NET_F_GUEST_ANNOUNCE, |
9aa47edd | 59 | VIRTIO_NET_F_STATUS, |
1e0a84ea CL |
60 | VHOST_INVALID_FEATURE_BIT |
61 | }; | |
62 | ||
63 | VHostNetState *vhost_vdpa_get_vhost_net(NetClientState *nc) | |
64 | { | |
65 | VhostVDPAState *s = DO_UPCAST(VhostVDPAState, nc, nc); | |
66 | assert(nc->info->type == NET_CLIENT_DRIVER_VHOST_VDPA); | |
67 | return s->vhost_net; | |
68 | } | |
69 | ||
1e0a84ea CL |
70 | static int vhost_vdpa_net_check_device_id(struct vhost_net *net) |
71 | { | |
72 | uint32_t device_id; | |
73 | int ret; | |
74 | struct vhost_dev *hdev; | |
75 | ||
76 | hdev = (struct vhost_dev *)&net->dev; | |
77 | ret = hdev->vhost_ops->vhost_get_device_id(hdev, &device_id); | |
78 | if (device_id != VIRTIO_ID_NET) { | |
79 | return -ENOTSUP; | |
80 | } | |
81 | return ret; | |
82 | } | |
83 | ||
1e0a84ea CL |
84 | static int vhost_vdpa_add(NetClientState *ncs, void *be) |
85 | { | |
86 | VhostNetOptions options; | |
87 | struct vhost_net *net = NULL; | |
88 | VhostVDPAState *s; | |
89 | int ret; | |
90 | ||
91 | options.backend_type = VHOST_BACKEND_TYPE_VDPA; | |
92 | assert(ncs->info->type == NET_CLIENT_DRIVER_VHOST_VDPA); | |
93 | s = DO_UPCAST(VhostVDPAState, nc, ncs); | |
94 | options.net_backend = ncs; | |
95 | options.opaque = be; | |
96 | options.busyloop_timeout = 0; | |
6a756d14 | 97 | options.nvqs = 2; |
1e0a84ea CL |
98 | |
99 | net = vhost_net_init(&options); | |
100 | if (!net) { | |
101 | error_report("failed to init vhost_net for queue"); | |
102 | goto err; | |
103 | } | |
1e0a84ea CL |
104 | s->vhost_net = net; |
105 | ret = vhost_vdpa_net_check_device_id(net); | |
106 | if (ret) { | |
107 | goto err; | |
108 | } | |
109 | return 0; | |
110 | err: | |
111 | if (net) { | |
112 | vhost_net_cleanup(net); | |
ab36edcf | 113 | g_free(net); |
1e0a84ea | 114 | } |
1e0a84ea CL |
115 | return -1; |
116 | } | |
117 | ||
118 | static void vhost_vdpa_cleanup(NetClientState *nc) | |
119 | { | |
120 | VhostVDPAState *s = DO_UPCAST(VhostVDPAState, nc, nc); | |
121 | ||
122 | if (s->vhost_net) { | |
123 | vhost_net_cleanup(s->vhost_net); | |
124 | g_free(s->vhost_net); | |
125 | s->vhost_net = NULL; | |
126 | } | |
57b3a7d8 CL |
127 | if (s->vhost_vdpa.device_fd >= 0) { |
128 | qemu_close(s->vhost_vdpa.device_fd); | |
129 | s->vhost_vdpa.device_fd = -1; | |
130 | } | |
1e0a84ea CL |
131 | } |
132 | ||
133 | static bool vhost_vdpa_has_vnet_hdr(NetClientState *nc) | |
134 | { | |
135 | assert(nc->info->type == NET_CLIENT_DRIVER_VHOST_VDPA); | |
136 | ||
137 | return true; | |
138 | } | |
139 | ||
140 | static bool vhost_vdpa_has_ufo(NetClientState *nc) | |
141 | { | |
142 | assert(nc->info->type == NET_CLIENT_DRIVER_VHOST_VDPA); | |
143 | VhostVDPAState *s = DO_UPCAST(VhostVDPAState, nc, nc); | |
144 | uint64_t features = 0; | |
145 | features |= (1ULL << VIRTIO_NET_F_HOST_UFO); | |
146 | features = vhost_net_get_features(s->vhost_net, features); | |
147 | return !!(features & (1ULL << VIRTIO_NET_F_HOST_UFO)); | |
148 | ||
149 | } | |
150 | ||
151 | static NetClientInfo net_vhost_vdpa_info = { | |
152 | .type = NET_CLIENT_DRIVER_VHOST_VDPA, | |
153 | .size = sizeof(VhostVDPAState), | |
154 | .cleanup = vhost_vdpa_cleanup, | |
155 | .has_vnet_hdr = vhost_vdpa_has_vnet_hdr, | |
156 | .has_ufo = vhost_vdpa_has_ufo, | |
157 | }; | |
158 | ||
159 | static int net_vhost_vdpa_init(NetClientState *peer, const char *device, | |
160 | const char *name, const char *vhostdev) | |
161 | { | |
162 | NetClientState *nc = NULL; | |
163 | VhostVDPAState *s; | |
164 | int vdpa_device_fd = -1; | |
165 | int ret = 0; | |
166 | assert(name); | |
167 | nc = qemu_new_net_client(&net_vhost_vdpa_info, peer, device, name); | |
56e6f594 | 168 | snprintf(nc->info_str, sizeof(nc->info_str), TYPE_VHOST_VDPA); |
1e0a84ea CL |
169 | nc->queue_index = 0; |
170 | s = DO_UPCAST(VhostVDPAState, nc, nc); | |
448058aa | 171 | vdpa_device_fd = qemu_open_old(vhostdev, O_RDWR); |
1e0a84ea CL |
172 | if (vdpa_device_fd == -1) { |
173 | return -errno; | |
174 | } | |
175 | s->vhost_vdpa.device_fd = vdpa_device_fd; | |
176 | ret = vhost_vdpa_add(nc, (void *)&s->vhost_vdpa); | |
177 | assert(s->vhost_net); | |
178 | return ret; | |
179 | } | |
180 | ||
181 | static int net_vhost_check_net(void *opaque, QemuOpts *opts, Error **errp) | |
182 | { | |
183 | const char *name = opaque; | |
184 | const char *driver, *netdev; | |
185 | ||
186 | driver = qemu_opt_get(opts, "driver"); | |
187 | netdev = qemu_opt_get(opts, "netdev"); | |
188 | if (!driver || !netdev) { | |
189 | return 0; | |
190 | } | |
191 | if (strcmp(netdev, name) == 0 && | |
192 | !g_str_has_prefix(driver, "virtio-net-")) { | |
193 | error_setg(errp, "vhost-vdpa requires frontend driver virtio-net-*"); | |
194 | return -1; | |
195 | } | |
196 | return 0; | |
197 | } | |
198 | ||
199 | int net_init_vhost_vdpa(const Netdev *netdev, const char *name, | |
200 | NetClientState *peer, Error **errp) | |
201 | { | |
202 | const NetdevVhostVDPAOptions *opts; | |
203 | ||
204 | assert(netdev->type == NET_CLIENT_DRIVER_VHOST_VDPA); | |
205 | opts = &netdev->u.vhost_vdpa; | |
206 | /* verify net frontend */ | |
207 | if (qemu_opts_foreach(qemu_find_opts("device"), net_vhost_check_net, | |
208 | (char *)name, errp)) { | |
209 | return -1; | |
210 | } | |
211 | return net_vhost_vdpa_init(peer, TYPE_VHOST_VDPA, name, opts->vhostdev); | |
212 | } |