Skip to contentSkip to conversation

relay: Add buffer-only channels; useful for early logging.

PATCH In review Eduard - Gabriel Munteanu 2 participants 4 messages Jun 21, 2008 – Jun 22, 2008

Subsystems (2)
lore
Share & tools
lore
#0 lore raw

PATCH 3/3 relay: Add buffer-only channels; useful for early logging.

Allows one to create and use a channel with no associated files. Files can be initialized later. This is useful in scenarios such as logging in early code, before VFS is up. Therefore, such channels can be created and used as soon as kmem_cache_init() completed.

This is needed by kmemtrace to do tracing in early kernel code.

---
 Documentation/filesystems/relay.txt |   11 +++
 include/linux/relay.h               |    5 +
 kernel/relay.c                      |  141 +++++++++++++++++++++++++++--------
 3 files changed, 126 insertions(+), 31 deletions(-)
Documentation/filesystems/relay.txt +11 -0
index 094f2d2..b417f83 100644
@@ -161,6 +161,7 @@ TBD(curr. line MT:/API/)
     relay_close(chan)
     relay_flush(chan)
     relay_reset(chan)
+    relay_late_setup_files(chan, base_filename, parent)
 
   channel management typically called on instigation of userspace:
 
@@ -294,6 +295,16 @@ user-defined data with a channel, and is immediately available
 (including in create_buf_file()) via chan->private_data or
 buf->chan->private_data.
 
+Buffer-only channels
+--------------------
+
+These channels have no files associated and can be created with
+relay_open(NULL, NULL, ...). Such channels are useful in scenarios such
+as when doing early tracing in the kernel, before the VFS is up. In these
+cases, one may open a buffer-only channel and then call 
+relay_late_setup_files() when the kernel is ready to handle files,
+to expose the buffered data to the userspace.
+
 Channel 'modes'
 ---------------
 
include/linux/relay.h +5 -0
index a3a03e7..1d3dcf8 100644
@@ -49,6 +49,7 @@ struct rchan_buf
 	size_t *padding;		/* padding counts per sub-buffer */
 	size_t prev_padding;		/* temporary variable */
 	size_t bytes_consumed;		/* bytes consumed in cur read subbuf */
+	size_t early_bytes;		/* bytes consumed before VFS inited */
 	unsigned int cpu;		/* this buf's cpu */
 } ____cacheline_aligned;
 
@@ -69,6 +70,7 @@ struct rchan
 	int is_global;			/* One global buffer ? */
 	struct list_head list;		/* for channel list */
 	struct dentry *parent;		/* parent dentry passed to open */
+	int has_base_filename;		/* has a filename associated? */
 	char base_filename[NAME_MAX];	/* saved base filename */
 };
 
@@ -170,6 +172,9 @@ struct rchan *relay_open(const char *base_filename,
 			 size_t n_subbufs,
 			 struct rchan_callbacks *cb,
 			 void *private_data);
