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 | if (drbd_submit_peer_request(device, peer_req, READ, fault_type) == 0) |
2697 | return 0; | 2700 | return 0; |
@@ -4601,6 +4604,7 @@ static void drbdd(struct drbd_connection *connection) | |||
4601 | struct data_cmd *cmd; | 4604 | struct data_cmd *cmd; |
4602 | 4605 | ||
4603 | drbd_thread_current_set_cpu(&connection->receiver); | 4606 | drbd_thread_current_set_cpu(&connection->receiver); |
4607 | update_receiver_timing_details(connection, drbd_recv_header); | ||
4604 | if (drbd_recv_header(connection, &pi)) | 4608 | if (drbd_recv_header(connection, &pi)) |
4605 | goto err_out; | 4609 | goto err_out; |
4606 | 4610 | ||
@@ -4619,12 +4623,14 @@ static void drbdd(struct drbd_connection *connection) | |||
4619 | } | 4623 | } |
4620 | 4624 | ||
4621 | if (shs) { | 4625 | if (shs) { |
4626 | update_receiver_timing_details(connection, drbd_recv_all_warn); | ||
4622 | err = drbd_recv_all_warn(connection, pi.data, shs); | 4627 | err = drbd_recv_all_warn(connection, pi.data, shs); |
4623 | if (err) | 4628 | if (err) |
4624 | goto err_out; | 4629 | goto err_out; |
4625 | pi.size -= shs; | 4630 | pi.size -= shs; |
4626 | } | 4631 | } |
4627 | 4632 | ||
4633 | update_receiver_timing_details(connection, cmd->fn); | ||
4628 | err = cmd->fn(connection, &pi); | 4634 | err = cmd->fn(connection, &pi); |
4629 | if (err) { | 4635 | if (err) { |
4630 | drbd_err(connection, "error receiving %s, e: %d l: %d!\n", | 4636 | drbd_err(connection, "error receiving %s, e: %d l: %d!\n", |
diff --git a/drivers/block/drbd/drbd_worker.c b/drivers/block/drbd/drbd_worker.c index b908e9b3f63e..50776b362828 100644 --- a/drivers/block/drbd/drbd_worker.c +++ b/drivers/block/drbd/drbd_worker.c | |||
@@ -1905,6 +1905,29 @@ static int do_md_sync(struct drbd_device *device) | |||
1905 | return 0; | 1905 | return 0; |
1906 | } | 1906 | } |
1907 | 1907 | ||
1908 | /* only called from drbd_worker thread, no locking */ | ||
1909 | void __update_timing_details( | ||
1910 | struct drbd_thread_timing_details *tdp, | ||
1911 | unsigned int *cb_nr, | ||
1912 | void *cb, | ||
1913 | const char *fn, const unsigned int line) | ||
1914 | { | ||
1915 | unsigned int i = *cb_nr % DRBD_THREAD_DETAILS_HIST; | ||
1916 | struct drbd_thread_timing_details *td = tdp + i; | ||
1917 | |||
1918 | td->start_jif = jiffies; | ||
1919 | td->cb_addr = cb; | ||
1920 | td->caller_fn = fn; | ||
1921 | td->line = line; | ||
1922 | td->cb_nr = *cb_nr; | ||
1923 | |||
1924 | i = (i+1) % DRBD_THREAD_DETAILS_HIST; | ||
1925 | td = tdp + i; | ||
1926 | memset(td, 0, sizeof(*td)); | ||
1927 | |||
1928 | ++(*cb_nr); | ||
1929 | } | ||
1930 | |||
1908 | #define WORK_PENDING(work_bit, todo) (todo & (1UL << work_bit)) | 1931 | #define WORK_PENDING(work_bit, todo) (todo & (1UL << work_bit)) |
1909 | static void do_device_work(struct drbd_device *device, const unsigned long todo) | 1932 | static void do_device_work(struct drbd_device *device, const unsigned long todo) |
1910 | { | 1933 | { |
@@ -2076,11 +2099,15 @@ int drbd_worker(struct drbd_thread *thi) | |||
2076 | while (get_t_state(thi) == RUNNING) { | 2099 | while (get_t_state(thi) == RUNNING) { |
2077 | drbd_thread_current_set_cpu(thi); | 2100 | drbd_thread_current_set_cpu(thi); |
2078 | 2101 | ||
2079 | if (list_empty(&work_list)) | 2102 | if (list_empty(&work_list)) { |
2103 | update_worker_timing_details(connection, wait_for_work); | ||
2080 | wait_for_work(connection, &work_list); | 2104 | wait_for_work(connection, &work_list); |
2105 | } | ||
2081 | 2106 | ||
2082 | if (test_and_clear_bit(DEVICE_WORK_PENDING, &connection->flags)) | 2107 | if (test_and_clear_bit(DEVICE_WORK_PENDING, &connection->flags)) { |
2108 | update_worker_timing_details(connection, do_unqueued_work); | ||
2083 | do_unqueued_work(connection); | 2109 | do_unqueued_work(connection); |
2110 | } | ||
2084 | 2111 | ||
2085 | if (signal_pending(current)) { | 2112 | if (signal_pending(current)) { |
2086 | flush_signals(current); | 2113 | flush_signals(current); |
@@ -2097,6 +2124,7 @@ int drbd_worker(struct drbd_thread *thi) | |||
2097 | while (!list_empty(&work_list)) { | 2124 | while (!list_empty(&work_list)) { |
2098 | w = list_first_entry(&work_list, struct drbd_work, list); | 2125 | w = list_first_entry(&work_list, struct drbd_work, list); |
2099 | list_del_init(&w->list); | 2126 | list_del_init(&w->list); |
2127 | update_worker_timing_details(connection, w->cb); | ||
2100 | if (w->cb(w, connection->cstate < C_WF_REPORT_PARAMS) == 0) | 2128 | if (w->cb(w, connection->cstate < C_WF_REPORT_PARAMS) == 0) |
2101 | continue; | 2129 | continue; |
2102 | if (connection->cstate >= C_WF_REPORT_PARAMS) | 2130 | if (connection->cstate >= C_WF_REPORT_PARAMS) |
@@ -2105,11 +2133,14 @@ int drbd_worker(struct drbd_thread *thi) | |||
2105 | } | 2133 | } |
2106 | 2134 | ||
2107 | do { | 2135 | do { |
2108 | if (test_and_clear_bit(DEVICE_WORK_PENDING, &connection->flags)) | 2136 | if (test_and_clear_bit(DEVICE_WORK_PENDING, &connection->flags)) { |
2137 | update_worker_timing_details(connection, do_unqueued_work); | ||
2109 | do_unqueued_work(connection); | 2138 | do_unqueued_work(connection); |
2139 | } | ||
2110 | while (!list_empty(&work_list)) { | 2140 | while (!list_empty(&work_list)) { |
2111 | w = list_first_entry(&work_list, struct drbd_work, list); | 2141 | w = list_first_entry(&work_list, struct drbd_work, list); |
2112 | list_del_init(&w->list); | 2142 | list_del_init(&w->list); |
2143 | update_worker_timing_details(connection, w->cb); | ||
2113 | w->cb(w, 1); | 2144 | w->cb(w, 1); |
2114 | } | 2145 | } |
2115 | dequeue_work_batch(&connection->sender_work, &work_list); | 2146 | dequeue_work_batch(&connection->sender_work, &work_list); |