FD.io VPP  v19.08-27-gf4dcae4
Vector Packet Processing
vmbus.c
Go to the documentation of this file.
1 /*
2  * Copyright (c) 2018, Microsoft Corporation.
3  * Licensed under the Apache License, Version 2.0 (the "License");
4  * you may not use this file except in compliance with the License.
5  * You may obtain a copy of the License at:
6  *
7  * http://www.apache.org/licenses/LICENSE-2.0
8  *
9  * Unless required by applicable law or agreed to in writing, software
10  * distributed under the License is distributed on an "AS IS" BASIS,
11  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12  * See the License for the specific language governing permissions and
13  * limitations under the License.
14  */
15 /*
16  * vmbus.c: Linux user space VMBus bus management.
17  */
18 
19 #include <vppinfra/linux/sysfs.h>
20 
21 #include <vlib/vlib.h>
22 #include <vlib/vmbus/vmbus.h>
23 #include <vlib/unix/unix.h>
24 
25 #include <sys/types.h>
26 #include <sys/stat.h>
27 #include <fcntl.h>
28 #include <dirent.h>
29 #include <sys/ioctl.h>
30 #include <net/if.h>
31 #include <linux/ethtool.h>
32 #include <linux/sockios.h>
33 
34 #include <uuid/uuid.h>
35 
36 static const char sysfs_vmbus_dev_path[] = "/sys/bus/vmbus/devices";
37 static const char sysfs_vmbus_drv_path[] = "/sys/bus/vmbus/drivers";
38 static const char sysfs_class_net_path[] = "/sys/class/net";
39 static const char uio_drv_name[] = "uio_hv_generic";
40 static const char netvsc_uuid[] = "f8615163-df3e-46c5-913f-f2d2f965ed0e";
41 
42 typedef struct
43 {
44  int fd;
45  void *addr;
46  size_t size;
48 
49 typedef struct
50 {
51  int fd;
54 
55 typedef struct
56 {
59 
60  /* Device File descriptor */
61  int fd;
62 
63  /* Minor device for uio device. */
65 
66  /* private data */
68 
70 
71 /* Pool of VMBUS devices. */
72 typedef struct
73 {
76 
78 
80 
81 static linux_vmbus_device_t *
83 {
85  return pool_elt_at_index (lpm->linux_vmbus_devices, h);
86 }
87 
88 uword
90 {
92  return d->private_data;
93 }
94 
95 void
97 {
99  d->private_data = private_data;
100 }
101 
104 {
106  return &d->addr;
107 }
108 
109 /* Call to allocate/initialize the vmbus subsystem.
110  This is not an init function so that users can explicitly enable
111  vmbus only when it's needed. */
113 
115 
116 /*
117  * Take VMBus address represented in standard form like:
118  * "f2c086b2-ff2e-11e8-88de-7bad0a57de05" and convert
119  * it to u8[16]
120  */
121 static uword
123 {
124  vlib_vmbus_addr_t *addr = va_arg (*args, vlib_vmbus_addr_t *);
125  uword ret = 0;
126  u8 *s;
127 
128  if (!unformat (input, "%s", &s))
129  return 0;
130 
131  if (uuid_parse ((char *) s, addr->guid) == 0)
132  ret = 1;
133 
134  vec_free (s);
135 
136  return ret;
137 }
138 
139 /* Convert bus address to standard UUID string */
140 static u8 *
141 format_vlib_vmbus_addr (u8 * s, va_list * va)
142 {
143  vlib_vmbus_addr_t *addr = va_arg (*va, vlib_vmbus_addr_t *);
144  char tmp[40];
145 
146  uuid_unparse (addr->guid, tmp);
147  return format (s, "%s", tmp);
148 }
149 
150 /* workaround for mlx bug, bring lower device up before unbind */
151 static clib_error_t *
152 vlib_vmbus_raise_lower (int fd, const char *upper_name)
153 {
154  clib_error_t *error = 0;
155  struct dirent *e;
156  struct ifreq ifr;
157  u8 *dev_net_dir;
158  DIR *dir;
159 
160  clib_memset (&ifr, 0, sizeof (ifr));
161 
162  dev_net_dir = format (0, "%s/%s%c", sysfs_class_net_path, upper_name, 0);
163 
164  dir = opendir ((char *) dev_net_dir);
165 
166  if (!dir)
167  {
168  error = clib_error_return (0, "VMBUS failed to open %s", dev_net_dir);
169  goto done;
170  }
171 
172  while ((e = readdir (dir)))
173  {
174  /* look for lower_enXXXX */
175  if (strncmp (e->d_name, "lower_", 6))
176  continue;
177 
178  strncpy (ifr.ifr_name, e->d_name + 6, IFNAMSIZ - 1);
179  break;
180  }
181  closedir (dir);
182 
183  if (!e)
184  goto done; /* no lower device */
185 
186  if (ioctl (fd, SIOCGIFFLAGS, &ifr) < 0)
187  error = clib_error_return_unix (0, "ioctl fetch intf %s flags",
188  ifr.ifr_name);
189  else if (!(ifr.ifr_flags & IFF_UP))
190  {
191  ifr.ifr_flags |= IFF_UP;
192 
193  if (ioctl (fd, SIOCSIFFLAGS, &ifr) < 0)
194  error = clib_error_return_unix (0, "ioctl set intf %s flags",
195  ifr.ifr_name);
196  }
197 done:
198  vec_free (dev_net_dir);
199  return error;
200 }
201 
202 static int
203 directory_exists (char *path)
204 {
205  struct stat s = { 0 };
206  if (stat (path, &s) == -1)
207  return 0;
208 
209  return S_ISDIR (s.st_mode);
210 }
211 
212 clib_error_t *
214 {
215  clib_error_t *error = 0;
216  u8 *dev_dir_name;
217  char *ifname = 0;
218  static int uio_new_id_needed = 1;
219  struct dirent *e;
220  struct ifreq ifr;
221  u8 *s, *driver_name;
222  DIR *dir;
223  int fd;
224 
225  dev_dir_name = format (0, "%s/%U", sysfs_vmbus_dev_path,
226  format_vlib_vmbus_addr, addr);
227  s = format (0, "%v/driver%c", dev_dir_name, 0);
228 
229  driver_name = clib_sysfs_link_to_name ((char *) s);
230  vec_reset_length (s);
231 
232  /* skip if not using the Linux kernel netvsc driver */
233  if (!driver_name || strcmp ("hv_netvsc", (char *) driver_name) != 0)
234  goto done;
235 
236  /* if uio_hv_generic is not loaded, then can't use native DPDK driver. */
237  if (!directory_exists ("/sys/module/uio_hv_generic"))
238  goto done;
239 
240  s = format (s, "%v/net%c", dev_dir_name, 0);
241  dir = opendir ((char *) s);
242  vec_reset_length (s);
243 
244  if (!dir)
245  return clib_error_return (0, "VMBUS failed to open %s", s);
246 
247  while ((e = readdir (dir)))
248  {
249  if (e->d_name[0] == '.') /* skip . and .. */
250  continue;
251 
252  ifname = strdup (e->d_name);
253  break;
254  }
255  closedir (dir);
256 
257  if (!ifname)
258  {
259  error = clib_error_return (0,
260  "VMBUS device %U eth not found",
261  format_vlib_vmbus_addr, addr);
262  goto done;
263  }
264 
265 
266  clib_memset (&ifr, 0, sizeof (ifr));
267  strncpy (ifr.ifr_name, ifname, IFNAMSIZ - 1);
268 
269  /* read up/down flags */
270  fd = socket (PF_INET, SOCK_DGRAM, 0);
271  if (fd < 0)
272  {
273  error = clib_error_return_unix (0, "socket");
274  goto done;
275  }
276 
277  if (ioctl (fd, SIOCGIFFLAGS, &ifr) < 0)
278  {
279  error = clib_error_return_unix (0, "ioctl fetch intf %s flags",
280  ifr.ifr_name);
281  close (fd);
282  goto done;
283  }
284 
285  if (ifr.ifr_flags & IFF_UP)
286  {
287  error = clib_error_return (0,
288  "Skipping VMBUS device %U as host interface %s is up",
289  format_vlib_vmbus_addr, addr, e->d_name);
290  close (fd);
291  goto done;
292  }
293 
294  /* tell uio_hv_generic about netvsc device type */
295  if (uio_new_id_needed)
296  {
297  vec_reset_length (s);
298  s = format (s, "%s/%s/new_id%c", sysfs_vmbus_drv_path, uio_drv_name, 0);
299  error = clib_sysfs_write ((char *) s, "%s", netvsc_uuid);
300 
301  if (error)
302  {
303  close (fd);
304  goto done;
305  }
306 
307  uio_new_id_needed = 0;
308 
309  }
310 
311  error = vlib_vmbus_raise_lower (fd, ifname);
312  close (fd);
313 
314  if (error)
315  goto done;
316 
317  /* prefer the simplier driver_override model */
318  vec_reset_length (s);
319  s = format (s, "%/driver_override%c", dev_dir_name, 0);
320  if (access ((char *) s, F_OK) == 0)
321  {
322  clib_sysfs_write ((char *) s, "%s", uio_drv_name);
323  }
324  else
325  {
326  vec_reset_length (s);
327 
328  s = format (s, "%v/driver/unbind%c", dev_dir_name, 0);
329  error =
330  clib_sysfs_write ((char *) s, "%U", format_vlib_vmbus_addr, addr);
331 
332  if (error)
333  goto done;
334 
335  vec_reset_length (s);
336 
337  s = format (s, "%s/%s/bind%c", sysfs_vmbus_drv_path, uio_drv_name, 0);
338  error =
339  clib_sysfs_write ((char *) s, "%U", format_vlib_vmbus_addr, addr);
340  }
341  vec_reset_length (s);
342 
343 done:
344  free (ifname);
345  vec_free (s);
346  vec_free (dev_dir_name);
347  vec_free (driver_name);
348  return error;
349 }
350 
351 static clib_error_t *
352 scan_vmbus_addr (void *arg, u8 * dev_dir_name, u8 * ignored)
353 {
354  vlib_vmbus_addr_t addr, **addrv = arg;
355  unformat_input_t input;
356  clib_error_t *err = 0;
357 
358  unformat_init_string (&input, (char *) dev_dir_name,
359  vec_len (dev_dir_name));
360 
361  if (!unformat (&input, "/sys/bus/vmbus/devices/%U",
362  unformat_vlib_vmbus_addr, &addr))
363  err = clib_error_return (0, "unformat error `%v`", dev_dir_name);
364 
365  unformat_free (&input);
366 
367  if (err)
368  return err;
369 
370  vec_add1 (*addrv, addr);
371  return 0;
372 }
373 
374 static int
375 vmbus_addr_cmp (void *v1, void *v2)
376 {
377  vlib_vmbus_addr_t *a1 = v1;
378  vlib_vmbus_addr_t *a2 = v2;
379 
380  return uuid_compare (a1->guid, a2->guid);
381 }
382 
385 {
386  vlib_vmbus_addr_t *addrs = 0;
387  clib_error_t *err;
388 
389  err =
391  &addrs, /* scan_dirs */ 0);
392  if (err)
393  {
394  vec_free (addrs);
395  return 0;
396  }
397 
399 
400  return addrs;
401 }
402 
403 clib_error_t *
405 {
407 
408  pm->vlib_main = vm;
409 
410  return 0;
411 }
412 
413 /* *INDENT-OFF* */
415 {
416  .runs_before = VLIB_INITS("unix_input_init"),
417 };
418 /* *INDENT-ON* */
419 
420 /*
421  * fd.io coding-style-patch-verification: ON
422  *
423  * Local Variables:
424  * eval: (c-set-style "gnu")
425  * End:
426  */
vlib_vmbus_addr_t * vlib_vmbus_get_addr(vlib_vmbus_dev_handle_t h)
Definition: vmbus.c:103
uword vlib_vmbus_get_private_data(vlib_vmbus_dev_handle_t h)
Definition: vmbus.c:89
static int vmbus_addr_cmp(void *v1, void *v2)
Definition: vmbus.c:375
clib_error_t * linux_vmbus_init(vlib_main_t *vm)
Definition: vmbus.c:404
clib_error_t * vlib_vmbus_bind_to_uio(vlib_vmbus_addr_t *addr)
Definition: vmbus.c:213
vlib_vmbus_addr_t addr
Definition: vmbus.c:58
#define vec_add1(V, E)
Add 1 element to end of vector (unspecified alignment).
Definition: vec.h:522
static const char uio_drv_name[]
Definition: vmbus.c:39
clib_memset(h->entries, 0, sizeof(h->entries[0])*entries)
u8 * format(u8 *s, const char *fmt,...)
Definition: format.c:424
vhost_vring_addr_t addr
Definition: vhost_user.h:147
clib_error_t * clib_sysfs_write(char *file_name, char *fmt,...)
Definition: sysfs.c:26
unsigned char u8
Definition: types.h:56
#define vec_reset_length(v)
Reset vector length to zero NULL-pointer tolerant.
clib_error_t * vmbus_bus_init(vlib_main_t *vm)
Definition: vmbus.c:34
vlib_vmbus_dev_handle_t handle
Definition: vmbus.c:57
#define VLIB_INIT_FUNCTION(x)
Definition: init.h:173
linux_vmbus_main_t linux_vmbus_main
Definition: vmbus.c:79
static int directory_exists(char *path)
Definition: vmbus.c:203
#define clib_error_return(e, args...)
Definition: error.h:99
static uword unformat_vlib_vmbus_addr(unformat_input_t *input, va_list *args)
Definition: vmbus.c:122
unsigned int u32
Definition: types.h:88
void unformat_init_string(unformat_input_t *input, char *string, int string_len)
Definition: unformat.c:1029
static const char sysfs_vmbus_drv_path[]
Definition: vmbus.c:37
vlib_main_t * vlib_main
Definition: vmbus.c:74
#define pool_elt_at_index(p, i)
Returns pointer to element at given index.
Definition: pool.h:514
struct _unformat_input_t unformat_input_t
#define clib_error_return_unix(e, args...)
Definition: error.h:102
static const char sysfs_vmbus_dev_path[]
Definition: vmbus.c:36
uword private_data
Definition: vmbus.c:67
static linux_vmbus_device_t * linux_vmbus_get_device(vlib_vmbus_dev_handle_t h)
Definition: vmbus.c:82
vlib_main_t * vm
Definition: buffer.c:312
#define vec_free(V)
Free vector&#39;s memory (no header).
Definition: vec.h:341
u32 clib_file_index
Definition: vmbus.c:52
static clib_error_t * vlib_vmbus_raise_lower(int fd, const char *upper_name)
Definition: vmbus.c:152
u32 vlib_vmbus_dev_handle_t
Definition: vmbus.h:28
u8 guid[16]
Definition: vmbus.h:26
static u8 * format_vlib_vmbus_addr(u8 *s, va_list *va)
Definition: vmbus.c:141
static const char sysfs_class_net_path[]
Definition: vmbus.c:38
vlib_vmbus_addr_t * vlib_vmbus_get_all_dev_addrs()
Definition: vmbus.c:384
clib_error_t * foreach_directory_file(char *dir_name, clib_error_t *(*f)(void *arg, u8 *path_name, u8 *file_name), void *arg, int scan_dirs)
Definition: util.c:49
#define vec_len(v)
Number of elements in vector (rvalue-only, NULL tolerant)
u64 uword
Definition: types.h:112
#define vec_sort_with_function(vec, f)
Sort a vector using the supplied element comparison function.
Definition: vec.h:980
static void unformat_free(unformat_input_t *i)
Definition: format.h:163
static clib_error_t * scan_vmbus_addr(void *arg, u8 *dev_dir_name, u8 *ignored)
Definition: vmbus.c:352
static const char netvsc_uuid[]
Definition: vmbus.c:40
linux_vmbus_device_t * linux_vmbus_devices
Definition: vmbus.c:75
void vlib_vmbus_set_private_data(vlib_vmbus_dev_handle_t h, uword private_data)
Definition: vmbus.c:96
#define VLIB_INITS(...)
Definition: init.h:344
uword unformat(unformat_input_t *i, const char *fmt,...)
Definition: unformat.c:978
u8 * clib_sysfs_link_to_name(char *link)
Definition: sysfs.c:90