summaryrefslogtreecommitdiffstats
path: root/drivers/s390/cio/qdio_debug.c
diff options
context:
space:
mode:
authorJan Glauber <jang@linux.vnet.ibm.com>2010-01-04 09:05:42 +0100
committerMartin Schwidefsky <sky@mschwide.boeblingen.de.ibm.com>2010-01-04 09:05:58 +0100
commit6486cda6c6b15368e2c925d89b4e9ed13e67b91b (patch)
treec4ca8e2b2b961adbc19733d1bcf3ebc6c5c6be32 /drivers/s390/cio/qdio_debug.c
parent45d28b097280a78893ce25a5d0db41e6a2717853 (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.c114
1 files changed, 106 insertions, 8 deletions
diff --git a/drivers/s390/cio/qdio_debug.c b/drivers/s390/cio/qdio_debug.c
index 76769978285..f49761ff9a0 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)
if (!q)
return 0;
- seq_printf(m, "device state indicator: %d\n", *(u32 *)q->irq_ptr->dsci);
- seq_printf(m, "nr_used: %d\n", atomic_read(&q->nr_buf_used));
- seq_printf(m, "ftc: %d\n", q->first_to_check);
- seq_printf(m, "last_move: %d\n", q->last_move);
- seq_printf(m, "polling: %d\n", q->u.in.polling);
- seq_printf(m, "ack start: %d\n", q->u.in.ack_start);
- seq_printf(m, "ack count: %d\n", q->u.in.ack_count);
+ seq_printf(m, "DSCI: %d nr_used: %d\n",
+ *(u32 *)q->irq_ptr->dsci, atomic_read(&q->nr_buf_used));
+ seq_printf(m, "ftc: %d last_move: %d\n", q->first_to_check, q->last_move);
+ seq_printf(m, "polling: %d ack start: %d ack count: %d\n",
+ q->u.in.polling, q->u.in.ack_start, q->u.in.ack_count);
seq_printf(m, "slsb buffer states:\n");
seq_printf(m, "|0 |8 |16 |24 |32 |40 |48 |56 63|\n");
@@ -110,7 +108,6 @@ static ssize_t qstat_seq_write(struct file *file, const char __user *buf,
if (!q)
return 0;
-
if (q->is_input_q)
xchg(q->irq_ptr->dsci, 1);
local_bh_disable();
@@ -134,6 +131,98 @@ static const struct file_operations debugfs_fops = {
.release = single_release,
};
+static char *qperf_names[] = {
+ "Assumed adapter interrupts",
+ "QDIO interrupts",
+ "Requested PCIs",
+ "Inbound tasklet runs",
+ "Inbound tasklet resched",
+ "Inbound tasklet resched2",
+ "Outbound tasklet runs",
+ "SIGA read",
+ "SIGA write",
+ "SIGA sync",
+ "Inbound calls",
+ "Inbound handler",
+ "Inbound stop_polling",
+ "Inbound queue full",
+ "Outbound calls",
+ "Outbound handler",
+ "Outbound fast_requeue",
+ "Outbound target_full",
+ "QEBSM eqbs",
+ "QEBSM eqbs partial",
+ "QEBSM sqbs",
+ "QEBSM sqbs partial"
+};
+
+static int qperf_show(struct seq_file *m, void *v)
+{
+ struct qdio_irq *irq_ptr = m->private;
+ unsigned int *stat;
+ int i;
+
+ if (!irq_ptr)
+ return 0;
+ if (!irq_ptr->perf_stat_enabled) {
+ seq_printf(m, "disabled\n");
+ return 0;
+ }
+ stat = (unsigned int *)&irq_ptr->perf_stat;
+
+ for (i = 0; i < ARRAY_SIZE(qperf_names); i++)
+ seq_printf(m, "%26s:\t%u\n",
+ qperf_names[i], *(stat + i));
+ return 0;
+}
+
+static ssize_t qperf_seq_write(struct file *file, const char __user *ubuf,
+ size_t count, loff_t *off)
+{
+ struct seq_file *seq = file->private_data;
+ struct qdio_irq *irq_ptr = seq->private;
+ unsigned long val;
+ char buf[8];
+ int ret;
+
+ if (!irq_ptr)
+ return 0;
+ if (count >= sizeof(buf))
+ return -EINVAL;
+ if (copy_from_user(&buf, ubuf, count))
+ return -EFAULT;
+ buf[count] = 0;
+
+ ret = strict_strtoul(buf, 10, &val);
+ if (ret < 0)
+ return ret;
+
+ switch (val) {
+ case 0:
+ irq_ptr->perf_stat_enabled = 0;
+ memset(&irq_ptr->perf_stat, 0, sizeof(irq_ptr->perf_stat));
+ break;
+ case 1:
+ irq_ptr->perf_stat_enabled = 1;
+ break;
+ }
+ return count;
+}
+
+static int qperf_seq_open(struct inode *inode, struct file *filp)
+{
+ return single_open(filp, qperf_show,
+ filp->f_path.dentry->d_inode->i_private);
+}
+
+static struct file_operations debugfs_perf_fops = {
+ .owner = THIS_MODULE,
+ .open = qperf_seq_open,
+ .read = seq_read,
+ .write = qperf_seq_write,
+ .llseek = seq_lseek,
+ .release = single_release,
+};
static void setup_debugfs_entry(struct qdio_q *q, struct ccw_device *cdev)
{
char name[QDIO_DEBUGFS_NAME_LEN];
@@ -156,6 +245,14 @@ void qdio_setup_debug_entries(struct qdio_irq *irq_ptr, struct ccw_device *cdev)
debugfs_root);
if (IS_ERR(irq_ptr->debugfs_dev))
irq_ptr->debugfs_dev = NULL;
+
+ irq_ptr->debugfs_perf = debugfs_create_file("statistics",
+ S_IFREG | S_IRUGO | S_IWUSR,
+ irq_ptr->debugfs_dev, irq_ptr,
+ &debugfs_perf_fops);
+ if (IS_ERR(irq_ptr->debugfs_perf))
+ irq_ptr->debugfs_perf = NULL;
+
for_each_input_queue(irq_ptr, q, i)
setup_debugfs_entry(q, cdev);
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
debugfs_remove(q->debugfs_q);
for_each_output_queue(irq_ptr, q, i)
debugfs_remove(q->debugfs_q);
+ debugfs_remove(irq_ptr->debugfs_perf);
debugfs_remove(irq_ptr->debugfs_dev);
}