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

Commit 9b781b20 authored by Jordan Crouse's avatar Jordan Crouse
Browse files

msm: kgsl: Allow kgsl_mem_entry_put() to be deferred



If kgsl_mem_entry_put() is called in an atomic context it could
cause issues if the put leads to a kgsl_mem_entry_destroy() and
the task needs to take a mutex.

Allow the put to be deferred so the destroy happens in a non
atomic context.

Change-Id: Ic0dedbad0bd9173f4b8df1af44f9ba4f395e1e37
Signed-off-by: default avatarJordan Crouse <jcrouse@codeaurora.org>
parent 7d8ab1b2
Loading
Loading
Loading
Loading
+1 −1
Original line number Diff line number Diff line
@@ -652,7 +652,7 @@ void adreno_cp_callback(struct adreno_device *adreno_dev, int bit)
{
	struct kgsl_device *device = &adreno_dev->dev;

	queue_work(device->work_queue, &device->event_work);
	kgsl_schedule_work(&device->event_work);
	adreno_dispatcher_schedule(device);
}

+1 −1
Original line number Diff line number Diff line
@@ -2767,7 +2767,7 @@ static void a5xx_gpmu_int_callback(struct adreno_device *adreno_dev, int bit)
			/* Stop GPMU */
			kgsl_regwrite(device, A5XX_GPMU_CM3_SYSRESET, 1);

			queue_work(device->work_queue, &adreno_dev->gpmu_work);
			kgsl_schedule_work(&adreno_dev->gpmu_work);

			KGSL_DRV_CRIT_RATELIMIT(device,
						"GPMU: Watchdog bite\n");
+3 −3
Original line number Diff line number Diff line
@@ -2135,7 +2135,7 @@ static void adreno_dispatcher_work(struct work_struct *work)
	 * stragglers
	 */
	if (dispatcher->inflight == 0 && count)
		queue_work(device->work_queue, &device->event_work);
		kgsl_schedule_work(&device->event_work);

	/* Try to dispatch new commands */
	_adreno_dispatcher_issuecmds(adreno_dev);
@@ -2188,7 +2188,7 @@ void adreno_dispatcher_schedule(struct kgsl_device *device)
	struct adreno_device *adreno_dev = ADRENO_DEVICE(device);
	struct adreno_dispatcher *dispatcher = &adreno_dev->dispatcher;

	queue_work(device->work_queue, &dispatcher->work);
	kgsl_schedule_work(&dispatcher->work);
}

/**
@@ -2577,7 +2577,7 @@ void adreno_preempt_process_dispatch_queue(struct adreno_device *adreno_dev,
		cmdbatch->expires = jiffies +
			msecs_to_jiffies(adreno_cmdbatch_timeout);
	}
	queue_work(device->work_queue, &device->event_work);
	kgsl_schedule_work(&device->event_work);
}

/**
+35 −24
Original line number Diff line number Diff line
@@ -228,13 +228,24 @@ int kgsl_readtimestamp(struct kgsl_device *device, void *priv,
}
EXPORT_SYMBOL(kgsl_readtimestamp);

/* Scheduled by kgsl_mem_entry_put_deferred() */
static void _deferred_put(struct work_struct *work)
{
	struct kgsl_mem_entry *entry =
		container_of(work, struct kgsl_mem_entry, work);

	kgsl_mem_entry_put(entry);
}

static inline struct kgsl_mem_entry *
kgsl_mem_entry_create(void)
{
	struct kgsl_mem_entry *entry = kzalloc(sizeof(*entry), GFP_KERNEL);

	if (entry)
	if (entry != NULL) {
		kref_init(&entry->refcount);
		INIT_WORK(&entry->work, _deferred_put);
	}

	return entry;
}
@@ -1256,6 +1267,8 @@ kgsl_sharedmem_find_id(struct kgsl_process_private *process, unsigned int id)
	int result = 0;
	struct kgsl_mem_entry *entry;

	drain_workqueue(kgsl_driver.mem_workqueue);

	spin_lock(&process->mem_lock);
	entry = idr_find(&process->mem_idr, id);
	if (entry)
