summaryrefslogtreecommitdiff
path: root/mali-midgard-16.0/mali_kbase_event.c
blob: 188148645f374b8cb3a2772837e9eac3448f18fa (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
/*
 *
 * (C) COPYRIGHT 2010-2016 ARM Limited. All rights reserved.
 *
 * This program is free software and is provided to you under the terms of the
 * GNU General Public License version 2 as published by the Free Software
 * Foundation, and any use by you of this program is subject to the terms
 * of such GNU licence.
 *
 * A copy of the licence is included with the program, and can also be obtained
 * from Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
 * Boston, MA  02110-1301, USA.
 *
 */





#include <mali_kbase.h>
#include <mali_kbase_debug.h>
#include <mali_kbase_tlstream.h>

static struct base_jd_udata kbase_event_process(struct kbase_context *kctx, struct kbase_jd_atom *katom)
{
	struct base_jd_udata data;

	lockdep_assert_held(&kctx->jctx.lock);

	KBASE_DEBUG_ASSERT(kctx != NULL);
	KBASE_DEBUG_ASSERT(katom != NULL);
	KBASE_DEBUG_ASSERT(katom->status == KBASE_JD_ATOM_STATE_COMPLETED);

	data = katom->udata;

	KBASE_TIMELINE_ATOMS_IN_FLIGHT(kctx, atomic_sub_return(1, &kctx->timeline.jd_atoms_in_flight));

	KBASE_TLSTREAM_TL_NRET_ATOM_CTX(katom, kctx);
	KBASE_TLSTREAM_TL_DEL_ATOM(katom);

	katom->status = KBASE_JD_ATOM_STATE_UNUSED;

	wake_up(&katom->completed);

	return data;
}

int kbase_event_pending(struct kbase_context *ctx)
{
	KBASE_DEBUG_ASSERT(ctx);

	return (atomic_read(&ctx->event_count) != 0) ||
			(atomic_read(&ctx->event_closed) != 0);
}

KBASE_EXPORT_TEST_API(kbase_event_pending);

int kbase_event_dequeue(struct kbase_context *ctx, struct base_jd_event_v2 *uevent)
{
	struct kbase_jd_atom *atom;

	KBASE_DEBUG_ASSERT(ctx);

	mutex_lock(&ctx->event_mutex);

	if (list_empty(&ctx->event_list)) {
		if (!atomic_read(&ctx->event_closed)) {
			mutex_unlock(&ctx->event_mutex);
			return -1;
		}

		/* generate the BASE_JD_EVENT_DRV_TERMINATED message on the fly */
		mutex_unlock(&ctx->event_mutex);
		uevent->event_code = BASE_JD_EVENT_DRV_TERMINATED;
		memset(&uevent->udata, 0, sizeof(uevent->udata));
		dev_dbg(ctx->kbdev->dev,
				"event system closed, returning BASE_JD_EVENT_DRV_TERMINATED(0x%X)\n",
				BASE_JD_EVENT_DRV_TERMINATED);
		return 0;
	}

	/* normal event processing */
	atomic_dec(&ctx->event_count);
	atom = list_entry(ctx->event_list.next, struct kbase_jd_atom, dep_item[0]);
	list_del(ctx->event_list.next);

	mutex_unlock(&ctx->event_mutex);

	dev_dbg(ctx->kbdev->dev, "event dequeuing %p\n", (void *)atom);
	uevent->event_code = atom->event_code;
	uevent->atom_number = (atom - ctx->jctx.atoms);

	if (atom->core_req & BASE_JD_REQ_EXTERNAL_RESOURCES)
		kbase_jd_free_external_resources(atom);

	mutex_lock(&ctx->jctx.lock);
	uevent->udata = kbase_event_process(ctx, atom);
	mutex_unlock(&ctx->jctx.lock);

	return 0;
}

KBASE_EXPORT_TEST_API(kbase_event_dequeue);

/**
 * kbase_event_process_noreport_worker - Worker for processing atoms that do not
 *                                       return an event but do have external
 *                                       resources
 * @data:  Work structure
 */
static void kbase_event_process_noreport_worker(struct work_struct *data)
{
	struct kbase_jd_atom *katom = container_of(data, struct kbase_jd_atom,
			work);
	struct kbase_context *kctx = katom->kctx;

	if (katom->core_req & BASE_JD_REQ_EXTERNAL_RESOURCES)
		kbase_jd_free_external_resources(katom);

	mutex_lock(&kctx->jctx.lock);
	kbase_event_process(kctx, katom);
	mutex_unlock(&kctx->jctx.lock);
}

/**
 * kbase_event_process_noreport - Process atoms that do not return an event
 * @kctx:  Context pointer
 * @katom: Atom to be processed
 *
 * Atoms that do not have external resources will be processed immediately.
 * Atoms that do have external resources will be processed on a workqueue, in
 * order to avoid locking issues.
 */
static void kbase_event_process_noreport(struct kbase_context *kctx,
		struct kbase_jd_atom *katom)
{
	if (katom->core_req & BASE_JD_REQ_EXTERNAL_RESOURCES) {
		INIT_WORK(&katom->work, kbase_event_process_noreport_worker);
		queue_work(kctx->event_workq, &katom->work);
	} else {
		kbase_event_process(kctx, katom);
	}
}

/**
 * kbase_event_coalesce - Move pending events to the main event list
 * @kctx:  Context pointer
 *
 * kctx->event_list and kctx->event_coalesce_count must be protected
 * by a lock unless this is the last thread using them
 * (and we're about to terminate the lock).
 *
 * Return: The number of pending events moved to the main event list
 */
static int kbase_event_coalesce(struct kbase_context *kctx)
{
	const int event_count = kctx->event_coalesce_count;

	/* Join the list of pending events onto the tail of the main list
	   and reset it */
	list_splice_tail_init(&kctx->event_coalesce_list, &kctx->event_list);
	kctx->event_coalesce_count = 0;

	/* Return the number of events moved */
	return event_count;
}

void kbase_event_post(struct kbase_context *ctx, struct kbase_jd_atom *atom)
{
	if (atom->core_req & BASE_JD_REQ_EVENT_ONLY_ON_FAILURE) {
		if (atom->event_code == BASE_JD_EVENT_DONE) {
			/* Don't report the event */
			kbase_event_process_noreport(ctx, atom);
			return;
		}
	}

	if (atom->core_req & BASEP_JD_REQ_EVENT_NEVER) {
		/* Don't report the event */
		kbase_event_process_noreport(ctx, atom);
		return;
	}
	KBASE_TLSTREAM_TL_ATTRIB_ATOM_STATE(atom, TL_ATOM_STATE_POSTED);
	if (atom->core_req & BASE_JD_REQ_EVENT_COALESCE) {
		/* Don't report the event until other event(s) have completed */
		mutex_lock(&ctx->event_mutex);
		list_add_tail(&atom->dep_item[0], &ctx->event_coalesce_list);
		++ctx->event_coalesce_count;
		mutex_unlock(&ctx->event_mutex);
	} else {
		/* Report the event and any pending events now */
		int event_count = 1;

		mutex_lock(&ctx->event_mutex);
		event_count += kbase_event_coalesce(ctx);
		list_add_tail(&atom->dep_item[0], &ctx->event_list);
		atomic_add(event_count, &ctx->event_count);
		mutex_unlock(&ctx->event_mutex);

		kbase_event_wakeup(ctx);
	}
}
KBASE_EXPORT_TEST_API(kbase_event_post);

void kbase_event_close(struct kbase_context *kctx)
{
	mutex_lock(&kctx->event_mutex);
	atomic_set(&kctx->event_closed, true);
	mutex_unlock(&kctx->event_mutex);
	kbase_event_wakeup(kctx);
}

int kbase_event_init(struct kbase_context *kctx)
{
	KBASE_DEBUG_ASSERT(kctx);

	INIT_LIST_HEAD(&kctx->event_list);
	INIT_LIST_HEAD(&kctx->event_coalesce_list);
	mutex_init(&kctx->event_mutex);
	atomic_set(&kctx->event_count, 0);
	kctx->event_coalesce_count = 0;
	atomic_set(&kctx->event_closed, false);
	kctx->event_workq = alloc_workqueue("kbase_event", WQ_MEM_RECLAIM, 1);

	if (NULL == kctx->event_workq)
		return -EINVAL;

	return 0;
}

KBASE_EXPORT_TEST_API(kbase_event_init);

void kbase_event_cleanup(struct kbase_context *kctx)
{
	int event_count;

	KBASE_DEBUG_ASSERT(kctx);
	KBASE_DEBUG_ASSERT(kctx->event_workq);

	flush_workqueue(kctx->event_workq);
	destroy_workqueue(kctx->event_workq);

	/* We use kbase_event_dequeue to remove the remaining events as that
	 * deals with all the cleanup needed for the atoms.
	 *
	 * Note: use of kctx->event_list without a lock is safe because this must be the last
	 * thread using it (because we're about to terminate the lock)
	 */
	event_count = kbase_event_coalesce(kctx);
	atomic_add(event_count, &kctx->event_count);

	while (!list_empty(&kctx->event_list)) {
		struct base_jd_event_v2 event;

		kbase_event_dequeue(kctx, &event);
	}
}

KBASE_EXPORT_TEST_API(kbase_event_cleanup);