vfio: support hotplug
[dpdk.git] / lib / librte_eal / linuxapp / eal / eal_vfio.c
index 9377a66..6e2e84c 100644 (file)
@@ -68,13 +68,32 @@ vfio_get_group_fd(int iommu_group_no)
 {
        int i;
        int vfio_group_fd;
+       int group_idx = -1;
        char filename[PATH_MAX];
 
        /* check if we already have the group descriptor open */
-       for (i = 0; i < vfio_cfg.vfio_group_idx; i++)
+       for (i = 0; i < VFIO_MAX_GROUPS; i++)
                if (vfio_cfg.vfio_groups[i].group_no == iommu_group_no)
                        return vfio_cfg.vfio_groups[i].fd;
 
+       /* Lets see first if there is room for a new group */
+       if (vfio_cfg.vfio_active_groups == VFIO_MAX_GROUPS) {
+               RTE_LOG(ERR, EAL, "Maximum number of VFIO groups reached!\n");
+               return -1;
+       }
+
+       /* Now lets get an index for the new group */
+       for (i = 0; i < VFIO_MAX_GROUPS; i++)
+               if (vfio_cfg.vfio_groups[i].group_no == -1) {
+                       group_idx = i;
+                       break;
+               }
+
+       /* This should not happen */
+       if (group_idx == -1) {
+               RTE_LOG(ERR, EAL, "No VFIO group free slot found\n");
+               return -1;
+       }
        /* if primary, try to open the group */
        if (internal_config.process_type == RTE_PROC_PRIMARY) {
                /* try regular group format */
@@ -104,14 +123,9 @@ vfio_get_group_fd(int iommu_group_no)
                        /* noiommu group found */
                }
 
-               /* if the fd is valid, create a new group for it */
-               if (vfio_cfg.vfio_group_idx == VFIO_MAX_GROUPS) {
-                       RTE_LOG(ERR, EAL, "Maximum number of VFIO groups reached!\n");
-                       close(vfio_group_fd);
-                       return -1;
-               }
-               vfio_cfg.vfio_groups[vfio_cfg.vfio_group_idx].group_no = iommu_group_no;
-               vfio_cfg.vfio_groups[vfio_cfg.vfio_group_idx].fd = vfio_group_fd;
+               vfio_cfg.vfio_groups[group_idx].group_no = iommu_group_no;
+               vfio_cfg.vfio_groups[group_idx].fd = vfio_group_fd;
+               vfio_cfg.vfio_active_groups++;
                return vfio_group_fd;
        }
        /* if we're in a secondary process, request group fd from the primary
@@ -158,14 +172,66 @@ vfio_get_group_fd(int iommu_group_no)
        return -1;
 }
 
-static void
-clear_current_group(void)
+int
+clear_group(int vfio_group_fd)
 {
-       vfio_cfg.vfio_groups[vfio_cfg.vfio_group_idx].group_no = 0;
-       vfio_cfg.vfio_groups[vfio_cfg.vfio_group_idx].fd = -1;
+       int i;
+       int socket_fd, ret;
+
+       if (internal_config.process_type == RTE_PROC_PRIMARY) {
+
+               for (i = 0; i < VFIO_MAX_GROUPS; i++)
+                       if (vfio_cfg.vfio_groups[i].fd == vfio_group_fd) {
+                               vfio_cfg.vfio_groups[i].group_no = -1;
+                               vfio_cfg.vfio_groups[i].fd = -1;
+                               vfio_cfg.vfio_active_groups--;
+                               return 0;
+                       }
+               return -1;
+       }
+
+       /* This is just for SECONDARY processes */
+       socket_fd = vfio_mp_sync_connect_to_primary();
+
+       if (socket_fd < 0) {
+               RTE_LOG(ERR, EAL, "  cannot connect to primary process!\n");
+               return -1;
+       }
+
+       if (vfio_mp_sync_send_request(socket_fd, SOCKET_CLR_GROUP) < 0) {
+               RTE_LOG(ERR, EAL, "  cannot request container fd!\n");
+               close(socket_fd);
+               return -1;
+       }
+
+       if (vfio_mp_sync_send_request(socket_fd, vfio_group_fd) < 0) {
+               RTE_LOG(ERR, EAL, "  cannot send group fd!\n");
+               close(socket_fd);
+               return -1;
+       }
+
+       ret = vfio_mp_sync_receive_request(socket_fd);
+       switch (ret) {
+       case SOCKET_NO_FD:
+               RTE_LOG(ERR, EAL, "  BAD VFIO group fd!\n");
+               close(socket_fd);
+               break;
+       case SOCKET_OK:
+               close(socket_fd);
+               return 0;
+       case SOCKET_ERR:
+               RTE_LOG(ERR, EAL, "  Socket error\n");
+               close(socket_fd);
+               break;
+       default:
+               RTE_LOG(ERR, EAL, "  UNKNOWN reply, %d\n", ret);
+               close(socket_fd);
+       }
+       return -1;
 }
 
