blob: d0efbd748ef3730bfcf7b08059764b05d2cabc7a [file] [log] [blame]
Stephen Hemminger6fbef232018-10-15 12:52:30 -07001/*
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
36static const char sysfs_vmbus_dev_path[] = "/sys/bus/vmbus/devices";
37static const char sysfs_vmbus_drv_path[] = "/sys/bus/vmbus/drivers";
38static const char sysfs_class_net_path[] = "/sys/class/net";
39static const char uio_drv_name[] = "uio_hv_generic";
40static const char netvsc_uuid[] = "f8615163-df3e-46c5-913f-f2d2f965ed0e";
41
42typedef struct
43{
44 int fd;
45 void *addr;
46 size_t size;
47} linux_vmbus_region_t;
48
49typedef struct
50{
51 int fd;
52 u32 clib_file_index;
53} linux_vmbus_irq_t;
54
55typedef struct
56{
57 vlib_vmbus_dev_handle_t handle;
58 vlib_vmbus_addr_t addr;
59
60 /* Device File descriptor */
61 int fd;
62
63 /* Minor device for uio device. */
64 u32 uio_minor;
65
66 /* private data */
67 uword private_data;
68
69} linux_vmbus_device_t;
70
71/* Pool of VMBUS devices. */
72typedef struct
73{
74 vlib_main_t *vlib_main;
75 linux_vmbus_device_t *linux_vmbus_devices;
76
77} linux_vmbus_main_t;
78
79linux_vmbus_main_t linux_vmbus_main;
80
81static linux_vmbus_device_t *
82linux_vmbus_get_device (vlib_vmbus_dev_handle_t h)
83{
84 linux_vmbus_main_t *lpm = &linux_vmbus_main;
85 return pool_elt_at_index (lpm->linux_vmbus_devices, h);
86}
87
88uword
89vlib_vmbus_get_private_data (vlib_vmbus_dev_handle_t h)
90{
91 linux_vmbus_device_t *d = linux_vmbus_get_device (h);
92 return d->private_data;
93}
94
95void
96vlib_vmbus_set_private_data (vlib_vmbus_dev_handle_t h, uword private_data)
97{
98 linux_vmbus_device_t *d = linux_vmbus_get_device (h);
99 d->private_data = private_data;
100}
101
102vlib_vmbus_addr_t *
103vlib_vmbus_get_addr (vlib_vmbus_dev_handle_t h)
104{
105 linux_vmbus_device_t *d = linux_vmbus_get_device (h);
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. */
112clib_error_t *vmbus_bus_init (vlib_main_t * vm);
113
114linux_vmbus_main_t linux_vmbus_main;
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 */
121static uword
122unformat_vlib_vmbus_addr (unformat_input_t * input, va_list * args)
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 */
140static u8 *
141format_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 */
151static clib_error_t *
152vlib_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
Stephen Hemminger29f0a5d2019-01-16 10:08:39 -0800160 clib_memset (&ifr, 0, sizeof (ifr));
Stephen Hemminger6fbef232018-10-15 12:52:30 -0700161
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
Stephen Hemminger29f0a5d2019-01-16 10:08:39 -0800178 strncpy (ifr.ifr_name, e->d_name + 6, IFNAMSIZ - 1);
Stephen Hemminger6fbef232018-10-15 12:52:30 -0700179 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 }
197done:
198 vec_free (dev_net_dir);
199 return error;
200}
201
Stephen Hemminger485710e2019-03-12 19:06:32 -0700202static int
203directory_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
Stephen Hemminger6fbef232018-10-15 12:52:30 -0700212clib_error_t *
213vlib_vmbus_bind_to_uio (vlib_vmbus_addr_t * addr)
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
Stephen Hemminger485710e2019-03-12 19:06:32 -0700236 /* 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
Stephen Hemminger6fbef232018-10-15 12:52:30 -0700240 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
Stephen Hemminger29f0a5d2019-01-16 10:08:39 -0800266 clib_memset (&ifr, 0, sizeof (ifr));
267 strncpy (ifr.ifr_name, ifname, IFNAMSIZ - 1);
Stephen Hemminger6fbef232018-10-15 12:52:30 -0700268
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
Stephen Hemminger6fbef232018-10-15 12:52:30 -0700294 /* tell uio_hv_generic about netvsc device type */
295 if (uio_new_id_needed)
296 {
Stephen Hemminger6fbef232018-10-15 12:52:30 -0700297 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)
Dave Baracha5160d72019-03-13 15:29:15 -0400302 {
303 close (fd);
304 goto done;
305 }
Stephen Hemminger6fbef232018-10-15 12:52:30 -0700306
Matthew Smithf6266b52019-03-04 09:43:45 -0600307 uio_new_id_needed = 0;
308
Stephen Hemminger6fbef232018-10-15 12:52:30 -0700309 }
310
Stephen Hemminger485710e2019-03-12 19:06:32 -0700311 error = vlib_vmbus_raise_lower (fd, ifname);
312 close (fd);
313
314 if (error)
315 goto done;
316
Stephen Hemminger6fbef232018-10-15 12:52:30 -0700317 /* 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
343done:
344 free (ifname);
345 vec_free (s);
346 vec_free (dev_dir_name);
347 vec_free (driver_name);
348 return error;
349}
350
351static clib_error_t *
352scan_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
374static int
375vmbus_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
383vlib_vmbus_addr_t *
384vlib_vmbus_get_all_dev_addrs ()
385{
386 vlib_vmbus_addr_t *addrs = 0;
387 clib_error_t *err;
388
389 err =
390 foreach_directory_file ((char *) sysfs_vmbus_dev_path, scan_vmbus_addr,
391 &addrs, /* scan_dirs */ 0);
392 if (err)
393 {
394 vec_free (addrs);
395 return 0;
396 }
397
398 vec_sort_with_function (addrs, vmbus_addr_cmp);
399
400 return addrs;
401}
402
403clib_error_t *
404linux_vmbus_init (vlib_main_t * vm)
405{
406 linux_vmbus_main_t *pm = &linux_vmbus_main;
407
408 pm->vlib_main = vm;
409
410 return vlib_call_init_function (vm, unix_input_init);
411}
412
413VLIB_INIT_FUNCTION (linux_vmbus_init);
414
415/*
416 * fd.io coding-style-patch-verification: ON
417 *
418 * Local Variables:
419 * eval: (c-set-style "gnu")
420 * End:
421 */