Commit 23c88752 authored by Mathieu Desnoyers's avatar Mathieu Desnoyers Committed by Linus Torvalds

[PATCH] Relay: add CPU hotplug support

Mathieu originally needed to add this for tracing Xen, but it's something
that's needed for any application that can be tracing while cpus are added.

unplug isn't supported by this patch.  The thought was that at minumum a new
buffer needs to be added when a cpu comes up, but it wasn't worth the effort
to remove buffers on cpu down since they'd be freed soon anyway when the
channel was closed.

[zanussi@us.ibm.com: avoid lock_cpu_hotplug deadlock]
Signed-off-by: default avatarMathieu Desnoyers <mathieu.desnoyers@polymtl.ca>
Cc: Tom Zanussi <zanussi@us.ibm.com>
Signed-off-by: default avatarAndrew Morton <akpm@linux-foundation.org>
Signed-off-by: default avatarLinus Torvalds <torvalds@linux-foundation.org>
parent 138c5d25
...@@ -157,7 +157,7 @@ TBD(curr. line MT:/API/) ...@@ -157,7 +157,7 @@ TBD(curr. line MT:/API/)
channel management functions: channel management functions:
relay_open(base_filename, parent, subbuf_size, n_subbufs, relay_open(base_filename, parent, subbuf_size, n_subbufs,
callbacks) callbacks, private_data)
relay_close(chan) relay_close(chan)
relay_flush(chan) relay_flush(chan)
relay_reset(chan) relay_reset(chan)
...@@ -251,7 +251,7 @@ static struct rchan_callbacks relay_callbacks = ...@@ -251,7 +251,7 @@ static struct rchan_callbacks relay_callbacks =
And an example relay_open() invocation using them: And an example relay_open() invocation using them:
chan = relay_open("cpu", NULL, SUBBUF_SIZE, N_SUBBUFS, &relay_callbacks); chan = relay_open("cpu", NULL, SUBBUF_SIZE, N_SUBBUFS, &relay_callbacks, NULL);
If the create_buf_file() callback fails, or isn't defined, channel If the create_buf_file() callback fails, or isn't defined, channel
creation and thus relay_open() will fail. creation and thus relay_open() will fail.
...@@ -289,6 +289,11 @@ they use the proper locking for such a buffer, either by wrapping ...@@ -289,6 +289,11 @@ they use the proper locking for such a buffer, either by wrapping
writes in a spinlock, or by copying a write function from relay.h and writes in a spinlock, or by copying a write function from relay.h and
creating a local version that internally does the proper locking. creating a local version that internally does the proper locking.
The private_data passed into relay_open() allows clients to associate
user-defined data with a channel, and is immediately available
(including in create_buf_file()) via chan->private_data or
buf->chan->private_data.
Channel 'modes' Channel 'modes'
--------------- ---------------
......
...@@ -363,10 +363,9 @@ static int blk_trace_setup(request_queue_t *q, struct block_device *bdev, ...@@ -363,10 +363,9 @@ static int blk_trace_setup(request_queue_t *q, struct block_device *bdev,
if (!bt->dropped_file) if (!bt->dropped_file)
goto err; 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) if (!bt->rchan)
goto err; goto err;
bt->rchan->private_data = bt;
bt->act_mask = buts.act_mask; bt->act_mask = buts.act_mask;
if (!bt->act_mask) if (!bt->act_mask)
......
...@@ -24,7 +24,7 @@ ...@@ -24,7 +24,7 @@
/* /*
* Tracks changes to rchan/rchan_buf structs * Tracks changes to rchan/rchan_buf structs
*/ */
#define RELAYFS_CHANNEL_VERSION 6 #define RELAYFS_CHANNEL_VERSION 7
/* /*
* Per-cpu relay channel buffer * Per-cpu relay channel buffer
...@@ -64,6 +64,10 @@ struct rchan ...@@ -64,6 +64,10 @@ struct rchan
void *private_data; /* for user-defined data */ void *private_data; /* for user-defined data */
size_t last_toobig; /* tried to log event > subbuf size */ size_t last_toobig; /* tried to log event > subbuf size */
struct rchan_buf *buf[NR_CPUS]; /* per-cpu channel buffers */ 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 */
}; };
/* /*
...@@ -162,7 +166,8 @@ struct rchan *relay_open(const char *base_filename, ...@@ -162,7 +166,8 @@ struct rchan *relay_open(const char *base_filename,
struct dentry *parent, struct dentry *parent,
size_t subbuf_size, size_t subbuf_size,
size_t n_subbufs, 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_close(struct rchan *chan);
extern void relay_flush(struct rchan *chan); extern void relay_flush(struct rchan *chan);
extern void relay_subbufs_consumed(struct rchan *chan, extern void relay_subbufs_consumed(struct rchan *chan,
......
...@@ -7,6 +7,8 @@ ...@@ -7,6 +7,8 @@
* Copyright (C) 1999-2005 - Karim Yaghmour (karim@opersys.com) * Copyright (C) 1999-2005 - Karim Yaghmour (karim@opersys.com)
* *
* Moved to kernel/relay.c by Paul Mundt, 2006. * Moved to kernel/relay.c by Paul Mundt, 2006.
* November 2006 - CPU hotplug support by Mathieu Desnoyers
* (mathieu.desnoyers@polymtl.ca)
* *
* This file is released under the GPL. * This file is released under the GPL.
*/ */
...@@ -18,6 +20,11 @@ ...@@ -18,6 +20,11 @@
#include <linux/relay.h> #include <linux/relay.h>
#include <linux/vmalloc.h> #include <linux/vmalloc.h>
#include <linux/mm.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. * close() vm_op implementation for relay file mapping.
...@@ -187,6 +194,7 @@ void relay_destroy_buf(struct rchan_buf *buf) ...@@ -187,6 +194,7 @@ void relay_destroy_buf(struct rchan_buf *buf)
__free_page(buf->page_array[i]); __free_page(buf->page_array[i]);
kfree(buf->page_array); kfree(buf->page_array);
} }
chan->buf[buf->cpu] = NULL;
kfree(buf->padding); kfree(buf->padding);
kfree(buf); kfree(buf);
kref_put(&chan->kref, relay_destroy_channel); kref_put(&chan->kref, relay_destroy_channel);
...@@ -362,51 +370,69 @@ static void __relay_reset(struct rchan_buf *buf, unsigned int init) ...@@ -362,51 +370,69 @@ static void __relay_reset(struct rchan_buf *buf, unsigned int init)
void relay_reset(struct rchan *chan) void relay_reset(struct rchan *chan)
{ {
unsigned int i; unsigned int i;
struct rchan_buf *prev = NULL;
if (!chan) if (!chan)
return; return;
for (i = 0; i < NR_CPUS; i++) { if (chan->is_global && chan->buf[0]) {
if (!chan->buf[i] || chan->buf[i] == prev) __relay_reset(chan->buf[0], 0);
break; return;
__relay_reset(chan->buf[i], 0);
prev = chan->buf[i];
} }
mutex_lock(&relay_channels_mutex);
for_each_online_cpu(i)
if (chan->buf[i])
__relay_reset(chan->buf[i], 0);
mutex_unlock(&relay_channels_mutex);
} }
EXPORT_SYMBOL_GPL(relay_reset); EXPORT_SYMBOL_GPL(relay_reset);
/* /*
* relay_open_buf - create a new relay channel buffer * 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, static struct rchan_buf *relay_open_buf(struct rchan *chan, unsigned int cpu)
const char *filename,
struct dentry *parent,
int *is_global)
{ {
struct rchan_buf *buf; struct rchan_buf *buf = NULL;
struct dentry *dentry; struct dentry *dentry;
char *tmpname;
if (*is_global) if (chan->is_global)
return chan->buf[0]; 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); buf = relay_create_buf(chan);
if (!buf) if (!buf)
return NULL; goto free_name;
buf->cpu = cpu;
__relay_reset(buf, 1);
/* Create file in fs */ /* Create file in fs */
dentry = chan->cb->create_buf_file(filename, parent, S_IRUSR, dentry = chan->cb->create_buf_file(tmpname, chan->parent, S_IRUSR,
buf, is_global); buf, &chan->is_global);
if (!dentry) { if (!dentry)
relay_destroy_buf(buf); goto free_buf;
return NULL;
}
buf->dentry = dentry; buf->dentry = dentry;
__relay_reset(buf, 1);
if(chan->is_global) {
chan->buf[0] = buf;
buf->cpu = 0;
}
goto free_name;
free_buf:
relay_destroy_buf(buf);
free_name:
kfree(tmpname);
end:
return buf; return buf;
} }
...@@ -447,6 +473,47 @@ static void setup_callbacks(struct rchan *chan, ...@@ -447,6 +473,47 @@ static void setup_callbacks(struct rchan *chan,
chan->cb = cb; chan->cb = cb;
} }
/**
*
* 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) {
if (chan->buf[hotcpu])
continue;
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 * relay_open - create a new relay channel
* @base_filename: base name of files to create * @base_filename: base name of files to create
...@@ -454,6 +521,7 @@ static void setup_callbacks(struct rchan *chan, ...@@ -454,6 +521,7 @@ static void setup_callbacks(struct rchan *chan,
* @subbuf_size: size of sub-buffers * @subbuf_size: size of sub-buffers
* @n_subbufs: number of sub-buffers * @n_subbufs: number of sub-buffers
* @cb: client callback functions * @cb: client callback functions
* @private_data: user-defined data
* *
* Returns channel pointer if successful, %NULL otherwise. * Returns channel pointer if successful, %NULL otherwise.
* *
...@@ -466,13 +534,11 @@ struct rchan *relay_open(const char *base_filename, ...@@ -466,13 +534,11 @@ struct rchan *relay_open(const char *base_filename,
struct dentry *parent, struct dentry *parent,
size_t subbuf_size, size_t subbuf_size,
size_t n_subbufs, size_t n_subbufs,
struct rchan_callbacks *cb) struct rchan_callbacks *cb,
void *private_data)
{ {
unsigned int i; unsigned int i;
struct rchan *chan; struct rchan *chan;
char *tmpname;
int is_global = 0;
if (!base_filename) if (!base_filename)
return NULL; return NULL;
...@@ -487,38 +553,32 @@ struct rchan *relay_open(const char *base_filename, ...@@ -487,38 +553,32 @@ struct rchan *relay_open(const char *base_filename,
chan->n_subbufs = n_subbufs; chan->n_subbufs = n_subbufs;
chan->subbuf_size = subbuf_size; chan->subbuf_size = subbuf_size;
chan->alloc_size = FIX_SIZE(subbuf_size * n_subbufs); 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);
setup_callbacks(chan, cb); setup_callbacks(chan, cb);
kref_init(&chan->kref); kref_init(&chan->kref);
tmpname = kmalloc(NAME_MAX + 1, GFP_KERNEL); mutex_lock(&relay_channels_mutex);
if (!tmpname)
goto free_chan;
for_each_online_cpu(i) { for_each_online_cpu(i) {
sprintf(tmpname, "%s%d", base_filename, i); chan->buf[i] = relay_open_buf(chan, i);
chan->buf[i] = relay_open_buf(chan, tmpname, parent,
&is_global);
if (!chan->buf[i]) if (!chan->buf[i])
goto free_bufs; goto free_bufs;
chan->buf[i]->cpu = i;
} }
list_add(&chan->list, &relay_channels);
mutex_unlock(&relay_channels_mutex);
kfree(tmpname);
return chan; return chan;
free_bufs: free_bufs:
for (i = 0; i < NR_CPUS; i++) { for_each_online_cpu(i) {
if (!chan->buf[i]) if (!chan->buf[i])
break; break;
relay_close_buf(chan->buf[i]); relay_close_buf(chan->buf[i]);
if (is_global)
break;
} }
kfree(tmpname);
free_chan:
kref_put(&chan->kref, relay_destroy_channel); kref_put(&chan->kref, relay_destroy_channel);
mutex_unlock(&relay_channels_mutex);
return NULL; return NULL;
} }
EXPORT_SYMBOL_GPL(relay_open); EXPORT_SYMBOL_GPL(relay_open);
...@@ -619,24 +679,26 @@ EXPORT_SYMBOL_GPL(relay_subbufs_consumed); ...@@ -619,24 +679,26 @@ EXPORT_SYMBOL_GPL(relay_subbufs_consumed);
void relay_close(struct rchan *chan) void relay_close(struct rchan *chan)
{ {
unsigned int i; unsigned int i;
struct rchan_buf *prev = NULL;
if (!chan) if (!chan)
return; return;
for (i = 0; i < NR_CPUS; i++) { mutex_lock(&relay_channels_mutex);
if (!chan->buf[i] || chan->buf[i] == prev) if (chan->is_global && chan->buf[0])
break; relay_close_buf(chan->buf[0]);
else
for_each_possible_cpu(i)
if (chan->buf[i])
relay_close_buf(chan->buf[i]); relay_close_buf(chan->buf[i]);
prev = chan->buf[i];
}
if (chan->last_toobig) if (chan->last_toobig)
printk(KERN_WARNING "relay: one or more items not logged " printk(KERN_WARNING "relay: one or more items not logged "
"[item size (%Zd) > sub-buffer size (%Zd)]\n", "[item size (%Zd) > sub-buffer size (%Zd)]\n",
chan->last_toobig, chan->subbuf_size); chan->last_toobig, chan->subbuf_size);
list_del(&chan->list);
kref_put(&chan->kref, relay_destroy_channel); kref_put(&chan->kref, relay_destroy_channel);
mutex_unlock(&relay_channels_mutex);
} }
EXPORT_SYMBOL_GPL(relay_close); EXPORT_SYMBOL_GPL(relay_close);
...@@ -649,17 +711,20 @@ EXPORT_SYMBOL_GPL(relay_close); ...@@ -649,17 +711,20 @@ EXPORT_SYMBOL_GPL(relay_close);
void relay_flush(struct rchan *chan) void relay_flush(struct rchan *chan)
{ {
unsigned int i; unsigned int i;
struct rchan_buf *prev = NULL;
if (!chan) if (!chan)
return; return;
for (i = 0; i < NR_CPUS; i++) { if (chan->is_global && chan->buf[0]) {
if (!chan->buf[i] || chan->buf[i] == prev) relay_switch_subbuf(chan->buf[0], 0);
break; return;
relay_switch_subbuf(chan->buf[i], 0);
prev = chan->buf[i];
} }
mutex_lock(&relay_channels_mutex);
for_each_possible_cpu(i)
if (chan->buf[i])
relay_switch_subbuf(chan->buf[i], 0);
mutex_unlock(&relay_channels_mutex);
} }
EXPORT_SYMBOL_GPL(relay_flush); EXPORT_SYMBOL_GPL(relay_flush);
...@@ -1022,3 +1087,12 @@ const struct file_operations relay_file_operations = { ...@@ -1022,3 +1087,12 @@ const struct file_operations relay_file_operations = {
.sendfile = relay_file_sendfile, .sendfile = relay_file_sendfile,
}; };
EXPORT_SYMBOL_GPL(relay_file_operations); EXPORT_SYMBOL_GPL(relay_file_operations);
static __init int relay_init(void)
{
hotcpu_notifier(relay_hotcpu_callback, 0);
return 0;
}
module_init(relay_init);
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment