xref: /dpdk/drivers/bus/vmbus/linux/vmbus_uio.c (revision 84aaf06d817c94761e7489b7d2472afd12a8ca66)
1831dba47SStephen Hemminger /* SPDX-License-Identifier: BSD-3-Clause
2831dba47SStephen Hemminger  * Copyright (c) 2018, Microsoft Corporation.
3831dba47SStephen Hemminger  * All Rights Reserved.
4831dba47SStephen Hemminger  */
5831dba47SStephen Hemminger 
6831dba47SStephen Hemminger #include <string.h>
7831dba47SStephen Hemminger #include <unistd.h>
8831dba47SStephen Hemminger #include <fcntl.h>
9831dba47SStephen Hemminger #include <dirent.h>
10831dba47SStephen Hemminger #include <inttypes.h>
11831dba47SStephen Hemminger #include <sys/stat.h>
12831dba47SStephen Hemminger #include <sys/mman.h>
13831dba47SStephen Hemminger 
1470cdd92eSLong Li #include <rte_eal.h>
15831dba47SStephen Hemminger #include <rte_log.h>
16831dba47SStephen Hemminger #include <rte_memory.h>
17831dba47SStephen Hemminger #include <rte_common.h>
18831dba47SStephen Hemminger #include <rte_malloc.h>
19831dba47SStephen Hemminger #include <rte_bus_vmbus.h>
205ef90536SStephen Hemminger #include <rte_string_fns.h>
21831dba47SStephen Hemminger 
22831dba47SStephen Hemminger #include "private.h"
23831dba47SStephen Hemminger 
24831dba47SStephen Hemminger /** Pathname of VMBUS devices directory. */
25831dba47SStephen Hemminger #define SYSFS_VMBUS_DEVICES "/sys/bus/vmbus/devices"
26831dba47SStephen Hemminger 
27831dba47SStephen Hemminger static void *vmbus_map_addr;
28831dba47SStephen Hemminger 
29831dba47SStephen Hemminger /* Control interrupts */
vmbus_uio_irq_control(struct rte_vmbus_device * dev,int32_t onoff)30831dba47SStephen Hemminger void vmbus_uio_irq_control(struct rte_vmbus_device *dev, int32_t onoff)
31831dba47SStephen Hemminger {
32*aedd054cSHarman Kalra 	if ((rte_intr_fd_get(dev->intr_handle) < 0) ||
33*aedd054cSHarman Kalra 	    write(rte_intr_fd_get(dev->intr_handle), &onoff,
34d61138d4SHarman Kalra 		  sizeof(onoff)) < 0) {
35831dba47SStephen Hemminger 		VMBUS_LOG(ERR, "cannot write to %d:%s",
36d61138d4SHarman Kalra 			  rte_intr_fd_get(dev->intr_handle),
37d61138d4SHarman Kalra 			  strerror(errno));
38831dba47SStephen Hemminger 	}
39831dba47SStephen Hemminger }
40831dba47SStephen Hemminger 
vmbus_uio_irq_read(struct rte_vmbus_device * dev)41831dba47SStephen Hemminger int vmbus_uio_irq_read(struct rte_vmbus_device *dev)
42831dba47SStephen Hemminger {
43831dba47SStephen Hemminger 	int32_t count;
44cd3e20a6SStephen Hemminger 	int cc;
45831dba47SStephen Hemminger 
46*aedd054cSHarman Kalra 	if (rte_intr_fd_get(dev->intr_handle) < 0)
47*aedd054cSHarman Kalra 		return -1;
48*aedd054cSHarman Kalra 
49d61138d4SHarman Kalra 	cc = read(rte_intr_fd_get(dev->intr_handle), &count,
50d61138d4SHarman Kalra 		  sizeof(count));
51cd3e20a6SStephen Hemminger 	if (cc < (int)sizeof(count)) {
52cd3e20a6SStephen Hemminger 		if (cc < 0) {
53cd3e20a6SStephen Hemminger 			VMBUS_LOG(ERR, "IRQ read failed %s",
54cd3e20a6SStephen Hemminger 				  strerror(errno));
55cd3e20a6SStephen Hemminger 			return -errno;
56cd3e20a6SStephen Hemminger 		}
57cd3e20a6SStephen Hemminger 		VMBUS_LOG(ERR, "can't read IRQ count");
58cd3e20a6SStephen Hemminger 		return -EINVAL;
59831dba47SStephen Hemminger 	}
60831dba47SStephen Hemminger 
61831dba47SStephen Hemminger 	return count;
62831dba47SStephen Hemminger }
63831dba47SStephen Hemminger 
64831dba47SStephen Hemminger void
vmbus_uio_free_resource(struct rte_vmbus_device * dev,struct mapped_vmbus_resource * uio_res)65831dba47SStephen Hemminger vmbus_uio_free_resource(struct rte_vmbus_device *dev,
66831dba47SStephen Hemminger 		struct mapped_vmbus_resource *uio_res)
67831dba47SStephen Hemminger {
68831dba47SStephen Hemminger 	rte_free(uio_res);
69831dba47SStephen Hemminger 
70d61138d4SHarman Kalra 	if (rte_intr_dev_fd_get(dev->intr_handle) >= 0) {
71d61138d4SHarman Kalra 		close(rte_intr_dev_fd_get(dev->intr_handle));
72d61138d4SHarman Kalra 		rte_intr_dev_fd_set(dev->intr_handle, -1);
73831dba47SStephen Hemminger 	}
74831dba47SStephen Hemminger 
75d61138d4SHarman Kalra 	if (rte_intr_fd_get(dev->intr_handle) >= 0) {
76d61138d4SHarman Kalra 		close(rte_intr_fd_get(dev->intr_handle));
77d61138d4SHarman Kalra 		rte_intr_fd_set(dev->intr_handle, -1);
78d61138d4SHarman Kalra 		rte_intr_type_set(dev->intr_handle, RTE_INTR_HANDLE_UNKNOWN);
79831dba47SStephen Hemminger 	}
80831dba47SStephen Hemminger }
81831dba47SStephen Hemminger 
82831dba47SStephen Hemminger int
vmbus_uio_alloc_resource(struct rte_vmbus_device * dev,struct mapped_vmbus_resource ** uio_res)83831dba47SStephen Hemminger vmbus_uio_alloc_resource(struct rte_vmbus_device *dev,
84831dba47SStephen Hemminger 			 struct mapped_vmbus_resource **uio_res)
85831dba47SStephen Hemminger {
86831dba47SStephen Hemminger 	char devname[PATH_MAX]; /* contains the /dev/uioX */
87d61138d4SHarman Kalra 	int fd;
88831dba47SStephen Hemminger 
89831dba47SStephen Hemminger 	/* save fd if in primary process */
90831dba47SStephen Hemminger 	snprintf(devname, sizeof(devname), "/dev/uio%u", dev->uio_num);
91d61138d4SHarman Kalra 	fd = open(devname, O_RDWR);
92d61138d4SHarman Kalra 	if (fd < 0) {
93831dba47SStephen Hemminger 		VMBUS_LOG(ERR, "Cannot open %s: %s",
94831dba47SStephen Hemminger 			devname, strerror(errno));
95831dba47SStephen Hemminger 		goto error;
96831dba47SStephen Hemminger 	}
97d61138d4SHarman Kalra 
98d61138d4SHarman Kalra 	if (rte_intr_fd_set(dev->intr_handle, fd))
99d61138d4SHarman Kalra 		goto error;
100d61138d4SHarman Kalra 
101d61138d4SHarman Kalra 	if (rte_intr_type_set(dev->intr_handle, RTE_INTR_HANDLE_UIO_INTX))
102d61138d4SHarman Kalra 		goto error;
103831dba47SStephen Hemminger 
104831dba47SStephen Hemminger 	/* allocate the mapping details for secondary processes*/
105831dba47SStephen Hemminger 	*uio_res = rte_zmalloc("UIO_RES", sizeof(**uio_res), 0);
106831dba47SStephen Hemminger 	if (*uio_res == NULL) {
107831dba47SStephen Hemminger 		VMBUS_LOG(ERR, "cannot store uio mmap details");
108831dba47SStephen Hemminger 		goto error;
109831dba47SStephen Hemminger 	}
110831dba47SStephen Hemminger 
1115ef90536SStephen Hemminger 	strlcpy((*uio_res)->path, devname, PATH_MAX);
112831dba47SStephen Hemminger 	rte_uuid_copy((*uio_res)->id, dev->device_id);
113831dba47SStephen Hemminger 
114831dba47SStephen Hemminger 	return 0;
115831dba47SStephen Hemminger 
116831dba47SStephen Hemminger error:
117831dba47SStephen Hemminger 	vmbus_uio_free_resource(dev, *uio_res);
118831dba47SStephen Hemminger 	return -1;
119831dba47SStephen Hemminger }
120831dba47SStephen Hemminger 
121831dba47SStephen Hemminger static int
find_max_end_va(const struct rte_memseg_list * msl,void * arg)122831dba47SStephen Hemminger find_max_end_va(const struct rte_memseg_list *msl, void *arg)
123831dba47SStephen Hemminger {
124831dba47SStephen Hemminger 	size_t sz = msl->memseg_arr.len * msl->page_sz;
125831dba47SStephen Hemminger 	void *end_va = RTE_PTR_ADD(msl->base_va, sz);
126831dba47SStephen Hemminger 	void **max_va = arg;
127831dba47SStephen Hemminger 
128831dba47SStephen Hemminger 	if (*max_va < end_va)
129831dba47SStephen Hemminger 		*max_va = end_va;
130831dba47SStephen Hemminger 	return 0;
131831dba47SStephen Hemminger }
132831dba47SStephen Hemminger 
133831dba47SStephen Hemminger /*
134831dba47SStephen Hemminger  * TODO: this should be part of memseg api.
135831dba47SStephen Hemminger  *       code is duplicated from PCI.
136831dba47SStephen Hemminger  */
137831dba47SStephen Hemminger static void *
vmbus_find_max_end_va(void)138831dba47SStephen Hemminger vmbus_find_max_end_va(void)
139831dba47SStephen Hemminger {
140831dba47SStephen Hemminger 	void *va = NULL;
141831dba47SStephen Hemminger 
142831dba47SStephen Hemminger 	rte_memseg_list_walk(find_max_end_va, &va);
143831dba47SStephen Hemminger 	return va;
144831dba47SStephen Hemminger }
145831dba47SStephen Hemminger 
146831dba47SStephen Hemminger int
vmbus_uio_map_resource_by_index(struct rte_vmbus_device * dev,int idx,struct mapped_vmbus_resource * uio_res,int flags)147831dba47SStephen Hemminger vmbus_uio_map_resource_by_index(struct rte_vmbus_device *dev, int idx,
148831dba47SStephen Hemminger 				struct mapped_vmbus_resource *uio_res,
149831dba47SStephen Hemminger 				int flags)
150831dba47SStephen Hemminger {
151831dba47SStephen Hemminger 	size_t size = dev->resource[idx].len;
152831dba47SStephen Hemminger 	struct vmbus_map *maps = uio_res->maps;
153831dba47SStephen Hemminger 	void *mapaddr;
154831dba47SStephen Hemminger 	off_t offset;
155831dba47SStephen Hemminger 	int fd;
156831dba47SStephen Hemminger 
157831dba47SStephen Hemminger 	/* devname for mmap  */
158831dba47SStephen Hemminger 	fd = open(uio_res->path, O_RDWR);
159831dba47SStephen Hemminger 	if (fd < 0) {
160831dba47SStephen Hemminger 		VMBUS_LOG(ERR, "Cannot open %s: %s",
161831dba47SStephen Hemminger 			  uio_res->path, strerror(errno));
162831dba47SStephen Hemminger 		return -1;
163831dba47SStephen Hemminger 	}
164831dba47SStephen Hemminger 
165831dba47SStephen Hemminger 	/* try mapping somewhere close to the end of hugepages */
166831dba47SStephen Hemminger 	if (vmbus_map_addr == NULL)
167831dba47SStephen Hemminger 		vmbus_map_addr = vmbus_find_max_end_va();
168831dba47SStephen Hemminger 
169831dba47SStephen Hemminger 	/* offset is special in uio it indicates which resource */
170924e6b76SThomas Monjalon 	offset = idx * rte_mem_page_size();
171831dba47SStephen Hemminger 
172831dba47SStephen Hemminger 	mapaddr = vmbus_map_resource(vmbus_map_addr, fd, offset, size, flags);
173831dba47SStephen Hemminger 	close(fd);
174831dba47SStephen Hemminger 
175831dba47SStephen Hemminger 	if (mapaddr == MAP_FAILED)
176831dba47SStephen Hemminger 		return -1;
177831dba47SStephen Hemminger 
178831dba47SStephen Hemminger 	dev->resource[idx].addr = mapaddr;
179831dba47SStephen Hemminger 	vmbus_map_addr = RTE_PTR_ADD(mapaddr, size);
180831dba47SStephen Hemminger 
1813a185ff4SStephen Hemminger 	/* Record result of successful mapping for use by secondary */
182831dba47SStephen Hemminger 	maps[idx].addr = mapaddr;
183831dba47SStephen Hemminger 	maps[idx].size = size;
184831dba47SStephen Hemminger 
185831dba47SStephen Hemminger 	return 0;
186831dba47SStephen Hemminger }
187831dba47SStephen Hemminger 
vmbus_uio_map_primary(struct vmbus_channel * chan,void ** ring_buf,uint32_t * ring_size)188831dba47SStephen Hemminger static int vmbus_uio_map_primary(struct vmbus_channel *chan,
189831dba47SStephen Hemminger 				 void **ring_buf, uint32_t *ring_size)
190831dba47SStephen Hemminger {
191831dba47SStephen Hemminger 	struct mapped_vmbus_resource *uio_res;
192831dba47SStephen Hemminger 
193831dba47SStephen Hemminger 	uio_res = vmbus_uio_find_resource(chan->device);
194831dba47SStephen Hemminger 	if (!uio_res) {
195831dba47SStephen Hemminger 		VMBUS_LOG(ERR, "can not find resources!");
196831dba47SStephen Hemminger 		return -ENOMEM;
197831dba47SStephen Hemminger 	}
198831dba47SStephen Hemminger 
199831dba47SStephen Hemminger 	if (uio_res->nb_maps < VMBUS_MAX_RESOURCE) {
200831dba47SStephen Hemminger 		VMBUS_LOG(ERR, "VMBUS: only %u resources found!",
201831dba47SStephen Hemminger 			  uio_res->nb_maps);
202831dba47SStephen Hemminger 		return -EINVAL;
203831dba47SStephen Hemminger 	}
204831dba47SStephen Hemminger 
205831dba47SStephen Hemminger 	*ring_size = uio_res->maps[HV_TXRX_RING_MAP].size / 2;
206831dba47SStephen Hemminger 	*ring_buf  = uio_res->maps[HV_TXRX_RING_MAP].addr;
207831dba47SStephen Hemminger 	return 0;
208831dba47SStephen Hemminger }
209831dba47SStephen Hemminger 
vmbus_uio_map_subchan(const struct rte_vmbus_device * dev,const struct vmbus_channel * chan,void ** ring_buf,uint32_t * ring_size)210831dba47SStephen Hemminger static int vmbus_uio_map_subchan(const struct rte_vmbus_device *dev,
211831dba47SStephen Hemminger 				 const struct vmbus_channel *chan,
212831dba47SStephen Hemminger 				 void **ring_buf, uint32_t *ring_size)
213831dba47SStephen Hemminger {
214831dba47SStephen Hemminger 	char ring_path[PATH_MAX];
215831dba47SStephen Hemminger 	size_t file_size;
216831dba47SStephen Hemminger 	struct stat sb;
2173f927703SStephen Hemminger 	void *mapaddr;
218831dba47SStephen Hemminger 	int fd;
21970cdd92eSLong Li 	struct mapped_vmbus_resource *uio_res;
22070cdd92eSLong Li 	int channel_idx;
22170cdd92eSLong Li 
22270cdd92eSLong Li 	uio_res = vmbus_uio_find_resource(dev);
22370cdd92eSLong Li 	if (!uio_res) {
22470cdd92eSLong Li 		VMBUS_LOG(ERR, "can not find resources for mapping subchan");
22570cdd92eSLong Li 		return -ENOMEM;
22670cdd92eSLong Li 	}
22770cdd92eSLong Li 
22870cdd92eSLong Li 	if (rte_eal_process_type() == RTE_PROC_PRIMARY) {
22970cdd92eSLong Li 		if (uio_res->nb_subchannels >= UIO_MAX_SUBCHANNEL) {
23070cdd92eSLong Li 			VMBUS_LOG(ERR,
23170cdd92eSLong Li 				"exceeding max subchannels UIO_MAX_SUBCHANNEL(%d)",
23270cdd92eSLong Li 				UIO_MAX_SUBCHANNEL);
23370cdd92eSLong Li 			VMBUS_LOG(ERR, "Change UIO_MAX_SUBCHANNEL and recompile");
23470cdd92eSLong Li 			return -ENOMEM;
23570cdd92eSLong Li 		}
23670cdd92eSLong Li 	} else {
23770cdd92eSLong Li 		for (channel_idx = 0; channel_idx < uio_res->nb_subchannels;
23870cdd92eSLong Li 		     channel_idx++)
23970cdd92eSLong Li 			if (uio_res->subchannel_maps[channel_idx].relid ==
24070cdd92eSLong Li 					chan->relid)
24170cdd92eSLong Li 				break;
24270cdd92eSLong Li 		if (channel_idx == uio_res->nb_subchannels) {
24370cdd92eSLong Li 			VMBUS_LOG(ERR,
24470cdd92eSLong Li 				"couldn't find sub channel %d from shared mapping in primary",
24570cdd92eSLong Li 				chan->relid);
24670cdd92eSLong Li 			return -ENOMEM;
24770cdd92eSLong Li 		}
24870cdd92eSLong Li 		vmbus_map_addr = uio_res->subchannel_maps[channel_idx].addr;
24970cdd92eSLong Li 	}
250831dba47SStephen Hemminger 
251831dba47SStephen Hemminger 	snprintf(ring_path, sizeof(ring_path),
252831dba47SStephen Hemminger 		 "%s/%s/channels/%u/ring",
253831dba47SStephen Hemminger 		 SYSFS_VMBUS_DEVICES, dev->device.name,
254831dba47SStephen Hemminger 		 chan->relid);
255831dba47SStephen Hemminger 
256831dba47SStephen Hemminger 	fd = open(ring_path, O_RDWR);
257831dba47SStephen Hemminger 	if (fd < 0) {
258831dba47SStephen Hemminger 		VMBUS_LOG(ERR, "Cannot open %s: %s",
259831dba47SStephen Hemminger 			  ring_path, strerror(errno));
260831dba47SStephen Hemminger 		return -errno;
261831dba47SStephen Hemminger 	}
262831dba47SStephen Hemminger 
263831dba47SStephen Hemminger 	if (fstat(fd, &sb) < 0) {
264831dba47SStephen Hemminger 		VMBUS_LOG(ERR, "Cannot state %s: %s",
265831dba47SStephen Hemminger 			  ring_path, strerror(errno));
266831dba47SStephen Hemminger 		close(fd);
267831dba47SStephen Hemminger 		return -errno;
268831dba47SStephen Hemminger 	}
269831dba47SStephen Hemminger 	file_size = sb.st_size;
270831dba47SStephen Hemminger 
271924e6b76SThomas Monjalon 	if (file_size == 0 || (file_size & (rte_mem_page_size() - 1))) {
272831dba47SStephen Hemminger 		VMBUS_LOG(ERR, "incorrect size %s: %zu",
273831dba47SStephen Hemminger 			  ring_path, file_size);
274831dba47SStephen Hemminger 
275831dba47SStephen Hemminger 		close(fd);
276831dba47SStephen Hemminger 		return -EINVAL;
277831dba47SStephen Hemminger 	}
278831dba47SStephen Hemminger 
2793f927703SStephen Hemminger 	mapaddr = vmbus_map_resource(vmbus_map_addr, fd,
2803f927703SStephen Hemminger 				     0, file_size, 0);
281831dba47SStephen Hemminger 	close(fd);
282831dba47SStephen Hemminger 
2833f927703SStephen Hemminger 	if (mapaddr == MAP_FAILED)
284831dba47SStephen Hemminger 		return -EIO;
285831dba47SStephen Hemminger 
28670cdd92eSLong Li 	if (rte_eal_process_type() == RTE_PROC_PRIMARY) {
28770cdd92eSLong Li 
28870cdd92eSLong Li 		/* Add this mapping to uio_res for use by secondary */
28970cdd92eSLong Li 		uio_res->subchannel_maps[uio_res->nb_subchannels].relid =
29070cdd92eSLong Li 			chan->relid;
29170cdd92eSLong Li 		uio_res->subchannel_maps[uio_res->nb_subchannels].addr =
29270cdd92eSLong Li 			mapaddr;
29370cdd92eSLong Li 		uio_res->subchannel_maps[uio_res->nb_subchannels].size =
29470cdd92eSLong Li 			file_size;
29570cdd92eSLong Li 		uio_res->nb_subchannels++;
29670cdd92eSLong Li 
29770cdd92eSLong Li 		vmbus_map_addr = RTE_PTR_ADD(mapaddr, file_size);
29870cdd92eSLong Li 	} else {
29970cdd92eSLong Li 		if (mapaddr != vmbus_map_addr) {
30070cdd92eSLong Li 			VMBUS_LOG(ERR, "failed to map channel %d to addr %p",
30170cdd92eSLong Li 					chan->relid, mapaddr);
30270cdd92eSLong Li 			vmbus_unmap_resource(mapaddr, file_size);
30370cdd92eSLong Li 			return -EIO;
30470cdd92eSLong Li 		}
30570cdd92eSLong Li 	}
30670cdd92eSLong Li 
3073f927703SStephen Hemminger 	*ring_size = file_size / 2;
3083f927703SStephen Hemminger 	*ring_buf = mapaddr;
3093f927703SStephen Hemminger 
310831dba47SStephen Hemminger 	return 0;
311831dba47SStephen Hemminger }
312831dba47SStephen Hemminger 
vmbus_uio_map_rings(struct vmbus_channel * chan)313831dba47SStephen Hemminger int vmbus_uio_map_rings(struct vmbus_channel *chan)
314831dba47SStephen Hemminger {
315831dba47SStephen Hemminger 	const struct rte_vmbus_device *dev = chan->device;
316831dba47SStephen Hemminger 	uint32_t ring_size;
317831dba47SStephen Hemminger 	void *ring_buf;
318831dba47SStephen Hemminger 	int ret;
319831dba47SStephen Hemminger 
320831dba47SStephen Hemminger 	/* Primary channel */
321831dba47SStephen Hemminger 	if (chan->subchannel_id == 0)
322831dba47SStephen Hemminger 		ret = vmbus_uio_map_primary(chan, &ring_buf, &ring_size);
323831dba47SStephen Hemminger 	else
324831dba47SStephen Hemminger 		ret = vmbus_uio_map_subchan(dev, chan, &ring_buf, &ring_size);
325831dba47SStephen Hemminger 
326831dba47SStephen Hemminger 	if (ret)
327831dba47SStephen Hemminger 		return ret;
328831dba47SStephen Hemminger 
329831dba47SStephen Hemminger 	vmbus_br_setup(&chan->txbr, ring_buf, ring_size);
330831dba47SStephen Hemminger 	vmbus_br_setup(&chan->rxbr, (char *)ring_buf + ring_size, ring_size);
331831dba47SStephen Hemminger 	return 0;
332831dba47SStephen Hemminger }
333831dba47SStephen Hemminger 
vmbus_uio_sysfs_read(const char * dir,const char * name,unsigned long * val,unsigned long max_range)334831dba47SStephen Hemminger static int vmbus_uio_sysfs_read(const char *dir, const char *name,
335831dba47SStephen Hemminger 				unsigned long *val, unsigned long max_range)
336831dba47SStephen Hemminger {
337831dba47SStephen Hemminger 	char path[PATH_MAX];
338831dba47SStephen Hemminger 	FILE *f;
339831dba47SStephen Hemminger 	int ret;
340831dba47SStephen Hemminger 
341831dba47SStephen Hemminger 	snprintf(path, sizeof(path), "%s/%s", dir, name);
342831dba47SStephen Hemminger 	f = fopen(path, "r");
343831dba47SStephen Hemminger 	if (!f) {
344831dba47SStephen Hemminger 		VMBUS_LOG(ERR, "can't open %s:%s",
345831dba47SStephen Hemminger 			  path, strerror(errno));
346831dba47SStephen Hemminger 		return -errno;
347831dba47SStephen Hemminger 	}
348831dba47SStephen Hemminger 
349831dba47SStephen Hemminger 	if (fscanf(f, "%lu", val) != 1)
350831dba47SStephen Hemminger 		ret = -EIO;
351831dba47SStephen Hemminger 	else if (*val > max_range)
352831dba47SStephen Hemminger 		ret = -ERANGE;
353831dba47SStephen Hemminger 	else
354831dba47SStephen Hemminger 		ret = 0;
355831dba47SStephen Hemminger 	fclose(f);
356831dba47SStephen Hemminger 
357831dba47SStephen Hemminger 	return ret;
358831dba47SStephen Hemminger }
359831dba47SStephen Hemminger 
vmbus_uio_ring_present(const struct rte_vmbus_device * dev,uint32_t relid)360831dba47SStephen Hemminger static bool vmbus_uio_ring_present(const struct rte_vmbus_device *dev,
361831dba47SStephen Hemminger 				   uint32_t relid)
362831dba47SStephen Hemminger {
363831dba47SStephen Hemminger 	char ring_path[PATH_MAX];
364831dba47SStephen Hemminger 
365831dba47SStephen Hemminger 	/* Check if kernel has subchannel sysfs files */
366831dba47SStephen Hemminger 	snprintf(ring_path, sizeof(ring_path),
367831dba47SStephen Hemminger 		 "%s/%s/channels/%u/ring",
368831dba47SStephen Hemminger 		 SYSFS_VMBUS_DEVICES, dev->device.name, relid);
369831dba47SStephen Hemminger 
370831dba47SStephen Hemminger 	return access(ring_path, R_OK|W_OK) == 0;
371831dba47SStephen Hemminger }
372831dba47SStephen Hemminger 
vmbus_uio_subchannels_supported(const struct rte_vmbus_device * dev,const struct vmbus_channel * chan)373831dba47SStephen Hemminger bool vmbus_uio_subchannels_supported(const struct rte_vmbus_device *dev,
374831dba47SStephen Hemminger 				     const struct vmbus_channel *chan)
375831dba47SStephen Hemminger {
376831dba47SStephen Hemminger 	return vmbus_uio_ring_present(dev, chan->relid);
377831dba47SStephen Hemminger }
378831dba47SStephen Hemminger 
vmbus_isnew_subchannel(struct vmbus_channel * primary,unsigned long id)379831dba47SStephen Hemminger static bool vmbus_isnew_subchannel(struct vmbus_channel *primary,
380831dba47SStephen Hemminger 				   unsigned long id)
381831dba47SStephen Hemminger {
382831dba47SStephen Hemminger 	const struct vmbus_channel *c;
383831dba47SStephen Hemminger 
384831dba47SStephen Hemminger 	STAILQ_FOREACH(c, &primary->subchannel_list, next) {
385831dba47SStephen Hemminger 		if (c->relid == id)
386831dba47SStephen Hemminger 			return false;
387831dba47SStephen Hemminger 	}
388831dba47SStephen Hemminger 	return true;
389831dba47SStephen Hemminger }
390831dba47SStephen Hemminger 
vmbus_uio_get_subchan(struct vmbus_channel * primary,struct vmbus_channel ** subchan)391831dba47SStephen Hemminger int vmbus_uio_get_subchan(struct vmbus_channel *primary,
392831dba47SStephen Hemminger 			  struct vmbus_channel **subchan)
393831dba47SStephen Hemminger {
394831dba47SStephen Hemminger 	const struct rte_vmbus_device *dev = primary->device;
395831dba47SStephen Hemminger 	char chan_path[PATH_MAX], subchan_path[PATH_MAX];
396831dba47SStephen Hemminger 	struct dirent *ent;
397831dba47SStephen Hemminger 	DIR *chan_dir;
3986521c9a2SStephen Hemminger 	int err;
399831dba47SStephen Hemminger 
400831dba47SStephen Hemminger 	snprintf(chan_path, sizeof(chan_path),
401831dba47SStephen Hemminger 		 "%s/%s/channels",
402831dba47SStephen Hemminger 		 SYSFS_VMBUS_DEVICES, dev->device.name);
403831dba47SStephen Hemminger 
404831dba47SStephen Hemminger 	chan_dir = opendir(chan_path);
405831dba47SStephen Hemminger 	if (!chan_dir) {
406831dba47SStephen Hemminger 		VMBUS_LOG(ERR, "cannot open %s: %s",
407831dba47SStephen Hemminger 			  chan_path, strerror(errno));
408831dba47SStephen Hemminger 		return -errno;
409831dba47SStephen Hemminger 	}
410831dba47SStephen Hemminger 
411831dba47SStephen Hemminger 	while ((ent = readdir(chan_dir))) {
412831dba47SStephen Hemminger 		unsigned long relid, subid, monid;
413831dba47SStephen Hemminger 		char *endp;
414831dba47SStephen Hemminger 
415831dba47SStephen Hemminger 		if (ent->d_name[0] == '.')
416831dba47SStephen Hemminger 			continue;
417831dba47SStephen Hemminger 
418831dba47SStephen Hemminger 		errno = 0;
419831dba47SStephen Hemminger 		relid = strtoul(ent->d_name, &endp, 0);
420831dba47SStephen Hemminger 		if (*endp || errno != 0 || relid > UINT16_MAX) {
421831dba47SStephen Hemminger 			VMBUS_LOG(NOTICE, "not a valid channel relid: %s",
422831dba47SStephen Hemminger 				  ent->d_name);
423831dba47SStephen Hemminger 			continue;
424831dba47SStephen Hemminger 		}
425831dba47SStephen Hemminger 
42607938364SStephen Hemminger 		if (!vmbus_isnew_subchannel(primary, relid)) {
42707938364SStephen Hemminger 			VMBUS_LOG(DEBUG, "skip already found channel: %lu",
42807938364SStephen Hemminger 				  relid);
42907938364SStephen Hemminger 			continue;
43007938364SStephen Hemminger 		}
4314970103eSStephen Hemminger 
43207938364SStephen Hemminger 		if (!vmbus_uio_ring_present(dev, relid)) {
43307938364SStephen Hemminger 			VMBUS_LOG(DEBUG, "ring mmap not found (yet) for: %lu",
43407938364SStephen Hemminger 				  relid);
43507938364SStephen Hemminger 			continue;
43607938364SStephen Hemminger 		}
4374970103eSStephen Hemminger 
438831dba47SStephen Hemminger 		snprintf(subchan_path, sizeof(subchan_path), "%s/%lu",
439831dba47SStephen Hemminger 			 chan_path, relid);
440831dba47SStephen Hemminger 		err = vmbus_uio_sysfs_read(subchan_path, "subchannel_id",
441831dba47SStephen Hemminger 					   &subid, UINT16_MAX);
442831dba47SStephen Hemminger 		if (err) {
44307938364SStephen Hemminger 			VMBUS_LOG(NOTICE, "no subchannel_id in %s:%s",
44407938364SStephen Hemminger 				  subchan_path, strerror(-err));
4456521c9a2SStephen Hemminger 			goto fail;
446831dba47SStephen Hemminger 		}
447831dba47SStephen Hemminger 
448831dba47SStephen Hemminger 		if (subid == 0)
449831dba47SStephen Hemminger 			continue;	/* skip primary channel */
450831dba47SStephen Hemminger 
451831dba47SStephen Hemminger 		err = vmbus_uio_sysfs_read(subchan_path, "monitor_id",
452831dba47SStephen Hemminger 					   &monid, UINT8_MAX);
453831dba47SStephen Hemminger 		if (err) {
45407938364SStephen Hemminger 			VMBUS_LOG(NOTICE, "no monitor_id in %s:%s",
45507938364SStephen Hemminger 				  subchan_path, strerror(-err));
4566521c9a2SStephen Hemminger 			goto fail;
457831dba47SStephen Hemminger 		}
458831dba47SStephen Hemminger 
459831dba47SStephen Hemminger 		err = vmbus_chan_create(dev, relid, subid, monid, subchan);
460831dba47SStephen Hemminger 		if (err) {
46107938364SStephen Hemminger 			VMBUS_LOG(ERR, "subchannel setup failed");
4626521c9a2SStephen Hemminger 			goto fail;
463831dba47SStephen Hemminger 		}
464831dba47SStephen Hemminger 		break;
465831dba47SStephen Hemminger 	}
466831dba47SStephen Hemminger 	closedir(chan_dir);
467831dba47SStephen Hemminger 
468831dba47SStephen Hemminger 	return (ent == NULL) ? -ENOENT : 0;
4696521c9a2SStephen Hemminger fail:
4706521c9a2SStephen Hemminger 	closedir(chan_dir);
4716521c9a2SStephen Hemminger 	return err;
472831dba47SStephen Hemminger }
473