]> Gitweb @ Texas Instruments - Open Source Git Repositories - git.TI.com/gitweb - android-sdk/kernel-video.git/blobdiff - drivers/gpu/drm/omapdrm/omap_gem.c
drm/omap: fix leak & crash in omap_gem_op_sync()
[android-sdk/kernel-video.git] / drivers / gpu / drm / omapdrm / omap_gem.c
index bc9b0e13eee971347a34a66f994d83f4b670bf03..afb7f93a39adcecf1204c79da07982e685910de9 100644 (file)
@@ -117,6 +117,18 @@ struct omap_gem_object {
                uint32_t read_pending;
                uint32_t read_complete;
        } *sync;
+
+       struct omap_gem_vm_ops *ops;
+
+       /**
+        * per-mapper private data..
+        *
+        * TODO maybe there can be a more flexible way to store per-mapper
+        * data.. for now I just keep it simple, and since this is only
+        * accessible externally via omap_gem_priv()/omap_get_set_priv()
+        */
+       void *priv[MAX_MAPPERS];
+
 };
 
 static int get_pages(struct drm_gem_object *obj, struct page ***pages);
@@ -304,6 +316,7 @@ uint32_t omap_gem_flags(struct drm_gem_object *obj)
 {
        return to_omap_bo(obj)->flags;
 }
+EXPORT_SYMBOL(omap_gem_flags);
 
 /** get mmap offset */
 static uint64_t mmap_offset(struct drm_gem_object *obj)
@@ -333,6 +346,7 @@ uint64_t omap_gem_mmap_offset(struct drm_gem_object *obj)
        mutex_unlock(&obj->dev->struct_mutex);
        return offset;
 }
+EXPORT_SYMBOL(omap_gem_mmap_offset);
 
 /** get mmap size */
 size_t omap_gem_mmap_size(struct drm_gem_object *obj)
@@ -365,6 +379,7 @@ int omap_gem_tiled_size(struct drm_gem_object *obj, uint16_t *w, uint16_t *h)
        }
        return -EINVAL;
 }
+EXPORT_SYMBOL(omap_gem_tiled_size);
 
 /* Normal handling for the case of faulting in non-tiled buffers */
 static int fault_1d(struct drm_gem_object *obj,
@@ -597,6 +612,9 @@ int omap_gem_mmap_obj(struct drm_gem_object *obj,
                vma->vm_page_prot = vm_get_page_prot(vma->vm_flags);
        }
 
+       if (omap_obj->ops && omap_obj->ops->mmap)
+               omap_obj->ops->mmap(obj->filp, vma);
+
        return 0;
 }
 
@@ -809,6 +827,7 @@ fail:
 
        return ret;
 }
+EXPORT_SYMBOL(omap_gem_get_paddr);
 
 /* Release physical address, when DMA is no longer being performed.. this
  * could potentially unpin and unmap buffers from TILER
@@ -839,6 +858,7 @@ void omap_gem_put_paddr(struct drm_gem_object *obj)
 
        mutex_unlock(&obj->dev->struct_mutex);
 }
+EXPORT_SYMBOL(omap_gem_put_paddr);
 
 /* Get rotated scanout address (only valid if already pinned), at the
  * specified orientation and x,y offset from top-left corner of buffer
@@ -869,6 +889,7 @@ int omap_gem_tiled_stride(struct drm_gem_object *obj, uint32_t orient)
                ret = tiler_stride(gem2fmt(omap_obj->flags), orient);
        return ret;
 }
+EXPORT_SYMBOL(omap_gem_tiled_stride);
 
 /* acquire pages when needed (for example, for DMA where physically
  * contiguous buffer is not required
@@ -918,6 +939,7 @@ int omap_gem_get_pages(struct drm_gem_object *obj, struct page ***pages,
        mutex_unlock(&obj->dev->struct_mutex);
        return ret;
 }
+EXPORT_SYMBOL(omap_gem_get_pages);
 
 /* release pages when DMA no longer being performed */
 int omap_gem_put_pages(struct drm_gem_object *obj)
@@ -928,6 +950,7 @@ int omap_gem_put_pages(struct drm_gem_object *obj)
         */
        return 0;
 }
+EXPORT_SYMBOL(omap_gem_put_pages);
 
 /* Get kernel virtual address for CPU access.. this more or less only
  * exists for omap_fbdev.  This should be called with struct_mutex
@@ -1064,15 +1087,26 @@ static inline bool is_waiting(struct omap_gem_sync_waiter *waiter)
 
 static void sync_op_update(void)
 {
+       LIST_HEAD(call_list);
+
        struct omap_gem_sync_waiter *waiter, *n;
        list_for_each_entry_safe(waiter, n, &waiters, list) {
                if (!is_waiting(waiter)) {
                        list_del(&waiter->list);
-                       SYNC("notify: %p", waiter);
-                       waiter->notify(waiter->arg);
-                       kfree(waiter);
+                       list_add(&waiter->list, &call_list);
                }
        }
+
+       spin_unlock(&sync_lock);
+
+       list_for_each_entry_safe(waiter, n, &call_list, list) {
+               SYNC("notify: %p", waiter);
+               list_del(&waiter->list);
+               waiter->notify(waiter->arg);
+               kfree(waiter);
+       }
+
+       spin_lock(&sync_lock);
 }
 
 static inline int sync_op(struct drm_gem_object *obj,
@@ -1123,17 +1157,20 @@ void omap_gem_op_update(void)
        sync_op_update();
        spin_unlock(&sync_lock);
 }
+EXPORT_SYMBOL(omap_gem_op_update);
 
 /* mark the start of read and/or write operation */
 int omap_gem_op_start(struct drm_gem_object *obj, enum omap_gem_op op)
 {
        return sync_op(obj, op, true);
 }
