1 /* SPDX-License-Identifier: BSD-3-Clause
2 * Copyright (c) 2018, Microsoft Corporation.
16 #include <rte_memory.h>
17 #include <rte_eal_memconfig.h>
18 #include <rte_common.h>
19 #include <rte_malloc.h>
20 #include <rte_bus_vmbus.h>
24 /** Pathname of VMBUS devices directory. */
25 #define SYSFS_VMBUS_DEVICES "/sys/bus/vmbus/devices"
27 static void *vmbus_map_addr;
29 /* Control interrupts */
30 void vmbus_uio_irq_control(struct rte_vmbus_device *dev, int32_t onoff)
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));
38 int vmbus_uio_irq_read(struct rte_vmbus_device *dev)
42 if (read(dev->intr_handle.fd, &count, sizeof(count)) < 0) {
43 VMBUS_LOG(ERR, "cannot read to %d:%s",
44 dev->intr_handle.fd, strerror(errno));
52 vmbus_uio_free_resource(struct rte_vmbus_device *dev,
53 struct mapped_vmbus_resource *uio_res)
57 if (dev->intr_handle.uio_cfg_fd >= 0) {
58 close(dev->intr_handle.uio_cfg_fd);
59 dev->intr_handle.uio_cfg_fd = -1;
62 if (dev->intr_handle.fd >= 0) {
63 close(dev->intr_handle.fd);
64 dev->intr_handle.fd = -1;
65 dev->intr_handle.type = RTE_INTR_HANDLE_UNKNOWN;
70 vmbus_uio_alloc_resource(struct rte_vmbus_device *dev,
71 struct mapped_vmbus_resource **uio_res)
73 char devname[PATH_MAX]; /* contains the /dev/uioX */
75 /* save fd if in primary process */
76 snprintf(devname, sizeof(devname), "/dev/uio%u", dev->uio_num);
77 dev->intr_handle.fd = open(devname, O_RDWR);
78 if (dev->intr_handle.fd < 0) {
79 VMBUS_LOG(ERR, "Cannot open %s: %s",
80 devname, strerror(errno));
83 dev->intr_handle.type = RTE_INTR_HANDLE_UIO_INTX;
85 /* allocate the mapping details for secondary processes*/
86 *uio_res = rte_zmalloc("UIO_RES", sizeof(**uio_res), 0);
87 if (*uio_res == NULL) {
88 VMBUS_LOG(ERR, "cannot store uio mmap details");
92 strncpy((*uio_res)->path, devname, PATH_MAX);
93 rte_uuid_copy((*uio_res)->id, dev->device_id);
98 vmbus_uio_free_resource(dev, *uio_res);
103 find_max_end_va(const struct rte_memseg_list *msl, void *arg)
105 size_t sz = msl->memseg_arr.len * msl->page_sz;
106 void *end_va = RTE_PTR_ADD(msl->base_va, sz);
109 if (*max_va < end_va)
115 * TODO: this should be part of memseg api.
116 * code is duplicated from PCI.
119 vmbus_find_max_end_va(void)
123 rte_memseg_list_walk(find_max_end_va, &va);
128 vmbus_uio_map_resource_by_index(struct rte_vmbus_device *dev, int idx,
129 struct mapped_vmbus_resource *uio_res,
132 size_t size = dev->resource[idx].len;
133 struct vmbus_map *maps = uio_res->maps;
138 /* devname for mmap */
139 fd = open(uio_res->path, O_RDWR);
141 VMBUS_LOG(ERR, "Cannot open %s: %s",
142 uio_res->path, strerror(errno));
146 /* try mapping somewhere close to the end of hugepages */
147 if (vmbus_map_addr == NULL)
148 vmbus_map_addr = vmbus_find_max_end_va();
150 /* offset is special in uio it indicates which resource */
151 offset = idx * PAGE_SIZE;
153 mapaddr = vmbus_map_resource(vmbus_map_addr, fd, offset, size, flags);
156 if (mapaddr == MAP_FAILED)
159 dev->resource[idx].addr = mapaddr;
160 vmbus_map_addr = RTE_PTR_ADD(mapaddr, size);
162 /* Record result of sucessful mapping for use by secondary */
163 maps[idx].addr = mapaddr;
164 maps[idx].size = size;
169 static int vmbus_uio_map_primary(struct vmbus_channel *chan,
170 void **ring_buf, uint32_t *ring_size)
172 struct mapped_vmbus_resource *uio_res;
174 uio_res = vmbus_uio_find_resource(chan->device);
176 VMBUS_LOG(ERR, "can not find resources!");
180 if (uio_res->nb_maps < VMBUS_MAX_RESOURCE) {
181 VMBUS_LOG(ERR, "VMBUS: only %u resources found!",
186 *ring_size = uio_res->maps[HV_TXRX_RING_MAP].size / 2;
187 *ring_buf = uio_res->maps[HV_TXRX_RING_MAP].addr;
191 static int vmbus_uio_map_subchan(const struct rte_vmbus_device *dev,
192 const struct vmbus_channel *chan,
193 void **ring_buf, uint32_t *ring_size)
195 char ring_path[PATH_MAX];
200 snprintf(ring_path, sizeof(ring_path),
201 "%s/%s/channels/%u/ring",
202 SYSFS_VMBUS_DEVICES, dev->device.name,
205 fd = open(ring_path, O_RDWR);
207 VMBUS_LOG(ERR, "Cannot open %s: %s",
208 ring_path, strerror(errno));
212 if (fstat(fd, &sb) < 0) {
213 VMBUS_LOG(ERR, "Cannot state %s: %s",
214 ring_path, strerror(errno));
218 file_size = sb.st_size;
220 if (file_size == 0 || (file_size & (PAGE_SIZE - 1))) {
221 VMBUS_LOG(ERR, "incorrect size %s: %zu",
222 ring_path, file_size);
228 *ring_size = file_size / 2;
229 *ring_buf = vmbus_map_resource(vmbus_map_addr, fd,
233 if (ring_buf == MAP_FAILED)
236 vmbus_map_addr = RTE_PTR_ADD(ring_buf, file_size);
240 int vmbus_uio_map_rings(struct vmbus_channel *chan)
242 const struct rte_vmbus_device *dev = chan->device;
247 /* Primary channel */
248 if (chan->subchannel_id == 0)
249 ret = vmbus_uio_map_primary(chan, &ring_buf, &ring_size);
251 ret = vmbus_uio_map_subchan(dev, chan, &ring_buf, &ring_size);
256 vmbus_br_setup(&chan->txbr, ring_buf, ring_size);
257 vmbus_br_setup(&chan->rxbr, (char *)ring_buf + ring_size, ring_size);
261 static int vmbus_uio_sysfs_read(const char *dir, const char *name,
262 unsigned long *val, unsigned long max_range)
268 snprintf(path, sizeof(path), "%s/%s", dir, name);
269 f = fopen(path, "r");
271 VMBUS_LOG(ERR, "can't open %s:%s",
272 path, strerror(errno));
276 if (fscanf(f, "%lu", val) != 1)
278 else if (*val > max_range)
287 static bool vmbus_uio_ring_present(const struct rte_vmbus_device *dev,
290 char ring_path[PATH_MAX];
292 /* Check if kernel has subchannel sysfs files */
293 snprintf(ring_path, sizeof(ring_path),
294 "%s/%s/channels/%u/ring",
295 SYSFS_VMBUS_DEVICES, dev->device.name, relid);
297 return access(ring_path, R_OK|W_OK) == 0;
300 bool vmbus_uio_subchannels_supported(const struct rte_vmbus_device *dev,
301 const struct vmbus_channel *chan)
303 return vmbus_uio_ring_present(dev, chan->relid);
306 static bool vmbus_isnew_subchannel(struct vmbus_channel *primary,
309 const struct vmbus_channel *c;
311 STAILQ_FOREACH(c, &primary->subchannel_list, next) {
318 int vmbus_uio_get_subchan(struct vmbus_channel *primary,
319 struct vmbus_channel **subchan)
321 const struct rte_vmbus_device *dev = primary->device;
322 char chan_path[PATH_MAX], subchan_path[PATH_MAX];
326 snprintf(chan_path, sizeof(chan_path),
328 SYSFS_VMBUS_DEVICES, dev->device.name);
330 chan_dir = opendir(chan_path);
332 VMBUS_LOG(ERR, "cannot open %s: %s",
333 chan_path, strerror(errno));
337 while ((ent = readdir(chan_dir))) {
338 unsigned long relid, subid, monid;
342 if (ent->d_name[0] == '.')
346 relid = strtoul(ent->d_name, &endp, 0);
347 if (*endp || errno != 0 || relid > UINT16_MAX) {
348 VMBUS_LOG(NOTICE, "not a valid channel relid: %s",
353 snprintf(subchan_path, sizeof(subchan_path), "%s/%lu",
355 err = vmbus_uio_sysfs_read(subchan_path, "subchannel_id",
358 VMBUS_LOG(NOTICE, "invalid subchannel id %lu",
364 continue; /* skip primary channel */
366 if (!vmbus_isnew_subchannel(primary, relid))
369 if (!vmbus_uio_ring_present(dev, relid))
370 continue; /* Ring may not be ready yet */
372 err = vmbus_uio_sysfs_read(subchan_path, "monitor_id",
375 VMBUS_LOG(NOTICE, "invalid monitor id %lu",
380 err = vmbus_chan_create(dev, relid, subid, monid, subchan);
382 VMBUS_LOG(NOTICE, "subchannel setup failed");
389 return (ent == NULL) ? -ENOENT : 0;