1 /* SPDX-License-Identifier: BSD-3-Clause
2 * Copyright (c) 2018, Microsoft Corporation.
3 * All Rights Reserved.
4 */
5
6 #include <string.h>
7 #include <unistd.h>
8 #include <fcntl.h>
9 #include <dirent.h>
10 #include <inttypes.h>
11 #include <sys/stat.h>
12 #include <sys/mman.h>
13
14 #include <rte_eal.h>
15 #include <rte_log.h>
16 #include <rte_memory.h>
17 #include <rte_common.h>
18 #include <rte_malloc.h>
19 #include <rte_bus_vmbus.h>
20 #include <rte_string_fns.h>
21
22 #include "private.h"
23
24 /** Pathname of VMBUS devices directory. */
25 #define SYSFS_VMBUS_DEVICES "/sys/bus/vmbus/devices"
26
27 static void *vmbus_map_addr;
28
29 /* Control interrupts */
vmbus_uio_irq_control(struct rte_vmbus_device * dev,int32_t onoff)30 void vmbus_uio_irq_control(struct rte_vmbus_device *dev, int32_t onoff)
31 {
32 if ((rte_intr_fd_get(dev->intr_handle) < 0) ||
33 write(rte_intr_fd_get(dev->intr_handle), &onoff,
34 sizeof(onoff)) < 0) {
35 VMBUS_LOG(ERR, "cannot write to %d:%s",
36 rte_intr_fd_get(dev->intr_handle),
37 strerror(errno));
38 }
39 }
40
vmbus_uio_irq_read(struct rte_vmbus_device * dev)41 int vmbus_uio_irq_read(struct rte_vmbus_device *dev)
42 {
43 int32_t count;
44 int cc;
45
46 if (rte_intr_fd_get(dev->intr_handle) < 0)
47 return -1;
48
49 cc = read(rte_intr_fd_get(dev->intr_handle), &count,
50 sizeof(count));
51 if (cc < (int)sizeof(count)) {
52 if (cc < 0) {
53 VMBUS_LOG(ERR, "IRQ read failed %s",
54 strerror(errno));
55 return -errno;
56 }
57 VMBUS_LOG(ERR, "can't read IRQ count");
58 return -EINVAL;
59 }
60
61 return count;
62 }
63
64 void
vmbus_uio_free_resource(struct rte_vmbus_device * dev,struct mapped_vmbus_resource * uio_res)65 vmbus_uio_free_resource(struct rte_vmbus_device *dev,
66 struct mapped_vmbus_resource *uio_res)
67 {
68 rte_free(uio_res);
69
70 if (rte_intr_dev_fd_get(dev->intr_handle) >= 0) {
71 close(rte_intr_dev_fd_get(dev->intr_handle));
72 rte_intr_dev_fd_set(dev->intr_handle, -1);
73 }
74
75 if (rte_intr_fd_get(dev->intr_handle) >= 0) {
76 close(rte_intr_fd_get(dev->intr_handle));
77 rte_intr_fd_set(dev->intr_handle, -1);
78 rte_intr_type_set(dev->intr_handle, RTE_INTR_HANDLE_UNKNOWN);
79 }
80 }
81
82 int
vmbus_uio_alloc_resource(struct rte_vmbus_device * dev,struct mapped_vmbus_resource ** uio_res)83 vmbus_uio_alloc_resource(struct rte_vmbus_device *dev,
84 struct mapped_vmbus_resource **uio_res)
85 {
86 char devname[PATH_MAX]; /* contains the /dev/uioX */
87 int fd;
88
89 /* save fd if in primary process */
90 snprintf(devname, sizeof(devname), "/dev/uio%u", dev->uio_num);
91 fd = open(devname, O_RDWR);
92 if (fd < 0) {
93 VMBUS_LOG(ERR, "Cannot open %s: %s",
94 devname, strerror(errno));
95 goto error;
96 }
97
98 if (rte_intr_fd_set(dev->intr_handle, fd))
99 goto error;
100
101 if (rte_intr_type_set(dev->intr_handle, RTE_INTR_HANDLE_UIO_INTX))
102 goto error;
103
104 /* allocate the mapping details for secondary processes*/
105 *uio_res = rte_zmalloc("UIO_RES", sizeof(**uio_res), 0);
106 if (*uio_res == NULL) {
107 VMBUS_LOG(ERR, "cannot store uio mmap details");
108 goto error;
109 }
110
111 strlcpy((*uio_res)->path, devname, PATH_MAX);
112 rte_uuid_copy((*uio_res)->id, dev->device_id);
113
114 return 0;
115
116 error:
117 vmbus_uio_free_resource(dev, *uio_res);
118 return -1;
119 }
120
121 static int
find_max_end_va(const struct rte_memseg_list * msl,void * arg)122 find_max_end_va(const struct rte_memseg_list *msl, void *arg)
123 {
124 size_t sz = msl->memseg_arr.len * msl->page_sz;
125 void *end_va = RTE_PTR_ADD(msl->base_va, sz);
126 void **max_va = arg;
127
128 if (*max_va < end_va)
129 *max_va = end_va;
130 return 0;
131 }
132
133 /*
134 * TODO: this should be part of memseg api.
135 * code is duplicated from PCI.
136 */
137 static void *
vmbus_find_max_end_va(void)138 vmbus_find_max_end_va(void)
139 {
140 void *va = NULL;
141
142 rte_memseg_list_walk(find_max_end_va, &va);
143 return va;
144 }
145
146 int
vmbus_uio_map_resource_by_index(struct rte_vmbus_device * dev,int idx,struct mapped_vmbus_resource * uio_res,int flags)147 vmbus_uio_map_resource_by_index(struct rte_vmbus_device *dev, int idx,
148 struct mapped_vmbus_resource *uio_res,
149 int flags)
150 {
151 size_t size = dev->resource[idx].len;
152 struct vmbus_map *maps = uio_res->maps;
153 void *mapaddr;
154 off_t offset;
155 int fd;
156
157 /* devname for mmap */
158 fd = open(uio_res->path, O_RDWR);
159 if (fd < 0) {
160 VMBUS_LOG(ERR, "Cannot open %s: %s",
161 uio_res->path, strerror(errno));
162 return -1;
163 }
164
165 /* try mapping somewhere close to the end of hugepages */
166 if (vmbus_map_addr == NULL)
167 vmbus_map_addr = vmbus_find_max_end_va();
168
169 /* offset is special in uio it indicates which resource */
170 offset = idx * rte_mem_page_size();
171
172 mapaddr = vmbus_map_resource(vmbus_map_addr, fd, offset, size, flags);
173 close(fd);
174
175 if (mapaddr == MAP_FAILED)
176 return -1;
177
178 dev->resource[idx].addr = mapaddr;
179 vmbus_map_addr = RTE_PTR_ADD(mapaddr, size);
180
181 /* Record result of successful mapping for use by secondary */
182 maps[idx].addr = mapaddr;
183 maps[idx].size = size;
184
185 return 0;
186 }
187
vmbus_uio_map_primary(struct vmbus_channel * chan,void ** ring_buf,uint32_t * ring_size)188 static int vmbus_uio_map_primary(struct vmbus_channel *chan,
189 void **ring_buf, uint32_t *ring_size)
190 {
191 struct mapped_vmbus_resource *uio_res;
192
193 uio_res = vmbus_uio_find_resource(chan->device);
194 if (!uio_res) {
195 VMBUS_LOG(ERR, "can not find resources!");
196 return -ENOMEM;
197 }
198
199 if (uio_res->nb_maps < VMBUS_MAX_RESOURCE) {
200 VMBUS_LOG(ERR, "VMBUS: only %u resources found!",
201 uio_res->nb_maps);
202 return -EINVAL;
203 }
204
205 *ring_size = uio_res->maps[HV_TXRX_RING_MAP].size / 2;
206 *ring_buf = uio_res->maps[HV_TXRX_RING_MAP].addr;
207 return 0;
208 }
209
vmbus_uio_map_subchan(const struct rte_vmbus_device * dev,const struct vmbus_channel * chan,void ** ring_buf,uint32_t * ring_size)210 static int vmbus_uio_map_subchan(const struct rte_vmbus_device *dev,
211 const struct vmbus_channel *chan,
212 void **ring_buf, uint32_t *ring_size)
213 {
214 char ring_path[PATH_MAX];
215 size_t file_size;
216 struct stat sb;
217 void *mapaddr;
218 int fd;
219 struct mapped_vmbus_resource *uio_res;
220 int channel_idx;
221
222 uio_res = vmbus_uio_find_resource(dev);
223 if (!uio_res) {
224 VMBUS_LOG(ERR, "can not find resources for mapping subchan");
225 return -ENOMEM;
226 }
227
228 if (rte_eal_process_type() == RTE_PROC_PRIMARY) {
229 if (uio_res->nb_subchannels >= UIO_MAX_SUBCHANNEL) {
230 VMBUS_LOG(ERR,
231 "exceeding max subchannels UIO_MAX_SUBCHANNEL(%d)",
232 UIO_MAX_SUBCHANNEL);
233 VMBUS_LOG(ERR, "Change UIO_MAX_SUBCHANNEL and recompile");
234 return -ENOMEM;
235 }
236 } else {
237 for (channel_idx = 0; channel_idx < uio_res->nb_subchannels;
238 channel_idx++)
239 if (uio_res->subchannel_maps[channel_idx].relid ==
240 chan->relid)
241 break;
242 if (channel_idx == uio_res->nb_subchannels) {
243 VMBUS_LOG(ERR,
244 "couldn't find sub channel %d from shared mapping in primary",
245 chan->relid);
246 return -ENOMEM;
247 }
248 vmbus_map_addr = uio_res->subchannel_maps[channel_idx].addr;
249 }
250
251 snprintf(ring_path, sizeof(ring_path),
252 "%s/%s/channels/%u/ring",
253 SYSFS_VMBUS_DEVICES, dev->device.name,
254 chan->relid);
255
256 fd = open(ring_path, O_RDWR);
257 if (fd < 0) {
258 VMBUS_LOG(ERR, "Cannot open %s: %s",
259 ring_path, strerror(errno));
260 return -errno;
261 }
262
263 if (fstat(fd, &sb) < 0) {
264 VMBUS_LOG(ERR, "Cannot state %s: %s",
265 ring_path, strerror(errno));
266 close(fd);
267 return -errno;
268 }
269 file_size = sb.st_size;
270
271 if (file_size == 0 || (file_size & (rte_mem_page_size() - 1))) {
272 VMBUS_LOG(ERR, "incorrect size %s: %zu",
273 ring_path, file_size);
274
275 close(fd);
276 return -EINVAL;
277 }
278
279 mapaddr = vmbus_map_resource(vmbus_map_addr, fd,
280 0, file_size, 0);
281 close(fd);
282
283 if (mapaddr == MAP_FAILED)
284 return -EIO;
285
286 if (rte_eal_process_type() == RTE_PROC_PRIMARY) {
287
288 /* Add this mapping to uio_res for use by secondary */
289 uio_res->subchannel_maps[uio_res->nb_subchannels].relid =
290 chan->relid;
291 uio_res->subchannel_maps[uio_res->nb_subchannels].addr =
292 mapaddr;
293 uio_res->subchannel_maps[uio_res->nb_subchannels].size =
294 file_size;
295 uio_res->nb_subchannels++;
296
297 vmbus_map_addr = RTE_PTR_ADD(mapaddr, file_size);
298 } else {
299 if (mapaddr != vmbus_map_addr) {
300 VMBUS_LOG(ERR, "failed to map channel %d to addr %p",
301 chan->relid, mapaddr);
302 vmbus_unmap_resource(mapaddr, file_size);
303 return -EIO;
304 }
305 }
306
307 *ring_size = file_size / 2;
308 *ring_buf = mapaddr;
309
310 return 0;
311 }
312
vmbus_uio_map_rings(struct vmbus_channel * chan)313 int vmbus_uio_map_rings(struct vmbus_channel *chan)
314 {
315 const struct rte_vmbus_device *dev = chan->device;
316 uint32_t ring_size;
317 void *ring_buf;
318 int ret;
319
320 /* Primary channel */
321 if (chan->subchannel_id == 0)
322 ret = vmbus_uio_map_primary(chan, &ring_buf, &ring_size);
323 else
324 ret = vmbus_uio_map_subchan(dev, chan, &ring_buf, &ring_size);
325
326 if (ret)
327 return ret;
328
329 vmbus_br_setup(&chan->txbr, ring_buf, ring_size);
330 vmbus_br_setup(&chan->rxbr, (char *)ring_buf + ring_size, ring_size);
331 return 0;
332 }
333
vmbus_uio_sysfs_read(const char * dir,const char * name,unsigned long * val,unsigned long max_range)334 static int vmbus_uio_sysfs_read(const char *dir, const char *name,
335 unsigned long *val, unsigned long max_range)
336 {
337 char path[PATH_MAX];
338 FILE *f;
339 int ret;
340
341 snprintf(path, sizeof(path), "%s/%s", dir, name);
342 f = fopen(path, "r");
343 if (!f) {
344 VMBUS_LOG(ERR, "can't open %s:%s",
345 path, strerror(errno));
346 return -errno;
347 }
348
349 if (fscanf(f, "%lu", val) != 1)
350 ret = -EIO;
351 else if (*val > max_range)
352 ret = -ERANGE;
353 else
354 ret = 0;
355 fclose(f);
356
357 return ret;
358 }
359
vmbus_uio_ring_present(const struct rte_vmbus_device * dev,uint32_t relid)360 static bool vmbus_uio_ring_present(const struct rte_vmbus_device *dev,
361 uint32_t relid)
362 {
363 char ring_path[PATH_MAX];
364
365 /* Check if kernel has subchannel sysfs files */
366 snprintf(ring_path, sizeof(ring_path),
367 "%s/%s/channels/%u/ring",
368 SYSFS_VMBUS_DEVICES, dev->device.name, relid);
369
370 return access(ring_path, R_OK|W_OK) == 0;
371 }
372
vmbus_uio_subchannels_supported(const struct rte_vmbus_device * dev,const struct vmbus_channel * chan)373 bool vmbus_uio_subchannels_supported(const struct rte_vmbus_device *dev,
374 const struct vmbus_channel *chan)
375 {
376 return vmbus_uio_ring_present(dev, chan->relid);
377 }
378
vmbus_isnew_subchannel(struct vmbus_channel * primary,unsigned long id)379 static bool vmbus_isnew_subchannel(struct vmbus_channel *primary,
380 unsigned long id)
381 {
382 const struct vmbus_channel *c;
383
384 STAILQ_FOREACH(c, &primary->subchannel_list, next) {
385 if (c->relid == id)
386 return false;
387 }
388 return true;
389 }
390
vmbus_uio_get_subchan(struct vmbus_channel * primary,struct vmbus_channel ** subchan)391 int vmbus_uio_get_subchan(struct vmbus_channel *primary,
392 struct vmbus_channel **subchan)
393 {
394 const struct rte_vmbus_device *dev = primary->device;
395 char chan_path[PATH_MAX], subchan_path[PATH_MAX];
396 struct dirent *ent;
397 DIR *chan_dir;
398 int err;
399
400 snprintf(chan_path, sizeof(chan_path),
401 "%s/%s/channels",
402 SYSFS_VMBUS_DEVICES, dev->device.name);
403
404 chan_dir = opendir(chan_path);
405 if (!chan_dir) {
406 VMBUS_LOG(ERR, "cannot open %s: %s",
407 chan_path, strerror(errno));
408 return -errno;
409 }
410
411 while ((ent = readdir(chan_dir))) {
412 unsigned long relid, subid, monid;
413 char *endp;
414
415 if (ent->d_name[0] == '.')
416 continue;
417
418 errno = 0;
419 relid = strtoul(ent->d_name, &endp, 0);
420 if (*endp || errno != 0 || relid > UINT16_MAX) {
421 VMBUS_LOG(NOTICE, "not a valid channel relid: %s",
422 ent->d_name);
423 continue;
424 }
425
426 if (!vmbus_isnew_subchannel(primary, relid)) {
427 VMBUS_LOG(DEBUG, "skip already found channel: %lu",
428 relid);
429 continue;
430 }
431
432 if (!vmbus_uio_ring_present(dev, relid)) {
433 VMBUS_LOG(DEBUG, "ring mmap not found (yet) for: %lu",
434 relid);
435 continue;
436 }
437
438 snprintf(subchan_path, sizeof(subchan_path), "%s/%lu",
439 chan_path, relid);
440 err = vmbus_uio_sysfs_read(subchan_path, "subchannel_id",
441 &subid, UINT16_MAX);
442 if (err) {
443 VMBUS_LOG(NOTICE, "no subchannel_id in %s:%s",
444 subchan_path, strerror(-err));
445 goto fail;
446 }
447
448 if (subid == 0)
449 continue; /* skip primary channel */
450
451 err = vmbus_uio_sysfs_read(subchan_path, "monitor_id",
452 &monid, UINT8_MAX);
453 if (err) {
454 VMBUS_LOG(NOTICE, "no monitor_id in %s:%s",
455 subchan_path, strerror(-err));
456 goto fail;
457 }
458
459 err = vmbus_chan_create(dev, relid, subid, monid, subchan);
460 if (err) {
461 VMBUS_LOG(ERR, "subchannel setup failed");
462 goto fail;
463 }
464 break;
465 }
466 closedir(chan_dir);
467
468 return (ent == NULL) ? -ENOENT : 0;
469 fail:
470 closedir(chan_dir);
471 return err;
472 }
473