示例#1
0
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);
	}
}