1 /* SPDX-License-Identifier: BSD-3-Clause
2 * Copyright (c) 2018, Microsoft Corporation.
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>
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)
43 cc = read(dev->intr_handle.fd, &count, sizeof(count));
44 if (cc < (int)sizeof(count)) {
46 VMBUS_LOG(ERR, "IRQ read failed %s",
50 VMBUS_LOG(ERR, "can't read IRQ count");
58 vmbus_uio_free_resource(struct rte_vmbus_device *dev,
59 struct mapped_vmbus_resource *uio_res)
63 if (dev->intr_handle.uio_cfg_fd >= 0) {
64 close(dev->intr_handle.uio_cfg_fd);
65 dev->intr_handle.uio_cfg_fd = -1;
68 if (dev->intr_handle.fd >= 0) {
69 close(dev->intr_handle.fd);
70 dev->intr_handle.fd = -1;
71 dev->intr_handle.type = RTE_INTR_HANDLE_UNKNOWN;
76 vmbus_uio_alloc_resource(struct rte_vmbus_device *dev,
77 struct mapped_vmbus_resource **uio_res)
79 char devname[PATH_MAX]; /* contains the /dev/uioX */
81 /* save fd if in primary process */
82 snprintf(devname, sizeof(devname), "/dev/uio%u", dev->uio_num);
83 dev->intr_handle.fd = open(devname, O_RDWR);
84 if (dev->intr_handle.fd < 0) {
85 VMBUS_LOG(ERR, "Cannot open %s: %s",
86 devname, strerror(errno));
89 dev->intr_handle.type = RTE_INTR_HANDLE_UIO_INTX;
91 /* allocate the mapping details for secondary processes*/
92 *uio_res = rte_zmalloc("UIO_RES", sizeof(**uio_res), 0);
93 if (*uio_res == NULL) {
94 VMBUS_LOG(ERR, "cannot store uio mmap details");
98 strlcpy((*uio_res)->path, devname, PATH_MAX);
99 rte_uuid_copy((*uio_res)->id, dev->device_id);
104 vmbus_uio_free_resource(dev, *uio_res);
109 find_max_end_va(const struct rte_memseg_list *msl, void *arg)
111 size_t sz = msl->memseg_arr.len * msl->page_sz;
112 void *end_va = RTE_PTR_ADD(msl->base_va, sz);
115 if (*max_va < end_va)
121 * TODO: this should be part of memseg api.
122 * code is duplicated from PCI.
125 vmbus_find_max_end_va(void)
129 rte_memseg_list_walk(find_max_end_va, &va);
134 vmbus_uio_map_resource_by_index(struct rte_vmbus_device *dev, int idx,
135 struct mapped_vmbus_resource *uio_res,
138 size_t size = dev->resource[idx].len;
139 struct vmbus_map *maps = uio_res->maps;
144 /* devname for mmap */
145 fd = open(uio_res->path, O_RDWR);
147 VMBUS_LOG(ERR, "Cannot open %s: %s",
148 uio_res->path, strerror(errno));
152 /* try mapping somewhere close to the end of hugepages */
153 if (vmbus_map_addr == NULL)
154 vmbus_map_addr = vmbus_find_max_end_va();
156 /* offset is special in uio it indicates which resource */
157 offset = idx * PAGE_SIZE;
159 mapaddr = vmbus_map_resource(vmbus_map_addr, fd, offset, size, flags);
162 if (mapaddr == MAP_FAILED)
165 dev->resource[idx].addr = mapaddr;
166 vmbus_map_addr = RTE_PTR_ADD(mapaddr, size);
168 /* Record result of successful mapping for use by secondary */
169 maps[idx].addr = mapaddr;
170 maps[idx].size = size;
175 static int vmbus_uio_map_primary(struct vmbus_channel *chan,
176 void **ring_buf, uint32_t *ring_size)
178 struct mapped_vmbus_resource *uio_res;
180 uio_res = vmbus_uio_find_resource(chan->device);
182 VMBUS_LOG(ERR, "can not find resources!");
186 if (uio_res->nb_maps < VMBUS_MAX_RESOURCE) {
187 VMBUS_LOG(ERR, "VMBUS: only %u resources found!",
192 *ring_size = uio_res->maps[HV_TXRX_RING_MAP].size / 2;
193 *ring_buf = uio_res->maps[HV_TXRX_RING_MAP].addr;
197 static int vmbus_uio_map_subchan(const struct rte_vmbus_device *dev,
198 const struct vmbus_channel *chan,
199 void **ring_buf, uint32_t *ring_size)
201 char ring_path[PATH_MAX];
207 snprintf(ring_path, sizeof(ring_path),
208 "%s/%s/channels/%u/ring",
209 SYSFS_VMBUS_DEVICES, dev->device.name,
212 fd = open(ring_path, O_RDWR);
214 VMBUS_LOG(ERR, "Cannot open %s: %s",
215 ring_path, strerror(errno));
219 if (fstat(fd, &sb) < 0) {
220 VMBUS_LOG(ERR, "Cannot state %s: %s",
221 ring_path, strerror(errno));
225 file_size = sb.st_size;
227 if (file_size == 0 || (file_size & (PAGE_SIZE - 1))) {
228 VMBUS_LOG(ERR, "incorrect size %s: %zu",
229 ring_path, file_size);
235 mapaddr = vmbus_map_resource(vmbus_map_addr, fd,
239 if (mapaddr == MAP_FAILED)
242 *ring_size = file_size / 2;
245 vmbus_map_addr = RTE_PTR_ADD(mapaddr, file_size);
250 vmbus_uio_map_secondary_subchan(const struct rte_vmbus_device *dev,
251 const struct vmbus_channel *chan)
253 const struct vmbus_br *br = &chan->txbr;
254 char ring_path[PATH_MAX];
255 void *mapaddr, *ring_buf;
259 snprintf(ring_path, sizeof(ring_path),
260 "%s/%s/channels/%u/ring",
261 SYSFS_VMBUS_DEVICES, dev->device.name,
265 ring_size = br->dsize + sizeof(struct vmbus_bufring);
266 VMBUS_LOG(INFO, "secondary ring_buf %p size %u",
267 ring_buf, ring_size);
269 fd = open(ring_path, O_RDWR);
271 VMBUS_LOG(ERR, "Cannot open %s: %s",
272 ring_path, strerror(errno));
276 mapaddr = vmbus_map_resource(ring_buf, fd, 0, 2 * ring_size, 0);
279 if (mapaddr == ring_buf)
282 if (mapaddr == MAP_FAILED)
284 "mmap subchan %u in secondary failed", chan->relid);
287 "mmap subchan %u in secondary address mismatch",
289 vmbus_unmap_resource(mapaddr, 2 * ring_size);
294 int vmbus_uio_map_rings(struct vmbus_channel *chan)
296 const struct rte_vmbus_device *dev = chan->device;
301 /* Primary channel */
302 if (chan->subchannel_id == 0)
303 ret = vmbus_uio_map_primary(chan, &ring_buf, &ring_size);
305 ret = vmbus_uio_map_subchan(dev, chan, &ring_buf, &ring_size);
310 vmbus_br_setup(&chan->txbr, ring_buf, ring_size);
311 vmbus_br_setup(&chan->rxbr, (char *)ring_buf + ring_size, ring_size);
315 static int vmbus_uio_sysfs_read(const char *dir, const char *name,
316 unsigned long *val, unsigned long max_range)
322 snprintf(path, sizeof(path), "%s/%s", dir, name);
323 f = fopen(path, "r");
325 VMBUS_LOG(ERR, "can't open %s:%s",
326 path, strerror(errno));
330 if (fscanf(f, "%lu", val) != 1)
332 else if (*val > max_range)
341 static bool vmbus_uio_ring_present(const struct rte_vmbus_device *dev,
344 char ring_path[PATH_MAX];
346 /* Check if kernel has subchannel sysfs files */
347 snprintf(ring_path, sizeof(ring_path),
348 "%s/%s/channels/%u/ring",
349 SYSFS_VMBUS_DEVICES, dev->device.name, relid);
351 return access(ring_path, R_OK|W_OK) == 0;
354 bool vmbus_uio_subchannels_supported(const struct rte_vmbus_device *dev,
355 const struct vmbus_channel *chan)
357 return vmbus_uio_ring_present(dev, chan->relid);
360 static bool vmbus_isnew_subchannel(struct vmbus_channel *primary,
363 const struct vmbus_channel *c;
365 STAILQ_FOREACH(c, &primary->subchannel_list, next) {
372 int vmbus_uio_get_subchan(struct vmbus_channel *primary,
373 struct vmbus_channel **subchan)
375 const struct rte_vmbus_device *dev = primary->device;
376 char chan_path[PATH_MAX], subchan_path[PATH_MAX];
381 snprintf(chan_path, sizeof(chan_path),
383 SYSFS_VMBUS_DEVICES, dev->device.name);
385 chan_dir = opendir(chan_path);
387 VMBUS_LOG(ERR, "cannot open %s: %s",
388 chan_path, strerror(errno));
392 while ((ent = readdir(chan_dir))) {
393 unsigned long relid, subid, monid;
396 if (ent->d_name[0] == '.')
400 relid = strtoul(ent->d_name, &endp, 0);
401 if (*endp || errno != 0 || relid > UINT16_MAX) {
402 VMBUS_LOG(NOTICE, "not a valid channel relid: %s",
407 if (!vmbus_isnew_subchannel(primary, relid)) {
408 VMBUS_LOG(DEBUG, "skip already found channel: %lu",
413 if (!vmbus_uio_ring_present(dev, relid)) {
414 VMBUS_LOG(DEBUG, "ring mmap not found (yet) for: %lu",
419 snprintf(subchan_path, sizeof(subchan_path), "%s/%lu",
421 err = vmbus_uio_sysfs_read(subchan_path, "subchannel_id",
424 VMBUS_LOG(NOTICE, "no subchannel_id in %s:%s",
425 subchan_path, strerror(-err));
430 continue; /* skip primary channel */
432 err = vmbus_uio_sysfs_read(subchan_path, "monitor_id",
435 VMBUS_LOG(NOTICE, "no monitor_id in %s:%s",
436 subchan_path, strerror(-err));
440 err = vmbus_chan_create(dev, relid, subid, monid, subchan);
442 VMBUS_LOG(ERR, "subchannel setup failed");
449 return (ent == NULL) ? -ENOENT : 0;