+extern int relay_late_setup_files(struct rchan *chan,
+				  const char *base_filename,
+				  struct dentry *parent);
 extern void relay_close(struct rchan *chan);
 extern void relay_flush(struct rchan *chan);
 extern void relay_subbufs_consumed(struct rchan *chan,
kernel/relay.c +110 -31

210 lines not on this page · raw message

Signature · 1 line
1.5.5.4
Reviewed-by on v1 3/3
#1 lore raw
Quoted 8 lines · Eduard - Gabriel Munteanu

Allows one to create and use a channel with no associated files. Files can be initialized later. This is useful in scenarios such as logging in early code, before VFS is up. Therefore, such channels can be created and used as soon as kmem_cache_init() completed.

This is needed by kmemtrace to do tracing in early kernel code.

#2 lore raw
In reply to Eduard - Gabriel Munteanu
Quoted 8 lines · Eduard - Gabriel Munteanu

Allows one to create and use a channel with no associated files. Files can be initialized later. This is useful in scenarios such as logging in early code, before VFS is up. Therefore, such channels can be created and used as soon as kmem_cache_init() completed.

This is needed by kmemtrace to do tracing in early kernel code.

kernel/relay.c +1 -0
+static int relay_setup_buf_file(struct rchan *chan,

kernel/relay.c void relay_reset(struct rchan *chan) · line 420

Quoted 10 lines
kernel/relay.c +10 -0
+                               struct rchan_buf *buf,
+                               unsigned int cpu)
+{
+       struct dentry *dentry;
+       unsigned long flags;
+       char *tmpname;
+
+       tmpname = kzalloc(NAME_MAX + 1, GFP_KERNEL);
+       if (!tmpname)
+               goto failed;

Why don't you return -ENOMEM here?

kernel/relay.c void relay_reset(struct rchan *chan) · line 431

Quoted 11 lines
kernel/relay.c +11 -0
+       snprintf(tmpname, NAME_MAX, "%s%d", chan->base_filename, cpu);
+
+       /* Create file in fs */
+       dentry = chan->cb->create_buf_file(tmpname, chan->parent,
+                                          S_IRUSR, buf,
+                                          &chan->is_global);
+
+       kfree(tmpname);
+
+       if (!dentry)
+               goto failed;

And here?

kernel/relay.c void relay_reset(struct rchan *chan) · line 440

Quoted 9 lines
kernel/relay.c +9 -0
+       spin_lock_irqsave(&buf->rw_lock, flags);
+       buf->dentry = dentry;
+       buf->dentry->d_inode->i_size = buf->early_bytes;
+       spin_unlock_irqrestore(&buf->rw_lock, flags);
+
+       return 0;
+
+failed:
+       return 1;

The separate goto seems pointless. Why don't you use negative return values for error handling?

Quoted 107 lines
kernel/relay.c +25 -27
+}
+
 /*
 *     relay_open_buf - create a new relay channel buffer
 *
@@ -415,48 +448,33 @@ EXPORT_SYMBOL_GPL(relay_reset);
 static struct rchan_buf *relay_open_buf(struct rchan *chan, unsigned int cpu)
 {
       struct rchan_buf *buf = NULL;
-       struct dentry *dentry;
-       char *tmpname;
 
       if (chan->is_global)
               return chan->buf[0];
 
-       tmpname = kzalloc(NAME_MAX + 1, GFP_KERNEL);
-       if (!tmpname)
-               goto end;
-       snprintf(tmpname, NAME_MAX, "%s%d", chan->base_filename, cpu);
-
       buf = relay_create_buf(chan);
       if (!buf)
-               goto free_name;
+               return NULL;
 
       spin_lock_init(&buf->rw_lock);
 
+       if (chan->has_base_filename)
+               if (relay_setup_buf_file(chan, buf, cpu))
+                       goto free_buf;
+
       buf->cpu = cpu;
       __relay_reset(buf, 1);
 
-       /* Create file in fs */
-       dentry = chan->cb->create_buf_file(tmpname, chan->parent, S_IRUSR,
-                                          buf, &chan->is_global);
-       if (!dentry)
-               goto free_buf;
-
-       buf->dentry = dentry;
-
       if(chan->is_global) {
               chan->buf[0] = buf;
               buf->cpu = 0;
       }
 
-       goto free_name;
+       return buf;
 
 free_buf:
       relay_destroy_buf(buf);
-       buf = NULL;
-free_name:
-       kfree(tmpname);
-end:
-       return buf;
+       return NULL;
 }
 
 /**
@@ -539,8 +557,8 @@ static int __cpuinit relay_hotcpu_callback(struct notifier_block *nb,
 
 /**
 *     relay_open - create a new relay channel
- *     @base_filename: base name of files to create
- *     @parent: dentry of parent directory, %NULL for root directory
+ *     @base_filename: base name of files to create, %NULL for buffering only
+ *     @parent: dentry of parent directory, %NULL for root directory or buffer
 *     @subbuf_size: size of sub-buffers
 *     @n_subbufs: number of sub-buffers
 *     @cb: client callback functions
@@ -562,8 +580,6 @@ struct rchan *relay_open(const char *base_filename,
 {
       unsigned int i;
       struct rchan *chan;
-       if (!base_filename)
-               return NULL;
 
       if (!(subbuf_size && n_subbufs))
               return NULL;
@@ -578,7 +594,10 @@ struct rchan *relay_open(const char *base_filename,
       chan->alloc_size = FIX_SIZE(subbuf_size * n_subbufs);
       chan->parent = parent;
       chan->private_data = private_data;
-       strlcpy(chan->base_filename, base_filename, NAME_MAX);
+       if (base_filename) {
+               chan->has_base_filename = 1;
+               strlcpy(chan->base_filename, base_filename, NAME_MAX);
+       }
       setup_callbacks(chan, cb);
       kref_init(&chan->kref);
 
@@ -607,6 +626,62 @@ free_bufs:
 EXPORT_SYMBOL_GPL(relay_open);
 
 /**
+ *     relay_late_setup_files - triggers file creation
+ *     @chan: channel to operate on
+ *     @base_filename: base name of files to create
+ *     @parent: dentry of parent directory, %NULL for root directory
+ *
+ *     Returns 0 if successful, non-zero otherwise.
+ *
+ *     Use to setup files for a previously buffer-only channel.
+ *     Useful to do early tracing in kernel, before VFS is up, for example.
+ */

kernel/relay.c free_bufs: · line 648

