xref: /openbmc/qemu/net/vhost-vdpa.c (revision 1f42e246)
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,
58      VHOST_INVALID_FEATURE_BIT
59  };
60  
61  VHostNetState *vhost_vdpa_get_vhost_net(NetClientState *nc)
62  {
63      VhostVDPAState *s = DO_UPCAST(VhostVDPAState, nc, nc);
64      assert(nc->info->type == NET_CLIENT_DRIVER_VHOST_VDPA);
65      return s->vhost_net;
66  }
67  
68  uint64_t vhost_vdpa_get_acked_features(NetClientState *nc)
69  {
70      VhostVDPAState *s = DO_UPCAST(VhostVDPAState, nc, nc);
71      assert(nc->info->type == NET_CLIENT_DRIVER_VHOST_VDPA);
72      s->acked_features = vhost_net_get_acked_features(s->vhost_net);
73  
74      return s->acked_features;
75  }
76  
77  static int vhost_vdpa_net_check_device_id(struct vhost_net *net)
78  {
79      uint32_t device_id;
80      int ret;
81      struct vhost_dev *hdev;
82  
83      hdev = (struct vhost_dev *)&net->dev;
84      ret = hdev->vhost_ops->vhost_get_device_id(hdev, &device_id);
85      if (device_id != VIRTIO_ID_NET) {
86          return -ENOTSUP;
87      }
88      return ret;
89  }
90  
91  static void vhost_vdpa_del(NetClientState *ncs)
92  {
93      VhostVDPAState *s;
94      assert(ncs->info->type == NET_CLIENT_DRIVER_VHOST_VDPA);
95      s = DO_UPCAST(VhostVDPAState, nc, ncs);
96      if (s->vhost_net) {
97          vhost_net_cleanup(s->vhost_net);
98      }
99  }
100  
101  static int vhost_vdpa_add(NetClientState *ncs, void *be)
102  {
103      VhostNetOptions options;
104      struct vhost_net *net = NULL;
105      VhostVDPAState *s;
106      int ret;
107  
108      options.backend_type = VHOST_BACKEND_TYPE_VDPA;
109      assert(ncs->info->type == NET_CLIENT_DRIVER_VHOST_VDPA);
110      s = DO_UPCAST(VhostVDPAState, nc, ncs);
111      options.net_backend = ncs;
112      options.opaque      = be;
113      options.busyloop_timeout = 0;
114  
115      net = vhost_net_init(&options);
116      if (!net) {
117          error_report("failed to init vhost_net for queue");
118          goto err;
119      }
120      if (s->vhost_net) {
121          vhost_net_cleanup(s->vhost_net);
122          g_free(s->vhost_net);
123      }
124      s->vhost_net = net;
125      ret = vhost_vdpa_net_check_device_id(net);
126      if (ret) {
127          goto err;
128      }
129      return 0;
130  err:
131      if (net) {
132          vhost_net_cleanup(net);
133      }
134      vhost_vdpa_del(ncs);
135      return -1;
136  }
137  
138  static void vhost_vdpa_cleanup(NetClientState *nc)
139  {
140      VhostVDPAState *s = DO_UPCAST(VhostVDPAState, nc, nc);
141  
142      if (s->vhost_net) {
143          vhost_net_cleanup(s->vhost_net);
144          g_free(s->vhost_net);
145          s->vhost_net = NULL;
146      }
147  }
148  
149  static bool vhost_vdpa_has_vnet_hdr(NetClientState *nc)
150  {
151      assert(nc->info->type == NET_CLIENT_DRIVER_VHOST_VDPA);
152  
153      return true;
154  }
155  
156  static bool vhost_vdpa_has_ufo(NetClientState *nc)
157  {
158      assert(nc->info->type == NET_CLIENT_DRIVER_VHOST_VDPA);
159      VhostVDPAState *s = DO_UPCAST(VhostVDPAState, nc, nc);
160      uint64_t features = 0;
161      features |= (1ULL << VIRTIO_NET_F_HOST_UFO);
162      features = vhost_net_get_features(s->vhost_net, features);
163      return !!(features & (1ULL << VIRTIO_NET_F_HOST_UFO));
164  
165  }
166  
167  static NetClientInfo net_vhost_vdpa_info = {
168          .type = NET_CLIENT_DRIVER_VHOST_VDPA,
169          .size = sizeof(VhostVDPAState),
170          .cleanup = vhost_vdpa_cleanup,
171          .has_vnet_hdr = vhost_vdpa_has_vnet_hdr,
172          .has_ufo = vhost_vdpa_has_ufo,
173  };
174  
175  static int net_vhost_vdpa_init(NetClientState *peer, const char *device,
176                                 const char *name, const char *vhostdev)
177  {
178      NetClientState *nc = NULL;
179      VhostVDPAState *s;
180      int vdpa_device_fd = -1;
181      int ret = 0;
182      assert(name);
183      nc = qemu_new_net_client(&net_vhost_vdpa_info, peer, device, name);
184      snprintf(nc->info_str, sizeof(nc->info_str), TYPE_VHOST_VDPA);
185      nc->queue_index = 0;
186      s = DO_UPCAST(VhostVDPAState, nc, nc);
187      vdpa_device_fd = qemu_open(vhostdev, O_RDWR);
188      if (vdpa_device_fd == -1) {
189          return -errno;
190      }
191      s->vhost_vdpa.device_fd = vdpa_device_fd;
192      ret = vhost_vdpa_add(nc, (void *)&s->vhost_vdpa);
193      assert(s->vhost_net);
194      return ret;
195  }
196  
197  static int net_vhost_check_net(void *opaque, QemuOpts *opts, Error **errp)
198  {
199      const char *name = opaque;
200      const char *driver, *netdev;
201  
202      driver = qemu_opt_get(opts, "driver");
203      netdev = qemu_opt_get(opts, "netdev");
204      if (!driver || !netdev) {
205          return 0;
206      }
207      if (strcmp(netdev, name) == 0 &&
208          !g_str_has_prefix(driver, "virtio-net-")) {
209          error_setg(errp, "vhost-vdpa requires frontend driver virtio-net-*");
210          return -1;
211      }
212      return 0;
213  }
214  
215  int net_init_vhost_vdpa(const Netdev *netdev, const char *name,
216                          NetClientState *peer, Error **errp)
217  {
218      const NetdevVhostVDPAOptions *opts;
219  
220      assert(netdev->type == NET_CLIENT_DRIVER_VHOST_VDPA);
221      opts = &netdev->u.vhost_vdpa;
222      /* verify net frontend */
223      if (qemu_opts_foreach(qemu_find_opts("device"), net_vhost_check_net,
224                            (char *)name, errp)) {
225          return -1;
226      }
227      return net_vhost_vdpa_init(peer, TYPE_VHOST_VDPA, name, opts->vhostdev);
228  }
229