0001
0002
0003
0004
0005
0006
0007
0008
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
0023
0024
0025
0026 struct stat_node {
0027 struct rb_node node;
0028 void *stat;
0029 };
0030
0031
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
0041 static LIST_HEAD(all_stat_sessions);
0042 static DEFINE_MUTEX(all_stat_sessions_mutex);
0043
0044
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
0087
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
0110
0111
0112
0113 static int dummy_cmp(const void *p1, const void *p2)
0114 {
0115 return -1;
0116 }
0117
0118
0119
0120
0121
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
0147
0148 for (i = 1; ; i++) {
0149 stat = ts->stat_next(stat, i);
0150
0151
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
0179 mutex_lock(&session->stat_mutex);
0180
0181
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
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
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
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
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
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 }