Commit | Line | Data |
---|---|---|
779e6e1c JG |
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)); | |
9286b7ed | 158 | sprintf(name, "%s", dev_name(&cdev->dev)); |
779e6e1c JG |
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 | } |