4 * Copyright(c) 2017-2018 Intel Corporation.
5 * Copyright(c) 2020 Red Hat, Inc.
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.
12 #include "qemu/osdep.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 <linux/vhost.h>
22 #include <sys/ioctl.h>
24 #include "standard-headers/linux/virtio_net.h"
25 #include "monitor/monitor.h"
26 #include "hw/virtio/vhost.h"
28 /* Todo:need to add the multiqueue support here */
29 typedef struct VhostVDPAState
{
31 struct vhost_vdpa vhost_vdpa
;
32 VHostNetState
*vhost_net
;
36 const int vdpa_feature_bits
[] = {
37 VIRTIO_F_NOTIFY_ON_EMPTY
,
38 VIRTIO_RING_F_INDIRECT_DESC
,
39 VIRTIO_RING_F_EVENT_IDX
,
43 VIRTIO_NET_F_GUEST_CSUM
,
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
,
56 VIRTIO_NET_F_CTRL_RX_EXTRA
,
57 VIRTIO_NET_F_CTRL_VLAN
,
58 VIRTIO_NET_F_GUEST_ANNOUNCE
,
59 VIRTIO_NET_F_CTRL_MAC_ADDR
,
63 VIRTIO_F_IOMMU_PLATFORM
,
66 VIRTIO_NET_F_HASH_REPORT
,
67 VIRTIO_NET_F_GUEST_ANNOUNCE
,
69 VHOST_INVALID_FEATURE_BIT
72 VHostNetState
*vhost_vdpa_get_vhost_net(NetClientState
*nc
)
74 VhostVDPAState
*s
= DO_UPCAST(VhostVDPAState
, nc
, nc
);
75 assert(nc
->info
->type
== NET_CLIENT_DRIVER_VHOST_VDPA
);
79 static int vhost_vdpa_net_check_device_id(struct vhost_net
*net
)
83 struct vhost_dev
*hdev
;
85 hdev
= (struct vhost_dev
*)&net
->dev
;
86 ret
= hdev
->vhost_ops
->vhost_get_device_id(hdev
, &device_id
);
87 if (device_id
!= VIRTIO_ID_NET
) {
93 static int vhost_vdpa_add(NetClientState
*ncs
, void *be
,
94 int queue_pair_index
, int nvqs
)
96 VhostNetOptions options
;
97 struct vhost_net
*net
= NULL
;
101 options
.backend_type
= VHOST_BACKEND_TYPE_VDPA
;
102 assert(ncs
->info
->type
== NET_CLIENT_DRIVER_VHOST_VDPA
);
103 s
= DO_UPCAST(VhostVDPAState
, nc
, ncs
);
104 options
.net_backend
= ncs
;
106 options
.busyloop_timeout
= 0;
109 net
= vhost_net_init(&options
);
111 error_report("failed to init vhost_net for queue");
115 ret
= vhost_vdpa_net_check_device_id(net
);
121 vhost_net_cleanup(net
);
127 static void vhost_vdpa_cleanup(NetClientState
*nc
)
129 VhostVDPAState
*s
= DO_UPCAST(VhostVDPAState
, nc
, nc
);
132 vhost_net_cleanup(s
->vhost_net
);
133 g_free(s
->vhost_net
);
136 if (s
->vhost_vdpa
.device_fd
>= 0) {
137 qemu_close(s
->vhost_vdpa
.device_fd
);
138 s
->vhost_vdpa
.device_fd
= -1;
142 static bool vhost_vdpa_has_vnet_hdr(NetClientState
*nc
)
144 assert(nc
->info
->type
== NET_CLIENT_DRIVER_VHOST_VDPA
);
149 static bool vhost_vdpa_has_ufo(NetClientState
*nc
)
151 assert(nc
->info
->type
== NET_CLIENT_DRIVER_VHOST_VDPA
);
152 VhostVDPAState
*s
= DO_UPCAST(VhostVDPAState
, nc
, nc
);
153 uint64_t features
= 0;
154 features
|= (1ULL << VIRTIO_NET_F_HOST_UFO
);
155 features
= vhost_net_get_features(s
->vhost_net
, features
);
156 return !!(features
& (1ULL << VIRTIO_NET_F_HOST_UFO
));
160 static bool vhost_vdpa_check_peer_type(NetClientState
*nc
, ObjectClass
*oc
,
163 const char *driver
= object_class_get_name(oc
);
165 if (!g_str_has_prefix(driver
, "virtio-net-")) {
166 error_setg(errp
, "vhost-vdpa requires frontend driver virtio-net-*");
173 static NetClientInfo net_vhost_vdpa_info
= {
174 .type
= NET_CLIENT_DRIVER_VHOST_VDPA
,
175 .size
= sizeof(VhostVDPAState
),
176 .cleanup
= vhost_vdpa_cleanup
,
177 .has_vnet_hdr
= vhost_vdpa_has_vnet_hdr
,
178 .has_ufo
= vhost_vdpa_has_ufo
,
179 .check_peer_type
= vhost_vdpa_check_peer_type
,
182 static NetClientState
*net_vhost_vdpa_init(NetClientState
*peer
,
186 int queue_pair_index
,
190 NetClientState
*nc
= NULL
;
195 nc
= qemu_new_net_client(&net_vhost_vdpa_info
, peer
, device
,
198 nc
= qemu_new_net_control_client(&net_vhost_vdpa_info
, peer
,
201 snprintf(nc
->info_str
, sizeof(nc
->info_str
), TYPE_VHOST_VDPA
);
202 s
= DO_UPCAST(VhostVDPAState
, nc
, nc
);
204 s
->vhost_vdpa
.device_fd
= vdpa_device_fd
;
205 s
->vhost_vdpa
.index
= queue_pair_index
;
206 ret
= vhost_vdpa_add(nc
, (void *)&s
->vhost_vdpa
, queue_pair_index
, nvqs
);
208 qemu_del_net_client(nc
);
214 static int vhost_vdpa_get_max_queue_pairs(int fd
, int *has_cvq
, Error
**errp
)
216 unsigned long config_size
= offsetof(struct vhost_vdpa_config
, buf
);
217 struct vhost_vdpa_config
*config
;
218 __virtio16
*max_queue_pairs
;
222 ret
= ioctl(fd
, VHOST_GET_FEATURES
, &features
);
224 error_setg(errp
, "Fail to query features from vhost-vDPA device");
228 if (features
& (1 << VIRTIO_NET_F_CTRL_VQ
)) {
234 if (features
& (1 << VIRTIO_NET_F_MQ
)) {
235 config
= g_malloc0(config_size
+ sizeof(*max_queue_pairs
));
236 config
->off
= offsetof(struct virtio_net_config
, max_virtqueue_pairs
);
237 config
->len
= sizeof(*max_queue_pairs
);
239 ret
= ioctl(fd
, VHOST_VDPA_GET_CONFIG
, config
);
241 error_setg(errp
, "Fail to get config from vhost-vDPA device");
245 max_queue_pairs
= (__virtio16
*)&config
->buf
;
247 return lduw_le_p(max_queue_pairs
);
253 int net_init_vhost_vdpa(const Netdev
*netdev
, const char *name
,
254 NetClientState
*peer
, Error
**errp
)
256 const NetdevVhostVDPAOptions
*opts
;
258 NetClientState
**ncs
, *nc
;
259 int queue_pairs
, i
, has_cvq
= 0;
261 assert(netdev
->type
== NET_CLIENT_DRIVER_VHOST_VDPA
);
262 opts
= &netdev
->u
.vhost_vdpa
;
264 vdpa_device_fd
= qemu_open_old(opts
->vhostdev
, O_RDWR
);
265 if (vdpa_device_fd
== -1) {
269 queue_pairs
= vhost_vdpa_get_max_queue_pairs(vdpa_device_fd
,
271 if (queue_pairs
< 0) {
272 qemu_close(vdpa_device_fd
);
276 ncs
= g_malloc0(sizeof(*ncs
) * queue_pairs
);
278 for (i
= 0; i
< queue_pairs
; i
++) {
279 ncs
[i
] = net_vhost_vdpa_init(peer
, TYPE_VHOST_VDPA
, name
,
280 vdpa_device_fd
, i
, 2, true);
286 nc
= net_vhost_vdpa_init(peer
, TYPE_VHOST_VDPA
, name
,
287 vdpa_device_fd
, i
, 1, false);
297 qemu_del_net_client(ncs
[0]);
299 qemu_close(vdpa_device_fd
);