brintos

brintos / linux-shallow public Read only

0
0
Text · 4.5 KiB · d3c7889 Raw
236 lines · c
1// SPDX-License-Identifier: GPL-2.0-only2/*3 * Copyright (C) 2013 Red Hat4 * Author: Rob Clark <robdclark@gmail.com>5 */6 7/* For profiling, userspace can:8 *9 *   tail -f /sys/kernel/debug/dri/<minor>/gpu10 *11 * This will enable performance counters/profiling to track the busy time12 * and any gpu specific performance counters that are supported.13 */14 15#ifdef CONFIG_DEBUG_FS16 17#include <linux/debugfs.h>18#include <linux/uaccess.h>19 20#include <drm/drm_file.h>21 22#include "msm_drv.h"23#include "msm_gpu.h"24 25struct msm_perf_state {26	struct drm_device *dev;27 28	bool open;29	int cnt;30	struct mutex read_lock;31 32	char buf[256];33	int buftot, bufpos;34 35	unsigned long next_jiffies;36};37 38#define SAMPLE_TIME (HZ/4)39 40/* wait for next sample time: */41static int wait_sample(struct msm_perf_state *perf)42{43	unsigned long start_jiffies = jiffies;44 45	if (time_after(perf->next_jiffies, start_jiffies)) {46		unsigned long remaining_jiffies =47			perf->next_jiffies - start_jiffies;48		int ret = schedule_timeout_interruptible(remaining_jiffies);49		if (ret > 0) {50			/* interrupted */51			return -ERESTARTSYS;52		}53	}54	perf->next_jiffies += SAMPLE_TIME;55	return 0;56}57 58static int refill_buf(struct msm_perf_state *perf)59{60	struct msm_drm_private *priv = perf->dev->dev_private;61	struct msm_gpu *gpu = priv->gpu;62	char *ptr = perf->buf;63	int rem = sizeof(perf->buf);64	int i, n;65 66	if ((perf->cnt++ % 32) == 0) {67		/* Header line: */68		n = snprintf(ptr, rem, "%%BUSY");69		ptr += n;70		rem -= n;71 72		for (i = 0; i < gpu->num_perfcntrs; i++) {73			const struct msm_gpu_perfcntr *perfcntr = &gpu->perfcntrs[i];74			n = snprintf(ptr, rem, "\t%s", perfcntr->name);75			ptr += n;76			rem -= n;77		}78	} else {79		/* Sample line: */80		uint32_t activetime = 0, totaltime = 0;81		uint32_t cntrs[5];82		uint32_t val;83		int ret;84 85		/* sleep until next sample time: */86		ret = wait_sample(perf);87		if (ret)88			return ret;89 90		ret = msm_gpu_perfcntr_sample(gpu, &activetime, &totaltime,91				ARRAY_SIZE(cntrs), cntrs);92		if (ret < 0)93			return ret;94 95		val = totaltime ? 1000 * activetime / totaltime : 0;96		n = snprintf(ptr, rem, "%3d.%d%%", val / 10, val % 10);97		ptr += n;98		rem -= n;99 100		for (i = 0; i < ret; i++) {101			/* cycle counters (I think).. convert to MHz.. */102			val = cntrs[i] / 10000;103			n = snprintf(ptr, rem, "\t%5d.%02d",104					val / 100, val % 100);105			ptr += n;106			rem -= n;107		}108	}109 110	n = snprintf(ptr, rem, "\n");111	ptr += n;112	rem -= n;113 114	perf->bufpos = 0;115	perf->buftot = ptr - perf->buf;116 117	return 0;118}119 120static ssize_t perf_read(struct file *file, char __user *buf,121		size_t sz, loff_t *ppos)122{123	struct msm_perf_state *perf = file->private_data;124	int n = 0, ret = 0;125 126	mutex_lock(&perf->read_lock);127 128	if (perf->bufpos >= perf->buftot) {129		ret = refill_buf(perf);130		if (ret)131			goto out;132	}133 134	n = min((int)sz, perf->buftot - perf->bufpos);135	if (copy_to_user(buf, &perf->buf[perf->bufpos], n)) {136		ret = -EFAULT;137		goto out;138	}139 140	perf->bufpos += n;141	*ppos += n;142 143out:144	mutex_unlock(&perf->read_lock);145	if (ret)146		return ret;147	return n;148}149 150static int perf_open(struct inode *inode, struct file *file)151{152	struct msm_perf_state *perf = inode->i_private;153	struct drm_device *dev = perf->dev;154	struct msm_drm_private *priv = dev->dev_private;155	struct msm_gpu *gpu = priv->gpu;156	int ret = 0;157 158	if (!gpu)159		return -ENODEV;160 161	mutex_lock(&gpu->lock);162 163	if (perf->open) {164		ret = -EBUSY;165		goto out;166	}167 168	file->private_data = perf;169	perf->open = true;170	perf->cnt = 0;171	perf->buftot = 0;172	perf->bufpos = 0;173	msm_gpu_perfcntr_start(gpu);174	perf->next_jiffies = jiffies + SAMPLE_TIME;175 176out:177	mutex_unlock(&gpu->lock);178	return ret;179}180 181static int perf_release(struct inode *inode, struct file *file)182{183	struct msm_perf_state *perf = inode->i_private;184	struct msm_drm_private *priv = perf->dev->dev_private;185	msm_gpu_perfcntr_stop(priv->gpu);186	perf->open = false;187	return 0;188}189 190 191static const struct file_operations perf_debugfs_fops = {192	.owner = THIS_MODULE,193	.open = perf_open,194	.read = perf_read,195	.release = perf_release,196};197 198int msm_perf_debugfs_init(struct drm_minor *minor)199{200	struct msm_drm_private *priv = minor->dev->dev_private;201	struct msm_perf_state *perf;202 203	/* only create on first minor: */204	if (priv->perf)205		return 0;206 207	perf = kzalloc(sizeof(*perf), GFP_KERNEL);208	if (!perf)209		return -ENOMEM;210 211	perf->dev = minor->dev;212 213	mutex_init(&perf->read_lock);214	priv->perf = perf;215 216	debugfs_create_file("perf", S_IFREG | S_IRUGO, minor->debugfs_root,217			    perf, &perf_debugfs_fops);218	return 0;219}220 221void msm_perf_debugfs_cleanup(struct msm_drm_private *priv)222{223	struct msm_perf_state *perf = priv->perf;224 225	if (!perf)226		return;227 228	priv->perf = NULL;229 230	mutex_destroy(&perf->read_lock);231 232	kfree(perf);233}234 235#endif236