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

Commit cf71c5ae authored by Linux Build Service Account's avatar Linux Build Service Account Committed by Gerrit - the friendly Code Review server
Browse files

Merge "msm: kgsl: Add a file count to struct kgsl_process_private"

parents f38e857b e392445c
Loading
Loading
Loading
Loading
+141 −120
Original line number Diff line number Diff line
@@ -860,114 +860,165 @@ struct kgsl_process_private *kgsl_process_private_find(pid_t pid)
	return private;
}

/**
 * kgsl_process_private_new() - Helper function to search for process private
 * Returns: Pointer to the found/newly created private struct
 */
static struct kgsl_process_private *kgsl_process_private_new(void)
static struct kgsl_process_private *kgsl_process_private_new(
		struct kgsl_device *device)
{
	struct kgsl_process_private *private;
	pid_t tgid = task_tgid_nr(current);

	/* Search in the process list */
	mutex_lock(&kgsl_driver.process_mutex);
	list_for_each_entry(private, &kgsl_driver.process_list, list) {
		if (private->pid == task_tgid_nr(current)) {
		if (private->pid == tgid) {
			if (!kgsl_process_private_get(private))
				private = NULL;
			goto done;
				private = ERR_PTR(-EINVAL);
			return private;
		}
	}

	/* no existing process private found for this dev_priv, create one */
	/* Create a new object */
	private = kzalloc(sizeof(struct kgsl_process_private), GFP_KERNEL);
	if (private == NULL)
		goto done;
		return ERR_PTR(-ENOMEM);

	kref_init(&private->refcount);

	private->pid = task_tgid_nr(current);
	private->pid = tgid;
	get_task_comm(private->comm, current->group_leader);
	private->mem_rb = RB_ROOT;

	spin_lock_init(&private->mem_lock);
	mutex_init(&private->process_private_mutex);
	/* Add the newly created process struct obj to the process list */
	list_add(&private->list, &kgsl_driver.process_list);
done:
	mutex_unlock(&kgsl_driver.process_mutex);
	spin_lock_init(&private->syncsource_lock);

	idr_init(&private->mem_idr);
	idr_init(&private->syncsource_idr);

	/* Allocate a pagetable for the new process object */
	if (kgsl_mmu_enabled()) {
		private->pagetable = kgsl_mmu_getpagetable(&device->mmu, tgid);
		if (private->pagetable == NULL) {
			idr_destroy(&private->mem_idr);
			idr_destroy(&private->syncsource_idr);

			kfree(private);
			private = ERR_PTR(-ENOMEM);
		}
	}

	return private;
}

/**
 * kgsl_detach_process_private() - Remove a process private from the process
 * private list and free things in the process private that relate to
 * the process id in order to allow next open from same process create
 * a new process private
 * @private: Pointer to process private to detach
static void process_release_memory(struct kgsl_device_private *dev_priv,
		struct kgsl_process_private *private)
{
	struct kgsl_mem_entry *entry;
	int next = 0;

	while (1) {
		spin_lock(&private->mem_lock);
		entry = idr_get_next(&private->mem_idr, &next);
		if (entry == NULL) {
			spin_unlock(&private->mem_lock);
			break;
		}
		/*
		 * If the free pending flag is not set it means that user space
		 * did not free it's reference to this entry, in that case
		 * free a reference to this entry, other references are from
		 * within kgsl so they will be freed eventually by kgsl
		 */
static void kgsl_detach_process_private(struct kgsl_process_private *private)
		if (entry->dev_priv == dev_priv && !entry->pending_free) {
			entry->pending_free = 1;
			spin_unlock(&private->mem_lock);
			kgsl_mem_entry_put(entry);
		} else {
			spin_unlock(&private->mem_lock);
		}
		next = next + 1;
	}
}

static void process_release_sync_sources(struct kgsl_process_private *private)
{
	mutex_lock(&kgsl_driver.process_mutex);
	list_del(&private->list);
	mutex_unlock(&kgsl_driver.process_mutex);
	struct kgsl_syncsource *syncsource;
	int next = 0;

	if (private->kobj.state_in_sysfs)
		kgsl_process_uninit_sysfs(private);
	debugfs_remove_recursive(private->debug_root);
	while (1) {
		spin_lock(&private->syncsource_lock);
		syncsource = idr_get_next(&private->syncsource_idr, &next);
		spin_unlock(&private->syncsource_lock);

		if (syncsource == NULL)
			break;

		kgsl_syncsource_put(syncsource);
		next = next + 1;
	}
}

/**
 * kgsl_get_process_private() - Used to find the process private structure
 * @cur_dev_priv: Current device pointer
 * Finds or creates a new porcess private structire and initializes its members
 * Returns: Pointer to the private process struct obj found/created or
 * NULL if pagetable creation for this process private obj failed.
 */
static struct kgsl_process_private *
kgsl_get_process_private(struct kgsl_device *device)
static void kgsl_process_private_close(struct kgsl_device_private *dev_priv,
		struct kgsl_process_private *private)
{
	struct kgsl_process_private *private;
	mutex_lock(&kgsl_driver.process_mutex);

