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