+EXPORT_SYMBOL(omap_gem_op_start);
 
 int omap_gem_op_finish(struct drm_gem_object *obj, enum omap_gem_op op)
 {
        return sync_op(obj, op, false);
 }
+EXPORT_SYMBOL(omap_gem_op_finish);
 
 static DECLARE_WAIT_QUEUE_HEAD(sync_event);
 
@@ -1168,18 +1205,9 @@ int omap_gem_op_sync(struct drm_gem_object *obj, enum omap_gem_op op)
                        SYNC("waited: %p", waiter);
                        list_add_tail(&waiter->list, &waiters);
                        spin_unlock(&sync_lock);
-                       ret = wait_event_interruptible(sync_event,
-                                       (waiter_task == NULL));
+                       wait_event(sync_event, (waiter_task == NULL));
                        spin_lock(&sync_lock);
-                       if (waiter_task) {
-                               SYNC("interrupted: %p", waiter);
-                               /* we were interrupted */
-                               list_del(&waiter->list);
-                               waiter_task = NULL;
-                       } else {
-                               /* freed in sync_op_update() */
-                               waiter = NULL;
-                       }
+                       waiter = NULL;
                }
                spin_unlock(&sync_lock);
 
@@ -1188,6 +1216,7 @@ int omap_gem_op_sync(struct drm_gem_object *obj, enum omap_gem_op op)
        }
        return ret;
 }
+EXPORT_SYMBOL(omap_gem_op_sync);
 
 /* call fxn(arg), either synchronously or asynchronously if the op
  * is currently blocked..  fxn() can be called from any context
@@ -1234,6 +1263,7 @@ int omap_gem_op_async(struct drm_gem_object *obj, enum omap_gem_op op,
 
        return 0;
 }
+EXPORT_SYMBOL(omap_gem_op_async);
 
 /* special API so PVR can update the buffer to use a sync-object allocated
  * from it's sync-obj heap.  Only used for a newly allocated (from PVR's
@@ -1271,6 +1301,7 @@ unlock:
        spin_unlock(&sync_lock);
        return ret;
 }
+EXPORT_SYMBOL(omap_gem_set_sync_object);
 
 /* don't call directly.. called from GEM core when it is time to actually
  * free the object..
@@ -1315,7 +1346,7 @@ void omap_gem_free_object(struct drm_gem_object *obj)
 
        drm_gem_object_release(obj);
 
-       kfree(obj);
+       kfree(omap_obj);
 }
 
 /* convenience method to construct a GEM buffer object, and userspace handle */
@@ -1331,8 +1362,9 @@ int omap_gem_new_handle(struct drm_device *dev, struct drm_file *file,
 
        ret = drm_gem_handle_create(file, obj, handle);
        if (ret) {
+               struct omap_gem_object *omap_obj = to_omap_bo(obj);
                drm_gem_object_release(obj);
-               kfree(obj); /* TODO isn't there a dtor to call? just copying i915 */
+               kfree(omap_obj); /* TODO isn't there a dtor to call? just copying i915 */
                return ret;
        }
 
@@ -1495,3 +1527,69 @@ void omap_gem_deinit(struct drm_device *dev)
         */
        kfree(usergart);
 }
+
+/****** PLUGIN API specific ******/
+
+/* This constructor is mainly to give plugins a way to wrap their
+ * own allocations
+ */
+struct drm_gem_object *omap_gem_new_ext(struct drm_device *dev,
+               union omap_gem_size gsize, uint32_t flags,
+               dma_addr_t paddr, struct page **pages,
+               struct omap_gem_vm_ops *ops)
+{
+       struct drm_gem_object *obj;
+
+       BUG_ON((flags & OMAP_BO_TILED) && !pages);
+
+       if (paddr)
+               flags |= OMAP_BO_DMA;
+
+       obj = omap_gem_new(dev, gsize, flags | OMAP_BO_EXT_MEM);
+       if (obj) {
+               struct omap_gem_object *omap_obj = to_omap_bo(obj);
+               omap_obj->paddr = paddr;
+               omap_obj->pages = pages;
+               omap_obj->ops = ops;
+       }
+       return obj;
+}
+EXPORT_SYMBOL(omap_gem_new_ext);
+
+void omap_gem_vm_open(struct vm_area_struct *vma)
+{
+       struct drm_gem_object *obj = vma->vm_private_data;
+       struct omap_gem_object *omap_obj = to_omap_bo(obj);
+
+       if (omap_obj->ops && omap_obj->ops->open)
+               omap_obj->ops->open(vma);
+       else
+               drm_gem_vm_open(vma);
+
+}
+
+void omap_gem_vm_close(struct vm_area_struct *vma)
+{
+       struct drm_gem_object *obj = vma->vm_private_data;
+       struct omap_gem_object *omap_obj = to_omap_bo(obj);
+
+       if (omap_obj->ops && omap_obj->ops->close)
+               omap_obj->ops->close(vma);
+       else
+               drm_gem_vm_close(vma);
+
+}
+
+void *omap_gem_priv(struct drm_gem_object *obj, int mapper_id)
+{
+       BUG_ON((mapper_id >= MAX_MAPPERS) || (mapper_id < 0));
+       return to_omap_bo(obj)->priv[mapper_id];
+}
+EXPORT_SYMBOL(omap_gem_priv);
+
+void omap_gem_set_priv(struct drm_gem_object *obj, int mapper_id, void *priv)
+{
+       BUG_ON((mapper_id >= MAX_MAPPERS) || (mapper_id < 0));
+       to_omap_bo(obj)->priv[mapper_id] = priv;
+}
+EXPORT_SYMBOL(omap_gem_set_priv);