aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/s390
diff options
context:
space:
mode:
authorStefan Weinhuber <wein@de.ibm.com>2011-07-24 04:48:32 -0400
committerMartin Schwidefsky <schwidefsky@de.ibm.com>2011-07-24 04:48:23 -0400
commit4fa52aa7a82f9226b3874a69816bda3af821f002 (patch)
tree7d61364211cf22bb942b5eff7286c3c1f037fdf0 /drivers/s390
parent8bb3a2ebcf2a406a60d04f5a8756ea936b7f0bf3 (diff)
[S390] dasd: add enhanced DASD statistics interface
This patch extends the DASD statistics to allow for a more detailed analysis of DASD I/O operations. In particular we want the statistics to provide answers to the following questions: - How many requests used a PAV alias? - How many requests used High Performance FICON? - How do read request perform versus write requests? The existing DASD statistics interface has several shortcomings - The interface for global data is a formatted text table in procfs (/proc/dasd/statistics). The layout is meant for human readers and is not to easy to parse. If values get to large for the table layout, they get scaled down. - The statistics which are collected per block device can be accessed via an ioctl interface, which can only be extended by defining a new ioctl. - There is no statistics interface for individual PAV base and alias devices. To overcome theses shortcomings we create a new DASD statistics interface in debugfs. This interface will contain one entry for global data, one per DASD block device, and one per DASD base and alias device. Each file contains the statistic data in easy to parse name/value and name/array pairs. The existing interfaces will remain functional, but they will not be extended. Signed-off-by: Stefan Weinhuber <wein@de.ibm.com> Signed-off-by: Martin Schwidefsky <schwidefsky@de.ibm.com>
Diffstat (limited to 'drivers/s390')
-rw-r--r--drivers/s390/block/dasd.c576
-rw-r--r--drivers/s390/block/dasd_int.h57
-rw-r--r--drivers/s390/block/dasd_ioctl.c38
-rw-r--r--drivers/s390/block/dasd_proc.c106
4 files changed, 686 insertions, 91 deletions
diff --git a/drivers/s390/block/dasd.c b/drivers/s390/block/dasd.c
index 86b6f1cc1b1..432444af7ee 100644
--- a/drivers/s390/block/dasd.c
+++ b/drivers/s390/block/dasd.c
@@ -22,6 +22,8 @@
22#include <linux/hdreg.h> 22#include <linux/hdreg.h>
23#include <linux/async.h> 23#include <linux/async.h>
24#include <linux/mutex.h> 24#include <linux/mutex.h>
25#include <linux/debugfs.h>
26#include <linux/seq_file.h>
25 27
26#include <asm/ccwdev.h> 28#include <asm/ccwdev.h>
27#include <asm/ebcdic.h> 29#include <asm/ebcdic.h>
@@ -45,6 +47,7 @@
45 * SECTION: exported variables of dasd.c 47 * SECTION: exported variables of dasd.c
46 */ 48 */
47debug_info_t *dasd_debug_area; 49debug_info_t *dasd_debug_area;
50static struct dentry *dasd_debugfs_root_entry;
48struct dasd_discipline *dasd_diag_discipline_pointer; 51struct dasd_discipline *dasd_diag_discipline_pointer;
49void dasd_int_handler(struct ccw_device *, unsigned long, struct irb *); 52void dasd_int_handler(struct ccw_device *, unsigned long, struct irb *);
50 53
@@ -71,6 +74,8 @@ static void dasd_return_cqr_cb(struct dasd_ccw_req *, void *);
71static void dasd_device_timeout(unsigned long); 74static void dasd_device_timeout(unsigned long);
72static void dasd_block_timeout(unsigned long); 75static void dasd_block_timeout(unsigned long);
73static void __dasd_process_erp(struct dasd_device *, struct dasd_ccw_req *); 76static void __dasd_process_erp(struct dasd_device *, struct dasd_ccw_req *);
77static void dasd_profile_init(struct dasd_profile *, struct dentry *);
78static void dasd_profile_exit(struct dasd_profile *);
74 79
75/* 80/*
76 * SECTION: Operations on the device structure. 81 * SECTION: Operations on the device structure.
@@ -121,7 +126,7 @@ struct dasd_device *dasd_alloc_device(void)
121 device->state = DASD_STATE_NEW; 126 device->state = DASD_STATE_NEW;
122 device->target = DASD_STATE_NEW; 127 device->target = DASD_STATE_NEW;
123 mutex_init(&device->state_mutex); 128 mutex_init(&device->state_mutex);
124 129 spin_lock_init(&device->profile.lock);
125 return device; 130 return device;
126} 131}
127 132
@@ -159,6 +164,7 @@ struct dasd_block *dasd_alloc_block(void)
159 init_timer(&block->timer); 164 init_timer(&block->timer);
160 block->timer.function = dasd_block_timeout; 165 block->timer.function = dasd_block_timeout;
161 block->timer.data = (unsigned long) block; 166 block->timer.data = (unsigned long) block;
167 spin_lock_init(&block->profile.lock);
162 168
163 return block; 169 return block;
164} 170}
@@ -222,19 +228,44 @@ static int dasd_state_known_to_new(struct dasd_device *device)
222 return 0; 228 return 0;
223} 229}
224 230
231static struct dentry *dasd_debugfs_setup(const char *name,
232 struct dentry *base_dentry)
233{
234 struct dentry *pde;
235
236 if (!base_dentry)
237 return NULL;
238 pde = debugfs_create_dir(name, base_dentry);
239 if (!pde || IS_ERR(pde))
240 return NULL;
241 return pde;
242}
243
225/* 244/*
226 * Request the irq line for the device. 245 * Request the irq line for the device.
227 */ 246 */
228static int dasd_state_known_to_basic(struct dasd_device *device) 247static int dasd_state_known_to_basic(struct dasd_device *device)
229{ 248{
249 struct dasd_block *block = device->block;
230 int rc; 250 int rc;
231 251
232 /* Allocate and register gendisk structure. */ 252 /* Allocate and register gendisk structure. */
233 if (device->block) { 253 if (block) {
234 rc = dasd_gendisk_alloc(device->block); 254 rc = dasd_gendisk_alloc(block);
235 if (rc) 255 if (rc)
236 return rc; 256 return rc;
237 } 257 block->debugfs_dentry =
258 dasd_debugfs_setup(block->gdp->disk_name,
259 dasd_debugfs_root_entry);
260 dasd_profile_init(&block->profile, block->debugfs_dentry);
261 if (dasd_global_profile_level == DASD_PROFILE_ON)
262 dasd_profile_on(&device->block->profile);
263 }
264 device->debugfs_dentry =
265 dasd_debugfs_setup(dev_name(&device->cdev->dev),
266 dasd_debugfs_root_entry);
267 dasd_profile_init(&device->profile, device->debugfs_dentry);
268
238 /* register 'device' debug area, used for all DBF_DEV_XXX calls */ 269 /* register 'device' debug area, used for all DBF_DEV_XXX calls */
239 device->debug_area = debug_register(dev_name(&device->cdev->dev), 4, 1, 270 device->debug_area = debug_register(dev_name(&device->cdev->dev), 4, 1,
240 8 * sizeof(long)); 271 8 * sizeof(long));
@@ -253,6 +284,9 @@ static int dasd_state_basic_to_known(struct dasd_device *device)
253{ 284{
254 int rc; 285 int rc;
255 if (device->block) { 286 if (device->block) {
287 dasd_profile_exit(&device->block->profile);
288 if (device->block->debugfs_dentry)
289 debugfs_remove(device->block->debugfs_dentry);
256 dasd_gendisk_free(device->block); 290 dasd_gendisk_free(device->block);
257 dasd_block_clear_timer(device->block); 291 dasd_block_clear_timer(device->block);
258 } 292 }
@@ -260,6 +294,9 @@ static int dasd_state_basic_to_known(struct dasd_device *device)
260 if (rc) 294 if (rc)
261 return rc; 295 return rc;
262 dasd_device_clear_timer(device); 296 dasd_device_clear_timer(device);
297 dasd_profile_exit(&device->profile);
298 if (device->debugfs_dentry)
299 debugfs_remove(device->debugfs_dentry);
263 300
264 DBF_DEV_EVENT(DBF_EMERG, device, "%p debug area deleted", device); 301 DBF_DEV_EVENT(DBF_EMERG, device, "%p debug area deleted", device);
265 if (device->debug_area != NULL) { 302 if (device->debug_area != NULL) {
@@ -609,21 +646,13 @@ void dasd_enable_device(struct dasd_device *device)
609/* 646/*
610 * SECTION: device operation (interrupt handler, start i/o, term i/o ...) 647 * SECTION: device operation (interrupt handler, start i/o, term i/o ...)
611 */ 648 */
612#ifdef CONFIG_DASD_PROFILE
613 649
614struct dasd_profile_info_t dasd_global_profile; 650unsigned int dasd_global_profile_level = DASD_PROFILE_OFF;
615unsigned int dasd_profile_level = DASD_PROFILE_OFF;
616 651
617/* 652#ifdef CONFIG_DASD_PROFILE
618 * Increments counter in global and local profiling structures. 653struct dasd_profile_info dasd_global_profile_data;
619 */ 654static struct dentry *dasd_global_profile_dentry;
620#define dasd_profile_counter(value, counter, block) \ 655static struct dentry *dasd_debugfs_global_entry;
621{ \
622 int index; \
623 for (index = 0; index < 31 && value >> (2+index); index++); \
624 dasd_global_profile.counter[index]++; \
625 block->profile.counter[index]++; \
626}
627 656
628/* 657/*
629 * Add profiling information for cqr before execution. 658 * Add profiling information for cqr before execution.
@@ -634,30 +663,121 @@ static void dasd_profile_start(struct dasd_block *block,
634{ 663{
635 struct list_head *l; 664 struct list_head *l;
636 unsigned int counter; 665 unsigned int counter;
637 666 struct dasd_device *device;
638 if (dasd_profile_level != DASD_PROFILE_ON)
639 return;
640 667
641 /* count the length of the chanq for statistics */ 668 /* count the length of the chanq for statistics */
642 counter = 0; 669 counter = 0;
643 list_for_each(l, &block->ccw_queue) 670 if (dasd_global_profile_level || block->profile.data)
644 if (++counter >= 31) 671 list_for_each(l, &block->ccw_queue)
645 break; 672 if (++counter >= 31)
646 dasd_global_profile.dasd_io_nr_req[counter]++; 673 break;
647 block->profile.dasd_io_nr_req[counter]++; 674
675 if (dasd_global_profile_level) {
676 dasd_global_profile_data.dasd_io_nr_req[counter]++;
677 if (rq_data_dir(req) == READ)
678 dasd_global_profile_data.dasd_read_nr_req[counter]++;
679 }
680
681 spin_lock(&block->profile.lock);
682 if (block->profile.data)
683 block->profile.data->dasd_io_nr_req[counter]++;
684 if (rq_data_dir(req) == READ)
685 block->profile.data->dasd_read_nr_req[counter]++;
686 spin_unlock(&block->profile.lock);
687
688 /*
689 * We count the request for the start device, even though it may run on
690 * some other device due to error recovery. This way we make sure that
691 * we count each request only once.
692 */
693 device = cqr->startdev;
694 if (device->profile.data) {
695 counter = 1; /* request is not yet queued on the start device */
696 list_for_each(l, &device->ccw_queue)
697 if (++counter >= 31)
698 break;
699 }
700 spin_lock(&device->profile.lock);
701 if (device->profile.data) {
702 device->profile.data->dasd_io_nr_req[counter]++;
703 if (rq_data_dir(req) == READ)
704 device->profile.data->dasd_read_nr_req[counter]++;
705 }
706 spin_unlock(&device->profile.lock);
648} 707}
649 708
650/* 709/*
651 * Add profiling information for cqr after execution. 710 * Add profiling information for cqr after execution.
652 */ 711 */
712
713#define dasd_profile_counter(value, index) \
714{ \
715 for (index = 0; index < 31 && value >> (2+index); index++) \
716 ; \
717}
718
719static void dasd_profile_end_add_data(struct dasd_profile_info *data,
720 int is_alias,
721 int is_tpm,
722 int is_read,
723 long sectors,
724 int sectors_ind,
725 int tottime_ind,
726 int tottimeps_ind,
727 int strtime_ind,
728 int irqtime_ind,
729 int irqtimeps_ind,
730 int endtime_ind)
731{
732 /* in case of an overflow, reset the whole profile */
733 if (data->dasd_io_reqs == UINT_MAX) {
734 memset(data, 0, sizeof(*data));
735 getnstimeofday(&data->starttod);
736 }
737 data->dasd_io_reqs++;
738 data->dasd_io_sects += sectors;
739 if (is_alias)
740 data->dasd_io_alias++;
741 if (is_tpm)
742 data->dasd_io_tpm++;
743
744 data->dasd_io_secs[sectors_ind]++;
745 data->dasd_io_times[tottime_ind]++;
746 data->dasd_io_timps[tottimeps_ind]++;
747 data->dasd_io_time1[strtime_ind]++;
748 data->dasd_io_time2[irqtime_ind]++;
749 data->dasd_io_time2ps[irqtimeps_ind]++;
750 data->dasd_io_time3[endtime_ind]++;
751
752 if (is_read) {
753 data->dasd_read_reqs++;
754 data->dasd_read_sects += sectors;
755 if (is_alias)
756 data->dasd_read_alias++;
757 if (is_tpm)
758 data->dasd_read_tpm++;
759 data->dasd_read_secs[sectors_ind]++;
760 data->dasd_read_times[tottime_ind]++;
761 data->dasd_read_time1[strtime_ind]++;
762 data->dasd_read_time2[irqtime_ind]++;
763 data->dasd_read_time3[endtime_ind]++;
764 }
765}
766
653static void dasd_profile_end(struct dasd_block *block, 767static void dasd_profile_end(struct dasd_block *block,
654 struct dasd_ccw_req *cqr, 768 struct dasd_ccw_req *cqr,
655 struct request *req) 769 struct request *req)
656{ 770{
657 long strtime, irqtime, endtime, tottime; /* in microseconds */ 771 long strtime, irqtime, endtime, tottime; /* in microseconds */
658 long tottimeps, sectors; 772 long tottimeps, sectors;
773 struct dasd_device *device;
774 int sectors_ind, tottime_ind, tottimeps_ind, strtime_ind;
775 int irqtime_ind, irqtimeps_ind, endtime_ind;
659 776
660 if (dasd_profile_level != DASD_PROFILE_ON) 777 device = cqr->startdev;
778 if (!(dasd_global_profile_level ||
779 block->profile.data ||
780 device->profile.data))
661 return; 781 return;
662 782
663 sectors = blk_rq_sectors(req); 783 sectors = blk_rq_sectors(req);
@@ -672,29 +792,392 @@ static void dasd_profile_end(struct dasd_block *block,
672 tottime = ((cqr->endclk - cqr->buildclk) >> 12); 792 tottime = ((cqr->endclk - cqr->buildclk) >> 12);
673 tottimeps = tottime / sectors; 793 tottimeps = tottime / sectors;
674 794
675 if (!dasd_global_profile.dasd_io_reqs) 795 dasd_profile_counter(sectors, sectors_ind);
676 memset(&dasd_global_profile, 0, 796 dasd_profile_counter(tottime, tottime_ind);
677 sizeof(struct dasd_profile_info_t)); 797 dasd_profile_counter(tottimeps, tottimeps_ind);
678 dasd_global_profile.dasd_io_reqs++; 798 dasd_profile_counter(strtime, strtime_ind);
679 dasd_global_profile.dasd_io_sects += sectors; 799 dasd_profile_counter(irqtime, irqtime_ind);
680 800 dasd_profile_counter(irqtime / sectors, irqtimeps_ind);
681 if (!block->profile.dasd_io_reqs) 801 dasd_profile_counter(endtime, endtime_ind);
682 memset(&block->profile, 0, 802
683 sizeof(struct dasd_profile_info_t)); 803 if (dasd_global_profile_level) {
684 block->profile.dasd_io_reqs++; 804 dasd_profile_end_add_data(&dasd_global_profile_data,
685 block->profile.dasd_io_sects += sectors; 805 cqr->startdev != block->base,
686 806 cqr->cpmode == 1,
687 dasd_profile_counter(sectors, dasd_io_secs, block); 807 rq_data_dir(req) == READ,
688 dasd_profile_counter(tottime, dasd_io_times, block); 808 sectors, sectors_ind, tottime_ind,
689 dasd_profile_counter(tottimeps, dasd_io_timps, block); 809 tottimeps_ind, strtime_ind,
690 dasd_profile_counter(strtime, dasd_io_time1, block); 810 irqtime_ind, irqtimeps_ind,
691 dasd_profile_counter(irqtime, dasd_io_time2, block); 811 endtime_ind);
692 dasd_profile_counter(irqtime / sectors, dasd_io_time2ps, block); 812 }
693 dasd_profile_counter(endtime, dasd_io_time3, block); 813
814 spin_lock(&block->profile.lock);
815 if (block->profile.data)
816 dasd_profile_end_add_data(block->profile.data,
817 cqr->startdev != block->base,
818 cqr->cpmode == 1,
819 rq_data_dir(req) == READ,
820 sectors, sectors_ind, tottime_ind,
821 tottimeps_ind, strtime_ind,
822 irqtime_ind, irqtimeps_ind,
823 endtime_ind);
824 spin_unlock(&block->profile.lock);
825
826 spin_lock(&device->profile.lock);
827 if (device->profile.data)
828 dasd_profile_end_add_data(device->profile.data,
829 cqr->startdev != block->base,
830 cqr->cpmode == 1,
831 rq_data_dir(req) == READ,
832 sectors, sectors_ind, tottime_ind,
833 tottimeps_ind, strtime_ind,
834 irqtime_ind, irqtimeps_ind,
835 endtime_ind);
836 spin_unlock(&device->profile.lock);
837}
838
839void dasd_profile_reset(struct dasd_profile *profile)
840{
841 struct dasd_profile_info *data;
842
843 spin_lock_bh(&profile->lock);
844 data = profile->data;
845 if (!data) {
846 spin_unlock_bh(&profile->lock);
847 return;
848 }
849 memset(data, 0, sizeof(*data));
850 getnstimeofday(&data->starttod);
851 spin_unlock_bh(&profile->lock);
852}
853
854void dasd_global_profile_reset(void)
855{
856 memset(&dasd_global_profile_data, 0, sizeof(dasd_global_profile_data));
857 getnstimeofday(&dasd_global_profile_data.starttod);
858}
859
860int dasd_profile_on(struct dasd_profile *profile)
861{
862 struct dasd_profile_info *data;
863
864 data = kzalloc(sizeof(*data), GFP_KERNEL);
865 if (!data)
866 return -ENOMEM;
867 spin_lock_bh(&profile->lock);
868 if (profile->data) {
869 spin_unlock_bh(&profile->lock);
870 kfree(data);
871 return 0;
872 }
873 getnstimeofday(&data->starttod);
874 profile->data = data;
875 spin_unlock_bh(&profile->lock);
876 return 0;
877}
878
879void dasd_profile_off(struct dasd_profile *profile)
880{
881 spin_lock_bh(&profile->lock);
882 kfree(profile->data);
883 profile->data = NULL;
884 spin_unlock_bh(&profile->lock);
885}
886
887char *dasd_get_user_string(const char __user *user_buf, size_t user_len)
888{
889 char *buffer;
890
891 buffer = kmalloc(user_len + 1, GFP_KERNEL);
892 if (buffer == NULL)
893 return ERR_PTR(-ENOMEM);
894 if (copy_from_user(buffer, user_buf, user_len) != 0) {
895 kfree(buffer);
896 return ERR_PTR(-EFAULT);
897 }
898 /* got the string, now strip linefeed. */
899 if (buffer[user_len - 1] == '\n')
900 buffer[user_len - 1] = 0;
901 else
902 buffer[user_len] = 0;
903 return buffer;
694} 904}
905
906static ssize_t dasd_stats_write(struct file *file,
907 const char __user *user_buf,
908 size_t user_len, loff_t *pos)
909{
910 char *buffer, *str;
911 int rc;
912 struct seq_file *m = (struct seq_file *)file->private_data;
913 struct dasd_profile *prof = m->private;
914
915 if (user_len > 65536)
916 user_len = 65536;
917 buffer = dasd_get_user_string(user_buf, user_len);
918 if (IS_ERR(buffer))
919 return PTR_ERR(buffer);
920
921 str = skip_spaces(buffer);
922 rc = user_len;
923 if (strncmp(str, "reset", 5) == 0) {
924 dasd_profile_reset(prof);
925 } else if (strncmp(str, "on", 2) == 0) {
926 rc = dasd_profile_on(prof);
927 if (!rc)
928 rc = user_len;
929 } else if (strncmp(str, "off", 3) == 0) {
930 dasd_profile_off(prof);
931 } else
932 rc = -EINVAL;
933 kfree(buffer);
934 return rc;
935}
936
937static void dasd_stats_array(struct seq_file *m, unsigned int *array)
938{
939 int i;
940
941 for (i = 0; i < 32; i++)
942 seq_printf(m, "%u ", array[i]);
943 seq_putc(m, '\n');
944}
945
946static void dasd_stats_seq_print(struct seq_file *m,
947 struct dasd_profile_info *data)
948{
949 seq_printf(m, "start_time %ld.%09ld\n",
950 data->starttod.tv_sec, data->starttod.tv_nsec);
951 seq_printf(m, "total_requests %u\n", data->dasd_io_reqs);
952 seq_printf(m, "total_sectors %u\n", data->dasd_io_sects);
953 seq_printf(m, "total_pav %u\n", data->dasd_io_alias);
954 seq_printf(m, "total_hpf %u\n", data->dasd_io_tpm);
955 seq_printf(m, "histogram_sectors ");
956 dasd_stats_array(m, data->dasd_io_secs);
957 seq_printf(m, "histogram_io_times ");
958 dasd_stats_array(m, data->dasd_io_times);
959 seq_printf(m, "histogram_io_times_weighted ");
960 dasd_stats_array(m, data->dasd_io_timps);
961 seq_printf(m, "histogram_time_build_to_ssch ");
962 dasd_stats_array(m, data->dasd_io_time1);
963 seq_printf(m, "histogram_time_ssch_to_irq ");
964 dasd_stats_array(m, data->dasd_io_time2);
965 seq_printf(m, "histogram_time_ssch_to_irq_weighted ");
966 dasd_stats_array(m, data->dasd_io_time2ps);
967 seq_printf(m, "histogram_time_irq_to_end ");
968 dasd_stats_array(m, data->dasd_io_time3);
969 seq_printf(m, "histogram_ccw_queue_length ");
970 dasd_stats_array(m, data->dasd_io_nr_req);
971 seq_printf(m, "total_read_requests %u\n", data->dasd_read_reqs);
972 seq_printf(m, "total_read_sectors %u\n", data->dasd_read_sects);
973 seq_printf(m, "total_read_pav %u\n", data->dasd_read_alias);
974 seq_printf(m, "total_read_hpf %u\n", data->dasd_read_tpm);
975 seq_printf(m, "histogram_read_sectors ");
976 dasd_stats_array(m, data->dasd_read_secs);
977 seq_printf(m, "histogram_read_times ");
978 dasd_stats_array(m, data->dasd_read_times);
979 seq_printf(m, "histogram_read_time_build_to_ssch ");
980 dasd_stats_array(m, data->dasd_read_time1);
981 seq_printf(m, "histogram_read_time_ssch_to_irq ");
982 dasd_stats_array(m, data->dasd_read_time2);
983 seq_printf(m, "histogram_read_time_irq_to_end ");
984 dasd_stats_array(m, data->dasd_read_time3);
985 seq_printf(m, "histogram_read_ccw_queue_length ");
986 dasd_stats_array(m, data->dasd_read_nr_req);
987}
988
989static int dasd_stats_show(struct seq_file *m, void *v)
990{
991 struct dasd_profile *profile;
992 struct dasd_profile_info *data;
993
994 profile = m->private;
995 spin_lock_bh(&profile->lock);
996 data = profile->data;
997 if (!data) {
998 spin_unlock_bh(&profile->lock);
999 seq_printf(m, "disabled\n");
1000 return 0;
1001 }
1002 dasd_stats_seq_print(m, data);
1003 spin_unlock_bh(&profile->lock);
1004 return 0;
1005}
1006
1007static int dasd_stats_open(struct inode *inode, struct file *file)
1008{
1009 struct dasd_profile *profile = inode->i_private;
1010 return single_open(file, dasd_stats_show, profile);
1011}
1012
1013static const struct file_operations dasd_stats_raw_fops = {
1014 .owner = THIS_MODULE,
1015 .open = dasd_stats_open,
1016 .read = seq_read,
1017 .llseek = seq_lseek,
1018 .release = single_release,
1019 .write = dasd_stats_write,
1020};
1021
1022static ssize_t dasd_stats_global_write(struct file *file,
1023 const char __user *user_buf,
1024 size_t user_len, loff_t *pos)
1025{
1026 char *buffer, *str;
1027 ssize_t rc;
1028
1029 if (user_len > 65536)
1030 user_len = 65536;
1031 buffer = dasd_get_user_string(user_buf, user_len);
1032 if (IS_ERR(buffer))
1033 return PTR_ERR(buffer);
1034 str = skip_spaces(buffer);
1035 rc = user_len;
1036 if (strncmp(str, "reset", 5) == 0) {
1037 dasd_global_profile_reset();
1038 } else if (strncmp(str, "on", 2) == 0) {
1039 dasd_global_profile_reset();
1040 dasd_global_profile_level = DASD_PROFILE_GLOBAL_ONLY;
1041 } else if (strncmp(str, "off", 3) == 0) {
1042 dasd_global_profile_level = DASD_PROFILE_OFF;
1043 } else
1044 rc = -EINVAL;
1045 kfree(buffer);
1046 return rc;
1047}
1048
1049static int dasd_stats_global_show(struct seq_file *m, void *v)
1050{
1051 if (!dasd_global_profile_level) {
1052 seq_printf(m, "disabled\n");
1053 return 0;
1054 }
1055 dasd_stats_seq_print(m, &dasd_global_profile_data);
1056 return 0;
1057}
1058
1059static int dasd_stats_global_open(struct inode *inode, struct file *file)
1060{
1061 return single_open(file, dasd_stats_global_show, NULL);
1062}
1063
1064static const struct file_operations dasd_stats_global_fops = {
1065 .owner = THIS_MODULE,
1066 .open = dasd_stats_global_open,
1067 .read = seq_read,
1068 .llseek = seq_lseek,
1069 .release = single_release,
1070 .write = dasd_stats_global_write,
1071};
1072
1073static void dasd_profile_init(struct dasd_profile *profile,
1074 struct dentry *base_dentry)
1075{
1076 mode_t mode;
1077 struct dentry *pde;
1078
1079 if (!base_dentry)
1080 return;
1081 profile->dentry = NULL;
1082 profile->data = NULL;
1083 mode = (S_IRUSR | S_IWUSR | S_IFREG);
1084 pde = debugfs_create_file("statistics", mode, base_dentry,
1085 profile, &dasd_stats_raw_fops);
1086 if (pde && !IS_ERR(pde))
1087 profile->dentry = pde;
1088 return;
1089}
1090
1091static void dasd_profile_exit(struct dasd_profile *profile)
1092{
1093 dasd_profile_off(profile);
1094 if (profile->dentry) {
1095 debugfs_remove(profile->dentry);
1096 profile->dentry = NULL;
1097 }
1098}
1099
1100static void dasd_statistics_removeroot(void)
1101{
1102 dasd_global_profile_level = DASD_PROFILE_OFF;
1103 if (dasd_global_profile_dentry) {
1104 debugfs_remove(dasd_global_profile_dentry);
1105 dasd_global_profile_dentry = NULL;
1106 }
1107 if (dasd_debugfs_global_entry)
1108 debugfs_remove(dasd_debugfs_global_entry);
1109 if (dasd_debugfs_root_entry)
1110 debugfs_remove(dasd_debugfs_root_entry);
1111}
1112
1113static void dasd_statistics_createroot(void)
1114{
1115 mode_t mode;
1116 struct dentry *pde;
1117
1118 dasd_debugfs_root_entry = NULL;
1119 dasd_debugfs_global_entry = NULL;
1120 dasd_global_profile_dentry = NULL;
1121 pde = debugfs_create_dir("dasd", NULL);
1122 if (!pde || IS_ERR(pde))
1123 goto error;
1124 dasd_debugfs_root_entry = pde;
1125 pde = debugfs_create_dir("global", dasd_debugfs_root_entry);
1126 if (!pde || IS_ERR(pde))
1127 goto error;
1128 dasd_debugfs_global_entry = pde;
1129
1130 mode = (S_IRUSR | S_IWUSR | S_IFREG);
1131 pde = debugfs_create_file("statistics", mode, dasd_debugfs_global_entry,
1132 NULL, &dasd_stats_global_fops);
1133 if (!pde || IS_ERR(pde))
1134 goto error;
1135 dasd_global_profile_dentry = pde;
1136 return;
1137
1138error:
1139 DBF_EVENT(DBF_ERR, "%s",
1140 "Creation of the dasd debugfs interface failed");
1141 dasd_statistics_removeroot();
1142 return;
1143}
1144
695#else 1145#else
696#define dasd_profile_start(block, cqr, req) do {} while (0) 1146#define dasd_profile_start(block, cqr, req) do {} while (0)
697#define dasd_profile_end(block, cqr, req) do {} while (0) 1147#define dasd_profile_end(block, cqr, req) do {} while (0)
1148
1149static void dasd_statistics_createroot(void)
1150{
1151 return;
1152}
1153
1154static void dasd_statistics_removeroot(void)
1155{
1156 return;
1157}
1158
1159int dasd_stats_generic_show(struct seq_file *m, void *v)
1160{
1161 seq_printf(m, "Statistics are not activated in this kernel\n");
1162 return 0;
1163}
1164
1165static void dasd_profile_init(struct dasd_profile *profile,
1166 struct dentry *base_dentry)
1167{
1168 return;
1169}
1170
1171static void dasd_profile_exit(struct dasd_profile *profile)
1172{
1173 return;
1174}
1175
1176int dasd_profile_on(struct dasd_profile *profile)
1177{
1178 return 0;
1179}
1180
698#endif /* CONFIG_DASD_PROFILE */ 1181#endif /* CONFIG_DASD_PROFILE */
699 1182
700/* 1183/*
@@ -2441,6 +2924,7 @@ dasd_exit(void)
2441 debug_unregister(dasd_debug_area); 2924 debug_unregister(dasd_debug_area);
2442 dasd_debug_area = NULL; 2925 dasd_debug_area = NULL;
2443 } 2926 }
2927 dasd_statistics_removeroot();
2444} 2928}
2445 2929
2446/* 2930/*
@@ -2992,6 +3476,8 @@ static int __init dasd_init(void)
2992 3476
2993 dasd_diag_discipline_pointer = NULL; 3477 dasd_diag_discipline_pointer = NULL;
2994 3478
3479 dasd_statistics_createroot();
3480
2995 rc = dasd_devmap_init(); 3481 rc = dasd_devmap_init();
2996 if (rc) 3482 if (rc)
2997 goto failed; 3483 goto failed;
diff --git a/drivers/s390/block/dasd_int.h b/drivers/s390/block/dasd_int.h
index d1e4f2c1264..1dd12bd85a6 100644
--- a/drivers/s390/block/dasd_int.h
+++ b/drivers/s390/block/dasd_int.h
@@ -382,6 +382,41 @@ struct dasd_path {
382 __u8 npm; 382 __u8 npm;
383}; 383};
384 384
385struct dasd_profile_info {
386 /* legacy part of profile data, as in dasd_profile_info_t */
387 unsigned int dasd_io_reqs; /* number of requests processed */
388 unsigned int dasd_io_sects; /* number of sectors processed */
389 unsigned int dasd_io_secs[32]; /* histogram of request's sizes */
390 unsigned int dasd_io_times[32]; /* histogram of requests's times */
391 unsigned int dasd_io_timps[32]; /* h. of requests's times per sector */
392 unsigned int dasd_io_time1[32]; /* hist. of time from build to start */
393 unsigned int dasd_io_time2[32]; /* hist. of time from start to irq */
394 unsigned int dasd_io_time2ps[32]; /* hist. of time from start to irq */
395 unsigned int dasd_io_time3[32]; /* hist. of time from irq to end */
396 unsigned int dasd_io_nr_req[32]; /* hist. of # of requests in chanq */
397
398 /* new data */
399 struct timespec starttod; /* time of start or last reset */
400 unsigned int dasd_io_alias; /* requests using an alias */
401 unsigned int dasd_io_tpm; /* requests using transport mode */
402 unsigned int dasd_read_reqs; /* total number of read requests */
403 unsigned int dasd_read_sects; /* total number read sectors */
404 unsigned int dasd_read_alias; /* read request using an alias */
405 unsigned int dasd_read_tpm; /* read requests in transport mode */
406 unsigned int dasd_read_secs[32]; /* histogram of request's sizes */
407 unsigned int dasd_read_times[32]; /* histogram of requests's times */
408 unsigned int dasd_read_time1[32]; /* hist. time from build to start */
409 unsigned int dasd_read_time2[32]; /* hist. of time from start to irq */
410 unsigned int dasd_read_time3[32]; /* hist. of time from irq to end */
411 unsigned int dasd_read_nr_req[32]; /* hist. of # of requests in chanq */
412};
413
414struct dasd_profile {
415 struct dentry *dentry;
416 struct dasd_profile_info *data;
417 spinlock_t lock;
418};
419
385struct dasd_device { 420struct dasd_device {
386 /* Block device stuff. */ 421 /* Block device stuff. */
387 struct dasd_block *block; 422 struct dasd_block *block;
@@ -431,6 +466,9 @@ struct dasd_device {
431 466
432 /* default expiration time in s */ 467 /* default expiration time in s */
433 unsigned long default_expires; 468 unsigned long default_expires;
469
470 struct dentry *debugfs_dentry;
471 struct dasd_profile profile;
434}; 472};
435 473
436struct dasd_block { 474struct dasd_block {
@@ -453,9 +491,8 @@ struct dasd_block {
453 struct tasklet_struct tasklet; 491 struct tasklet_struct tasklet;
454 struct timer_list timer; 492 struct timer_list timer;
455 493
456#ifdef CONFIG_DASD_PROFILE 494 struct dentry *debugfs_dentry;
457 struct dasd_profile_info_t profile; 495 struct dasd_profile profile;
458#endif
459}; 496};
460 497
461 498
@@ -589,12 +626,13 @@ dasd_check_blocksize(int bsize)
589} 626}
590 627
591/* externals in dasd.c */ 628/* externals in dasd.c */
592#define DASD_PROFILE_ON 1 629#define DASD_PROFILE_OFF 0
593#define DASD_PROFILE_OFF 0 630#define DASD_PROFILE_ON 1
631#define DASD_PROFILE_GLOBAL_ONLY 2
594 632
595extern debug_info_t *dasd_debug_area; 633extern debug_info_t *dasd_debug_area;
596extern struct dasd_profile_info_t dasd_global_profile; 634extern struct dasd_profile_info dasd_global_profile_data;
597extern unsigned int dasd_profile_level; 635extern unsigned int dasd_global_profile_level;
598extern const struct block_device_operations dasd_device_operations; 636extern const struct block_device_operations dasd_device_operations;
599 637
600extern struct kmem_cache *dasd_page_cache; 638extern struct kmem_cache *dasd_page_cache;
@@ -662,6 +700,11 @@ void dasd_device_remove_stop_bits(struct dasd_device *, int);
662 700
663int dasd_device_is_ro(struct dasd_device *); 701int dasd_device_is_ro(struct dasd_device *);
664 702
703void dasd_profile_reset(struct dasd_profile *);
704int dasd_profile_on(struct dasd_profile *);
705void dasd_profile_off(struct dasd_profile *);
706void dasd_global_profile_reset(void);
707char *dasd_get_user_string(const char __user *, size_t);
665 708
666/* externals in dasd_devmap.c */ 709/* externals in dasd_devmap.c */
667extern int dasd_max_devindex; 710extern int dasd_max_devindex;
diff --git a/drivers/s390/block/dasd_ioctl.c b/drivers/s390/block/dasd_ioctl.c
index 72261e4c516..eb4e034378c 100644
--- a/drivers/s390/block/dasd_ioctl.c
+++ b/drivers/s390/block/dasd_ioctl.c
@@ -239,7 +239,7 @@ dasd_ioctl_format(struct block_device *bdev, void __user *argp)
239 */ 239 */
240static int dasd_ioctl_reset_profile(struct dasd_block *block) 240static int dasd_ioctl_reset_profile(struct dasd_block *block)
241{ 241{
242 memset(&block->profile, 0, sizeof(struct dasd_profile_info_t)); 242 dasd_profile_reset(&block->profile);
243 return 0; 243 return 0;
244} 244}
245 245
@@ -248,10 +248,40 @@ static int dasd_ioctl_reset_profile(struct dasd_block *block)
248 */ 248 */
249static int dasd_ioctl_read_profile(struct dasd_block *block, void __user *argp) 249static int dasd_ioctl_read_profile(struct dasd_block *block, void __user *argp)
250{ 250{
251 if (dasd_profile_level == DASD_PROFILE_OFF) 251 struct dasd_profile_info_t *data;
252
253 data = kmalloc(sizeof(*data), GFP_KERNEL);
254 if (!data)
255 return -ENOMEM;
256
257 spin_lock_bh(&block->profile.lock);
258 if (block->profile.data) {
259 data->dasd_io_reqs = block->profile.data->dasd_io_reqs;
260 data->dasd_io_sects = block->profile.data->dasd_io_sects;
261 memcpy(data->dasd_io_secs, block->profile.data->dasd_io_secs,
262 sizeof(data->dasd_io_secs));
263 memcpy(data->dasd_io_times, block->profile.data->dasd_io_times,
264 sizeof(data->dasd_io_times));
265 memcpy(data->dasd_io_timps, block->profile.data->dasd_io_timps,
266 sizeof(data->dasd_io_timps));
267 memcpy(data->dasd_io_time1, block->profile.data->dasd_io_time1,
268 sizeof(data->dasd_io_time1));
269 memcpy(data->dasd_io_time2, block->profile.data->dasd_io_time2,
270 sizeof(data->dasd_io_time2));
271 memcpy(data->dasd_io_time2ps,
272 block->profile.data->dasd_io_time2ps,
273 sizeof(data->dasd_io_time2ps));
274 memcpy(data->dasd_io_time3, block->profile.data->dasd_io_time3,
275 sizeof(data->dasd_io_time3));
276 memcpy(data->dasd_io_nr_req,
277 block->profile.data->dasd_io_nr_req,
278 sizeof(data->dasd_io_nr_req));
279 spin_unlock_bh(&block->profile.lock);
280 } else {
281 spin_unlock_bh(&block->profile.lock);
252 return -EIO; 282 return -EIO;
253 if (copy_to_user(argp, &block->profile, 283 }
254 sizeof(struct dasd_profile_info_t))) 284 if (copy_to_user(argp, data, sizeof(*data)))
255 return -EFAULT; 285 return -EFAULT;
256 return 0; 286 return 0;
257} 287}
diff --git a/drivers/s390/block/dasd_proc.c b/drivers/s390/block/dasd_proc.c
index c4a6a31bd9c..6c3c5364d08 100644
--- a/drivers/s390/block/dasd_proc.c
+++ b/drivers/s390/block/dasd_proc.c
@@ -32,28 +32,6 @@ static struct proc_dir_entry *dasd_proc_root_entry = NULL;
32static struct proc_dir_entry *dasd_devices_entry = NULL; 32static struct proc_dir_entry *dasd_devices_entry = NULL;
33static struct proc_dir_entry *dasd_statistics_entry = NULL; 33static struct proc_dir_entry *dasd_statistics_entry = NULL;
34 34
35#ifdef CONFIG_DASD_PROFILE
36static char *
37dasd_get_user_string(const char __user *user_buf, size_t user_len)
38{
39 char *buffer;
40
41 buffer = kmalloc(user_len + 1, GFP_KERNEL);
42 if (buffer == NULL)
43 return ERR_PTR(-ENOMEM);
44 if (copy_from_user(buffer, user_buf, user_len) != 0) {
45 kfree(buffer);
46 return ERR_PTR(-EFAULT);
47 }
48 /* got the string, now strip linefeed. */
49 if (buffer[user_len - 1] == '\n')
50 buffer[user_len - 1] = 0;
51 else
52 buffer[user_len] = 0;
53 return buffer;
54}
55#endif /* CONFIG_DASD_PROFILE */
56
57static int 35static int
58dasd_devices_show(struct seq_file *m, void *v) 36dasd_devices_show(struct seq_file *m, void *v)
59{ 37{
@@ -167,6 +145,55 @@ static const struct file_operations dasd_devices_file_ops = {
167}; 145};
168 146
169#ifdef CONFIG_DASD_PROFILE 147#ifdef CONFIG_DASD_PROFILE
148static int dasd_stats_all_block_on(void)
149{
150 int i, rc;
151 struct dasd_device *device;
152
153 rc = 0;
154 for (i = 0; i < dasd_max_devindex; ++i) {
155 device = dasd_device_from_devindex(i);
156 if (IS_ERR(device))
157 continue;
158 if (device->block)
159 rc = dasd_profile_on(&device->block->profile);
160 dasd_put_device(device);
161 if (rc)
162 return rc;
163 }
164 return 0;
165}
166
167static void dasd_stats_all_block_off(void)
168{
169 int i;
170 struct dasd_device *device;
171
172 for (i = 0; i < dasd_max_devindex; ++i) {
173 device = dasd_device_from_devindex(i);
174 if (IS_ERR(device))
175 continue;
176 if (device->block)
177 dasd_profile_off(&device->block->profile);
178 dasd_put_device(device);
179 }
180}
181
182static void dasd_stats_all_block_reset(void)
183{
184 int i;
185 struct dasd_device *device;
186
187 for (i = 0; i < dasd_max_devindex; ++i) {
188 device = dasd_device_from_devindex(i);
189 if (IS_ERR(device))
190 continue;
191 if (device->block)
192 dasd_profile_reset(&device->block->profile);
193 dasd_put_device(device);
194 }
195}
196
170static void dasd_statistics_array(struct seq_file *m, unsigned int *array, int factor) 197static void dasd_statistics_array(struct seq_file *m, unsigned int *array, int factor)
171{ 198{
172 int i; 199 int i;
@@ -183,18 +210,18 @@ static void dasd_statistics_array(struct seq_file *m, unsigned int *array, int f
183static int dasd_stats_proc_show(struct seq_file *m, void *v) 210static int dasd_stats_proc_show(struct seq_file *m, void *v)
184{ 211{
185#ifdef CONFIG_DASD_PROFILE 212#ifdef CONFIG_DASD_PROFILE
186 struct dasd_profile_info_t *prof; 213 struct dasd_profile_info *prof;
187 int factor; 214 int factor;
188 215
189 /* check for active profiling */ 216 /* check for active profiling */
190 if (dasd_profile_level == DASD_PROFILE_OFF) { 217 if (!dasd_global_profile_level) {
191 seq_printf(m, "Statistics are off - they might be " 218 seq_printf(m, "Statistics are off - they might be "
192 "switched on using 'echo set on > " 219 "switched on using 'echo set on > "
193 "/proc/dasd/statistics'\n"); 220 "/proc/dasd/statistics'\n");
194 return 0; 221 return 0;
195 } 222 }
223 prof = &dasd_global_profile_data;
196 224
197 prof = &dasd_global_profile;
198 /* prevent counter 'overflow' on output */ 225 /* prevent counter 'overflow' on output */
199 for (factor = 1; (prof->dasd_io_reqs / factor) > 9999999; 226 for (factor = 1; (prof->dasd_io_reqs / factor) > 9999999;
200 factor *= 10); 227 factor *= 10);
@@ -245,6 +272,7 @@ static ssize_t dasd_stats_proc_write(struct file *file,
245{ 272{
246#ifdef CONFIG_DASD_PROFILE 273#ifdef CONFIG_DASD_PROFILE
247 char *buffer, *str; 274 char *buffer, *str;
275 int rc;
248 276
249 if (user_len > 65536) 277 if (user_len > 65536)
250 user_len = 65536; 278 user_len = 65536;
@@ -259,32 +287,40 @@ static ssize_t dasd_stats_proc_write(struct file *file,
259 str = skip_spaces(str + 4); 287 str = skip_spaces(str + 4);
260 if (strcmp(str, "on") == 0) { 288 if (strcmp(str, "on") == 0) {
261 /* switch on statistics profiling */ 289 /* switch on statistics profiling */
262 dasd_profile_level = DASD_PROFILE_ON; 290 rc = dasd_stats_all_block_on();
291 if (rc) {
292 dasd_stats_all_block_off();
293 goto out_error;
294 }
295 dasd_global_profile_reset();
296 dasd_global_profile_level = DASD_PROFILE_ON;
263 pr_info("The statistics feature has been switched " 297 pr_info("The statistics feature has been switched "
264 "on\n"); 298 "on\n");
265 } else if (strcmp(str, "off") == 0) { 299 } else if (strcmp(str, "off") == 0) {
266 /* switch off and reset statistics profiling */ 300 /* switch off and reset statistics profiling */
267 memset(&dasd_global_profile, 301 dasd_global_profile_level = DASD_PROFILE_OFF;
268 0, sizeof (struct dasd_profile_info_t)); 302 dasd_global_profile_reset();
269 dasd_profile_level = DASD_PROFILE_OFF; 303 dasd_stats_all_block_off();
270 pr_info("The statistics feature has been switched " 304 pr_info("The statistics feature has been switched "
271 "off\n"); 305 "off\n");
272 } else 306 } else
273 goto out_error; 307 goto out_parse_error;
274 } else if (strncmp(str, "reset", 5) == 0) { 308 } else if (strncmp(str, "reset", 5) == 0) {
275 /* reset the statistics */ 309 /* reset the statistics */
276 memset(&dasd_global_profile, 0, 310 dasd_global_profile_reset();
277 sizeof (struct dasd_profile_info_t)); 311 dasd_stats_all_block_reset();
278 pr_info("The statistics have been reset\n"); 312 pr_info("The statistics have been reset\n");
279 } else 313 } else
280 goto out_error; 314 goto out_parse_error;
281 kfree(buffer); 315 kfree(buffer);
282 return user_len; 316 return user_len;
283out_error: 317out_parse_error:
318 rc = -EINVAL;
284 pr_warning("%s is not a supported value for /proc/dasd/statistics\n", 319 pr_warning("%s is not a supported value for /proc/dasd/statistics\n",
285 str); 320 str);
321out_error:
286 kfree(buffer); 322 kfree(buffer);
287 return -EINVAL; 323 return rc;
288#else 324#else
289 pr_warning("/proc/dasd/statistics: is not activated in this kernel\n"); 325 pr_warning("/proc/dasd/statistics: is not activated in this kernel\n");
290 return user_len; 326 return user_len;