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