[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
7 changed files with 177 additions and 265 deletions
|
@ -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 {
|
||||||
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 {
|
||||||
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 {
|
||||||
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 {
|
||||||
(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)
|
||||||
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,
|
||||||
|
|
||||||
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 = {
|
||||||
.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)
|
||||||
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
|
||||||
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 @@
|
||||||
#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,
|
||||||
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:
|
||||||
* 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,
|
||||||
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:
|
||||||
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,
|
||||||
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)
|
||||||
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)
|
||||||
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)
|
||||||
/* 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)
|
||||||
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)
|
||||||
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)
|
||||||
|
|
||||||
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)
|
||||||
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)
|
||||||
|
|
||||||
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)
|
||||||
*/
|
*/
|
||||||
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)
|
||||||
|
|
||||||
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)
|
||||||
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)
|
||||||
* 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)
|
||||||
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,
|
||||||
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,
|
||||||
{
|
{
|
||||||
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,
|
||||||
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,
|
||||||
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)
|
||||||
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)
|
||||||
{
|
{
|
||||||
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();
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
* 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 @@
|
||||||
#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)
|
||||||
{
|
{
|
||||||
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)
|
||||||
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))
|
||||||
|
|
Loading…
Add table
Reference in a new issue