import PULS_20180308
[GitHub/mt8127/android_kernel_alcatel_ttab.git] / drivers / staging / android / ion / ion.c
index a59d14bb5aea2ec56002a7b5e50133418f05ef34..c6551ffa068a2ca9146b73c21fb27d5db3f1f796 100644 (file)
@@ -16,6 +16,8 @@
  */
 
 #include <linux/device.h>
+#include <linux/err.h>
+#include <linux/atomic.h>
 #include <linux/file.h>
 #include <linux/freezer.h>
 #include <linux/fs.h>
@@ -455,18 +457,76 @@ static void ion_handle_get(struct ion_handle *handle)
        kref_get(&handle->ref);
 }
 
-static int ion_handle_put(struct ion_handle *handle)
+/* Must hold the client lock */
+static struct ion_handle* ion_handle_get_check_overflow(struct ion_handle *handle)
+{
+       if (atomic_read(&handle->ref.refcount) + 1 == 0)
+               return ERR_PTR(-EOVERFLOW);
+       ion_handle_get(handle);
+       return handle;
+}
+
+static int ion_handle_put_nolock(struct ion_handle *handle)
+{
+       int ret;
+
+       ret = kref_put(&handle->ref, ion_handle_destroy);
+
+       return ret;
+}
+
+int ion_handle_put(struct ion_handle *handle)
 {
        struct ion_client *client = handle->client;
        int ret;
 
        mutex_lock(&client->lock);
-       ret = kref_put(&handle->ref, ion_handle_destroy);
+       ret = ion_handle_put_nolock(handle);
        mutex_unlock(&client->lock);
 
        return ret;
 }
 
