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