VIGS: Command list patching implemented
authorStanislav Vorobiov <s.vorobiov@samsung.com>
Mon, 29 Apr 2013 14:02:11 +0000 (18:02 +0400)
committerSeokYeon Hwang <syeon.hwang@samsung.com>
Wed, 9 Apr 2014 05:42:20 +0000 (14:42 +0900)
It's necessary to patch commands such as update_vram and update_gpu
in kernel since only kernel knows about VRAM offset of a surface

Change-Id: Ia4a0f53e768d40d8323cebfe101b8e86c4c21261

drivers/gpu/drm/vigs/vigs_device.c
drivers/gpu/drm/vigs/vigs_device.h
drivers/gpu/drm/vigs/vigs_execbuffer.c
drivers/gpu/drm/vigs/vigs_framebuffer.c
drivers/gpu/drm/vigs/vigs_gem.c
drivers/gpu/drm/vigs/vigs_gem.h
drivers/gpu/drm/vigs/vigs_surface.c

index e71dc40..aabead4 100644 (file)
@@ -9,6 +9,125 @@
 #include "vigs_surface.h"
 #include <drm/vigs_drm.h>
 
+/*
+ * Must be called with drm_device::struct_mutex held.
+ */
+static struct vigs_surface
+    *vigs_device_reference_surface(struct vigs_device *vigs_dev,
+                                   vigsp_surface_id sfc_id)
+{
+    struct vigs_surface *sfc;
+
+    sfc = idr_find(&vigs_dev->surface_idr, sfc_id);
+
+    if (sfc) {
+        drm_gem_object_reference(&sfc->gem.base);
+    }
+
+    return sfc;
+}
+
+/*
+ * 'gem_list' will hold a list of GEMs that should be
+ * unreserved and unreferenced after execution.
+ */
+static int vigs_device_patch_commands(struct vigs_device *vigs_dev,
+                                      void *data,
+                                      u32 data_size,
+                                      struct list_head* gem_list)
+{
+    struct vigsp_cmd_batch_header *batch_header = data;
+    struct vigsp_cmd_request_header *request_header =
+        (struct vigsp_cmd_request_header*)(batch_header + 1);
+    struct vigsp_cmd_update_vram_request *update_vram_request;
+    struct vigsp_cmd_update_gpu_request *update_gpu_request;
+    vigsp_u32 i;
+    struct vigs_surface *sfc;
+    int ret = 0;
+
+    mutex_lock(&vigs_dev->drm_dev->struct_mutex);
+
+    /*
+     * GEM is always at least PAGE_SIZE long, so don't check
+     * if batch header is out of bounds.
+     */
+
+    for (i = 0; i < batch_header->num_requests; ++i) {
+        if (((void*)(request_header) + sizeof(*request_header)) >
+            (data + data_size)) {
+            DRM_ERROR("request header outside of GEM\n");
+            ret = -EINVAL;
+            break;
+        }
+
+        if (((void*)(request_header + 1) + request_header->size) >
+            (data + data_size)) {
+            DRM_ERROR("request data outside of GEM\n");
+            ret = -EINVAL;
+            break;
+        }
+
+        switch (request_header->cmd) {
+        case vigsp_cmd_update_vram:
+            update_vram_request =
+                (struct vigsp_cmd_update_vram_request*)(request_header + 1);
+            sfc = vigs_device_reference_surface(vigs_dev, update_vram_request->sfc_id);
+            if (!sfc) {
+                DRM_ERROR("Surface %u not found\n", update_vram_request->sfc_id);
+                ret = -EINVAL;
+                break;
+            }
+            vigs_gem_reserve(&sfc->gem);
+            if (vigs_gem_in_vram(&sfc->gem)) {
+                update_vram_request->offset = vigs_gem_offset(&sfc->gem);
+            } else {
+                update_vram_request->sfc_id = 0;
+            }
+            list_add_tail(&sfc->gem.list, gem_list);
+            break;
+        case vigsp_cmd_update_gpu:
+            update_gpu_request =
+                (struct vigsp_cmd_update_gpu_request*)(request_header + 1);
+            sfc = vigs_device_reference_surface(vigs_dev, update_gpu_request->sfc_id);
+            if (!sfc) {
+                DRM_ERROR("Surface %u not found\n", update_gpu_request->sfc_id);
+                ret = -EINVAL;
+                break;
+            }
+            vigs_gem_reserve(&sfc->gem);
+            if (vigs_gem_in_vram(&sfc->gem)) {
+                update_gpu_request->offset = vigs_gem_offset(&sfc->gem);
+            } else {
+                update_gpu_request->sfc_id = 0;
+            }
+            list_add_tail(&sfc->gem.list, gem_list);
+            break;
+        default:
+            break;
+        }
+
+        request_header =
+            (struct vigsp_cmd_request_header*)((u8*)(request_header + 1) +
+            request_header->size);
+    }
+
+    mutex_unlock(&vigs_dev->drm_dev->struct_mutex);
+
+    return 0;
+}
+
+static void vigs_device_finish_patch_commands(struct list_head* gem_list)
+{
+    struct vigs_gem_object *gem, *gem_tmp;
+
+    list_for_each_entry_safe(gem, gem_tmp, gem_list, list)
+    {
+        list_del(&gem->list);
+        vigs_gem_unreserve(gem);
+        drm_gem_object_unreference_unlocked(&gem->base);
+    }
+}
+
 int vigs_device_init(struct vigs_device *vigs_dev,
                      struct drm_device *drm_dev,
                      struct pci_dev *pci_dev,
@@ -170,25 +289,6 @@ int vigs_device_add_surface_unlocked(struct vigs_device *vigs_dev,
     return ret;
 }
 
-struct vigs_surface
-    *vigs_device_reference_surface_unlocked(struct vigs_device *vigs_dev,
-                                            vigsp_surface_id sfc_id)
-{
-    struct vigs_surface *sfc;
-
-    mutex_lock(&vigs_dev->drm_dev->struct_mutex);
-
-    sfc = idr_find(&vigs_dev->surface_idr, sfc_id);
-
-    if (sfc) {
-        drm_gem_object_reference(&sfc->gem.base);
-    }
-
-    mutex_unlock(&vigs_dev->drm_dev->struct_mutex);
-
-    return sfc;
-}
-
 void vigs_device_remove_surface_unlocked(struct vigs_device *vigs_dev,
                                          vigsp_surface_id sfc_id)
 {
@@ -206,6 +306,10 @@ int vigs_device_exec_ioctl(struct drm_device *drm_dev,
     struct drm_gem_object *gem;
     struct vigs_gem_object *vigs_gem;
     struct vigs_execbuffer *execbuffer;
+    struct list_head gem_list;
+    int ret;
+
+    INIT_LIST_HEAD(&gem_list);
 
     gem = drm_gem_object_lookup(drm_dev, file_priv, args->handle);
 
@@ -216,13 +320,42 @@ int vigs_device_exec_ioctl(struct drm_device *drm_dev,
     vigs_gem = gem_to_vigs_gem(gem);
 
     if (vigs_gem->type != VIGS_GEM_TYPE_EXECBUFFER) {
+        drm_gem_object_unreference_unlocked(gem);
         return -ENOENT;
     }
 
     execbuffer = vigs_gem_to_vigs_execbuffer(vigs_gem);
 
+    vigs_gem_reserve(vigs_gem);
+
+    /*
+     * Never unmap for optimization, but we got to be careful,
+     * worst case scenario is when whole RAM BAR is mapped into kernel.
+     */
+    ret = vigs_gem_kmap(vigs_gem);
+
+    if (ret != 0) {
+        vigs_gem_unreserve(vigs_gem);
+        drm_gem_object_unreference_unlocked(gem);
+        return ret;
+    }
+
+    vigs_gem_unreserve(vigs_gem);
+
+    ret = vigs_device_patch_commands(vigs_dev,
+                                     execbuffer->gem.kptr,
+                                     vigs_gem_size(&execbuffer->gem),
+                                     &gem_list);
+
+    if (ret != 0) {
+        vigs_device_finish_patch_commands(&gem_list);
+        drm_gem_object_unreference_unlocked(gem);
+        return ret;
+    }
+
     vigs_comm_exec(vigs_dev->comm, execbuffer);
 
+    vigs_device_finish_patch_commands(&gem_list);
     drm_gem_object_unreference_unlocked(gem);
 
     return 0;
index 63d0684..a3ea2fb 100644 (file)
@@ -63,10 +63,6 @@ int vigs_device_add_surface_unlocked(struct vigs_device *vigs_dev,
                                      struct vigs_surface *sfc,
                                      vigsp_surface_id* id);
 
-struct vigs_surface
-    *vigs_device_reference_surface_unlocked(struct vigs_device *vigs_dev,
-                                            vigsp_surface_id sfc_id);
-
 void vigs_device_remove_surface_unlocked(struct vigs_device *vigs_dev,
                                          vigsp_surface_id sfc_id);
 
index 6487a91..580a57c 100644 (file)
@@ -67,6 +67,7 @@ int vigs_execbuffer_create_ioctl(struct drm_device *drm_dev,
     drm_gem_object_unreference_unlocked(&execbuffer->gem.base);
 
     if (ret == 0) {
+        args->size = vigs_gem_size(&execbuffer->gem);
         args->handle = handle;
         args->mmap_offset = vigs_gem_mmap_offset(&execbuffer->gem);
     }
index a0121bb..5d6a10f 100644 (file)
@@ -30,6 +30,7 @@ static struct drm_framebuffer *vigs_fb_create(struct drm_device *drm_dev,
 
     if (vigs_gem->type != VIGS_GEM_TYPE_SURFACE) {
         DRM_ERROR("GEM is not a surface, handle = %u\n", mode_cmd->handles[0]);
+        drm_gem_object_unreference_unlocked(gem);
         return NULL;
     }
 
index aa2e44e..6b7b143 100644 (file)
@@ -42,6 +42,8 @@ int vigs_gem_init(struct vigs_gem_object *vigs_gem,
         return -EINVAL;
     }
 
+    INIT_LIST_HEAD(&vigs_gem->list);
+
     memset(&placement, 0, sizeof(placement));
 
     placement.placement = placements;
@@ -119,6 +121,8 @@ int vigs_gem_pin(struct vigs_gem_object *vigs_gem)
     }
 
     if (vigs_gem->type == VIGS_GEM_TYPE_EXECBUFFER) {
+        vigs_gem->pin_count = 1;
+
         return 0;
     }
 
@@ -245,6 +249,11 @@ void vigs_gem_kunmap(struct vigs_gem_object *vigs_gem)
                      vigs_gem_size(vigs_gem));
 }
 
+int vigs_gem_in_vram(struct vigs_gem_object *vigs_gem)
+{
+    return vigs_gem->bo.mem.mem_type == TTM_PL_VRAM;
+}
+
 void vigs_gem_free_object(struct drm_gem_object *gem)
 {
     struct vigs_gem_object *vigs_gem = gem_to_vigs_gem(gem);
index cf1f560..c3d4c71 100644 (file)
@@ -19,6 +19,12 @@ struct vigs_gem_object
 
     struct ttm_buffer_object bo;
 
+    /*
+     * Use it only when this GEM is reserved. This makes it easier
+     * to reserve a set of GEMs and then unreserve them later.
+     */
+    struct list_head list;
+
     enum ttm_object_type type;
 
     /*
@@ -130,6 +136,12 @@ void vigs_gem_kunmap(struct vigs_gem_object *vigs_gem);
  */
 
 /*
+ * true if GEM is currently in VRAM. Note that this doesn't
+ * necessarily mean that it's pinned.
+ */
+int vigs_gem_in_vram(struct vigs_gem_object *vigs_gem);
+
+/*
  * @}
  */
 
index c9ab783..99c2367 100644 (file)
@@ -131,6 +131,7 @@ int vigs_surface_info_ioctl(struct drm_device *drm_dev,
     vigs_gem = gem_to_vigs_gem(gem);
 
     if (vigs_gem->type != VIGS_GEM_TYPE_SURFACE) {
+        drm_gem_object_unreference_unlocked(gem);
         return -ENOENT;
     }