Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0
0002 /*
0003  * Infrastructure for statistic tracing (histogram output).
0004  *
0005  * Copyright (C) 2008-2009 Frederic Weisbecker <fweisbec@gmail.com>
0006  *
0007  * Based on the code from trace_branch.c which is
0008  * Copyright (C) 2008 Steven Rostedt <srostedt@redhat.com>
0009  *
0010  */
0011 
0012 #include <linux/security.h>
0013 #include <linux/list.h>
0014 #include <linux/slab.h>
0015 #include <linux/rbtree.h>
0016 #include <linux/tracefs.h>
0017 #include "trace_stat.h"
0018 #include "trace.h"
0019 
0020 
0021 /*
0022  * List of stat red-black nodes from a tracer
0023  * We use a such tree to sort quickly the stat
0024  * entries from the tracer.
0025  */
0026 struct stat_node {
0027     struct rb_node      node;
0028     void            *stat;
0029 };
0030 
0031 /* A stat session is the stats output in one file */
0032 struct stat_session {
0033     struct list_head    session_list;
0034     struct tracer_stat  *ts;
0035     struct rb_root      stat_root;
0036     struct mutex        stat_mutex;
0037     struct dentry       *file;
0038 };
0039 
0040 /* All of the sessions currently in use. Each stat file embed one session */
0041 static LIST_HEAD(all_stat_sessions);
0042 static DEFINE_MUTEX(all_stat_sessions_mutex);
0043 
0044 /* The root directory for all stat files */
0045 static struct dentry        *stat_dir;
0046 
0047 static void __reset_stat_session(struct stat_session *session)
0048 {
0049     struct stat_node *snode, *n;
0050 
0051     rbtree_postorder_for_each_entry_safe(snode, n, &session->stat_root, node) {
0052         if (session->ts->stat_release)
0053             session->ts->stat_release(snode->stat);
0054         kfree(snode);
0055     }
0056 
0057     session->stat_root = RB_ROOT;
0058 }
0059 
0060 static void reset_stat_session(struct stat_session *session)
0061 {
0062     mutex_lock(&session->stat_mutex);
0063     __reset_stat_session(session);
0064     mutex_unlock(&session->stat_mutex);
0065 }
0066 
0067 static void destroy_session(struct stat_session *session)
0068 {
0069     tracefs_remove(session->file);
0070     __reset_stat_session(session);
0071     mutex_destroy(&session->stat_mutex);
0072     kfree(session);
0073 }
0074 
0075 static int insert_stat(struct rb_root *root, void *stat, cmp_func_t cmp)
0076 {
0077     struct rb_node **new = &(root->rb_node), *parent = NULL;
0078     struct stat_node *data;
0079 
0080     data = kzalloc(sizeof(*data), GFP_KERNEL);
0081     if (!data)
0082         return -ENOMEM;
0083     data->stat = stat;
0084 
0085     /*
0086      * Figure out where to put new node
0087      * This is a descendent sorting
0088      */
0089     while (*new) {
0090         struct stat_node *this;
0091         int result;
0092 
0093         this = container_of(*new, struct stat_node, node);
0094         result = cmp(data->stat, this->stat);
0095 
0096         parent = *new;
0097         if (result >= 0)
0098             new = &((*new)->rb_left);
0099         else
0100             new = &((*new)->rb_right);
0101     }
0102 
0103     rb_link_node(&data->node, parent, new);
0104     rb_insert_color(&data->node, root);
0105     return 0;
0106 }
0107 
0108 /*
0109  * For tracers that don't provide a stat_cmp callback.
0110  * This one will force an insertion as right-most node
0111  * in the rbtree.
0112  */
0113 static int dummy_cmp(const void *p1, const void *p2)
0114 {
0115     return -1;
0116 }
0117 
0118 /*
0119  * Initialize the stat rbtree at each trace_stat file opening.
0120  * All of these copies and sorting are required on all opening
0121  * since the stats could have changed between two file sessions.
0122  */
0123 static int stat_seq_init(struct stat_session *session)
0124 {
0125     struct tracer_stat *ts = session->ts;
0126     struct rb_root *root = &session->stat_root;
0127     void *stat;
0128     int ret = 0;
0129     int i;
0130 
0131     mutex_lock(&session->stat_mutex);
0132     __reset_stat_session(session);
0133 
0134     if (!ts->stat_cmp)
0135         ts->stat_cmp = dummy_cmp;
0136 
0137     stat = ts->stat_start(ts);
0138     if (!stat)
0139         goto exit;
0140 
0141     ret = insert_stat(root, stat, ts->stat_cmp);
0142     if (ret)
0143         goto exit;
0144 
0145     /*
0146      * Iterate over the tracer stat entries and store them in an rbtree.
0147      */
0148     for (i = 1; ; i++) {
0149         stat = ts->stat_next(stat, i);
0150 
0151         /* End of insertion */
0152         if (!stat)
0153             break;
0154 
0155         ret = insert_stat(root, stat, ts->stat_cmp);
0156         if (ret)
0157             goto exit_free_rbtree;
0158     }
0159 
0160 exit:
0161     mutex_unlock(&session->stat_mutex);
0162     return ret;
0163 
0164 exit_free_rbtree:
0165     __reset_stat_session(session);
0166     mutex_unlock(&session->stat_mutex);
0167     return ret;
0168 }
0169 
0170 
0171 static void *stat_seq_start(struct seq_file *s, loff_t *pos)
0172 {
0173     struct stat_session *session = s->private;
0174     struct rb_node *node;
0175     int n = *pos;
0176     int i;
0177 
0178     /* Prevent from tracer switch or rbtree modification */
0179     mutex_lock(&session->stat_mutex);
0180 
0181     /* If we are in the beginning of the file, print the headers */
0182     if (session->ts->stat_headers) {
0183         if (n == 0)
0184             return SEQ_START_TOKEN;
0185         n--;
0186     }
0187 
0188     node = rb_first(&session->stat_root);
0189     for (i = 0; node && i < n; i++)
0190         node = rb_next(node);
0191 
0192     return node;
0193 }
0194 
0195 static void *stat_seq_next(struct seq_file *s, void *p, loff_t *pos)
0196 {
0197     struct stat_session *session = s->private;
0198     struct rb_node *node = p;
0199 
0200     (*pos)++;
0201 
0202     if (p == SEQ_START_TOKEN)
0203         return rb_first(&session->stat_root);
0204 
0205     return rb_next(node);
0206 }
0207 
0208 static void stat_seq_stop(struct seq_file *s, void *p)
0209 {
0210     struct stat_session *session = s->private;
0211     mutex_unlock(&session->stat_mutex);
0212 }
0213 
0214 static int stat_seq_show(struct seq_file *s, void *v)
0215 {
0216     struct stat_session *session = s->private;
0217     struct stat_node *l = container_of(v, struct stat_node, node);
0218 
0219     if (v == SEQ_START_TOKEN)
0220         return session->ts->stat_headers(s);
0221 
0222     return session->ts->stat_show(s, l->stat);
0223 }
0224 
0225 static const struct seq_operations trace_stat_seq_ops = {
0226     .start      = stat_seq_start,
0227     .next       = stat_seq_next,
0228     .stop       = stat_seq_stop,
0229     .show       = stat_seq_show
0230 };
0231 
0232 /* The session stat is refilled and resorted at each stat file opening */
0233 static int tracing_stat_open(struct inode *inode, struct file *file)
0234 {
0235     int ret;
0236     struct seq_file *m;
0237     struct stat_session *session = inode->i_private;
0238 
0239     ret = security_locked_down(LOCKDOWN_TRACEFS);
0240     if (ret)
0241         return ret;
0242 
0243     ret = stat_seq_init(session);
0244     if (ret)
0245         return ret;
0246 
0247     ret = seq_open(file, &trace_stat_seq_ops);
0248     if (ret) {
0249         reset_stat_session(session);
0250         return ret;
0251     }
0252 
0253     m = file->private_data;
0254     m->private = session;
0255     return ret;
0256 }
0257 
0258 /*
0259  * Avoid consuming memory with our now useless rbtree.
0260  */
0261 static int tracing_stat_release(struct inode *i, struct file *f)
0262 {
0263     struct stat_session *session = i->i_private;
0264 
0265     reset_stat_session(session);
0266 
0267     return seq_release(i, f);
0268 }
0269 
0270 static const struct file_operations tracing_stat_fops = {
0271     .open       = tracing_stat_open,
0272     .read       = seq_read,
0273     .llseek     = seq_lseek,
0274     .release    = tracing_stat_release
0275 };
0276 
0277 static int tracing_stat_init(void)
0278 {
0279     int ret;
0280 
0281     ret = tracing_init_dentry();
0282     if (ret)
0283         return -ENODEV;
0284 
0285     stat_dir = tracefs_create_dir("trace_stat", NULL);
0286     if (!stat_dir) {
0287         pr_warn("Could not create tracefs 'trace_stat' entry\n");
0288         return -ENOMEM;
0289     }
0290     return 0;
0291 }
0292 
0293 static int init_stat_file(struct stat_session *session)
0294 {
0295     int ret;
0296 
0297     if (!stat_dir && (ret = tracing_stat_init()))
0298         return ret;
0299 
0300     session->file = tracefs_create_file(session->ts->name, TRACE_MODE_WRITE,
0301                         stat_dir, session,
0302                         &tracing_stat_fops);
0303     if (!session->file)
0304         return -ENOMEM;
0305     return 0;
0306 }
0307 
0308 int register_stat_tracer(struct tracer_stat *trace)
0309 {
0310     struct stat_session *session, *node;
0311     int ret = -EINVAL;
0312 
0313     if (!trace)
0314         return -EINVAL;
0315 
0316     if (!trace->stat_start || !trace->stat_next || !trace->stat_show)
0317         return -EINVAL;
0318 
0319     /* Already registered? */
0320     mutex_lock(&all_stat_sessions_mutex);
0321     list_for_each_entry(node, &all_stat_sessions, session_list) {
0322         if (node->ts == trace)
0323             goto out;
0324     }
0325 
0326     ret = -ENOMEM;
0327     /* Init the session */
0328     session = kzalloc(sizeof(*session), GFP_KERNEL);
0329     if (!session)
0330         goto out;
0331 
0332     session->ts = trace;
0333     INIT_LIST_HEAD(&session->session_list);
0334     mutex_init(&session->stat_mutex);
0335 
0336     ret = init_stat_file(session);
0337     if (ret) {
0338         destroy_session(session);
0339         goto out;
0340     }
0341 
0342     ret = 0;
0343     /* Register */
0344     list_add_tail(&session->session_list, &all_stat_sessions);
0345  out:
0346     mutex_unlock(&all_stat_sessions_mutex);
0347 
0348     return ret;
0349 }
0350 
0351 void unregister_stat_tracer(struct tracer_stat *trace)
0352 {
0353     struct stat_session *node, *tmp;
0354 
0355     mutex_lock(&all_stat_sessions_mutex);
0356     list_for_each_entry_safe(node, tmp, &all_stat_sessions, session_list) {
0357         if (node->ts == trace) {
0358             list_del(&node->session_list);
0359             destroy_session(node);
0360             break;
0361         }
0362     }
0363     mutex_unlock(&all_stat_sessions_mutex);
0364 }