1 /* SPDX-License-Identifier: BSD-3-Clause
2 * Copyright (c) 2018, Microsoft Corporation.
17 #include <rte_memory.h>
18 #include <rte_common.h>
19 #include <rte_malloc.h>
20 #include <rte_bus_vmbus.h>
21 #include <rte_string_fns.h>
25 /** Pathname of VMBUS devices directory. */
26 #define SYSFS_VMBUS_DEVICES "/sys/bus/vmbus/devices"
28 static void *vmbus_map_addr;
30 /* Control interrupts */
31 void vmbus_uio_irq_control(struct rte_vmbus_device *dev, int32_t onoff)
33 if (write(rte_intr_fd_get(dev->intr_handle), &onoff,
35 VMBUS_LOG(ERR, "cannot write to %d:%s",
36 rte_intr_fd_get(dev->intr_handle),
41 int vmbus_uio_irq_read(struct rte_vmbus_device *dev)
46 cc = read(rte_intr_fd_get(dev->intr_handle), &count,
48 if (cc < (int)sizeof(count)) {
50 VMBUS_LOG(ERR, "IRQ read failed %s",
54 VMBUS_LOG(ERR, "can't read IRQ count");
62 vmbus_uio_free_resource(struct rte_vmbus_device *dev,
63 struct mapped_vmbus_resource *uio_res)
67 if (rte_intr_dev_fd_get(dev->intr_handle) >= 0) {
68 close(rte_intr_dev_fd_get(dev->intr_handle));
69 rte_intr_dev_fd_set(dev->intr_handle, -1);
72 if (rte_intr_fd_get(dev->intr_handle) >= 0) {
73 close(rte_intr_fd_get(dev->intr_handle));
74 rte_intr_fd_set(dev->intr_handle, -1);
75 rte_intr_type_set(dev->intr_handle, RTE_INTR_HANDLE_UNKNOWN);
80 vmbus_uio_alloc_resource(struct rte_vmbus_device *dev,
81 struct mapped_vmbus_resource **uio_res)
83 char devname[PATH_MAX]; /* contains the /dev/uioX */
86 /* save fd if in primary process */
87 snprintf(devname, sizeof(devname), "/dev/uio%u", dev->uio_num);
88 fd = open(devname, O_RDWR);
90 VMBUS_LOG(ERR, "Cannot open %s: %s",
91 devname, strerror(errno));
95 if (rte_intr_fd_set(dev->intr_handle, fd))
98 if (rte_intr_type_set(dev->intr_handle, RTE_INTR_HANDLE_UIO_INTX))
101 /* allocate the mapping details for secondary processes*/
102 *uio_res = rte_zmalloc("UIO_RES", sizeof(**uio_res), 0);
103 if (*uio_res == NULL) {
104 VMBUS_LOG(ERR, "cannot store uio mmap details");
108 strlcpy((*uio_res)->path, devname, PATH_MAX);
109 rte_uuid_copy((*uio_res)->id, dev->device_id);
114 vmbus_uio_free_resource(dev, *uio_res);
119 find_max_end_va(const struct rte_memseg_list *msl, void *arg)
121 size_t sz = msl->memseg_arr.len * msl->page_sz;
122 void *end_va = RTE_PTR_ADD(msl->base_va, sz);
125 if (*max_va < end_va)
131 * TODO: this should be part of memseg api.
132 * code is duplicated from PCI.
135 vmbus_find_max_end_va(void)
139 rte_memseg_list_walk(find_max_end_va, &va);
144 vmbus_uio_map_resource_by_index(struct rte_vmbus_device *dev, int idx,
145 struct mapped_vmbus_resource *uio_res,
148 size_t size = dev->resource[idx].len;
149 struct vmbus_map *maps = uio_res->maps;
154 /* devname for mmap */
155 fd = open(uio_res->path, O_RDWR);
157 VMBUS_LOG(ERR, "Cannot open %s: %s",
158 uio_res->path, strerror(errno));
162 /* try mapping somewhere close to the end of hugepages */
163 if (vmbus_map_addr == NULL)
164 vmbus_map_addr = vmbus_find_max_end_va();
166 /* offset is special in uio it indicates which resource */
167 offset = idx * rte_mem_page_size();
169 mapaddr = vmbus_map_resource(vmbus_map_addr, fd, offset, size, flags);
172 if (mapaddr == MAP_FAILED)
175 dev->resource[idx].addr = mapaddr;
176 vmbus_map_addr = RTE_PTR_ADD(mapaddr, size);
178 /* Record result of successful mapping for use by secondary */
179 maps[idx].addr = mapaddr;
180 maps[idx].size = size;
185 static int vmbus_uio_map_primary(struct vmbus_channel *chan,
186 void **ring_buf, uint32_t *ring_size)
188 struct mapped_vmbus_resource *uio_res;
190 uio_res = vmbus_uio_find_resource(chan->device);
192 VMBUS_LOG(ERR, "can not find resources!");
196 if (uio_res->nb_maps < VMBUS_MAX_RESOURCE) {
197 VMBUS_LOG(ERR, "VMBUS: only %u resources found!",
202 *ring_size = uio_res->maps[HV_TXRX_RING_MAP].size / 2;
203 *ring_buf = uio_res->maps[HV_TXRX_RING_MAP].addr;
207 static int vmbus_uio_map_subchan(const struct rte_vmbus_device *dev,
208 const struct vmbus_channel *chan,
209 void **ring_buf, uint32_t *ring_size)
211 char ring_path[PATH_MAX];
216 struct mapped_vmbus_resource *uio_res;
219 uio_res = vmbus_uio_find_resource(dev);
221 VMBUS_LOG(ERR, "can not find resources for mapping subchan");
225 if (rte_eal_process_type() == RTE_PROC_PRIMARY) {
226 if (uio_res->nb_subchannels >= UIO_MAX_SUBCHANNEL) {
228 "exceeding max subchannels UIO_MAX_SUBCHANNEL(%d)",
230 VMBUS_LOG(ERR, "Change UIO_MAX_SUBCHANNEL and recompile");
234 for (channel_idx = 0; channel_idx < uio_res->nb_subchannels;
236 if (uio_res->subchannel_maps[channel_idx].relid ==
239 if (channel_idx == uio_res->nb_subchannels) {
241 "couldn't find sub channel %d from shared mapping in primary",
245 vmbus_map_addr = uio_res->subchannel_maps[channel_idx].addr;
248 snprintf(ring_path, sizeof(ring_path),
249 "%s/%s/channels/%u/ring",
250 SYSFS_VMBUS_DEVICES, dev->device.name,
253 fd = open(ring_path, O_RDWR);
255 VMBUS_LOG(ERR, "Cannot open %s: %s",
256 ring_path, strerror(errno));
260 if (fstat(fd, &sb) < 0) {
261 VMBUS_LOG(ERR, "Cannot state %s: %s",
262 ring_path, strerror(errno));
266 file_size = sb.st_size;
268 if (file_size == 0 || (file_size & (rte_mem_page_size() - 1))) {
269 VMBUS_LOG(ERR, "incorrect size %s: %zu",
270 ring_path, file_size);
276 mapaddr = vmbus_map_resource(vmbus_map_addr, fd,
280 if (mapaddr == MAP_FAILED)
283 if (rte_eal_process_type() == RTE_PROC_PRIMARY) {
285 /* Add this mapping to uio_res for use by secondary */
286 uio_res->subchannel_maps[uio_res->nb_subchannels].relid =
288 uio_res->subchannel_maps[uio_res->nb_subchannels].addr =
290 uio_res->subchannel_maps[uio_res->nb_subchannels].size =
292 uio_res->nb_subchannels++;
294 vmbus_map_addr = RTE_PTR_ADD(mapaddr, file_size);
296 if (mapaddr != vmbus_map_addr) {
297 VMBUS_LOG(ERR, "failed to map channel %d to addr %p",
298 chan->relid, mapaddr);
299 vmbus_unmap_resource(mapaddr, file_size);
304 *ring_size = file_size / 2;
310 int vmbus_uio_map_rings(struct vmbus_channel *chan)
312 const struct rte_vmbus_device *dev = chan->device;
317 /* Primary channel */
318 if (chan->subchannel_id == 0)
319 ret = vmbus_uio_map_primary(chan, &ring_buf, &ring_size);
321 ret = vmbus_uio_map_subchan(dev, chan, &ring_buf, &ring_size);
326 vmbus_br_setup(&chan->txbr, ring_buf, ring_size);
327 vmbus_br_setup(&chan->rxbr, (char *)ring_buf + ring_size, ring_size);
331 static int vmbus_uio_sysfs_read(const char *dir, const char *name,
332 unsigned long *val, unsigned long max_range)
338 snprintf(path, sizeof(path), "%s/%s", dir, name);
339 f = fopen(path, "r");
341 VMBUS_LOG(ERR, "can't open %s:%s",
342 path, strerror(errno));
346 if (fscanf(f, "%lu", val) != 1)
348 else if (*val > max_range)
357 static bool vmbus_uio_ring_present(const struct rte_vmbus_device *dev,
360 char ring_path[PATH_MAX];
362 /* Check if kernel has subchannel sysfs files */
363 snprintf(ring_path, sizeof(ring_path),
364 "%s/%s/channels/%u/ring",
365 SYSFS_VMBUS_DEVICES, dev->device.name, relid);
367 return access(ring_path, R_OK|W_OK) == 0;
370 bool vmbus_uio_subchannels_supported(const struct rte_vmbus_device *dev,
371 const struct vmbus_channel *chan)
373 return vmbus_uio_ring_present(dev, chan->relid);
376 static bool vmbus_isnew_subchannel(struct vmbus_channel *primary,
379 const struct vmbus_channel *c;
381 STAILQ_FOREACH(c, &primary->subchannel_list, next) {
388 int vmbus_uio_get_subchan(struct vmbus_channel *primary,
389 struct vmbus_channel **subchan)
391 const struct rte_vmbus_device *dev = primary->device;
392 char chan_path[PATH_MAX], subchan_path[PATH_MAX];
397 snprintf(chan_path, sizeof(chan_path),
399 SYSFS_VMBUS_DEVICES, dev->device.name);
401 chan_dir = opendir(chan_path);
403 VMBUS_LOG(ERR, "cannot open %s: %s",
404 chan_path, strerror(errno));
408 while ((ent = readdir(chan_dir))) {
409 unsigned long relid, subid, monid;
412 if (ent->d_name[0] == '.')
416 relid = strtoul(ent->d_name, &endp, 0);
417 if (*endp || errno != 0 || relid > UINT16_MAX) {
418 VMBUS_LOG(NOTICE, "not a valid channel relid: %s",
423 if (!vmbus_isnew_subchannel(primary, relid)) {
424 VMBUS_LOG(DEBUG, "skip already found channel: %lu",
429 if (!vmbus_uio_ring_present(dev, relid)) {
430 VMBUS_LOG(DEBUG, "ring mmap not found (yet) for: %lu",
435 snprintf(subchan_path, sizeof(subchan_path), "%s/%lu",
437 err = vmbus_uio_sysfs_read(subchan_path, "subchannel_id",
440 VMBUS_LOG(NOTICE, "no subchannel_id in %s:%s",
441 subchan_path, strerror(-err));
446 continue; /* skip primary channel */
448 err = vmbus_uio_sysfs_read(subchan_path, "monitor_id",
451 VMBUS_LOG(NOTICE, "no monitor_id in %s:%s",
452 subchan_path, strerror(-err));
456 err = vmbus_chan_create(dev, relid, subid, monid, subchan);
458 VMBUS_LOG(ERR, "subchannel setup failed");
465 return (ent == NULL) ? -ENOENT : 0;