Jan Glauber | 779e6e1 | 2008-07-17 17:16:48 +0200 | [diff] [blame^] | 1 | /* |
| 2 | * drivers/s390/cio/qdio_perf.c |
| 3 | * |
| 4 | * Copyright IBM Corp. 2008 |
| 5 | * |
| 6 | * Author: Jan Glauber (jang@linux.vnet.ibm.com) |
| 7 | */ |
| 8 | #include <linux/kernel.h> |
| 9 | #include <linux/proc_fs.h> |
| 10 | #include <linux/seq_file.h> |
| 11 | #include <asm/ccwdev.h> |
| 12 | |
| 13 | #include "cio.h" |
| 14 | #include "css.h" |
| 15 | #include "device.h" |
| 16 | #include "ioasm.h" |
| 17 | #include "chsc.h" |
| 18 | #include "qdio_debug.h" |
| 19 | #include "qdio_perf.h" |
| 20 | |
| 21 | int qdio_performance_stats; |
| 22 | struct qdio_perf_stats perf_stats; |
| 23 | |
| 24 | #ifdef CONFIG_PROC_FS |
| 25 | static struct proc_dir_entry *qdio_perf_pde; |
| 26 | #endif |
| 27 | |
| 28 | inline void qdio_perf_stat_inc(atomic_long_t *count) |
| 29 | { |
| 30 | if (qdio_performance_stats) |
| 31 | atomic_long_inc(count); |
| 32 | } |
| 33 | |
| 34 | inline void qdio_perf_stat_dec(atomic_long_t *count) |
| 35 | { |
| 36 | if (qdio_performance_stats) |
| 37 | atomic_long_dec(count); |
| 38 | } |
| 39 | |
| 40 | /* |
| 41 | * procfs functions |
| 42 | */ |
| 43 | static int qdio_perf_proc_show(struct seq_file *m, void *v) |
| 44 | { |
| 45 | seq_printf(m, "Number of qdio interrupts\t\t\t: %li\n", |
| 46 | (long)atomic_long_read(&perf_stats.qdio_int)); |
| 47 | seq_printf(m, "Number of PCI interrupts\t\t\t: %li\n", |
| 48 | (long)atomic_long_read(&perf_stats.pci_int)); |
| 49 | seq_printf(m, "Number of adapter interrupts\t\t\t: %li\n", |
| 50 | (long)atomic_long_read(&perf_stats.thin_int)); |
| 51 | seq_printf(m, "\n"); |
| 52 | seq_printf(m, "Inbound tasklet runs\t\t\t\t: %li\n", |
| 53 | (long)atomic_long_read(&perf_stats.tasklet_inbound)); |
| 54 | seq_printf(m, "Outbound tasklet runs\t\t\t\t: %li\n", |
| 55 | (long)atomic_long_read(&perf_stats.tasklet_outbound)); |
| 56 | seq_printf(m, "Adapter interrupt tasklet runs/loops\t\t: %li/%li\n", |
| 57 | (long)atomic_long_read(&perf_stats.tasklet_thinint), |
| 58 | (long)atomic_long_read(&perf_stats.tasklet_thinint_loop)); |
| 59 | seq_printf(m, "Adapter interrupt inbound tasklet runs/loops\t: %li/%li\n", |
| 60 | (long)atomic_long_read(&perf_stats.thinint_inbound), |
| 61 | (long)atomic_long_read(&perf_stats.thinint_inbound_loop)); |
| 62 | seq_printf(m, "\n"); |
| 63 | seq_printf(m, "Number of SIGA In issued\t\t\t: %li\n", |
| 64 | (long)atomic_long_read(&perf_stats.siga_in)); |
| 65 | seq_printf(m, "Number of SIGA Out issued\t\t\t: %li\n", |
| 66 | (long)atomic_long_read(&perf_stats.siga_out)); |
| 67 | seq_printf(m, "Number of SIGA Sync issued\t\t\t: %li\n", |
| 68 | (long)atomic_long_read(&perf_stats.siga_sync)); |
| 69 | seq_printf(m, "\n"); |
| 70 | seq_printf(m, "Number of inbound transfers\t\t\t: %li\n", |
| 71 | (long)atomic_long_read(&perf_stats.inbound_handler)); |
| 72 | seq_printf(m, "Number of outbound transfers\t\t\t: %li\n", |
| 73 | (long)atomic_long_read(&perf_stats.outbound_handler)); |
| 74 | seq_printf(m, "\n"); |
| 75 | seq_printf(m, "Number of fast requeues (outg. SBAL w/o SIGA)\t: %li\n", |
| 76 | (long)atomic_long_read(&perf_stats.fast_requeue)); |
| 77 | seq_printf(m, "Number of outbound tasklet mod_timer calls\t: %li\n", |
| 78 | (long)atomic_long_read(&perf_stats.debug_tl_out_timer)); |
| 79 | seq_printf(m, "Number of stop polling calls\t\t\t: %li\n", |
| 80 | (long)atomic_long_read(&perf_stats.debug_stop_polling)); |
| 81 | seq_printf(m, "AI inbound tasklet loops after stop polling\t: %li\n", |
| 82 | (long)atomic_long_read(&perf_stats.thinint_inbound_loop2)); |
| 83 | seq_printf(m, "\n"); |
| 84 | return 0; |
| 85 | } |
| 86 | static int qdio_perf_seq_open(struct inode *inode, struct file *filp) |
| 87 | { |
| 88 | return single_open(filp, qdio_perf_proc_show, NULL); |
| 89 | } |
| 90 | |
| 91 | static struct file_operations qdio_perf_proc_fops = { |
| 92 | .owner = THIS_MODULE, |
| 93 | .open = qdio_perf_seq_open, |
| 94 | .read = seq_read, |
| 95 | .llseek = seq_lseek, |
| 96 | .release = single_release, |
| 97 | }; |
| 98 | |
| 99 | /* |
| 100 | * sysfs functions |
| 101 | */ |
| 102 | static ssize_t qdio_perf_stats_show(struct bus_type *bus, char *buf) |
| 103 | { |
| 104 | return sprintf(buf, "%i\n", qdio_performance_stats ? 1 : 0); |
| 105 | } |
| 106 | |
| 107 | static ssize_t qdio_perf_stats_store(struct bus_type *bus, |
| 108 | const char *buf, size_t count) |
| 109 | { |
| 110 | unsigned long i; |
| 111 | |
| 112 | if (strict_strtoul(buf, 16, &i) != 0) |
| 113 | return -EINVAL; |
| 114 | if ((i != 0) && (i != 1)) |
| 115 | return -EINVAL; |
| 116 | if (i == qdio_performance_stats) |
| 117 | return count; |
| 118 | |
| 119 | qdio_performance_stats = i; |
| 120 | /* reset performance statistics */ |
| 121 | if (i == 0) |
| 122 | memset(&perf_stats, 0, sizeof(struct qdio_perf_stats)); |
| 123 | return count; |
| 124 | } |
| 125 | |
| 126 | static BUS_ATTR(qdio_performance_stats, 0644, qdio_perf_stats_show, |
| 127 | qdio_perf_stats_store); |
| 128 | |
| 129 | int __init qdio_setup_perf_stats(void) |
| 130 | { |
| 131 | int rc; |
| 132 | |
| 133 | rc = bus_create_file(&ccw_bus_type, &bus_attr_qdio_performance_stats); |
| 134 | if (rc) |
| 135 | return rc; |
| 136 | |
| 137 | #ifdef CONFIG_PROC_FS |
| 138 | memset(&perf_stats, 0, sizeof(struct qdio_perf_stats)); |
| 139 | qdio_perf_pde = proc_create("qdio_perf", S_IFREG | S_IRUGO, |
| 140 | NULL, &qdio_perf_proc_fops); |
| 141 | #endif |
| 142 | return 0; |
| 143 | } |
| 144 | |
| 145 | void __exit qdio_remove_perf_stats(void) |
| 146 | { |
| 147 | #ifdef CONFIG_PROC_FS |
| 148 | remove_proc_entry("qdio_perf", NULL); |
| 149 | #endif |
| 150 | bus_remove_file(&ccw_bus_type, &bus_attr_qdio_performance_stats); |
| 151 | } |