+/* Must hold the client lock */
+static void user_ion_handle_get(struct ion_handle *handle)
+{
+       if (handle->user_ref_count++ == 0)
+               kref_get(&handle->ref);
+
+}
+
+/* Must hold the client lock */
+static struct ion_handle *user_ion_handle_get_check_overflow(struct ion_handle *handle)
+{
+       if (handle->user_ref_count + 1 == 0)
+               return ERR_PTR(-EOVERFLOW);
+       user_ion_handle_get(handle);
+       return handle;
+}
+/* passes a kref to the user ref count.
+ * We know we're holding a kref to the object before and
+ * after this call, so no need to reverify handle.
+ */
+static struct ion_handle *pass_to_user(struct ion_handle *handle)
+{
+       struct ion_client *client = handle->client;
+       struct ion_handle *ret;
+       mutex_lock(&client->lock);
+       ret = user_ion_handle_get_check_overflow(handle);
+       ion_handle_put_nolock(handle);
+       mutex_unlock(&client->lock);
+       return ret;
+}
+
+/* Must hold the client lock */
+static int user_ion_handle_put_nolock(struct ion_handle *handle)
+{
+       int ret;
+       if (--handle->user_ref_count == 0)
+               ret = ion_handle_put_nolock(handle);
+
+       return ret;
+}
 static struct ion_handle *ion_handle_lookup(struct ion_client *client,
                                            struct ion_buffer *buffer)
 {
@@ -484,20 +544,31 @@ static struct ion_handle *ion_handle_lookup(struct ion_client *client,
        return ERR_PTR(-EINVAL);
 }
 
-static struct ion_handle *ion_handle_get_by_id(struct ion_client *client,
+static struct ion_handle *ion_handle_get_by_id_nolock(struct ion_client *client,
                                                int id)
 {
        struct ion_handle *handle;
 
-       mutex_lock(&client->lock);
        handle = idr_find(&client->idr, id);
        if (handle)
-               ion_handle_get(handle);
+               return ion_handle_get_check_overflow(handle);
+
+       return ERR_PTR(-EINVAL);
+}
+
+struct ion_handle *ion_handle_get_by_id(struct ion_client *client,
+                                               int id)
+{
+       struct ion_handle *handle;
+
+       mutex_lock(&client->lock);
+       handle = ion_handle_get_by_id_nolock(client, id);
        mutex_unlock(&client->lock);
 
-       return handle ? handle : ERR_PTR(-EINVAL);
+       return handle;
 }
 
+
 static bool ion_handle_validate(struct ion_client *client,
                                struct ion_handle *handle)
 {
@@ -540,7 +611,7 @@ static int ion_handle_add(struct ion_client *client, struct ion_handle *handle)
 
 struct ion_handle *__ion_alloc(struct ion_client *client, size_t len,
                             size_t align, unsigned int heap_id_mask,
-                            unsigned int flags)
+                            unsigned int flags, bool grab_handle)
 {
        struct ion_handle *handle;
        struct ion_device *dev = client->dev;
@@ -606,6 +677,8 @@ struct ion_handle *__ion_alloc(struct ion_client *client, size_t len,
         }
 
        mutex_lock(&client->lock);
+       if (grab_handle)
+               ion_handle_get(handle);
        ret = ion_handle_add(client, handle);
        mutex_unlock(&client->lock);
        if (ret) {
@@ -623,44 +696,49 @@ struct ion_handle *ion_alloc(struct ion_client *client, size_t len,
                              size_t align, unsigned int heap_id_mask,
                              unsigned int flags)
 {
-    struct ion_handle * handle;
-    handle = __ion_alloc(client, len, align, heap_id_mask, flags);
-    if(IS_ERR_OR_NULL(handle)) {
-        IONMSG("%s handle is error 0x%p", __func__, handle);
-       return handle;
-    }
-    
-    ion_debug_kern_rec(client, handle->buffer, handle, ION_FUNCTION_ALLOC, 0, 0, 0, 0);
-
-    return handle;
+    return __ion_alloc(client, len, align, heap_id_mask, flags, false);
 }
 EXPORT_SYMBOL(ion_alloc);
 
-void __ion_free(struct ion_client *client, struct ion_handle *handle, int from_kern)
+static void user_ion_free_nolock(struct ion_client *client, struct ion_handle *handle)
+{
+       bool valid_handle;
+       BUG_ON(client != handle->client);
+       valid_handle = ion_handle_validate(client, handle);
+       if (!valid_handle) {
+               WARN(1, "%s: invalid handle passed to free.\n", __func__);
+               return;
+       }
+       if (!handle->user_ref_count > 0) {
+               WARN(1, "%s: User does not have access!\n", __func__);
+               return;
+       }
+       user_ion_handle_put_nolock(handle);
+}
+static void ion_free_nolock(struct ion_client *client, struct ion_handle *handle)
 {
        bool valid_handle;
 
        BUG_ON(client != handle->client);
 
-       mutex_lock(&client->lock);
        valid_handle = ion_handle_validate(client, handle);
 
        if (!valid_handle) {
                WARN(1, "%s: invalid handle passed to free.\n", __func__);
-               mutex_unlock(&client->lock);
                return;
        }
-       mutex_unlock(&client->lock);
-
-       if(from_kern)
-               ion_debug_kern_rec(client, handle->buffer, NULL, ION_FUNCTION_FREE, 0, 0, 0, 0);
-
-       ion_handle_put(handle);
+       ion_handle_put_nolock(handle);
 }
 
 void ion_free(struct ion_client *client, struct ion_handle *handle)
 {
-       return __ion_free(client, handle, 1);
+       BUG_ON(client != handle->client);
+
+       mutex_lock(&client->lock);
+       ion_free_nolock(client, handle);
+       mutex_unlock(&client->lock);
+
+       ion_debug_kern_rec(client, handle->buffer, NULL, ION_FUNCTION_FREE, 0, 0, 0, 0);
 }
 EXPORT_SYMBOL(ion_free);
 
@@ -1355,7 +1433,7 @@ struct ion_handle *__ion_import_dma_buf(struct ion_client *client, int fd, int f
        /* if a handle exists for this buffer just take a reference to it */
        handle = ion_handle_lookup(client, buffer);
        if (!IS_ERR(handle)) {
-               ion_handle_get(handle);
+               handle = ion_handle_get_check_overflow(handle);
                mutex_unlock(&client->lock);
                goto end;
        }
@@ -1471,13 +1549,13 @@ static long ion_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
                handle = __ion_alloc(client, data.allocation.len,
                                                data.allocation.align,
                                                data.allocation.heap_id_mask,
-                                               data.allocation.flags);
+                                               data.allocation.flags, true);
                if (IS_ERR(handle)) {
                         ret = PTR_ERR(handle);
                         IONMSG("ION_IOC_ALLOC handle is invalid. ret = %d.\n", ret);
                        return ret;
                 }
-
+               pass_to_user(handle);
                data.allocation.handle = handle->id;
 
                cleanup_handle = handle;
@@ -1487,14 +1565,16 @@ static long ion_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
        {
                struct ion_handle *handle;
 
-               handle = ion_handle_get_by_id(client, data.handle.handle);
+               mutex_lock(&client->lock);
+               handle = ion_handle_get_by_id_nolock(client, data.handle.handle);
                if (IS_ERR(handle)) {
-                        ret = PTR_ERR(handle);
-                        IONMSG("ION_IOC_FREE handle is invalid. handle = %d, ret = %d.\n", data.handle.handle, ret);
-                       return ret;
-                }
-               __ion_free(client, handle, 0);
-               ion_handle_put(handle);
+                       mutex_unlock(&client->lock);
+                       IONMSG("ION_IOC_FREE handle is invalid. handle = %d, ret = %d.\n", data.handle.handle, ret);
+                       return PTR_ERR(handle);
+               }
+               user_ion_free_nolock(client, handle);
+               ion_handle_put_nolock(handle);
+               mutex_unlock(&client->lock);
                break;
        }
        case ION_IOC_SHARE:
@@ -1524,10 +1604,13 @@ static long ion_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
                {
                        ret = PTR_ERR(handle);
                        IONMSG("ion_import fail: fd=%d, ret=%d\n", data.fd.fd, ret);
-               }
-               else
-                       data.handle.handle = handle->id;
-               break;
+               } else {
+                       handle = pass_to_user(handle);
+                       if (IS_ERR(handle))
+                               ret = PTR_ERR(handle);
+                       else
+                               data.handle.handle = handle->id;
+               }               break;
        }
        case ION_IOC_SYNC:
        {
@@ -1552,12 +1635,18 @@ static long ion_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
 
        if (dir & _IOC_READ) {
                if (copy_to_user((void __user *)arg, &data, _IOC_SIZE(cmd))) {
-                       if (cleanup_handle) 
-                               ion_free(client, cleanup_handle);
+                       if (cleanup_handle) {
+                               mutex_lock(&client->lock);
+                               user_ion_free_nolock(client, cleanup_handle);
+                               ion_handle_put_nolock(cleanup_handle);
+                               mutex_unlock(&client->lock);
+                       }
                         IONMSG("ion_ioctl copy_to_user fail! cmd = %d, n = %d.\n", cmd, _IOC_SIZE(cmd));
                        return -EFAULT;
                }
        }
+       if (cleanup_handle)
+               ion_handle_put(cleanup_handle);
        return ret;
 }