diff options
| author | Lars Ellenberg <lars.ellenberg@linbit.com> | 2014-05-06 09:02:05 -0400 |
|---|---|---|
| committer | Philipp Reisner <philipp.reisner@linbit.com> | 2014-07-10 12:35:18 -0400 |
| commit | 944410e97cfcec38369eeb5f77d0e8da91d68afb (patch) | |
| tree | 486f49038fa17542d14cda8028a9b122cf1df94a /drivers/block | |
| parent | f418815f7adad4917e92e9d11fdc1ca21cd616a1 (diff) | |
drbd: debugfs: add callback_history
Add a per-connection worker thread callback_history
with timing details, call site and callback function.
Signed-off-by: Philipp Reisner <philipp.reisner@linbit.com>
Signed-off-by: Lars Ellenberg <lars.ellenberg@linbit.com>
Diffstat (limited to 'drivers/block')
| -rw-r--r-- | drivers/block/drbd/drbd_debugfs.c | 78 | ||||
| -rw-r--r-- | drivers/block/drbd/drbd_int.h | 26 | ||||
| -rw-r--r-- | drivers/block/drbd/drbd_receiver.c | 6 | ||||
| -rw-r--r-- | drivers/block/drbd/drbd_worker.c | 37 |
4 files changed, 144 insertions, 3 deletions
diff --git a/drivers/block/drbd/drbd_debugfs.c b/drivers/block/drbd/drbd_debugfs.c index 12d072d9751f..230d9e1fc85c 100644 --- a/drivers/block/drbd/drbd_debugfs.c +++ b/drivers/block/drbd/drbd_debugfs.c | |||
| @@ -521,6 +521,77 @@ void drbd_debugfs_resource_cleanup(struct drbd_resource *resource) | |||
| 521 | drbd_debugfs_remove(&resource->debugfs_res); | 521 | drbd_debugfs_remove(&resource->debugfs_res); |
| 522 | } | 522 | } |
| 523 | 523 | ||
| 524 | static void seq_print_one_timing_detail(struct seq_file *m, | ||
| 525 | const struct drbd_thread_timing_details *tdp, | ||
| 526 | unsigned long now) | ||
| 527 | { | ||
| 528 | struct drbd_thread_timing_details td; | ||
| 529 | /* No locking... | ||
| 530 | * use temporary assignment to get at consistent data. */ | ||
| 531 | do { | ||
| 532 | td = *tdp; | ||
| 533 | } while (td.cb_nr != tdp->cb_nr); | ||
| 534 | if (!td.cb_addr) | ||
| 535 | return; | ||
| 536 | seq_printf(m, "%u\t%d\t%s:%u\t%ps\n", | ||
| 537 | td.cb_nr, | ||
| 538 | jiffies_to_msecs(now - td.start_jif), | ||
| 539 | td.caller_fn, td.line, | ||
| 540 | td.cb_addr); | ||
| 541 | } | ||
| 542 | |||
| 543 | static void seq_print_timing_details(struct seq_file *m, | ||
| 544 | const char *title, | ||
| 545 | unsigned int cb_nr, struct drbd_thread_timing_details *tdp, unsigned long now) | ||
| 546 | { | ||
| 547 | unsigned int start_idx; | ||
| 548 | unsigned int i; | ||
| 549 | |||
| 550 | seq_printf(m, "%s\n", title); | ||
| 551 | /* If not much is going on, this will result in natural ordering. | ||
| 552 | * If it is very busy, we will possibly skip events, or even see wrap | ||
| 553 | * arounds, which could only be avoided with locking. | ||
| 554 | */ | ||
| 555 | start_idx = cb_nr % DRBD_THREAD_DETAILS_HIST; | ||
| 556 | for (i = start_idx; i < DRBD_THREAD_DETAILS_HIST; i++) | ||
| 557 | seq_print_one_timing_detail(m, tdp+i, now); | ||
| 558 | for (i = 0; i < start_idx; i++) | ||
| 559 | seq_print_one_timing_detail(m, tdp+i, now); | ||
| 560 | } | ||
| 561 | |||
| 562 | static int callback_history_show(struct seq_file *m, void *ignored) | ||
| 563 | { | ||
| 564 | struct drbd_connection *connection = m->private; | ||
| 565 | unsigned long jif = jiffies; | ||
| 566 | |||
| 567 | seq_puts(m, "n\tage\tcallsite\tfn\n"); | ||
| 568 | seq_print_timing_details(m, "worker", connection->w_cb_nr, connection->w_timing_details, jif); | ||
| 569 | seq_print_timing_details(m, "receiver", connection->r_cb_nr, connection->r_timing_details, jif); | ||
| 570 | return 0; | ||
| 571 | } | ||
| 572 | |||
| 573 | static int callback_history_open(struct inode *inode, struct file *file) | ||
| 574 | { | ||
| 575 | struct drbd_connection *connection = inode->i_private; | ||
| 576 | return drbd_single_open(file, callback_history_show, connection, | ||
| 577 | &connection->kref, drbd_destroy_connection); | ||
| 578 | } | ||
| 579 | |||
| 580 | static int callback_history_release(struct inode *inode, struct file *file) | ||
| 581 | { | ||
| 582 | struct drbd_connection *connection = inode->i_private; | ||
| 583 | kref_put(&connection->kref, drbd_destroy_connection); | ||
| 584 | return single_release(inode, file); | ||
| 585 | } | ||
| 586 | |||
| 587 | static const struct file_operations connection_callback_history_fops = { | ||
| 588 | .owner = THIS_MODULE, | ||
| 589 | .open = callback_history_open, | ||
| 590 | .read = seq_read, | ||
| 591 | .llseek = seq_lseek, | ||
| 592 | .release = callback_history_release, | ||
| 593 | }; | ||
| 594 | |||
| 524 | void drbd_debugfs_connection_add(struct drbd_connection *connection) | 595 | void drbd_debugfs_connection_add(struct drbd_connection *connection) |
| 525 | { | 596 | { |
| 526 | struct dentry *conns_dir = connection->resource->debugfs_res_connections; | 597 | struct dentry *conns_dir = connection->resource->debugfs_res_connections; |
| @@ -535,6 +606,13 @@ void drbd_debugfs_connection_add(struct drbd_connection *connection) | |||
| 535 | if (IS_ERR_OR_NULL(dentry)) | 606 | if (IS_ERR_OR_NULL(dentry)) |
| 536 | goto fail; | 607 | goto fail; |
| 537 | connection->debugfs_conn = dentry; | 608 | connection->debugfs_conn = dentry; |
| 609 | |||
| 610 | dentry = debugfs_create_file("callback_history", S_IRUSR|S_IRGRP, | ||
| 611 | connection->debugfs_conn, connection, | ||
| 612 | &connection_callback_history_fops); | ||
| 613 | if (IS_ERR_OR_NULL(dentry)) | ||
| 614 | goto fail; | ||
| 615 | connection->debugfs_conn_callback_history = dentry; | ||
| 538 | return; | 616 | return; |
| 539 | 617 | ||
| 540 | fail: | 618 | fail: |
diff --git a/drivers/block/drbd/drbd_int.h b/drivers/block/drbd/drbd_int.h index 30ed430f57bf..1a000016ccdf 100644 --- a/drivers/block/drbd/drbd_int.h +++ b/drivers/block/drbd/drbd_int.h | |||
| @@ -697,6 +697,15 @@ struct drbd_resource { | |||
| 697 | cpumask_var_t cpu_mask; | 697 | cpumask_var_t cpu_mask; |
| 698 | }; | 698 | }; |
| 699 | 699 | ||
| 700 | struct drbd_thread_timing_details | ||
| 701 | { | ||
| 702 | unsigned long start_jif; | ||
| 703 | void *cb_addr; | ||
| 704 | const char *caller_fn; | ||
| 705 | unsigned int line; | ||
| 706 | unsigned int cb_nr; | ||
| 707 | }; | ||
| 708 | |||
| 700 | struct drbd_connection { | 709 | struct drbd_connection { |
| 701 | struct list_head connections; | 710 | struct list_head connections; |
| 702 | struct drbd_resource *resource; | 711 | struct drbd_resource *resource; |
| @@ -759,6 +768,12 @@ struct drbd_connection { | |||
| 759 | /* sender side */ | 768 | /* sender side */ |
| 760 | struct drbd_work_queue sender_work; | 769 | struct drbd_work_queue sender_work; |
| 761 | 770 | ||
| 771 | #define DRBD_THREAD_DETAILS_HIST 16 | ||
| 772 | unsigned int w_cb_nr; /* keeps counting up */ | ||
| 773 | unsigned int r_cb_nr; /* keeps counting up */ | ||
| 774 | struct drbd_thread_timing_details w_timing_details[DRBD_THREAD_DETAILS_HIST]; | ||
| 775 | struct drbd_thread_timing_details r_timing_details[DRBD_THREAD_DETAILS_HIST]; | ||
| 776 | |||
| 762 | struct { | 777 | struct { |
| 763 | /* whether this sender thread | 778 | /* whether this sender thread |
| 764 | * has processed a single write yet. */ | 779 | * has processed a single write yet. */ |
| @@ -774,6 +789,17 @@ struct drbd_connection { | |||
| 774 | } send; | 789 | } send; |
| 775 | }; | 790 | }; |
| 776 | 791 | ||
| 792 | void __update_timing_details( | ||
| 793 | struct drbd_thread_timing_details *tdp, | ||
| 794 | unsigned int *cb_nr, | ||
| 795 | void *cb, | ||
| 796 | const char *fn, const unsigned int line); | ||
| 797 | |||
| 798 | #define update_worker_timing_details(c, cb) \ | ||
| 799 | __update_timing_details(c->w_timing_details, &c->w_cb_nr, cb, __func__ , __LINE__ ) | ||
| 800 | #define update_receiver_timing_details(c, cb) \ | ||
| 801 | __update_timing_details(c->r_timing_details, &c->r_cb_nr, cb, __func__ , __LINE__ ) | ||
| 802 | |||
| 777 | struct submit_worker { | 803 | struct submit_worker { |
| 778 | struct workqueue_struct *wq; | 804 | struct workqueue_struct *wq; |
| 779 | struct work_struct worker; | 805 | struct work_struct worker; |
diff --git a/drivers/block/drbd/drbd_receiver.c b/drivers/block/drbd/drbd_receiver.c index 9f6ed240e075..f972988291c5 100644 --- a/drivers/block/drbd/drbd_receiver.c +++ b/drivers/block/drbd/drbd_receiver.c | |||
| @@ -2682,9 +2682,11 @@ static int receive_DataRequest(struct drbd_connection *connection, struct packet | |||
| 2682 | list_add_tail(&peer_req->w.list, &device->read_ee); | 2682 | list_add_tail(&peer_req->w.list, &device->read_ee); |
| 2683 | spin_unlock_irq(&device->resource->req_lock); | 2683 | spin_unlock_irq(&device->resource->req_lock); |
| 2684 | 2684 | ||
| 2685 | update_receiver_timing_details(connection, drbd_rs_should_slow_down); | ||
| 2685 | if (device->state.peer != R_PRIMARY | 2686 | if (device->state.peer != R_PRIMARY |
| 2686 | && drbd_rs_should_slow_down(device, sector, false)) | 2687 | && drbd_rs_should_slow_down(device, sector, false)) |
| 2687 | schedule_timeout_uninterruptible(HZ/10); | 2688 | schedule_timeout_uninterruptible(HZ/10); |
| 2689 | update_receiver_timing_details(connection, drbd_rs_begin_io); | ||
| 2688 | if (drbd_rs_begin_io(device, sector)) | 2690 | if (drbd_rs_begin_io(device, sector)) |
| 2689 | goto out_free_e; | 2691 | goto out_free_e; |
| 2690 | 2692 | ||
| @@ -2692,6 +2694,7 @@ submit_for_resync: | |||
| 2692 | atomic_add(size >> 9, &device->rs_sect_ev); | 2694 | atomic_add(size >> 9, &device->rs_sect_ev); |
| 2693 | 2695 | ||
| 2694 | submit: | 2696 | submit: |
| 2697 | update_receiver_timing_details(connection, drbd_submit_peer_request); | ||
| 2695 | inc_unacked(device); | 2698 | inc_unacked(device); |
| 2696 | if (drbd_submit_peer_request(device, peer_req, READ, fault_type) == 0) | 2699 | |
