forked from torvalds/linux
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
[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. [[email protected]: avoid lock_cpu_hotplug deadlock] Signed-off-by: Mathieu Desnoyers <[email protected]> Cc: Tom Zanussi <[email protected]> Signed-off-by: Andrew Morton <[email protected]> Signed-off-by: Linus Torvalds <[email protected]>
- Loading branch information
Mathieu Desnoyers
authored and
Linus Torvalds
committed
Feb 11, 2007
1 parent
138c5d2
commit 23c8875
Showing
4 changed files
with
142 additions
and
59 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -7,6 +7,8 @@ | |
* Copyright (C) 1999-2005 - Karim Yaghmour ([email protected]) | ||
* | ||
* Moved to kernel/relay.c by Paul Mundt, 2006. | ||
* November 2006 - CPU hotplug support by Mathieu Desnoyers | ||
* ([email protected]) | ||
* | ||
* This file is released under the GPL. | ||
*/ | ||
|
@@ -18,6 +20,11 @@ | |
#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. | ||
|
@@ -187,6 +194,7 @@ void relay_destroy_buf(struct rchan_buf *buf) | |
__free_page(buf->page_array[i]); | ||
kfree(buf->page_array); | ||
} | ||
chan->buf[buf->cpu] = NULL; | ||
kfree(buf->padding); | ||
kfree(buf); | ||
kref_put(&chan->kref, relay_destroy_channel); | ||
|
@@ -362,51 +370,69 @@ static void __relay_reset(struct rchan_buf *buf, unsigned int init) | |
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]; | ||
if (chan->is_global && chan->buf[0]) { | ||
__relay_reset(chan->buf[0], 0); | ||
return; | ||
} | ||
|
||
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); | ||
|
||
/* | ||
* 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) | ||
static 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) | ||
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) | ||
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; | ||
|
||
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; | ||
} | ||
|
||
|
@@ -447,13 +473,55 @@ static void setup_callbacks(struct rchan *chan, | |
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 | ||
* @base_filename: base name of files to create | ||
* @parent: dentry of parent directory, %NULL for root directory | ||
* @subbuf_size: size of sub-buffers | ||
* @n_subbufs: number of sub-buffers | ||
* @cb: client callback functions | ||
* @private_data: user-defined data | ||
* | ||
* Returns channel pointer if successful, %NULL otherwise. | ||
* | ||
|
@@ -466,13 +534,11 @@ struct rchan *relay_open(const char *base_filename, | |
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; | ||
|
||
|
@@ -487,38 +553,32 @@ struct rchan *relay_open(const char *base_filename, | |
chan->n_subbufs = n_subbufs; | ||
chan->subbuf_size = subbuf_size; | ||
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); | ||
kref_init(&chan->kref); | ||
|
||
tmpname = kmalloc(NAME_MAX + 1, GFP_KERNEL); | ||
if (!tmpname) | ||
goto free_chan; | ||
|
||
mutex_lock(&relay_channels_mutex); | ||
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; | ||
} | ||
list_add(&chan->list, &relay_channels); | ||
mutex_unlock(&relay_channels_mutex); | ||
|
||
kfree(tmpname); | ||
return chan; | ||
|
||
free_bufs: | ||
for (i = 0; i < NR_CPUS; i++) { | ||
for_each_online_cpu(i) { | ||
if (!chan->buf[i]) | ||
break; | ||
relay_close_buf(chan->buf[i]); | ||
if (is_global) | ||
break; | ||
} | ||
kfree(tmpname); | ||
|
||
free_chan: | ||
kref_put(&chan->kref, relay_destroy_channel); | ||
mutex_unlock(&relay_channels_mutex); | ||
return NULL; | ||
} | ||
EXPORT_SYMBOL_GPL(relay_open); | ||
|
@@ -619,24 +679,26 @@ 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]; | ||
} | ||
mutex_lock(&relay_channels_mutex); | ||
if (chan->is_global && 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); | ||
|
||
list_del(&chan->list); | ||
kref_put(&chan->kref, relay_destroy_channel); | ||
mutex_unlock(&relay_channels_mutex); | ||
} | ||
EXPORT_SYMBOL_GPL(relay_close); | ||
|
||
|
@@ -649,17 +711,20 @@ 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]; | ||
if (chan->is_global && chan->buf[0]) { | ||
relay_switch_subbuf(chan->buf[0], 0); | ||
return; | ||
} | ||
|
||
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); | ||
|
||
|
@@ -1022,3 +1087,12 @@ const struct file_operations relay_file_operations = { | |
.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); |