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