msm: kgsl: hold a process_private refcount in process_mem_print

There is a possible race condition where the process can be going
away while its debugfs 'mem' file is being read, which could cause
memory corruption.

Conflicts:

	drivers/gpu/msm/kgsl.c

CRs-Fixed: 627780
Change-Id: I697486faeb3f186fd1220d0acc1e449a4f7b77b0
Signed-off-by: Jeremy Gebben <jgebben@codeaurora.org>
Signed-off-by: Hareesh Gundu <hareeshg@codeaurora.org>
This commit is contained in:
Jeremy Gebben
2014-05-28 17:17:13 +05:30
committed by Hareesh Gundu
parent 789a7e2fde
commit 441f3c2d77
3 changed files with 54 additions and 21 deletions
+12 -19
View File
@@ -64,14 +64,8 @@ struct kgsl_dma_buf_meta {
struct sg_table *table;
};
static void kgsl_put_process_private(struct kgsl_device *device,
struct kgsl_process_private *private);
static void kgsl_mem_entry_detach_process(struct kgsl_mem_entry *entry);
static void
kgsl_put_process_private(struct kgsl_device *device,
struct kgsl_process_private *private);
/**
* kgsl_trace_issueibcmds() - Call trace_issueibcmds by proxy
* device: KGSL device
@@ -352,8 +346,8 @@ kgsl_mem_entry_attach_process(struct kgsl_mem_entry *entry,
{
int ret;
struct kgsl_process_private *process = dev_priv->process_priv;
ret = kref_get_unless_zero(&process->refcount);
ret = kgsl_process_private_get(process);
if (!ret)
return -EBADF;
@@ -392,7 +386,7 @@ kgsl_mem_entry_attach_process(struct kgsl_mem_entry *entry,
return ret;
err_put_proc_priv:
kgsl_put_process_private(dev_priv->device, process);
kgsl_process_private_put(process);
return ret;
}
@@ -415,7 +409,7 @@ static void kgsl_mem_entry_detach_process(struct kgsl_mem_entry *entry)
entry->priv->stats[entry->memtype].cur -= entry->memdesc.size;
spin_unlock(&entry->priv->mem_lock);
kgsl_put_process_private(entry->dev_priv->device, entry->priv);
kgsl_process_private_put(entry->priv);
entry->priv = NULL;
}
@@ -473,7 +467,7 @@ int kgsl_context_init(struct kgsl_device_private *dev_priv,
* the context is destroyed. This will also prevent the pagetable
* from being destroyed
*/
if (!kref_get_unless_zero(&dev_priv->process_priv->refcount))
if (!kgsl_process_private_get(dev_priv->process_priv))
goto fail_free_id;
context->device = dev_priv->device;
context->dev_priv = dev_priv;
@@ -568,8 +562,7 @@ kgsl_context_destroy(struct kref *kref)
}
write_unlock(&device->context_lock);
kgsl_sync_timeline_destroy(context);
kgsl_put_process_private(device,
context->proc_priv);
kgsl_process_private_put(context->proc_priv);
device->ftbl->drawctxt_destroy(context);
}
@@ -811,9 +804,8 @@ static void kgsl_destroy_process_private(struct kref *kref)
return;
}
static void
kgsl_put_process_private(struct kgsl_device *device,
struct kgsl_process_private *private)
void
kgsl_process_private_put(struct kgsl_process_private *private)
{
mutex_lock(&kgsl_driver.process_mutex);
@@ -842,7 +834,8 @@ kgsl_find_process_private(struct kgsl_device_private *cur_dev_priv)
mutex_lock(&kgsl_driver.process_mutex);
list_for_each_entry(private, &kgsl_driver.process_list, list) {
if (private->pid == task_tgid_nr(current)) {
kref_get(&private->refcount);
if (!kgsl_process_private_get(private))
private = NULL;
goto done;
}
}
@@ -915,7 +908,7 @@ done:
error:
mutex_unlock(&private->process_private_mutex);
kgsl_put_process_private(cur_dev_priv->device, private);
kgsl_process_private_put(private);
return NULL;
}
@@ -1009,7 +1002,7 @@ static int kgsl_release(struct inode *inodep, struct file *filep)
kfree(dev_priv);
kgsl_put_process_private(device, private);
kgsl_process_private_put(private);
pm_runtime_put(device->parentdev);
return result;
+24 -2
View File
@@ -303,14 +303,36 @@ static int process_mem_print(struct seq_file *s, void *unused)
static int process_mem_open(struct inode *inode, struct file *file)
{
return single_open(file, process_mem_print, inode->i_private);
struct kgsl_process_private *private = inode->i_private;
/*
* Hold a reference count on the process while open
* in case the process tries to die in the meantime.
* If the process is already dying we cannot get a
* refcount, print nothing.
*/
if (!private || !kgsl_process_private_get(private))
return -ENODEV;
return single_open(file, process_mem_print, private);
}
static int process_mem_release(struct inode *inode, struct file *file)
{
struct kgsl_process_private *private = inode->i_private;
if (private)
kgsl_process_private_put(private);
return single_release(inode, file);
}
static const struct file_operations process_mem_fops = {
.open = process_mem_open,
.read = seq_read,
.llseek = seq_lseek,
.release = single_release,
.release = process_mem_release,
};
+18
View File
@@ -696,6 +696,24 @@ void kgsl_cmdbatch_destroy(struct kgsl_cmdbatch *cmdbatch);
void kgsl_cmdbatch_destroy_object(struct kref *kref);
/**
* kgsl_process_private_get() - increment the refcount on a kgsl_process_private
* struct
* @process: Pointer to the KGSL process_private
*
* Returns 0 if the structure is invalid and a reference count could not be
* obtained, nonzero otherwise.
*/
static inline int kgsl_process_private_get(struct kgsl_process_private *process)
{
int ret = 0;
if (process != NULL)
ret = kref_get_unless_zero(&process->refcount);
return ret;
}
void kgsl_process_private_put(struct kgsl_process_private *private);
/**
* kgsl_cmdbatch_put() - Decrement the refcount for a command batch object
* @cmdbatch: Pointer to the command batch object