xref: /dpdk/drivers/bus/vmbus/linux/vmbus_uio.c (revision 68a03efeed657e6e05f281479b33b51102797e15)
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 */
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 
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
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
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
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 *
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
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 * rte_mem_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 
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 
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 & (rte_mem_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
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 
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 
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 
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 
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 
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 
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