diff options
author | Jan Glauber <jang@linux.vnet.ibm.com> | 2010-01-04 03:05:42 -0500 |
---|---|---|
committer | Martin Schwidefsky <sky@mschwide.boeblingen.de.ibm.com> | 2010-01-04 03:05:58 -0500 |
commit | 6486cda6c6b15368e2c925d89b4e9ed13e67b91b (patch) | |
tree | c4ca8e2b2b961adbc19733d1bcf3ebc6c5c6be32 /drivers/s390/cio/qdio_debug.c | |
parent | 45d28b097280a78893ce25a5d0db41e6a2717853 (diff) |
[S390] qdio: convert global statistics to per-device stats
Revamp the qdio performance statistics and move them from procfs to
debugfs using the seq_file interface. Since the statistics are not
intended for the general user the removal of /proc/qdio_perf should
not surprise anyone.
The per device statistics are disabled by default, writing 1 to
/<debugfs mountpoint>/qdio/<device bus ID>/statistics enables the
statistics for the given device.
Signed-off-by: Jan Glauber <jang@linux.vnet.ibm.com>
Signed-off-by: Martin Schwidefsky <schwidefsky@de.ibm.com>
Diffstat (limited to 'drivers/s390/cio/qdio_debug.c')
-rw-r--r-- | drivers/s390/cio/qdio_debug.c | 114 |
1 files changed, 106 insertions, 8 deletions
diff --git a/drivers/s390/cio/qdio_debug.c b/drivers/s390/cio/qdio_debug.c index 76769978285f..f49761ff9a00 100644 --- a/drivers/s390/cio/qdio_debug.c +++ b/drivers/s390/cio/qdio_debug.c | |||
@@ -55,13 +55,11 @@ static int qstat_show(struct seq_file *m, void *v) | |||
55 | if (!q) | 55 | if (!q) |
56 | return 0; | 56 | return 0; |
57 | 57 | ||
58 | seq_printf(m, "device state indicator: %d\n", *(u32 *)q->irq_ptr->dsci); | 58 | seq_printf(m, "DSCI: %d nr_used: %d\n", |
59 | seq_printf(m, "nr_used: %d\n", atomic_read(&q->nr_buf_used)); | 59 | *(u32 *)q->irq_ptr->dsci, atomic_read(&q->nr_buf_used)); |
60 | seq_printf(m, "ftc: %d\n", q->first_to_check); | 60 | seq_printf(m, "ftc: %d last_move: %d\n", q->first_to_check, q->last_move); |
61 | seq_printf(m, "last_move: %d\n", q->last_move); | 61 | seq_printf(m, "polling: %d ack start: %d ack count: %d\n", |
62 | seq_printf(m, "polling: %d\n", q->u.in.polling); | 62 | q->u.in.polling, q->u.in.ack_start, q->u.in.ack_count); |
63 | seq_printf(m, "ack start: %d\n", q->u.in.ack_start); | ||
64 | seq_printf(m, "ack count: %d\n", q->u.in.ack_count); | ||
65 | seq_printf(m, "slsb buffer states:\n"); | 63 | seq_printf(m, "slsb buffer states:\n"); |
66 | seq_printf(m, "|0 |8 |16 |24 |32 |40 |48 |56 63|\n"); | 64 | seq_printf(m, "|0 |8 |16 |24 |32 |40 |48 |56 63|\n"); |
67 | 65 | ||
@@ -110,7 +108,6 @@ static ssize_t qstat_seq_write(struct file *file, const char __user *buf, | |||
110 | 108 | ||
111 | if (!q) | 109 | if (!q) |
112 | return 0; | 110 | return 0; |
113 | |||
114 | if (q->is_input_q) | 111 | if (q->is_input_q) |
115 | xchg(q->irq_ptr->dsci, 1); | 112 | xchg(q->irq_ptr->dsci, 1); |
116 | local_bh_disable(); | 113 | local_bh_disable(); |
@@ -134,6 +131,98 @@ static const struct file_operations debugfs_fops = { | |||
134 | .release = single_release, | 131 | .release = single_release, |
135 | }; | 132 | }; |
136 | 133 | ||
134 | static char *qperf_names[] = { | ||
135 | "Assumed adapter interrupts", | ||
136 | "QDIO interrupts", | ||
137 | "Requested PCIs", | ||
138 | "Inbound tasklet runs", | ||
139 | "Inbound tasklet resched", | ||
140 | "Inbound tasklet resched2", | ||
141 | "Outbound tasklet runs", | ||
142 | "SIGA read", | ||
143 | "SIGA write", | ||
144 | "SIGA sync", | ||
145 | "Inbound calls", | ||
146 | "Inbound handler", | ||
147 | "Inbound stop_polling", | ||
148 | "Inbound queue full", | ||
149 | "Outbound calls", | ||
150 | "Outbound handler", | ||
151 | "Outbound fast_requeue", | ||
152 | "Outbound target_full", | ||
153 | "QEBSM eqbs", | ||
154 | "QEBSM eqbs partial", | ||
155 | "QEBSM sqbs", | ||
156 | "QEBSM sqbs partial" | ||
157 | }; | ||
158 | |||
159 | static int qperf_show(struct seq_file *m, void *v) | ||
160 | { | ||
161 | struct qdio_irq *irq_ptr = m->private; | ||
162 | unsigned int *stat; | ||
163 | int i; | ||
164 | |||
165 | if (!irq_ptr) | ||
166 | return 0; | ||
167 | if (!irq_ptr->perf_stat_enabled) { | ||
168 | seq_printf(m, "disabled\n"); | ||
169 | return 0; | ||
170 | } | ||
171 | stat = (unsigned int *)&irq_ptr->perf_stat; | ||
172 | |||
173 | for (i = 0; i < ARRAY_SIZE(qperf_names); i++) | ||
174 | seq_printf(m, "%26s:\t%u\n", | ||
175 | qperf_names[i], *(stat + i)); | ||
176 | return 0; | ||
177 | } | ||
178 | |||
179 | static ssize_t qperf_seq_write(struct file *file, const char __user *ubuf, | ||
180 | size_t count, loff_t *off) | ||
181 | { | ||
182 | struct seq_file *seq = file->private_data; | ||
183 | struct qdio_irq *irq_ptr = seq->private; | ||
184 | unsigned long val; | ||
185 | char buf[8]; | ||
186 | int ret; | ||
187 | |||
188 | if (!irq_ptr) | ||
189 | return 0; | ||
190 | if (count >= sizeof(buf)) | ||
191 | return -EINVAL; | ||
192 | if (copy_from_user(&buf, ubuf, count)) | ||
193 | return -EFAULT; | ||
194 | buf[count] = 0; | ||
195 | |||
196 | ret = strict_strtoul(buf, 10, &val); | ||
197 | if (ret < 0) | ||
198 | return ret; | ||
199 | |||
200 | switch (val) { | ||
201 | case 0: | ||
202 | irq_ptr->perf_stat_enabled = 0; | ||
203 | memset(&irq_ptr->perf_stat, 0, sizeof(irq_ptr->perf_stat)); | ||
204 | break; | ||
205 | case 1: | ||
206 | irq_ptr->perf_stat_enabled = 1; | ||
207 | break; | ||
208 | } | ||
209 | return count; | ||
210 | } | ||
211 | |||
212 | static int qperf_seq_open(struct inode *inode, struct file *filp) | ||
213 | { | ||
214 | return single_open(filp, qperf_show, | ||
215 | filp->f_path.dentry->d_inode->i_private); | ||
216 | } | ||
217 | |||
218 | static struct file_operations debugfs_perf_fops = { | ||
219 | .owner = THIS_MODULE, | ||
220 | .open = qperf_seq_open, | ||
221 | .read = seq_read, | ||
222 | .write = qperf_seq_write, | ||
223 | .llseek = seq_lseek, | ||
224 | .release = single_release, | ||
225 | }; | ||
137 | static void setup_debugfs_entry(struct qdio_q *q, struct ccw_device *cdev) | 226 | static void setup_debugfs_entry(struct qdio_q *q, struct ccw_device *cdev) |
138 | { | 227 | { |
139 | char name[QDIO_DEBUGFS_NAME_LEN]; | 228 | char name[QDIO_DEBUGFS_NAME_LEN]; |
@@ -156,6 +245,14 @@ void qdio_setup_debug_entries(struct qdio_irq *irq_ptr, struct ccw_device *cdev) | |||
156 | debugfs_root); | 245 | debugfs_root); |
157 | if (IS_ERR(irq_ptr->debugfs_dev)) | 246 | if (IS_ERR(irq_ptr->debugfs_dev)) |
158 | irq_ptr->debugfs_dev = NULL; | 247 | irq_ptr->debugfs_dev = NULL; |
248 | |||
249 | irq_ptr->debugfs_perf = debugfs_create_file("statistics", | ||
250 | S_IFREG | S_IRUGO | S_IWUSR, | ||
251 | irq_ptr->debugfs_dev, irq_ptr, | ||
252 | &debugfs_perf_fops); | ||
253 | if (IS_ERR(irq_ptr->debugfs_perf)) | ||
254 | irq_ptr->debugfs_perf = NULL; | ||
255 | |||
159 | for_each_input_queue(irq_ptr, q, i) | 256 | for_each_input_queue(irq_ptr, q, i) |
160 | setup_debugfs_entry(q, cdev); | 257 | setup_debugfs_entry(q, cdev); |
161 | for_each_output_queue(irq_ptr, q, i) | 258 | for_each_output_queue(irq_ptr, q, i) |
@@ -171,6 +268,7 @@ void qdio_shutdown_debug_entries(struct qdio_irq *irq_ptr, struct ccw_device *cd | |||
171 | debugfs_remove(q->debugfs_q); | 268 | debugfs_remove(q->debugfs_q); |
172 | for_each_output_queue(irq_ptr, q, i) | 269 | for_each_output_queue(irq_ptr, q, i) |
173 | debugfs_remove(q->debugfs_q); | 270 | debugfs_remove(q->debugfs_q); |
271 | debugfs_remove(irq_ptr->debugfs_perf); | ||
174 | debugfs_remove(irq_ptr->debugfs_dev); | 272 | debugfs_remove(irq_ptr->debugfs_dev); |
175 | } | 273 | } |
176 | 274 | ||