From 176b56b261a711822f35e36ec5e2d5e0758770de Mon Sep 17 00:00:00 2001 From: David Smith Date: Wed, 4 Mar 2009 12:51:29 -0600 Subject: [PATCH] Start of new ring_buffer transport. 2009-03-04 David Smith * runtime.h: Added _stp_exit() prototype. * transport/ring_buffer.c: New file. * transport/transport.c: Removed unneeded utt_trace lines. Includes transport/ring_buffer.c. (_stp_transport_fs_init): Calls _stp_transport_data_fs_init(). (_stp_transport_fs_close): Calls _stp_transport_data_fs_close(). * transport/transport.h: Added prototypes. --- runtime/runtime.h | 2 + runtime/transport/ring_buffer.c | 338 ++++++++++++++++++++++++++++++++ runtime/transport/transport.c | 29 ++- runtime/transport/transport.h | 4 + 4 files changed, 363 insertions(+), 10 deletions(-) create mode 100644 runtime/transport/ring_buffer.c diff --git a/runtime/runtime.h b/runtime/runtime.h index 25456beb0..28283db6c 100644 --- a/runtime/runtime.h +++ b/runtime/runtime.h @@ -47,6 +47,8 @@ static void _stp_dbug (const char *func, int line, const char *fmt, ...); static void _stp_error (const char *fmt, ...); static void _stp_warn (const char *fmt, ...); +static void _stp_exit(void); + #include "debug.h" /* atomic globals */ diff --git a/runtime/transport/ring_buffer.c b/runtime/transport/ring_buffer.c new file mode 100644 index 000000000..53149fcd9 --- /dev/null +++ b/runtime/transport/ring_buffer.c @@ -0,0 +1,338 @@ +#include +#include + +static struct ring_buffer *__stp_ring_buffer = NULL; +//DEFINE_PER_CPU(struct oprofile_cpu_buffer, cpu_buffer); + +static void __stp_free_ring_buffer(void) +{ + if (__stp_ring_buffer) + ring_buffer_free(__stp_ring_buffer); + __stp_ring_buffer = NULL; +} + +static int __stp_alloc_ring_buffer(void) +{ + int i; + unsigned long buffer_size = _stp_bufsize; + + dbug_trans(1, "%lu\n", buffer_size); + __stp_ring_buffer = ring_buffer_alloc(buffer_size, 0); + if (!__stp_ring_buffer) + goto fail; + +// DRS: do we need this? +#if 0 + for_each_possible_cpu(i) { + struct oprofile_cpu_buffer *b = &per_cpu(cpu_buffer, i); + + b->last_task = NULL; + b->last_is_kernel = -1; + b->tracing = 0; + b->buffer_size = buffer_size; + b->sample_received = 0; + b->sample_lost_overflow = 0; + b->backtrace_aborted = 0; + b->sample_invalid_eip = 0; + b->cpu = i; + INIT_DELAYED_WORK(&b->work, wq_sync_buffer); + } +#endif + return 0; + +fail: + __stp_free_ring_buffer(); + return -ENOMEM; +} + + +static atomic_t _stp_trace_attached = ATOMIC_INIT(0); +static struct trace_iterator _stp_trace_iter; + +static int _stp_data_open_trace(struct inode *inode, struct file *file) +{ + /* We only allow for one reader */ + dbug_trans(1, "trace attach\n"); + if (atomic_inc_return(&_stp_trace_attached) != 1) { + atomic_dec(&_stp_trace_attached); + dbug_trans(1, "returning EBUSY\n"); + return -EBUSY; + } + + file->private_data = &_stp_trace_iter; + return 0; +} + +static int _stp_data_release_trace(struct inode *inode, struct file *file) +{ + dbug_trans(1, "trace detach\n"); + atomic_dec(&_stp_trace_attached); + return 0; +} + +struct trace_seq { + unsigned char buffer[PAGE_SIZE]; + unsigned int len; + unsigned int readpos; +}; + +ssize_t +_stp_trace_seq_to_user(struct trace_seq *s, char __user *ubuf, size_t cnt) +{ + int len; + int ret; + + dbug_trans(1, "s: %p\n", s); + if (s == NULL) + return -EFAULT; + + dbug_trans(1, "len: %d, readpos: %d, buffer: %p\n", s->len, + s->readpos, s->buffer); + if (s->len <= s->readpos) + return -EBUSY; + + len = s->len - s->readpos; + if (cnt > len) + cnt = len; + ret = copy_to_user(ubuf, s->buffer + s->readpos, cnt); + if (ret) + return -EFAULT; + + s->readpos += len; + return cnt; +} + +static void +trace_seq_reset(struct trace_seq *s) +{ + s->len = 0; + s->readpos = 0; +} + +/* + * Trace iterator - used by printout routines who present trace + * results to users and which routines might sleep, etc: + */ +struct trace_iterator { +#if 0 + struct trace_array *tr; + struct tracer *trace; + void *private; +#endif + struct ring_buffer_iter *buffer_iter[NR_CPUS]; + + /* The below is zeroed out in pipe_read */ + struct trace_seq seq; +#if 0 + struct trace_entry *ent; + int cpu; + u64 ts; + + unsigned long iter_flags; +#endif + loff_t pos; +#if 0 + long idx; + + cpumask_var_t started; +#endif +}; + +static int trace_empty(struct trace_iterator *iter) +{ + int cpu; + + for_each_possible_cpu(cpu) { + if (iter->buffer_iter[cpu]) { + if (!ring_buffer_iter_empty(iter->buffer_iter[cpu])) + return 0; +#if 0 + } else { + if (!ring_buffer_empty_cpu(iter->tr->buffer, cpu)) + return 0; +#endif + } + } + + return 1; +} + +/* Must be called with trace_types_lock mutex held. */ +static int tracing_wait_pipe(struct file *filp) +{ + struct trace_iterator *iter = filp->private_data; + + while (trace_empty(iter)) { + + if ((filp->f_flags & O_NONBLOCK)) { + return -EAGAIN; + } + + /* + * This is a make-shift waitqueue. The reason we don't use + * an actual wait queue is because: + * 1) we only ever have one waiter + * 2) the tracing, traces all functions, we don't want + * the overhead of calling wake_up and friends + * (and tracing them too) + * Anyway, this is really very primitive wakeup. + */ + set_current_state(TASK_INTERRUPTIBLE); + //iter->tr->waiter = current; + + /* sleep for 100 msecs, and try again. */ + schedule_timeout(HZ/10); + + //iter->tr->waiter = NULL; + + if (signal_pending(current)) { + return -EINTR; + } + +#if 0 + if (iter->trace != current_trace) + return 0; + + /* + * We block until we read something and tracing is disabled. + * We still block if tracing is disabled, but we have never + * read anything. This allows a user to cat this file, and + * then enable tracing. But after we have read something, + * we give an EOF when tracing is again disabled. + * + * iter->pos will be 0 if we haven't read anything. + */ + if (!tracer_enabled && iter->pos) + break; +#else + if (iter->pos) + break; +#endif + + continue; + } + + return 1; +} + +/* + * Consumer reader. + */ +static ssize_t +_stp_data_read_trace(struct file *filp, char __user *ubuf, + size_t cnt, loff_t *ppos) +{ + struct trace_iterator *iter = filp->private_data; + ssize_t sret; + + /* return any leftover data */ + dbug_trans(1, "%lu\n", (unsigned long)cnt); + sret = _stp_trace_seq_to_user(&iter->seq, ubuf, cnt); + if (sret != -EBUSY) + return sret; + + trace_seq_reset(&iter->seq); + +waitagain: + sret = tracing_wait_pipe(filp); + if (sret <= 0) + goto out; + + /* stop when tracing is finished */ + if (trace_empty(iter)) { + sret = 0; + goto out; + } + + if (cnt >= PAGE_SIZE) + cnt = PAGE_SIZE - 1; + + /* reset all but tr, trace, and overruns */ + memset(&iter->seq, 0, + sizeof(struct trace_iterator) - + offsetof(struct trace_iterator, seq)); + iter->pos = -1; + +#if 0 + while (find_next_entry_inc(iter) != NULL) { + enum print_line_t ret; + int len = iter->seq.len; + + ret = print_trace_line(iter); + if (ret == TRACE_TYPE_PARTIAL_LINE) { + /* don't print partial lines */ + iter->seq.len = len; + break; + } + if (ret != TRACE_TYPE_NO_CONSUME) + trace_consume(iter); + + if (iter->seq.len >= cnt) + break; + } +#endif + + /* Now copy what we have to the user */ + sret = _stp_trace_seq_to_user(&iter->seq, ubuf, cnt); + if (iter->seq.readpos >= iter->seq.len) + trace_seq_reset(&iter->seq); + + /* + * If there was nothing to send to user, inspite of consuming trace + * entries, go back to wait for more entries. + */ + if (sret == -EBUSY) + goto waitagain; + +out: + return sret; +} + +static struct file_operations __stp_data_fops = { + .owner = THIS_MODULE, + .open = _stp_data_open_trace, + .release = _stp_data_release_trace, +#if 0 + .poll = tracing_poll_pipe, +#endif + .read = _stp_data_read_trace, +#if 0 + .splice_read = tracing_splice_read_pipe, +#endif +}; + +static struct dentry *__stp_entry; + +static int _stp_transport_data_fs_init(void) +{ + int rc; + + // allocate buffer + dbug_trans(1, "entry...\n"); + rc = __stp_alloc_ring_buffer(); + if (rc != 0) + return rc; + + // create file(s) + __stp_entry = debugfs_create_file("trace", 0600, _stp_get_module_dir(), + NULL, &__stp_data_fops); + if (!__stp_entry) + pr_warning("Could not create debugfs 'trace' entry\n"); + else { + __stp_entry->d_inode->i_uid = _stp_uid; + __stp_entry->d_inode->i_gid = _stp_gid; + } + + dbug_trans(1, "returning 0...\n"); + return 0; +} + +static void _stp_transport_data_fs_close(void) +{ + if (__stp_entry) + debugfs_remove(__stp_entry); + __stp_entry = NULL; + + __stp_free_ring_buffer(); +} + diff --git a/runtime/transport/transport.c b/runtime/transport/transport.c index 12e98a381..093d8b7b8 100644 --- a/runtime/transport/transport.c +++ b/runtime/transport/transport.c @@ -19,11 +19,6 @@ #include #include -struct utt_trace { - int dummy; -}; -//static struct utt_trace *_stp_utt = NULL; - static void utt_set_overwrite(int overwrite) { return; @@ -59,6 +54,7 @@ static unsigned int utt_seq = 1; #include "control.h" #include "debugfs.c" #include "control.c" +#include "ring_buffer.c" #endif /* if 0 */ static unsigned _stp_nsubbufs = 8; static unsigned _stp_subbuf_size = 65536*4; @@ -71,9 +67,6 @@ MODULE_PARM_DESC(_stp_bufsize, "buffer size"); /* forward declarations */ static void probe_exit(void); static int probe_start(void); -#if 0 -static void _stp_exit(void); -#endif /* #if 0 */ /* check for new workqueue API */ #ifdef DECLARE_DELAYED_WORK @@ -287,8 +280,8 @@ static int _stp_transport_init(void) dbug_trans(1, "Using %d subbufs of size %d\n", _stp_nsubbufs, _stp_subbuf_size); } - if (_stp_transport_fs_init(THIS_MODULE->name)) - return -1; + if (_stp_transport_fs_init(THIS_MODULE->name) != 0) + goto err0; #if 0 #if !defined (STP_OLD_TRANSPORT) || defined (STP_BULKMODE) @@ -297,6 +290,7 @@ static int _stp_transport_init(void) if (!_stp_utt) goto err0; #endif +#else /* #if 0 */ #endif /* #if 0 */ /* create control channel */ @@ -322,20 +316,25 @@ static int _stp_transport_init(void) _stp_ctl_send(STP_TRANSPORT, NULL, 0); #endif /* #if 0 */ + dbug_trans(1, "returning 0...\n"); return 0; err3: + dbug_trans(1, "err3\n"); _stp_print_cleanup(); err2: + dbug_trans(1, "err2\n"); _stp_unregister_ctl_channel(); err1: #if 0 if (_stp_utt) utt_trace_remove(_stp_utt); #else + dbug_trans(1, "err1\n"); _stp_transport_fs_close(); #endif /* #if 0 */ err0: + dbug_trans(1, "err0\n"); return -1; } @@ -469,6 +468,7 @@ static int _stp_transport_fs_init(const char *module_name) { struct dentry *root_dir; + dbug_trans(1, "entry\n"); if (module_name == NULL) return -1; @@ -481,12 +481,21 @@ static int _stp_transport_fs_init(const char *module_name) _stp_remove_root_dir(); return -1; } + + if (_stp_transport_data_fs_init() != 0) { + _stp_remove_root_dir(); + return -1; + } + dbug_trans(1, "returning 0\n"); return 0; } static void _stp_transport_fs_close(void) { dbug_trans(1, "stp_transport_fs_close\n"); + + _stp_transport_data_fs_close(); + if (__stp_module_dir) { debugfs_remove(__stp_module_dir); __stp_module_dir = NULL; diff --git a/runtime/transport/transport.h b/runtime/transport/transport.h index dec23c723..c79549d4d 100644 --- a/runtime/transport/transport.h +++ b/runtime/transport/transport.h @@ -60,4 +60,8 @@ static gid_t _stp_gid; static int _stp_ctl_attached; +static int _stp_bufsize; +static int _stp_transport_data_fs_init(void); +static void _stp_transport_data_fs_close(void); + #endif /* _TRANSPORT_TRANSPORT_H_ */ -- 2.43.5