diff options
Diffstat (limited to 'drivers/gpu/ion/ion.c')
| -rw-r--r-- | drivers/gpu/ion/ion.c | 75 |
1 files changed, 73 insertions, 2 deletions
diff --git a/drivers/gpu/ion/ion.c b/drivers/gpu/ion/ion.c index df03cfa0144..dda8c3d77cb 100644 --- a/drivers/gpu/ion/ion.c +++ b/drivers/gpu/ion/ion.c @@ -103,6 +103,7 @@ struct ion_client { */ struct ion_handle { struct kref ref; + unsigned int user_ref_count; struct ion_client *client; struct ion_buffer *buffer; struct rb_node node; @@ -348,6 +349,48 @@ static int ion_handle_put(struct ion_handle *handle) return kref_put(&handle->ref, ion_handle_destroy); } +/* 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(handle); + mutex_unlock(&client->lock); + + return ret; +} + +static int user_ion_handle_put(struct ion_handle *handle) +{ + int ret = 0; + + if (--handle->user_ref_count == 0) + ret = ion_handle_put(handle); + + return ret; +} + static struct ion_handle *ion_handle_lookup(struct ion_client *client, struct ion_buffer *buffer) { @@ -521,6 +564,29 @@ void ion_free(struct ion_client *client, struct ion_handle *handle) } EXPORT_SYMBOL(ion_free); +void user_ion_free(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) { + mutex_unlock(&client->lock); + WARN(1, "%s: invalid handle passed to free.\n", __func__); + return; + } + if (!handle->user_ref_count > 0) { + mutex_unlock(&client->lock); + WARN(1, "%s: User does not have access!\n", __func__); + return; + } + user_ion_handle_put(handle); + mutex_unlock(&client->lock); +} +EXPORT_SYMBOL(user_ion_free); + int ion_phys(struct ion_client *client, struct ion_handle *handle, ion_phys_addr_t *addr, size_t *len) { @@ -1367,8 +1433,9 @@ static long ion_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) if (IS_ERR(data.handle)) return PTR_ERR(data.handle); + pass_to_user(data.handle); if (copy_to_user((void __user *)arg, &data, sizeof(data))) { - ion_free(client, data.handle); + user_ion_free(client, data.handle); return -EFAULT; } break; @@ -1404,7 +1471,7 @@ static long ion_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) mutex_unlock(&client->lock); if (!valid) return -EINVAL; - ion_free(client, data.handle); + user_ion_free(client, data.handle); break; } case ION_IOC_MAP: @@ -1438,6 +1505,10 @@ static long ion_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) if (IS_ERR(data.handle)) { ret = PTR_ERR(data.handle); data.handle = NULL; + } else { + data.handle = pass_to_user(data.handle); + if (IS_ERR(data.handle)) + ret = PTR_ERR(data.handle); } if (copy_to_user((void __user *)arg, &data, sizeof(struct ion_fd_data))) |
