{
struct vfio_group *group = container_of(kref, struct vfio_group, kref);
struct vfio_unbound_dev *unbound, *tmp;
+ struct iommu_group *iommu_group = group->iommu_group;
WARN_ON(!list_empty(&group->device_list));
list_del(&group->vfio_next);
vfio_free_group_minor(group->minor);
vfio_group_unlock_and_free(group);
+ iommu_group_put(iommu_group);
}
static void vfio_group_put(struct vfio_group *group)
iommu_group_put(iommu_group);
return PTR_ERR(group);
}
+ } else {
+ /*
+ * A found vfio_group already holds a reference to the
+ * iommu_group. A created vfio_group keeps the reference.
+ */
+ iommu_group_put(iommu_group);
}
device = vfio_group_get_device(group, dev);
dev_name(dev), iommu_group_id(iommu_group));
vfio_device_put(device);
vfio_group_put(group);
- iommu_group_put(iommu_group);
return -EBUSY;
}
device = vfio_group_create_device(group, dev, ops, device_data);
if (IS_ERR(device)) {
vfio_group_put(group);
- iommu_group_put(iommu_group);
return PTR_ERR(device);
}
/*
- * Added device holds reference to iommu_group and vfio_device
- * (which in turn holds reference to vfio_group). Drop extra
- * group reference used while acquiring device.
+ * Drop all but the vfio_device reference. The vfio_device holds
+ * a reference to the vfio_group, which holds a reference to the
+ * iommu_group.
*/
vfio_group_put(group);
{
struct vfio_device *device = dev_get_drvdata(dev);
struct vfio_group *group = device->group;
- struct iommu_group *iommu_group = group->iommu_group;
void *device_data = device->device_data;
struct vfio_unbound_dev *unbound;
+ unsigned int i = 0;
/*
* The group exists so long as we have a device reference. Get
vfio_device_put(device);
- /* TODO send a signal to encourage this to be released */
- wait_event(vfio.release_q, !vfio_dev_present(group, dev));
+ /*
+ * If the device is still present in the group after the above
+ * 'put', then it is in use and we need to request it from the
+ * bus driver. The driver may in turn need to request the
+ * device from the user. We send the request on an arbitrary
+ * interval with counter to allow the driver to take escalating
+ * measures to release the device if it has the ability to do so.
+ */
+ do {
+ device = vfio_group_get_device(group, dev);
+ if (!device)
+ break;
- vfio_group_put(group);
+ if (device->ops->request)
+ device->ops->request(device_data, i++);
- iommu_group_put(iommu_group);
+ vfio_device_put(device);
+
+ } while (wait_event_interruptible_timeout(vfio.release_q,
+ !vfio_dev_present(group, dev),
+ HZ * 10) <= 0);
+
+ vfio_group_put(group);
return device_data;
}