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