int rte_vfio_setup_device(const char *sysfs_base, const char *dev_addr, int *vfio_dev_fd, struct vfio_device_info *device_info) { struct vfio_group_status group_status = { .argsz = sizeof(group_status) }; int vfio_group_fd; int iommu_group_no; int ret; /* get group number */ ret = vfio_get_group_no(sysfs_base, dev_addr, &iommu_group_no); if (ret == 0) { RTE_LOG(WARNING, EAL, " %s not managed by VFIO driver, skipping\n", dev_addr); return 1; } /* if negative, something failed */ if (ret < 0) return -1; /* get the actual group fd */ vfio_group_fd = vfio_get_group_fd(iommu_group_no); if (vfio_group_fd < 0) return -1; /* if group_fd == 0, that means the device isn't managed by VFIO */ if (vfio_group_fd == 0) { RTE_LOG(WARNING, EAL, " %s not managed by VFIO driver, skipping\n", dev_addr); return 1; } /* * at this point, we know that this group is viable (meaning, all devices * are either bound to VFIO or not bound to anything) */ /* check if the group is viable */ ret = ioctl(vfio_group_fd, VFIO_GROUP_GET_STATUS, &group_status); if (ret) { RTE_LOG(ERR, EAL, " %s cannot get group status, " "error %i (%s)\n", dev_addr, errno, strerror(errno)); close(vfio_group_fd); clear_group(vfio_group_fd); return -1; } else if (!(group_status.flags & VFIO_GROUP_FLAGS_VIABLE)) { RTE_LOG(ERR, EAL, " %s VFIO group is not viable!\n", dev_addr); close(vfio_group_fd); clear_group(vfio_group_fd); return -1; } /* check if group does not have a container yet */ if (!(group_status.flags & VFIO_GROUP_FLAGS_CONTAINER_SET)) { /* add group to a container */ ret = ioctl(vfio_group_fd, VFIO_GROUP_SET_CONTAINER, &vfio_cfg.vfio_container_fd); if (ret) { RTE_LOG(ERR, EAL, " %s cannot add VFIO group to container, " "error %i (%s)\n", dev_addr, errno, strerror(errno)); close(vfio_group_fd); clear_group(vfio_group_fd); return -1; } /* * pick an IOMMU type and set up DMA mappings for container * * needs to be done only once, only when first group is * assigned to a container and only in primary process. * Note this can happen several times with the hotplug * functionality. */ if (internal_config.process_type == RTE_PROC_PRIMARY && vfio_cfg.vfio_active_groups == 1) { /* select an IOMMU type which we will be using */ const struct vfio_iommu_type *t = vfio_set_iommu_type(vfio_cfg.vfio_container_fd); if (!t) { RTE_LOG(ERR, EAL, " %s failed to select IOMMU type\n", dev_addr); close(vfio_group_fd); clear_group(vfio_group_fd); return -1; } ret = t->dma_map_func(vfio_cfg.vfio_container_fd); if (ret) { RTE_LOG(ERR, EAL, " %s DMA remapping failed, error %i (%s)\n", dev_addr, errno, strerror(errno)); close(vfio_group_fd); clear_group(vfio_group_fd); return -1; } } } /* get a file descriptor for the device */ *vfio_dev_fd = ioctl(vfio_group_fd, VFIO_GROUP_GET_DEVICE_FD, dev_addr); if (*vfio_dev_fd < 0) { /* if we cannot get a device fd, this implies a problem with * the VFIO group or the container not having IOMMU configured. */ RTE_LOG(WARNING, EAL, "Getting a vfio_dev_fd for %s failed\n", dev_addr); close(vfio_group_fd); clear_group(vfio_group_fd); return -1; } /* test and setup the device */ ret = ioctl(*vfio_dev_fd, VFIO_DEVICE_GET_INFO, device_info); if (ret) { RTE_LOG(ERR, EAL, " %s cannot get device info, " "error %i (%s)\n", dev_addr, errno, strerror(errno)); close(*vfio_dev_fd); close(vfio_group_fd); clear_group(vfio_group_fd); return -1; } vfio_group_device_get(vfio_group_fd); return 0; } int rte_vfio_release_device(const char *sysfs_base, const char *dev_addr, int vfio_dev_fd) { struct vfio_group_status group_status = { .argsz = sizeof(group_status) }; int vfio_group_fd; int iommu_group_no; int ret; /* get group number */ ret = vfio_get_group_no(sysfs_base, dev_addr, &iommu_group_no); if (ret <= 0) { RTE_LOG(WARNING, EAL, " %s not managed by VFIO driver\n", dev_addr); /* This is an error at this point. */ return -1; } /* get the actual group fd */ vfio_group_fd = vfio_get_group_fd(iommu_group_no); if (vfio_group_fd <= 0) { RTE_LOG(INFO, EAL, "vfio_get_group_fd failed for %s\n", dev_addr); return -1; } /* At this point we got an active group. Closing it will make the * container detachment. If this is the last active group, VFIO kernel * code will unset the container and the IOMMU mappings. */ /* Closing a device */ if (close(vfio_dev_fd) < 0) { RTE_LOG(INFO, EAL, "Error when closing vfio_dev_fd for %s\n", dev_addr); return -1; } /* An VFIO group can have several devices attached. Just when there is * no devices remaining should the group be closed. */ vfio_group_device_put(vfio_group_fd); if (!vfio_group_device_count(vfio_group_fd)) { if (close(vfio_group_fd) < 0) { RTE_LOG(INFO, EAL, "Error when closing vfio_group_fd for %s\n", dev_addr); return -1; } if (clear_group(vfio_group_fd) < 0) { RTE_LOG(INFO, EAL, "Error when clearing group for %s\n", dev_addr); return -1; } } return 0; } int rte_vfio_enable(const char *modname) { /* initialize group list */ int i; int vfio_available; for (i = 0; i < VFIO_MAX_GROUPS; i++) { vfio_cfg.vfio_groups[i].fd = -1; vfio_cfg.vfio_groups[i].group_no = -1; vfio_cfg.vfio_groups[i].devices = 0; } /* inform the user that we are probing for VFIO */ RTE_LOG(INFO, EAL, "Probing VFIO support...\n"); /* check if vfio module is loaded */ vfio_available = rte_eal_check_module(modname); /* return error directly */ if (vfio_available == -1) { RTE_LOG(INFO, EAL, "Could not get loaded module details!\n"); return -1; } /* return 0 if VFIO modules not loaded */ if (vfio_available == 0) { RTE_LOG(DEBUG, EAL, "VFIO modules not loaded, " "skipping VFIO support...\n"); return 0; } vfio_cfg.vfio_container_fd = vfio_get_container_fd(); /* check if we have VFIO driver enabled */ if (vfio_cfg.vfio_container_fd != -1) { RTE_LOG(NOTICE, EAL, "VFIO support initialized\n"); vfio_cfg.vfio_enabled = 1; } else { RTE_LOG(NOTICE, EAL, "VFIO support could not be initialized\n"); } return 0; } int rte_vfio_is_enabled(const char *modname) { const int mod_available = rte_eal_check_module(modname) > 0; return vfio_cfg.vfio_enabled && mod_available; } const struct vfio_iommu_type * vfio_set_iommu_type(int vfio_container_fd) { unsigned idx; for (idx = 0; idx < RTE_DIM(iommu_types); idx++) { const struct vfio_iommu_type *t = &iommu_types[idx]; int ret = ioctl(vfio_container_fd, VFIO_SET_IOMMU, t->type_id); if (!ret) { RTE_LOG(NOTICE, EAL, " using IOMMU type %d (%s)\n", t->type_id, t->name); return t; } /* not an error, there may be more supported IOMMU types */ RTE_LOG(DEBUG, EAL, " set IOMMU type %d (%s) failed, " "error %i (%s)\n", t->type_id, t->name, errno, strerror(errno)); } /* if we didn't find a suitable IOMMU type, fail */ return NULL; }
/* * socket listening thread for primary process */ static __attribute__((noreturn)) void * vfio_mp_sync_thread(void __rte_unused * arg) { int ret, fd, vfio_group_no; /* wait for requests on the socket */ for (;;) { int conn_sock; struct sockaddr_un addr; socklen_t sockaddr_len = sizeof(addr); /* this is a blocking call */ conn_sock = accept(mp_socket_fd, (struct sockaddr *) &addr, &sockaddr_len); /* just restart on error */ if (conn_sock == -1) continue; /* set socket to linger after close */ struct linger l; l.l_onoff = 1; l.l_linger = 60; if (setsockopt(conn_sock, SOL_SOCKET, SO_LINGER, &l, sizeof(l)) < 0) RTE_LOG(WARNING, EAL, "Cannot set SO_LINGER option " "on listen socket (%s)\n", strerror(errno)); ret = vfio_mp_sync_receive_request(conn_sock); switch (ret) { case SOCKET_REQ_CONTAINER: fd = vfio_get_container_fd(); if (fd < 0) vfio_mp_sync_send_request(conn_sock, SOCKET_ERR); else vfio_mp_sync_send_fd(conn_sock, fd); break; case SOCKET_REQ_GROUP: /* wait for group number */ vfio_group_no = vfio_mp_sync_receive_request(conn_sock); if (vfio_group_no < 0) { close(conn_sock); continue; } fd = vfio_get_group_fd(vfio_group_no); if (fd < 0) vfio_mp_sync_send_request(conn_sock, SOCKET_ERR); /* if VFIO group exists but isn't bound to VFIO driver */ else if (fd == 0) vfio_mp_sync_send_request(conn_sock, SOCKET_NO_FD); /* if group exists and is bound to VFIO driver */ else { vfio_mp_sync_send_request(conn_sock, SOCKET_OK); vfio_mp_sync_send_fd(conn_sock, fd); } break; default: vfio_mp_sync_send_request(conn_sock, SOCKET_ERR); break; } close(conn_sock); } }