	private = kgsl_process_private_new();
	/*
	 * If this is the last file on the process take down the debug
	 * directories and garbage collect any outstanding resources
	 */

	if (!private)
		return NULL;
	if (--private->fd_count == 0) {
		kgsl_process_uninit_sysfs(private);
		debugfs_remove_recursive(private->debug_root);

	mutex_lock(&private->process_private_mutex);
		process_release_memory(dev_priv, private);
		process_release_sync_sources(private);

	if (test_bit(KGSL_PROCESS_INIT, &private->priv))
		goto done;
		/* Remove the process struct from the master list */
		list_del(&private->list);
	}

	get_task_comm(private->comm, current->group_leader);
	kgsl_process_private_put(private);
	mutex_unlock(&kgsl_driver.process_mutex);
}

	private->mem_rb = RB_ROOT;
	idr_init(&private->mem_idr);

	idr_init(&private->syncsource_idr);
static struct kgsl_process_private *kgsl_process_private_open(
		struct kgsl_device *device)
{
	struct kgsl_process_private *private;

	if ((!private->pagetable) && kgsl_mmu_enabled()) {
		unsigned long pt_name;
	mutex_lock(&kgsl_driver.process_mutex);
	private = kgsl_process_private_new(device);

		pt_name = task_tgid_nr(current);
		private->pagetable =
			kgsl_mmu_getpagetable(&device->mmu, pt_name);
		if (private->pagetable == NULL)
			goto error;
	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) {
		int ret = kgsl_process_init_sysfs(device, private);
		if (ret) {
			kgsl_process_private_put(private);
			private = ERR_PTR(ret);
			goto done;
		}

	if (kgsl_process_init_sysfs(device, private))
		goto error;
	if (kgsl_process_init_debugfs(private))
		goto error;
		ret = kgsl_process_init_debugfs(private);
		if (ret) {
			kgsl_process_uninit_sysfs(private);
			kgsl_process_private_put(private);
			private = ERR_PTR(ret);
			goto done;
		}

	set_bit(KGSL_PROCESS_INIT, &private->priv);
		list_add(&private->list, &kgsl_driver.process_list);
	}

done:
	mutex_unlock(&private->process_private_mutex);
	mutex_unlock(&kgsl_driver.process_mutex);
	return private;

error:
	mutex_unlock(&private->process_private_mutex);
	kgsl_detach_process_private(private);
	kgsl_process_private_put(private);
	return NULL;
}

static int kgsl_close_device(struct kgsl_device *device)
@@ -992,30 +1043,12 @@ static int kgsl_close_device(struct kgsl_device *device)

}

