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

Commit 3d920d87 authored by Jordan Crouse's avatar Jordan Crouse
Browse files

msm: kgsl: Remove a race condition in the event processing code



There could be a race condition if a thread is adding an event while
another thread is processing events. Say that thread A is adding an
event and thread B is processing events. A could read the timestamp
and see that it isn't retired yet and wait for the spinlock to add
it to the list.  Meanwhile the timestamp gets retired and the retire
thread takes the spinlock first and proceses the list.  End result
is the event for the newly expired timestamp gets added to the list
after the timestamp retires. If there are no subsequent events then
the lame duck event will just sit there.

Avoid this race condition by taking the spinlock before considering
the timestamp.  This serializes the two threads ensuring that a new
event cannot be added after the same timestamp is retired.

The race condition could still fail with the locks in the right place
if different cores got different timestamp values so add the correct
barriers to the memory read/write functions to ensure that all cores
read the same value at the same time.

Change-Id: Ic0dedbad02e7aeeaab48373ab28f4dc9cce50db2
Signed-off-by: default avatarJordan Crouse <jcrouse@codeaurora.org>
parent 22b9645b
Loading
Loading
Loading
Loading
+0 −2
Original line number Diff line number Diff line
@@ -3055,8 +3055,6 @@ static int adreno_readtimestamp(struct kgsl_device *device,
		break;
	}

	rmb();

	return status;
}

+6 −3
Original line number Diff line number Diff line
@@ -70,9 +70,10 @@ static void retire_events(struct kgsl_device *device,

	_kgsl_context_get(context);

	spin_lock(&group->lock);

	kgsl_readtimestamp(device, context, KGSL_TIMESTAMP_RETIRED, &timestamp);

	spin_lock(&group->lock);
	/*
	 * If no timestamps have been retired since the last time we were here
	 * then we can avoid going through this loop
@@ -207,6 +208,8 @@ int kgsl_add_event(struct kgsl_device *device, struct kgsl_event_group *group,

	trace_kgsl_register_event(KGSL_CONTEXT_ID(context), timestamp, func);

	spin_lock(&group->lock);

	/*
	 * Check to see if the requested timestamp has already retired.  If so,
	 * schedule the callback right away
@@ -216,13 +219,13 @@ int kgsl_add_event(struct kgsl_device *device, struct kgsl_event_group *group,
	if (timestamp_cmp(retired, timestamp) >= 0) {
		event->result = KGSL_EVENT_RETIRED;
		queue_work(device->events_wq, &event->work);

		spin_unlock(&group->lock);
		return 0;
	}

	/* Add the event to the group list */
	spin_lock(&group->lock);
	list_add_tail(&event->node, &group->events);

	spin_unlock(&group->lock);

	return 0;
+5 −0
Original line number Diff line number Diff line
@@ -848,6 +848,8 @@ kgsl_sharedmem_readl(const struct kgsl_memdesc *memdesc,
	WARN_ON(offsetbytes + sizeof(uint32_t) > memdesc->size);
	if (offsetbytes + sizeof(uint32_t) > memdesc->size)
		return -ERANGE;

	rmb();
	src = (uint32_t *)(memdesc->hostptr + offsetbytes);
	*dst = *src;
	return 0;
@@ -874,6 +876,9 @@ kgsl_sharedmem_writel(struct kgsl_device *device,
		src, sizeof(uint32_t));
	dst = (uint32_t *)(memdesc->hostptr + offsetbytes);
	*dst = src;

	wmb();

	return 0;
}
EXPORT_SYMBOL(kgsl_sharedmem_writel);