vfio: Replace the DMA unmapping notifier with a callback
Instead of having drivers register the notifier with explicit code just have them provide a dma_unmap callback op in their driver ops and rely on the core code to wire it up. Suggested-by: Christoph Hellwig <hch@lst.de> Reviewed-by: Christoph Hellwig <hch@lst.de> Reviewed-by: Kevin Tian <kevin.tian@intel.com> Reviewed-by: Tony Krowiak <akrowiak@linux.ibm.com> Reviewed-by: Eric Farman <farman@linux.ibm.com> Reviewed-by: Zhenyu Wang <zhenyuw@linux.intel.com> Signed-off-by: Jason Gunthorpe <jgg@nvidia.com> Link: https://lore.kernel.org/r/1-v4-681e038e30fd+78-vfio_unmap_notif_jgg@nvidia.com Signed-off-by: Alex Williamson <alex.williamson@redhat.com>
This commit is contained in:
parent
2a8ed7ef00
commit
ce4b4657ff
9 changed files with 86 additions and 240 deletions
|
@ -226,7 +226,6 @@ struct intel_vgpu {
|
||||||
unsigned long nr_cache_entries;
|
unsigned long nr_cache_entries;
|
||||||
struct mutex cache_lock;
|
struct mutex cache_lock;
|
||||||
|
|
||||||
struct notifier_block iommu_notifier;
|
|
||||||
atomic_t released;
|
atomic_t released;
|
||||||
|
|
||||||
struct kvm_page_track_notifier_node track_node;
|
struct kvm_page_track_notifier_node track_node;
|
||||||
|
|
|
@ -729,34 +729,25 @@ int intel_gvt_set_edid(struct intel_vgpu *vgpu, int port_num)
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int intel_vgpu_iommu_notifier(struct notifier_block *nb,
|
static void intel_vgpu_dma_unmap(struct vfio_device *vfio_dev, u64 iova,
|
||||||
unsigned long action, void *data)
|
u64 length)
|
||||||
{
|
{
|
||||||
struct intel_vgpu *vgpu =
|
struct intel_vgpu *vgpu = vfio_dev_to_vgpu(vfio_dev);
|
||||||
container_of(nb, struct intel_vgpu, iommu_notifier);
|
struct gvt_dma *entry;
|
||||||
|
u64 iov_pfn = iova >> PAGE_SHIFT;
|
||||||
|
u64 end_iov_pfn = iov_pfn + length / PAGE_SIZE;
|
||||||
|
|
||||||
if (action == VFIO_IOMMU_NOTIFY_DMA_UNMAP) {
|
mutex_lock(&vgpu->cache_lock);
|
||||||
struct vfio_iommu_type1_dma_unmap *unmap = data;
|
for (; iov_pfn < end_iov_pfn; iov_pfn++) {
|
||||||
struct gvt_dma *entry;
|
entry = __gvt_cache_find_gfn(vgpu, iov_pfn);
|
||||||
unsigned long iov_pfn, end_iov_pfn;
|
if (!entry)
|
||||||
|
continue;
|
||||||
|
|
||||||
iov_pfn = unmap->iova >> PAGE_SHIFT;
|
gvt_dma_unmap_page(vgpu, entry->gfn, entry->dma_addr,
|
||||||
end_iov_pfn = iov_pfn + unmap->size / PAGE_SIZE;
|
entry->size);
|
||||||
|
__gvt_cache_remove_entry(vgpu, entry);
|
||||||
mutex_lock(&vgpu->cache_lock);
|
|
||||||
for (; iov_pfn < end_iov_pfn; iov_pfn++) {
|
|
||||||
entry = __gvt_cache_find_gfn(vgpu, iov_pfn);
|
|
||||||
if (!entry)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
gvt_dma_unmap_page(vgpu, entry->gfn, entry->dma_addr,
|
|
||||||
entry->size);
|
|
||||||
__gvt_cache_remove_entry(vgpu, entry);
|
|
||||||
}
|
|
||||||
mutex_unlock(&vgpu->cache_lock);
|
|
||||||
}
|
}
|
||||||
|
mutex_unlock(&vgpu->cache_lock);
|
||||||
return NOTIFY_OK;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool __kvmgt_vgpu_exist(struct intel_vgpu *vgpu)
|
static bool __kvmgt_vgpu_exist(struct intel_vgpu *vgpu)
|
||||||
|
@ -783,36 +774,20 @@ out:
|
||||||
static int intel_vgpu_open_device(struct vfio_device *vfio_dev)
|
static int intel_vgpu_open_device(struct vfio_device *vfio_dev)
|
||||||
{
|
{
|
||||||
struct intel_vgpu *vgpu = vfio_dev_to_vgpu(vfio_dev);
|
struct intel_vgpu *vgpu = vfio_dev_to_vgpu(vfio_dev);
|
||||||
unsigned long events;
|
|
||||||
int ret;
|
|
||||||
|
|
||||||
vgpu->iommu_notifier.notifier_call = intel_vgpu_iommu_notifier;
|
|
||||||
|
|
||||||
events = VFIO_IOMMU_NOTIFY_DMA_UNMAP;
|
|
||||||
ret = vfio_register_notifier(vfio_dev, VFIO_IOMMU_NOTIFY, &events,
|
|
||||||
&vgpu->iommu_notifier);
|
|
||||||
if (ret != 0) {
|
|
||||||
gvt_vgpu_err("vfio_register_notifier for iommu failed: %d\n",
|
|
||||||
ret);
|
|
||||||
goto out;
|
|
||||||
}
|
|
||||||
|
|
||||||
ret = -EEXIST;
|
|
||||||
if (vgpu->attached)
|
if (vgpu->attached)
|
||||||
goto undo_iommu;
|
return -EEXIST;
|
||||||
|
|
||||||
ret = -ESRCH;
|
|
||||||
if (!vgpu->vfio_device.kvm ||
|
if (!vgpu->vfio_device.kvm ||
|
||||||
vgpu->vfio_device.kvm->mm != current->mm) {
|
vgpu->vfio_device.kvm->mm != current->mm) {
|
||||||
gvt_vgpu_err("KVM is required to use Intel vGPU\n");
|
gvt_vgpu_err("KVM is required to use Intel vGPU\n");
|
||||||
goto undo_iommu;
|
return -ESRCH;
|
||||||
}
|
}
|
||||||
|
|
||||||
kvm_get_kvm(vgpu->vfio_device.kvm);
|
kvm_get_kvm(vgpu->vfio_device.kvm);
|
||||||
|
|
||||||
ret = -EEXIST;
|
|
||||||
if (__kvmgt_vgpu_exist(vgpu))
|
if (__kvmgt_vgpu_exist(vgpu))
|
||||||
goto undo_iommu;
|
return -EEXIST;
|
||||||
|
|
||||||
vgpu->attached = true;
|
vgpu->attached = true;
|
||||||
|
|
||||||
|
@ -831,12 +806,6 @@ static int intel_vgpu_open_device(struct vfio_device *vfio_dev)
|
||||||
|
|
||||||
atomic_set(&vgpu->released, 0);
|
atomic_set(&vgpu->released, 0);
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
undo_iommu:
|
|
||||||
vfio_unregister_notifier(vfio_dev, VFIO_IOMMU_NOTIFY,
|
|
||||||
&vgpu->iommu_notifier);
|
|
||||||
out:
|
|
||||||
return ret;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void intel_vgpu_release_msi_eventfd_ctx(struct intel_vgpu *vgpu)
|
static void intel_vgpu_release_msi_eventfd_ctx(struct intel_vgpu *vgpu)
|
||||||
|
@ -853,8 +822,6 @@ static void intel_vgpu_release_msi_eventfd_ctx(struct intel_vgpu *vgpu)
|
||||||
static void intel_vgpu_close_device(struct vfio_device *vfio_dev)
|
static void intel_vgpu_close_device(struct vfio_device *vfio_dev)
|
||||||
{
|
{
|
||||||
struct intel_vgpu *vgpu = vfio_dev_to_vgpu(vfio_dev);
|
struct intel_vgpu *vgpu = vfio_dev_to_vgpu(vfio_dev);
|
||||||
struct drm_i915_private *i915 = vgpu->gvt->gt->i915;
|
|
||||||
int ret;
|
|
||||||
|
|
||||||
if (!vgpu->attached)
|
if (!vgpu->attached)
|
||||||
return;
|
return;
|
||||||
|
@ -864,11 +831,6 @@ static void intel_vgpu_close_device(struct vfio_device *vfio_dev)
|
||||||
|
|
||||||
intel_gvt_release_vgpu(vgpu);
|
intel_gvt_release_vgpu(vgpu);
|
||||||
|
|
||||||
ret = vfio_unregister_notifier(&vgpu->vfio_device, VFIO_IOMMU_NOTIFY,
|
|
||||||
&vgpu->iommu_notifier);
|
|
||||||
drm_WARN(&i915->drm, ret,
|
|
||||||
"vfio_unregister_notifier for iommu failed: %d\n", ret);
|
|
||||||
|
|
||||||
debugfs_remove(debugfs_lookup(KVMGT_DEBUGFS_FILENAME, vgpu->debugfs));
|
debugfs_remove(debugfs_lookup(KVMGT_DEBUGFS_FILENAME, vgpu->debugfs));
|
||||||
|
|
||||||
kvm_page_track_unregister_notifier(vgpu->vfio_device.kvm,
|
kvm_page_track_unregister_notifier(vgpu->vfio_device.kvm,
|
||||||
|
@ -1610,6 +1572,7 @@ static const struct vfio_device_ops intel_vgpu_dev_ops = {
|
||||||
.write = intel_vgpu_write,
|
.write = intel_vgpu_write,
|
||||||
.mmap = intel_vgpu_mmap,
|
.mmap = intel_vgpu_mmap,
|
||||||
.ioctl = intel_vgpu_ioctl,
|
.ioctl = intel_vgpu_ioctl,
|
||||||
|
.dma_unmap = intel_vgpu_dma_unmap,
|
||||||
};
|
};
|
||||||
|
|
||||||
static int intel_vgpu_probe(struct mdev_device *mdev)
|
static int intel_vgpu_probe(struct mdev_device *mdev)
|
||||||
|
|
|
@ -33,30 +33,16 @@ static int vfio_ccw_mdev_reset(struct vfio_ccw_private *private)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int vfio_ccw_mdev_notifier(struct notifier_block *nb,
|
static void vfio_ccw_dma_unmap(struct vfio_device *vdev, u64 iova, u64 length)
|
||||||
unsigned long action,
|
|
||||||
void *data)
|
|
||||||
{
|
{
|
||||||
struct vfio_ccw_private *private =
|
struct vfio_ccw_private *private =
|
||||||
container_of(nb, struct vfio_ccw_private, nb);
|
container_of(vdev, struct vfio_ccw_private, vdev);
|
||||||
|
|
||||||
/*
|
/* Drivers MUST unpin pages in response to an invalidation. */
|
||||||
* Vendor drivers MUST unpin pages in response to an
|
if (!cp_iova_pinned(&private->cp, iova))
|
||||||
* invalidation.
|
return;
|
||||||
*/
|
|
||||||
if (action == VFIO_IOMMU_NOTIFY_DMA_UNMAP) {
|
|
||||||
struct vfio_iommu_type1_dma_unmap *unmap = data;
|
|
||||||
|
|
||||||
if (!cp_iova_pinned(&private->cp, unmap->iova))
|
vfio_ccw_mdev_reset(private);
|
||||||
return NOTIFY_OK;
|
|
||||||
|
|
||||||
if (vfio_ccw_mdev_reset(private))
|
|
||||||
return NOTIFY_BAD;
|
|
||||||
|
|
||||||
return NOTIFY_OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
return NOTIFY_DONE;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static ssize_t name_show(struct mdev_type *mtype,
|
static ssize_t name_show(struct mdev_type *mtype,
|
||||||
|
@ -154,23 +140,15 @@ static int vfio_ccw_mdev_open_device(struct vfio_device *vdev)
|
||||||
{
|
{
|
||||||
struct vfio_ccw_private *private =
|
struct vfio_ccw_private *private =
|
||||||
container_of(vdev, struct vfio_ccw_private, vdev);
|
container_of(vdev, struct vfio_ccw_private, vdev);
|
||||||
unsigned long events = VFIO_IOMMU_NOTIFY_DMA_UNMAP;
|
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
/* Device cannot simply be opened again from this state */
|
/* Device cannot simply be opened again from this state */
|
||||||
if (private->state == VFIO_CCW_STATE_NOT_OPER)
|
if (private->state == VFIO_CCW_STATE_NOT_OPER)
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
|
|
||||||
private->nb.notifier_call = vfio_ccw_mdev_notifier;
|
|
||||||
|
|
||||||
ret = vfio_register_notifier(vdev, VFIO_IOMMU_NOTIFY,
|
|
||||||
&events, &private->nb);
|
|
||||||
if (ret)
|
|
||||||
return ret;
|
|
||||||
|
|
||||||
ret = vfio_ccw_register_async_dev_regions(private);
|
ret = vfio_ccw_register_async_dev_regions(private);
|
||||||
if (ret)
|
if (ret)
|
||||||
goto out_unregister;
|
return ret;
|
||||||
|
|
||||||
ret = vfio_ccw_register_schib_dev_regions(private);
|
ret = vfio_ccw_register_schib_dev_regions(private);
|
||||||
if (ret)
|
if (ret)
|
||||||
|
@ -190,7 +168,6 @@ static int vfio_ccw_mdev_open_device(struct vfio_device *vdev)
|
||||||
|
|
||||||
out_unregister:
|
out_unregister:
|
||||||
vfio_ccw_unregister_dev_regions(private);
|
vfio_ccw_unregister_dev_regions(private);
|
||||||
vfio_unregister_notifier(vdev, VFIO_IOMMU_NOTIFY, &private->nb);
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -201,7 +178,6 @@ static void vfio_ccw_mdev_close_device(struct vfio_device *vdev)
|
||||||
|
|
||||||
vfio_ccw_fsm_event(private, VFIO_CCW_EVENT_CLOSE);
|
vfio_ccw_fsm_event(private, VFIO_CCW_EVENT_CLOSE);
|
||||||
vfio_ccw_unregister_dev_regions(private);
|
vfio_ccw_unregister_dev_regions(private);
|
||||||
vfio_unregister_notifier(vdev, VFIO_IOMMU_NOTIFY, &private->nb);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static ssize_t vfio_ccw_mdev_read_io_region(struct vfio_ccw_private *private,
|
static ssize_t vfio_ccw_mdev_read_io_region(struct vfio_ccw_private *private,
|
||||||
|
@ -624,6 +600,7 @@ static const struct vfio_device_ops vfio_ccw_dev_ops = {
|
||||||
.write = vfio_ccw_mdev_write,
|
.write = vfio_ccw_mdev_write,
|
||||||
.ioctl = vfio_ccw_mdev_ioctl,
|
.ioctl = vfio_ccw_mdev_ioctl,
|
||||||
.request = vfio_ccw_mdev_request,
|
.request = vfio_ccw_mdev_request,
|
||||||
|
.dma_unmap = vfio_ccw_dma_unmap,
|
||||||
};
|
};
|
||||||
|
|
||||||
struct mdev_driver vfio_ccw_mdev_driver = {
|
struct mdev_driver vfio_ccw_mdev_driver = {
|
||||||
|
|
|
@ -73,7 +73,6 @@ struct vfio_ccw_crw {
|
||||||
* @state: internal state of the device
|
* @state: internal state of the device
|
||||||
* @completion: synchronization helper of the I/O completion
|
* @completion: synchronization helper of the I/O completion
|
||||||
* @avail: available for creating a mediated device
|
* @avail: available for creating a mediated device
|
||||||
* @nb: notifier for vfio events
|
|
||||||
* @io_region: MMIO region to input/output I/O arguments/results
|
* @io_region: MMIO region to input/output I/O arguments/results
|
||||||
* @io_mutex: protect against concurrent update of I/O regions
|
* @io_mutex: protect against concurrent update of I/O regions
|
||||||
* @region: additional regions for other subchannel operations
|
* @region: additional regions for other subchannel operations
|
||||||
|
@ -96,7 +95,6 @@ struct vfio_ccw_private {
|
||||||
int state;
|
int state;
|
||||||
struct completion *completion;
|
struct completion *completion;
|
||||||
atomic_t avail;
|
atomic_t avail;
|
||||||
struct notifier_block nb;
|
|
||||||
struct ccw_io_region *io_region;
|
struct ccw_io_region *io_region;
|
||||||
struct mutex io_mutex;
|
struct mutex io_mutex;
|
||||||
struct vfio_ccw_region *region;
|
struct vfio_ccw_region *region;
|
||||||
|
|
|
@ -1226,34 +1226,14 @@ static int vfio_ap_mdev_set_kvm(struct ap_matrix_mdev *matrix_mdev,
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
static void vfio_ap_mdev_dma_unmap(struct vfio_device *vdev, u64 iova,
|
||||||
* vfio_ap_mdev_iommu_notifier - IOMMU notifier callback
|
u64 length)
|
||||||
*
|
|
||||||
* @nb: The notifier block
|
|
||||||
* @action: Action to be taken
|
|
||||||
* @data: data associated with the request
|
|
||||||
*
|
|
||||||
* For an UNMAP request, unpin the guest IOVA (the NIB guest address we
|
|
||||||
* pinned before). Other requests are ignored.
|
|
||||||
*
|
|
||||||
* Return: for an UNMAP request, NOFITY_OK; otherwise NOTIFY_DONE.
|
|
||||||
*/
|
|
||||||
static int vfio_ap_mdev_iommu_notifier(struct notifier_block *nb,
|
|
||||||
unsigned long action, void *data)
|
|
||||||
{
|
{
|
||||||
struct ap_matrix_mdev *matrix_mdev;
|
struct ap_matrix_mdev *matrix_mdev =
|
||||||
|
container_of(vdev, struct ap_matrix_mdev, vdev);
|
||||||
|
unsigned long g_pfn = iova >> PAGE_SHIFT;
|
||||||
|
|
||||||
matrix_mdev = container_of(nb, struct ap_matrix_mdev, iommu_notifier);
|
vfio_unpin_pages(&matrix_mdev->vdev, &g_pfn, 1);
|
||||||
|
|
||||||
if (action == VFIO_IOMMU_NOTIFY_DMA_UNMAP) {
|
|
||||||
struct vfio_iommu_type1_dma_unmap *unmap = data;
|
|
||||||
unsigned long g_pfn = unmap->iova >> PAGE_SHIFT;
|
|
||||||
|
|
||||||
vfio_unpin_pages(&matrix_mdev->vdev, &g_pfn, 1);
|
|
||||||
return NOTIFY_OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
return NOTIFY_DONE;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -1380,27 +1360,11 @@ static int vfio_ap_mdev_open_device(struct vfio_device *vdev)
|
||||||
{
|
{
|
||||||
struct ap_matrix_mdev *matrix_mdev =
|
struct ap_matrix_mdev *matrix_mdev =
|
||||||
container_of(vdev, struct ap_matrix_mdev, vdev);
|
container_of(vdev, struct ap_matrix_mdev, vdev);
|
||||||
unsigned long events;
|
|
||||||
int ret;
|
|
||||||
|
|
||||||
if (!vdev->kvm)
|
if (!vdev->kvm)
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
|
|
||||||
ret = vfio_ap_mdev_set_kvm(matrix_mdev, vdev->kvm);
|
return vfio_ap_mdev_set_kvm(matrix_mdev, vdev->kvm);
|
||||||
if (ret)
|
|
||||||
return ret;
|
|
||||||
|
|
||||||
matrix_mdev->iommu_notifier.notifier_call = vfio_ap_mdev_iommu_notifier;
|
|
||||||
events = VFIO_IOMMU_NOTIFY_DMA_UNMAP;
|
|
||||||
ret = vfio_register_notifier(vdev, VFIO_IOMMU_NOTIFY, &events,
|
|
||||||
&matrix_mdev->iommu_notifier);
|
|
||||||
if (ret)
|
|
||||||
goto err_kvm;
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
err_kvm:
|
|
||||||
vfio_ap_mdev_unset_kvm(matrix_mdev);
|
|
||||||
return ret;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void vfio_ap_mdev_close_device(struct vfio_device *vdev)
|
static void vfio_ap_mdev_close_device(struct vfio_device *vdev)
|
||||||
|
@ -1408,8 +1372,6 @@ static void vfio_ap_mdev_close_device(struct vfio_device *vdev)
|
||||||
struct ap_matrix_mdev *matrix_mdev =
|
struct ap_matrix_mdev *matrix_mdev =
|
||||||
container_of(vdev, struct ap_matrix_mdev, vdev);
|
container_of(vdev, struct ap_matrix_mdev, vdev);
|
||||||
|
|
||||||
vfio_unregister_notifier(vdev, VFIO_IOMMU_NOTIFY,
|
|
||||||
&matrix_mdev->iommu_notifier);
|
|
||||||
vfio_ap_mdev_unset_kvm(matrix_mdev);
|
vfio_ap_mdev_unset_kvm(matrix_mdev);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1461,6 +1423,7 @@ static const struct vfio_device_ops vfio_ap_matrix_dev_ops = {
|
||||||
.open_device = vfio_ap_mdev_open_device,
|
.open_device = vfio_ap_mdev_open_device,
|
||||||
.close_device = vfio_ap_mdev_close_device,
|
.close_device = vfio_ap_mdev_close_device,
|
||||||
.ioctl = vfio_ap_mdev_ioctl,
|
.ioctl = vfio_ap_mdev_ioctl,
|
||||||
|
.dma_unmap = vfio_ap_mdev_dma_unmap,
|
||||||
};
|
};
|
||||||
|
|
||||||
static struct mdev_driver vfio_ap_matrix_driver = {
|
static struct mdev_driver vfio_ap_matrix_driver = {
|
||||||
|
|
|
@ -81,8 +81,6 @@ struct ap_matrix {
|
||||||
* @node: allows the ap_matrix_mdev struct to be added to a list
|
* @node: allows the ap_matrix_mdev struct to be added to a list
|
||||||
* @matrix: the adapters, usage domains and control domains assigned to the
|
* @matrix: the adapters, usage domains and control domains assigned to the
|
||||||
* mediated matrix device.
|
* mediated matrix device.
|
||||||
* @iommu_notifier: notifier block used for specifying callback function for
|
|
||||||
* handling the VFIO_IOMMU_NOTIFY_DMA_UNMAP even
|
|
||||||
* @kvm: the struct holding guest's state
|
* @kvm: the struct holding guest's state
|
||||||
* @pqap_hook: the function pointer to the interception handler for the
|
* @pqap_hook: the function pointer to the interception handler for the
|
||||||
* PQAP(AQIC) instruction.
|
* PQAP(AQIC) instruction.
|
||||||
|
@ -92,7 +90,6 @@ struct ap_matrix_mdev {
|
||||||
struct vfio_device vdev;
|
struct vfio_device vdev;
|
||||||
struct list_head node;
|
struct list_head node;
|
||||||
struct ap_matrix matrix;
|
struct ap_matrix matrix;
|
||||||
struct notifier_block iommu_notifier;
|
|
||||||
struct kvm *kvm;
|
struct kvm *kvm;
|
||||||
crypto_hook pqap_hook;
|
crypto_hook pqap_hook;
|
||||||
struct mdev_device *mdev;
|
struct mdev_device *mdev;
|
||||||
|
|
|
@ -231,6 +231,9 @@ int vfio_register_iommu_driver(const struct vfio_iommu_driver_ops *ops)
|
||||||
{
|
{
|
||||||
struct vfio_iommu_driver *driver, *tmp;
|
struct vfio_iommu_driver *driver, *tmp;
|
||||||
|
|
||||||
|
if (WARN_ON(!ops->register_notifier != !ops->unregister_notifier))
|
||||||
|
return -EINVAL;
|
||||||
|
|
||||||
driver = kzalloc(sizeof(*driver), GFP_KERNEL);
|
driver = kzalloc(sizeof(*driver), GFP_KERNEL);
|
||||||
if (!driver)
|
if (!driver)
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
|
@ -1079,8 +1082,20 @@ static void vfio_device_unassign_container(struct vfio_device *device)
|
||||||
up_write(&device->group->group_rwsem);
|
up_write(&device->group->group_rwsem);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int vfio_iommu_notifier(struct notifier_block *nb, unsigned long action,
|
||||||
|
void *data)
|
||||||
|
{
|
||||||
|
struct vfio_device *vfio_device =
|
||||||
|
container_of(nb, struct vfio_device, iommu_nb);
|
||||||
|
struct vfio_iommu_type1_dma_unmap *unmap = data;
|
||||||
|
|
||||||
|
vfio_device->ops->dma_unmap(vfio_device, unmap->iova, unmap->size);
|
||||||
|
return NOTIFY_OK;
|
||||||
|
}
|
||||||
|
|
||||||
static struct file *vfio_device_open(struct vfio_device *device)
|
static struct file *vfio_device_open(struct vfio_device *device)
|
||||||
{
|
{
|
||||||
|
struct vfio_iommu_driver *iommu_driver;
|
||||||
struct file *filep;
|
struct file *filep;
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
|
@ -1111,6 +1126,18 @@ static struct file *vfio_device_open(struct vfio_device *device)
|
||||||
if (ret)
|
if (ret)
|
||||||
goto err_undo_count;
|
goto err_undo_count;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
iommu_driver = device->group->container->iommu_driver;
|
||||||
|
if (device->ops->dma_unmap && iommu_driver &&
|
||||||
|
iommu_driver->ops->register_notifier) {
|
||||||
|
unsigned long events = VFIO_IOMMU_NOTIFY_DMA_UNMAP;
|
||||||
|
|
||||||
|
device->iommu_nb.notifier_call = vfio_iommu_notifier;
|
||||||
|
iommu_driver->ops->register_notifier(
|
||||||
|
device->group->container->iommu_data, &events,
|
||||||
|
&device->iommu_nb);
|
||||||
|
}
|
||||||
|
|
||||||
up_read(&device->group->group_rwsem);
|
up_read(&device->group->group_rwsem);
|
||||||
}
|
}
|
||||||
mutex_unlock(&device->dev_set->lock);
|
mutex_unlock(&device->dev_set->lock);
|
||||||
|
@ -1145,8 +1172,16 @@ static struct file *vfio_device_open(struct vfio_device *device)
|
||||||
err_close_device:
|
err_close_device:
|
||||||
mutex_lock(&device->dev_set->lock);
|
mutex_lock(&device->dev_set->lock);
|
||||||
down_read(&device->group->group_rwsem);
|
down_read(&device->group->group_rwsem);
|
||||||
if (device->open_count == 1 && device->ops->close_device)
|
if (device->open_count == 1 && device->ops->close_device) {
|
||||||
device->ops->close_device(device);
|
device->ops->close_device(device);
|
||||||
|
|
||||||
|
iommu_driver = device->group->container->iommu_driver;
|
||||||
|
if (device->ops->dma_unmap && iommu_driver &&
|
||||||
|
iommu_driver->ops->unregister_notifier)
|
||||||
|
iommu_driver->ops->unregister_notifier(
|
||||||
|
device->group->container->iommu_data,
|
||||||
|
&device->iommu_nb);
|
||||||
|
}
|
||||||
err_undo_count:
|
err_undo_count:
|
||||||
up_read(&device->group->group_rwsem);
|
up_read(&device->group->group_rwsem);
|
||||||
device->open_count--;
|
device->open_count--;
|
||||||
|
@ -1341,12 +1376,20 @@ static const struct file_operations vfio_group_fops = {
|
||||||
static int vfio_device_fops_release(struct inode *inode, struct file *filep)
|
static int vfio_device_fops_release(struct inode *inode, struct file *filep)
|
||||||
{
|
{
|
||||||
struct vfio_device *device = filep->private_data;
|
struct vfio_device *device = filep->private_data;
|
||||||
|
struct vfio_iommu_driver *iommu_driver;
|
||||||
|
|
||||||
mutex_lock(&device->dev_set->lock);
|
mutex_lock(&device->dev_set->lock);
|
||||||
vfio_assert_device_open(device);
|
vfio_assert_device_open(device);
|
||||||
down_read(&device->group->group_rwsem);
|
down_read(&device->group->group_rwsem);
|
||||||
if (device->open_count == 1 && device->ops->close_device)
|
if (device->open_count == 1 && device->ops->close_device)
|
||||||
device->ops->close_device(device);
|
device->ops->close_device(device);
|
||||||
|
|
||||||
|
iommu_driver = device->group->container->iommu_driver;
|
||||||
|
if (device->ops->dma_unmap && iommu_driver &&
|
||||||
|
iommu_driver->ops->unregister_notifier)
|
||||||
|
iommu_driver->ops->unregister_notifier(
|
||||||
|
device->group->container->iommu_data,
|
||||||
|
&device->iommu_nb);
|
||||||
up_read(&device->group->group_rwsem);
|
up_read(&device->group->group_rwsem);
|
||||||
device->open_count--;
|
device->open_count--;
|
||||||
if (device->open_count == 0)
|
if (device->open_count == 0)
|
||||||
|
@ -2029,90 +2072,6 @@ int vfio_dma_rw(struct vfio_device *device, dma_addr_t user_iova, void *data,
|
||||||
}
|
}
|
||||||
EXPORT_SYMBOL(vfio_dma_rw);
|
EXPORT_SYMBOL(vfio_dma_rw);
|
||||||
|
|
||||||
static int vfio_register_iommu_notifier(struct vfio_group *group,
|
|
||||||
unsigned long *events,
|
|
||||||
struct notifier_block *nb)
|
|
||||||
{
|
|
||||||
struct vfio_container *container;
|
|
||||||
struct vfio_iommu_driver *driver;
|
|
||||||
int ret;
|
|
||||||
|
|
||||||
lockdep_assert_held_read(&group->group_rwsem);
|
|
||||||
|
|
||||||
container = group->container;
|
|
||||||
driver = container->iommu_driver;
|
|
||||||
if (likely(driver && driver->ops->register_notifier))
|
|
||||||
ret = driver->ops->register_notifier(container->iommu_data,
|
|
||||||
events, nb);
|
|
||||||
else
|
|
||||||
ret = -ENOTTY;
|
|
||||||
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int vfio_unregister_iommu_notifier(struct vfio_group *group,
|
|
||||||
struct notifier_block *nb)
|
|
||||||
{
|
|
||||||
struct vfio_container *container;
|
|
||||||
struct vfio_iommu_driver *driver;
|
|
||||||
int ret;
|
|
||||||
|
|
||||||
lockdep_assert_held_read(&group->group_rwsem);
|
|
||||||
|
|
||||||
container = group->container;
|
|
||||||
driver = container->iommu_driver;
|
|
||||||
if (likely(driver && driver->ops->unregister_notifier))
|
|
||||||
ret = driver->ops->unregister_notifier(container->iommu_data,
|
|
||||||
nb);
|
|
||||||
else
|
|
||||||
ret = -ENOTTY;
|
|
||||||
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
int vfio_register_notifier(struct vfio_device *device,
|
|
||||||
enum vfio_notify_type type, unsigned long *events,
|
|
||||||
struct notifier_block *nb)
|
|
||||||
{
|
|
||||||
struct vfio_group *group = device->group;
|
|
||||||
int ret;
|
|
||||||
|
|
||||||
if (!nb || !events || (*events == 0) ||
|
|
||||||
!vfio_assert_device_open(device))
|
|
||||||
return -EINVAL;
|
|
||||||
|
|
||||||
switch (type) {
|
|
||||||
case VFIO_IOMMU_NOTIFY:
|
|
||||||
ret = vfio_register_iommu_notifier(group, events, nb);
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
ret = -EINVAL;
|
|
||||||
}
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
EXPORT_SYMBOL(vfio_register_notifier);
|
|
||||||
|
|
||||||
int vfio_unregister_notifier(struct vfio_device *device,
|
|
||||||
enum vfio_notify_type type,
|
|
||||||
struct notifier_block *nb)
|
|
||||||
{
|
|
||||||
struct vfio_group *group = device->group;
|
|
||||||
int ret;
|
|
||||||
|
|
||||||
if (!nb || !vfio_assert_device_open(device))
|
|
||||||
return -EINVAL;
|
|
||||||
|
|
||||||
switch (type) {
|
|
||||||
case VFIO_IOMMU_NOTIFY:
|
|
||||||
ret = vfio_unregister_iommu_notifier(group, nb);
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
ret = -EINVAL;
|
|
||||||
}
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
EXPORT_SYMBOL(vfio_unregister_notifier);
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Module/class support
|
* Module/class support
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -33,6 +33,9 @@ enum vfio_iommu_notify_type {
|
||||||
VFIO_IOMMU_CONTAINER_CLOSE = 0,
|
VFIO_IOMMU_CONTAINER_CLOSE = 0,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/* events for register_notifier() */
|
||||||
|
#define VFIO_IOMMU_NOTIFY_DMA_UNMAP BIT(0)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* struct vfio_iommu_driver_ops - VFIO IOMMU driver callbacks
|
* struct vfio_iommu_driver_ops - VFIO IOMMU driver callbacks
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -49,6 +49,7 @@ struct vfio_device {
|
||||||
unsigned int open_count;
|
unsigned int open_count;
|
||||||
struct completion comp;
|
struct completion comp;
|
||||||
struct list_head group_next;
|
struct list_head group_next;
|
||||||
|
struct notifier_block iommu_nb;
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -65,6 +66,8 @@ struct vfio_device {
|
||||||
* @match: Optional device name match callback (return: 0 for no-match, >0 for
|
* @match: Optional device name match callback (return: 0 for no-match, >0 for
|
||||||
* match, -errno for abort (ex. match with insufficient or incorrect
|
* match, -errno for abort (ex. match with insufficient or incorrect
|
||||||
* additional args)
|
* additional args)
|
||||||
|
* @dma_unmap: Called when userspace unmaps IOVA from the container
|
||||||
|
* this device is attached to.
|
||||||
* @device_feature: Optional, fill in the VFIO_DEVICE_FEATURE ioctl
|
* @device_feature: Optional, fill in the VFIO_DEVICE_FEATURE ioctl
|
||||||
*/
|
*/
|
||||||
struct vfio_device_ops {
|
struct vfio_device_ops {
|
||||||
|
@ -80,6 +83,7 @@ struct vfio_device_ops {
|
||||||
int (*mmap)(struct vfio_device *vdev, struct vm_area_struct *vma);
|
int (*mmap)(struct vfio_device *vdev, struct vm_area_struct *vma);
|
||||||
void (*request)(struct vfio_device *vdev, unsigned int count);
|
void (*request)(struct vfio_device *vdev, unsigned int count);
|
||||||
int (*match)(struct vfio_device *vdev, char *buf);
|
int (*match)(struct vfio_device *vdev, char *buf);
|
||||||
|
void (*dma_unmap)(struct vfio_device *vdev, u64 iova, u64 length);
|
||||||
int (*device_feature)(struct vfio_device *device, u32 flags,
|
int (*device_feature)(struct vfio_device *device, u32 flags,
|
||||||
void __user *arg, size_t argsz);
|
void __user *arg, size_t argsz);
|
||||||
};
|
};
|
||||||
|
@ -164,23 +168,6 @@ int vfio_unpin_pages(struct vfio_device *device, unsigned long *user_pfn,
|
||||||
int vfio_dma_rw(struct vfio_device *device, dma_addr_t user_iova,
|
int vfio_dma_rw(struct vfio_device *device, dma_addr_t user_iova,
|
||||||
void *data, size_t len, bool write);
|
void *data, size_t len, bool write);
|
||||||
|
|
||||||
/* each type has independent events */
|
|
||||||
enum vfio_notify_type {
|
|
||||||
VFIO_IOMMU_NOTIFY = 0,
|
|
||||||
};
|
|
||||||
|
|
||||||
/* events for VFIO_IOMMU_NOTIFY */
|
|
||||||
#define VFIO_IOMMU_NOTIFY_DMA_UNMAP BIT(0)
|
|
||||||
|
|
||||||
int vfio_register_notifier(struct vfio_device *device,
|
|
||||||
enum vfio_notify_type type,
|
|
||||||
unsigned long *required_events,
|
|
||||||
struct notifier_block *nb);
|
|
||||||
int vfio_unregister_notifier(struct vfio_device *device,
|
|
||||||
enum vfio_notify_type type,
|
|
||||||
struct notifier_block *nb);
|
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Sub-module helpers
|
* Sub-module helpers
|
||||||
*/
|
*/
|
||||||
|
|
Loading…
Add table
Reference in a new issue