diff options
Diffstat (limited to 'drivers/s390/cio/qdio_debug.c')
-rw-r--r-- | drivers/s390/cio/qdio_debug.c | 240 |
1 files changed, 240 insertions, 0 deletions
diff --git a/drivers/s390/cio/qdio_debug.c b/drivers/s390/cio/qdio_debug.c new file mode 100644 index 000000000000..337aa3087a78 --- /dev/null +++ b/drivers/s390/cio/qdio_debug.c | |||
@@ -0,0 +1,240 @@ | |||
1 | /* | ||
2 | * drivers/s390/cio/qdio_debug.c | ||
3 | * | ||
4 | * Copyright IBM Corp. 2008 | ||
5 | * | ||
6 | * Author: Jan Glauber (jang@linux.vnet.ibm.com) | ||
7 | */ | ||
8 | #include <linux/proc_fs.h> | ||
9 | #include <linux/seq_file.h> | ||
10 | #include <linux/debugfs.h> | ||
11 | #include <asm/qdio.h> | ||
12 | #include <asm/debug.h> | ||
13 | #include "qdio_debug.h" | ||
14 | #include "qdio.h" | ||
15 | |||
16 | debug_info_t *qdio_dbf_setup; | ||
17 | debug_info_t *qdio_dbf_trace; | ||
18 | |||
19 | static struct dentry *debugfs_root; | ||
20 | #define MAX_DEBUGFS_QUEUES 32 | ||
21 | static struct dentry *debugfs_queues[MAX_DEBUGFS_QUEUES] = { NULL }; | ||
22 | static DEFINE_MUTEX(debugfs_mutex); | ||
23 | |||
24 | void qdio_allocate_do_dbf(struct qdio_initialize *init_data) | ||
25 | { | ||
26 | char dbf_text[20]; | ||
27 | |||
28 | sprintf(dbf_text, "qfmt:%x", init_data->q_format); | ||
29 | QDIO_DBF_TEXT0(0, setup, dbf_text); | ||
30 | QDIO_DBF_HEX0(0, setup, init_data->adapter_name, 8); | ||
31 | sprintf(dbf_text, "qpff%4x", init_data->qib_param_field_format); | ||
32 | QDIO_DBF_TEXT0(0, setup, dbf_text); | ||
33 | QDIO_DBF_HEX0(0, setup, &init_data->qib_param_field, sizeof(void *)); | ||
34 | QDIO_DBF_HEX0(0, setup, &init_data->input_slib_elements, sizeof(void *)); | ||
35 | QDIO_DBF_HEX0(0, setup, &init_data->output_slib_elements, sizeof(void *)); | ||
36 | sprintf(dbf_text, "niq:%4x", init_data->no_input_qs); | ||
37 | QDIO_DBF_TEXT0(0, setup, dbf_text); | ||
38 | sprintf(dbf_text, "noq:%4x", init_data->no_output_qs); | ||
39 | QDIO_DBF_TEXT0(0, setup, dbf_text); | ||
40 | QDIO_DBF_HEX0(0, setup, &init_data->input_handler, sizeof(void *)); | ||
41 | QDIO_DBF_HEX0(0, setup, &init_data->output_handler, sizeof(void *)); | ||
42 | QDIO_DBF_HEX0(0, setup, &init_data->int_parm, sizeof(long)); | ||
43 | QDIO_DBF_HEX0(0, setup, &init_data->flags, sizeof(long)); | ||
44 | QDIO_DBF_HEX0(0, setup, &init_data->input_sbal_addr_array, sizeof(void *)); | ||
45 | QDIO_DBF_HEX0(0, setup, &init_data->output_sbal_addr_array, sizeof(void *)); | ||
46 | } | ||
47 | |||
48 | static void qdio_unregister_dbf_views(void) | ||
49 | { | ||
50 | if (qdio_dbf_setup) | ||
51 | debug_unregister(qdio_dbf_setup); | ||
52 | if (qdio_dbf_trace) | ||
53 | debug_unregister(qdio_dbf_trace); | ||
54 | } | ||
55 | |||
56 | static int qdio_register_dbf_views(void) | ||
57 | { | ||
58 | qdio_dbf_setup = debug_register("qdio_setup", QDIO_DBF_SETUP_PAGES, | ||
59 | QDIO_DBF_SETUP_NR_AREAS, | ||
60 | QDIO_DBF_SETUP_LEN); | ||
61 | if (!qdio_dbf_setup) | ||
62 | goto oom; | ||
63 | debug_register_view(qdio_dbf_setup, &debug_hex_ascii_view); | ||
64 | debug_set_level(qdio_dbf_setup, QDIO_DBF_SETUP_LEVEL); | ||
65 | |||
66 | qdio_dbf_trace = debug_register("qdio_trace", QDIO_DBF_TRACE_PAGES, | ||
67 | QDIO_DBF_TRACE_NR_AREAS, | ||
68 | QDIO_DBF_TRACE_LEN); | ||
69 | if (!qdio_dbf_trace) | ||
70 | goto oom; | ||
71 | debug_register_view(qdio_dbf_trace, &debug_hex_ascii_view); | ||
72 | debug_set_level(qdio_dbf_trace, QDIO_DBF_TRACE_LEVEL); | ||
73 | return 0; | ||
74 | oom: | ||
75 | qdio_unregister_dbf_views(); | ||
76 | return -ENOMEM; | ||
77 | } | ||
78 | |||
79 | static int qstat_show(struct seq_file *m, void *v) | ||
80 | { | ||
81 | unsigned char state; | ||
82 | struct qdio_q *q = m->private; | ||
83 | int i; | ||
84 | |||
85 | if (!q) | ||
86 | return 0; | ||
87 | |||
88 | seq_printf(m, "device state indicator: %d\n", *q->irq_ptr->dsci); | ||
89 | seq_printf(m, "nr_used: %d\n", atomic_read(&q->nr_buf_used)); | ||
90 | seq_printf(m, "ftc: %d\n", q->first_to_check); | ||
91 | seq_printf(m, "last_move_ftc: %d\n", q->last_move_ftc); | ||
92 | seq_printf(m, "polling: %d\n", q->u.in.polling); | ||
93 | seq_printf(m, "slsb buffer states:\n"); | ||
94 | |||
95 | qdio_siga_sync_q(q); | ||
96 | for (i = 0; i < QDIO_MAX_BUFFERS_PER_Q; i++) { | ||
97 | get_buf_state(q, i, &state); | ||
98 | switch (state) { | ||
99 | case SLSB_P_INPUT_NOT_INIT: | ||
100 | case SLSB_P_OUTPUT_NOT_INIT: | ||
101 | seq_printf(m, "N"); | ||
102 | break; | ||
103 | case SLSB_P_INPUT_PRIMED: | ||
104 | case SLSB_CU_OUTPUT_PRIMED: | ||
105 | seq_printf(m, "+"); | ||
106 | break; | ||
107 | case SLSB_P_INPUT_ACK: | ||
108 | seq_printf(m, "A"); | ||
109 | break; | ||
110 | case SLSB_P_INPUT_ERROR: | ||
111 | case SLSB_P_OUTPUT_ERROR: | ||
112 | seq_printf(m, "x"); | ||
113 | break; | ||
114 | case SLSB_CU_INPUT_EMPTY: | ||
115 | case SLSB_P_OUTPUT_EMPTY: | ||
116 | seq_printf(m, "-"); | ||
117 | break; | ||
118 | case SLSB_P_INPUT_HALTED: | ||
119 | case SLSB_P_OUTPUT_HALTED: | ||
120 | seq_printf(m, "."); | ||
121 | break; | ||
122 | default: | ||
123 | seq_printf(m, "?"); | ||
124 | } | ||
125 | if (i == 63) | ||
126 | seq_printf(m, "\n"); | ||
127 | } | ||
128 | seq_printf(m, "\n"); | ||
129 | return 0; | ||
130 | } | ||
131 | |||
132 | static ssize_t qstat_seq_write(struct file *file, const char __user *buf, | ||
133 | size_t count, loff_t *off) | ||
134 | { | ||
135 | struct seq_file *seq = file->private_data; | ||
136 | struct qdio_q *q = seq->private; | ||
137 | |||
138 | if (!q) | ||
139 | return 0; | ||
140 | |||
141 | if (q->is_input_q) | ||
142 | xchg(q->irq_ptr->dsci, 1); | ||
143 | local_bh_disable(); | ||
144 | tasklet_schedule(&q->tasklet); | ||
145 | local_bh_enable(); | ||
146 | return count; | ||
147 | } | ||
148 | |||
149 | static int qstat_seq_open(struct inode *inode, struct file *filp) | ||
150 | { | ||
151 | return single_open(filp, qstat_show, | ||
152 | filp->f_path.dentry->d_inode->i_private); | ||
153 | } | ||
154 | |||
155 | static void get_queue_name(struct qdio_q *q, struct ccw_device *cdev, char *name) | ||
156 | { | ||
157 | memset(name, 0, sizeof(name)); | ||
158 | sprintf(name, "%s", cdev->dev.bus_id); | ||
159 | if (q->is_input_q) | ||
160 | sprintf(name + strlen(name), "_input"); | ||
161 | else | ||
162 | sprintf(name + strlen(name), "_output"); | ||
163 | sprintf(name + strlen(name), "_%d", q->nr); | ||
164 | } | ||
165 | |||
166 | static void remove_debugfs_entry(struct qdio_q *q) | ||
167 | { | ||
168 | int i; | ||
169 | |||
170 | for (i = 0; i < MAX_DEBUGFS_QUEUES; i++) { | ||
171 | if (!debugfs_queues[i]) | ||
172 | continue; | ||
173 | if (debugfs_queues[i]->d_inode->i_private == q) { | ||
174 | debugfs_remove(debugfs_queues[i]); | ||
175 | debugfs_queues[i] = NULL; | ||
176 | } | ||
177 | } | ||
178 | } | ||
179 | |||
180 | static struct file_operations debugfs_fops = { | ||
181 | .owner = THIS_MODULE, | ||
182 | .open = qstat_seq_open, | ||
183 | .read = seq_read, | ||
184 | .write = qstat_seq_write, | ||
185 | .llseek = seq_lseek, | ||
186 | .release = single_release, | ||
187 | }; | ||
188 | |||
189 | static void setup_debugfs_entry(struct qdio_q *q, struct ccw_device *cdev) | ||
190 | { | ||
191 | int i = 0; | ||
192 | char name[40]; | ||
193 | |||
194 | while (debugfs_queues[i] != NULL) { | ||
195 | i++; | ||
196 | if (i >= MAX_DEBUGFS_QUEUES) | ||
197 | return; | ||
198 | } | ||
199 | get_queue_name(q, cdev, name); | ||
200 | debugfs_queues[i] = debugfs_create_file(name, S_IFREG | S_IRUGO | S_IWUSR, | ||
201 | debugfs_root, q, &debugfs_fops); | ||
202 | } | ||
203 | |||
204 | void qdio_setup_debug_entries(struct qdio_irq *irq_ptr, struct ccw_device *cdev) | ||
205 | { | ||
206 | struct qdio_q *q; | ||
207 | int i; | ||
208 | |||
209 | mutex_lock(&debugfs_mutex); | ||
210 | for_each_input_queue(irq_ptr, q, i) | ||
211 | setup_debugfs_entry(q, cdev); | ||
212 | for_each_output_queue(irq_ptr, q, i) | ||
213 | setup_debugfs_entry(q, cdev); | ||
214 | mutex_unlock(&debugfs_mutex); | ||
215 | } | ||
216 | |||
217 | void qdio_shutdown_debug_entries(struct qdio_irq *irq_ptr, struct ccw_device *cdev) | ||
218 | { | ||
219 | struct qdio_q *q; | ||
220 | int i; | ||
221 | |||
222 | mutex_lock(&debugfs_mutex); | ||
223 | for_each_input_queue(irq_ptr, q, i) | ||
224 | remove_debugfs_entry(q); | ||
225 | for_each_output_queue(irq_ptr, q, i) | ||
226 | remove_debugfs_entry(q); | ||
227 | mutex_unlock(&debugfs_mutex); | ||
228 | } | ||
229 | |||
230 | int __init qdio_debug_init(void) | ||
231 | { | ||
232 | debugfs_root = debugfs_create_dir("qdio_queues", NULL); | ||
233 | return qdio_register_dbf_views(); | ||
234 | } | ||
235 | |||
236 | void qdio_debug_exit(void) | ||
237 | { | ||
238 | debugfs_remove(debugfs_root); | ||
239 | qdio_unregister_dbf_views(); | ||
240 | } | ||