Merge v5.6-rc5 into drm-next
[linux-block.git] / drivers / gpu / drm / virtio / virtgpu_object.c
index 017a9e0fc3bb80b5aa7ee92cd5c2a5946972607e..d2c529a319397acc1231aba2273e767097dced2d 100644 (file)
@@ -23,6 +23,7 @@
  * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
  */
 
+#include <linux/dma-mapping.h>
 #include <linux/moduleparam.h>
 
 #include "virtgpu_drv.h"
@@ -42,8 +43,8 @@ static int virtio_gpu_resource_id_get(struct virtio_gpu_device *vgdev,
                 * "f91a9dd35715 Fix unlinking resources from hash
                 * table." (Feb 2019) fixes the bug.
                 */
-               static int handle;
-               handle++;
+               static atomic_t seqno = ATOMIC_INIT(0);
+               int handle = atomic_inc_return(&seqno);
                *resid = handle + 1;
        } else {
                int handle = ida_alloc(&vgdev->resource_ida, GFP_KERNEL);
@@ -61,21 +62,40 @@ static void virtio_gpu_resource_id_put(struct virtio_gpu_device *vgdev, uint32_t
        }
 }
 
-static void virtio_gpu_free_object(struct drm_gem_object *obj)
+void virtio_gpu_cleanup_object(struct virtio_gpu_object *bo)
 {
-       struct virtio_gpu_object *bo = gem_to_virtio_gpu_obj(obj);
        struct virtio_gpu_device *vgdev = bo->base.base.dev->dev_private;
 
-       if (bo->pages)
-               virtio_gpu_object_detach(vgdev, bo);
-       if (bo->created)
-               virtio_gpu_cmd_unref_resource(vgdev, bo->hw_res_handle);
+       if (bo->pages) {
+               if (bo->mapped) {
+                       dma_unmap_sg(vgdev->vdev->dev.parent,
+                                    bo->pages->sgl, bo->mapped,
+                                    DMA_TO_DEVICE);
+                       bo->mapped = 0;
+               }
+               sg_free_table(bo->pages);
+               bo->pages = NULL;
+               drm_gem_shmem_unpin(&bo->base.base);
+       }
        virtio_gpu_resource_id_put(vgdev, bo->hw_res_handle);
+       drm_gem_shmem_free_object(&bo->base.base);
+}
+
+static void virtio_gpu_free_object(struct drm_gem_object *obj)
+{
+       struct virtio_gpu_object *bo = gem_to_virtio_gpu_obj(obj);
+       struct virtio_gpu_device *vgdev = bo->base.base.dev->dev_private;
 
-       drm_gem_shmem_free_object(obj);
+       if (bo->created) {
+               virtio_gpu_cmd_unref_resource(vgdev, bo);
+               virtio_gpu_notify(vgdev);
+               /* completion handler calls virtio_gpu_cleanup_object() */
+               return;
+       }
+       virtio_gpu_cleanup_object(bo);
 }
 
-static const struct drm_gem_object_funcs virtio_gpu_gem_funcs = {
+static const struct drm_gem_object_funcs virtio_gpu_shmem_funcs = {
        .free = virtio_gpu_free_object,
        .open = virtio_gpu_gem_object_open,
        .close = virtio_gpu_gem_object_close,
@@ -86,9 +106,14 @@ static const struct drm_gem_object_funcs virtio_gpu_gem_funcs = {
        .get_sg_table = drm_gem_shmem_get_sg_table,
        .vmap = drm_gem_shmem_vmap,
        .vunmap = drm_gem_shmem_vunmap,
-       .mmap = &drm_gem_shmem_mmap,
+       .mmap = drm_gem_shmem_mmap,
 };
 
+bool virtio_gpu_is_shmem(struct drm_gem_object *obj)
+{
+       return obj->funcs == &virtio_gpu_shmem_funcs;
+}
+
 struct drm_gem_object *virtio_gpu_create_object(struct drm_device *dev,
                                                size_t size)
 {
@@ -98,10 +123,56 @@ struct drm_gem_object *virtio_gpu_create_object(struct drm_device *dev,
        if (!bo)
                return NULL;
 
-       bo->base.base.funcs = &virtio_gpu_gem_funcs;
+       bo->base.base.funcs = &virtio_gpu_shmem_funcs;
+       bo->base.map_cached = true;
        return &bo->base.base;
 }
 
+static int virtio_gpu_object_shmem_init(struct virtio_gpu_device *vgdev,
+                                       struct virtio_gpu_object *bo,
+                                       struct virtio_gpu_mem_entry **ents,
+                                       unsigned int *nents)
+{
+       bool use_dma_api = !virtio_has_iommu_quirk(vgdev->vdev);
+       struct scatterlist *sg;
+       int si, ret;
+
+       ret = drm_gem_shmem_pin(&bo->base.base);
+       if (ret < 0)
+               return -EINVAL;
+
+       bo->pages = drm_gem_shmem_get_sg_table(&bo->base.base);
+       if (!bo->pages) {
+               drm_gem_shmem_unpin(&bo->base.base);
+               return -EINVAL;
+       }
+
+       if (use_dma_api) {
+               bo->mapped = dma_map_sg(vgdev->vdev->dev.parent,
+                                       bo->pages->sgl, bo->pages->nents,
+                                       DMA_TO_DEVICE);
+               *nents = bo->mapped;
+       } else {
+               *nents = bo->pages->nents;
+       }
+
+       *ents = kmalloc_array(*nents, sizeof(struct virtio_gpu_mem_entry),
+                             GFP_KERNEL);
+       if (!(*ents)) {
+               DRM_ERROR("failed to allocate ent list\n");
+               return -ENOMEM;
+       }
+
+       for_each_sg(bo->pages->sgl, sg, *nents, si) {
+               (*ents)[si].addr = cpu_to_le64(use_dma_api
+                                              ? sg_dma_address(sg)
+                                              : sg_phys(sg));
+               (*ents)[si].length = cpu_to_le32(sg->length);
+               (*ents)[si].padding = 0;
+       }
+       return 0;
+}
+
 int virtio_gpu_object_create(struct virtio_gpu_device *vgdev,
                             struct virtio_gpu_object_params *params,
                             struct virtio_gpu_object **bo_ptr,
@@ -110,6 +181,8 @@ int virtio_gpu_object_create(struct virtio_gpu_device *vgdev,
        struct virtio_gpu_object_array *objs = NULL;
        struct drm_gem_shmem_object *shmem_obj;
        struct virtio_gpu_object *bo;
+       struct virtio_gpu_mem_entry *ents;
+       unsigned int nents;
        int ret;
 
        *bo_ptr = NULL;
@@ -146,12 +219,19 @@ int virtio_gpu_object_create(struct virtio_gpu_device *vgdev,
                                               objs, fence);
        }
 
-       ret = virtio_gpu_object_attach(vgdev, bo, NULL);
+       ret = virtio_gpu_object_shmem_init(vgdev, bo, &ents, &nents);
+       if (ret != 0) {
+               virtio_gpu_free_object(&shmem_obj->base);
+               return ret;
+       }
+
+       ret = virtio_gpu_object_attach(vgdev, bo, ents, nents);
        if (ret != 0) {
                virtio_gpu_free_object(&shmem_obj->base);
                return ret;
        }
 
+       virtio_gpu_notify(vgdev);
        *bo_ptr = bo;
        return 0;