/* Copyright (c) 2012-2013, The Linux Foundation. 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 #include #include #include #include #include "wfd-util.h" static struct dentry *wfd_debugfs_root; int wfd_stats_setup() { wfd_debugfs_root = debugfs_create_dir("wfd", NULL); if (wfd_debugfs_root == ERR_PTR(-ENODEV)) return -ENODEV; else if (!wfd_debugfs_root) return -ENOMEM; else return 0; } void wfd_stats_teardown() { if (wfd_debugfs_root) debugfs_remove_recursive(wfd_debugfs_root); } int wfd_stats_init(struct wfd_stats *stats, int device) { char device_str[NAME_MAX] = ""; int rc = 0; if (!stats) { rc = -EINVAL; goto wfd_stats_init_fail; } else if (!wfd_debugfs_root) { WFD_MSG_ERR("wfd debugfs root does not exist\n"); rc = -ENOENT; goto wfd_stats_init_fail; } memset(stats, 0, sizeof(*stats)); INIT_LIST_HEAD(&stats->enc_queue); mutex_init(&stats->mutex); snprintf(device_str, sizeof(device_str), "%d", device); stats->d_parent = debugfs_create_dir(device_str, wfd_debugfs_root); if (IS_ERR(stats->d_parent)) { rc = PTR_ERR(stats->d_parent); stats->d_parent = NULL; goto wfd_stats_init_fail; } stats->d_v4l2_buf_count = debugfs_create_u32("v4l2_buf_count", S_IRUGO, stats->d_parent, &stats->v4l2_buf_count); if (IS_ERR(stats->d_v4l2_buf_count)) { rc = PTR_ERR(stats->d_v4l2_buf_count); stats->d_v4l2_buf_count = NULL; goto wfd_stats_init_fail; } stats->d_mdp_buf_count = debugfs_create_u32("mdp_buf_count", S_IRUGO, stats->d_parent, &stats->mdp_buf_count); if (IS_ERR(stats->d_mdp_buf_count)) { rc = PTR_ERR(stats->d_mdp_buf_count); stats->d_mdp_buf_count = NULL; goto wfd_stats_init_fail; } stats->d_vsg_buf_count = debugfs_create_u32("vsg_buf_count", S_IRUGO, stats->d_parent, &stats->vsg_buf_count); if (IS_ERR(stats->d_vsg_buf_count)) { rc = PTR_ERR(stats->d_vsg_buf_count); stats->d_vsg_buf_count = NULL; goto wfd_stats_init_fail; } stats->d_enc_buf_count = debugfs_create_u32("enc_buf_count", S_IRUGO, stats->d_parent, &stats->enc_buf_count); if (IS_ERR(stats->d_enc_buf_count)) { rc = PTR_ERR(stats->d_enc_buf_count); stats->d_enc_buf_count = NULL; goto wfd_stats_init_fail; } stats->d_frames_encoded = debugfs_create_u32("frames_encoded", S_IRUGO, stats->d_parent, &stats->frames_encoded); if (IS_ERR(stats->d_frames_encoded)) { rc = PTR_ERR(stats->d_frames_encoded); stats->d_frames_encoded = NULL; goto wfd_stats_init_fail; } stats->d_mdp_updates = debugfs_create_u32("mdp_updates", S_IRUGO, stats->d_parent, &stats->mdp_updates); if (IS_ERR(stats->d_mdp_updates)) { rc = PTR_ERR(stats->d_mdp_updates); stats->d_mdp_updates = NULL; goto wfd_stats_init_fail; } stats->d_enc_avg_latency = debugfs_create_u32("enc_avg_latency", S_IRUGO, stats->d_parent, &stats->enc_avg_latency); if (IS_ERR(stats->d_enc_avg_latency)) { rc = PTR_ERR(stats->d_enc_avg_latency); stats->d_enc_avg_latency = NULL; goto wfd_stats_init_fail; } return rc; wfd_stats_init_fail: return rc; } int wfd_stats_update(struct wfd_stats *stats, enum wfd_stats_event event) { int rc = 0; mutex_lock(&stats->mutex); switch (event) { case WFD_STAT_EVENT_CLIENT_QUEUE: stats->v4l2_buf_count++; break; case WFD_STAT_EVENT_CLIENT_DEQUEUE: { struct wfd_stats_encode_sample *sample = NULL; stats->v4l2_buf_count--; if (!list_empty(&stats->enc_queue)) sample = list_first_entry(&stats->enc_queue, struct wfd_stats_encode_sample, list); if (sample) { ktime_t kdiff = ktime_sub(ktime_get(), sample->encode_start_ts); uint32_t diff = ktime_to_ms(kdiff); stats->enc_cumulative_latency += diff; stats->enc_latency_samples++; stats->enc_avg_latency = stats->enc_cumulative_latency / stats->enc_latency_samples; list_del(&sample->list); kfree(sample); sample = NULL; } break; } case WFD_STAT_EVENT_MDP_QUEUE: stats->mdp_buf_count++; break; case WFD_STAT_EVENT_MDP_DEQUEUE: stats->mdp_buf_count--; stats->mdp_updates++; break; case WFD_STAT_EVENT_ENC_QUEUE: { struct wfd_stats_encode_sample *sample = NULL; stats->enc_buf_count++; stats->frames_encoded++; sample = kzalloc(sizeof(*sample), GFP_KERNEL); if (sample) { INIT_LIST_HEAD(&sample->list); sample->encode_start_ts = ktime_get(); list_add_tail(&sample->list, &stats->enc_queue); } else { WFD_MSG_WARN("Unable to measure latency\n"); } break; } case WFD_STAT_EVENT_ENC_DEQUEUE: stats->enc_buf_count--; break; case WFD_STAT_EVENT_VSG_QUEUE: stats->vsg_buf_count++; break; case WFD_STAT_EVENT_VSG_DEQUEUE: stats->vsg_buf_count--; break; default: rc = -ENOTSUPP; } mutex_unlock(&stats->mutex); return rc; } int wfd_stats_deinit(struct wfd_stats *stats) { WFD_MSG_DBG("Latencies: avg enc. latency %d", stats->enc_avg_latency); /* Delete all debugfs files in one shot :) */ if (stats->d_parent) debugfs_remove_recursive(stats->d_parent); stats->d_parent = stats->d_v4l2_buf_count = stats->d_mdp_buf_count = stats->d_vsg_buf_count = stats->d_enc_buf_count = stats->d_frames_encoded = stats->d_mdp_updates = stats->d_enc_avg_latency = NULL; return 0; }