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