Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 9607ab92 authored by Hareesh Gundu's avatar Hareesh Gundu
Browse files

msm: kgsl: Keep private intact until last refcount is put



Currently, we are partially freeing private once last file is
closed but closing last file doesn't necessarily means that
all refcount on private is also put. This results in having a
partially initialized private struct which is not yet freed
and also can't be re-used. As a result if an application is
just opening and closing kgsl device in a loop we will end up
allocating and initializing a new private every time. This also
allows application to mis-use this and create more than allowed
limit of 200 contexts as they can use newly created private to
create new context every time.

To fix this, keep private intact until last refcount is put.
This ensures we can re-use same private if same pid is doing
kgsl open and close repeatedly and also ensures that an app.
can't breach 200 context limit.

Change-Id: Ia1d1071275c7691a3d50bc15477b71387c5806d6
Signed-off-by: default avatarDeepak Kumar <dkumar@codeaurora.org>
Signed-off-by: default avatarHareesh Gundu <hareeshg@codeaurora.org>
parent e8c7d22e
Loading
Loading
Loading
Loading
+34 −34
Original line number Diff line number Diff line
@@ -904,11 +904,27 @@ static void kgsl_destroy_process_private(struct kref *kref)
	struct kgsl_process_private *private = container_of(kref,
			struct kgsl_process_private, refcount);

	mutex_lock(&kgsl_driver.process_mutex);

	debugfs_remove_recursive(private->debug_root);
	kgsl_process_uninit_sysfs(private);

	/* When using global pagetables, do not detach global pagetable */
	if (private->pagetable->name != KGSL_MMU_GLOBAL_PT)
		kgsl_mmu_detach_pagetable(private->pagetable);

	/* Remove the process struct from the master list */
	spin_lock(&kgsl_driver.proclist_lock);
	list_del(&private->list);
	spin_unlock(&kgsl_driver.proclist_lock);

	mutex_unlock(&kgsl_driver.process_mutex);

	put_pid(private->pid);
	idr_destroy(&private->mem_idr);
	idr_destroy(&private->syncsource_idr);

	/* When using global pagetables, do not detach global pagetable */
	/* When using global pagetables, do not put global pagetable */
	if (private->pagetable->name != KGSL_MMU_GLOBAL_PT)
		kgsl_mmu_putpagetable(private->pagetable);

@@ -951,13 +967,6 @@ static struct kgsl_process_private *kgsl_process_private_new(
	struct kgsl_process_private *private;
	struct pid *cur_pid = get_task_pid(current->group_leader, PIDTYPE_PID);

	/*
	 * Flush mem_workqueue to make sure that any lingering
	 * structs (process pagetable etc) are released before
	 * starting over again.
	 */
	flush_workqueue(kgsl_driver.mem_workqueue);

	/* Search in the process list */
	list_for_each_entry(private, &kgsl_driver.process_list, list) {
		if (private->pid == cur_pid) {
@@ -1006,9 +1015,17 @@ static struct kgsl_process_private *kgsl_process_private_new(
		put_pid(private->pid);

		kfree(private);
		private = ERR_PTR(err);
		return ERR_PTR(err);
	}

	/* create the debug directories and add it to the process list */
	kgsl_process_init_sysfs(device, private);
	kgsl_process_init_debugfs(private);

	spin_lock(&kgsl_driver.proclist_lock);
	list_add(&private->list, &kgsl_driver.process_list);
	spin_unlock(&kgsl_driver.proclist_lock);

	return private;
}

@@ -1062,18 +1079,6 @@ static void kgsl_process_private_close(struct kgsl_device_private *dev_priv,
	/* Release all syncsource objects from process private */
	kgsl_syncsource_process_release_syncsources(private);

	debugfs_remove_recursive(private->debug_root);
	kgsl_process_uninit_sysfs(private);

	/* When using global pagetables, do not detach global pagetable */
	if (private->pagetable->name != KGSL_MMU_GLOBAL_PT)
		kgsl_mmu_detach_pagetable(private->pagetable);

	/* Remove the process struct from the master list */
	spin_lock(&kgsl_driver.proclist_lock);
	list_del(&private->list);
	spin_unlock(&kgsl_driver.proclist_lock);

	mutex_unlock(&kgsl_driver.process_mutex);

	kgsl_process_private_put(private);
@@ -1085,25 +1090,20 @@ static struct kgsl_process_private *kgsl_process_private_open(
{
	struct kgsl_process_private *private;

	/*
	 * Flush mem_workqueue to make sure that any lingering
	 * structs (process pagetable etc) are released before
	 * starting over again.
	 */
	flush_workqueue(kgsl_driver.mem_workqueue);

	mutex_lock(&kgsl_driver.process_mutex);
	private = kgsl_process_private_new(device);

	if (IS_ERR(private))
		goto done;

	/*
	 * If this is a new process create the debug directories and add it to
	 * the process list
	 */

	if (private->fd_count++ == 0) {
		kgsl_process_init_sysfs(device, private);
		kgsl_process_init_debugfs(private);

		spin_lock(&kgsl_driver.proclist_lock);
		list_add(&private->list, &kgsl_driver.process_list);
		spin_unlock(&kgsl_driver.proclist_lock);
	}
	private->fd_count++;

done:
	mutex_unlock(&kgsl_driver.process_mutex);
+2 −9
Original line number Diff line number Diff line
// SPDX-License-Identifier: GPL-2.0-only
/*
 * Copyright (c) 2002,2007-2020, The Linux Foundation. All rights reserved.
 * Copyright (c) 2002,2007-2021, The Linux Foundation. All rights reserved.
 */

#include <asm/cacheflush.h>
@@ -247,13 +247,9 @@ static ssize_t process_sysfs_store(struct kobject *kobj,
	return -EIO;
}

/* Dummy release function - we have nothing to do here */
static void process_sysfs_release(struct kobject *kobj)
{
	struct kgsl_process_private *priv;

	priv = container_of(kobj, struct kgsl_process_private, kobj);
	/* Put the refcount we got in kgsl_process_init_sysfs */
	kgsl_process_private_put(priv);
}

static const struct sysfs_ops process_sysfs_ops = {
@@ -301,9 +297,6 @@ void kgsl_process_init_sysfs(struct kgsl_device *device,
{
	int i;

	/* Keep private valid until the sysfs enries are removed. */
	kgsl_process_private_get(private);

	if (kobject_init_and_add(&private->kobj, &process_ktype,
		kgsl_driver.prockobj, "%d", pid_nr(private->pid))) {
		dev_err(device->dev, "Unable to add sysfs for process %d\n",