Commit 6486cda6 authored by Jan Glauber's avatar Jan Glauber Committed by Martin Schwidefsky

[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: default avatarJan Glauber <jang@linux.vnet.ibm.com>
Signed-off-by: default avatarMartin Schwidefsky <schwidefsky@de.ibm.com>
parent 45d28b09
...@@ -10,5 +10,5 @@ obj-y += ccw_device.o cmf.o ...@@ -10,5 +10,5 @@ obj-y += ccw_device.o cmf.o
obj-$(CONFIG_CHSC_SCH) += chsc_sch.o obj-$(CONFIG_CHSC_SCH) += chsc_sch.o
obj-$(CONFIG_CCWGROUP) += ccwgroup.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 obj-$(CONFIG_QDIO) += qdio.o
...@@ -182,6 +182,34 @@ struct scssc_area { ...@@ -182,6 +182,34 @@ struct scssc_area {
u32:32; u32:32;
} __attribute__ ((packed)); } __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 { struct qdio_input_q {
/* input buffer acknowledgement flag */ /* input buffer acknowledgement flag */
int polling; int polling;
...@@ -269,6 +297,7 @@ struct qdio_irq { ...@@ -269,6 +297,7 @@ struct qdio_irq {
u32 *dsci; /* address of device state change indicator */ u32 *dsci; /* address of device state change indicator */
struct ccw_device *cdev; struct ccw_device *cdev;
struct dentry *debugfs_dev; struct dentry *debugfs_dev;
struct dentry *debugfs_perf;
unsigned long int_parm; unsigned long int_parm;
struct subchannel_id schid; struct subchannel_id schid;
...@@ -286,9 +315,10 @@ struct qdio_irq { ...@@ -286,9 +315,10 @@ struct qdio_irq {
struct ciw aqueue; struct ciw aqueue;
struct qdio_ssqd_desc ssqd_desc; struct qdio_ssqd_desc ssqd_desc;
void (*orig_handler) (struct ccw_device *, unsigned long, struct irb *); 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 * Warning: Leave these members together at the end so they won't be
* cleared in qdio_setup_irq. * cleared in qdio_setup_irq.
...@@ -311,6 +341,10 @@ struct qdio_irq { ...@@ -311,6 +341,10 @@ struct qdio_irq {
(irq->qib.qfmt == QDIO_IQDIO_QFMT || \ (irq->qib.qfmt == QDIO_IQDIO_QFMT || \
css_general_characteristics.aif_osa) 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 */ /* the highest iqdio queue is used for multicast */
static inline int multicast_outbound(struct qdio_q *q) static inline int multicast_outbound(struct qdio_q *q)
{ {
......
...@@ -55,13 +55,11 @@ static int qstat_show(struct seq_file *m, void *v) ...@@ -55,13 +55,11 @@ static int qstat_show(struct seq_file *m, void *v)
if (!q) if (!q)
return 0; return 0;
seq_printf(m, "device state indicator: %d\n", *(u32 *)q->irq_ptr->dsci); seq_printf(m, "DSCI: %d nr_used: %d\n",
seq_printf(m, "nr_used: %d\n", atomic_read(&q->nr_buf_used)); *(u32 *)q->irq_ptr->dsci, atomic_read(&q->nr_buf_used));
seq_printf(m, "ftc: %d\n", q->first_to_check); seq_printf(m, "ftc: %d last_move: %d\n", q->first_to_check, q->last_move);
seq_printf(m, "last_move: %d\n", q->last_move); seq_printf(m, "polling: %d ack start: %d ack count: %d\n",
seq_printf(m, "polling: %d\n", q->u.in.polling); q->u.in.polling, q->u.in.ack_start, q->u.in.ack_count);
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, "slsb buffer states:\n"); seq_printf(m, "slsb buffer states:\n");
seq_printf(m, "|0 |8 |16 |24 |32 |40 |48 |56 63|\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, ...@@ -110,7 +108,6 @@ static ssize_t qstat_seq_write(struct file *file, const char __user *buf,
if (!q) if (!q)
return 0; return 0;
if (q->is_input_q) if (q->is_input_q)
xchg(q->irq_ptr->dsci, 1); xchg(q->irq_ptr->dsci, 1);
local_bh_disable(); local_bh_disable();
...@@ -134,6 +131,98 @@ static const struct file_operations debugfs_fops = { ...@@ -134,6 +131,98 @@ static const struct file_operations debugfs_fops = {
.release = single_release, .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) static void setup_debugfs_entry(struct qdio_q *q, struct ccw_device *cdev)
{ {
char name[QDIO_DEBUGFS_NAME_LEN]; 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,6 +245,14 @@ void qdio_setup_debug_entries(struct qdio_irq *irq_ptr, struct ccw_device *cdev)
debugfs_root); debugfs_root);
if (IS_ERR(irq_ptr->debugfs_dev)) if (IS_ERR(irq_ptr->debugfs_dev))
irq_ptr->debugfs_dev = NULL; 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) for_each_input_queue(irq_ptr, q, i)
setup_debugfs_entry(q, cdev); setup_debugfs_entry(q, cdev);
for_each_output_queue(irq_ptr, q, i) 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,6 +268,7 @@ void qdio_shutdown_debug_entries(struct qdio_irq *irq_ptr, struct ccw_device *cd
debugfs_remove(q->debugfs_q); debugfs_remove(q->debugfs_q);
for_each_output_queue(irq_ptr, q, i) for_each_output_queue(irq_ptr, q, i)
debugfs_remove(q->debugfs_q); debugfs_remove(q->debugfs_q);
debugfs_remove(irq_ptr->debugfs_perf);
debugfs_remove(irq_ptr->debugfs_dev); debugfs_remove(irq_ptr->debugfs_dev);
} }
......
...@@ -22,7 +22,6 @@ ...@@ -22,7 +22,6 @@
#include "device.h" #include "device.h"
#include "qdio.h" #include "qdio.h"
#include "qdio_debug.h" #include "qdio_debug.h"
#include "qdio_perf.h"
MODULE_AUTHOR("Utz Bacher <utz.bacher@de.ibm.com>,"\ MODULE_AUTHOR("Utz Bacher <utz.bacher@de.ibm.com>,"\
"Jan Glauber <jang@linux.vnet.ibm.com>"); "Jan Glauber <jang@linux.vnet.ibm.com>");
...@@ -126,7 +125,7 @@ static int qdio_do_eqbs(struct qdio_q *q, unsigned char *state, ...@@ -126,7 +125,7 @@ static int qdio_do_eqbs(struct qdio_q *q, unsigned char *state,
int rc; int rc;
BUG_ON(!q->irq_ptr->sch_token); BUG_ON(!q->irq_ptr->sch_token);
qdio_perf_stat_inc(&perf_stats.debug_eqbs_all); qperf_inc(q, eqbs);
if (!q->is_input_q) if (!q->is_input_q)
nr += q->irq_ptr->nr_input_qs; nr += q->irq_ptr->nr_input_qs;
...@@ -139,7 +138,7 @@ again: ...@@ -139,7 +138,7 @@ again:
* buffers later. * buffers later.
*/ */
if ((ccq == 96) && (count != tmp_count)) { if ((ccq == 96) && (count != tmp_count)) {
qdio_perf_stat_inc(&perf_stats.debug_eqbs_incomplete); qperf_inc(q, eqbs_partial);
return (count - tmp_count); return (count - tmp_count);
} }
...@@ -182,7 +181,7 @@ static int qdio_do_sqbs(struct qdio_q *q, unsigned char state, int start, ...@@ -182,7 +181,7 @@ static int qdio_do_sqbs(struct qdio_q *q, unsigned char state, int start,
return 0; return 0;
BUG_ON(!q->irq_ptr->sch_token); BUG_ON(!q->irq_ptr->sch_token);
qdio_perf_stat_inc(&perf_stats.debug_sqbs_all); qperf_inc(q, sqbs);
if (!q->is_input_q) if (!q->is_input_q)
nr += q->irq_ptr->nr_input_qs; nr += q->irq_ptr->nr_input_qs;
...@@ -191,7 +190,7 @@ again: ...@@ -191,7 +190,7 @@ again:
rc = qdio_check_ccq(q, ccq); rc = qdio_check_ccq(q, ccq);
if (rc == 1) { if (rc == 1) {
DBF_DEV_EVENT(DBF_INFO, q->irq_ptr, "SQBS again:%2d", ccq); DBF_DEV_EVENT(DBF_INFO, q->irq_ptr, "SQBS again:%2d", ccq);
qdio_perf_stat_inc(&perf_stats.debug_sqbs_incomplete); qperf_inc(q, sqbs_partial);
goto again; goto again;
} }
if (rc < 0) { if (rc < 0) {
...@@ -285,7 +284,7 @@ static inline int qdio_siga_sync(struct qdio_q *q, unsigned int output, ...@@ -285,7 +284,7 @@ static inline int qdio_siga_sync(struct qdio_q *q, unsigned int output,
return 0; return 0;
DBF_DEV_EVENT(DBF_INFO, q->irq_ptr, "siga-s:%1d", q->nr); DBF_DEV_EVENT(DBF_INFO, q->irq_ptr, "siga-s:%1d", q->nr);
qdio_perf_stat_inc(&perf_stats.siga_sync); qperf_inc(q, siga_sync);
cc = do_siga_sync(q->irq_ptr->schid, output, input); cc = do_siga_sync(q->irq_ptr->schid, output, input);
if (cc) if (cc)
...@@ -350,7 +349,7 @@ static inline int qdio_siga_input(struct qdio_q *q) ...@@ -350,7 +349,7 @@ static inline int qdio_siga_input(struct qdio_q *q)
int cc; int cc;
DBF_DEV_EVENT(DBF_INFO, q->irq_ptr, "siga-r:%1d", q->nr); DBF_DEV_EVENT(DBF_INFO, q->irq_ptr, "siga-r:%1d", q->nr);
qdio_perf_stat_inc(&perf_stats.siga_in); qperf_inc(q, siga_read);
cc = do_siga_input(q->irq_ptr->schid, q->mask); cc = do_siga_input(q->irq_ptr->schid, q->mask);
if (cc) if (cc)
...@@ -382,7 +381,7 @@ static inline void qdio_stop_polling(struct qdio_q *q) ...@@ -382,7 +381,7 @@ static inline void qdio_stop_polling(struct qdio_q *q)
return; return;
q->u.in.polling = 0; q->u.in.polling = 0;
qdio_perf_stat_inc(&perf_stats.debug_stop_polling); qperf_inc(q, stop_polling);
/* show the card that we are not polling anymore */ /* show the card that we are not polling anymore */
if (is_qebsm(q)) { if (is_qebsm(q)) {
...@@ -400,7 +399,7 @@ static void announce_buffer_error(struct qdio_q *q, int count) ...@@ -400,7 +399,7 @@ static void announce_buffer_error(struct qdio_q *q, int count)
/* special handling for no target buffer empty */ /* special handling for no target buffer empty */
if ((!q->is_input_q && if ((!q->is_input_q &&
(q->sbal[q->first_to_check]->element[15].flags & 0xff) == 0x10)) { (q->sbal[q->first_to_check]->element[15].flags & 0xff) == 0x10)) {
qdio_perf_stat_inc(&perf_stats.outbound_target_full); qperf_inc(q, target_full);
DBF_DEV_EVENT(DBF_INFO, q->irq_ptr, "OUTFULL FTC:%02x", DBF_DEV_EVENT(DBF_INFO, q->irq_ptr, "OUTFULL FTC:%02x",
q->first_to_check); q->first_to_check);
return; return;
...@@ -487,7 +486,7 @@ static int get_inbound_buffer_frontier(struct qdio_q *q) ...@@ -487,7 +486,7 @@ static int get_inbound_buffer_frontier(struct qdio_q *q)
inbound_primed(q, count); inbound_primed(q, count);
q->first_to_check = add_buf(q->first_to_check, count); q->first_to_check = add_buf(q->first_to_check, count);
if (atomic_sub(count, &q->nr_buf_used) == 0) if (atomic_sub(count, &q->nr_buf_used) == 0)
qdio_perf_stat_inc(&perf_stats.inbound_queue_full); qperf_inc(q, inbound_queue_full);
break; break;
case SLSB_P_INPUT_ERROR: case SLSB_P_INPUT_ERROR:
announce_buffer_error(q, count); announce_buffer_error(q, count);
...@@ -567,9 +566,10 @@ static void qdio_kick_handler(struct qdio_q *q) ...@@ -567,9 +566,10 @@ static void qdio_kick_handler(struct qdio_q *q)
count = sub_buf(end, start); count = sub_buf(end, start);
if (q->is_input_q) { if (q->is_input_q) {
qdio_perf_stat_inc(&perf_stats.inbound_handler); qperf_inc(q, inbound_handler);
DBF_DEV_EVENT(DBF_INFO, q->irq_ptr, "kih s:%02x c:%02x", start, count); DBF_DEV_EVENT(DBF_INFO, q->irq_ptr, "kih s:%02x c:%02x", start, count);
} else } else
qperf_inc(q, outbound_handler);
DBF_DEV_EVENT(DBF_INFO, q->irq_ptr, "koh: s:%02x c:%02x", DBF_DEV_EVENT(DBF_INFO, q->irq_ptr, "koh: s:%02x c:%02x",
start, count); start, count);
...@@ -583,24 +583,28 @@ static void qdio_kick_handler(struct qdio_q *q) ...@@ -583,24 +583,28 @@ static void qdio_kick_handler(struct qdio_q *q)
static void __qdio_inbound_processing(struct qdio_q *q) static void __qdio_inbound_processing(struct qdio_q *q)
{ {
qdio_perf_stat_inc(&perf_stats.tasklet_inbound); qperf_inc(q, tasklet_inbound);
again: again:
if (!qdio_inbound_q_moved(q)) if (!qdio_inbound_q_moved(q))
return; return;
qdio_kick_handler(q); qdio_kick_handler(q);
if (!qdio_inbound_q_done(q)) if (!qdio_inbound_q_done(q)) {
/* means poll time is not yet over */ /* means poll time is not yet over */
qperf_inc(q, tasklet_inbound_resched);
goto again; goto again;
}
qdio_stop_polling(q); qdio_stop_polling(q);
/* /*
* We need to check again to not lose initiative after * We need to check again to not lose initiative after
* resetting the ACK state. * resetting the ACK state.
*/ */
if (!qdio_inbound_q_done(q)) if (!qdio_inbound_q_done(q)) {
qperf_inc(q, tasklet_inbound_resched2);
goto again; goto again;
}
} }
void qdio_inbound_processing(unsigned long data) void qdio_inbound_processing(unsigned long data)
...@@ -688,7 +692,7 @@ static int qdio_kick_outbound_q(struct qdio_q *q) ...@@ -688,7 +692,7 @@ static int qdio_kick_outbound_q(struct qdio_q *q)
return 0; return 0;
DBF_DEV_EVENT(DBF_INFO, q->irq_ptr, "siga-w:%1d", q->nr); DBF_DEV_EVENT(DBF_INFO, q->irq_ptr, "siga-w:%1d", q->nr);
qdio_perf_stat_inc(&perf_stats.siga_out); qperf_inc(q, siga_write);
cc = qdio_siga_output(q, &busy_bit); cc = qdio_siga_output(q, &busy_bit);
switch (cc) { switch (cc) {
...@@ -711,7 +715,7 @@ static int qdio_kick_outbound_q(struct qdio_q *q) ...@@ -711,7 +715,7 @@ static int qdio_kick_outbound_q(struct qdio_q *q)
static void __qdio_outbound_processing(struct qdio_q *q) static void __qdio_outbound_processing(struct qdio_q *q)
{ {
qdio_perf_stat_inc(&perf_stats.tasklet_outbound); qperf_inc(q, tasklet_outbound);
BUG_ON(atomic_read(&q->nr_buf_used) < 0); BUG_ON(atomic_read(&q->nr_buf_used) < 0);
if (qdio_outbound_q_moved(q)) if (qdio_outbound_q_moved(q))
...@@ -739,12 +743,9 @@ static void __qdio_outbound_processing(struct qdio_q *q) ...@@ -739,12 +743,9 @@ static void __qdio_outbound_processing(struct qdio_q *q)
*/ */
if (qdio_outbound_q_done(q)) if (qdio_outbound_q_done(q))
del_timer(&q->u.out.timer); del_timer(&q->u.out.timer);
else { else
if (!timer_pending(&q->u.out.timer)) { if (!timer_pending(&q->u.out.timer))
mod_timer(&q->u.out.timer, jiffies + 10 * HZ); mod_timer(&q->u.out.timer, jiffies + 10 * HZ);
qdio_perf_stat_inc(&perf_stats.debug_tl_out_timer);
}
}
return; return;
sched: sched:
...@@ -784,7 +785,7 @@ static inline void qdio_check_outbound_after_thinint(struct qdio_q *q) ...@@ -784,7 +785,7 @@ static inline void qdio_check_outbound_after_thinint(struct qdio_q *q)
static void __tiqdio_inbound_processing(struct qdio_q *q) static void __tiqdio_inbound_processing(struct qdio_q *q)
{ {
qdio_perf_stat_inc(&perf_stats.thinint_inbound); qperf_inc(q, tasklet_inbound);
qdio_sync_after_thinint(q); qdio_sync_after_thinint(q);
/* /*
...@@ -799,7 +800,7 @@ static void __tiqdio_inbound_processing(struct qdio_q *q) ...@@ -799,7 +800,7 @@ static void __tiqdio_inbound_processing(struct qdio_q *q)
qdio_kick_handler(q); qdio_kick_handler(q);
if (!qdio_inbound_q_done(q)) { if (!qdio_inbound_q_done(q)) {
qdio_perf_stat_inc(&perf_stats.thinint_inbound_loop); qperf_inc(q, tasklet_inbound_resched);
if (likely(q->irq_ptr->state != QDIO_IRQ_STATE_STOPPED)) { if (likely(q->irq_ptr->state != QDIO_IRQ_STATE_STOPPED)) {
tasklet_schedule(&q->tasklet); tasklet_schedule(&q->tasklet);
return; return;
...@@ -812,7 +813,7 @@ static void __tiqdio_inbound_processing(struct qdio_q *q) ...@@ -812,7 +813,7 @@ static void __tiqdio_inbound_processing(struct qdio_q *q)
* resetting the ACK state. * resetting the ACK state.
*/ */
if (!qdio_inbound_q_done(q)) { if (!qdio_inbound_q_done(q)) {
qdio_perf_stat_inc(&perf_stats.thinint_inbound_loop2); qperf_inc(q, tasklet_inbound_resched2);
if (likely(q->irq_ptr->state != QDIO_IRQ_STATE_STOPPED)) if (likely(q->irq_ptr->state != QDIO_IRQ_STATE_STOPPED))
tasklet_schedule(&q->tasklet); tasklet_schedule(&q->tasklet);
} }
...@@ -851,8 +852,6 @@ static void qdio_int_handler_pci(struct qdio_irq *irq_ptr) ...@@ -851,8 +852,6 @@ static void qdio_int_handler_pci(struct qdio_irq *irq_ptr)
if (unlikely(irq_ptr->state == QDIO_IRQ_STATE_STOPPED)) if (unlikely(irq_ptr->state == QDIO_IRQ_STATE_STOPPED))
return; return;
qdio_perf_stat_inc(&perf_stats.pci_int);
for_each_input_queue(irq_ptr, q, i) for_each_input_queue(irq_ptr, q, i)
tasklet_schedule(&q->tasklet); tasklet_schedule(&q->tasklet);
...@@ -923,8 +922,6 @@ void qdio_int_handler(struct ccw_device *cdev, unsigned long intparm, ...@@ -923,8 +922,6 @@ void qdio_int_handler(struct ccw_device *cdev, unsigned long intparm,
struct qdio_irq *irq_ptr = cdev->private->qdio_data; struct qdio_irq *irq_ptr = cdev->private->qdio_data;
int cstat, dstat; int cstat, dstat;
qdio_perf_stat_inc(&perf_stats.qdio_int);
if (!intparm || !irq_ptr) { if (!intparm || !irq_ptr) {
DBF_ERROR("qint:%4x", cdev->private->schid.sch_no); DBF_ERROR("qint:%4x", cdev->private->schid.sch_no);
return; return;
...@@ -1383,6 +1380,8 @@ static int handle_inbound(struct qdio_q *q, unsigned int callflags, ...@@ -1383,6 +1380,8 @@ static int handle_inbound(struct qdio_q *q, unsigned int callflags,
{ {
int used, diff; int used, diff;
qperf_inc(q, inbound_call);
if (!q->u.in.polling) if (!q->u.in.polling)
goto set; goto set;
...@@ -1438,14 +1437,16 @@ static int handle_outbound(struct qdio_q *q, unsigned int callflags, ...@@ -1438,14 +1437,16 @@ static int handle_outbound(struct qdio_q *q, unsigned int callflags,
unsigned char state; unsigned char state;
int used, rc = 0; int used, rc = 0;
qdio_perf_stat_inc(&perf_stats.outbound_handler); qperf_inc(q, outbound_call);
count = set_buf_states(q, bufnr, SLSB_CU_OUTPUT_PRIMED, count); count = set_buf_states(q, bufnr, SLSB_CU_OUTPUT_PRIMED, count);
used = atomic_add_return(count, &q->nr_buf_used); used = atomic_add_return(count, &q->nr_buf_used);
BUG_ON(used > QDIO_MAX_BUFFERS_PER_Q); BUG_ON(used > QDIO_MAX_BUFFERS_PER_Q);
if (callflags & QDIO_FLAG_PCI_OUT) if (callflags & QDIO_FLAG_PCI_OUT) {
q->u.out.pci_out_enabled = 1; q->u.out.pci_out_enabled = 1;
qperf_inc(q, pci_request_int);
}
else else
q->u.out.pci_out_enabled = 0; q->u.out.pci_out_enabled = 0;
...@@ -1484,7 +1485,7 @@ static int handle_outbound(struct qdio_q *q, unsigned int callflags, ...@@ -1484,7 +1485,7 @@ static int handle_outbound(struct qdio_q *q, unsigned int callflags,
if (state != SLSB_CU_OUTPUT_PRIMED) if (state != SLSB_CU_OUTPUT_PRIMED)
rc = qdio_kick_outbound_q(q); rc = qdio_kick_outbound_q(q);
else else
qdio_perf_stat_inc(&perf_stats.fast_requeue); qperf_inc(q, fast_requeue);
out: out:
tasklet_schedule(&q->tasklet); tasklet_schedule(&q->tasklet);
...@@ -1540,16 +1541,11 @@ static int __init init_QDIO(void) ...@@ -1540,16 +1541,11 @@ static int __init init_QDIO(void)
rc = qdio_debug_init(); rc = qdio_debug_init();
if (rc) if (rc)
goto out_ti; goto out_ti;
rc = qdio_setup_perf_stats();
if (rc)
goto out_debug;
rc = tiqdio_register_thinints(); rc = tiqdio_register_thinints();
if (rc) if (rc)
goto out_perf; goto out_debug;
return 0; return 0;
out_perf:
qdio_remove_perf_stats();
out_debug: out_debug:
qdio_debug_exit(); qdio_debug_exit();
out_ti: out_ti:
...@@ -1563,7 +1559,6 @@ static void __exit exit_QDIO(void) ...@@ -1563,7 +1559,6 @@ static void __exit exit_QDIO(void)
{ {
tiqdio_unregister_thinints(); tiqdio_unregister_thinints();
tiqdio_free_memory(); tiqdio_free_memory();
qdio_remove_perf_stats();
qdio_debug_exit(); qdio_debug_exit();
qdio_setup_exit(); qdio_setup_exit();
} }
......
/*
* drivers/s390/cio/qdio_perf.c
*
* Copyright IBM Corp. 2008
*
* Author: Jan Glauber (jang@linux.vnet.ibm.com)
*/
#include <linux/kernel.h>
#include <linux/proc_fs.h>
#include <linux/seq_file.h>
#include <asm/ccwdev.h>
#include "cio.h"
#include "css.h"
#include "device.h"
#include "ioasm.h"
#include "chsc.h"
#include "qdio_debug.h"
#include "qdio_perf.h"
int qdio_performance_stats;
struct qdio_perf_stats perf_stats;
#ifdef CONFIG_PROC_FS
static struct proc_dir_entry *qdio_perf_pde;
#endif
/*
* procfs functions
*/
static int qdio_perf_proc_show(struct seq_file *m, void *v)
{
seq_printf(m, "Number of qdio interrupts\t\t\t: %li\n",
(long)atomic_long_read(&perf_stats.qdio_int));
seq_printf(m, "Number of PCI interrupts\t\t\t: %li\n",
(long)atomic_long_read(&perf_stats.pci_int));
seq_printf(m, "Number of adapter interrupts\t\t\t: %li\n",
(long)atomic_long_read(&perf_stats.thin_int));
seq_printf(m, "\n");
seq_printf(m, "Inbound tasklet runs\t\t\t\t: %li\n",
(long)atomic_long_read(&perf_stats.tasklet_inbound));
seq_printf(m, "Outbound tasklet runs\t\t\t\t: %li\n",
(long)atomic_long_read(&perf_stats.tasklet_outbound));
seq_printf(m, "Adapter interrupt tasklet runs/loops\t\t: %li/%li\n",
(long)atomic_long_read(&perf_stats.tasklet_thinint),
(long)atomic_long_read(&perf_stats.tasklet_thinint_loop));
seq_printf(m, "Adapter interrupt inbound tasklet runs/loops\t: %li/%li\n",
(long)atomic_long_read(&perf_stats.thinint_inbound),
(long)atomic_long_read(&perf_stats.thinint_inbound_loop));
seq_printf(m, "\n");
seq_printf(m, "Number of SIGA In issued\t\t\t: %li\n",
(long)atomic_long_read(&perf_stats.siga_in));
seq_printf(m, "Number of SIGA Out issued\t\t\t: %li\n",
(long)atomic_long_read(&perf_stats.siga_out));
seq_printf(m, "Number of SIGA Sync issued\t\t\t: %li\n",
(long)atomic_long_read(&perf_stats.siga_sync));
seq_printf(m, "\n");
seq_printf(m, "Number of inbound transfers\t\t\t: %li\n",
(long)atomic_long_read(&perf_stats.inbound_handler));
seq_printf(m, "Number of outbound transfers\t\t\t: %li\n",
(long)atomic_long_read(&perf_stats.outbound_handler));
seq_printf(m, "\n");
seq_printf(m, "Number of fast requeues (outg. SBAL w/o SIGA)\t: %li\n",
(long)atomic_long_read(&perf_stats.fast_requeue));
seq_printf(m, "Number of outbound target full condition\t: %li\n",
(long)atomic_long_read(&perf_stats.outbound_target_full));
seq_printf(m, "Number of inbound queue full condition\t\t: %li\n",
(long)atomic_long_read(&perf_stats.inbound_queue_full));
seq_printf(m, "Number of outbound tasklet mod_timer calls\t: %li\n",
(long)atomic_long_read(&perf_stats.debug_tl_out_timer));
seq_printf(m, "Number of stop polling calls\t\t\t: %li\n",
(long)atomic_long_read(&perf_stats.debug_stop_polling));
seq_printf(m, "AI inbound tasklet loops after stop polling\t: %li\n",
(long)atomic_long_read(&perf_stats.thinint_inbound_loop2));
seq_printf(m, "QEBSM EQBS total/incomplete\t\t\t: %li/%li\n",
(long)atomic_long_read(&perf_stats.debug_eqbs_all),
(long)atomic_long_read(&perf_stats.debug_eqbs_incomplete));
seq_printf(m, "QEBSM SQBS total/incomplete\t\t\t: %li/%li\n",
(long)atomic_long_read(&perf_stats.debug_sqbs_all),
(long)atomic_long_read(&perf_stats.debug_sqbs_incomplete));
seq_printf(m, "\n");
return 0;
}
static int qdio_perf_seq_open(struct inode *inode, struct file *filp)
{
return single_open(filp, qdio_perf_proc_show, NULL);
}
static const struct file_operations qdio_perf_proc_fops = {
.owner = THIS_MODULE,
.open = qdio_perf_seq_open,
.read = seq_read,
.llseek = seq_lseek,
.release = single_release,
};
/*
* sysfs functions
*/
static ssize_t qdio_perf_stats_show(struct bus_type *bus, char *buf)
{
return sprintf(buf, "%i\n", qdio_performance_stats ? 1 : 0);
}
static ssize_t qdio_perf_stats_store(struct bus_type *bus,
const char *buf, size_t count)
{
unsigned long i;
if (strict_strtoul(buf, 16, &i) != 0)
return -EINVAL;
if ((i != 0) && (i != 1))
return -EINVAL;
if (i == qdio_performance_stats)
return count;
qdio_performance_stats = i;
/* reset performance statistics */
if (i == 0)
memset(&perf_stats, 0, sizeof(struct qdio_perf_stats));
return count;
}
static BUS_ATTR(qdio_performance_stats, 0644, qdio_perf_stats_show,
qdio_perf_stats_store);
int __init qdio_setup_perf_stats(void)
{
int rc;
rc = bus_create_file(&ccw_bus_type, &bus_attr_qdio_performance_stats);
if (rc)
return rc;
#ifdef CONFIG_PROC_FS
memset(&perf_stats, 0, sizeof(struct qdio_perf_stats));
qdio_perf_pde = proc_create("qdio_perf", S_IFREG | S_IRUGO,
NULL, &qdio_perf_proc_fops);
#endif
return 0;
}
void qdio_remove_perf_stats(void)
{
#ifdef CONFIG_PROC_FS
remove_proc_entry("qdio_perf", NULL);
#endif
bus_remove_file(&ccw_bus_type, &bus_attr_qdio_performance_stats);
}
/*
* drivers/s390/cio/qdio_perf.h
*
* Copyright IBM Corp. 2008
*
* Author: Jan Glauber (jang@linux.vnet.ibm.com)
*/
#ifndef QDIO_PERF_H
#define QDIO_PERF_H
#include <linux/types.h>
#include <asm/atomic.h>
struct qdio_perf_stats {
/* interrupt handler calls */
atomic_long_t qdio_int;
atomic_long_t pci_int;
atomic_long_t thin_int;
/* tasklet runs */
atomic_long_t tasklet_inbound;
atomic_long_t tasklet_outbound;
atomic_long_t tasklet_thinint;
atomic_long_t tasklet_thinint_loop;
atomic_long_t thinint_inbound;
atomic_long_t thinint_inbound_loop;
atomic_long_t thinint_inbound_loop2;
/* signal adapter calls */
atomic_long_t siga_out;
atomic_long_t siga_in;
atomic_long_t siga_sync;
/* misc */
atomic_long_t inbound_handler;
atomic_long_t outbound_handler;
atomic_long_t fast_requeue;
atomic_long_t outbound_target_full;
atomic_long_t inbound_queue_full;
/* for debugging */
atomic_long_t debug_tl_out_timer;
atomic_long_t debug_stop_polling;
atomic_long_t debug_eqbs_all;
atomic_long_t debug_eqbs_incomplete;
atomic_long_t debug_sqbs_all;
atomic_long_t debug_sqbs_incomplete;
};
extern struct qdio_perf_stats perf_stats;
extern int qdio_performance_stats;
static inline void qdio_perf_stat_inc(atomic_long_t *count)
{
if (qdio_performance_stats)
atomic_long_inc(count);
}
int qdio_setup_perf_stats(void);
void qdio_remove_perf_stats(void);
#endif
/* /*
* linux/drivers/s390/cio/thinint_qdio.c * linux/drivers/s390/cio/thinint_qdio.c
* *
* thin interrupt support for qdio * Copyright 2000,2009 IBM Corp.
*
* Copyright 2000-2008 IBM Corp.
* Author(s): Utz Bacher <utz.bacher@de.ibm.com> * Author(s): Utz Bacher <utz.bacher@de.ibm.com>
* Cornelia Huck <cornelia.huck@de.ibm.com> * Cornelia Huck <cornelia.huck@de.ibm.com>
* Jan Glauber <jang@linux.vnet.ibm.com> * Jan Glauber <jang@linux.vnet.ibm.com>
...@@ -19,7 +17,6 @@ ...@@ -19,7 +17,6 @@
#include "ioasm.h" #include "ioasm.h"
#include "qdio.h" #include "qdio.h"
#include "qdio_debug.h" #include "qdio_debug.h"
#include "qdio_perf.h"
/* /*
* Restriction: only 63 iqdio subchannels would have its own indicator, * Restriction: only 63 iqdio subchannels would have its own indicator,
...@@ -132,8 +129,6 @@ static void tiqdio_thinint_handler(void *ind, void *drv_data) ...@@ -132,8 +129,6 @@ static void tiqdio_thinint_handler(void *ind, void *drv_data)
{ {
struct qdio_q *q; struct qdio_q *q;
qdio_perf_stat_inc(&perf_stats.thin_int);
/* /*
* SVS only when needed: issue SVS to benefit from iqdio interrupt * SVS only when needed: issue SVS to benefit from iqdio interrupt
* avoidance (SVS clears adapter interrupt suppression overwrite) * avoidance (SVS clears adapter interrupt suppression overwrite)
...@@ -154,6 +149,7 @@ static void tiqdio_thinint_handler(void *ind, void *drv_data) ...@@ -154,6 +149,7 @@ static void tiqdio_thinint_handler(void *ind, void *drv_data)
list_for_each_entry_rcu(q, &tiq_list, entry) list_for_each_entry_rcu(q, &tiq_list, entry)
/* only process queues from changed sets */ /* only process queues from changed sets */
if (*q->irq_ptr->dsci) { if (*q->irq_ptr->dsci) {
qperf_inc(q, adapter_int);
/* only clear it if the indicator is non-shared */ /* only clear it if the indicator is non-shared */
if (!shared_ind(q->irq_ptr)) if (!shared_ind(q->irq_ptr))
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment