[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>
This commit is contained in:
parent
45d28b0972
commit
6486cda6c6
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -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.
|
||||
|
@ -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)
|
||||
{
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
@ -22,7 +22,6 @@
|
|||
#include "device.h"
|
||||
#include "qdio.h"
|
||||
#include "qdio_debug.h"
|
||||
#include "qdio_perf.h"
|
||||
|
||||
MODULE_AUTHOR("Utz Bacher <utz.bacher@de.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,
|
|||
int rc;
|
||||
|
||||
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)
|
||||
nr += q->irq_ptr->nr_input_qs;
|
||||
|
@ -139,7 +138,7 @@ again:
|
|||
* buffers later.
|
||||
*/
|
||||
if ((ccq == 96) && (count != tmp_count)) {
|
||||
qdio_perf_stat_inc(&perf_stats.debug_eqbs_incomplete);
|
||||
qperf_inc(q, eqbs_partial);
|
||||
return (count - tmp_count);
|
||||
}
|
||||
|
||||
|
@ -182,7 +181,7 @@ static int qdio_do_sqbs(struct qdio_q *q, unsigned char state, int start,
|
|||
return 0;
|
||||
|
||||
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)
|
||||
nr += q->irq_ptr->nr_input_qs;
|
||||
|
@ -191,7 +190,7 @@ again:
|
|||
rc = qdio_check_ccq(q, ccq);
|
||||
if (rc == 1) {
|
||||
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;
|
||||
}
|
||||
if (rc < 0) {
|
||||
|
@ -285,7 +284,7 @@ static inline int qdio_siga_sync(struct qdio_q *q, unsigned int output,
|
|||
return 0;
|
||||
|
||||
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);
|
||||
if (cc)
|
||||
|
@ -350,7 +349,7 @@ static inline int qdio_siga_input(struct qdio_q *q)
|
|||
int cc;
|
||||
|
||||
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);
|
||||
if (cc)
|
||||
|
@ -382,7 +381,7 @@ static inline void qdio_stop_polling(struct qdio_q *q)
|
|||
return;
|
||||
|
||||
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 */
|
||||
if (is_qebsm(q)) {
|
||||
|
@ -400,7 +399,7 @@ static void announce_buffer_error(struct qdio_q *q, int count)
|
|||
/* special handling for no target buffer empty */
|
||||
if ((!q->is_input_q &&
|
||||
(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",
|
||||
q->first_to_check);
|
||||
return;
|
||||
|
@ -487,7 +486,7 @@ static int get_inbound_buffer_frontier(struct qdio_q *q)
|
|||
inbound_primed(q, count);
|
||||
q->first_to_check = add_buf(q->first_to_check, count);
|
||||
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;
|
||||
case SLSB_P_INPUT_ERROR:
|
||||
announce_buffer_error(q, count);
|
||||
|
@ -567,9 +566,10 @@ static void qdio_kick_handler(struct qdio_q *q)
|
|||
count = sub_buf(end, start);
|
||||
|
||||
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);
|
||||
} else
|
||||
qperf_inc(q, outbound_handler);
|
||||
DBF_DEV_EVENT(DBF_INFO, q->irq_ptr, "koh: s:%02x c:%02x",
|
||||
start, count);
|
||||
|
||||
|
@ -583,24 +583,28 @@ static void qdio_kick_handler(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:
|
||||
if (!qdio_inbound_q_moved(q))
|
||||
return;
|
||||
|
||||
qdio_kick_handler(q);
|
||||
|
||||
if (!qdio_inbound_q_done(q))
|
||||
if (!qdio_inbound_q_done(q)) {
|
||||
/* means poll time is not yet over */
|
||||
qperf_inc(q, tasklet_inbound_resched);
|
||||
goto again;
|
||||
}
|
||||
|
||||
qdio_stop_polling(q);
|
||||
/*
|
||||
* We need to check again to not lose initiative after
|
||||
* resetting the ACK state.
|
||||
*/
|
||||
if (!qdio_inbound_q_done(q))
|
||||
if (!qdio_inbound_q_done(q)) {
|
||||
qperf_inc(q, tasklet_inbound_resched2);
|
||||
goto again;
|
||||
}
|
||||
}
|
||||
|
||||
void qdio_inbound_processing(unsigned long data)
|
||||
|
@ -688,7 +692,7 @@ static int qdio_kick_outbound_q(struct qdio_q *q)
|
|||
return 0;
|
||||
|
||||
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);
|
||||
switch (cc) {
|
||||
|
@ -711,7 +715,7 @@ static int qdio_kick_outbound_q(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);
|
||||
|
||||
if (qdio_outbound_q_moved(q))
|
||||
|
@ -739,12 +743,9 @@ static void __qdio_outbound_processing(struct qdio_q *q)
|
|||
*/
|
||||
if (qdio_outbound_q_done(q))
|
||||
del_timer(&q->u.out.timer);
|
||||
else {
|
||||
if (!timer_pending(&q->u.out.timer)) {
|
||||
else
|
||||
if (!timer_pending(&q->u.out.timer))
|
||||
mod_timer(&q->u.out.timer, jiffies + 10 * HZ);
|
||||
qdio_perf_stat_inc(&perf_stats.debug_tl_out_timer);
|
||||
}
|
||||
}
|
||||
return;
|
||||
|
||||
sched:
|
||||
|
@ -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)
|
||||
{
|
||||
qdio_perf_stat_inc(&perf_stats.thinint_inbound);
|
||||
qperf_inc(q, tasklet_inbound);
|
||||
qdio_sync_after_thinint(q);
|
||||
|
||||
/*
|
||||
|
@ -799,7 +800,7 @@ static void __tiqdio_inbound_processing(struct qdio_q *q)
|
|||
qdio_kick_handler(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)) {
|
||||
tasklet_schedule(&q->tasklet);
|
||||
return;
|
||||
|
@ -812,7 +813,7 @@ static void __tiqdio_inbound_processing(struct qdio_q *q)
|
|||
* resetting the ACK state.
|
||||
*/
|
||||
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))
|
||||
tasklet_schedule(&q->tasklet);
|
||||
}
|
||||
|
@ -851,8 +852,6 @@ static void qdio_int_handler_pci(struct qdio_irq *irq_ptr)
|
|||
if (unlikely(irq_ptr->state == QDIO_IRQ_STATE_STOPPED))
|
||||
return;
|
||||
|
||||
qdio_perf_stat_inc(&perf_stats.pci_int);
|
||||
|
||||
for_each_input_queue(irq_ptr, q, i)
|
||||
tasklet_schedule(&q->tasklet);
|
||||
|
||||
|
@ -923,8 +922,6 @@ void qdio_int_handler(struct ccw_device *cdev, unsigned long intparm,
|
|||
struct qdio_irq *irq_ptr = cdev->private->qdio_data;
|
||||
int cstat, dstat;
|
||||
|
||||
qdio_perf_stat_inc(&perf_stats.qdio_int);
|
||||
|
||||
if (!intparm || !irq_ptr) {
|
||||
DBF_ERROR("qint:%4x", cdev->private->schid.sch_no);
|
||||
return;
|
||||
|
@ -1383,6 +1380,8 @@ static int handle_inbound(struct qdio_q *q, unsigned int callflags,
|
|||
{
|
||||
int used, diff;
|
||||
|
||||
qperf_inc(q, inbound_call);
|
||||
|
||||
if (!q->u.in.polling)
|
||||
goto set;
|
||||
|
||||
|
@ -1438,14 +1437,16 @@ static int handle_outbound(struct qdio_q *q, unsigned int callflags,
|
|||
unsigned char state;
|
||||
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);
|
||||
used = atomic_add_return(count, &q->nr_buf_used);
|
||||
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;
|
||||
qperf_inc(q, pci_request_int);
|
||||
}
|
||||
else
|
||||
q->u.out.pci_out_enabled = 0;
|
||||
|
||||
|
@ -1484,7 +1485,7 @@ static int handle_outbound(struct qdio_q *q, unsigned int callflags,
|
|||
if (state != SLSB_CU_OUTPUT_PRIMED)
|
||||
rc = qdio_kick_outbound_q(q);
|
||||
else
|
||||
qdio_perf_stat_inc(&perf_stats.fast_requeue);
|
||||
qperf_inc(q, fast_requeue);
|
||||
|
||||
out:
|
||||
tasklet_schedule(&q->tasklet);
|
||||
|
@ -1540,16 +1541,11 @@ static int __init init_QDIO(void)
|
|||
rc = qdio_debug_init();
|
||||
if (rc)
|
||||
goto out_ti;
|
||||
rc = qdio_setup_perf_stats();
|
||||
if (rc)
|
||||
goto out_debug;
|
||||
rc = tiqdio_register_thinints();
|
||||
if (rc)
|
||||
goto out_perf;
|
||||
goto out_debug;
|
||||
return 0;
|
||||
|
||||
out_perf:
|
||||
qdio_remove_perf_stats();
|
||||
out_debug:
|
||||
qdio_debug_exit();
|
||||
out_ti:
|
||||
|
@ -1563,7 +1559,6 @@ static void __exit exit_QDIO(void)
|
|||
{
|
||||
tiqdio_unregister_thinints();
|
||||
tiqdio_free_memory();
|
||||
qdio_remove_perf_stats();
|
||||
qdio_debug_exit();
|
||||
qdio_setup_exit();
|
||||
}
|
||||
|
|
|
@ -1,149 +0,0 @@
|
|||
/*
|
||||
* 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);
|
||||
}
|
|
@ -1,62 +0,0 @@
|
|||
/*
|
||||
* 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
|
|
@ -1,9 +1,7 @@
|
|||
/*
|
||||
* linux/drivers/s390/cio/thinint_qdio.c
|
||||
*
|
||||
* thin interrupt support for qdio
|
||||
*
|
||||
* Copyright 2000-2008 IBM Corp.
|
||||
* Copyright 2000,2009 IBM Corp.
|
||||
* Author(s): Utz Bacher <utz.bacher@de.ibm.com>
|
||||
* Cornelia Huck <cornelia.huck@de.ibm.com>
|
||||
* Jan Glauber <jang@linux.vnet.ibm.com>
|
||||
|
@ -19,7 +17,6 @@
|
|||
#include "ioasm.h"
|
||||
#include "qdio.h"
|
||||
#include "qdio_debug.h"
|
||||
#include "qdio_perf.h"
|
||||
|
||||
/*
|
||||
* Restriction: only 63 iqdio subchannels would have its own indicator,
|
||||
|
@ -132,8 +129,6 @@ static void tiqdio_thinint_handler(void *ind, void *drv_data)
|
|||
{
|
||||
struct qdio_q *q;
|
||||
|
||||
qdio_perf_stat_inc(&perf_stats.thin_int);
|
||||
|
||||
/*
|
||||
* SVS only when needed: issue SVS to benefit from iqdio interrupt
|
||||
* avoidance (SVS clears adapter interrupt suppression overwrite)
|
||||
|
@ -154,6 +149,7 @@ static void tiqdio_thinint_handler(void *ind, void *drv_data)
|
|||
list_for_each_entry_rcu(q, &tiq_list, entry)
|
||||
/* only process queues from changed sets */
|
||||
if (*q->irq_ptr->dsci) {
|
||||
qperf_inc(q, adapter_int);
|
||||
|
||||
/* only clear it if the indicator is non-shared */
|
||||
if (!shared_ind(q->irq_ptr))
|
||||
|
|
Loading…
Reference in a new issue