[PATCH 2/16] LTTng 0.6.36 for 2.6.18 : relay hotplug support

From: Mathieu Desnoyers
Date: Fri Nov 24 2006 - 16:53:11 EST


Patch submitted to Tom Zanussi to add hotplug support to relay.

patch02-2.6.18-lttng-core-0.6.36-relay.diff

Signed-off-by : Mathieu Desnoyers <mathieu.desnoyers@xxxxxxxxxx>

--BEGIN--
--- a/include/linux/relay.h
+++ b/include/linux/relay.h
@@ -24,7 +24,7 @@ #define FIX_SIZE(x) ((((x) - 1) & PAGE_M
/*
* Tracks changes to rchan/rchan_buf structs
*/
-#define RELAYFS_CHANNEL_VERSION 6
+#define RELAYFS_CHANNEL_VERSION 7

/*
* Per-cpu relay channel buffer
@@ -41,6 +41,7 @@ struct rchan_buf
struct work_struct wake_readers; /* reader wake-up work struct */
struct dentry *dentry; /* channel file dentry */
struct kref kref; /* channel buffer refcount */
+ struct kref krefw; /* channel buffer writers refcount */
struct page **page_array; /* array of current buffer pages */
unsigned int page_count; /* number of current buffer pages */
unsigned int finalized; /* buffer has been finalized */
@@ -64,6 +65,10 @@ struct rchan
void *private_data; /* for user-defined data */
size_t last_toobig; /* tried to log event > subbuf size */
struct rchan_buf *buf[NR_CPUS]; /* per-cpu channel buffers */
+ int is_global; /* One global buffer ? */
+ struct list_head list; /* for channel list */
+ struct dentry *parent; /* parent dentry passed to open */
+ char base_filename[NAME_MAX]; /* saved base filename */
};

