diff options
author | Lars Ellenberg <lars.ellenberg@linbit.com> | 2014-05-08 07:39:35 -0400 |
---|---|---|
committer | Philipp Reisner <philipp.reisner@linbit.com> | 2014-07-10 12:35:19 -0400 |
commit | 54e6fc38e888a54b016e1e04e1eceea78ddf7ace (patch) | |
tree | b4908bb91f765a1f117df38379ff37cbcf8b451c | |
parent | 944410e97cfcec38369eeb5f77d0e8da91d68afb (diff) |
drbd: debugfs: add per volume oldest_requests
Show oldest requests
* pending master bio completion and,
* if different, local disk bio completion.
Signed-off-by: Philipp Reisner <philipp.reisner@linbit.com>
Signed-off-by: Lars Ellenberg <lars.ellenberg@linbit.com>
-rw-r--r-- | drivers/block/drbd/drbd_debugfs.c | 153 | ||||
-rw-r--r-- | lib/lru_cache.c | 21 |
2 files changed, 160 insertions, 14 deletions
diff --git a/drivers/block/drbd/drbd_debugfs.c b/drivers/block/drbd/drbd_debugfs.c index 230d9e1fc85c..45b52056f88d 100644 --- a/drivers/block/drbd/drbd_debugfs.c +++ b/drivers/block/drbd/drbd_debugfs.c | |||
@@ -434,12 +434,10 @@ static int drbd_single_open(struct file *file, int (*show)(struct seq_file *, vo | |||
434 | goto out; | 434 | goto out; |
435 | /* serialize with d_delete() */ | 435 | /* serialize with d_delete() */ |
436 | mutex_lock(&parent->d_inode->i_mutex); | 436 | mutex_lock(&parent->d_inode->i_mutex); |
437 | if (!debugfs_positive(file->f_dentry)) | ||
438 | goto out_unlock; | ||
439 | /* Make sure the object is still alive */ | 437 | /* Make sure the object is still alive */ |
440 | if (kref_get_unless_zero(kref)) | 438 | if (debugfs_positive(file->f_dentry) |
439 | && kref_get_unless_zero(kref)) | ||
441 | ret = 0; | 440 | ret = 0; |
442 | out_unlock: | ||
443 | mutex_unlock(&parent->d_inode->i_mutex); | 441 | mutex_unlock(&parent->d_inode->i_mutex); |
444 | if (!ret) { | 442 | if (!ret) { |
445 | ret = single_open(file, show, data); | 443 | ret = single_open(file, show, data); |
@@ -592,6 +590,53 @@ static const struct file_operations connection_callback_history_fops = { | |||
592 | .release = callback_history_release, | 590 | .release = callback_history_release, |
593 | }; | 591 | }; |
594 | 592 | ||
593 | static int connection_oldest_requests_show(struct seq_file *m, void *ignored) | ||
594 | { | ||
595 | struct drbd_connection *connection = m->private; | ||
596 | unsigned long now = jiffies; | ||
597 | struct drbd_request *r1, *r2; | ||
598 | |||
599 | /* BUMP me if you change the file format/content/presentation */ | ||
600 | seq_printf(m, "v: %u\n\n", 0); | ||
601 | |||
602 | spin_lock_irq(&connection->resource->req_lock); | ||
603 | r1 = connection->req_next; | ||
604 | if (r1) | ||
605 | seq_print_minor_vnr_req(m, r1, now); | ||
606 | r2 = connection->req_ack_pending; | ||
607 | if (r2 && r2 != r1) { | ||
608 | r1 = r2; | ||
609 | seq_print_minor_vnr_req(m, r1, now); | ||
610 | } | ||
611 | r2 = connection->req_not_net_done; | ||
612 | if (r2 && r2 != r1) | ||
613 | seq_print_minor_vnr_req(m, r2, now); | ||
614 | spin_unlock_irq(&connection->resource->req_lock); | ||
615 | return 0; | ||
616 | } | ||
617 | |||
618 | static int connection_oldest_requests_open(struct inode *inode, struct file *file) | ||
619 | { | ||
620 | struct drbd_connection *connection = inode->i_private; | ||
621 | return drbd_single_open(file, connection_oldest_requests_show, connection, | ||
622 | &connection->kref, drbd_destroy_connection); | ||
623 | } | ||
624 | |||
625 | static int connection_oldest_requests_release(struct inode *inode, struct file *file) | ||
626 | { | ||
627 | struct drbd_connection *connection = inode->i_private; | ||
628 | kref_put(&connection->kref, drbd_destroy_connection); | ||
629 | return single_release(inode, file); | ||
630 | } | ||
631 | |||
632 | static const struct file_operations connection_oldest_requests_fops = { | ||
633 | .owner = THIS_MODULE, | ||
634 | .open = connection_oldest_requests_open, | ||
635 | .read = seq_read, | ||
636 | .llseek = seq_lseek, | ||
637 | .release = connection_oldest_requests_release, | ||
638 | }; | ||
639 | |||
595 | void drbd_debugfs_connection_add(struct drbd_connection *connection) | 640 | void drbd_debugfs_connection_add(struct drbd_connection *connection) |
596 | { | 641 | { |
597 | struct dentry *conns_dir = connection->resource->debugfs_res_connections; | 642 | struct dentry *conns_dir = connection->resource->debugfs_res_connections; |
@@ -627,6 +672,89 @@ void drbd_debugfs_connection_cleanup(struct drbd_connection *connection) | |||
627 | drbd_debugfs_remove(&connection->debugfs_conn); | 672 | drbd_debugfs_remove(&connection->debugfs_conn); |
628 | } | 673 | } |
629 | 674 | ||
675 | static void resync_dump_detail(struct seq_file *m, struct lc_element *e) | ||
676 | { | ||
677 | struct bm_extent *bme = lc_entry(e, struct bm_extent, lce); | ||
678 | |||
679 | seq_printf(m, "%5d %s %s %s\n", bme->rs_left, | ||
680 | test_bit(BME_NO_WRITES, &bme->flags) ? "NO_WRITES" : "---------", | ||
681 | test_bit(BME_LOCKED, &bme->flags) ? "LOCKED" : "------", | ||
682 | test_bit(BME_PRIORITY, &bme->flags) ? "PRIORITY" : "--------" | ||
683 | ); | ||
684 | } | ||
685 | |||
686 | static int device_resync_extents_show(struct seq_file *m, void *ignored) | ||
687 | { | ||
688 | struct drbd_device *device = m->private; | ||
689 | if (get_ldev_if_state(device, D_FAILED)) { | ||
690 | lc_seq_printf_stats(m, device->resync); | ||
691 | lc_seq_dump_details(m, device->resync, "rs_left flags", resync_dump_detail); | ||
692 | put_ldev(device); | ||
693 | } | ||
694 | return 0; | ||
695 | } | ||
696 | |||
697 | static int device_act_log_extents_show(struct seq_file *m, void *ignored) | ||
698 | { | ||
699 | struct drbd_device *device = m->private; | ||
700 | if (get_ldev_if_state(device, D_FAILED)) { | ||
701 | lc_seq_printf_stats(m, device->act_log); | ||
702 | lc_seq_dump_details(m, device->act_log, "", NULL); | ||
703 | put_ldev(device); | ||
704 | } | ||
705 | return 0; | ||
706 | } | ||
707 | |||
708 | static int device_oldest_requests_show(struct seq_file *m, void *ignored) | ||
709 | { | ||
710 | struct drbd_device *device = m->private; | ||
711 | struct drbd_resource *resource = device->resource; | ||
712 | unsigned long now = jiffies; | ||
713 | struct drbd_request *r1, *r2; | ||
714 | int i; | ||
715 | |||
716 | seq_puts(m, RQ_HDR); | ||
717 | spin_lock_irq(&resource->req_lock); | ||
718 | /* WRITE, then READ */ | ||
719 | for (i = 1; i >= 0; --i) { | ||
720 | r1 = list_first_entry_or_null(&device->pending_master_completion[i], | ||
721 | struct drbd_request, req_pending_master_completion); | ||
722 | r2 = list_first_entry_or_null(&device->pending_completion[i], | ||
723 | struct drbd_request, req_pending_local); | ||
724 | if (r1) | ||
725 | seq_print_one_request(m, r1, now); | ||
726 | if (r2 && r2 != r1) | ||
727 | seq_print_one_request(m, r2, now); | ||
728 | } | ||
729 | spin_unlock_irq(&resource->req_lock); | ||
730 | return 0; | ||
731 | } | ||
732 | |||
733 | #define drbd_debugfs_device_attr(name) \ | ||
734 | static int device_ ## name ## _open(struct inode *inode, struct file *file) \ | ||
735 | { \ | ||
736 | struct drbd_device *device = inode->i_private; \ | ||
737 | return drbd_single_open(file, device_ ## name ## _show, device, \ | ||
738 | &device->kref, drbd_destroy_device); \ | ||
739 | } \ | ||
740 | static int device_ ## name ## _release(struct inode *inode, struct file *file) \ | ||
741 | { \ | ||
742 | struct drbd_device *device = inode->i_private; \ | ||
743 | kref_put(&device->kref, drbd_destroy_device); \ | ||
744 | return single_release(inode, file); \ | ||
745 | } \ | ||
746 | static const struct file_operations device_ ## name ## _fops = { \ | ||
747 | .owner = THIS_MODULE, \ | ||
748 | .open = device_ ## name ## _open, \ | ||
749 | .read = seq_read, \ | ||
750 | .llseek = seq_lseek, \ | ||
751 | .release = device_ ## name ## _release, \ | ||
752 | }; | ||
753 | |||
754 | drbd_debugfs_device_attr(oldest_requests) | ||
755 | drbd_debugfs_device_attr(act_log_extents) | ||
756 | drbd_debugfs_device_attr(resync_extents) | ||
757 | |||
630 | void drbd_debugfs_device_add(struct drbd_device *device) | 758 | void drbd_debugfs_device_add(struct drbd_device *device) |
631 | { | 759 | { |
632 | struct dentry *vols_dir = device->resource->debugfs_res_volumes; | 760 | struct dentry *vols_dir = device->resource->debugfs_res_volumes; |
@@ -650,10 +778,25 @@ void drbd_debugfs_device_add(struct drbd_device *device) | |||
650 | if (!slink_name) | 778 | if (!slink_name) |
651 | goto fail; | 779 | goto fail; |
652 | dentry = debugfs_create_symlink(minor_buf, drbd_debugfs_minors, slink_name); | 780 | dentry = debugfs_create_symlink(minor_buf, drbd_debugfs_minors, slink_name); |
781 | kfree(slink_name); | ||
782 | slink_name = NULL; | ||
653 | if (IS_ERR_OR_NULL(dentry)) | 783 | if (IS_ERR_OR_NULL(dentry)) |
654 | goto fail; | 784 | goto fail; |
655 | device->debugfs_minor = dentry; | 785 | device->debugfs_minor = dentry; |
656 | kfree(slink_name); | 786 | |
787 | #define DCF(name) do { \ | ||
788 | dentry = debugfs_create_file(#name, S_IRUSR|S_IRGRP, \ | ||
789 | device->debugfs_vol, device, \ | ||
790 | &device_ ## name ## _fops); \ | ||
791 | if (IS_ERR_OR_NULL(dentry)) \ | ||
792 | goto fail; \ | ||
793 | device->debugfs_vol_ ## name = dentry; \ | ||
794 | } while (0) | ||
795 | |||
796 | DCF(oldest_requests); | ||
797 | DCF(act_log_extents); | ||
798 | DCF(resync_extents); | ||
799 | return; | ||
657 | 800 | ||
658 | fail: | 801 | fail: |
659 | drbd_debugfs_device_cleanup(device); | 802 | drbd_debugfs_device_cleanup(device); |
diff --git a/lib/lru_cache.c b/lib/lru_cache.c index 6111cd19762d..852c81e3ba9a 100644 --- a/lib/lru_cache.c +++ b/lib/lru_cache.c | |||
@@ -643,9 +643,10 @@ void lc_set(struct lru_cache *lc, unsigned int enr, int index) | |||
643 | * lc_dump - Dump a complete LRU cache to seq in textual form. | 643 | * lc_dump - Dump a complete LRU cache to seq in textual form. |
644 | * @lc: the lru cache to operate on | 644 | * @lc: the lru cache to operate on |
645 | * @seq: the &struct seq_file pointer to seq_printf into | 645 | * @seq: the &struct seq_file pointer to seq_printf into |
646 | * @utext: user supplied "heading" or other info | 646 | * @utext: user supplied additional "heading" or other info |
647 | * @detail: function pointer the user may provide to dump further details | 647 | * @detail: function pointer the user may provide to dump further details |
648 | * of the object the lc_element is embedded in. | 648 | * of the object the lc_element is embedded in. May be NULL. |
649 | * Note: a leading space ' ' and trailing newline '\n' is implied. | ||
649 | */ | 650 | */ |
650 | void lc_seq_dump_details(struct seq_file *seq, struct lru_cache *lc, char *utext, | 651 | void lc_seq_dump_details(struct seq_file *seq, struct lru_cache *lc, char *utext, |
651 | void (*detail) (struct seq_file *, struct lc_element *)) | 652 | void (*detail) (struct seq_file *, struct lc_element *)) |
@@ -654,16 +655,18 @@ void lc_seq_dump_details(struct seq_file *seq, struct lru_cache *lc, char *utext | |||
654 | struct lc_element *e; | 655 | struct lc_element *e; |
655 | int i; | 656 | int i; |
656 | 657 | ||
657 | seq_printf(seq, "\tnn: lc_number refcnt %s\n ", utext); | 658 | seq_printf(seq, "\tnn: lc_number (new nr) refcnt %s\n ", utext); |
658 | for (i = 0; i < nr_elements; i++) { | 659 | for (i = 0; i < nr_elements; i++) { |
659 | e = lc_element_by_index(lc, i); | 660 | e = lc_element_by_index(lc, i); |
660 | if (e->lc_number == LC_FREE) { | 661 | if (e->lc_number != e->lc_new_number) |
661 | seq_printf(seq, "\t%2d: FREE\n", i); | 662 | seq_printf(seq, "\t%5d: %6d %8d %6d ", |
662 | } else { | 663 | i, e->lc_number, e->lc_new_number, e->refcnt); |
663 | seq_printf(seq, "\t%2d: %4u %4u ", i, | 664 | else |
664 | e->lc_number, e->refcnt); | 665 | seq_printf(seq, "\t%5d: %6d %-8s %6d ", |
666 | i, e->lc_number, "-\"-", e->refcnt); | ||
667 | if (detail) | ||
665 | detail(seq, e); | 668 | detail(seq, e); |
666 | } | 669 | seq_putc(seq, '\n'); |
667 | } | 670 | } |
668 | } | 671 | } |
669 | 672 | ||