]> sourceware.org Git - systemtap.git/commitdiff
Start of new ring_buffer transport.
authorDavid Smith <dsmith@redhat.com>
Wed, 4 Mar 2009 18:51:29 +0000 (12:51 -0600)
committerDavid Smith <dsmith@redhat.com>
Wed, 4 Mar 2009 18:51:29 +0000 (12:51 -0600)
2009-03-04  David Smith  <dsmith@redhat.com>

        * 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
runtime/transport/ring_buffer.c [new file with mode: 0644]
runtime/transport/transport.c
runtime/transport/transport.h

index 25456beb03af6459bd7f45d359165631aca3a0d7..28283db6cf1a6a45fb5f25e50f430623bf020d20 100644 (file)
@@ -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 (file)
index 0000000..53149fc
--- /dev/null
@@ -0,0 +1,338 @@
+#include <linux/types.h>
+#include <linux/ring_buffer.h>
+
+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();
+}
+
index 12e98a381313d916bd0750628194fbbcb0722345..093d8b7b8c19d5cb3187cbf6a2ab0acacc9666f6 100644 (file)
 #include <linux/namei.h>
 #include <linux/workqueue.h>
 
-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;
index dec23c7232157300230b7525f9a848771e0ba6e6..c79549d4de72c4a0b331f07aa71934a20d382a94 100644 (file)
@@ -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_ */
This page took 0.03772 seconds and 5 git commands to generate.