static int kgsl_release(struct inode *inodep, struct file *filep)
static void device_release_contexts(struct kgsl_device_private *dev_priv)
{
	int result = 0;
	struct kgsl_device_private *dev_priv = filep->private_data;
	struct kgsl_process_private *private = dev_priv->process_priv;
	struct kgsl_device *device = dev_priv->device;
	struct kgsl_context *context;
	struct kgsl_syncsource *syncsource;
	struct kgsl_mem_entry *entry;
	int next = 0;

	filep->private_data = NULL;

	next = 0;
	while (1) {
		syncsource = idr_get_next(&private->syncsource_idr, &next);

		if (syncsource == NULL)
			break;
		kgsl_syncsource_put(syncsource);
		next = next + 1;
	}

	next = 0;
	while (1) {
		read_lock(&device->context_lock);
		context = idr_get_next(&device->context_idr, &next);
@@ -1038,39 +1071,27 @@ static int kgsl_release(struct inode *inodep, struct file *filep)

		next = next + 1;
	}
	next = 0;
	while (1) {
		spin_lock(&private->mem_lock);
		entry = idr_get_next(&private->mem_idr, &next);
		if (entry == NULL) {
			spin_unlock(&private->mem_lock);
			break;
		}
		/*
		 * If the free pending flag is not set it means that user space
		 * did not free it's reference to this entry, in that case
		 * free a reference to this entry, other references are from
		 * within kgsl so they will be freed eventually by kgsl
		 */
		if (entry->dev_priv == dev_priv && !entry->pending_free) {
			entry->pending_free = 1;
			spin_unlock(&private->mem_lock);
			kgsl_mem_entry_put(entry);
		} else {
			spin_unlock(&private->mem_lock);
		}
		next = next + 1;
}

	result = kgsl_close_device(device);
static int kgsl_release(struct inode *inodep, struct file *filep)
{
	struct kgsl_device_private *dev_priv = filep->private_data;
	struct kgsl_device *device = dev_priv->device;
	int result;

	kfree(dev_priv);
	filep->private_data = NULL;

	kgsl_detach_process_private(private);
	/* Release the contexts for the file */
	device_release_contexts(dev_priv);

	kgsl_process_private_put(private);
	/* Close down the process wide resources for the file */
	kgsl_process_private_close(dev_priv, dev_priv->process_priv);

	kfree(dev_priv);

	result = kgsl_close_device(device);
	pm_runtime_put(&device->pdev->dev);

	return result;
}

@@ -1150,10 +1171,10 @@ static int kgsl_open(struct inode *inodep, struct file *filep)
	 * after the first start so that the global pagetable mappings
	 * are set up before we create the per-process pagetable.
	 */
	dev_priv->process_priv = kgsl_get_process_private(device);
	if (dev_priv->process_priv ==  NULL) {
	dev_priv->process_priv = kgsl_process_private_open(device);
	if (IS_ERR(dev_priv->process_priv)) {
		result = PTR_ERR(dev_priv->process_priv);
		kgsl_close_device(device);
		result = -ENOMEM;
		goto err;
	}

+4 −8
Original line number Diff line number Diff line
@@ -479,7 +479,6 @@ struct kgsl_context {
 * @comm: task name of the process
 * @mem_lock: Spinlock to protect the process memory lists
 * @refcount: kref object for reference counting the process
 * @process_private_mutex: Mutex to synchronize access to the process struct
 * @mem_rb: RB tree node for the memory owned by this process
 * @idr: Iterator for assigning IDs to memory allocations
 * @pagetable: Pointer to the pagetable owned by this process
@@ -487,31 +486,28 @@ struct kgsl_context {
 * @debug_root: Pointer to the debugfs root for this process
 * @stats: Memory allocation statistics for this process
 * @syncsource_idr: sync sources created by this process
 * @syncsource_lock: Spinlock to protect the syncsource idr
 * @fd_count: Counter for the number of FDs for this process
 */
struct kgsl_process_private {
	unsigned long priv;
	pid_t pid;
	char comm[TASK_COMM_LEN];
	spinlock_t mem_lock;

	/* General refcount for process private struct obj */
	struct kref refcount;
	/* Mutex to synchronize access to each process_private struct obj */
	struct mutex process_private_mutex;

	struct rb_root mem_rb;
	struct idr mem_idr;
	struct kgsl_pagetable *pagetable;
	struct list_head list;
	struct kobject kobj;
	struct dentry *debug_root;

	struct {
		unsigned int cur;
		unsigned int max;
	} stats[KGSL_MEM_ENTRY_MAX];

	struct idr syncsource_idr;
	spinlock_t syncsource_lock;
	int fd_count;
};

/**
+13 −9
Original line number Diff line number Diff line
@@ -476,8 +476,12 @@ long kgsl_ioctl_syncsource_create(struct kgsl_device_private *dev_priv,
		goto out;
	}

	mutex_lock(&private->process_private_mutex);
	id = idr_alloc(&private->syncsource_idr, syncsource, 1, 0, GFP_KERNEL);
	idr_preload(GFP_KERNEL);
	spin_lock(&private->syncsource_lock);
	id = idr_alloc(&private->syncsource_idr, syncsource, 1, 0, GFP_NOWAIT);
	spin_unlock(&private->syncsource_lock);
	idr_preload_end();

	if (id > 0) {
		kref_init(&syncsource->refcount);
		syncsource->id = id;
@@ -488,7 +492,7 @@ long kgsl_ioctl_syncsource_create(struct kgsl_device_private *dev_priv,
	} else {
		ret = id;
	}
	mutex_unlock(&private->process_private_mutex);

out:
	if (ret) {
		if (syncsource && syncsource->oneshot)
@@ -505,13 +509,13 @@ kgsl_syncsource_get(struct kgsl_process_private *private, int id)
	int result = 0;
	struct kgsl_syncsource *syncsource = NULL;

	mutex_lock(&private->process_private_mutex);
	spin_lock(&private->syncsource_lock);

	syncsource = idr_find(&private->syncsource_idr, id);
	if (syncsource)
		result = kref_get_unless_zero(&syncsource->refcount);

	mutex_unlock(&private->process_private_mutex);
	spin_unlock(&private->syncsource_lock);

	return result ? syncsource : NULL;
}
@@ -524,13 +528,13 @@ static void kgsl_syncsource_destroy(struct kref *kref)

	struct kgsl_process_private *private = syncsource->private;

	mutex_lock(&private->process_private_mutex);
	spin_lock(&private->syncsource_lock);
	if (syncsource->id != 0) {
		idr_remove(&private->syncsource_idr, syncsource->id);
		syncsource->id = 0;
	}
	oneshot_timeline_destroy(syncsource->oneshot);
	mutex_unlock(&private->process_private_mutex);
	spin_unlock(&private->syncsource_lock);

	kfree(syncsource);
}
@@ -556,10 +560,10 @@ long kgsl_ioctl_syncsource_destroy(struct kgsl_device_private *dev_priv,

	private = syncsource->private;

	mutex_lock(&private->process_private_mutex);
	spin_lock(&private->syncsource_lock);
	idr_remove(&private->syncsource_idr, param->id);
	syncsource->id = 0;
	mutex_unlock(&private->process_private_mutex);
	spin_unlock(&private->syncsource_lock);

	/* put reference from syncsource creation */
	kgsl_syncsource_put(syncsource);