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