diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2011-07-24 12:55:45 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2011-07-24 12:55:45 -0400 |
commit | 21c7075fa5a756f1c95f6b463ff42cd320cc0301 (patch) | |
tree | 69524dd01fbebe662abe3b7296664592d3ce562b /drivers | |
parent | ff0c4ad2c3a75ccfe6adca916e50804eb45bb2d9 (diff) | |
parent | 73b7d40ff1bcd44b4245c2714b88cf872fe44685 (diff) |
Merge branch 'for-linus' of git://git390.marist.edu/pub/scm/linux-2.6
* 'for-linus' of git://git390.marist.edu/pub/scm/linux-2.6: (21 commits)
[S390] use siginfo for sigtrap signals
[S390] dasd: add enhanced DASD statistics interface
[S390] kvm: make sigp emerg smp capable
[S390] disable cpu measurement alerts on a dying cpu
[S390] initial cr0 bits
[S390] iucv cr0 enablement bit
[S390] race safe external interrupt registration
[S390] remove tape block docu
[S390] ap: toleration support for ap device type 10
[S390] cleanup program check handler prototypes
[S390] remove kvm mmu reload on s390
[S390] Use gmap translation for accessing guest memory
[S390] use gmap address spaces for kvm guest images
[S390] kvm guest address space mapping
[S390] fix s390 assembler code alignments
[S390] move sie code to entry.S
[S390] kvm: handle tprot intercepts
[S390] qdio: clear shared DSCI before scheduling the queue handler
[S390] reference bit testing for unmapped pages
[S390] irqs: Do not trace arch_local_{*,irq_*} functions
...
Diffstat (limited to 'drivers')
-rw-r--r-- | drivers/s390/block/dasd.c | 576 | ||||
-rw-r--r-- | drivers/s390/block/dasd_int.h | 57 | ||||
-rw-r--r-- | drivers/s390/block/dasd_ioctl.c | 38 | ||||
-rw-r--r-- | drivers/s390/block/dasd_proc.c | 106 | ||||
-rw-r--r-- | drivers/s390/char/Kconfig | 3 | ||||
-rw-r--r-- | drivers/s390/cio/qdio_thinint.c | 15 | ||||
-rw-r--r-- | drivers/s390/crypto/ap_bus.c | 96 | ||||
-rw-r--r-- | drivers/s390/crypto/ap_bus.h | 22 |
8 files changed, 782 insertions, 131 deletions
diff --git a/drivers/s390/block/dasd.c b/drivers/s390/block/dasd.c index 86b6f1cc1b10..432444af7ee4 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 | */ |
47 | debug_info_t *dasd_debug_area; | 49 | debug_info_t *dasd_debug_area; |
50 | static struct dentry *dasd_debugfs_root_entry; | ||
48 | struct dasd_discipline *dasd_diag_discipline_pointer; | 51 | struct dasd_discipline *dasd_diag_discipline_pointer; |
49 | void dasd_int_handler(struct ccw_device *, unsigned long, struct irb *); | 52 | void 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 *); | |||
71 | static void dasd_device_timeout(unsigned long); | 74 | static void dasd_device_timeout(unsigned long); |
72 | static void dasd_block_timeout(unsigned long); | 75 | static void dasd_block_timeout(unsigned long); |
73 | static void __dasd_process_erp(struct dasd_device *, struct dasd_ccw_req *); | 76 | static void __dasd_process_erp(struct dasd_device *, struct dasd_ccw_req *); |
77 | static void dasd_profile_init(struct dasd_profile *, struct dentry *); | ||
78 | static 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 | ||
231 | static 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 | */ |
228 | static int dasd_state_known_to_basic(struct dasd_device *device) | 247 | static 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 | ||
614 | struct dasd_profile_info_t dasd_global_profile; | 650 | unsigned int dasd_global_profile_level = DASD_PROFILE_OFF; |
615 | unsigned int dasd_profile_level = DASD_PROFILE_OFF; | ||
616 | 651 | ||
617 | /* | 652 | #ifdef CONFIG_DASD_PROFILE |
618 | * Increments counter in global and local profiling structures. | 653 | struct dasd_profile_info dasd_global_profile_data; |
619 | */ | 654 | static struct dentry *dasd_global_profile_dentry; |
620 | #define dasd_profile_counter(value, counter, block) \ | 655 | static 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 | |||
719 | static 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 | |||
653 | static void dasd_profile_end(struct dasd_block *block, | 767 | static 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 | |||
839 | void 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 | |||
854 | void 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 | |||
860 | int 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 | |||
879 | void 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 | |||
887 | char *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 | |||
906 | static 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 | |||
937 | static 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 | |||
946 | static 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 | |||
989 | static 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 | |||
1007 | static 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 | |||
1013 | static 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 | |||
1022 | static 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 | |||
1049 | static 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 | |||
1059 | static int dasd_stats_global_open(struct inode *inode, struct file *file) | ||
1060 | { | ||
1061 | return single_open(file, dasd_stats_global_show, NULL); | ||
1062 | } | ||
1063 | |||
1064 | static 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 | |||
1073 | static 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 | |||
1091 | static 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 | |||
1100 | static 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 | |||
1113 | static 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 | |||
1138 | error: | ||
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 | |||
1149 | static void dasd_statistics_createroot(void) | ||
1150 | { | ||
1151 | return; | ||
1152 | } | ||
1153 | |||
1154 | static void dasd_statistics_removeroot(void) | ||
1155 | { | ||
1156 | return; | ||
1157 | } | ||
1158 | |||
1159 | int 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 | |||
1165 | static void dasd_profile_init(struct dasd_profile *profile, | ||
1166 | struct dentry *base_dentry) | ||
1167 | { | ||
1168 | return; | ||
1169 | } | ||
1170 | |||
1171 | static void dasd_profile_exit(struct dasd_profile *profile) | ||
1172 | { | ||
1173 | return; | ||
1174 | } | ||
1175 | |||
1176 | int 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 d1e4f2c1264c..1dd12bd85a69 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 | ||
385 | struct 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 | |||
414 | struct dasd_profile { | ||
415 | struct dentry *dentry; | ||
416 | struct dasd_profile_info *data; | ||
417 | spinlock_t lock; | ||
418 | }; | ||
419 | |||
385 | struct dasd_device { | 420 | struct 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 | ||
436 | struct dasd_block { | 474 | struct 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 | ||
595 | extern debug_info_t *dasd_debug_area; | 633 | extern debug_info_t *dasd_debug_area; |
596 | extern struct dasd_profile_info_t dasd_global_profile; | 634 | extern struct dasd_profile_info dasd_global_profile_data; |
597 | extern unsigned int dasd_profile_level; | 635 | extern unsigned int dasd_global_profile_level; |
598 | extern const struct block_device_operations dasd_device_operations; | 636 | extern const struct block_device_operations dasd_device_operations; |
599 | 637 | ||
600 | extern struct kmem_cache *dasd_page_cache; | 638 | extern struct kmem_cache *dasd_page_cache; |
@@ -662,6 +700,11 @@ void dasd_device_remove_stop_bits(struct dasd_device *, int); | |||
662 | 700 | ||
663 | int dasd_device_is_ro(struct dasd_device *); | 701 | int dasd_device_is_ro(struct dasd_device *); |
664 | 702 | ||
703 | void dasd_profile_reset(struct dasd_profile *); | ||
704 | int dasd_profile_on(struct dasd_profile *); | ||
705 | void dasd_profile_off(struct dasd_profile *); | ||
706 | void dasd_global_profile_reset(void); | ||
707 | char *dasd_get_user_string(const char __user *, size_t); | ||
665 | 708 | ||
666 | /* externals in dasd_devmap.c */ | 709 | /* externals in dasd_devmap.c */ |
667 | extern int dasd_max_devindex; | 710 | extern int dasd_max_devindex; |
diff --git a/drivers/s390/block/dasd_ioctl.c b/drivers/s390/block/dasd_ioctl.c index 72261e4c516d..eb4e034378cd 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 | */ |
240 | static int dasd_ioctl_reset_profile(struct dasd_block *block) | 240 | static 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 | */ |
249 | static int dasd_ioctl_read_profile(struct dasd_block *block, void __user *argp) | 249 | static 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 c4a6a31bd9cd..6c3c5364d082 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; | |||
32 | static struct proc_dir_entry *dasd_devices_entry = NULL; | 32 | static struct proc_dir_entry *dasd_devices_entry = NULL; |
33 | static struct proc_dir_entry *dasd_statistics_entry = NULL; | 33 | static struct proc_dir_entry *dasd_statistics_entry = NULL; |
34 | 34 | ||
35 | #ifdef CONFIG_DASD_PROFILE | ||
36 | static char * | ||
37 | dasd_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 | |||
57 | static int | 35 | static int |
58 | dasd_devices_show(struct seq_file *m, void *v) | 36 | dasd_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 |
148 | static 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 | |||
167 | static 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 | |||
182 | static 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 | |||
170 | static void dasd_statistics_array(struct seq_file *m, unsigned int *array, int factor) | 197 | static 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 | |||
183 | static int dasd_stats_proc_show(struct seq_file *m, void *v) | 210 | static 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; |
283 | out_error: | 317 | out_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); |
321 | out_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; |
diff --git a/drivers/s390/char/Kconfig b/drivers/s390/char/Kconfig index a4f117d9fdc6..2c9a776bd63c 100644 --- a/drivers/s390/char/Kconfig +++ b/drivers/s390/char/Kconfig | |||
@@ -116,9 +116,6 @@ config S390_TAPE | |||
116 | called tape390 and include all selected interfaces and | 116 | called tape390 and include all selected interfaces and |
117 | hardware drivers. | 117 | hardware drivers. |
118 | 118 | ||
119 | comment "S/390 tape interface support" | ||
120 | depends on S390_TAPE | ||
121 | |||
122 | comment "S/390 tape hardware support" | 119 | comment "S/390 tape hardware support" |
123 | depends on S390_TAPE | 120 | depends on S390_TAPE |
124 | 121 | ||
diff --git a/drivers/s390/cio/qdio_thinint.c b/drivers/s390/cio/qdio_thinint.c index 5c4e741d8221..68be6e157126 100644 --- a/drivers/s390/cio/qdio_thinint.c +++ b/drivers/s390/cio/qdio_thinint.c | |||
@@ -95,9 +95,11 @@ void tiqdio_remove_input_queues(struct qdio_irq *irq_ptr) | |||
95 | } | 95 | } |
96 | } | 96 | } |
97 | 97 | ||
98 | static inline u32 shared_ind_set(void) | 98 | static inline u32 clear_shared_ind(void) |
99 | { | 99 | { |
100 | return q_indicators[TIQDIO_SHARED_IND].ind; | 100 | if (!atomic_read(&q_indicators[TIQDIO_SHARED_IND].count)) |
101 | return 0; | ||
102 | return xchg(&q_indicators[TIQDIO_SHARED_IND].ind, 0); | ||
101 | } | 103 | } |
102 | 104 | ||
103 | /** | 105 | /** |
@@ -107,7 +109,7 @@ static inline u32 shared_ind_set(void) | |||
107 | */ | 109 | */ |
108 | static void tiqdio_thinint_handler(void *alsi, void *data) | 110 | static void tiqdio_thinint_handler(void *alsi, void *data) |
109 | { | 111 | { |
110 | u32 si_used = shared_ind_set(); | 112 | u32 si_used = clear_shared_ind(); |
111 | struct qdio_q *q; | 113 | struct qdio_q *q; |
112 | 114 | ||
113 | last_ai_time = S390_lowcore.int_clock; | 115 | last_ai_time = S390_lowcore.int_clock; |
@@ -150,13 +152,6 @@ static void tiqdio_thinint_handler(void *alsi, void *data) | |||
150 | qperf_inc(q, adapter_int); | 152 | qperf_inc(q, adapter_int); |
151 | } | 153 | } |
152 | rcu_read_unlock(); | 154 | rcu_read_unlock(); |
153 | |||
154 | /* | ||
155 | * If the shared indicator was used clear it now after all queues | ||
156 | * were processed. | ||
157 | */ | ||
158 | if (si_used && shared_ind_set()) | ||
159 | xchg(&q_indicators[TIQDIO_SHARED_IND].ind, 0); | ||
160 | } | 155 | } |
161 | 156 | ||
162 | static int set_subchannel_ind(struct qdio_irq *irq_ptr, int reset) | 157 | static int set_subchannel_ind(struct qdio_irq *irq_ptr, int reset) |
diff --git a/drivers/s390/crypto/ap_bus.c b/drivers/s390/crypto/ap_bus.c index 16e4a25596e7..f8134a44cefa 100644 --- a/drivers/s390/crypto/ap_bus.c +++ b/drivers/s390/crypto/ap_bus.c | |||
@@ -6,6 +6,7 @@ | |||
6 | * Martin Schwidefsky <schwidefsky@de.ibm.com> | 6 | * Martin Schwidefsky <schwidefsky@de.ibm.com> |
7 | * Ralph Wuerthner <rwuerthn@de.ibm.com> | 7 | * Ralph Wuerthner <rwuerthn@de.ibm.com> |
8 | * Felix Beck <felix.beck@de.ibm.com> | 8 | * Felix Beck <felix.beck@de.ibm.com> |
9 | * Holger Dengler <hd@linux.vnet.ibm.com> | ||
9 | * | 10 | * |
10 | * Adjunct processor bus. | 11 | * Adjunct processor bus. |
11 | * | 12 | * |
@@ -222,47 +223,52 @@ ap_queue_interruption_control(ap_qid_t qid, void *ind) | |||
222 | } | 223 | } |
223 | #endif | 224 | #endif |
224 | 225 | ||
225 | static inline struct ap_queue_status __ap_4096_commands_available(ap_qid_t qid, | 226 | #ifdef CONFIG_64BIT |
226 | int *support) | 227 | static inline struct ap_queue_status |
228 | __ap_query_functions(ap_qid_t qid, unsigned int *functions) | ||
227 | { | 229 | { |
228 | register unsigned long reg0 asm ("0") = 0UL | qid | (1UL << 23); | 230 | register unsigned long reg0 asm ("0") = 0UL | qid | (1UL << 23); |
229 | register struct ap_queue_status reg1 asm ("1"); | 231 | register struct ap_queue_status reg1 asm ("1") = AP_QUEUE_STATUS_INVALID; |
230 | register unsigned long reg2 asm ("2") = 0UL; | 232 | register unsigned long reg2 asm ("2"); |
231 | 233 | ||
232 | asm volatile( | 234 | asm volatile( |
233 | ".long 0xb2af0000\n" | 235 | ".long 0xb2af0000\n" |
234 | "0: la %1,0\n" | 236 | "0:\n" |
235 | "1:\n" | 237 | EX_TABLE(0b, 0b) |
236 | EX_TABLE(0b, 1b) | 238 | : "+d" (reg0), "+d" (reg1), "=d" (reg2) |
237 | : "+d" (reg0), "=d" (reg1), "=d" (reg2) | ||
238 | : | 239 | : |
239 | : "cc"); | 240 | : "cc"); |
240 | 241 | ||
241 | if (reg2 & 0x6000000000000000ULL) | 242 | *functions = (unsigned int)(reg2 >> 32); |
242 | *support = 1; | ||
243 | else | ||
244 | *support = 0; | ||
245 | |||
246 | return reg1; | 243 | return reg1; |
247 | } | 244 | } |
245 | #endif | ||
248 | 246 | ||
249 | /** | 247 | /** |
250 | * ap_4096_commands_availablen(): Check for availability of 4096 bit RSA | 248 | * ap_query_functions(): Query supported functions. |
251 | * support. | ||
252 | * @qid: The AP queue number | 249 | * @qid: The AP queue number |
250 | * @functions: Pointer to functions field. | ||
253 | * | 251 | * |
254 | * Returns 1 if 4096 bit RSA keys are support fo the AP, returns 0 if not. | 252 | * Returns |
253 | * 0 on success. | ||
254 | * -ENODEV if queue not valid. | ||
255 | * -EBUSY if device busy. | ||
256 | * -EINVAL if query function is not supported | ||
255 | */ | 257 | */ |
256 | int ap_4096_commands_available(ap_qid_t qid) | 258 | static int ap_query_functions(ap_qid_t qid, unsigned int *functions) |
257 | { | 259 | { |
260 | #ifdef CONFIG_64BIT | ||
258 | struct ap_queue_status status; | 261 | struct ap_queue_status status; |
259 | int i, support = 0; | 262 | int i; |
260 | status = __ap_4096_commands_available(qid, &support); | 263 | status = __ap_query_functions(qid, functions); |
261 | 264 | ||
262 | for (i = 0; i < AP_MAX_RESET; i++) { | 265 | for (i = 0; i < AP_MAX_RESET; i++) { |
266 | if (ap_queue_status_invalid_test(&status)) | ||
267 | return -ENODEV; | ||
268 | |||
263 | switch (status.response_code) { | 269 | switch (status.response_code) { |
264 | case AP_RESPONSE_NORMAL: | 270 | case AP_RESPONSE_NORMAL: |
265 | return support; | 271 | return 0; |
266 | case AP_RESPONSE_RESET_IN_PROGRESS: | 272 | case AP_RESPONSE_RESET_IN_PROGRESS: |
267 | case AP_RESPONSE_BUSY: | 273 | case AP_RESPONSE_BUSY: |
268 | break; | 274 | break; |
@@ -270,7 +276,7 @@ int ap_4096_commands_available(ap_qid_t qid) | |||
270 | case AP_RESPONSE_DECONFIGURED: | 276 | case AP_RESPONSE_DECONFIGURED: |
271 | case AP_RESPONSE_CHECKSTOPPED: | 277 | case AP_RESPONSE_CHECKSTOPPED: |
272 | case AP_RESPONSE_INVALID_ADDRESS: | 278 | case AP_RESPONSE_INVALID_ADDRESS: |
273 | return 0; | 279 | return -ENODEV; |
274 | case AP_RESPONSE_OTHERWISE_CHANGED: | 280 | case AP_RESPONSE_OTHERWISE_CHANGED: |
275 | break; | 281 | break; |
276 | default: | 282 | default: |
@@ -278,10 +284,31 @@ int ap_4096_commands_available(ap_qid_t qid) | |||
278 | } | 284 | } |
279 | if (i < AP_MAX_RESET - 1) { | 285 | if (i < AP_MAX_RESET - 1) { |
280 | udelay(5); | 286 | udelay(5); |
281 | status = __ap_4096_commands_available(qid, &support); | 287 | status = __ap_query_functions(qid, functions); |
282 | } | 288 | } |
283 | } | 289 | } |
284 | return support; | 290 | return -EBUSY; |
291 | #else | ||
292 | return -EINVAL; | ||
293 | #endif | ||
294 | } | ||
295 | |||
296 | /** | ||
297 | * ap_4096_commands_availablen(): Check for availability of 4096 bit RSA | ||
298 | * support. | ||
299 | * @qid: The AP queue number | ||
300 | * | ||
301 | * Returns 1 if 4096 bit RSA keys are support fo the AP, returns 0 if not. | ||
302 | */ | ||
303 | int ap_4096_commands_available(ap_qid_t qid) | ||
304 | { | ||
305 | unsigned int functions; | ||
306 | |||
307 | if (ap_query_functions(qid, &functions)) | ||
308 | return 0; | ||
309 | |||
310 | return test_ap_facility(functions, 1) && | ||
311 | test_ap_facility(functions, 2); | ||
285 | } | 312 | } |
286 | EXPORT_SYMBOL(ap_4096_commands_available); | 313 | EXPORT_SYMBOL(ap_4096_commands_available); |
287 | 314 | ||
@@ -1135,6 +1162,7 @@ static void ap_scan_bus(struct work_struct *unused) | |||
1135 | struct device *dev; | 1162 | struct device *dev; |
1136 | ap_qid_t qid; | 1163 | ap_qid_t qid; |
1137 | int queue_depth, device_type; | 1164 | int queue_depth, device_type; |
1165 | unsigned int device_functions; | ||
1138 | int rc, i; | 1166 | int rc, i; |
1139 | 1167 | ||
1140 | if (ap_select_domain() != 0) | 1168 | if (ap_select_domain() != 0) |
@@ -1183,14 +1211,30 @@ static void ap_scan_bus(struct work_struct *unused) | |||
1183 | INIT_LIST_HEAD(&ap_dev->list); | 1211 | INIT_LIST_HEAD(&ap_dev->list); |
1184 | setup_timer(&ap_dev->timeout, ap_request_timeout, | 1212 | setup_timer(&ap_dev->timeout, ap_request_timeout, |
1185 | (unsigned long) ap_dev); | 1213 | (unsigned long) ap_dev); |
1186 | if (device_type == 0) { | 1214 | switch (device_type) { |
1215 | case 0: | ||
1187 | if (ap_probe_device_type(ap_dev)) { | 1216 | if (ap_probe_device_type(ap_dev)) { |
1188 | kfree(ap_dev); | 1217 | kfree(ap_dev); |
1189 | continue; | 1218 | continue; |
1190 | } | 1219 | } |
1191 | } | 1220 | break; |
1192 | else | 1221 | case 10: |
1222 | if (ap_query_functions(qid, &device_functions)) { | ||
1223 | kfree(ap_dev); | ||
1224 | continue; | ||
1225 | } | ||
1226 | if (test_ap_facility(device_functions, 3)) | ||
1227 | ap_dev->device_type = AP_DEVICE_TYPE_CEX3C; | ||
1228 | else if (test_ap_facility(device_functions, 4)) | ||
1229 | ap_dev->device_type = AP_DEVICE_TYPE_CEX3A; | ||
1230 | else { | ||
1231 | kfree(ap_dev); | ||
1232 | continue; | ||
1233 | } | ||
1234 | break; | ||
1235 | default: | ||
1193 | ap_dev->device_type = device_type; | 1236 | ap_dev->device_type = device_type; |
1237 | } | ||
1194 | 1238 | ||
1195 | ap_dev->device.bus = &ap_bus_type; | 1239 | ap_dev->device.bus = &ap_bus_type; |
1196 | ap_dev->device.parent = ap_root_device; | 1240 | ap_dev->device.parent = ap_root_device; |
diff --git a/drivers/s390/crypto/ap_bus.h b/drivers/s390/crypto/ap_bus.h index 08b9738285b4..d960a6309eec 100644 --- a/drivers/s390/crypto/ap_bus.h +++ b/drivers/s390/crypto/ap_bus.h | |||
@@ -6,6 +6,7 @@ | |||
6 | * Martin Schwidefsky <schwidefsky@de.ibm.com> | 6 | * Martin Schwidefsky <schwidefsky@de.ibm.com> |
7 | * Ralph Wuerthner <rwuerthn@de.ibm.com> | 7 | * Ralph Wuerthner <rwuerthn@de.ibm.com> |
8 | * Felix Beck <felix.beck@de.ibm.com> | 8 | * Felix Beck <felix.beck@de.ibm.com> |
9 | * Holger Dengler <hd@linux.vnet.ibm.com> | ||
9 | * | 10 | * |
10 | * Adjunct processor bus header file. | 11 | * Adjunct processor bus header file. |
11 | * | 12 | * |
@@ -72,7 +73,26 @@ struct ap_queue_status { | |||
72 | unsigned int int_enabled : 1; | 73 | unsigned int int_enabled : 1; |
73 | unsigned int response_code : 8; | 74 | unsigned int response_code : 8; |
74 | unsigned int pad2 : 16; | 75 | unsigned int pad2 : 16; |
75 | }; | 76 | } __packed; |
77 | |||
78 | #define AP_QUEUE_STATUS_INVALID \ | ||
79 | { 1, 1, 1, 0xF, 1, 0xFF, 0xFFFF } | ||
80 | |||
81 | static inline | ||
82 | int ap_queue_status_invalid_test(struct ap_queue_status *status) | ||
83 | { | ||
84 | struct ap_queue_status invalid = AP_QUEUE_STATUS_INVALID; | ||
85 | return !(memcmp(status, &invalid, sizeof(struct ap_queue_status))); | ||
86 | } | ||
87 | |||
88 | #define MAX_AP_FACILITY 31 | ||
89 | |||
90 | static inline int test_ap_facility(unsigned int function, unsigned int nr) | ||
91 | { | ||
92 | if (nr > MAX_AP_FACILITY) | ||
93 | return 0; | ||
94 | return function & (unsigned int)(0x80000000 >> nr); | ||
95 | } | ||
76 | 96 | ||
77 | #define AP_RESPONSE_NORMAL 0x00 | 97 | #define AP_RESPONSE_NORMAL 0x00 |
78 | #define AP_RESPONSE_Q_NOT_AVAIL 0x01 | 98 | #define AP_RESPONSE_Q_NOT_AVAIL 0x01 |