Skip to content

Commit

Permalink
[S390] qdio: convert global statistics to per-device stats
Browse files Browse the repository at this point in the history
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>
  • Loading branch information
Jan Glauber authored and Martin Schwidefsky committed Jan 4, 2010
1 parent 45d28b0 commit 6486cda
Show file tree
Hide file tree
Showing 7 changed files with 177 additions and 265 deletions.
2 changes: 1 addition & 1 deletion drivers/s390/cio/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -10,5 +10,5 @@ obj-y += ccw_device.o cmf.o
obj-$(CONFIG_CHSC_SCH) += chsc_sch.o
obj-$(CONFIG_CCWGROUP) += ccwgroup.o

qdio-objs := qdio_main.o qdio_thinint.o qdio_debug.o qdio_perf.o qdio_setup.o
qdio-objs := qdio_main.o qdio_thinint.o qdio_debug.o qdio_setup.o
obj-$(CONFIG_QDIO) += qdio.o
36 changes: 35 additions & 1 deletion drivers/s390/cio/qdio.h
Original file line number Diff line number Diff line change
Expand Up @@ -182,6 +182,34 @@ struct scssc_area {
u32:32;
} __attribute__ ((packed));

struct qdio_dev_perf_stat {
unsigned int adapter_int;
unsigned int qdio_int;
unsigned int pci_request_int;

unsigned int tasklet_inbound;
unsigned int tasklet_inbound_resched;
unsigned int tasklet_inbound_resched2;
unsigned int tasklet_outbound;

unsigned int siga_read;
unsigned int siga_write;
unsigned int siga_sync;

unsigned int inbound_call;
unsigned int inbound_handler;
unsigned int stop_polling;
unsigned int inbound_queue_full;
unsigned int outbound_call;
unsigned int outbound_handler;
unsigned int fast_requeue;
unsigned int target_full;
unsigned int eqbs;
unsigned int eqbs_partial;
unsigned int sqbs;
unsigned int sqbs_partial;
};

struct qdio_input_q {
/* input buffer acknowledgement flag */
int polling;
Expand Down Expand Up @@ -269,6 +297,7 @@ struct qdio_irq {
u32 *dsci; /* address of device state change indicator */
struct ccw_device *cdev;
struct dentry *debugfs_dev;
struct dentry *debugfs_perf;

unsigned long int_parm;
struct subchannel_id schid;
Expand All @@ -286,9 +315,10 @@ struct qdio_irq {
struct ciw aqueue;

struct qdio_ssqd_desc ssqd_desc;

void (*orig_handler) (struct ccw_device *, unsigned long, struct irb *);

struct qdio_dev_perf_stat perf_stat;
int perf_stat_enabled;
/*
* Warning: Leave these members together at the end so they won't be
* cleared in qdio_setup_irq.
Expand All @@ -311,6 +341,10 @@ struct qdio_irq {
(irq->qib.qfmt == QDIO_IQDIO_QFMT || \
css_general_characteristics.aif_osa)

#define qperf(qdev,attr) qdev->perf_stat.attr
#define qperf_inc(q,attr) if (q->irq_ptr->perf_stat_enabled) \
q->irq_ptr->perf_stat.attr++

/* the highest iqdio queue is used for multicast */
static inline int multicast_outbound(struct qdio_q *q)
{
Expand Down
114 changes: 106 additions & 8 deletions drivers/s390/cio/qdio_debug.c
Original file line number Diff line number Diff line change
Expand Up @@ -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");

Expand Down Expand Up @@ -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();
Expand All @@ -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];
Expand All @@ -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)
Expand All @@ -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);
}

Expand Down
Loading

0 comments on commit 6486cda

Please sign in to comment.