diff options
Diffstat (limited to 'kernel')
-rw-r--r-- | kernel/relay.c | 180 |
1 files changed, 127 insertions, 53 deletions
diff --git a/kernel/relay.c b/kernel/relay.c index 284e2e8b4eed..ef923f6de2e7 100644 --- a/kernel/relay.c +++ b/kernel/relay.c | |||
@@ -7,6 +7,8 @@ | |||
7 | * Copyright (C) 1999-2005 - Karim Yaghmour (karim@opersys.com) | 7 | * Copyright (C) 1999-2005 - Karim Yaghmour (karim@opersys.com) |
8 | * | 8 | * |
9 | * Moved to kernel/relay.c by Paul Mundt, 2006. | 9 | * Moved to kernel/relay.c by Paul Mundt, 2006. |
10 | * November 2006 - CPU hotplug support by Mathieu Desnoyers | ||
11 | * (mathieu.desnoyers@polymtl.ca) | ||
10 | * | 12 | * |
11 | * This file is released under the GPL. | 13 | * This file is released under the GPL. |
12 | */ | 14 | */ |
@@ -18,6 +20,11 @@ | |||
18 | #include <linux/relay.h> | 20 | #include <linux/relay.h> |
19 | #include <linux/vmalloc.h> | 21 | #include <linux/vmalloc.h> |
20 | #include <linux/mm.h> | 22 | #include <linux/mm.h> |
23 | #include <linux/cpu.h> | ||
24 | |||
25 | /* list of open channels, for cpu hotplug */ | ||
26 | static DEFINE_MUTEX(relay_channels_mutex); | ||
27 | static LIST_HEAD(relay_channels); | ||
21 | 28 | ||
22 | /* | 29 | /* |
23 | * close() vm_op implementation for relay file mapping. | 30 | * close() vm_op implementation for relay file mapping. |
@@ -187,6 +194,7 @@ void relay_destroy_buf(struct rchan_buf *buf) | |||
187 | __free_page(buf->page_array[i]); | 194 | __free_page(buf->page_array[i]); |
188 | kfree(buf->page_array); | 195 | kfree(buf->page_array); |
189 | } | 196 | } |
197 | chan->buf[buf->cpu] = NULL; | ||
190 | kfree(buf->padding); | 198 | kfree(buf->padding); |
191 | kfree(buf); | 199 | kfree(buf); |
192 | kref_put(&chan->kref, relay_destroy_channel); | 200 | kref_put(&chan->kref, relay_destroy_channel); |
@@ -362,51 +370,69 @@ static void __relay_reset(struct rchan_buf *buf, unsigned int init) | |||
362 | void relay_reset(struct rchan *chan) | 370 | void relay_reset(struct rchan *chan) |
363 | { | 371 | { |
364 | unsigned int i; | 372 | unsigned int i; |
365 | struct rchan_buf *prev = NULL; | ||
366 | 373 | ||
367 | if (!chan) | 374 | if (!chan) |
368 | return; | 375 | return; |
369 | 376 | ||
370 | for (i = 0; i < NR_CPUS; i++) { | 377 | if (chan->is_global && chan->buf[0]) { |
371 | if (!chan->buf[i] || chan->buf[i] == prev) | 378 | __relay_reset(chan->buf[0], 0); |
372 | break; | 379 | return; |
373 | __relay_reset(chan->buf[i], 0); | ||
374 | prev = chan->buf[i]; | ||
375 | } | 380 | } |
381 | |||
382 | mutex_lock(&relay_channels_mutex); | ||
383 | for_each_online_cpu(i) | ||
384 | if (chan->buf[i]) | ||
385 | __relay_reset(chan->buf[i], 0); | ||
386 | mutex_unlock(&relay_channels_mutex); | ||
376 | } | 387 | } |
377 | EXPORT_SYMBOL_GPL(relay_reset); | 388 | EXPORT_SYMBOL_GPL(relay_reset); |
378 | 389 | ||
379 | /* | 390 | /* |
380 | * relay_open_buf - create a new relay channel buffer | 391 | * relay_open_buf - create a new relay channel buffer |
381 | * | 392 | * |
382 | * Internal - used by relay_open(). | 393 | * used by relay_open() and CPU hotplug. |
383 | */ | 394 | */ |
384 | static struct rchan_buf *relay_open_buf(struct rchan *chan, | 395 | static struct rchan_buf *relay_open_buf(struct rchan *chan, unsigned int cpu) |
385 | const char *filename, | ||
386 | struct dentry *parent, | ||
387 | int *is_global) | ||
388 | { | 396 | { |
389 | struct rchan_buf *buf; | 397 | struct rchan_buf *buf = NULL; |
390 | struct dentry *dentry; | 398 | struct dentry *dentry; |
399 | char *tmpname; | ||
391 | 400 | ||
392 | if (*is_global) | 401 | if (chan->is_global) |
393 | return chan->buf[0]; | 402 | return chan->buf[0]; |
394 | 403 | ||
404 | tmpname = kzalloc(NAME_MAX + 1, GFP_KERNEL); | ||
405 | if (!tmpname) | ||
406 | goto end; | ||
407 | snprintf(tmpname, NAME_MAX, "%s%d", chan->base_filename, cpu); | ||
408 | |||
395 | buf = relay_create_buf(chan); | 409 | buf = relay_create_buf(chan); |
396 | if (!buf) | 410 | if (!buf) |
397 | return NULL; | 411 | goto free_name; |
412 | |||
413 | buf->cpu = cpu; | ||
414 | __relay_reset(buf, 1); | ||
398 | 415 | ||
399 | /* Create file in fs */ | 416 | /* Create file in fs */ |
400 | dentry = chan->cb->create_buf_file(filename, parent, S_IRUSR, | 417 | dentry = chan->cb->create_buf_file(tmpname, chan->parent, S_IRUSR, |
401 | buf, is_global); | 418 | buf, &chan->is_global); |
402 | if (!dentry) { | 419 | if (!dentry) |
403 | relay_destroy_buf(buf); | 420 | goto free_buf; |
404 | return NULL; | ||
405 | } | ||
406 | 421 | ||
407 | buf->dentry = dentry; | 422 | buf->dentry = dentry; |
408 | __relay_reset(buf, 1); | ||
409 | 423 | ||
424 | if(chan->is_global) { | ||
425 | chan->buf[0] = buf; | ||
426 | buf->cpu = 0; | ||
427 | } | ||
428 | |||
429 | goto free_name; | ||
430 | |||
431 | free_buf: | ||
432 | relay_destroy_buf(buf); | ||
433 | free_name: | ||
434 | kfree(tmpname); | ||
435 | end: | ||
410 | return buf; | 436 | return buf; |
411 | } | 437 | } |
412 | 438 | ||
@@ -448,12 +474,54 @@ static void setup_callbacks(struct rchan *chan, | |||
448 | } | 474 | } |
449 | 475 | ||
450 | /** | 476 | /** |
477 | * | ||
478 | * relay_hotcpu_callback - CPU hotplug callback | ||
479 | * @nb: notifier block | ||
480 | * @action: hotplug action to take | ||
481 | * @hcpu: CPU number | ||
482 | * | ||
483 | * Returns the success/failure of the operation. (NOTIFY_OK, NOTIFY_BAD) | ||
484 | */ | ||
485 | static int __cpuinit relay_hotcpu_callback(struct notifier_block *nb, | ||
486 | unsigned long action, | ||
487 | void *hcpu) | ||
488 | { | ||
489 | unsigned int hotcpu = (unsigned long)hcpu; | ||
490 | struct rchan *chan; | ||
491 | |||
492 | switch(action) { | ||
493 | case CPU_UP_PREPARE: | ||
494 | mutex_lock(&relay_channels_mutex); | ||
495 | list_for_each_entry(chan, &relay_channels, list) { | ||
496 | if (chan->buf[hotcpu]) | ||
497 | continue; | ||
498 | chan->buf[hotcpu] = relay_open_buf(chan, hotcpu); | ||
499 | if(!chan->buf[hotcpu]) { | ||
500 | printk(KERN_ERR | ||
501 | "relay_hotcpu_callback: cpu %d buffer " | ||
502 | "creation failed\n", hotcpu); | ||
503 | mutex_unlock(&relay_channels_mutex); | ||
504 | return NOTIFY_BAD; | ||
505 | } | ||
506 | } | ||
507 | mutex_unlock(&relay_channels_mutex); | ||
508 | break; | ||
509 | case CPU_DEAD: | ||
510 | /* No need to flush the cpu : will be flushed upon | ||
511 | * final relay_flush() call. */ | ||
512 | break; | ||
513 | } | ||
514 | return NOTIFY_OK; | ||
515 | } | ||
516 | |||
517 | /** | ||
451 | * relay_open - create a new relay channel | 518 | * relay_open - create a new relay channel |
452 | * @base_filename: base name of files to create | 519 | * @base_filename: base name of files to create |
453 | * @parent: dentry of parent directory, %NULL for root directory | 520 | * @parent: dentry of parent directory, %NULL for root directory |
454 | * @subbuf_size: size of sub-buffers | 521 | * @subbuf_size: size of sub-buffers |
455 | * @n_subbufs: number of sub-buffers | 522 | * @n_subbufs: number of sub-buffers |
456 | * @cb: client callback functions | 523 | * @cb: client callback functions |
524 | * @private_data: user-defined data | ||
457 | * | 525 | * |
458 | * Returns channel pointer if successful, %NULL otherwise. | 526 | * Returns channel pointer if successful, %NULL otherwise. |
459 | * | 527 | * |
@@ -466,13 +534,11 @@ struct rchan *relay_open(const char *base_filename, | |||
466 | struct dentry *parent, | 534 | struct dentry *parent, |
467 | size_t subbuf_size, | 535 | size_t subbuf_size, |
468 | size_t n_subbufs, | 536 | size_t n_subbufs, |
469 | struct rchan_callbacks *cb) | 537 | struct rchan_callbacks *cb, |
538 | void *private_data) | ||
470 | { | 539 | { |
471 | unsigned int i; | 540 | unsigned int i; |
472 | struct rchan *chan; | 541 | struct rchan *chan; |
473 | char *tmpname; | ||
474 | int is_global = 0; | ||
475 | |||
476 | if (!base_filename) | 542 | if (!base_filename) |
477 | return NULL; | 543 | return NULL; |
478 | 544 | ||
@@ -487,38 +553,32 @@ struct rchan *relay_open(const char *base_filename, | |||
487 | chan->n_subbufs = n_subbufs; | 553 | chan->n_subbufs = n_subbufs; |
488 | chan->subbuf_size = subbuf_size; | 554 | chan->subbuf_size = subbuf_size; |
489 | chan->alloc_size = FIX_SIZE(subbuf_size * n_subbufs); | 555 | chan->alloc_size = FIX_SIZE(subbuf_size * n_subbufs); |
556 | chan->parent = parent; | ||
557 | chan->private_data = private_data; | ||
558 | strlcpy(chan->base_filename, base_filename, NAME_MAX); | ||
490 | setup_callbacks(chan, cb); | 559 | setup_callbacks(chan, cb); |
491 | kref_init(&chan->kref); | 560 | kref_init(&chan->kref); |
492 | 561 | ||
493 | tmpname = kmalloc(NAME_MAX + 1, GFP_KERNEL); | 562 | mutex_lock(&relay_channels_mutex); |
494 | if (!tmpname) | ||
495 | goto free_chan; | ||
496 | |||
497 | for_each_online_cpu(i) { | 563 | for_each_online_cpu(i) { |
498 | sprintf(tmpname, "%s%d", base_filename, i); | 564 | chan->buf[i] = relay_open_buf(chan, i); |
499 | chan->buf[i] = relay_open_buf(chan, tmpname, parent, | ||
500 | &is_global); | ||
501 | if (!chan->buf[i]) | 565 | if (!chan->buf[i]) |
502 | goto free_bufs; | 566 | goto free_bufs; |
503 | |||
504 | chan->buf[i]->cpu = i; | ||
505 | } | 567 | } |
568 | list_add(&chan->list, &relay_channels); | ||
569 | mutex_unlock(&relay_channels_mutex); | ||
506 | 570 | ||
507 | kfree(tmpname); | ||
508 | return chan; | 571 | return chan; |
509 | 572 | ||
510 | free_bufs: | 573 | free_bufs: |
511 | for (i = 0; i < NR_CPUS; i++) { | 574 | for_each_online_cpu(i) { |
512 | if (!chan->buf[i]) | 575 | if (!chan->buf[i]) |
513 | break; | 576 | break; |
514 | relay_close_buf(chan->buf[i]); | 577 | relay_close_buf(chan->buf[i]); |
515 | if (is_global) | ||
516 | break; | ||
517 | } | 578 | } |
518 | kfree(tmpname); | ||
519 | 579 | ||
520 | free_chan: | ||
521 | kref_put(&chan->kref, relay_destroy_channel); | 580 | kref_put(&chan->kref, relay_destroy_channel); |
581 | mutex_unlock(&relay_channels_mutex); | ||
522 | return NULL; | 582 | return NULL; |
523 | } | 583 | } |
524 | EXPORT_SYMBOL_GPL(relay_open); | 584 | EXPORT_SYMBOL_GPL(relay_open); |
@@ -619,24 +679,26 @@ EXPORT_SYMBOL_GPL(relay_subbufs_consumed); | |||
619 | void relay_close(struct rchan *chan) | 679 | void relay_close(struct rchan *chan) |
620 | { | 680 | { |
621 | unsigned int i; | 681 | unsigned int i; |
622 | struct rchan_buf *prev = NULL; | ||
623 | 682 | ||
624 | if (!chan) | 683 | if (!chan) |
625 | return; | 684 | return; |
626 | 685 | ||
627 | for (i = 0; i < NR_CPUS; i++) { | 686 | mutex_lock(&relay_channels_mutex); |
628 | if (!chan->buf[i] || chan->buf[i] == prev) | 687 | if (chan->is_global && chan->buf[0]) |
629 | break; | 688 | relay_close_buf(chan->buf[0]); |
630 | relay_close_buf(chan->buf[i]); | 689 | else |
631 | prev = chan->buf[i]; | 690 | for_each_possible_cpu(i) |
632 | } | 691 | if (chan->buf[i]) |
692 | relay_close_buf(chan->buf[i]); | ||
633 | 693 | ||
634 | if (chan->last_toobig) | 694 | if (chan->last_toobig) |
635 | printk(KERN_WARNING "relay: one or more items not logged " | 695 | printk(KERN_WARNING "relay: one or more items not logged " |
636 | "[item size (%Zd) > sub-buffer size (%Zd)]\n", | 696 | "[item size (%Zd) > sub-buffer size (%Zd)]\n", |
637 | chan->last_toobig, chan->subbuf_size); | 697 | chan->last_toobig, chan->subbuf_size); |
638 | 698 | ||
699 | list_del(&chan->list); | ||
639 | kref_put(&chan->kref, relay_destroy_channel); | 700 | kref_put(&chan->kref, relay_destroy_channel); |
701 | mutex_unlock(&relay_channels_mutex); | ||
640 | } | 702 | } |
641 | EXPORT_SYMBOL_GPL(relay_close); | 703 | EXPORT_SYMBOL_GPL(relay_close); |
642 | 704 | ||
@@ -649,17 +711,20 @@ EXPORT_SYMBOL_GPL(relay_close); | |||
649 | void relay_flush(struct rchan *chan) | 711 | void relay_flush(struct rchan *chan) |
650 | { | 712 | { |
651 | unsigned int i; | 713 | unsigned int i; |
652 | struct rchan_buf *prev = NULL; | ||
653 | 714 | ||
654 | if (!chan) | 715 | if (!chan) |
655 | return; | 716 | return; |
656 | 717 | ||
657 | for (i = 0; i < NR_CPUS; i++) { | 718 | if (chan->is_global && chan->buf[0]) { |
658 | if (!chan->buf[i] || chan->buf[i] == prev) | 719 | relay_switch_subbuf(chan->buf[0], 0); |
659 | break; | 720 | return; |
660 | relay_switch_subbuf(chan->buf[i], 0); | ||
661 | prev = chan->buf[i]; | ||
662 | } | 721 | } |
722 | |||
723 | mutex_lock(&relay_channels_mutex); | ||
724 | for_each_possible_cpu(i) | ||
725 | if (chan->buf[i]) | ||
726 | relay_switch_subbuf(chan->buf[i], 0); | ||
727 | mutex_unlock(&relay_channels_mutex); | ||
663 | } | 728 | } |
664 | EXPORT_SYMBOL_GPL(relay_flush); | 729 | EXPORT_SYMBOL_GPL(relay_flush); |
665 | 730 | ||
@@ -1022,3 +1087,12 @@ const struct file_operations relay_file_operations = { | |||
1022 | .sendfile = relay_file_sendfile, | 1087 | .sendfile = relay_file_sendfile, |
1023 | }; | 1088 | }; |
1024 | EXPORT_SYMBOL_GPL(relay_file_operations); | 1089 | EXPORT_SYMBOL_GPL(relay_file_operations); |
1090 | |||
1091 | static __init int relay_init(void) | ||
1092 | { | ||
1093 | |||
1094 | hotcpu_notifier(relay_hotcpu_callback, 0); | ||
1095 | return 0; | ||
1096 | } | ||
1097 | |||
1098 | module_init(relay_init); | ||