msm: kgsl: Fix context reference counting
Get rid of kgsl_find_context. Use instead kgsl_context_get that does
correct RCU read locking around the itr_find and increases the
reference count on the context before returning it. This eliminates
the chance that a context will be destroyed while somebody is still
using it. Of course increased use of kgsl_context_get is accompanied
by kgsl_context_put in all the right places.
Change-Id: Ic0dedbad73d497fd9b451aefad8e5b28d33b829d
Signed-off-by: Jordan Crouse <jcrouse@codeaurora.org>
diff --git a/drivers/gpu/msm/kgsl.c b/drivers/gpu/msm/kgsl.c
index 602d47b..a218c08 100644
--- a/drivers/gpu/msm/kgsl.c
+++ b/drivers/gpu/msm/kgsl.c
@@ -972,7 +972,7 @@
result = -EFAULT;
break;
}
- context = kgsl_find_context(dev_priv, id);
+ context = kgsl_context_get_owner(dev_priv, id);
if (!context) {
result = -EINVAL;
break;
@@ -982,12 +982,14 @@
* the out parameter
*/
if (copy_to_user(param->value, &(context->reset_status),
- sizeof(unsigned int))) {
+ sizeof(unsigned int)))
result = -EFAULT;
- break;
+ else {
+ /* Clear reset status once its been queried */
+ context->reset_status = KGSL_CTX_STAT_NO_ERROR;
}
- /* Clear reset status once its been queried */
- context->reset_status = KGSL_CTX_STAT_NO_ERROR;
+
+ kgsl_context_put(context);
break;
}
default:
@@ -1063,19 +1065,14 @@
{
struct kgsl_device_waittimestamp_ctxtid *param = data;
struct kgsl_context *context;
- int result;
+ long result = -EINVAL;
- context = kgsl_find_context(dev_priv, param->context_id);
- if (context == NULL)
- return -EINVAL;
- /*
- * A reference count is needed here, because waittimestamp may
- * block with the device mutex unlocked and userspace could
- * request for the context to be destroyed during that time.
- */
- kgsl_context_get(context);
- result = _device_waittimestamp(dev_priv, context,
+ context = kgsl_context_get_owner(dev_priv, param->context_id);
+
+ if (context)
+ result = _device_waittimestamp(dev_priv, context,
param->timestamp, param->timeout);
+
kgsl_context_put(context);
return result;
}
@@ -1088,7 +1085,7 @@
struct kgsl_ibdesc *ibdesc;
struct kgsl_context *context;
- context = kgsl_find_context(dev_priv, param->drawctxt_id);
+ context = kgsl_context_get_owner(dev_priv, param->drawctxt_id);
if (context == NULL) {
result = -EINVAL;
goto done;
@@ -1164,7 +1161,7 @@
free_ibdesc:
kfree(ibdesc);
done:
-
+ kgsl_context_put(context);
return result;
}
@@ -1197,14 +1194,16 @@
{
struct kgsl_cmdstream_readtimestamp_ctxtid *param = data;
struct kgsl_context *context;
+ long result = -EINVAL;
- context = kgsl_find_context(dev_priv, param->context_id);
- if (context == NULL)
- return -EINVAL;
+ context = kgsl_context_get_owner(dev_priv, param->context_id);
-
- return _cmdstream_readtimestamp(dev_priv, context,
+ if (context)
+ result = _cmdstream_readtimestamp(dev_priv, context,
param->type, ¶m->timestamp);
+
+ kgsl_context_put(context);
+ return result;
}
static void kgsl_freemem_event_cb(struct kgsl_device *device,
@@ -1261,16 +1260,14 @@
{
struct kgsl_cmdstream_freememontimestamp_ctxtid *param = data;
struct kgsl_context *context;
+ long result = -EINVAL;
- context = kgsl_find_context(dev_priv, param->context_id);
- if (context == NULL) {
- KGSL_DRV_ERR(dev_priv->device,
- "invalid drawctxt context_id %d\n", param->context_id);
- return -EINVAL;
- }
-
- return _cmdstream_freememontimestamp(dev_priv, param->gpuaddr,
+ context = kgsl_context_get_owner(dev_priv, param->context_id);
+ if (context)
+ result = _cmdstream_freememontimestamp(dev_priv, param->gpuaddr,
context, param->timestamp, param->type);
+ kgsl_context_put(context);
+ return result;
}
static long kgsl_ioctl_drawctxt_create(struct kgsl_device_private *dev_priv,
@@ -1306,19 +1303,18 @@
static long kgsl_ioctl_drawctxt_destroy(struct kgsl_device_private *dev_priv,
unsigned int cmd, void *data)
{
- int result = 0;
struct kgsl_drawctxt_destroy *param = data;
struct kgsl_context *context;
+ long result = -EINVAL;
- context = kgsl_find_context(dev_priv, param->drawctxt_id);
+ context = kgsl_context_get_owner(dev_priv, param->drawctxt_id);
- if (context == NULL) {
- result = -EINVAL;
- goto done;
+ if (context) {
+ kgsl_context_detach(context);
+ result = 0;
}
- kgsl_context_detach(context);
-done:
+ kgsl_context_put(context);
return result;
}