| // 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" |
| |
| struct uid_entry { |
| int32_t uid; |
| uint64_t time_in_state[EDGETPU_NUM_STATES]; |
| struct hlist_node node; |
| }; |
| |
| static int tpu_state_map(uint32_t state) |
| { |
| int i; |
| |
| 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(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); |
| } |
| |
| 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; |
| 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:"); |
| |
| for (i = 0; i < EDGETPU_NUM_STATES; i++) |
| ret += scnprintf(buf + ret, PAGE_SIZE - ret, " %d", |
| edgetpu_states_display[i]); |
| |
| 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 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 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_outstanding_commands_max.attr, |
| &dev_attr_preempt_depth_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); |
| } |
| |
| etdev_dbg(etdev, "%s exit\n", __func__); |
| } |