cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

msm_perf.c (4674B)


      1// SPDX-License-Identifier: GPL-2.0-only
      2/*
      3 * Copyright (C) 2013 Red Hat
      4 * Author: Rob Clark <robdclark@gmail.com>
      5 */
      6
      7/* For profiling, userspace can:
      8 *
      9 *   tail -f /sys/kernel/debug/dri/<minor>/gpu
     10 *
     11 * This will enable performance counters/profiling to track the busy time
     12 * and any gpu specific performance counters that are supported.
     13 */
     14
     15#ifdef CONFIG_DEBUG_FS
     16
     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	.llseek = no_llseek,
    196	.release = perf_release,
    197};
    198
    199int msm_perf_debugfs_init(struct drm_minor *minor)
    200{
    201	struct msm_drm_private *priv = minor->dev->dev_private;
    202	struct msm_perf_state *perf;
    203
    204	/* only create on first minor: */
    205	if (priv->perf)
    206		return 0;
    207
    208	perf = kzalloc(sizeof(*perf), GFP_KERNEL);
    209	if (!perf)
    210		return -ENOMEM;
    211
    212	perf->dev = minor->dev;
    213
    214	mutex_init(&perf->read_lock);
    215	priv->perf = perf;
    216
    217	debugfs_create_file("perf", S_IFREG | S_IRUGO, minor->debugfs_root,
    218			    perf, &perf_debugfs_fops);
    219	return 0;
    220}
    221
    222void msm_perf_debugfs_cleanup(struct msm_drm_private *priv)
    223{
    224	struct msm_perf_state *perf = priv->perf;
    225
    226	if (!perf)
    227		return;
    228
    229	priv->perf = NULL;
    230
    231	mutex_destroy(&perf->read_lock);
    232
    233	kfree(perf);
    234}
    235
    236#endif