@@ -1833,8 +1846,7 @@ static long gpuobj_free_on_timestamp(struct kgsl_device_private *dev_priv,

static void gpuobj_free_fence_func(void *priv)
{
	struct kgsl_mem_entry *entry = priv;
	kgsl_mem_entry_put(entry);
	kgsl_mem_entry_put_deferred((struct kgsl_mem_entry *) priv);
}

static long gpuobj_free_on_fence(struct kgsl_device_private *dev_priv,
@@ -1844,15 +1856,22 @@ static long gpuobj_free_on_fence(struct kgsl_device_private *dev_priv,
	struct kgsl_gpu_event_fence event;
	long ret;

	if (!kgsl_mem_entry_set_pend(entry))
		return -EBUSY;

	memset(&event, 0, sizeof(event));

	ret = _copy_from_user(&event, to_user_ptr(param->priv),
		sizeof(event), param->len);
	if (ret)
	if (ret) {
		kgsl_mem_entry_unset_pend(entry);
		return ret;
	}

	if (event.fd < 0)
	if (event.fd < 0) {
		kgsl_mem_entry_unset_pend(entry);
		return -EINVAL;
	}

	handle = kgsl_sync_fence_async_wait(event.fd,
		gpuobj_free_fence_func, entry);
@@ -1861,7 +1880,12 @@ static long gpuobj_free_on_fence(struct kgsl_device_private *dev_priv,
	if (handle == NULL)
		return gpumem_free_entry(entry);

	return IS_ERR(handle) ? PTR_ERR(handle) : 0;
	if (IS_ERR(handle)) {
		kgsl_mem_entry_unset_pend(entry);
		return PTR_ERR(handle);
	}

	return 0;
}

long kgsl_ioctl_gpuobj_free(struct kgsl_device_private *dev_priv,
@@ -3882,20 +3906,10 @@ int kgsl_device_platform_probe(struct kgsl_device *device)

	setup_timer(&device->idle_timer, kgsl_timer, (unsigned long) device);

	/* Create our primary workqueue for events and power */
	device->work_queue = create_singlethread_workqueue(device->name);
	if (device->work_queue == NULL) {
		status = -ENODEV;
		KGSL_DRV_ERR(device,
			"create_singelthreaded_workqueue(%s) failed\n",
			device->name);
		goto error_pwrctrl_close;
	}

	status = kgsl_mmu_init(device);
	if (status != 0) {
		KGSL_DRV_ERR(device, "kgsl_mmu_init failed %d\n", status);
		goto error_dest_work_q;
		goto error_pwrctrl_close;
	}

	/* Check to see if our device can perform DMA correctly */
@@ -3946,9 +3960,6 @@ int kgsl_device_platform_probe(struct kgsl_device *device)

error_close_mmu:
	kgsl_mmu_close(device);
error_dest_work_q:
	destroy_workqueue(device->work_queue);
	device->work_queue = NULL;
error_pwrctrl_close:
	kgsl_pwrctrl_close(device);
error:
@@ -3973,10 +3984,6 @@ void kgsl_device_platform_remove(struct kgsl_device *device)

	kgsl_mmu_close(device);

	if (device->work_queue) {
		destroy_workqueue(device->work_queue);
		device->work_queue = NULL;
	}
	kgsl_pwrctrl_close(device);

	_unregister_device(device);
@@ -4073,6 +4080,10 @@ static int __init kgsl_core_init(void)

	INIT_LIST_HEAD(&kgsl_driver.pagetable_list);

	kgsl_driver.workqueue = create_singlethread_workqueue("kgsl-workqueue");
	kgsl_driver.mem_workqueue =
		create_singlethread_workqueue("kgsl-mementry");

	kgsl_mmu_set_mmutype(ksgl_mmu_type);

	kgsl_events_init();
+47 −10
Original line number Diff line number Diff line
@@ -58,29 +58,38 @@ static inline void KGSL_STATS_ADD(uint64_t size, atomic_long_t *stat,
struct kgsl_device;
struct kgsl_context;

/**
 * struct kgsl_driver - main container for global KGSL things
 * @cdev: Character device struct
 * @major: Major ID for the KGSL device
 * @class: Pointer to the class struct for the core KGSL sysfs entries
 * @virtdev: Virtual device for managing the core
 * @ptkobj: kobject for storing the pagetable statistics
 * @prockobj: kobject for storing the process statistics
 * @devp: Array of pointers to the individual KGSL device structs
 * @process_list: List of open processes
 * @pagetable_list: LIst of open pagetables
 * @ptlock: Lock for accessing the pagetable list
 * @process_mutex: Mutex for accessing the process list
 * @devlock: Mutex protecting the device list
 * @stats: Struct containing atomic memory statistics
 * @full_cache_threshold: the threshold that triggers a full cache flush
 * @workqueue: Pointer to a single threaded workqueue
 * @mem_workqueue: Pointer to a workqueue for deferring memory entries
 */
struct kgsl_driver {
	struct cdev cdev;
	dev_t major;
	struct class *class;
	/* Virtual device for managing the core */
	struct device virtdev;
	/* Kobjects for storing pagetable and process statistics */
	struct kobject *ptkobj;
	struct kobject *prockobj;
	struct kgsl_device *devp[KGSL_DEVICE_MAX];

	/* Global lilst of open processes */
	struct list_head process_list;
	/* Global list of pagetables */
	struct list_head pagetable_list;
	/* Spinlock for accessing the pagetable list */
	spinlock_t ptlock;
	/* Mutex for accessing the process list */
	struct mutex process_mutex;

	/* Mutex for protecting the device list */
	struct mutex devlock;

	struct {
		atomic_long_t vmalloc;
		atomic_long_t vmalloc_max;
@@ -94,6 +103,8 @@ struct kgsl_driver {
		atomic_long_t mapped_max;
	} stats;
	unsigned int full_cache_threshold;
	struct workqueue_struct *workqueue;
	struct workqueue_struct *mem_workqueue;
};

extern struct kgsl_driver kgsl_driver;
@@ -190,6 +201,7 @@ struct kgsl_memdesc {
 *  are still references to it.
 * @dev_priv: back pointer to the device file that created this entry.
 * @metadata: String containing user specified metadata for the entry
 * @work: Work struct used to schedule a kgsl_mem_entry_put in atomic contexts
 */
struct kgsl_mem_entry {
	struct kref refcount;
@@ -200,6 +212,7 @@ struct kgsl_mem_entry {
	struct kgsl_process_private *priv;
	int pending_free;
	char metadata[KGSL_GPUOBJ_ALLOC_METADATA_MAX + 1];
	struct work_struct work;
};

struct kgsl_device_private;
@@ -410,6 +423,15 @@ static inline int timestamp_cmp(unsigned int a, unsigned int b)
	return ((a > b) && (a - b <= KGSL_TIMESTAMP_WINDOW)) ? 1 : -1;
}

/**
 * kgsl_schedule_work() - Schedule a work item on the KGSL workqueue
 * @work: work item to schedule
 */
static inline void kgsl_schedule_work(struct work_struct *work)
{
	queue_work(kgsl_driver.workqueue, work);
}

static inline int
kgsl_mem_entry_get(struct kgsl_mem_entry *entry)
{
@@ -422,6 +444,21 @@ kgsl_mem_entry_put(struct kgsl_mem_entry *entry)
	kref_put(&entry->refcount, kgsl_mem_entry_destroy);
}

/**
 * kgsl_mem_entry_put_deferred() - Schedule a task to put the memory entry
 * @entry: Mem entry to put
 *
 * This function is for atomic contexts where a normal kgsl_mem_entry_put()
 * would result in the memory entry getting destroyed and possibly taking
 * mutexes along the way.  Schedule the work to happen outside of the atomic
 * context.
 */
static inline void kgsl_mem_entry_put_deferred(struct kgsl_mem_entry *entry)
{
	if (entry != NULL)
		queue_work(kgsl_driver.mem_workqueue, &entry->work);
}

/*
 * kgsl_addr_range_overlap() - Checks if 2 ranges overlap
 * @gpuaddr1: Start of first address range
Loading