/*
@@ -133,6 +138,8 @@ struct rchan_callbacks
* cause relay_open() to create a single global buffer rather
* than the default set of per-cpu buffers.
*
+ * buf->chan->buf[buf->cpu] is not set when this callback is called.
+ *
* See Documentation/filesystems/relayfs.txt for more info.
*/
struct dentry *(*create_buf_file)(const char *filename,
@@ -162,7 +169,8 @@ struct rchan *relay_open(const char *bas
struct dentry *parent,
size_t subbuf_size,
size_t n_subbufs,
- struct rchan_callbacks *cb);
+ struct rchan_callbacks *cb,
+ void *private_data);
extern void relay_close(struct rchan *chan);
extern void relay_flush(struct rchan *chan);
extern void relay_subbufs_consumed(struct rchan *chan,
--- a/kernel/relay.c
+++ b/kernel/relay.c
@@ -7,8 +7,14 @@
* Copyright (C) 1999-2005 - Karim Yaghmour (karim@xxxxxxxxxxx)
*
* Moved to kernel/relay.c by Paul Mundt, 2006.
+ * November 2006 - CPU hotplug support by Mathieu Desnoyers
+ * (mathieu.desnoyers@xxxxxxxxxx)
*
* This file is released under the GPL.
+ *
+ * In case CPU_DEAD CPU hotplug is someday implemented, we protect against
+ * buffer removal by locking cpu hotplug around each test on buf[cpu] being
+ * NULL everywhere in the file.
*/
#include <linux/errno.h>
#include <linux/stddef.h>
@@ -18,6 +24,11 @@ #include <linux/string.h>
#include <linux/relay.h>
#include <linux/vmalloc.h>
#include <linux/mm.h>
+#include <linux/cpu.h>
+
+/* list of open channels, for cpu hotplug */
+static DEFINE_MUTEX(relay_channels_mutex);
+static LIST_HEAD(relay_channels);

/*
* close() vm_op implementation for relay file mapping.
@@ -189,6 +200,7 @@ void relay_destroy_buf(struct rchan_buf
}
kfree(buf->padding);
kfree(buf);
+ chan->buf[buf->cpu] = NULL;
kref_put(&chan->kref, relay_destroy_channel);
}

@@ -327,6 +339,7 @@ static inline void __relay_reset(struct
if (init) {
init_waitqueue_head(&buf->read_wait);
kref_init(&buf->kref);
+ kref_init(&buf->krefw);
INIT_WORK(&buf->wake_readers, NULL, NULL);
} else {
cancel_delayed_work(&buf->wake_readers);
@@ -360,70 +373,104 @@ static inline void __relay_reset(struct
void relay_reset(struct rchan *chan)
{
unsigned int i;
- struct rchan_buf *prev = NULL;

if (!chan)
return;
-
- for (i = 0; i < NR_CPUS; i++) {
- if (!chan->buf[i] || chan->buf[i] == prev)
- break;
- __relay_reset(chan->buf[i], 0);
- prev = chan->buf[i];
+ lock_cpu_hotplug();
+ if (chan->is_global) {
+ if (chan->buf[0])
+ __relay_reset(chan->buf[0], 0);
+ } else {
+ for_each_online_cpu(i)
+ if (chan->buf[i])
+ __relay_reset(chan->buf[i], 0);
}
+ unlock_cpu_hotplug();
}
EXPORT_SYMBOL_GPL(relay_reset);

/**
* relay_open_buf - create a new relay channel buffer
*
- * Internal - used by relay_open().
+ * used by relay_open() and CPU hotplug.
*/
-static struct rchan_buf *relay_open_buf(struct rchan *chan,
- const char *filename,
- struct dentry *parent,
- int *is_global)
+struct rchan_buf *relay_open_buf(struct rchan *chan, unsigned int cpu)
{
- struct rchan_buf *buf;
+ struct rchan_buf *buf = NULL;
struct dentry *dentry;
+ char *tmpname;

- if (*is_global)
+ if (chan->is_global) {
+ kref_get(&chan->buf[0]->krefw);
return chan->buf[0];
+ }
+
+ tmpname = kmalloc(NAME_MAX + 1, GFP_KERNEL);
+ if (!tmpname)
+ goto end;
+ sprintf(tmpname, "%s%d", chan->base_filename, cpu);

buf = relay_create_buf(chan);
if (!buf)
- return NULL;
+ goto free_name;
+
+ buf->cpu = cpu;
+ __relay_reset(buf, 1);

/* Create file in fs */
- dentry = chan->cb->create_buf_file(filename, parent, S_IRUSR,
- buf, is_global);
- if (!dentry) {
- relay_destroy_buf(buf);
- return NULL;
+ dentry = chan->cb->create_buf_file(tmpname, chan->parent, S_IRUSR,
+ buf, &chan->is_global);
+ if (!dentry)
+ goto free_buf;
+
+ if(chan->is_global) {
+ chan->buf[0] = buf;
+ buf->cpu = 0;
}

buf->dentry = dentry;
- __relay_reset(buf, 1);
+ goto free_name;

+free_buf:
+ relay_destroy_buf(buf);
+free_name:
+ kfree(tmpname);
+end:
return buf;
}
+EXPORT_SYMBOL_GPL(relay_open_buf);

/**
- * relay_close_buf - close a channel buffer
- * @buf: channel buffer
+ * relay_close_write_buf - close write to a channel buffer
+ * @kref: buffer writers reference
*
* Marks the buffer finalized and restores the default callbacks.
* The channel buffer and channel buffer data structure are then freed
* automatically when the last reference is given up.
*/
-static inline void relay_close_buf(struct rchan_buf *buf)
+static void relay_close_write_buf(struct kref *kref)
{
+ struct rchan_buf *buf = container_of(kref, struct rchan_buf, krefw);
+
buf->finalized = 1;
cancel_delayed_work(&buf->wake_readers);
flush_scheduled_work();
kref_put(&buf->kref, relay_remove_buf);
}

+/**
+ * relay_close_buf - close a channel buffer
+ * @buf: channel buffer
+ *
+ * Remove a writer reference.
+ */
+
+void relay_close_buf(struct rchan_buf *buf)
+{
+ kref_put(&buf->krefw, relay_close_write_buf);
+}
+EXPORT_SYMBOL_GPL(relay_close_buf);
+
static inline void setup_callbacks(struct rchan *chan,
struct rchan_callbacks *cb)
{
@@ -446,6 +493,45 @@ static inline void setup_callbacks(struc
}

/**
+ *
+ * relay_hotcpu_callback - CPU hotplug callback
+ * @nb: notifier block
+ * @action: hotplug action to take
+ * @hcpu: CPU number
+ *
+ * Returns the success/failure of the operation. (NOTIFY_OK, NOTIFY_BAD)
+ */
+static int __cpuinit relay_hotcpu_callback(struct notifier_block *nb,
+ unsigned long action,
+ void *hcpu)
+{
+ unsigned int hotcpu = (unsigned long)hcpu;
+ struct rchan *chan;
+
+ switch(action) {
+ case CPU_UP_PREPARE:
+ mutex_lock(&relay_channels_mutex);
+ list_for_each_entry(chan, &relay_channels, list) {
+ chan->buf[hotcpu] = relay_open_buf(chan, hotcpu);
+ if(!chan->buf[hotcpu]) {
+ printk(KERN_ERR
+ "relay_hotcpu_callback: cpu %d buffer "
+ "creation failed\n", hotcpu);
+ mutex_unlock(&relay_channels_mutex);
+ return NOTIFY_BAD;
+ }
+ }
+ mutex_unlock(&relay_channels_mutex);
+ break;
+ case CPU_DEAD:
+ /* No need to flush the cpu : will be flushed upon
+ * final relay_flush() call. */
+ break;
+ }
+ return NOTIFY_OK;
+}
+
+/**
* relay_open - create a new relay channel
* @base_filename: base name of files to create
* @parent: dentry of parent directory, NULL for root directory
@@ -464,13 +550,11 @@ struct rchan *relay_open(const char *bas
struct dentry *parent,
size_t subbuf_size,
size_t n_subbufs,
- struct rchan_callbacks *cb)
+ struct rchan_callbacks *cb,
+ void *private_data)
{
unsigned int i;
struct rchan *chan;
- char *tmpname;
- int is_global = 0;
-
if (!base_filename)
return NULL;

@@ -485,38 +569,40 @@ struct rchan *relay_open(const char *bas
chan->n_subbufs = n_subbufs;
chan->subbuf_size = subbuf_size;
chan->alloc_size = FIX_SIZE(subbuf_size * n_subbufs);
+ chan->is_global = 0;
+ chan->parent = parent;
+ chan->private_data = private_data;
+ strcpy(chan->base_filename, base_filename);
setup_callbacks(chan, cb);
kref_init(&chan->kref);

- tmpname = kmalloc(NAME_MAX + 1, GFP_KERNEL);
- if (!tmpname)
- goto free_chan;
-
+ lock_cpu_hotplug();
for_each_online_cpu(i) {
- sprintf(tmpname, "%s%d", base_filename, i);
- chan->buf[i] = relay_open_buf(chan, tmpname, parent,
- &is_global);
+ chan->buf[i] = relay_open_buf(chan, i);
if (!chan->buf[i])
goto free_bufs;
-
- chan->buf[i]->cpu = i;
}
+ mutex_lock(&relay_channels_mutex);
+ list_add(&chan->list, &relay_channels);
+ mutex_unlock(&relay_channels_mutex);
+ unlock_cpu_hotplug();

- kfree(tmpname);
return chan;

free_bufs:
- for (i = 0; i < NR_CPUS; i++) {
- if (!chan->buf[i])
- break;
- relay_close_buf(chan->buf[i]);
- if (is_global)
- break;
+ if (chan->is_global) {
+ if (chan->buf[0])
+ relay_close_buf(chan->buf[0]);
+ } else {
+ for_each_online_cpu(i) {
+ if (!chan->buf[i])
+ break;
+ relay_close_buf(chan->buf[i]);
+ }
}
- kfree(tmpname);

-free_chan:
kref_put(&chan->kref, relay_destroy_channel);
+ unlock_cpu_hotplug();
return NULL;
}
EXPORT_SYMBOL_GPL(relay_open);
@@ -616,24 +702,29 @@ EXPORT_SYMBOL_GPL(relay_subbufs_consumed
void relay_close(struct rchan *chan)
{
unsigned int i;
- struct rchan_buf *prev = NULL;

if (!chan)
return;

- for (i = 0; i < NR_CPUS; i++) {
- if (!chan->buf[i] || chan->buf[i] == prev)
- break;
- relay_close_buf(chan->buf[i]);
- prev = chan->buf[i];
- }
+ lock_cpu_hotplug();
+ if (chan->is_global) {
+ if(chan->buf[0])
+ relay_close_buf(chan->buf[0]);
+ } else
+ for_each_possible_cpu(i)
+ if (chan->buf[i])
+ relay_close_buf(chan->buf[i]);

if (chan->last_toobig)
printk(KERN_WARNING "relay: one or more items not logged "
"[item size (%Zd) > sub-buffer size (%Zd)]\n",
chan->last_toobig, chan->subbuf_size);

+ mutex_lock(&relay_channels_mutex);
+ list_del(&chan->list);
+ mutex_unlock(&relay_channels_mutex);
kref_put(&chan->kref, relay_destroy_channel);
+ unlock_cpu_hotplug();
}
EXPORT_SYMBOL_GPL(relay_close);

@@ -646,17 +737,18 @@ EXPORT_SYMBOL_GPL(relay_close);
void relay_flush(struct rchan *chan)
{
unsigned int i;
- struct rchan_buf *prev = NULL;

if (!chan)
return;
-
- for (i = 0; i < NR_CPUS; i++) {
- if (!chan->buf[i] || chan->buf[i] == prev)
- break;
- relay_switch_subbuf(chan->buf[i], 0);
- prev = chan->buf[i];
- }
+ lock_cpu_hotplug();
+ if (chan->is_global) {
+ if(chan->buf[0])
+ relay_switch_subbuf(chan->buf[0], 0);
+ } else
+ for_each_possible_cpu(i)
+ if (chan->buf[i])
+ relay_switch_subbuf(chan->buf[i], 0);
+ unlock_cpu_hotplug();
}
EXPORT_SYMBOL_GPL(relay_flush);

@@ -1010,3 +1102,12 @@ struct file_operations relay_file_operat
.sendfile = relay_file_sendfile,
};
EXPORT_SYMBOL_GPL(relay_file_operations);
+
+static __init int relay_init(void)
+{
+
+ hotcpu_notifier(relay_hotcpu_callback, 0);
+ return 0;
+}
+
+module_init(relay_init);
--- a/block/blktrace.c
+++ b/block/blktrace.c
@@ -332,10 +332,9 @@ static int blk_trace_setup(request_queue
if (!bt->dropped_file)
goto err;

- bt->rchan = relay_open("trace", dir, buts.buf_size, buts.buf_nr, &blk_relay_callbacks);
+ bt->rchan = relay_open("trace", dir, buts.buf_size, buts.buf_nr, &blk_relay_callbacks, bt);
if (!bt->rchan)
goto err;
- bt->rchan->private_data = bt;

bt->act_mask = buts.act_mask;
if (!bt->act_mask)
--END--

OpenPGP public key: http://krystal.dyndns.org:8080/key/compudj.gpg
Key fingerprint: 8CD5 52C3 8E3C 4140 715F BA06 3F25 A8FE 3BAE 9A68
-
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/