drivers/gpu/msm/kgsl_threadstats.c (166 lines of code) (raw):
/* Copyright (c) 2018, Facebook Technologies, LLC. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
* only version 2 as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
*/
#include "kgsl.h"
#include "kgsl_device.h"
#include "kgsl_threadstats.h"
static void kgsl_destroy_thread_private(struct kref *kref)
{
struct kgsl_thread_private *private = container_of(kref,
struct kgsl_thread_private, refcount);
kfree(private);
}
int kgsl_thread_private_get(struct kgsl_thread_private *thread)
{
int ret = 0;
if (thread != NULL)
ret = kref_get_unless_zero(&thread->refcount);
return ret;
}
void kgsl_thread_private_put(struct kgsl_thread_private *thread)
{
if (thread)
kref_put(&thread->refcount, kgsl_destroy_thread_private);
}
struct kgsl_thread_private *kgsl_thread_private_find(pid_t tid)
{
struct kgsl_thread_private *t, *private = NULL;
mutex_lock(&kgsl_driver.process_mutex);
list_for_each_entry(t, &kgsl_driver.thread_list, list) {
if (t->tid == tid) {
if (kgsl_thread_private_get(t))
private = t;
break;
}
}
mutex_unlock(&kgsl_driver.process_mutex);
return private;
}
static struct kgsl_thread_private *kgsl_thread_private_new(
struct kgsl_device *device)
{
struct kgsl_thread_private *private;
pid_t tid = task_pid_nr(current);
list_for_each_entry(private, &kgsl_driver.thread_list, list) {
if (private->tid == tid) {
if (!kgsl_thread_private_get(private))
private = ERR_PTR(-EINVAL);
return private;
}
}
private = kzalloc(sizeof(struct kgsl_thread_private), GFP_KERNEL);
if (private == NULL)
return ERR_PTR(-ENOMEM);
kref_init(&private->refcount);
private->tid = tid;
get_task_comm(private->comm, current);
return private;
}
struct kgsl_threadstat_attribute {
struct attribute attr;
int type;
ssize_t (*show)(struct kgsl_thread_private *priv, int type, char *buf);
};
static ssize_t
threadstat_attr_show(struct kgsl_thread_private *priv, int type, char *buf) {
return snprintf(buf, PAGE_SIZE, "%llu\n", priv->stats[type]);
}
#define THREADSTAT_ATTR(_type, _name) \
{ \
.attr = { .name = __stringify(_name), .mode = 0444 }, \
.type = _type, \
.show = threadstat_attr_show, \
}
struct kgsl_threadstat_attribute threadstat_attrs[] = {
THREADSTAT_ATTR(KGSL_THREADSTATS_SUBMITTED, submitted),
THREADSTAT_ATTR(KGSL_THREADSTATS_SUBMITTED_ID, submitted_id),
THREADSTAT_ATTR(KGSL_THREADSTATS_SUBMITTED_COUNT, submitted_count),
THREADSTAT_ATTR(KGSL_THREADSTATS_RETIRED, retired),
THREADSTAT_ATTR(KGSL_THREADSTATS_RETIRED_ID, retired_id),
THREADSTAT_ATTR(KGSL_THREADSTATS_RETIRED_COUNT, retired_count),
THREADSTAT_ATTR(KGSL_THREADSTATS_QUEUED, queued),
THREADSTAT_ATTR(KGSL_THREADSTATS_QUEUED_ID, queued_id),
THREADSTAT_ATTR(KGSL_THREADSTATS_QUEUED_COUNT, queued_count),
THREADSTAT_ATTR(KGSL_THREADSTATS_ACTIVE_TIME, active_time),
};
#define to_threadstat_attr(a) \
container_of(a, struct kgsl_threadstat_attribute, attr)
static ssize_t threadstat_sysfs_show(struct kobject *kobj,
struct attribute *attr, char *buf)
{
struct kgsl_threadstat_attribute *pattr = to_threadstat_attr(attr);
struct kgsl_thread_private *priv;
ssize_t ret;
priv = kobj ? container_of(kobj, struct kgsl_thread_private, kobj) :
NULL;
if (priv && pattr->show)
ret = pattr->show(priv, pattr->type, buf);
else
ret = -EIO;
return ret;
}
static const struct sysfs_ops threadstat_sysfs_ops = {
.show = threadstat_sysfs_show,
};
static struct kobj_type ktype_threadstat = {
.sysfs_ops = &threadstat_sysfs_ops,
};
void kgsl_thread_uninit_sysfs(struct kgsl_thread_private *private)
{
int i;
for (i = 0; i < KGSL_THREADSTATS_MAX; i++) {
sysfs_put(private->event_sd[i]);
sysfs_remove_file(&private->kobj, &threadstat_attrs[i].attr);
}
kobject_put(&private->kobj);
/* Put the refcount we got in kgsl_thread_init_sysfs */
kgsl_thread_private_put(private);
}
void kgsl_thread_init_sysfs(struct kgsl_device *device,
struct kgsl_thread_private *private)
{
int i;
unsigned char name[16];
/* Keep private valid until the sysfs enries are removed. */
kgsl_thread_private_get(private);
snprintf(name, sizeof(name), "%d", private->tid);
if (kobject_init_and_add(&private->kobj, &ktype_threadstat,
kgsl_driver.threadkobj, name)) {
WARN(1, "Unable to add sysfs dir '%s'\n", name);
return;
}
for (i = 0; i < KGSL_THREADSTATS_MAX; i++) {
if (sysfs_create_file(&private->kobj,
&threadstat_attrs[i].attr))
WARN(1, "Couldn't create sysfs file '%s'\n",
threadstat_attrs[i].attr.name);
private->event_sd[i] = sysfs_get_dirent(
private->kobj.sd, threadstat_attrs[i].attr.name);
}
}
void kgsl_thread_private_close(struct kgsl_thread_private *private)
{
mutex_lock(&kgsl_driver.process_mutex);
if (--private->fd_count > 0) {
mutex_unlock(&kgsl_driver.process_mutex);
kgsl_thread_private_put(private);
return;
}
pr_debug("thread: %s [%d]\n", private->comm, private->tid);
kgsl_thread_uninit_sysfs(private);
list_del(&private->list);
mutex_unlock(&kgsl_driver.process_mutex);
kgsl_thread_private_put(private);
}
struct kgsl_thread_private *kgsl_thread_private_open(
struct kgsl_device *device)
{
struct kgsl_thread_private *private;
mutex_lock(&kgsl_driver.process_mutex);
private = kgsl_thread_private_new(device);
if (IS_ERR(private))
goto done;
if (private->fd_count++ == 0) {
pr_debug("thread: %s [%d]\n", private->comm, private->tid);
kgsl_thread_init_sysfs(device, private);
list_add(&private->list, &kgsl_driver.thread_list);
}
done:
mutex_unlock(&kgsl_driver.process_mutex);
return private;
}