-int vfio_setup_device(const char *sysfs_base, const char *dev_addr,
+int
+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 = {
@@ -192,18 +258,10 @@ int vfio_setup_device(const char *sysfs_base, const char *dev_addr,
        if (vfio_group_fd < 0)
                return -1;
 
-       /* store group fd */
-       vfio_cfg.vfio_groups[vfio_cfg.vfio_group_idx].group_no = iommu_group_no;
-       vfio_cfg.vfio_groups[vfio_cfg.vfio_group_idx].fd = vfio_group_fd;
-
        /* 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",
+               RTE_LOG(WARNING, EAL, " %s not managed by VFIO driver, skipping\n",
                                dev_addr);
-               /* we store 0 as group fd to distinguish between existing but
-                * unbound VFIO groups, and groups that don't exist at all.
-                */
-               vfio_cfg.vfio_group_idx++;
                return 1;
        }
 
@@ -218,12 +276,12 @@ int vfio_setup_device(const char *sysfs_base, const char *dev_addr,
                RTE_LOG(ERR, EAL, "  %s cannot get group status, "
                                "error %i (%s)\n", dev_addr, errno, strerror(errno));
                close(vfio_group_fd);
-               clear_current_group();
+               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_current_group();
+               clear_group(vfio_group_fd);
                return -1;
        }
 
@@ -237,64 +295,116 @@ int vfio_setup_device(const char *sysfs_base, const char *dev_addr,
                        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_current_group();
+                       clear_group(vfio_group_fd);
                        return -1;
                }
-               /*
-                * at this point we know that this group has been successfully
-                * initialized, so we increment vfio_group_idx to indicate that we can
-                * add new groups.
-                */
-               vfio_cfg.vfio_group_idx++;
        }
 
        /*
         * pick an IOMMU type and set up DMA mappings for container
         *
-        * needs to be done only once, only when at least one group is assigned to
-        * a container and only in primary process
+        * 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_container_has_dma == 0) {
+                       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;
                }
-               vfio_cfg.vfio_container_has_dma = 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 simply means that this
-               * particular port is not bound to VFIO
-               */
-               RTE_LOG(WARNING, EAL, "  %s not managed by VFIO driver, skipping\n",
+               /* 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);
-               return 1;
+               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));
+                               "error %i (%s)\n", dev_addr, errno,
+                               strerror(errno));
                close(*vfio_dev_fd);
+               close(vfio_group_fd);
+               clear_group(vfio_group_fd);
                return -1;
        }
 
        return 0;
 }
 
+int
+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.
+        */
+
+       if (close(vfio_group_fd) < 0)
+               RTE_LOG(INFO, EAL, "Error when closing vfio_group_fd for %s\n",
+                                  dev_addr);
+
+       if (close(vfio_dev_fd) < 0)
+               RTE_LOG(INFO, EAL, "Error when closing vfio_dev_fd for %s\n",
+                                  dev_addr);
+
+       if (clear_group(vfio_group_fd) < 0)
+               RTE_LOG(INFO, EAL, "Error when clearing group for %s\n",
+                                  dev_addr);
+
+       return 0;
+}
+
 int
 vfio_enable(const char *modname)
 {
@@ -534,7 +644,8 @@ vfio_type1_dma_map(int vfio_container_fd)
 
                if (ret) {
                        RTE_LOG(ERR, EAL, "  cannot set up DMA remapping, "
-                                       "error %i (%s)\n", errno, strerror(errno));
+                                         "error %i (%s)\n", errno,
+                                         strerror(errno));
                        return -1;
                }
        }