blob: 541c88b65377a993d4892cdad79d1bc45fb1f6aa [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
Stephen Hemminger6fbef232018-10-15 12:52:30 -070034static const char sysfs_vmbus_dev_path[] = "/sys/bus/vmbus/devices";
35static const char sysfs_vmbus_drv_path[] = "/sys/bus/vmbus/drivers";
36static const char sysfs_class_net_path[] = "/sys/class/net";
37static const char uio_drv_name[] = "uio_hv_generic";
38static const char netvsc_uuid[] = "f8615163-df3e-46c5-913f-f2d2f965ed0e";
39
40typedef struct
41{
42 int fd;
43 void *addr;
44 size_t size;
45} linux_vmbus_region_t;
46
47typedef struct
48{
49 int fd;
50 u32 clib_file_index;
51} linux_vmbus_irq_t;
52
53typedef struct
54{
55 vlib_vmbus_dev_handle_t handle;
56 vlib_vmbus_addr_t addr;
57
58 /* Device File descriptor */
59 int fd;
60
61 /* Minor device for uio device. */
62 u32 uio_minor;
63
64 /* private data */
65 uword private_data;
66
67} linux_vmbus_device_t;
68
69/* Pool of VMBUS devices. */
70typedef struct
71{
72 vlib_main_t *vlib_main;
73 linux_vmbus_device_t *linux_vmbus_devices;
74
75} linux_vmbus_main_t;
76
77linux_vmbus_main_t linux_vmbus_main;
78
79static linux_vmbus_device_t *
80linux_vmbus_get_device (vlib_vmbus_dev_handle_t h)
81{
82 linux_vmbus_main_t *lpm = &linux_vmbus_main;
83 return pool_elt_at_index (lpm->linux_vmbus_devices, h);
84}
85
86uword
87vlib_vmbus_get_private_data (vlib_vmbus_dev_handle_t h)
88{
89 linux_vmbus_device_t *d = linux_vmbus_get_device (h);
90 return d->private_data;
91}
92
93void
94vlib_vmbus_set_private_data (vlib_vmbus_dev_handle_t h, uword private_data)
95{
96 linux_vmbus_device_t *d = linux_vmbus_get_device (h);
97 d->private_data = private_data;
98}
99
100vlib_vmbus_addr_t *
101vlib_vmbus_get_addr (vlib_vmbus_dev_handle_t h)
102{
103 linux_vmbus_device_t *d = linux_vmbus_get_device (h);
104 return &d->addr;
105}
106
107/* Call to allocate/initialize the vmbus subsystem.
108 This is not an init function so that users can explicitly enable
109 vmbus only when it's needed. */
110clib_error_t *vmbus_bus_init (vlib_main_t * vm);
111
112linux_vmbus_main_t linux_vmbus_main;
113
114/*
115 * Take VMBus address represented in standard form like:
116 * "f2c086b2-ff2e-11e8-88de-7bad0a57de05" and convert
117 * it to u8[16]
118 */
Vladimir Ratnikov98227292020-11-11 08:00:48 -0500119uword
120unformat_vlib_vmbus_addr (unformat_input_t *input, va_list *args)
Stephen Hemminger6fbef232018-10-15 12:52:30 -0700121{
122 vlib_vmbus_addr_t *addr = va_arg (*args, vlib_vmbus_addr_t *);
123 uword ret = 0;
Damjan Marion37066362023-07-28 20:06:09 +0200124 u8 *s = 0;
Stephen Hemminger6fbef232018-10-15 12:52:30 -0700125
Damjan Marion37066362023-07-28 20:06:09 +0200126 if (!unformat (input, "%U", unformat_token, "a-zA-Z0-9-", &s))
Stephen Hemminger6fbef232018-10-15 12:52:30 -0700127 return 0;
128
Damjan Marion37066362023-07-28 20:06:09 +0200129 if (vec_len (s) != 36)
130 goto fail;
Stephen Hemminger6fbef232018-10-15 12:52:30 -0700131
Damjan Marion37066362023-07-28 20:06:09 +0200132 if (s[8] != '-' || s[13] != '-' || s[18] != '-' || s[23] != '-')
133 goto fail;
134
135 clib_memmove (s + 8, s + 9, 4);
136 clib_memmove (s + 12, s + 14, 4);
137 clib_memmove (s + 16, s + 19, 4);
138 clib_memmove (s + 20, s + 24, 12);
139
140 for (int i = 0; i < 32; i++)
141 if (s[i] >= '0' && s[i] <= '9')
142 s[i] -= '0';
143 else if (s[i] >= 'A' && s[i] <= 'F')
144 s[i] -= 'A' - 10;
145 else if (s[i] >= 'a' && s[i] <= 'f')
146 s[i] -= 'a' - 10;
147 else
148 goto fail;
149
150 for (int i = 0; i < 16; i++)
151 addr->guid[i] = s[2 * i] * 16 + s[2 * i + 1];
152
153 ret = 1;
154
155fail:
Stephen Hemminger6fbef232018-10-15 12:52:30 -0700156 vec_free (s);
Stephen Hemminger6fbef232018-10-15 12:52:30 -0700157 return ret;
158}
159
160/* Convert bus address to standard UUID string */
Vladimir Ratnikov98227292020-11-11 08:00:48 -0500161u8 *
162format_vlib_vmbus_addr (u8 *s, va_list *va)
Stephen Hemminger6fbef232018-10-15 12:52:30 -0700163{
164 vlib_vmbus_addr_t *addr = va_arg (*va, vlib_vmbus_addr_t *);
Damjan Marion37066362023-07-28 20:06:09 +0200165 u8 *bytes = addr->guid;
Stephen Hemminger6fbef232018-10-15 12:52:30 -0700166
Damjan Marion37066362023-07-28 20:06:09 +0200167 for (int i = 0; i < 4; i++)
Alexander Skorichenkoe1489262023-12-19 13:08:13 +0100168 s = format (s, "%02x", bytes++[0]);
Damjan Marion37066362023-07-28 20:06:09 +0200169 vec_add1 (s, '-');
170 for (int i = 0; i < 2; i++)
Alexander Skorichenkoe1489262023-12-19 13:08:13 +0100171 s = format (s, "%02x", bytes++[0]);
Damjan Marion37066362023-07-28 20:06:09 +0200172 vec_add1 (s, '-');
173 for (int i = 0; i < 2; i++)
Alexander Skorichenkoe1489262023-12-19 13:08:13 +0100174 s = format (s, "%02x", bytes++[0]);
Damjan Marion37066362023-07-28 20:06:09 +0200175 vec_add1 (s, '-');
176 for (int i = 0; i < 2; i++)
Alexander Skorichenkoe1489262023-12-19 13:08:13 +0100177 s = format (s, "%02x", bytes++[0]);
Damjan Marion37066362023-07-28 20:06:09 +0200178 vec_add1 (s, '-');
179 for (int i = 0; i < 6; i++)
Alexander Skorichenkoe1489262023-12-19 13:08:13 +0100180 s = format (s, "%02x", bytes++[0]);
Damjan Marion37066362023-07-28 20:06:09 +0200181
182 return s;
Stephen Hemminger6fbef232018-10-15 12:52:30 -0700183}
184
185/* workaround for mlx bug, bring lower device up before unbind */
186static clib_error_t *
187vlib_vmbus_raise_lower (int fd, const char *upper_name)
188{
189 clib_error_t *error = 0;
190 struct dirent *e;
191 struct ifreq ifr;
192 u8 *dev_net_dir;
193 DIR *dir;
194
Stephen Hemminger29f0a5d2019-01-16 10:08:39 -0800195 clib_memset (&ifr, 0, sizeof (ifr));
Stephen Hemminger6fbef232018-10-15 12:52:30 -0700196
197 dev_net_dir = format (0, "%s/%s%c", sysfs_class_net_path, upper_name, 0);
198
199 dir = opendir ((char *) dev_net_dir);
200
201 if (!dir)
202 {
203 error = clib_error_return (0, "VMBUS failed to open %s", dev_net_dir);
204 goto done;
205 }
206
207 while ((e = readdir (dir)))
208 {
209 /* look for lower_enXXXX */
210 if (strncmp (e->d_name, "lower_", 6))
211 continue;
212
Stephen Hemminger29f0a5d2019-01-16 10:08:39 -0800213 strncpy (ifr.ifr_name, e->d_name + 6, IFNAMSIZ - 1);
Stephen Hemminger6fbef232018-10-15 12:52:30 -0700214 break;
215 }
216 closedir (dir);
217
218 if (!e)
219 goto done; /* no lower device */
220
221 if (ioctl (fd, SIOCGIFFLAGS, &ifr) < 0)
222 error = clib_error_return_unix (0, "ioctl fetch intf %s flags",
223 ifr.ifr_name);
224 else if (!(ifr.ifr_flags & IFF_UP))
225 {
226 ifr.ifr_flags |= IFF_UP;
227
228 if (ioctl (fd, SIOCSIFFLAGS, &ifr) < 0)
229 error = clib_error_return_unix (0, "ioctl set intf %s flags",
230 ifr.ifr_name);
231 }
232done:
233 vec_free (dev_net_dir);
234 return error;
235}
236
Stephen Hemminger485710e2019-03-12 19:06:32 -0700237static int
238directory_exists (char *path)
239{
240 struct stat s = { 0 };
241 if (stat (path, &s) == -1)
242 return 0;
243
244 return S_ISDIR (s.st_mode);
245}
246
Stephen Hemminger6fbef232018-10-15 12:52:30 -0700247clib_error_t *
248vlib_vmbus_bind_to_uio (vlib_vmbus_addr_t * addr)
249{
250 clib_error_t *error = 0;
251 u8 *dev_dir_name;
252 char *ifname = 0;
253 static int uio_new_id_needed = 1;
254 struct dirent *e;
255 struct ifreq ifr;
Damjan Marion40f48102023-08-07 01:07:09 +0200256 u8 *s = 0, *driver_name;
Stephen Hemminger6fbef232018-10-15 12:52:30 -0700257 DIR *dir;
258 int fd;
259
260 dev_dir_name = format (0, "%s/%U", sysfs_vmbus_dev_path,
261 format_vlib_vmbus_addr, addr);
Stephen Hemminger6fbef232018-10-15 12:52:30 -0700262
Damjan Marion40f48102023-08-07 01:07:09 +0200263 driver_name = clib_file_get_resolved_basename ("%v/driver", dev_dir_name);
Stephen Hemminger6fbef232018-10-15 12:52:30 -0700264
265 /* skip if not using the Linux kernel netvsc driver */
266 if (!driver_name || strcmp ("hv_netvsc", (char *) driver_name) != 0)
267 goto done;
268
Stephen Hemminger485710e2019-03-12 19:06:32 -0700269 /* if uio_hv_generic is not loaded, then can't use native DPDK driver. */
270 if (!directory_exists ("/sys/module/uio_hv_generic"))
271 goto done;
272
Stephen Hemminger6fbef232018-10-15 12:52:30 -0700273 s = format (s, "%v/net%c", dev_dir_name, 0);
274 dir = opendir ((char *) s);
275 vec_reset_length (s);
276
277 if (!dir)
278 return clib_error_return (0, "VMBUS failed to open %s", s);
279
280 while ((e = readdir (dir)))
281 {
282 if (e->d_name[0] == '.') /* skip . and .. */
283 continue;
284
285 ifname = strdup (e->d_name);
286 break;
287 }
288 closedir (dir);
289
290 if (!ifname)
291 {
292 error = clib_error_return (0,
293 "VMBUS device %U eth not found",
294 format_vlib_vmbus_addr, addr);
295 goto done;
296 }
297
298
Stephen Hemminger29f0a5d2019-01-16 10:08:39 -0800299 clib_memset (&ifr, 0, sizeof (ifr));
300 strncpy (ifr.ifr_name, ifname, IFNAMSIZ - 1);
Stephen Hemminger6fbef232018-10-15 12:52:30 -0700301
302 /* read up/down flags */
303 fd = socket (PF_INET, SOCK_DGRAM, 0);
304 if (fd < 0)
305 {
306 error = clib_error_return_unix (0, "socket");
307 goto done;
308 }
309
310 if (ioctl (fd, SIOCGIFFLAGS, &ifr) < 0)
311 {
312 error = clib_error_return_unix (0, "ioctl fetch intf %s flags",
313 ifr.ifr_name);
314 close (fd);
315 goto done;
316 }
317
318 if (ifr.ifr_flags & IFF_UP)
319 {
BenoƮt Ganne57cc4bc2021-10-13 11:37:04 +0200320 error = clib_error_return (
321 0, "Skipping VMBUS device %U as host interface %s is up",
322 format_vlib_vmbus_addr, addr, ifname);
Stephen Hemminger6fbef232018-10-15 12:52:30 -0700323 close (fd);
324 goto done;
325 }
326
Stephen Hemminger6fbef232018-10-15 12:52:30 -0700327 /* tell uio_hv_generic about netvsc device type */
328 if (uio_new_id_needed)
329 {
Stephen Hemminger6fbef232018-10-15 12:52:30 -0700330 vec_reset_length (s);
331 s = format (s, "%s/%s/new_id%c", sysfs_vmbus_drv_path, uio_drv_name, 0);
332 error = clib_sysfs_write ((char *) s, "%s", netvsc_uuid);
Vladimir Ratnikov98227292020-11-11 08:00:48 -0500333 /* If device already exists, we can bind/unbind/override driver */
Stephen Hemminger6fbef232018-10-15 12:52:30 -0700334 if (error)
Dave Baracha5160d72019-03-13 15:29:15 -0400335 {
Vladimir Ratnikov98227292020-11-11 08:00:48 -0500336 if (error->code == EEXIST)
337 {
338 clib_error_free (error);
339 }
340 else
341 {
342 close (fd);
343 goto done;
344 }
Dave Baracha5160d72019-03-13 15:29:15 -0400345 }
Stephen Hemminger6fbef232018-10-15 12:52:30 -0700346
Matthew Smithf6266b52019-03-04 09:43:45 -0600347 uio_new_id_needed = 0;
Stephen Hemminger6fbef232018-10-15 12:52:30 -0700348 }
349
Stephen Hemminger485710e2019-03-12 19:06:32 -0700350 error = vlib_vmbus_raise_lower (fd, ifname);
351 close (fd);
352
353 if (error)
354 goto done;
355
Stephen Hemminger6fbef232018-10-15 12:52:30 -0700356 /* prefer the simplier driver_override model */
357 vec_reset_length (s);
358 s = format (s, "%/driver_override%c", dev_dir_name, 0);
359 if (access ((char *) s, F_OK) == 0)
360 {
361 clib_sysfs_write ((char *) s, "%s", uio_drv_name);
362 }
363 else
364 {
365 vec_reset_length (s);
366
367 s = format (s, "%v/driver/unbind%c", dev_dir_name, 0);
368 error =
369 clib_sysfs_write ((char *) s, "%U", format_vlib_vmbus_addr, addr);
370
371 if (error)
372 goto done;
373
374 vec_reset_length (s);
375
376 s = format (s, "%s/%s/bind%c", sysfs_vmbus_drv_path, uio_drv_name, 0);
377 error =
378 clib_sysfs_write ((char *) s, "%U", format_vlib_vmbus_addr, addr);
379 }
380 vec_reset_length (s);
381
382done:
383 free (ifname);
384 vec_free (s);
385 vec_free (dev_dir_name);
386 vec_free (driver_name);
387 return error;
388}
389
390static clib_error_t *
391scan_vmbus_addr (void *arg, u8 * dev_dir_name, u8 * ignored)
392{
393 vlib_vmbus_addr_t addr, **addrv = arg;
394 unformat_input_t input;
395 clib_error_t *err = 0;
396
397 unformat_init_string (&input, (char *) dev_dir_name,
398 vec_len (dev_dir_name));
399
400 if (!unformat (&input, "/sys/bus/vmbus/devices/%U",
401 unformat_vlib_vmbus_addr, &addr))
402 err = clib_error_return (0, "unformat error `%v`", dev_dir_name);
403
404 unformat_free (&input);
405
406 if (err)
407 return err;
408
409 vec_add1 (*addrv, addr);
410 return 0;
411}
412
413static int
414vmbus_addr_cmp (void *v1, void *v2)
415{
416 vlib_vmbus_addr_t *a1 = v1;
417 vlib_vmbus_addr_t *a2 = v2;
418
Damjan Marion37066362023-07-28 20:06:09 +0200419 for (int i = 0; i < ARRAY_LEN (a1->guid); i++)
420 if (a1->guid[i] > a2->guid[i])
421 return 1;
422 else if (a1->guid[i] < a2->guid[i])
423 return -1;
424
425 return 0;
Stephen Hemminger6fbef232018-10-15 12:52:30 -0700426}
427
428vlib_vmbus_addr_t *
429vlib_vmbus_get_all_dev_addrs ()
430{
431 vlib_vmbus_addr_t *addrs = 0;
432 clib_error_t *err;
433
434 err =
435 foreach_directory_file ((char *) sysfs_vmbus_dev_path, scan_vmbus_addr,
436 &addrs, /* scan_dirs */ 0);
437 if (err)
438 {
439 vec_free (addrs);
440 return 0;
441 }
442
443 vec_sort_with_function (addrs, vmbus_addr_cmp);
444
445 return addrs;
446}
447
448clib_error_t *
449linux_vmbus_init (vlib_main_t * vm)
450{
451 linux_vmbus_main_t *pm = &linux_vmbus_main;
452
453 pm->vlib_main = vm;
454
Dave Barachf8d50682019-05-14 18:01:44 -0400455 return 0;
Stephen Hemminger6fbef232018-10-15 12:52:30 -0700456}
457
Dave Barachf8d50682019-05-14 18:01:44 -0400458/* *INDENT-OFF* */
459VLIB_INIT_FUNCTION (linux_vmbus_init) =
460{
461 .runs_before = VLIB_INITS("unix_input_init"),
462};
463/* *INDENT-ON* */
Stephen Hemminger6fbef232018-10-15 12:52:30 -0700464
465/*
466 * fd.io coding-style-patch-verification: ON
467 *
468 * Local Variables:
469 * eval: (c-set-style "gnu")
470 * End:
471 */