blob: ba93d497ec391104afcb85e4d33936755b1b5587 [file] [log] [blame]
// SPDX-License-Identifier: GPL-2.0
/*
* EdgeTPU usage stats
*
* Copyright (C) 2020 Google, Inc.
*/
#include <linux/slab.h>
#include <linux/sysfs.h>
#include "edgetpu-config.h"
#include "edgetpu-internal.h"
#include "edgetpu-kci.h"
#include "edgetpu-usage-stats.h"
/* Max number of frequencies to support */
#define EDGETPU_MAX_STATES 10
struct uid_entry {
int32_t uid;
uint64_t time_in_state[EDGETPU_MAX_STATES];
struct hlist_node node;
};
static int tpu_state_map(struct edgetpu_dev *etdev, uint32_t state)
{
int i, idx = 0;
mutex_lock(&etdev->freq_lock);
/* Use frequency table if f/w already reported via usage_stats */
if (etdev->freq_table) {
for (i = etdev->freq_count - 1; i >= 0; i--) {
if (state == etdev->freq_table[i])
idx = i;
}
mutex_unlock(&etdev->freq_lock);
return idx;
}
mutex_unlock(&etdev->freq_lock);
/*
* use predefined state table in case of no f/w reported supported
* frequencies.
*/
for (i = (EDGETPU_NUM_STATES - 1); i >= 0; i--) {
if (state >= edgetpu_active_states[i])
return i;
}
return 0;
}
/* Caller must hold usage_stats lock */
static struct uid_entry *
find_uid_entry_locked(int32_t uid, struct edgetpu_usage_stats *ustats)
{
struct uid_entry *uid_entry;
hash_for_each_possible(ustats->uid_hash_table, uid_entry, node, uid) {
if (uid_entry->uid == uid)
return uid_entry;
}
return NULL;
}
int edgetpu_usage_add(struct edgetpu_dev *etdev, struct tpu_usage *tpu_usage)
{
struct edgetpu_usage_stats *ustats = etdev->usage_stats;
struct uid_entry *uid_entry;
int state = tpu_state_map(etdev, tpu_usage->power_state);
if (!ustats)
return 0;
etdev_dbg(etdev, "%s: uid=%u state=%u dur=%u", __func__,
tpu_usage->uid, tpu_usage->power_state,
tpu_usage->duration_us);
mutex_lock(&ustats->usage_stats_lock);
/* Find the uid in uid_hash_table first */
uid_entry = find_uid_entry_locked(tpu_usage->uid, ustats);
if (uid_entry) {
uid_entry->time_in_state[state] += tpu_usage->duration_us;
mutex_unlock(&ustats->usage_stats_lock);
return 0;
}
/* Allocate memory for this uid */
uid_entry = kzalloc(sizeof(*uid_entry), GFP_KERNEL);
if (!uid_entry) {
mutex_unlock(&ustats->usage_stats_lock);
return -ENOMEM;
}
uid_entry->uid = tpu_usage->uid;
uid_entry->time_in_state[state] += tpu_usage->duration_us;
/* Add uid_entry to the uid_hash_table */
hash_add(ustats->uid_hash_table, &uid_entry->node, tpu_usage->uid);
mutex_unlock(&ustats->usage_stats_lock);
return 0;
}
static void edgetpu_utilization_update(
struct edgetpu_dev *etdev,
struct edgetpu_component_activity *activity)
{
struct edgetpu_usage_stats *ustats = etdev->usage_stats;
if (!ustats)
return;
etdev_dbg(etdev, "%s: comp=%d utilized %d%%\n", __func__,
activity->component, activity->utilization);
mutex_lock(&ustats->usage_stats_lock);
if (activity->utilization && activity->component >= 0 &&
activity->component < EDGETPU_USAGE_COMPONENT_COUNT)
ustats->component_utilization[activity->component] =
activity->utilization;
mutex_unlock(&ustats->usage_stats_lock);
}
static void edgetpu_counter_update(
struct edgetpu_dev *etdev,
struct edgetpu_usage_counter *counter)
{
struct edgetpu_usage_stats *ustats = etdev->usage_stats;
if (!ustats)
return;
etdev_dbg(etdev, "%s: type=%d value=%llu\n", __func__,
counter->type, counter->value);
mutex_lock(&ustats->usage_stats_lock);
if (counter->type >= 0 && counter->type < EDGETPU_COUNTER_COUNT)
ustats->counter[counter->type] += counter->value;
mutex_unlock(&ustats->usage_stats_lock);
}
static void edgetpu_counter_clear(
struct edgetpu_dev *etdev,
enum edgetpu_usage_counter_type counter_type)
{
struct edgetpu_usage_stats *ustats = etdev->usage_stats;
if (!ustats)
return;
if (counter_type >= EDGETPU_COUNTER_COUNT)
return;
mutex_lock(&ustats->usage_stats_lock);
ustats->counter[counter_type] = 0;
mutex_unlock(&ustats->usage_stats_lock);
}
static void edgetpu_max_watermark_update(
struct edgetpu_dev *etdev,
struct edgetpu_usage_max_watermark *max_watermark)
{
struct edgetpu_usage_stats *ustats = etdev->usage_stats;
if (!ustats)
return;
etdev_dbg(etdev, "%s: type=%d value=%llu\n", __func__,
max_watermark->type, max_watermark->value);
if (max_watermark->type < 0 ||
max_watermark->type >= EDGETPU_MAX_WATERMARK_TYPE_COUNT)
return;
mutex_lock(&ustats->usage_stats_lock);
if (max_watermark->value > ustats->max_watermark[max_watermark->type])
ustats->max_watermark[max_watermark->type] =
max_watermark->value;
mutex_unlock(&ustats->usage_stats_lock);
}
static void edgetpu_thread_stats_update(
struct edgetpu_dev *etdev,
struct edgetpu_thread_stats *thread_stats)
{
struct edgetpu_usage_stats *ustats = etdev->usage_stats;
if (!ustats)
return;
etdev_dbg(etdev, "%s: id=%d stackmax=%u\n", __func__,
thread_stats->thread_id, thread_stats->max_stack_usage_bytes);
if (thread_stats->thread_id < 0 ||
thread_stats->thread_id >= EDGETPU_FW_THREAD_COUNT)
return;
mutex_lock(&ustats->usage_stats_lock);
if (thread_stats->max_stack_usage_bytes >
ustats->thread_stack_max[thread_stats->thread_id])
ustats->thread_stack_max[thread_stats->thread_id] =
thread_stats->max_stack_usage_bytes;
mutex_unlock(&ustats->usage_stats_lock);
}
/* Record new supported frequencies if reported by firmware */
static void edgetpu_dvfs_frequency_update(struct edgetpu_dev *etdev, uint32_t frequency)
{
uint32_t *freq_table, i;
mutex_lock(&etdev->freq_lock);
if (!etdev->freq_table) {
freq_table = kvmalloc(EDGETPU_MAX_STATES * sizeof(uint32_t), GFP_KERNEL);
if (!freq_table) {
etdev_warn(etdev, "Unable to create supported frequencies table");
goto out;
}
etdev->freq_count = 0;
etdev->freq_table = freq_table;
}
freq_table = etdev->freq_table;
for (i = 0; i < etdev->freq_count; i++) {
if (freq_table[i] == frequency)
goto out;
}
if (etdev->freq_count >= EDGETPU_MAX_STATES) {
etdev_warn(etdev, "Unable to record supported frequencies");
goto out;
}
freq_table[etdev->freq_count++] = frequency;
out:
mutex_unlock(&etdev->freq_lock);
}
void edgetpu_usage_stats_process_buffer(struct edgetpu_dev *etdev, void *buf)
{
struct edgetpu_usage_header *header = buf;
struct edgetpu_usage_metric *metric =
(struct edgetpu_usage_metric *)(header + 1);
int i;
etdev_dbg(etdev, "%s: n=%u sz=%u", __func__,
header->num_metrics, header->metric_size);
if (header->metric_size != sizeof(struct edgetpu_usage_metric)) {
etdev_dbg(etdev, "%s: expected sz=%zu, discard", __func__,
sizeof(struct edgetpu_usage_metric));
return;
}
for (i = 0; i < header->num_metrics; i++) {
switch (metric->type) {
case EDGETPU_METRIC_TYPE_TPU_USAGE:
edgetpu_usage_add(etdev, &metric->tpu_usage);
break;
case EDGETPU_METRIC_TYPE_COMPONENT_ACTIVITY:
edgetpu_utilization_update(
etdev, &metric->component_activity);
break;
case EDGETPU_METRIC_TYPE_COUNTER:
edgetpu_counter_update(etdev, &metric->counter);
break;
case EDGETPU_METRIC_TYPE_MAX_WATERMARK:
edgetpu_max_watermark_update(
etdev, &metric->max_watermark);
break;
case EDGETPU_METRIC_TYPE_THREAD_STATS:
edgetpu_thread_stats_update(
etdev, &metric->thread_stats);
break;
case EDGETPU_METRIC_TYPE_DVFS_FREQUENCY_INFO:
edgetpu_dvfs_frequency_update(
etdev, metric->dvfs_frequency_info);
break;
default:
etdev_dbg(etdev, "%s: %d: skip unknown type=%u",
__func__, i, metric->type);
break;
}
metric++;
}
}
int edgetpu_usage_get_utilization(struct edgetpu_dev *etdev,
enum edgetpu_usage_component component)
{
struct edgetpu_usage_stats *ustats = etdev->usage_stats;
int32_t val;
if (component >= EDGETPU_USAGE_COMPONENT_COUNT)
return -1;
edgetpu_kci_update_usage(etdev);
mutex_lock(&ustats->usage_stats_lock);
val = ustats->component_utilization[component];
ustats->component_utilization[component] = 0;
mutex_unlock(&ustats->usage_stats_lock);
return val;
}
static int64_t edgetpu_usage_get_counter(
struct edgetpu_dev *etdev,
enum edgetpu_usage_counter_type counter_type)
{
struct edgetpu_usage_stats *ustats = etdev->usage_stats;
int64_t val;
if (counter_type >= EDGETPU_COUNTER_COUNT)
return -1;
edgetpu_kci_update_usage(etdev);
mutex_lock(&ustats->usage_stats_lock);
val = ustats->counter[counter_type];
mutex_unlock(&ustats->usage_stats_lock);
return val;
}
static int64_t edgetpu_usage_get_max_watermark(
struct edgetpu_dev *etdev,
enum edgetpu_usage_max_watermark_type max_watermark_type)
{
struct edgetpu_usage_stats *ustats = etdev->usage_stats;
int64_t val;
if (max_watermark_type >= EDGETPU_MAX_WATERMARK_TYPE_COUNT)
return -1;
edgetpu_kci_update_usage(etdev);
mutex_lock(&ustats->usage_stats_lock);
val = ustats->max_watermark[max_watermark_type];
mutex_unlock(&ustats->usage_stats_lock);
return val;
}
static ssize_t tpu_usage_show(struct device *dev,
struct device_attribute *attr,
char *buf)
{
struct edgetpu_dev *etdev = dev_get_drvdata(dev);
struct edgetpu_usage_stats *ustats = etdev->usage_stats;
int i;
int ret = 0;
unsigned int bkt;
struct uid_entry *uid_entry;
edgetpu_kci_update_usage(etdev);
/* uid: state0speed state1speed ... */
ret += scnprintf(buf, PAGE_SIZE, "uid:");
mutex_lock(&etdev->freq_lock);
if (!etdev->freq_table) {
mutex_unlock(&etdev->freq_lock);
for (i = 0; i < EDGETPU_NUM_STATES; i++)
ret += scnprintf(buf + ret, PAGE_SIZE - ret, " %d",
edgetpu_states_display[i]);
} else {
for (i = 0; i < etdev->freq_count; i++)
ret += scnprintf(buf + ret, PAGE_SIZE - ret, " %d",
etdev->freq_table[i]);
mutex_unlock(&etdev->freq_lock);
}
ret += scnprintf(buf + ret, PAGE_SIZE - ret, "\n");
mutex_lock(&ustats->usage_stats_lock);
hash_for_each(ustats->uid_hash_table, bkt, uid_entry, node) {
ret += scnprintf(buf + ret, PAGE_SIZE - ret, "%d:",
uid_entry->uid);
for (i = 0; i < EDGETPU_NUM_STATES; i++)
ret += scnprintf(buf + ret, PAGE_SIZE - ret, " %lld",
uid_entry->time_in_state[i]);
ret += scnprintf(buf + ret, PAGE_SIZE - ret, "\n");
}
mutex_unlock(&ustats->usage_stats_lock);
return ret;
}
static void usage_stats_remove_uids(struct edgetpu_usage_stats *ustats)
{
unsigned int bkt;
struct uid_entry *uid_entry;
struct hlist_node *tmp;
mutex_lock(&ustats->usage_stats_lock);
hash_for_each_safe(ustats->uid_hash_table, bkt, tmp, uid_entry, node) {
hash_del(&uid_entry->node);
kfree(uid_entry);
}
mutex_unlock(&ustats->usage_stats_lock);
}
/* Write to clear all entries in uid_hash_table */
static ssize_t tpu_usage_clear(struct device *dev,
struct device_attribute *attr,
const char *buf,
size_t count)
{
struct edgetpu_dev *etdev = dev_get_drvdata(dev);
struct edgetpu_usage_stats *ustats = etdev->usage_stats;
usage_stats_remove_uids(ustats);
return count;
}
static DEVICE_ATTR(tpu_usage, 0664, tpu_usage_show, tpu_usage_clear);
static ssize_t device_utilization_show(struct device *dev,
struct device_attribute *attr,
char *buf)
{
struct edgetpu_dev *etdev = dev_get_drvdata(dev);
int32_t val;
val = edgetpu_usage_get_utilization(
etdev, EDGETPU_USAGE_COMPONENT_DEVICE);
return scnprintf(buf, PAGE_SIZE, "%d\n", val);
}
static DEVICE_ATTR_RO(device_utilization);
static ssize_t tpu_utilization_show(struct device *dev,
struct device_attribute *attr,
char *buf)
{
struct edgetpu_dev *etdev = dev_get_drvdata(dev);
int32_t val;
val = edgetpu_usage_get_utilization(
etdev, EDGETPU_USAGE_COMPONENT_TPU);
return scnprintf(buf, PAGE_SIZE, "%d\n", val);
}
static DEVICE_ATTR_RO(tpu_utilization);
static ssize_t tpu_active_cycle_count_show(struct device *dev,
struct device_attribute *attr,
char *buf)
{
struct edgetpu_dev *etdev = dev_get_drvdata(dev);
int64_t val;
val = edgetpu_usage_get_counter(etdev,
EDGETPU_COUNTER_TPU_ACTIVE_CYCLES);
return scnprintf(buf, PAGE_SIZE, "%llu\n", val);
}
static ssize_t tpu_active_cycle_count_store(struct device *dev,
struct device_attribute *attr,
const char *buf,
size_t count)
{
struct edgetpu_dev *etdev = dev_get_drvdata(dev);
edgetpu_counter_clear(etdev, EDGETPU_COUNTER_TPU_ACTIVE_CYCLES);
return count;
}
static DEVICE_ATTR(tpu_active_cycle_count, 0664, tpu_active_cycle_count_show,
tpu_active_cycle_count_store);
static ssize_t tpu_throttle_stall_count_show(struct device *dev,
struct device_attribute *attr,
char *buf)
{
struct edgetpu_dev *etdev = dev_get_drvdata(dev);
int64_t val;
val = edgetpu_usage_get_counter(etdev,
EDGETPU_COUNTER_TPU_THROTTLE_STALLS);
return scnprintf(buf, PAGE_SIZE, "%llu\n", val);
}
static ssize_t tpu_throttle_stall_count_store(struct device *dev,
struct device_attribute *attr,
const char *buf,
size_t count)
{
struct edgetpu_dev *etdev = dev_get_drvdata(dev);
edgetpu_counter_clear(etdev, EDGETPU_COUNTER_TPU_THROTTLE_STALLS);
return count;
}
static DEVICE_ATTR(tpu_throttle_stall_count, 0664,
tpu_throttle_stall_count_show,
tpu_throttle_stall_count_store);
static ssize_t inference_count_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct edgetpu_dev *etdev = dev_get_drvdata(dev);
int64_t val;
val = edgetpu_usage_get_counter(etdev,
EDGETPU_COUNTER_INFERENCES);
return scnprintf(buf, PAGE_SIZE, "%llu\n", val);
}
static ssize_t inference_count_store(struct device *dev,
struct device_attribute *attr,
const char *buf,
size_t count)
{
struct edgetpu_dev *etdev = dev_get_drvdata(dev);
edgetpu_counter_clear(etdev, EDGETPU_COUNTER_INFERENCES);
return count;
}
static DEVICE_ATTR(inference_count, 0664, inference_count_show,
inference_count_store);
static ssize_t tpu_op_count_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct edgetpu_dev *etdev = dev_get_drvdata(dev);
int64_t val;
val = edgetpu_usage_get_counter(etdev,
EDGETPU_COUNTER_TPU_OPS);
return scnprintf(buf, PAGE_SIZE, "%llu\n", val);
}
static ssize_t tpu_op_count_store(struct device *dev,
struct device_attribute *attr,
const char *buf,
size_t count)
{
struct edgetpu_dev *etdev = dev_get_drvdata(dev);
edgetpu_counter_clear(etdev, EDGETPU_COUNTER_TPU_OPS);
return count;
}
static DEVICE_ATTR(tpu_op_count, 0664, tpu_op_count_show, tpu_op_count_store);
static ssize_t param_cache_hit_count_show(struct device *dev,
struct device_attribute *attr,
char *buf)
{
struct edgetpu_dev *etdev = dev_get_drvdata(dev);
int64_t val;
val = edgetpu_usage_get_counter(etdev,
EDGETPU_COUNTER_PARAM_CACHE_HITS);
return scnprintf(buf, PAGE_SIZE, "%llu\n", val);
}
static ssize_t param_cache_hit_count_store(struct device *dev,
struct device_attribute *attr,
const char *buf,
size_t count)
{
struct edgetpu_dev *etdev = dev_get_drvdata(dev);
edgetpu_counter_clear(etdev, EDGETPU_COUNTER_PARAM_CACHE_HITS);
return count;
}
static DEVICE_ATTR(param_cache_hit_count, 0664, param_cache_hit_count_show,
param_cache_hit_count_store);
static ssize_t param_cache_miss_count_show(struct device *dev,
struct device_attribute *attr,
char *buf)
{
struct edgetpu_dev *etdev = dev_get_drvdata(dev);
int64_t val;
val = edgetpu_usage_get_counter(etdev,
EDGETPU_COUNTER_PARAM_CACHE_MISSES);
return scnprintf(buf, PAGE_SIZE, "%llu\n", val);
}
static ssize_t param_cache_miss_count_store(struct device *dev,
struct device_attribute *attr,
const char *buf,
size_t count)
{
struct edgetpu_dev *etdev = dev_get_drvdata(dev);
edgetpu_counter_clear(etdev, EDGETPU_COUNTER_PARAM_CACHE_MISSES);
return count;
}
static DEVICE_ATTR(param_cache_miss_count, 0664, param_cache_miss_count_show,
param_cache_miss_count_store);
static ssize_t context_preempt_count_show(struct device *dev,
struct device_attribute *attr,
char *buf)
{
struct edgetpu_dev *etdev = dev_get_drvdata(dev);
int64_t val;
val = edgetpu_usage_get_counter(etdev,
EDGETPU_COUNTER_CONTEXT_PREEMPTS);
return scnprintf(buf, PAGE_SIZE, "%llu\n", val);
}
static ssize_t context_preempt_count_store(struct device *dev,
struct device_attribute *attr,
const char *buf,
size_t count)
{
struct edgetpu_dev *etdev = dev_get_drvdata(dev);
edgetpu_counter_clear(etdev, EDGETPU_COUNTER_CONTEXT_PREEMPTS);
return count;
}
static DEVICE_ATTR(context_preempt_count, 0664, context_preempt_count_show,
context_preempt_count_store);
static ssize_t hardware_preempt_count_show(struct device *dev, struct device_attribute *attr,
char *buf)
{
struct edgetpu_dev *etdev = dev_get_drvdata(dev);
int64_t val;
val = edgetpu_usage_get_counter(etdev, EDGETPU_COUNTER_HARDWARE_PREEMPTS);
return scnprintf(buf, PAGE_SIZE, "%llu\n", val);
}
static ssize_t hardware_preempt_count_store(struct device *dev, struct device_attribute *attr,
const char *buf, size_t count)
{
struct edgetpu_dev *etdev = dev_get_drvdata(dev);
edgetpu_counter_clear(etdev, EDGETPU_COUNTER_HARDWARE_PREEMPTS);
return count;
}
static DEVICE_ATTR(hardware_preempt_count, 0664, hardware_preempt_count_show,
hardware_preempt_count_store);
static ssize_t hardware_ctx_save_time_show(struct device *dev, struct device_attribute *attr,
char *buf)
{
struct edgetpu_dev *etdev = dev_get_drvdata(dev);
int64_t val;
val = edgetpu_usage_get_counter(etdev, EDGETPU_COUNTER_HARDWARE_CTX_SAVE_TIME_US);
return scnprintf(buf, PAGE_SIZE, "%llu\n", val);
}
static ssize_t hardware_ctx_save_time_store(struct device *dev, struct device_attribute *attr,
const char *buf, size_t count)
{
struct edgetpu_dev *etdev = dev_get_drvdata(dev);
edgetpu_counter_clear(etdev, EDGETPU_COUNTER_HARDWARE_CTX_SAVE_TIME_US);
return count;
}
static DEVICE_ATTR(hardware_ctx_save_time, 0664, hardware_ctx_save_time_show,
hardware_ctx_save_time_store);
static ssize_t scalar_fence_wait_time_show(struct device *dev, struct device_attribute *attr,
char *buf)
{
struct edgetpu_dev *etdev = dev_get_drvdata(dev);
int64_t val;
val = edgetpu_usage_get_counter(etdev, EDGETPU_COUNTER_SCALAR_FENCE_WAIT_TIME_US);
return scnprintf(buf, PAGE_SIZE, "%llu\n", val);
}
static ssize_t scalar_fence_wait_time_store(struct device *dev, struct device_attribute *attr,
const char *buf, size_t count)
{
struct edgetpu_dev *etdev = dev_get_drvdata(dev);
edgetpu_counter_clear(etdev, EDGETPU_COUNTER_SCALAR_FENCE_WAIT_TIME_US);
return count;
}
static DEVICE_ATTR(scalar_fence_wait_time, 0664, scalar_fence_wait_time_show,
scalar_fence_wait_time_store);
static ssize_t long_suspend_count_show(struct device *dev, struct device_attribute *attr,
char *buf)
{
struct edgetpu_dev *etdev = dev_get_drvdata(dev);
int64_t val;
val = edgetpu_usage_get_counter(etdev, EDGETPU_COUNTER_LONG_SUSPEND);
return scnprintf(buf, PAGE_SIZE, "%llu\n", val);
}
static ssize_t long_suspend_count_store(struct device *dev, struct device_attribute *attr,
const char *buf, size_t count)
{
struct edgetpu_dev *etdev = dev_get_drvdata(dev);
edgetpu_counter_clear(etdev, EDGETPU_COUNTER_LONG_SUSPEND);
return count;
}
static DEVICE_ATTR(long_suspend_count, 0664, long_suspend_count_show,
long_suspend_count_store);
static ssize_t outstanding_commands_max_show(
struct device *dev, struct device_attribute *attr, char *buf)
{
struct edgetpu_dev *etdev = dev_get_drvdata(dev);
int64_t val;
val = edgetpu_usage_get_max_watermark(
etdev, EDGETPU_MAX_WATERMARK_OUT_CMDS);
return scnprintf(buf, PAGE_SIZE, "%llu\n", val);
}
static ssize_t outstanding_commands_max_store(
struct device *dev, struct device_attribute *attr,
const char *buf, size_t count)
{
struct edgetpu_dev *etdev = dev_get_drvdata(dev);
struct edgetpu_usage_stats *ustats = etdev->usage_stats;
if (ustats) {
mutex_lock(&ustats->usage_stats_lock);
ustats->max_watermark[EDGETPU_MAX_WATERMARK_OUT_CMDS] = 0;
mutex_unlock(&ustats->usage_stats_lock);
}
return count;
}
static DEVICE_ATTR(outstanding_commands_max, 0664,
outstanding_commands_max_show,
outstanding_commands_max_store);
static ssize_t preempt_depth_max_show(
struct device *dev, struct device_attribute *attr, char *buf)
{
struct edgetpu_dev *etdev = dev_get_drvdata(dev);
int64_t val;
val = edgetpu_usage_get_max_watermark(
etdev, EDGETPU_MAX_WATERMARK_PREEMPT_DEPTH);
return scnprintf(buf, PAGE_SIZE, "%llu\n", val);
}
static ssize_t preempt_depth_max_store(
struct device *dev, struct device_attribute *attr,
const char *buf, size_t count)
{
struct edgetpu_dev *etdev = dev_get_drvdata(dev);
struct edgetpu_usage_stats *ustats = etdev->usage_stats;
if (ustats) {
mutex_lock(&ustats->usage_stats_lock);
ustats->max_watermark[EDGETPU_MAX_WATERMARK_PREEMPT_DEPTH] = 0;
mutex_unlock(&ustats->usage_stats_lock);
}
return count;
}
static DEVICE_ATTR(preempt_depth_max, 0664, preempt_depth_max_show,
preempt_depth_max_store);
static ssize_t hardware_ctx_save_time_max_show(
struct device *dev, struct device_attribute *attr, char *buf)
{
struct edgetpu_dev *etdev = dev_get_drvdata(dev);
int64_t val;
val = edgetpu_usage_get_max_watermark(
etdev, EDGETPU_MAX_WATERMARK_HARDWARE_CTX_SAVE_TIME_US);
return scnprintf(buf, PAGE_SIZE, "%llu\n", val);
}
static ssize_t hardware_ctx_save_time_max_store(
struct device *dev, struct device_attribute *attr,
const char *buf, size_t count)
{
struct edgetpu_dev *etdev = dev_get_drvdata(dev);
struct edgetpu_usage_stats *ustats = etdev->usage_stats;
if (ustats) {
mutex_lock(&ustats->usage_stats_lock);
ustats->max_watermark[EDGETPU_MAX_WATERMARK_HARDWARE_CTX_SAVE_TIME_US] = 0;
mutex_unlock(&ustats->usage_stats_lock);
}
return count;
}
static DEVICE_ATTR(hardware_ctx_save_time_max, 0664, hardware_ctx_save_time_max_show,
hardware_ctx_save_time_max_store);
static ssize_t scalar_fence_wait_time_max_show(
struct device *dev, struct device_attribute *attr, char *buf)
{
struct edgetpu_dev *etdev = dev_get_drvdata(dev);
int64_t val;
val = edgetpu_usage_get_max_watermark(
etdev, EDGETPU_MAX_WATERMARK_SCALAR_FENCE_WAIT_TIME_US);
return scnprintf(buf, PAGE_SIZE, "%llu\n", val);
}
static ssize_t scalar_fence_wait_time_max_store(
struct device *dev, struct device_attribute *attr,
const char *buf, size_t count)
{
struct edgetpu_dev *etdev = dev_get_drvdata(dev);
struct edgetpu_usage_stats *ustats = etdev->usage_stats;
if (ustats) {
mutex_lock(&ustats->usage_stats_lock);
ustats->max_watermark[EDGETPU_MAX_WATERMARK_SCALAR_FENCE_WAIT_TIME_US] = 0;
mutex_unlock(&ustats->usage_stats_lock);
}
return count;
}
static DEVICE_ATTR(scalar_fence_wait_time_max, 0664, scalar_fence_wait_time_max_show,
scalar_fence_wait_time_max_store);
static ssize_t suspend_time_max_show(
struct device *dev, struct device_attribute *attr, char *buf)
{
struct edgetpu_dev *etdev = dev_get_drvdata(dev);
int64_t val;
val = edgetpu_usage_get_max_watermark(
etdev, EDGETPU_MAX_WATERMARK_SUSPEND_TIME_US);
return scnprintf(buf, PAGE_SIZE, "%llu\n", val);
}
static ssize_t suspend_time_max_store(
struct device *dev, struct device_attribute *attr,
const char *buf, size_t count)
{
struct edgetpu_dev *etdev = dev_get_drvdata(dev);
struct edgetpu_usage_stats *ustats = etdev->usage_stats;
if (ustats) {
mutex_lock(&ustats->usage_stats_lock);
ustats->max_watermark[EDGETPU_MAX_WATERMARK_SUSPEND_TIME_US] = 0;
mutex_unlock(&ustats->usage_stats_lock);
}
return count;
}
static DEVICE_ATTR(suspend_time_max, 0664, suspend_time_max_show,
suspend_time_max_store);
static ssize_t fw_thread_stats_show(
struct device *dev, struct device_attribute *attr, char *buf)
{
struct edgetpu_dev *etdev = dev_get_drvdata(dev);
struct edgetpu_usage_stats *ustats = etdev->usage_stats;
int i;
ssize_t ret = 0;
edgetpu_kci_update_usage(etdev);
mutex_lock(&ustats->usage_stats_lock);
for (i = 0; i < EDGETPU_FW_THREAD_COUNT; i++) {
if (!ustats->thread_stack_max[i])
continue;
ret += scnprintf(buf + ret, PAGE_SIZE - ret,
"%u\t%u\n", i, ustats->thread_stack_max[i]);
/* Not checking ret < PAGE_SIZE is intended. */
}
mutex_unlock(&ustats->usage_stats_lock);
return ret;
}
static ssize_t fw_thread_stats_store(
struct device *dev, struct device_attribute *attr,
const char *buf, size_t count)
{
struct edgetpu_dev *etdev = dev_get_drvdata(dev);
struct edgetpu_usage_stats *ustats = etdev->usage_stats;
int i;
mutex_lock(&ustats->usage_stats_lock);
for (i = 0; i < EDGETPU_FW_THREAD_COUNT; i++)
ustats->thread_stack_max[i] = 0;
mutex_unlock(&ustats->usage_stats_lock);
return count;
}
static DEVICE_ATTR(fw_thread_stats, 0664, fw_thread_stats_show,
fw_thread_stats_store);
static struct attribute *usage_stats_dev_attrs[] = {
&dev_attr_tpu_usage.attr,
&dev_attr_device_utilization.attr,
&dev_attr_tpu_utilization.attr,
&dev_attr_tpu_active_cycle_count.attr,
&dev_attr_tpu_throttle_stall_count.attr,
&dev_attr_inference_count.attr,
&dev_attr_tpu_op_count.attr,
&dev_attr_param_cache_hit_count.attr,
&dev_attr_param_cache_miss_count.attr,
&dev_attr_context_preempt_count.attr,
&dev_attr_hardware_preempt_count.attr,
&dev_attr_hardware_ctx_save_time.attr,
&dev_attr_scalar_fence_wait_time.attr,
&dev_attr_long_suspend_count.attr,
&dev_attr_outstanding_commands_max.attr,
&dev_attr_preempt_depth_max.attr,
&dev_attr_hardware_ctx_save_time_max.attr,
&dev_attr_scalar_fence_wait_time_max.attr,
&dev_attr_suspend_time_max.attr,
&dev_attr_fw_thread_stats.attr,
NULL,
};
static const struct attribute_group usage_stats_attr_group = {
.attrs = usage_stats_dev_attrs,
};
void edgetpu_usage_stats_init(struct edgetpu_dev *etdev)
{
struct edgetpu_usage_stats *ustats;
int ret;
ustats = devm_kzalloc(etdev->dev, sizeof(*etdev->usage_stats),
GFP_KERNEL);
if (!ustats) {
etdev_warn(etdev,
"failed to allocate memory for usage stats\n");
return;
}
hash_init(ustats->uid_hash_table);
mutex_init(&ustats->usage_stats_lock);
etdev->usage_stats = ustats;
ret = device_add_group(etdev->dev, &usage_stats_attr_group);
if (ret)
etdev_warn(etdev, "failed to create the usage_stats attrs\n");
etdev_dbg(etdev, "%s init\n", __func__);
}
void edgetpu_usage_stats_exit(struct edgetpu_dev *etdev)
{
struct edgetpu_usage_stats *ustats = etdev->usage_stats;
if (ustats) {
usage_stats_remove_uids(ustats);
device_remove_group(etdev->dev, &usage_stats_attr_group);
/* free the frequency table if allocated */
mutex_lock(&etdev->freq_lock);
if (etdev->freq_table)
kvfree(etdev->freq_table);
etdev->freq_table = NULL;
etdev->freq_count = 0;
mutex_unlock(&etdev->freq_lock);
}
etdev_dbg(etdev, "%s exit\n", __func__);
}