Quoted 10 lines
kernel/relay.c +10 -0
+int relay_late_setup_files(struct rchan *chan,
+                          const char *base_filename,
+                          struct dentry *parent)
+{
+       unsigned int i;
+       int err;
+       struct rchan_buf *buf;
+
+       if (!chan || !base_filename)
+               return 1;

-EINVAL?

kernel/relay.c free_bufs: · line 654

Quoted 6 lines
kernel/relay.c +6 -0
+
+       strlcpy(chan->base_filename, base_filename, NAME_MAX);
+
+       mutex_lock(&relay_channels_mutex);
+       if (unlikely(chan->has_base_filename))
+               goto out;

-EINVAL?

Quoted 17 lines
kernel/relay.c +17 -0
+       chan->has_base_filename = 1;
+       chan->parent = parent;
+       /*
+        * The CPU hotplug notifier ran before us and created buffers with
+        * no files associated. So it's safe to call relay_setup_buf_file()
+        * on all currently online CPUs.
+        */
+       for_each_online_cpu(i) {
+               buf = chan->buf[i];
+
+               if (unlikely(!buf)) {
+                       printk(KERN_ERR "relay_late_setup_files: "
+                              "all CPUs should have buffers!\n");
+                       goto out;
+               }
+
+               err = relay_setup_buf_file(chan, buf, i);

kernel/relay.c free_bufs: · line 681

Quoted 10 lines
kernel/relay.c +10 -0
+               if (unlikely(err))
+                       goto out;
+       }
+       mutex_unlock(&relay_channels_mutex);
+
+       return 0;
+
+out:
+       mutex_unlock(&relay_channels_mutex);
+       return 1;

You don't need two return statements (and coincidentally two unlocks) if you keep the return value in a local variable 'err'.

Quoted 41 lines
kernel/relay.c +11 -5
+}
+
+/**
 *     relay_switch_subbuf - switch to a new sub-buffer
 *     @buf: channel buffer
 *     @length: size of current event
@@ -629,8 +704,13 @@ size_t relay_switch_subbuf(struct rchan_buf *buf, size_t length)
               old_subbuf = buf->subbufs_produced % buf->chan->n_subbufs;
               buf->padding[old_subbuf] = buf->prev_padding;
               buf->subbufs_produced++;
-               buf->dentry->d_inode->i_size += buf->chan->subbuf_size -
-                       buf->padding[old_subbuf];
+               if (buf->dentry)
+                       buf->dentry->d_inode->i_size +=
+                               buf->chan->subbuf_size -
+                               buf->padding[old_subbuf];
+               else
+                       buf->early_bytes += buf->chan->subbuf_size -
+                                           buf->padding[old_subbuf];
               smp_mb();
               if (waitqueue_active(&buf->read_wait))
                       /*
@@ -1241,9 +1321,8 @@ EXPORT_SYMBOL_GPL(relay_file_operations);
 
 static __init int relay_init(void)
 {
-
       hotcpu_notifier(relay_hotcpu_callback, 0);
       return 0;
 }
 
-module_init(relay_init);
+early_initcall(relay_init);
--

1.5.5.4
--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in the body of a message to [email protected]
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/

#3 lore raw
Quoted 7 lines · On Sun, 22 Jun 2008 22:29:53 +0300 "Pekka Enberg" <[email protected]>…

kernel/relay.c void relay_reset(struct rchan *chan) · line 420

Quoted 3 lines · Eduard - Gabriel Munteanu
kernel/relay.c +3 -0
+       tmpname = kzalloc(NAME_MAX + 1, GFP_KERNEL);
+       if (!tmpname)
+               goto failed;

Why don't you return -ENOMEM here?

The original function from which I ripped this thing off (relay_open_buf()) returned 1 on error, so I assumed I wasn't supposed to change this behavior.

I should probably do this in a separate patch.

Quoted 2 lines

The separate goto seems pointless. Why don't you use negative return values for error handling?

Yes, it's better to use 'return' directly.

Quoted 17 lines
Quoted 17 lines

/**

+ *     relay_late_setup_files - triggers file creation
+ *     @chan: channel to operate on
+ *     @base_filename: base name of files to create
+ *     @parent: dentry of parent directory, %NULL for root

directory
+ *
+ * Returns 0 if successful, non-zero otherwise.
+ *
+ * Use to setup files for a previously buffer-only channel.
+ * Useful to do early tracing in kernel, before VFS is up, for
example.
+ */

kernel/relay.c +4 -0
+int relay_late_setup_files(struct rchan *chan,
+                          const char *base_filename,
+                          struct dentry *parent)
+{
Quoted 8 lines

kernel/relay.c free_bufs: · line 648

Quoted 6 lines
kernel/relay.c +6 -0
+       unsigned int i;
+       int err;
+       struct rchan_buf *buf;
+
+       if (!chan || !base_filename)
+               return 1;

-EINVAL?

Will do and resubmit. Being new code (called directly by other kernel users), it allows me to be a bit inconsistent about return values.

Quoted 6 lines
Quoted 6 lines
kernel/relay.c +6 -0
+               err = relay_setup_buf_file(chan, buf, i);
+               if (unlikely(err))
+                       goto out;
+       }
+       mutex_unlock(&relay_channels_mutex);
+
Quoted 8 lines

kernel/relay.c free_bufs: · line 681

Quoted 5 lines
kernel/relay.c +5 -0
+       return 0;
+
+out:
+       mutex_unlock(&relay_channels_mutex);
+       return 1;

You don't need two return statements (and coincidentally two unlocks) if you keep the return value in a local variable 'err'.

Thanks, I missed this possibility. Sounds better.

Eduard