xref: /dpdk/drivers/bus/vmbus/linux/vmbus_uio.c (revision aedd054c)
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