Something went wrong. Try again.
Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
Something went wrong. Try again.
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389// SPDX-License-Identifier: GPL-2.0/* * Copyright (C) 2025 - Google LLC * Author: Vincent Donnefort <vdonnefort@google.com> */
#include <linux/kstrtox.h>#include <linux/lockdep.h>#include <linux/mutex.h>#include <linux/tracefs.h>#include <linux/trace_remote.h>#include <linux/trace_seq.h>#include <linux/types.h>
#include "trace.h"
#define TRACEFS_DIR "remotes"#define TRACEFS_MODE_WRITE 0640#define TRACEFS_MODE_READ 0440
enum tri_type { TRI_CONSUMING, TRI_NONCONSUMING,};
struct trace_remote_iterator { struct trace_remote *remote; struct trace_seq seq; struct delayed_work poll_work; unsigned long lost_events; u64 ts; struct ring_buffer_iter *rb_iter; struct ring_buffer_iter **rb_iters; struct remote_event_hdr *evt; int cpu; int evt_cpu; loff_t pos; enum tri_type type;};
struct trace_remote { struct trace_remote_callbacks *cbs; void *priv; struct trace_buffer *trace_buffer; struct trace_buffer_desc *trace_buffer_desc; struct dentry *dentry; struct eventfs_inode *eventfs; struct remote_event *events; unsigned long nr_events; unsigned long trace_buffer_size; struct ring_buffer_remote rb_remote; struct mutex lock; struct rw_semaphore reader_lock; struct rw_semaphore *pcpu_reader_locks; unsigned int nr_readers; unsigned int poll_ms; bool tracing_on;};
static bool trace_remote_loaded(struct trace_remote *remote){ return !!remote->trace_buffer;}
static int trace_remote_load(struct trace_remote *remote){ struct ring_buffer_remote *rb_remote = &remote->rb_remote; struct trace_buffer_desc *desc;
lockdep_assert_held(&remote->lock);
if (trace_remote_loaded(remote)) return 0;
desc = remote->cbs->load_trace_buffer(remote->trace_buffer_size, remote->priv); if (IS_ERR(desc)) return PTR_ERR(desc);
rb_remote->desc = desc; rb_remote->swap_reader_page = remote->cbs->swap_reader_page; rb_remote->priv = remote->priv; rb_remote->reset = remote->cbs->reset; remote->trace_buffer = ring_buffer_alloc_remote(rb_remote); if (!remote->trace_buffer) { remote->cbs->unload_trace_buffer(desc, remote->priv); return -ENOMEM; }
remote->trace_buffer_desc = desc;
return 0;}
static void trace_remote_try_unload(struct trace_remote *remote){ lockdep_assert_held(&remote->lock);
if (!trace_remote_loaded(remote)) return;
/* The buffer is being read or writable */ if (remote->nr_readers || remote->tracing_on) return;
/* The buffer has readable data */ if (!ring_buffer_empty(remote->trace_buffer)) return;
ring_buffer_free(remote->trace_buffer); remote->trace_buffer = NULL; remote->cbs->unload_trace_buffer(remote->trace_buffer_desc, remote->priv);}
static int trace_remote_enable_tracing(struct trace_remote *remote){ int ret;
lockdep_assert_held(&remote->lock);
if (remote->tracing_on) return 0;
ret = trace_remote_load(remote); if (ret) return ret;
ret = remote->cbs->enable_tracing(true, remote->priv); if (ret) { trace_remote_try_unload(remote); return ret; }
remote->tracing_on = true;
return 0;}
static int trace_remote_disable_tracing(struct trace_remote *remote){ int ret;
lockdep_assert_held(&remote->lock);
if (!remote->tracing_on) return 0;
ret = remote->cbs->enable_tracing(false, remote->priv); if (ret) return ret;
ring_buffer_poll_remote(remote->trace_buffer, RING_BUFFER_ALL_CPUS); remote->tracing_on = false; trace_remote_try_unload(remote);
return 0;}
static void trace_remote_reset(struct trace_remote *remote, int cpu){ lockdep_assert_held(&remote->lock);
if (!trace_remote_loaded(remote)) return;
if (cpu == RING_BUFFER_ALL_CPUS) ring_buffer_reset(remote->trace_buffer); else ring_buffer_reset_cpu(remote->trace_buffer, cpu);
trace_remote_try_unload(remote);}
static ssize_ttracing_on_write(struct file *filp, const char __user *ubuf, size_t cnt, loff_t *ppos){ struct seq_file *seq = filp->private_data; struct trace_remote *remote = seq->private; unsigned long val; int ret;
ret = kstrtoul_from_user(ubuf, cnt, 10, &val); if (ret) return ret;
guard(mutex)(&remote->lock);
ret = val ? trace_remote_enable_tracing(remote) : trace_remote_disable_tracing(remote); if (ret) return ret;
return cnt;}static int tracing_on_show(struct seq_file *s, void *unused){ struct trace_remote *remote = s->private;
seq_printf(s, "%d\n", remote->tracing_on);
return 0;}DEFINE_SHOW_STORE_ATTRIBUTE(tracing_on);
static ssize_t buffer_size_kb_write(struct file *filp, const char __user *ubuf, size_t cnt, loff_t *ppos){ struct seq_file *seq = filp->private_data; struct trace_remote *remote = seq->private; unsigned long val; int ret;
ret = kstrtoul_from_user(ubuf, cnt, 10, &val); if (ret) return ret;
/* KiB to Bytes */ if (!val || check_shl_overflow(val, 10, &val)) return -EINVAL;
guard(mutex)(&remote->lock);
if (trace_remote_loaded(remote)) return -EBUSY;
remote->trace_buffer_size = val;
return cnt;}
static int buffer_size_kb_show(struct seq_file *s, void *unused){ struct trace_remote *remote = s->private;
seq_printf(s, "%lu (%s)\n", remote->trace_buffer_size >> 10, trace_remote_loaded(remote) ? "loaded" : "unloaded");
return 0;}DEFINE_SHOW_STORE_ATTRIBUTE(buffer_size_kb);
static int trace_remote_get(struct trace_remote *remote, int cpu){ int ret;
if (remote->nr_readers == UINT_MAX) return -EBUSY;
ret = trace_remote_load(remote); if (ret) return ret;
if (cpu != RING_BUFFER_ALL_CPUS && !remote->pcpu_reader_locks) { int lock_cpu;
remote->pcpu_reader_locks = kcalloc(nr_cpu_ids, sizeof(*remote->pcpu_reader_locks), GFP_KERNEL); if (!remote->pcpu_reader_locks) { trace_remote_try_unload(remote); return -ENOMEM; }
for_each_possible_cpu(lock_cpu) init_rwsem(&remote->pcpu_reader_locks[lock_cpu]); }
remote->nr_readers++;
return 0;}
static void trace_remote_put(struct trace_remote *remote){ if (WARN_ON(!remote->nr_readers)) return;
remote->nr_readers--; if (remote->nr_readers) return;
kfree(remote->pcpu_reader_locks); remote->pcpu_reader_locks = NULL;
trace_remote_try_unload(remote);}
static bool trace_remote_has_cpu(struct trace_remote *remote, int cpu){ if (cpu == RING_BUFFER_ALL_CPUS) return true;
return ring_buffer_poll_remote(remote->trace_buffer, cpu) == 0;}
static void __poll_remote(struct work_struct *work){ struct delayed_work *dwork = to_delayed_work(work); struct trace_remote_iterator *iter;
iter = container_of(dwork, struct trace_remote_iterator, poll_work); ring_buffer_poll_remote(iter->remote->trace_buffer, iter->cpu); schedule_delayed_work((struct delayed_work *)work, msecs_to_jiffies(iter->remote->poll_ms));}
static void __free_ring_buffer_iter(struct trace_remote_iterator *iter, int cpu){ if (cpu != RING_BUFFER_ALL_CPUS) { ring_buffer_read_finish(iter->rb_iter); return; }
for_each_possible_cpu(cpu) { if (iter->rb_iters[cpu]) ring_buffer_read_finish(iter->rb_iters[cpu]); }
kfree(iter->rb_iters);}
static int __alloc_ring_buffer_iter(struct trace_remote_iterator *iter, int cpu){ if (cpu != RING_BUFFER_ALL_CPUS) { iter->rb_iter = ring_buffer_read_start(iter->remote->trace_buffer, cpu, GFP_KERNEL);
return iter->rb_iter ? 0 : -ENOMEM; }
iter->rb_iters = kcalloc(nr_cpu_ids, sizeof(*iter->rb_iters), GFP_KERNEL); if (!iter->rb_iters) return -ENOMEM;
for_each_possible_cpu(cpu) { iter->rb_iters[cpu] = ring_buffer_read_start(iter->remote->trace_buffer, cpu, GFP_KERNEL); if (!iter->rb_iters[cpu]) { /* This CPU isn't part of trace_buffer. Skip it */ if (!trace_remote_has_cpu(iter->remote, cpu)) continue;
__free_ring_buffer_iter(iter, RING_BUFFER_ALL_CPUS); return -ENOMEM; } }
return 0;}
static struct trace_remote_iterator*trace_remote_iter(struct trace_remote *remote, int cpu, enum tri_type type){ struct trace_remote_iterator *iter = NULL; int ret;
lockdep_assert_held(&remote->lock);
if (type == TRI_NONCONSUMING && !trace_remote_loaded(remote)) return NULL;
ret = trace_remote_get(remote, cpu); if (ret) return ERR_PTR(ret);
if (!trace_remote_has_cpu(remote, cpu)) { ret = -ENODEV; goto err; }
iter = kzalloc_obj(*iter); if (iter) { iter->remote = remote; iter->cpu = cpu; iter->type = type; trace_seq_init(&iter->seq);
switch (type) { case TRI_CONSUMING: ring_buffer_poll_remote(remote->trace_buffer, cpu); INIT_DELAYED_WORK(&iter->poll_work, __poll_remote); schedule_delayed_work(&iter->poll_work, msecs_to_jiffies(remote->poll_ms)); break; case TRI_NONCONSUMING: ret = __alloc_ring_buffer_iter(iter, cpu); break; }
if (ret) goto err;
return iter; } ret = -ENOMEM;
err: kfree(iter); trace_remote_put(remote);
return ERR_PTR(ret);}
static void trace_remote_iter_free(struct trace_remote_iterator *iter){ struct trace_remote *remote;
if (!iter) return;
remote = iter->remote;
lockdep_assert_held(&remote->lock);
switch (iter->type) { case TRI_CONSUMING: cancel_delayed_work_sync(&iter->poll_work); break; case TRI_NONCONSUMING: __free_ring_buffer_iter(iter, iter->cpu); break; }
kfree(iter); trace_remote_put(remote);}
static void trace_remote_iter_read_start(struct trace_remote_iterator *iter){ struct trace_remote *remote = iter->remote; int cpu = iter->cpu;
/* Acquire global reader lock */ if (cpu == RING_BUFFER_ALL_CPUS && iter->type == TRI_CONSUMING) down_write(&remote->reader_lock); else down_read(&remote->reader_lock);
if (cpu == RING_BUFFER_ALL_CPUS) return;
/* * No need for the remote lock here, iter holds a reference on * remote->nr_readers */
/* Get the per-CPU one */ if (WARN_ON_ONCE(!remote->pcpu_reader_locks)) return;
if (iter->type == TRI_CONSUMING) down_write(&remote->pcpu_reader_locks[cpu]); else down_read(&remote->pcpu_reader_locks[cpu]);}
static void trace_remote_iter_read_finished(struct trace_remote_iterator *iter){ struct trace_remote *remote = iter->remote; int cpu = iter->cpu;
/* Release per-CPU reader lock */ if (cpu != RING_BUFFER_ALL_CPUS) { /* * No need for the remote lock here, iter holds a reference on * remote->nr_readers */ if (iter->type == TRI_CONSUMING) up_write(&remote->pcpu_reader_locks[cpu]); else up_read(&remote->pcpu_reader_locks[cpu]); }
/* Release global reader lock */ if (cpu == RING_BUFFER_ALL_CPUS && iter->type == TRI_CONSUMING) up_write(&remote->reader_lock); else up_read(&remote->reader_lock);}
static struct ring_buffer_iter *__get_rb_iter(struct trace_remote_iterator *iter, int cpu){ return iter->cpu != RING_BUFFER_ALL_CPUS ? iter->rb_iter : iter->rb_iters[cpu];}
static struct ring_buffer_event *__peek_event(struct trace_remote_iterator *iter, int cpu, u64 *ts, unsigned long *lost_events){ struct ring_buffer_event *rb_evt; struct ring_buffer_iter *rb_iter;
switch (iter->type) { case TRI_CONSUMING: return ring_buffer_peek(iter->remote->trace_buffer, cpu, ts, lost_events); case TRI_NONCONSUMING: rb_iter = __get_rb_iter(iter, cpu); if (!rb_iter) return NULL;
rb_evt = ring_buffer_iter_peek(rb_iter, ts); if (!rb_evt) return NULL;
*lost_events = ring_buffer_iter_dropped(rb_iter);
return rb_evt; }
return NULL;}
static bool trace_remote_iter_read_event(struct trace_remote_iterator *iter){ struct trace_buffer *trace_buffer = iter->remote->trace_buffer; struct ring_buffer_event *rb_evt; int cpu = iter->cpu;
if (cpu != RING_BUFFER_ALL_CPUS) { if (ring_buffer_empty_cpu(trace_buffer, cpu)) return false;
rb_evt = __peek_event(iter, cpu, &iter->ts, &iter->lost_events); if (!rb_evt) return false;
iter->evt_cpu = cpu; iter->evt = ring_buffer_event_data(rb_evt); return true; }
iter->ts = U64_MAX; for_each_possible_cpu(cpu) { unsigned long lost_events; u64 ts;
if (ring_buffer_empty_cpu(trace_buffer, cpu)) continue;
rb_evt = __peek_event(iter, cpu, &ts, &lost_events); if (!rb_evt) continue;
if (ts >= iter->ts) continue;
iter->ts = ts; iter->evt_cpu = cpu; iter->evt = ring_buffer_event_data(rb_evt); iter->lost_events = lost_events; }
return iter->ts != U64_MAX;}
static void trace_remote_iter_move(struct trace_remote_iterator *iter){ struct trace_buffer *trace_buffer = iter->remote->trace_buffer;
switch (iter->type) { case TRI_CONSUMING: ring_buffer_consume(trace_buffer, iter->evt_cpu, NULL, NULL); break; case TRI_NONCONSUMING: ring_buffer_iter_advance(__get_rb_iter(iter, iter->evt_cpu)); break; }}
static struct remote_event *trace_remote_find_event(struct trace_remote *remote, unsigned short id);
static int trace_remote_iter_print_event(struct trace_remote_iterator *iter){ struct remote_event *evt; unsigned long usecs_rem; u64 ts = iter->ts;
if (iter->lost_events) trace_seq_printf(&iter->seq, "CPU:%d [LOST %lu EVENTS]\n", iter->evt_cpu, iter->lost_events);
do_div(ts, 1000); usecs_rem = do_div(ts, USEC_PER_SEC);
trace_seq_printf(&iter->seq, "[%03d]\t%5llu.%06lu: ", iter->evt_cpu, ts, usecs_rem);
evt = trace_remote_find_event(iter->remote, iter->evt->id); if (!evt) trace_seq_printf(&iter->seq, "UNKNOWN id=%d\n", iter->evt->id); else evt->print(iter->evt, &iter->seq);
return trace_seq_has_overflowed(&iter->seq) ? -EOVERFLOW : 0;}
static int trace_pipe_open(struct inode *inode, struct file *filp){ struct trace_remote *remote = inode->i_private; struct trace_remote_iterator *iter; int cpu = tracing_get_cpu(inode);
guard(mutex)(&remote->lock);
iter = trace_remote_iter(remote, cpu, TRI_CONSUMING); if (IS_ERR(iter)) return PTR_ERR(iter);
filp->private_data = iter;
return 0;}
static int trace_pipe_release(struct inode *inode, struct file *filp){ struct trace_remote_iterator *iter = filp->private_data; struct trace_remote *remote = iter->remote;
guard(mutex)(&remote->lock);
trace_remote_iter_free(iter);
return 0;}
static ssize_t trace_pipe_read(struct file *filp, char __user *ubuf, size_t cnt, loff_t *ppos){ struct trace_remote_iterator *iter = filp->private_data; struct trace_buffer *trace_buffer = iter->remote->trace_buffer; int ret;
copy_to_user: ret = trace_seq_to_user(&iter->seq, ubuf, cnt); if (ret != -EBUSY) return ret;
trace_seq_init(&iter->seq);
ret = ring_buffer_wait(trace_buffer, iter->cpu, 0, NULL, NULL); if (ret < 0) return ret;
trace_remote_iter_read_start(iter);
while (trace_remote_iter_read_event(iter)) { int prev_len = iter->seq.seq.len;
if (trace_remote_iter_print_event(iter)) { iter->seq.seq.len = prev_len; break; }
trace_remote_iter_move(iter); }
trace_remote_iter_read_finished(iter);
goto copy_to_user;}
static const struct file_operations trace_pipe_fops = { .open = trace_pipe_open, .read = trace_pipe_read, .release = trace_pipe_release,};
static void *trace_next(struct seq_file *m, void *v, loff_t *pos){ struct trace_remote_iterator *iter = m->private;
++*pos;
if (!iter || !trace_remote_iter_read_event(iter)) return NULL;
trace_remote_iter_move(iter); iter->pos++;
return iter;}
static void *trace_start(struct seq_file *m, loff_t *pos){ struct trace_remote_iterator *iter = m->private; loff_t i;
if (!iter) return NULL;
trace_remote_iter_read_start(iter);
if (!*pos) { iter->pos = -1; return trace_next(m, NULL, &i); }
i = iter->pos; while (i < *pos) { iter = trace_next(m, NULL, &i); if (!iter) return NULL; }
return iter;}
static int trace_show(struct seq_file *m, void *v){ struct trace_remote_iterator *iter = v;
trace_seq_init(&iter->seq);
if (trace_remote_iter_print_event(iter)) { seq_printf(m, "[EVENT %d PRINT TOO BIG]\n", iter->evt->id); return 0; }
return trace_print_seq(m, &iter->seq);}
static void trace_stop(struct seq_file *m, void *v){ struct trace_remote_iterator *iter = m->private;
if (iter) trace_remote_iter_read_finished(iter);}
static const struct seq_operations trace_sops = { .start = trace_start, .next = trace_next, .show = trace_show, .stop = trace_stop,};
static int trace_open(struct inode *inode, struct file *filp){ struct trace_remote *remote = inode->i_private; struct trace_remote_iterator *iter = NULL; int cpu = tracing_get_cpu(inode); int ret;
if (!(filp->f_mode & FMODE_READ)) return 0;
guard(mutex)(&remote->lock);
iter = trace_remote_iter(remote, cpu, TRI_NONCONSUMING); if (IS_ERR(iter)) return PTR_ERR(iter);
ret = seq_open(filp, &trace_sops); if (ret) { trace_remote_iter_free(iter); return ret; }
((struct seq_file *)filp->private_data)->private = (void *)iter;
return 0;}
static int trace_release(struct inode *inode, struct file *filp){ struct trace_remote_iterator *iter;
if (!(filp->f_mode & FMODE_READ)) return 0;
iter = ((struct seq_file *)filp->private_data)->private; seq_release(inode, filp);
if (!iter) return 0;
guard(mutex)(&iter->remote->lock);
trace_remote_iter_free(iter);
return 0;}
static ssize_t trace_write(struct file *filp, const char __user *ubuf, size_t cnt, loff_t *ppos){ struct inode *inode = file_inode(filp); struct trace_remote *remote = inode->i_private; int cpu = tracing_get_cpu(inode);
guard(mutex)(&remote->lock);
trace_remote_reset(remote, cpu);
return cnt;}
static const struct file_operations trace_fops = { .open = trace_open, .write = trace_write, .read = seq_read, .read_iter = seq_read_iter, .release = trace_release,};
static int trace_remote_init_tracefs(const char *name, struct trace_remote *remote){ struct dentry *remote_d, *percpu_d, *d; static struct dentry *root; static DEFINE_MUTEX(lock); bool root_inited = false; int cpu;
guard(mutex)(&lock);
if (!root) { root = tracefs_create_dir(TRACEFS_DIR, NULL); if (!root) { pr_err("Failed to create tracefs dir "TRACEFS_DIR"\n"); return -ENOMEM; } root_inited = true; }
remote_d = tracefs_create_dir(name, root); if (!remote_d) { pr_err("Failed to create tracefs dir "TRACEFS_DIR"%s/\n", name); goto err; }
d = trace_create_file("tracing_on", TRACEFS_MODE_WRITE, remote_d, remote, &tracing_on_fops); if (!d) goto err;
d = trace_create_file("buffer_size_kb", TRACEFS_MODE_WRITE, remote_d, remote, &buffer_size_kb_fops); if (!d) goto err;
d = trace_create_file("trace_pipe", TRACEFS_MODE_READ, remote_d, remote, &trace_pipe_fops); if (!d) goto err;
d = trace_create_file("trace", TRACEFS_MODE_WRITE, remote_d, remote, &trace_fops); if (!d) goto err;
percpu_d = tracefs_create_dir("per_cpu", remote_d); if (!percpu_d) { pr_err("Failed to create tracefs dir "TRACEFS_DIR"%s/per_cpu/\n", name); goto err; }
for_each_possible_cpu(cpu) { struct dentry *cpu_d; char cpu_name[16];
snprintf(cpu_name, sizeof(cpu_name), "cpu%d", cpu); cpu_d = tracefs_create_dir(cpu_name, percpu_d); if (!cpu_d) { pr_err("Failed to create tracefs dir "TRACEFS_DIR"%s/percpu/cpu%d\n", name, cpu); goto err; }
d = trace_create_cpu_file("trace_pipe", TRACEFS_MODE_READ, cpu_d, remote, cpu, &trace_pipe_fops); if (!d) goto err;
d = trace_create_cpu_file("trace", TRACEFS_MODE_WRITE, cpu_d, remote, cpu, &trace_fops); if (!d) goto err; }
remote->dentry = remote_d;
return 0;
err: if (root_inited) { tracefs_remove(root); root = NULL; } else { tracefs_remove(remote_d); }
return -ENOMEM;}
static int trace_remote_register_events(const char *remote_name, struct trace_remote *remote, struct remote_event *events, size_t nr_events);
/** * trace_remote_register() - Register a Tracefs remote * @name: Name of the remote, used for the Tracefs remotes/ directory. * @cbs: Set of callbacks used to control the remote. * @priv: Private data, passed to each callback from @cbs. * @events: Array of events. &remote_event.name and &remote_event.id must be * filled by the caller. * @nr_events: Number of events in the @events array. * * A trace remote is an entity, outside of the kernel (most likely firmware or * hypervisor) capable of writing events into a Tracefs compatible ring-buffer. * The kernel would then act as a reader. * * The registered remote will be found under the Tracefs directory * remotes/<name>. * * Return: 0 on success, negative error code on failure. */int trace_remote_register(const char *name, struct trace_remote_callbacks *cbs, void *priv, struct remote_event *events, size_t nr_events){ struct trace_remote *remote; int ret;
remote = kzalloc_obj(*remote); if (!remote) return -ENOMEM;
remote->cbs = cbs; remote->priv = priv; remote->trace_buffer_size = 7 << 10; remote->poll_ms = 100; mutex_init(&remote->lock); init_rwsem(&remote->reader_lock);
if (trace_remote_init_tracefs(name, remote)) { kfree(remote); return -ENOMEM; }
ret = trace_remote_register_events(name, remote, events, nr_events); if (ret) { pr_err("Failed to register events for trace remote '%s' (%d)\n", name, ret); return ret; }
ret = cbs->init ? cbs->init(remote->dentry, priv) : 0; if (ret) pr_err("Init failed for trace remote '%s' (%d)\n", name, ret);
return ret;}EXPORT_SYMBOL_GPL(trace_remote_register);
/** * trace_remote_free_buffer() - Free trace buffer allocated with trace_remote_alloc_buffer() * @desc: Descriptor of the per-CPU ring-buffers, originally filled by * trace_remote_alloc_buffer() * * Most likely called from &trace_remote_callbacks.unload_trace_buffer. */void trace_remote_free_buffer(struct trace_buffer_desc *desc){ struct ring_buffer_desc *rb_desc; int cpu;
for_each_ring_buffer_desc(rb_desc, cpu, desc) { unsigned int id;
free_page(rb_desc->meta_va);
for (id = 0; id < rb_desc->nr_page_va; id++) free_page(rb_desc->page_va[id]); }}EXPORT_SYMBOL_GPL(trace_remote_free_buffer);
/** * trace_remote_alloc_buffer() - Dynamically allocate a trace buffer * @desc: Uninitialized trace_buffer_desc * @desc_size: Size of the trace_buffer_desc. Must be at least equal to * trace_buffer_desc_size() * @buffer_size: Size in bytes of each per-CPU ring-buffer * @cpumask: CPUs to allocate a ring-buffer for * * Helper to dynamically allocate a set of pages (enough to cover @buffer_size) * for each CPU from @cpumask and fill @desc. Most likely called from * &trace_remote_callbacks.load_trace_buffer. * * Return: 0 on success, negative error code on failure. */int trace_remote_alloc_buffer(struct trace_buffer_desc *desc, size_t desc_size, size_t buffer_size, const struct cpumask *cpumask){ size_t min_desc_size = trace_buffer_desc_size(buffer_size, cpumask_weight(cpumask)); unsigned int nr_pages = max(DIV_ROUND_UP(buffer_size, PAGE_SIZE), 2UL) + 1; struct ring_buffer_desc *rb_desc; int cpu, ret = -ENOMEM;
if (desc_size < min_desc_size) return -EINVAL;
desc->nr_cpus = 0; desc->struct_len = min_desc_size;
rb_desc = __first_ring_buffer_desc(desc);
for_each_cpu(cpu, cpumask) { unsigned int id;
rb_desc->cpu = cpu; rb_desc->nr_page_va = 0; rb_desc->meta_va = (unsigned long)__get_free_page(GFP_KERNEL); if (!rb_desc->meta_va) goto err;
desc->nr_cpus++;
for (id = 0; id < nr_pages; id++) { rb_desc->nr_page_va++; rb_desc->page_va[id] = (unsigned long)__get_free_page(GFP_KERNEL); if (!rb_desc->page_va[id]) goto err; } rb_desc = __next_ring_buffer_desc(rb_desc); }
return 0;
err: trace_remote_free_buffer(desc); return ret;}EXPORT_SYMBOL_GPL(trace_remote_alloc_buffer);
static inttrace_remote_enable_event(struct trace_remote *remote, struct remote_event *evt, bool enable){ int ret;
lockdep_assert_held(&remote->lock);
if (evt->enabled == enable) return 0;
ret = remote->cbs->enable_event(evt->id, enable, remote->priv); if (ret) return ret;
evt->enabled = enable;
return 0;}
static int remote_event_enable_show(struct seq_file *s, void *unused){ struct remote_event *evt = s->private;
seq_printf(s, "%d\n", evt->enabled);
return 0;}
static ssize_t remote_event_enable_write(struct file *filp, const char __user *ubuf, size_t count, loff_t *ppos){ struct seq_file *seq = filp->private_data; struct remote_event *evt = seq->private; struct trace_remote *remote = evt->remote; u8 enable; int ret;
ret = kstrtou8_from_user(ubuf, count, 10, &enable); if (ret) return ret;
guard(mutex)(&remote->lock);
ret = trace_remote_enable_event(remote, evt, enable); if (ret) return ret;
return count;}DEFINE_SHOW_STORE_ATTRIBUTE(remote_event_enable);
static int remote_event_id_show(struct seq_file *s, void *unused){ struct remote_event *evt = s->private;
seq_printf(s, "%d\n", evt->id);
return 0;}DEFINE_SHOW_ATTRIBUTE(remote_event_id);
static int remote_event_format_show(struct seq_file *s, void *unused){ size_t offset = sizeof(struct remote_event_hdr); struct remote_event *evt = s->private; struct trace_event_fields *field;
seq_printf(s, "name: %s\n", evt->name); seq_printf(s, "ID: %d\n", evt->id); seq_puts(s, "format:\n\tfield:unsigned short common_type;\toffset:0;\tsize:2;\tsigned:0;\n\n");
field = &evt->fields[0]; while (field->name) { seq_printf(s, "\tfield:%s %s;\toffset:%zu;\tsize:%u;\tsigned:%d;\n", field->type, field->name, offset, field->size, field->is_signed); offset += field->size; field++; }
if (field != &evt->fields[0]) seq_puts(s, "\n");
seq_printf(s, "print fmt: %s\n", evt->print_fmt);
return 0;}DEFINE_SHOW_ATTRIBUTE(remote_event_format);
static int remote_event_callback(const char *name, umode_t *mode, void **data, const struct file_operations **fops){ if (!strcmp(name, "enable")) { *mode = TRACEFS_MODE_WRITE; *fops = &remote_event_enable_fops; return 1; }
if (!strcmp(name, "id")) { *mode = TRACEFS_MODE_READ; *fops = &remote_event_id_fops; return 1; }
if (!strcmp(name, "format")) { *mode = TRACEFS_MODE_READ; *fops = &remote_event_format_fops; return 1; }
return 0;}
static ssize_t remote_events_dir_enable_write(struct file *filp, const char __user *ubuf, size_t count, loff_t *ppos){ struct trace_remote *remote = file_inode(filp)->i_private; int i, ret; u8 enable;
ret = kstrtou8_from_user(ubuf, count, 10, &enable); if (ret) return ret;
guard(mutex)(&remote->lock);
for (i = 0; i < remote->nr_events; i++) { struct remote_event *evt = &remote->events[i]; int eret;
eret = trace_remote_enable_event(remote, evt, enable); /* * Save the first error and return that. Some events * may still have been enabled, but let the user * know that something went wrong. */ if (!ret && eret) ret = eret; }
if (ret) return ret;
return count;}
static ssize_t remote_events_dir_enable_read(struct file *filp, char __user *ubuf, size_t cnt, loff_t *ppos){ struct trace_remote *remote = file_inode(filp)->i_private; const char enabled_char[] = {'0', '1', 'X'}; char enabled_str[] = " \n"; int i, enabled = -1;
guard(mutex)(&remote->lock);
for (i = 0; i < remote->nr_events; i++) { struct remote_event *evt = &remote->events[i];
if (enabled == -1) { enabled = evt->enabled; } else if (enabled != evt->enabled) { enabled = 2; break; } }
enabled_str[0] = enabled_char[enabled == -1 ? 0 : enabled];
return simple_read_from_buffer(ubuf, cnt, ppos, enabled_str, 2);}
static const struct file_operations remote_events_dir_enable_fops = { .write = remote_events_dir_enable_write, .read = remote_events_dir_enable_read,};
static ssize_tremote_events_dir_header_page_read(struct file *filp, char __user *ubuf, size_t cnt, loff_t *ppos){ struct trace_seq *s; int ret;
s = kmalloc(sizeof(*s), GFP_KERNEL); if (!s) return -ENOMEM;
trace_seq_init(s);
ring_buffer_print_page_header(NULL, s); ret = simple_read_from_buffer(ubuf, cnt, ppos, s->buffer, trace_seq_used(s)); kfree(s);
return ret;}
static const struct file_operations remote_events_dir_header_page_fops = { .read = remote_events_dir_header_page_read,};
static ssize_tremote_events_dir_header_event_read(struct file *filp, char __user *ubuf, size_t cnt, loff_t *ppos){ struct trace_seq *s; int ret;
s = kmalloc(sizeof(*s), GFP_KERNEL); if (!s) return -ENOMEM;
trace_seq_init(s);
ring_buffer_print_entry_header(s); ret = simple_read_from_buffer(ubuf, cnt, ppos, s->buffer, trace_seq_used(s)); kfree(s);
return ret;}
static const struct file_operations remote_events_dir_header_event_fops = { .read = remote_events_dir_header_event_read,};
static int remote_events_dir_callback(const char *name, umode_t *mode, void **data, const struct file_operations **fops){ if (!strcmp(name, "enable")) { *mode = TRACEFS_MODE_WRITE; *fops = &remote_events_dir_enable_fops; return 1; }
if (!strcmp(name, "header_page")) { *mode = TRACEFS_MODE_READ; *fops = &remote_events_dir_header_page_fops; return 1; }
if (!strcmp(name, "header_event")) { *mode = TRACEFS_MODE_READ; *fops = &remote_events_dir_header_event_fops; return 1; }
return 0;}
static int trace_remote_init_eventfs(const char *remote_name, struct trace_remote *remote, struct remote_event *evt){ struct eventfs_inode *eventfs = remote->eventfs; static struct eventfs_entry dir_entries[] = { { .name = "enable", .callback = remote_events_dir_callback, }, { .name = "header_page", .callback = remote_events_dir_callback, }, { .name = "header_event", .callback = remote_events_dir_callback, } }; static struct eventfs_entry entries[] = { { .name = "enable", .callback = remote_event_callback, }, { .name = "id", .callback = remote_event_callback, }, { .name = "format", .callback = remote_event_callback, } }; bool eventfs_create = false;
if (!eventfs) { eventfs = eventfs_create_events_dir("events", remote->dentry, dir_entries, ARRAY_SIZE(dir_entries), remote); if (IS_ERR(eventfs)) return PTR_ERR(eventfs);
/* * Create similar hierarchy as local events even if a single system is supported at * the moment */ eventfs = eventfs_create_dir(remote_name, eventfs, NULL, 0, NULL); if (IS_ERR(eventfs)) return PTR_ERR(eventfs);
remote->eventfs = eventfs; eventfs_create = true; }
eventfs = eventfs_create_dir(evt->name, eventfs, entries, ARRAY_SIZE(entries), evt); if (IS_ERR(eventfs)) { if (eventfs_create) { eventfs_remove_events_dir(remote->eventfs); remote->eventfs = NULL; } return PTR_ERR(eventfs); }
return 0;}
static int trace_remote_attach_events(struct trace_remote *remote, struct remote_event *events, size_t nr_events){ int i;
for (i = 0; i < nr_events; i++) { struct remote_event *evt = &events[i];
if (evt->remote) return -EEXIST;
evt->remote = remote;
/* We need events to be sorted for efficient lookup */ if (i && evt->id <= events[i - 1].id) return -EINVAL; }
remote->events = events; remote->nr_events = nr_events;
return 0;}
static int trace_remote_register_events(const char *remote_name, struct trace_remote *remote, struct remote_event *events, size_t nr_events){ int i, ret;
ret = trace_remote_attach_events(remote, events, nr_events); if (ret) return ret;
for (i = 0; i < nr_events; i++) { struct remote_event *evt = &events[i];
ret = trace_remote_init_eventfs(remote_name, remote, evt); if (ret) pr_warn("Failed to init eventfs for event '%s' (%d)", evt->name, ret); }
return 0;}
static int __cmp_events(const void *key, const void *data){ const struct remote_event *evt = data; int id = (int)((long)key);
return id - (int)evt->id;}
static struct remote_event *trace_remote_find_event(struct trace_remote *remote, unsigned short id){ return bsearch((const void *)(unsigned long)id, remote->events, remote->nr_events, sizeof(*remote->events), __cmp_events);}