msm: kgsl: cancel events from kgsl_release

Events need to be cancelled when an fd is released,
to avoid possible memory leaks or use after free.

When the event is cancelled, its callback is called.
Currently this is sufficient since events are used for
resource management and we have no option but to
release the lock or memory. If future uses need to
distinguish between the callback firing and
a cancel, they can look at the timestamp passed to
the callback, which will be before the timestamp they
expected. Otherwise a separate cancel callback can
be added.
This commit is contained in:
SecureCRT 2012-06-23 18:52:06 +08:00
parent f6acf3ab9f
commit 4520a7c383
2 changed files with 46 additions and 6 deletions

View File

@ -1,4 +1,4 @@
/* Copyright (c) 2008-2011, Code Aurora Forum. All rights reserved.
/* Copyright (c) 2008-2012, Code Aurora Forum. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@ -55,12 +55,14 @@ static struct ion_client *kgsl_ion_client;
* @ts - the timestamp to trigger the event on
* @cb - callback function to call when the timestamp expires
* @priv - private data for the specific event type
* @owner - driver instance that owns this event
*
* @returns - 0 on success or error code on failure
*/
static int kgsl_add_event(struct kgsl_device *device, u32 ts,
void (*cb)(struct kgsl_device *, void *, u32), void *priv)
void (*cb)(struct kgsl_device *, void *, u32), void *priv,
struct kgsl_device_private *owner)
{
struct kgsl_event *event;
struct list_head *n;
@ -84,6 +86,7 @@ static int kgsl_add_event(struct kgsl_device *device, u32 ts,
event->timestamp = ts;
event->priv = priv;
event->func = cb;
event->owner = owner;
/* Add the event in order to the list */
@ -105,6 +108,36 @@ static int kgsl_add_event(struct kgsl_device *device, u32 ts,
}
#endif
/**
* kgsl_cancel_events - Cancel all events for a process
* @device - KGSL device for the events to cancel
* @owner - driver instance that owns the events to cancel
*
*/
static void kgsl_cancel_events(struct kgsl_device *device,
struct kgsl_device_private *owner)
{
struct kgsl_event *event, *event_tmp;
unsigned int cur = device->ftbl->readtimestamp(device,
KGSL_TIMESTAMP_RETIRED);
list_for_each_entry_safe(event, event_tmp, &device->events, list) {
if (event->owner != owner)
continue;
/*
* "cancel" the events by calling their callback.
* Currently, events are used for lock and memory
* management, so if the process is dying the right
* thing to do is release or free.
*/
if (event->func)
event->func(device, event->priv, cur);
list_del(&event->list);
kfree(event);
}
}
static inline struct kgsl_mem_entry *
kgsl_mem_entry_create(void)
{
@ -656,6 +689,7 @@ static int kgsl_release(struct inode *inodep, struct file *filep)
* process and this device
*/
kgsl_memqueue_cleanup(device, private);
kgsl_cancel_events(device, dev_priv);
mutex_unlock(&device->mutex);
kfree(dev_priv);
@ -1796,6 +1830,7 @@ static void kgsl_genlock_event_cb(struct kgsl_device *device,
* @timestamp - Timestamp to trigger the event
* @data - User space buffer containing struct kgsl_genlock_event_priv
* @len - length of the userspace buffer
* @owner - driver instance that owns this event
* @returns 0 on success or error code on error
*
* Attack to a genlock handle and register an event to release the
@ -1803,7 +1838,8 @@ static void kgsl_genlock_event_cb(struct kgsl_device *device,
*/
static int kgsl_add_genlock_event(struct kgsl_device *device,
u32 timestamp, void __user *data, int len)
u32 timestamp, void __user *data, int len,
struct kgsl_device_private *owner)
{
struct kgsl_genlock_event_priv *event;
struct kgsl_timestamp_event_genlock priv;
@ -1828,7 +1864,8 @@ static int kgsl_add_genlock_event(struct kgsl_device *device,
return ret;
}
ret = kgsl_add_event(device, timestamp, kgsl_genlock_event_cb, event);
ret = kgsl_add_event(device, timestamp, kgsl_genlock_event_cb, event,
owner);
if (ret)
kfree(event);
@ -1836,7 +1873,8 @@ static int kgsl_add_genlock_event(struct kgsl_device *device,
}
#else
static long kgsl_add_genlock_event(struct kgsl_device *device,
u32 timestamp, void __user *data, int len)
u32 timestamp, void __user *data, int len,
struct kgsl_device_private *owner)
{
return -EINVAL;
}
@ -1859,7 +1897,8 @@ static long kgsl_ioctl_timestamp_event(struct kgsl_device_private *dev_priv,
switch (param->type) {
case KGSL_TIMESTAMP_EVENT_GENLOCK:
ret = kgsl_add_genlock_event(dev_priv->device,
param->timestamp, param->priv, param->len);
param->timestamp, param->priv, param->len,
dev_priv);
break;
default:
ret = -EINVAL;

View File

@ -124,6 +124,7 @@ struct kgsl_event {
void (*func)(struct kgsl_device *, void *, u32);
void *priv;
struct list_head list;
struct kgsl_device_private *owner;
};