xref: /linux/drivers/gpu/drm/msm/msm_perfcntr.c (revision 0eaed89c18aeedf0898baf2dbf5ff027c6795152)
102195633SRob Clark // SPDX-License-Identifier: GPL-2.0
202195633SRob Clark /*
302195633SRob Clark  * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries.
402195633SRob Clark  */
502195633SRob Clark 
6a7b378c9SRob Clark #include "drm/drm_file.h"
7a7b378c9SRob Clark #include "drm/msm_drm.h"
8a7b378c9SRob Clark 
9a7b378c9SRob Clark #include "linux/anon_inodes.h"
10a7b378c9SRob Clark #include "linux/gfp_types.h"
11a7b378c9SRob Clark #include "linux/poll.h"
12a7b378c9SRob Clark #include "linux/slab.h"
13a7b378c9SRob Clark 
1402195633SRob Clark #include "msm_drv.h"
1502195633SRob Clark #include "msm_gpu.h"
1602195633SRob Clark #include "msm_perfcntr.h"
1702195633SRob Clark 
18a7b378c9SRob Clark #include "adreno/adreno_gpu.h"
19a7b378c9SRob Clark 
20a7b378c9SRob Clark /* space used: */
21a7b378c9SRob Clark #define fifo_count(stream) \
22a7b378c9SRob Clark 	(CIRC_CNT((stream)->fifo.head, (stream)->fifo.tail, (stream)->fifo_size))
23a7b378c9SRob Clark #define fifo_count_to_end(stream) \
24a7b378c9SRob Clark 	(CIRC_CNT_TO_END(smp_load_acquire(&(stream)->fifo.head), (stream)->fifo.tail, (stream)->fifo_size))
25a7b378c9SRob Clark /* space available: */
26a7b378c9SRob Clark #define fifo_space(stream) \
27a7b378c9SRob Clark 	(CIRC_SPACE((stream)->fifo.head, (stream)->fifo.tail, (stream)->fifo_size))
28a7b378c9SRob Clark 
2902195633SRob Clark static int
3002195633SRob Clark msm_perfcntr_resume_locked(struct msm_perfcntr_stream *stream)
3102195633SRob Clark {
32a7b378c9SRob Clark 	if (!stream)
33a7b378c9SRob Clark 		return 0;
34a7b378c9SRob Clark 
35a7b378c9SRob Clark 	/* Reprogram SEL regs on highest priority rb: */
36a7b378c9SRob Clark 	struct msm_ringbuffer *ring = stream->gpu->rb[0];
37a7b378c9SRob Clark 
38a7b378c9SRob Clark 	queue_work(ring->sched.submit_wq, &stream->sel_work);
39a7b378c9SRob Clark 
40a7b378c9SRob Clark 	hrtimer_start(&stream->sample_timer,
41a7b378c9SRob Clark 		      ns_to_ktime(stream->sample_period_ns),
42a7b378c9SRob Clark 		      HRTIMER_MODE_REL_PINNED);
43a7b378c9SRob Clark 
4402195633SRob Clark 	return 0;
4502195633SRob Clark }
4602195633SRob Clark 
4702195633SRob Clark int
4802195633SRob Clark msm_perfcntr_resume(struct msm_gpu *gpu)
4902195633SRob Clark {
5002195633SRob Clark 	if (!gpu->perfcntrs)
5102195633SRob Clark 		return 0;
5202195633SRob Clark 	guard(mutex)(&gpu->perfcntr_lock);
5302195633SRob Clark 	return msm_perfcntr_resume_locked(gpu->perfcntrs->stream);
5402195633SRob Clark }
5502195633SRob Clark 
5602195633SRob Clark static void
5702195633SRob Clark msm_perfcntr_suspend_locked(struct msm_perfcntr_stream *stream)
5802195633SRob Clark {
59a7b378c9SRob Clark 	if (!stream)
60a7b378c9SRob Clark 		return;
61a7b378c9SRob Clark 
62a7b378c9SRob Clark 	hrtimer_cancel(&stream->sample_timer);
63a7b378c9SRob Clark 	kthread_cancel_work_sync(&stream->sample_work);
64a7b378c9SRob Clark 
65a7b378c9SRob Clark 	/*
66a7b378c9SRob Clark 	 * We can't use cancel_work_sync() here, since sel_work acquires
67a7b378c9SRob Clark 	 * gpu->lock which (a) in suspend path can already be held, or
68a7b378c9SRob Clark 	 * (b) in release path would invert the order of gpu->lock and
69a7b378c9SRob Clark 	 * gpu->perfcntr_lock.  Either would cause deadlock.
70a7b378c9SRob Clark 	 */
71a7b378c9SRob Clark 	cancel_work(&stream->sel_work);
72a7b378c9SRob Clark 
73a7b378c9SRob Clark 	stream->sel_fence = ++stream->gpu->perfcntrs->sel_seqno;
74a7b378c9SRob Clark 	stream->seqno = 0;
7502195633SRob Clark }
7602195633SRob Clark 
7702195633SRob Clark void
7802195633SRob Clark msm_perfcntr_suspend(struct msm_gpu *gpu)
7902195633SRob Clark {
8002195633SRob Clark 	if (!gpu->perfcntrs)
8102195633SRob Clark 		return;
8202195633SRob Clark 	guard(mutex)(&gpu->perfcntr_lock);
8302195633SRob Clark 	msm_perfcntr_suspend_locked(gpu->perfcntrs->stream);
8402195633SRob Clark }
8502195633SRob Clark 
86a7b378c9SRob Clark static int
87a7b378c9SRob Clark msm_perfcntrs_stream_release(struct inode *inode, struct file *file)
88a7b378c9SRob Clark {
89a7b378c9SRob Clark 	struct msm_perfcntr_stream *stream = file->private_data;
90a7b378c9SRob Clark 	struct msm_gpu *gpu = stream->gpu;
91a7b378c9SRob Clark 
92a7b378c9SRob Clark 	scoped_guard (mutex, &gpu->perfcntr_lock) {
93a7b378c9SRob Clark 		struct msm_perfcntr_state *perfcntrs = gpu->perfcntrs;
94a7b378c9SRob Clark 
95a7b378c9SRob Clark 		msm_perfcntr_suspend_locked(stream);
96a7b378c9SRob Clark 		perfcntrs->stream = NULL;
97a7b378c9SRob Clark 
98a7b378c9SRob Clark 		/* release previously allocated counters: */
99a7b378c9SRob Clark 		for (unsigned i = 0; i < gpu->num_perfcntr_groups; i++)
100a7b378c9SRob Clark 			perfcntrs->groups[i]->allocated_counters = 0;
101a7b378c9SRob Clark 	}
102a7b378c9SRob Clark 
103a7b378c9SRob Clark 	/*
104a7b378c9SRob Clark 	 * In the suspend path we use async cancel_work(), to avoid blocking
105a7b378c9SRob Clark 	 * on sel_work, which acquires gpu->lock (which could deadlock since
106a7b378c9SRob Clark 	 * other paths acquire gpu->lock before perfcntr_lock) or already
107a7b378c9SRob Clark 	 * hold gpu->lock.
108a7b378c9SRob Clark 	 *
109a7b378c9SRob Clark 	 * But since we are freeing the stream, after dropping perfcntr_lock
110a7b378c9SRob Clark 	 * we need to block until sel_work is done:
111a7b378c9SRob Clark 	 */
112a7b378c9SRob Clark 	cancel_work_sync(&stream->sel_work);
113a7b378c9SRob Clark 
114a7b378c9SRob Clark 	kfree(stream->group_idx);
115a7b378c9SRob Clark 	kfree(stream->fifo.buf);
116a7b378c9SRob Clark 	kfree(stream);
117a7b378c9SRob Clark 
118a7b378c9SRob Clark 	return 0;
119a7b378c9SRob Clark }
120a7b378c9SRob Clark 
121a7b378c9SRob Clark static __poll_t
122a7b378c9SRob Clark msm_perfcntrs_stream_poll(struct file *file, poll_table *wait)
123a7b378c9SRob Clark {
124a7b378c9SRob Clark 	struct msm_perfcntr_stream *stream = file->private_data;
125a7b378c9SRob Clark 	__poll_t events = 0;
126a7b378c9SRob Clark 
127a7b378c9SRob Clark 	poll_wait(file, &stream->poll_wq, wait);
128a7b378c9SRob Clark 
129a7b378c9SRob Clark 	/* Are there samples to read? */
130a7b378c9SRob Clark 	if (fifo_count(stream) > 0)
131a7b378c9SRob Clark 		events |= EPOLLIN;
132a7b378c9SRob Clark 
133a7b378c9SRob Clark 	return events;
134a7b378c9SRob Clark }
135a7b378c9SRob Clark 
136a7b378c9SRob Clark static ssize_t
137a7b378c9SRob Clark msm_perfcntrs_stream_read(struct file *file, char __user *buf,
138a7b378c9SRob Clark 			  size_t count, loff_t *ppos)
139a7b378c9SRob Clark {
140a7b378c9SRob Clark 	struct msm_perfcntr_stream *stream = file->private_data;
141a7b378c9SRob Clark 	int ret;
142a7b378c9SRob Clark 
143a7b378c9SRob Clark 	if (!(file->f_flags & O_NONBLOCK)) {
144a7b378c9SRob Clark 		ret = wait_event_interruptible(stream->poll_wq,
145a7b378c9SRob Clark 					       fifo_count(stream) > 0);
146a7b378c9SRob Clark 		if (ret)
147a7b378c9SRob Clark 			return ret;
148a7b378c9SRob Clark 	}
149a7b378c9SRob Clark 
150a7b378c9SRob Clark 	guard(mutex)(&stream->read_lock);
151a7b378c9SRob Clark 
152a7b378c9SRob Clark 	struct circ_buf *fifo = &stream->fifo;
153a7b378c9SRob Clark 	const char *fptr = &fifo->buf[fifo->tail];
154a7b378c9SRob Clark 
155a7b378c9SRob Clark 	count = min_t(size_t, count, fifo_count_to_end(stream));
156a7b378c9SRob Clark 	if (!count)
157a7b378c9SRob Clark 		return -EAGAIN;
158a7b378c9SRob Clark 	if (copy_to_user(buf, fptr, count))
159a7b378c9SRob Clark 		return -EFAULT;
160a7b378c9SRob Clark 
161a7b378c9SRob Clark 	smp_store_release(&fifo->tail, (fifo->tail + count) & (stream->fifo_size - 1));
162a7b378c9SRob Clark 	*ppos += count;
163a7b378c9SRob Clark 
164a7b378c9SRob Clark 	return count;
165a7b378c9SRob Clark }
166a7b378c9SRob Clark 
167a7b378c9SRob Clark static const struct file_operations stream_fops = {
168a7b378c9SRob Clark 	.owner		= THIS_MODULE,
169a7b378c9SRob Clark 	.release	= msm_perfcntrs_stream_release,
170a7b378c9SRob Clark 	.poll		= msm_perfcntrs_stream_poll,
171a7b378c9SRob Clark 	.read		= msm_perfcntrs_stream_read,
172a7b378c9SRob Clark };
173a7b378c9SRob Clark 
174a7b378c9SRob Clark static void
175a7b378c9SRob Clark sel_worker(struct work_struct *w)
176a7b378c9SRob Clark {
177a7b378c9SRob Clark 	struct msm_perfcntr_stream *stream =
178a7b378c9SRob Clark 		container_of(w, typeof(*stream), sel_work);
179a7b378c9SRob Clark 	struct msm_gpu *gpu = stream->gpu;
180a7b378c9SRob Clark 	/* Reprogram SEL regs on highest priority rb: */
181a7b378c9SRob Clark 	struct msm_ringbuffer *ring = stream->gpu->rb[0];
182a7b378c9SRob Clark 
183a7b378c9SRob Clark 	/*
184a7b378c9SRob Clark 	 * If in the process of resuming, wait for that.  Otherwise sel_worker
185a7b378c9SRob Clark 	 * which is enqueued in the resume path can be scheduled before the
186a7b378c9SRob Clark 	 * resume completes.
187a7b378c9SRob Clark 	 */
188a7b378c9SRob Clark 	pm_runtime_barrier(&gpu->pdev->dev);
189a7b378c9SRob Clark 
190a7b378c9SRob Clark 	/*
191a7b378c9SRob Clark 	 * sel_work could end up scheduled before suspend, but running
192a7b378c9SRob Clark 	 * after.  See msm_perfcntr_suspend_locked()
193a7b378c9SRob Clark 	 *
194a7b378c9SRob Clark 	 * So if we end up running sel_work after the GPU is already
195a7b378c9SRob Clark 	 * suspended, just bail.  It will be scheduled again after
196a7b378c9SRob Clark 	 * the GPU is resumed.
197a7b378c9SRob Clark 	 */
198a7b378c9SRob Clark 	if (!pm_runtime_get_if_active(&gpu->pdev->dev))
199a7b378c9SRob Clark 		return;
200a7b378c9SRob Clark 
201a7b378c9SRob Clark 	scoped_guard (mutex, &gpu->lock) {
202a7b378c9SRob Clark 		guard(mutex)(&gpu->perfcntr_lock);
203a7b378c9SRob Clark 
204a7b378c9SRob Clark 		if (stream == gpu->perfcntrs->stream) {
205a7b378c9SRob Clark 			msm_gpu_hw_init(gpu);
206a7b378c9SRob Clark 			gpu->funcs->perfcntr_configure(gpu, ring, stream);
207a7b378c9SRob Clark 		}
208a7b378c9SRob Clark 	}
209a7b378c9SRob Clark 
210a7b378c9SRob Clark 	pm_runtime_put_autosuspend(&gpu->pdev->dev);
211a7b378c9SRob Clark }
212a7b378c9SRob Clark 
213a7b378c9SRob Clark static void
214a7b378c9SRob Clark sample_write(struct msm_perfcntr_stream *stream, int *head, const void *buf, size_t sz)
215a7b378c9SRob Clark {
216a7b378c9SRob Clark 	/*
217a7b378c9SRob Clark 	 * FIFO size is power-of-two, and guaranteed to have enough space to
218a7b378c9SRob Clark 	 * fit what we are writing.  So we should not hit the wrap-around
219a7b378c9SRob Clark 	 * point writing things that are power-of-two sized
220a7b378c9SRob Clark 	 */
221a7b378c9SRob Clark 	WARN_ON(CIRC_SPACE_TO_END(*head, stream->fifo.tail, stream->fifo_size) < sz);
222a7b378c9SRob Clark 
223a7b378c9SRob Clark 	memcpy(&stream->fifo.buf[*head], buf, sz);
224a7b378c9SRob Clark 
225a7b378c9SRob Clark 	/* Advance head, wrapping around if necessary: */
226a7b378c9SRob Clark 	*head = (*head + sz) & (stream->fifo_size - 1);
227a7b378c9SRob Clark }
228a7b378c9SRob Clark 
229a7b378c9SRob Clark static void
230a7b378c9SRob Clark sample_write_u32(struct msm_perfcntr_stream *stream, int *head, uint32_t val)
231a7b378c9SRob Clark {
232a7b378c9SRob Clark 	sample_write(stream, head, &val, sizeof(val));
233a7b378c9SRob Clark }
234a7b378c9SRob Clark 
235a7b378c9SRob Clark static void
236a7b378c9SRob Clark sample_write_u64(struct msm_perfcntr_stream *stream, int *head, uint64_t val)
237a7b378c9SRob Clark {
238a7b378c9SRob Clark 	sample_write(stream, head, &val, sizeof(val));
239a7b378c9SRob Clark }
240a7b378c9SRob Clark 
241a7b378c9SRob Clark static void
242a7b378c9SRob Clark sample_worker(struct kthread_work *work)
243a7b378c9SRob Clark {
244a7b378c9SRob Clark 	struct msm_perfcntr_stream *stream =
245a7b378c9SRob Clark 		container_of(work, typeof(*stream), sample_work);
246a7b378c9SRob Clark 	struct msm_gpu *gpu = stream->gpu;
247a7b378c9SRob Clark 	struct msm_rbmemptrs *memptrs = gpu->rb[0]->memptrs;
248a7b378c9SRob Clark 
249a7b378c9SRob Clark 	if (memptrs->perfcntr_fence != stream->sel_fence)
250a7b378c9SRob Clark 		return;
251a7b378c9SRob Clark 
252a7b378c9SRob Clark 	/*
253a7b378c9SRob Clark 	 * Ensure we have enough space to capture a sample period's
254a7b378c9SRob Clark 	 * worth of data:
255a7b378c9SRob Clark 	 */
256a7b378c9SRob Clark 	if (stream->period_size > fifo_space(stream)) {
257a7b378c9SRob Clark 		stream->seqno = 0;
258a7b378c9SRob Clark 		return;
259a7b378c9SRob Clark 	}
260a7b378c9SRob Clark 
261*632ad551SRob Clark 	/* Inhibit IFPC while accessing registers: */
262*632ad551SRob Clark 	if (gpu->funcs->sysprof_setup)
263*632ad551SRob Clark 		gpu->funcs->sysprof_setup(gpu, true);
264*632ad551SRob Clark 
265a7b378c9SRob Clark 	if (gpu->funcs->perfcntr_flush)
266a7b378c9SRob Clark 		gpu->funcs->perfcntr_flush(gpu);
267a7b378c9SRob Clark 
268a7b378c9SRob Clark 	/* Keep local copy of head to avoid updating fifo until the end: */
269a7b378c9SRob Clark 	int head = stream->fifo.head;
270a7b378c9SRob Clark 
271a7b378c9SRob Clark 	/*
272a7b378c9SRob Clark 	 * We expect the GPU to be powered at this point, as the timer
273a7b378c9SRob Clark 	 * and kthread work are canceled/flushed in the suspend path:
274a7b378c9SRob Clark 	 */
275a7b378c9SRob Clark 	sample_write_u64(stream, &head,
276a7b378c9SRob Clark 			 to_adreno_gpu(gpu)->funcs->get_timestamp(gpu));
277a7b378c9SRob Clark 	sample_write_u32(stream, &head, stream->seqno++);
278a7b378c9SRob Clark 	sample_write_u32(stream, &head, 0);
279a7b378c9SRob Clark 
280a7b378c9SRob Clark 	for (unsigned i = 0; i < stream->nr_groups; i++) {
281a7b378c9SRob Clark 		unsigned group_idx = msm_perfcntr_group_idx(stream, i);
282a7b378c9SRob Clark 		unsigned base = msm_perfcntr_counter_base(stream, group_idx);
283a7b378c9SRob Clark 
284a7b378c9SRob Clark 		const struct msm_perfcntr_group *group =
285a7b378c9SRob Clark 			&gpu->perfcntr_groups[group_idx];
286a7b378c9SRob Clark 
287a7b378c9SRob Clark 		struct msm_perfcntr_group_state *group_state =
288a7b378c9SRob Clark 			gpu->perfcntrs->groups[group_idx];
289a7b378c9SRob Clark 
290a7b378c9SRob Clark 		unsigned nr = group_state->allocated_counters;
291a7b378c9SRob Clark 		for (unsigned j = 0; j < nr; j++) {
292a7b378c9SRob Clark 			const struct msm_perfcntr_counter *counter =
293a7b378c9SRob Clark 				&group->counters[j + base];
294a7b378c9SRob Clark 			uint64_t val = gpu_read64(gpu, counter->counter_reg_lo);
295a7b378c9SRob Clark 			sample_write_u64(stream, &head, val);
296a7b378c9SRob Clark 		}
297a7b378c9SRob Clark 	}
298a7b378c9SRob Clark 
299*632ad551SRob Clark 	/* Re-enable IFPC: */
300*632ad551SRob Clark 	if (gpu->funcs->sysprof_setup)
301*632ad551SRob Clark 		gpu->funcs->sysprof_setup(gpu, false);
302*632ad551SRob Clark 
303a7b378c9SRob Clark 	smp_store_release(&stream->fifo.head, head);
304a7b378c9SRob Clark 	wake_up_all(&stream->poll_wq);
305a7b378c9SRob Clark }
306a7b378c9SRob Clark 
307a7b378c9SRob Clark static enum hrtimer_restart
308a7b378c9SRob Clark sample_timer(struct hrtimer *hrtimer)
309a7b378c9SRob Clark {
310a7b378c9SRob Clark 	struct msm_perfcntr_stream *stream =
311a7b378c9SRob Clark 		container_of(hrtimer, typeof(*stream), sample_timer);
312a7b378c9SRob Clark 
313a7b378c9SRob Clark 	kthread_queue_work(stream->gpu->worker, &stream->sample_work);
314a7b378c9SRob Clark 
315a7b378c9SRob Clark 	hrtimer_forward_now(hrtimer, ns_to_ktime(stream->sample_period_ns));
316a7b378c9SRob Clark 
317a7b378c9SRob Clark 	return HRTIMER_RESTART;
318a7b378c9SRob Clark }
319a7b378c9SRob Clark 
320a7b378c9SRob Clark static int
321a7b378c9SRob Clark get_group_idx(struct msm_gpu *gpu, const char *name, size_t len)
322a7b378c9SRob Clark {
323a7b378c9SRob Clark 	for (unsigned i = 0; i < gpu->num_perfcntr_groups; i++) {
324a7b378c9SRob Clark 		const struct msm_perfcntr_group *group =
325a7b378c9SRob Clark 			&gpu->perfcntr_groups[i];
326a7b378c9SRob Clark 		if (!strncmp(group->name, name, len))
327a7b378c9SRob Clark 			return i;
328a7b378c9SRob Clark 	}
329a7b378c9SRob Clark 
330a7b378c9SRob Clark 	return -1;
331a7b378c9SRob Clark }
332a7b378c9SRob Clark 
333a7b378c9SRob Clark static int
334a7b378c9SRob Clark get_available_counters(struct msm_gpu *gpu, int group_idx, uint32_t flags)
335a7b378c9SRob Clark {
336a7b378c9SRob Clark 	struct msm_perfcntr_state *perfcntrs = gpu->perfcntrs;
337a7b378c9SRob Clark 
338a7b378c9SRob Clark 	/*
339a7b378c9SRob Clark 	 * For local counter reservation, anything that is not used by
340a7b378c9SRob Clark 	 * global perfcntr stream is available:
341a7b378c9SRob Clark 	 */
342a7b378c9SRob Clark 	if (!(flags & MSM_PERFCNTR_STREAM)) {
343a7b378c9SRob Clark 		return gpu->perfcntr_groups[group_idx].num_counters -
344a7b378c9SRob Clark 			perfcntrs->groups[group_idx]->allocated_counters;
345a7b378c9SRob Clark 	}
346a7b378c9SRob Clark 
347a7b378c9SRob Clark 	/*
348a7b378c9SRob Clark 	 * For global counter collection, anything that is not reserved by
349a7b378c9SRob Clark 	 * one or more contexts is available:
350a7b378c9SRob Clark 	 */
351a7b378c9SRob Clark 	guard(mutex)(&gpu->dev->filelist_mutex);
352a7b378c9SRob Clark 
353a7b378c9SRob Clark 	unsigned reserved_counters = 0;
354a7b378c9SRob Clark 	struct drm_file *file;
355a7b378c9SRob Clark 
356a7b378c9SRob Clark 	list_for_each_entry (file, &gpu->dev->filelist, lhead) {
357a7b378c9SRob Clark 		struct msm_context *ctx = file->driver_priv;
358a7b378c9SRob Clark 
359a7b378c9SRob Clark 		if (!ctx || !ctx->perfctx)
360a7b378c9SRob Clark 			continue;
361a7b378c9SRob Clark 
362a7b378c9SRob Clark 		unsigned n = ctx->perfctx->reserved_counters[group_idx];
363a7b378c9SRob Clark 		reserved_counters = max(reserved_counters, n);
364a7b378c9SRob Clark 	}
365a7b378c9SRob Clark 
366a7b378c9SRob Clark 	return gpu->perfcntr_groups[group_idx].num_counters - reserved_counters;
367a7b378c9SRob Clark }
368a7b378c9SRob Clark 
369a7b378c9SRob Clark int
370a7b378c9SRob Clark msm_ioctl_perfcntr_config(struct drm_device *dev, void *data, struct drm_file *file)
371a7b378c9SRob Clark {
372a7b378c9SRob Clark 	struct msm_drm_private *priv = dev->dev_private;
373a7b378c9SRob Clark 	const struct drm_msm_perfcntr_config *args = data;
374a7b378c9SRob Clark 	struct msm_context *ctx = file->driver_priv;
375a7b378c9SRob Clark 	struct msm_gpu *gpu = priv->gpu;
376a7b378c9SRob Clark 	int stream_fd = 0;
377a7b378c9SRob Clark 
378a7b378c9SRob Clark 	if (!gpu || !gpu->num_perfcntr_groups)
379a7b378c9SRob Clark 		return -ENXIO;
380a7b378c9SRob Clark 
381a7b378c9SRob Clark 	struct msm_perfcntr_state *perfcntrs = gpu->perfcntrs;
382a7b378c9SRob Clark 
383a7b378c9SRob Clark 	/*
384a7b378c9SRob Clark 	 * Validate args that don't require locks/power first:
385a7b378c9SRob Clark 	 */
386a7b378c9SRob Clark 
387a7b378c9SRob Clark 	if (args->flags & ~MSM_PERFCNTR_FLAGS)
388a7b378c9SRob Clark 		return UERR(EINVAL, dev, "invalid flags");
389a7b378c9SRob Clark 
390a7b378c9SRob Clark 	if (args->nr_groups && !args->group_stride)
391a7b378c9SRob Clark 		return UERR(EINVAL, dev, "invalid group_stride");
392a7b378c9SRob Clark 
393a7b378c9SRob Clark 	if (args->nr_groups > gpu->num_perfcntr_groups)
394a7b378c9SRob Clark 		return UERR(EINVAL, dev, "too many groups");
395a7b378c9SRob Clark 
396a7b378c9SRob Clark 	if (args->nr_groups && !args->groups)
397a7b378c9SRob Clark 		return UERR(EINVAL, dev, "no groups");
398a7b378c9SRob Clark 
399a7b378c9SRob Clark 	if (args->flags & MSM_PERFCNTR_STREAM) {
400a7b378c9SRob Clark 		if (!perfmon_capable())
401a7b378c9SRob Clark 			return UERR(EPERM, dev, "invalid permissions");
402a7b378c9SRob Clark 		if (!args->nr_groups)
403a7b378c9SRob Clark 			return UERR(EINVAL, dev, "invalid nr_groups");
404a7b378c9SRob Clark 		if (!args->period)
405a7b378c9SRob Clark 			return UERR(EINVAL, dev, "invalid sampling period");
406a7b378c9SRob Clark 		if (args->bufsz_shift > const_ilog2(SZ_128M))
407a7b378c9SRob Clark 			return UERR(EINVAL, dev, "buffer size too big (>128M)");
408a7b378c9SRob Clark 	} else {
409a7b378c9SRob Clark 		if (args->period)
410a7b378c9SRob Clark 			return UERR(EINVAL, dev, "sampling period not allowed");
411a7b378c9SRob Clark 		if (args->bufsz_shift)
412a7b378c9SRob Clark 			return UERR(EINVAL, dev, "sample buf size not allowed");
413a7b378c9SRob Clark 	}
414a7b378c9SRob Clark 
415a7b378c9SRob Clark 	/*
416a7b378c9SRob Clark 	 * To avoid iterating over the groups multiple times, allocate and setup
417a7b378c9SRob Clark 	 * both a ctx and global stream object.  Only one of the two will be
418a7b378c9SRob Clark 	 * kept in the end.
419a7b378c9SRob Clark 	 */
420a7b378c9SRob Clark 
421a7b378c9SRob Clark 	struct msm_perfcntr_context_state *perfctx __free(kfree) = kzalloc(
422a7b378c9SRob Clark 		struct_size(perfctx, reserved_counters, gpu->num_perfcntr_groups),
423a7b378c9SRob Clark 		GFP_KERNEL);
424a7b378c9SRob Clark 	if (!perfctx)
425a7b378c9SRob Clark 		return -ENOMEM;
426a7b378c9SRob Clark 
427a7b378c9SRob Clark 	struct msm_perfcntr_stream *stream __free(kfree) = kzalloc_obj(*stream);
428a7b378c9SRob Clark 	if (!stream)
429a7b378c9SRob Clark 		return -ENOMEM;
430a7b378c9SRob Clark 
431a7b378c9SRob Clark 	uint8_t *nr_counters __free(kfree) = kzalloc_objs(uint8_t, gpu->num_perfcntr_groups);
432a7b378c9SRob Clark 	if (!nr_counters)
433a7b378c9SRob Clark 		return -ENOMEM;
434a7b378c9SRob Clark 
435a7b378c9SRob Clark 	uint32_t *group_idx __free(kfree) = kzalloc_objs(uint32_t, args->nr_groups);
436a7b378c9SRob Clark 	if (!group_idx)
437a7b378c9SRob Clark 		return -ENOMEM;
438a7b378c9SRob Clark 
439a7b378c9SRob Clark 	stream->gpu = gpu;
440a7b378c9SRob Clark 	stream->sample_period_ns = args->period;
441a7b378c9SRob Clark 	stream->nr_groups = args->nr_groups;
442a7b378c9SRob Clark 	stream->fifo_size = 1ull << args->bufsz_shift;
443a7b378c9SRob Clark 
444a7b378c9SRob Clark 	mutex_init(&stream->read_lock);
445a7b378c9SRob Clark 
446a7b378c9SRob Clark 	guard(mutex)(&gpu->perfcntr_lock);
447a7b378c9SRob Clark 
448a7b378c9SRob Clark 	if (args->flags & MSM_PERFCNTR_STREAM) {
449a7b378c9SRob Clark 		if (perfcntrs->stream)
450a7b378c9SRob Clark 			return UERR(EBUSY, dev, "perfcntr stream already open");
451a7b378c9SRob Clark 	}
452a7b378c9SRob Clark 
453a7b378c9SRob Clark 	size_t bufsz = 16;  /* header size includes seqno and 64b timestamp: */
454a7b378c9SRob Clark 	int ret = 0;
455a7b378c9SRob Clark 
456a7b378c9SRob Clark 	for (unsigned i = 0; i < args->nr_groups; i++) {
457a7b378c9SRob Clark 		struct drm_msm_perfcntr_group g = {0};
458a7b378c9SRob Clark 		size_t sz = min_t(size_t, args->group_stride, sizeof(g));
459a7b378c9SRob Clark 		void __user *userptr =
460a7b378c9SRob Clark 			u64_to_user_ptr(args->groups + (i * args->group_stride));
461a7b378c9SRob Clark 
462a7b378c9SRob Clark 		if (copy_from_user(&g, userptr, sz))
463a7b378c9SRob Clark 			return -EFAULT;
464a7b378c9SRob Clark 
465a7b378c9SRob Clark 		if (g.pad)
466a7b378c9SRob Clark 			return UERR(EINVAL, dev, "groups[%d]: invalid pad", i);
467a7b378c9SRob Clark 
468a7b378c9SRob Clark 		int idx = get_group_idx(gpu, g.group_name, sizeof(g.group_name));
469a7b378c9SRob Clark 
470a7b378c9SRob Clark 		if (idx < 0)
471a7b378c9SRob Clark 			return UERR(EINVAL, dev, "groups[%d]: unknown group", i);
472a7b378c9SRob Clark 
473a7b378c9SRob Clark 		if (nr_counters[idx])
474a7b378c9SRob Clark 			return UERR(EINVAL, dev, "groups[%d]: duplicate group", i);
475a7b378c9SRob Clark 
476a7b378c9SRob Clark 		if (g.nr_countables > gpu->perfcntr_groups[idx].num_counters)
477a7b378c9SRob Clark 			return UERR(EINVAL, dev, "groups[%d]: too many counters", i);
478a7b378c9SRob Clark 
479a7b378c9SRob Clark 		if (args->flags & MSM_PERFCNTR_STREAM) {
480a7b378c9SRob Clark 			if (g.nr_countables && !g.countables)
481a7b378c9SRob Clark 				return UERR(EINVAL, dev, "groups[%d]: no countables", i);
482a7b378c9SRob Clark 		} else {
483a7b378c9SRob Clark 			if (g.countables)
484a7b378c9SRob Clark 				return UERR(EINVAL, dev, "groups[%d]: countables should be NULL", i);
485a7b378c9SRob Clark 		}
486a7b378c9SRob Clark 
487a7b378c9SRob Clark 		int avail_counters = get_available_counters(gpu, idx, args->flags);
488a7b378c9SRob Clark 		if (g.nr_countables > avail_counters) {
489a7b378c9SRob Clark 			/*
490a7b378c9SRob Clark 			 * Defer error return until we process all groups, in
491a7b378c9SRob Clark 			 * case there are other E2BIG groups:
492a7b378c9SRob Clark 			 */
493a7b378c9SRob Clark 			ret = UERR(E2BIG, dev, "groups[%d]: too few counters available", i);
494a7b378c9SRob Clark 
495a7b378c9SRob Clark 			if (args->flags & MSM_PERFCNTR_UPDATE) {
496a7b378c9SRob Clark 				/* Let userspace know how many counters are actually avail: */
497a7b378c9SRob Clark 				g.nr_countables = avail_counters;
498a7b378c9SRob Clark 				if (copy_to_user(userptr, &g, sz))
499a7b378c9SRob Clark 					return -EFAULT;
500a7b378c9SRob Clark 			}
501a7b378c9SRob Clark 		}
502a7b378c9SRob Clark 
503a7b378c9SRob Clark 		group_idx[i] = idx;
504a7b378c9SRob Clark 		perfctx->reserved_counters[idx] = g.nr_countables;
505a7b378c9SRob Clark 
506a7b378c9SRob Clark 		/* +1 to catch duplicate zero sized groups: */
507a7b378c9SRob Clark 		nr_counters[idx] = g.nr_countables + 1;
508a7b378c9SRob Clark 
509a7b378c9SRob Clark 		if (args->flags & MSM_PERFCNTR_STREAM) {
510a7b378c9SRob Clark 			size_t sz = sizeof(uint32_t) * g.nr_countables;
511a7b378c9SRob Clark 			void __user *userptr = u64_to_user_ptr(g.countables);
512a7b378c9SRob Clark 
513a7b378c9SRob Clark 			if (copy_from_user(perfcntrs->groups[idx]->countables, userptr, sz))
514a7b378c9SRob Clark 				return -EFAULT;
515a7b378c9SRob Clark 
516a7b378c9SRob Clark 			/* Samples are 64b per countable: */
517a7b378c9SRob Clark 			bufsz += 2 * sz;
518a7b378c9SRob Clark 		}
519a7b378c9SRob Clark 	}
520a7b378c9SRob Clark 
521a7b378c9SRob Clark 	if (ret)
522a7b378c9SRob Clark 		return ret;
523a7b378c9SRob Clark 
524a7b378c9SRob Clark 	if (args->flags & MSM_PERFCNTR_STREAM) {
525a7b378c9SRob Clark 		/*
526a7b378c9SRob Clark 		 * Validate requested buffer size is large enough for at least
527a7b378c9SRob Clark 		 * a single sample period.
528a7b378c9SRob Clark 		 *
529a7b378c9SRob Clark 		 * Note the circ_buf implementation needs to be 1 byte larger
530a7b378c9SRob Clark 		 * than max it can hold (see CIRC_SPACE()).
531a7b378c9SRob Clark 		 */
532a7b378c9SRob Clark 		if (stream->fifo_size <= bufsz)
533a7b378c9SRob Clark 			return UERR(EINVAL, dev, "required buffer size: %zu", bufsz);
534a7b378c9SRob Clark 
535a7b378c9SRob Clark 		/* There aren't enough counters to hit this limit: */
536a7b378c9SRob Clark 		WARN_ON(bufsz > SZ_128M);
537a7b378c9SRob Clark 
538a7b378c9SRob Clark 		stream->period_size = bufsz;
539a7b378c9SRob Clark 
540a7b378c9SRob Clark 		void *buf __free(kfree) = kmalloc(stream->fifo_size, GFP_KERNEL);
541a7b378c9SRob Clark 		if (!buf)
542a7b378c9SRob Clark 			return -ENOMEM;
543a7b378c9SRob Clark 
544a7b378c9SRob Clark 		FD_PREPARE(fdf, O_CLOEXEC,
545a7b378c9SRob Clark 			   anon_inode_getfile("[msm_perfcntrs]", &stream_fops, stream, 0));
546a7b378c9SRob Clark 		if (fdf.err)
547a7b378c9SRob Clark 			return fdf.err;
548a7b378c9SRob Clark 
549a7b378c9SRob Clark 		INIT_WORK(&stream->sel_work, sel_worker);
550a7b378c9SRob Clark 		kthread_init_work(&stream->sample_work, sample_worker);
551a7b378c9SRob Clark 		init_waitqueue_head(&stream->poll_wq);
552a7b378c9SRob Clark 		hrtimer_setup(&stream->sample_timer, sample_timer,
553a7b378c9SRob Clark 			      CLOCK_MONOTONIC, HRTIMER_MODE_REL);
554a7b378c9SRob Clark 
555a7b378c9SRob Clark 		stream->sel_fence = ++perfcntrs->sel_seqno;
556a7b378c9SRob Clark 		stream->group_idx = no_free_ptr(group_idx);
557a7b378c9SRob Clark 		stream->fifo.buf = no_free_ptr(buf);
558a7b378c9SRob Clark 
559a7b378c9SRob Clark 		/* commit the allocated counters, subtracting off original +1: */
560a7b378c9SRob Clark 		for (unsigned i = 0; i < gpu->num_perfcntr_groups; i++)
561a7b378c9SRob Clark 			perfcntrs->groups[i]->allocated_counters = nr_counters[i] - 1;
562a7b378c9SRob Clark 
563a7b378c9SRob Clark 		perfcntrs->stream = no_free_ptr(stream);
564a7b378c9SRob Clark 
565a7b378c9SRob Clark 		msm_perfcntr_resume_locked(perfcntrs->stream);
566a7b378c9SRob Clark 
567a7b378c9SRob Clark 		stream_fd = fd_publish(fdf);
568a7b378c9SRob Clark 	} else {
569a7b378c9SRob Clark 		kfree(ctx->perfctx);
570a7b378c9SRob Clark 		ctx->perfctx = no_free_ptr(perfctx);
571a7b378c9SRob Clark 	}
572a7b378c9SRob Clark 
573a7b378c9SRob Clark 	return stream_fd;
574a7b378c9SRob Clark }
575a7b378c9SRob Clark 
57602195633SRob Clark /**
57702195633SRob Clark  * msm_perfcntr_group_idx - map idx of perfcntr group to group_idx
57802195633SRob Clark  * @stream: The global perfcntr stream
57902195633SRob Clark  * @n: The requested group_idx
58002195633SRob Clark  *
58102195633SRob Clark  * The PERFCNTR_CONFIG ioctl requested N counters/countables per perfcntr
58202195633SRob Clark  * group, but the order of groups is not required to match the order they
58302195633SRob Clark  * are defined in the perfcntr tables (which is not stable/UABI, only the
58402195633SRob Clark  * group names are UABI).
58502195633SRob Clark  *
58602195633SRob Clark  * But the order samples are returned in the stream should match the
58702195633SRob Clark  * order they are requested in the PERFCNTR_CONFIG ioctl.  This helper
58802195633SRob Clark  * handles the order remapping.
58902195633SRob Clark  *
59002195633SRob Clark  * Returns an index into gpu->perfcntr_groups[] and perfcntrs->groups[].
59102195633SRob Clark  */
59202195633SRob Clark uint32_t
59302195633SRob Clark msm_perfcntr_group_idx(const struct msm_perfcntr_stream *stream, uint32_t n)
59402195633SRob Clark {
59502195633SRob Clark 	WARN_ON_ONCE(n >= stream->nr_groups);
59602195633SRob Clark 	return stream->group_idx[n];
59702195633SRob Clark }
59802195633SRob Clark 
59902195633SRob Clark /**
60002195633SRob Clark  * msm_perfcntr_counter_base - get idx of the first counter in group
60102195633SRob Clark  * @stream: The global perfcntr stream
60202195633SRob Clark  * @group_idx: the index of the counter group
60302195633SRob Clark  *
60402195633SRob Clark  * For global counter collection, counters are allocated from the end
60502195633SRob Clark  * (last counter) while UMD allocates them from the start (0..N-1).
60602195633SRob Clark  * Since UMD always allocated them from the start this also minimizes
60702195633SRob Clark  * the chance of conflict when using old UMD which predates
60802195633SRob Clark  * PERFCNTR_CONFIG ioctl.
60902195633SRob Clark  *
61002195633SRob Clark  * Returns the index of first counter to use.  An index into
61102195633SRob Clark  * msm_perfcntr_group::counters[].
61202195633SRob Clark  */
61302195633SRob Clark uint32_t
61402195633SRob Clark msm_perfcntr_counter_base(const struct msm_perfcntr_stream *stream, uint32_t group_idx)
61502195633SRob Clark {
61602195633SRob Clark 	struct msm_gpu *gpu = stream->gpu;
61702195633SRob Clark 	struct msm_perfcntr_state *perfcntrs = gpu->perfcntrs;
61802195633SRob Clark 	unsigned num_counters = gpu->perfcntr_groups[group_idx].num_counters;
61902195633SRob Clark 	unsigned allocated_counters = perfcntrs->groups[group_idx]->allocated_counters;
62002195633SRob Clark 
62102195633SRob Clark 	return num_counters - allocated_counters;
62202195633SRob Clark }
62302195633SRob Clark 
62402195633SRob Clark static void
62502195633SRob Clark __msm_perfcntr_cleanup(struct msm_gpu *gpu, struct msm_perfcntr_state *perfcntrs)
62602195633SRob Clark {
62702195633SRob Clark 	struct device *dev = &gpu->pdev->dev;
62802195633SRob Clark 
62902195633SRob Clark 	for (unsigned i = 0; i < gpu->num_perfcntr_groups; i++)
63002195633SRob Clark 		devm_kfree(dev, perfcntrs->groups[i]);
63102195633SRob Clark 
63202195633SRob Clark 	devm_kfree(dev, perfcntrs);
63302195633SRob Clark }
63402195633SRob Clark 
63502195633SRob Clark void
63602195633SRob Clark msm_perfcntr_cleanup(struct msm_gpu *gpu)
63702195633SRob Clark {
63802195633SRob Clark 	if (!gpu->perfcntrs)
63902195633SRob Clark 		return;
64002195633SRob Clark 
64102195633SRob Clark 	__msm_perfcntr_cleanup(gpu, gpu->perfcntrs);
64202195633SRob Clark 	gpu->perfcntrs = NULL;
64302195633SRob Clark }
64402195633SRob Clark 
64502195633SRob Clark struct msm_perfcntr_state *
64602195633SRob Clark msm_perfcntr_init(struct msm_gpu *gpu)
64702195633SRob Clark {
64802195633SRob Clark 	struct msm_perfcntr_state *perfcntrs;
64902195633SRob Clark 	struct device *dev = &gpu->pdev->dev;
65002195633SRob Clark 	size_t sz;
65102195633SRob Clark 
65202195633SRob Clark 	sz = struct_size(perfcntrs, groups, gpu->num_perfcntr_groups);
65302195633SRob Clark 	perfcntrs = devm_kzalloc(dev, sz, GFP_KERNEL);
65402195633SRob Clark 	if (!perfcntrs)
65502195633SRob Clark 		return ERR_PTR(-ENOMEM);
65602195633SRob Clark 
65702195633SRob Clark 	for (unsigned i = 0; i < gpu->num_perfcntr_groups; i++) {
65802195633SRob Clark 		const struct msm_perfcntr_group *group =
65902195633SRob Clark 			&gpu->perfcntr_groups[i];
66002195633SRob Clark 
66102195633SRob Clark 		sz = struct_size(perfcntrs->groups[i], countables, group->num_counters);
66202195633SRob Clark 		perfcntrs->groups[i] = devm_kzalloc(dev, sz, GFP_KERNEL);
66302195633SRob Clark 		if (!perfcntrs->groups[i]) {
66402195633SRob Clark 			__msm_perfcntr_cleanup(gpu, perfcntrs);
66502195633SRob Clark 			return ERR_PTR(-ENOMEM);
66602195633SRob Clark 		}
66702195633SRob Clark 	}
66802195633SRob Clark 
66902195633SRob Clark 	return perfcntrs;
67002195633SRob Clark }
671