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:
7 * http://www.apache.org/licenses/LICENSE-2.0
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.
16 * vmbus.c: Linux user space VMBus bus management.
19 #include <vppinfra/linux/sysfs.h>
21 #include <vlib/vlib.h>
22 #include <vlib/vmbus/vmbus.h>
23 #include <vlib/unix/unix.h>
25 #include <sys/types.h>
29 #include <sys/ioctl.h>
31 #include <linux/ethtool.h>
32 #include <linux/sockios.h>
34 #include <uuid/uuid.h>
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";
47 } linux_vmbus_region_t;
57 vlib_vmbus_dev_handle_t handle;
58 vlib_vmbus_addr_t addr;
60 /* Device File descriptor */
63 /* Minor device for uio device. */
69 } linux_vmbus_device_t;
71 /* Pool of VMBUS devices. */
74 vlib_main_t *vlib_main;
75 linux_vmbus_device_t *linux_vmbus_devices;
79 linux_vmbus_main_t linux_vmbus_main;
81 static linux_vmbus_device_t *
82 linux_vmbus_get_device (vlib_vmbus_dev_handle_t h)
84 linux_vmbus_main_t *lpm = &linux_vmbus_main;
85 return pool_elt_at_index (lpm->linux_vmbus_devices, h);
89 vlib_vmbus_get_private_data (vlib_vmbus_dev_handle_t h)
91 linux_vmbus_device_t *d = linux_vmbus_get_device (h);
92 return d->private_data;
96 vlib_vmbus_set_private_data (vlib_vmbus_dev_handle_t h, uword private_data)
98 linux_vmbus_device_t *d = linux_vmbus_get_device (h);
99 d->private_data = private_data;
103 vlib_vmbus_get_addr (vlib_vmbus_dev_handle_t h)
105 linux_vmbus_device_t *d = linux_vmbus_get_device (h);
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. */
112 clib_error_t *vmbus_bus_init (vlib_main_t * vm);
114 linux_vmbus_main_t linux_vmbus_main;
117 * Take VMBus address represented in standard form like:
118 * "f2c086b2-ff2e-11e8-88de-7bad0a57de05" and convert
122 unformat_vlib_vmbus_addr (unformat_input_t * input, va_list * args)
124 vlib_vmbus_addr_t *addr = va_arg (*args, vlib_vmbus_addr_t *);
128 if (!unformat (input, "%s", &s))
131 if (uuid_parse ((char *) s, addr->guid) == 0)
139 /* Convert bus address to standard UUID string */
141 format_vlib_vmbus_addr (u8 * s, va_list * va)
143 vlib_vmbus_addr_t *addr = va_arg (*va, vlib_vmbus_addr_t *);
146 uuid_unparse (addr->guid, tmp);
147 return format (s, "%s", tmp);
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)
154 clib_error_t *error = 0;
160 memset (&ifr, 0, sizeof (ifr));
162 dev_net_dir = format (0, "%s/%s%c", sysfs_class_net_path, upper_name, 0);
164 dir = opendir ((char *) dev_net_dir);
168 error = clib_error_return (0, "VMBUS failed to open %s", dev_net_dir);
172 while ((e = readdir (dir)))
174 /* look for lower_enXXXX */
175 if (strncmp (e->d_name, "lower_", 6))
178 strncpy (ifr.ifr_name, e->d_name + 6, IFNAMSIZ);
184 goto done; /* no lower device */
186 if (ioctl (fd, SIOCGIFFLAGS, &ifr) < 0)
187 error = clib_error_return_unix (0, "ioctl fetch intf %s flags",
189 else if (!(ifr.ifr_flags & IFF_UP))
191 ifr.ifr_flags |= IFF_UP;
193 if (ioctl (fd, SIOCSIFFLAGS, &ifr) < 0)
194 error = clib_error_return_unix (0, "ioctl set intf %s flags",
198 vec_free (dev_net_dir);
203 vlib_vmbus_bind_to_uio (vlib_vmbus_addr_t * addr)
205 clib_error_t *error = 0;
208 static int uio_new_id_needed = 1;
215 dev_dir_name = format (0, "%s/%U", sysfs_vmbus_dev_path,
216 format_vlib_vmbus_addr, addr);
217 s = format (0, "%v/driver%c", dev_dir_name, 0);
219 driver_name = clib_sysfs_link_to_name ((char *) s);
220 vec_reset_length (s);
222 /* skip if not using the Linux kernel netvsc driver */
223 if (!driver_name || strcmp ("hv_netvsc", (char *) driver_name) != 0)
226 s = format (s, "%v/net%c", dev_dir_name, 0);
227 dir = opendir ((char *) s);
228 vec_reset_length (s);
231 return clib_error_return (0, "VMBUS failed to open %s", s);
233 while ((e = readdir (dir)))
235 if (e->d_name[0] == '.') /* skip . and .. */
238 ifname = strdup (e->d_name);
245 error = clib_error_return (0,
246 "VMBUS device %U eth not found",
247 format_vlib_vmbus_addr, addr);
252 memset (&ifr, 0, sizeof (ifr));
253 strncpy (ifr.ifr_name, ifname, IFNAMSIZ);
255 /* read up/down flags */
256 fd = socket (PF_INET, SOCK_DGRAM, 0);
259 error = clib_error_return_unix (0, "socket");
263 if (ioctl (fd, SIOCGIFFLAGS, &ifr) < 0)
265 error = clib_error_return_unix (0, "ioctl fetch intf %s flags",
271 if (ifr.ifr_flags & IFF_UP)
273 error = clib_error_return (0,
274 "Skipping VMBUS device %U as host interface %s is up",
275 format_vlib_vmbus_addr, addr, e->d_name);
280 error = vlib_vmbus_raise_lower (fd, ifname);
287 /* tell uio_hv_generic about netvsc device type */
288 if (uio_new_id_needed)
290 uio_new_id_needed = 0;
292 vec_reset_length (s);
293 s = format (s, "%s/%s/new_id%c", sysfs_vmbus_drv_path, uio_drv_name, 0);
294 error = clib_sysfs_write ((char *) s, "%s", netvsc_uuid);
301 /* prefer the simplier driver_override model */
302 vec_reset_length (s);
303 s = format (s, "%/driver_override%c", dev_dir_name, 0);
304 if (access ((char *) s, F_OK) == 0)
306 clib_sysfs_write ((char *) s, "%s", uio_drv_name);
310 vec_reset_length (s);
312 s = format (s, "%v/driver/unbind%c", dev_dir_name, 0);
314 clib_sysfs_write ((char *) s, "%U", format_vlib_vmbus_addr, addr);
319 vec_reset_length (s);
321 s = format (s, "%s/%s/bind%c", sysfs_vmbus_drv_path, uio_drv_name, 0);
323 clib_sysfs_write ((char *) s, "%U", format_vlib_vmbus_addr, addr);
325 vec_reset_length (s);
330 vec_free (dev_dir_name);
331 vec_free (driver_name);
335 static clib_error_t *
336 scan_vmbus_addr (void *arg, u8 * dev_dir_name, u8 * ignored)
338 vlib_vmbus_addr_t addr, **addrv = arg;
339 unformat_input_t input;
340 clib_error_t *err = 0;
342 unformat_init_string (&input, (char *) dev_dir_name,
343 vec_len (dev_dir_name));
345 if (!unformat (&input, "/sys/bus/vmbus/devices/%U",
346 unformat_vlib_vmbus_addr, &addr))
347 err = clib_error_return (0, "unformat error `%v`", dev_dir_name);
349 unformat_free (&input);
354 vec_add1 (*addrv, addr);
359 vmbus_addr_cmp (void *v1, void *v2)
361 vlib_vmbus_addr_t *a1 = v1;
362 vlib_vmbus_addr_t *a2 = v2;
364 return uuid_compare (a1->guid, a2->guid);
368 vlib_vmbus_get_all_dev_addrs ()
370 vlib_vmbus_addr_t *addrs = 0;
374 foreach_directory_file ((char *) sysfs_vmbus_dev_path, scan_vmbus_addr,
375 &addrs, /* scan_dirs */ 0);
382 vec_sort_with_function (addrs, vmbus_addr_cmp);
388 linux_vmbus_init (vlib_main_t * vm)
390 linux_vmbus_main_t *pm = &linux_vmbus_main;
394 return vlib_call_init_function (vm, unix_input_init);
397 VLIB_INIT_FUNCTION (linux_vmbus_init);
400 * fd.io coding-style-patch-verification: ON
403 * eval: (c-set-style "gnu")