Commit | Line | Data |
---|---|---|
1da177e4 LT |
1 | /* |
2 | * | |
e018ba1f | 3 | * linux/drivers/s390/net/qeth_fs.c |
1da177e4 LT |
4 | * |
5 | * Linux on zSeries OSA Express and HiperSockets support | |
6 | * This file contains code related to procfs. | |
7 | * | |
8 | * Copyright 2000,2003 IBM Corporation | |
9 | * | |
10 | * Author(s): Thomas Spatzier <tspat@de.ibm.com> | |
11 | * | |
12 | */ | |
13 | #include <linux/module.h> | |
14 | #include <linux/init.h> | |
15 | #include <linux/proc_fs.h> | |
16 | #include <linux/seq_file.h> | |
17 | #include <linux/list.h> | |
18 | #include <linux/rwsem.h> | |
19 | ||
20 | #include "qeth.h" | |
21 | #include "qeth_mpc.h" | |
22 | #include "qeth_fs.h" | |
23 | ||
1da177e4 LT |
24 | /***** /proc/qeth *****/ |
25 | #define QETH_PROCFILE_NAME "qeth" | |
26 | static struct proc_dir_entry *qeth_procfile; | |
27 | ||
66aea23f CH |
28 | static int |
29 | qeth_procfile_seq_match(struct device *dev, void *data) | |
30 | { | |
3df3cc6d | 31 | return(dev ? 1 : 0); |
66aea23f CH |
32 | } |
33 | ||
1da177e4 LT |
34 | static void * |
35 | qeth_procfile_seq_start(struct seq_file *s, loff_t *offset) | |
36 | { | |
3df3cc6d FP |
37 | struct device *dev = NULL; |
38 | loff_t nr = 0; | |
e82b0f2c | 39 | |
1da177e4 | 40 | down_read(&qeth_ccwgroup_driver.driver.bus->subsys.rwsem); |
3df3cc6d | 41 | if (*offset == 0) |
1da177e4 | 42 | return SEQ_START_TOKEN; |
3df3cc6d | 43 | while (1) { |
66aea23f CH |
44 | dev = driver_find_device(&qeth_ccwgroup_driver.driver, dev, |
45 | NULL, qeth_procfile_seq_match); | |
3df3cc6d FP |
46 | if (++nr == *offset) |
47 | break; | |
48 | put_device(dev); | |
49 | } | |
50 | return dev; | |
1da177e4 LT |
51 | } |
52 | ||
53 | static void | |
54 | qeth_procfile_seq_stop(struct seq_file *s, void* it) | |
55 | { | |
56 | up_read(&qeth_ccwgroup_driver.driver.bus->subsys.rwsem); | |
57 | } | |
58 | ||
59 | static void * | |
60 | qeth_procfile_seq_next(struct seq_file *s, void *it, loff_t *offset) | |
61 | { | |
66aea23f | 62 | struct device *prev, *next; |
e82b0f2c JG |
63 | |
64 | if (it == SEQ_START_TOKEN) | |
3df3cc6d FP |
65 | prev = NULL; |
66 | else | |
67 | prev = (struct device *) it; | |
66aea23f CH |
68 | next = driver_find_device(&qeth_ccwgroup_driver.driver, |
69 | prev, NULL, qeth_procfile_seq_match); | |
3df3cc6d | 70 | (*offset)++; |
66aea23f | 71 | return (void *) next; |
1da177e4 LT |
72 | } |
73 | ||
74 | static inline const char * | |
75 | qeth_get_router_str(struct qeth_card *card, int ipv) | |
76 | { | |
1380fee2 | 77 | enum qeth_routing_types routing_type = NO_ROUTER; |
1da177e4 | 78 | |
3df3cc6d | 79 | if (ipv == 4) { |
1da177e4 LT |
80 | routing_type = card->options.route4.type; |
81 | } else { | |
82 | #ifdef CONFIG_QETH_IPV6 | |
83 | routing_type = card->options.route6.type; | |
84 | #else | |
85 | return "n/a"; | |
86 | #endif /* CONFIG_QETH_IPV6 */ | |
87 | } | |
88 | ||
1380fee2 FP |
89 | switch (routing_type){ |
90 | case PRIMARY_ROUTER: | |
1da177e4 | 91 | return "pri"; |
1380fee2 | 92 | case SECONDARY_ROUTER: |
1da177e4 | 93 | return "sec"; |
1380fee2 | 94 | case MULTICAST_ROUTER: |
1da177e4 LT |
95 | if (card->info.broadcast_capable == QETH_BROADCAST_WITHOUT_ECHO) |
96 | return "mc+"; | |
97 | return "mc"; | |
1380fee2 | 98 | case PRIMARY_CONNECTOR: |
1da177e4 LT |
99 | if (card->info.broadcast_capable == QETH_BROADCAST_WITHOUT_ECHO) |
100 | return "p+c"; | |
101 | return "p.c"; | |
1380fee2 | 102 | case SECONDARY_CONNECTOR: |
1da177e4 LT |
103 | if (card->info.broadcast_capable == QETH_BROADCAST_WITHOUT_ECHO) |
104 | return "s+c"; | |
105 | return "s.c"; | |
1380fee2 | 106 | default: /* NO_ROUTER */ |
1da177e4 | 107 | return "no"; |
1380fee2 | 108 | } |
1da177e4 LT |
109 | } |
110 | ||
111 | static int | |
112 | qeth_procfile_seq_show(struct seq_file *s, void *it) | |
113 | { | |
114 | struct device *device; | |
115 | struct qeth_card *card; | |
116 | char tmp[12]; /* for qeth_get_prioq_str */ | |
117 | ||
118 | if (it == SEQ_START_TOKEN){ | |
119 | seq_printf(s, "devices CHPID interface " | |
120 | "cardtype port chksum prio-q'ing rtr4 " | |
121 | "rtr6 fsz cnt\n"); | |
122 | seq_printf(s, "-------------------------- ----- ---------- " | |
123 | "-------------- ---- ------ ---------- ---- " | |
124 | "---- ----- -----\n"); | |
125 | } else { | |
66aea23f | 126 | device = (struct device *) it; |
1da177e4 LT |
127 | card = device->driver_data; |
128 | seq_printf(s, "%s/%s/%s x%02X %-10s %-14s %-4i ", | |
129 | CARD_RDEV_ID(card), | |
130 | CARD_WDEV_ID(card), | |
131 | CARD_DDEV_ID(card), | |
132 | card->info.chpid, | |
133 | QETH_CARD_IFNAME(card), | |
134 | qeth_get_cardname_short(card), | |
135 | card->info.portno); | |
136 | if (card->lan_online) | |
137 | seq_printf(s, "%-6s %-10s %-4s %-4s %-5s %-5i\n", | |
138 | qeth_get_checksum_str(card), | |
139 | qeth_get_prioq_str(card, tmp), | |
140 | qeth_get_router_str(card, 4), | |
141 | qeth_get_router_str(card, 6), | |
142 | qeth_get_bufsize_str(card), | |
143 | card->qdio.in_buf_pool.buf_count); | |
144 | else | |
145 | seq_printf(s, " +++ LAN OFFLINE +++\n"); | |
3df3cc6d | 146 | put_device(device); |
1da177e4 LT |
147 | } |
148 | return 0; | |
149 | } | |
150 | ||
151 | static struct seq_operations qeth_procfile_seq_ops = { | |
152 | .start = qeth_procfile_seq_start, | |
153 | .stop = qeth_procfile_seq_stop, | |
154 | .next = qeth_procfile_seq_next, | |
155 | .show = qeth_procfile_seq_show, | |
156 | }; | |
157 | ||
158 | static int | |
159 | qeth_procfile_open(struct inode *inode, struct file *file) | |
160 | { | |
161 | return seq_open(file, &qeth_procfile_seq_ops); | |
162 | } | |
163 | ||
d54b1fdb | 164 | static const struct file_operations qeth_procfile_fops = { |
1da177e4 LT |
165 | .owner = THIS_MODULE, |
166 | .open = qeth_procfile_open, | |
167 | .read = seq_read, | |
168 | .llseek = seq_lseek, | |
169 | .release = seq_release, | |
170 | }; | |
171 | ||
172 | /***** /proc/qeth_perf *****/ | |
173 | #define QETH_PERF_PROCFILE_NAME "qeth_perf" | |
174 | static struct proc_dir_entry *qeth_perf_procfile; | |
175 | ||
1da177e4 LT |
176 | static int |
177 | qeth_perf_procfile_seq_show(struct seq_file *s, void *it) | |
178 | { | |
179 | struct device *device; | |
180 | struct qeth_card *card; | |
181 | ||
e82b0f2c | 182 | |
3df3cc6d FP |
183 | if (it == SEQ_START_TOKEN) |
184 | return 0; | |
185 | ||
66aea23f | 186 | device = (struct device *) it; |
1da177e4 LT |
187 | card = device->driver_data; |
188 | seq_printf(s, "For card with devnos %s/%s/%s (%s):\n", | |
189 | CARD_RDEV_ID(card), | |
190 | CARD_WDEV_ID(card), | |
191 | CARD_DDEV_ID(card), | |
192 | QETH_CARD_IFNAME(card) | |
193 | ); | |
09d2d38a FP |
194 | if (!card->options.performance_stats) |
195 | seq_printf(s, "Performance statistics are deactivated.\n"); | |
95f6b5a1 FP |
196 | seq_printf(s, " Skb's/buffers received : %lu/%u\n" |
197 | " Skb's/buffers sent : %lu/%u\n\n", | |
09d2d38a FP |
198 | card->stats.rx_packets - |
199 | card->perf_stats.initial_rx_packets, | |
200 | card->perf_stats.bufs_rec, | |
201 | card->stats.tx_packets - | |
202 | card->perf_stats.initial_tx_packets, | |
203 | card->perf_stats.bufs_sent | |
1da177e4 | 204 | ); |
95f6b5a1 FP |
205 | seq_printf(s, " Skb's/buffers sent without packing : %lu/%u\n" |
206 | " Skb's/buffers sent with packing : %u/%u\n\n", | |
09d2d38a FP |
207 | card->stats.tx_packets - card->perf_stats.initial_tx_packets |
208 | - card->perf_stats.skbs_sent_pack, | |
1da177e4 LT |
209 | card->perf_stats.bufs_sent - card->perf_stats.bufs_sent_pack, |
210 | card->perf_stats.skbs_sent_pack, | |
211 | card->perf_stats.bufs_sent_pack | |
212 | ); | |
95f6b5a1 FP |
213 | seq_printf(s, " Skbs sent in SG mode : %u\n" |
214 | " Skb fragments sent in SG mode : %u\n\n", | |
1da177e4 LT |
215 | card->perf_stats.sg_skbs_sent, |
216 | card->perf_stats.sg_frags_sent); | |
95f6b5a1 FP |
217 | seq_printf(s, " large_send tx (in Kbytes) : %u\n" |
218 | " large_send count : %u\n\n", | |
1da177e4 LT |
219 | card->perf_stats.large_send_bytes >> 10, |
220 | card->perf_stats.large_send_cnt); | |
95f6b5a1 | 221 | seq_printf(s, " Packing state changes no pkg.->packing : %u/%u\n" |
1da177e4 LT |
222 | " Watermarks L/H : %i/%i\n" |
223 | " Current buffer usage (outbound q's) : " | |
224 | "%i/%i/%i/%i\n\n", | |
225 | card->perf_stats.sc_dp_p, card->perf_stats.sc_p_dp, | |
226 | QETH_LOW_WATERMARK_PACK, QETH_HIGH_WATERMARK_PACK, | |
227 | atomic_read(&card->qdio.out_qs[0]->used_buffers), | |
228 | (card->qdio.no_out_queues > 1)? | |
229 | atomic_read(&card->qdio.out_qs[1]->used_buffers) | |
230 | : 0, | |
231 | (card->qdio.no_out_queues > 2)? | |
232 | atomic_read(&card->qdio.out_qs[2]->used_buffers) | |
233 | : 0, | |
234 | (card->qdio.no_out_queues > 3)? | |
235 | atomic_read(&card->qdio.out_qs[3]->used_buffers) | |
236 | : 0 | |
237 | ); | |
95f6b5a1 FP |
238 | seq_printf(s, " Inbound handler time (in us) : %u\n" |
239 | " Inbound handler count : %u\n" | |
240 | " Inbound do_QDIO time (in us) : %u\n" | |
241 | " Inbound do_QDIO count : %u\n\n" | |
242 | " Outbound handler time (in us) : %u\n" | |
243 | " Outbound handler count : %u\n\n" | |
244 | " Outbound time (in us, incl QDIO) : %u\n" | |
245 | " Outbound count : %u\n" | |
246 | " Outbound do_QDIO time (in us) : %u\n" | |
247 | " Outbound do_QDIO count : %u\n\n", | |
1da177e4 LT |
248 | card->perf_stats.inbound_time, |
249 | card->perf_stats.inbound_cnt, | |
250 | card->perf_stats.inbound_do_qdio_time, | |
251 | card->perf_stats.inbound_do_qdio_cnt, | |
252 | card->perf_stats.outbound_handler_time, | |
253 | card->perf_stats.outbound_handler_cnt, | |
254 | card->perf_stats.outbound_time, | |
255 | card->perf_stats.outbound_cnt, | |
256 | card->perf_stats.outbound_do_qdio_time, | |
257 | card->perf_stats.outbound_do_qdio_cnt | |
258 | ); | |
3df3cc6d | 259 | put_device(device); |
1da177e4 LT |
260 | return 0; |
261 | } | |
262 | ||
263 | static struct seq_operations qeth_perf_procfile_seq_ops = { | |
3df3cc6d FP |
264 | .start = qeth_procfile_seq_start, |
265 | .stop = qeth_procfile_seq_stop, | |
266 | .next = qeth_procfile_seq_next, | |
1da177e4 LT |
267 | .show = qeth_perf_procfile_seq_show, |
268 | }; | |
269 | ||
270 | static int | |
271 | qeth_perf_procfile_open(struct inode *inode, struct file *file) | |
272 | { | |
273 | return seq_open(file, &qeth_perf_procfile_seq_ops); | |
274 | } | |
275 | ||
d54b1fdb | 276 | static const struct file_operations qeth_perf_procfile_fops = { |
1da177e4 LT |
277 | .owner = THIS_MODULE, |
278 | .open = qeth_perf_procfile_open, | |
279 | .read = seq_read, | |
280 | .llseek = seq_lseek, | |
281 | .release = seq_release, | |
282 | }; | |
283 | ||
1da177e4 LT |
284 | int __init |
285 | qeth_create_procfs_entries(void) | |
286 | { | |
287 | qeth_procfile = create_proc_entry(QETH_PROCFILE_NAME, | |
288 | S_IFREG | 0444, NULL); | |
289 | if (qeth_procfile) | |
290 | qeth_procfile->proc_fops = &qeth_procfile_fops; | |
291 | ||
1da177e4 LT |
292 | qeth_perf_procfile = create_proc_entry(QETH_PERF_PROCFILE_NAME, |
293 | S_IFREG | 0444, NULL); | |
294 | if (qeth_perf_procfile) | |
295 | qeth_perf_procfile->proc_fops = &qeth_perf_procfile_fops; | |
1da177e4 | 296 | |
1da177e4 | 297 | if (qeth_procfile && |
09d2d38a | 298 | qeth_perf_procfile) |
1da177e4 LT |
299 | return 0; |
300 | else | |
301 | return -ENOMEM; | |
302 | } | |
303 | ||
304 | void __exit | |
305 | qeth_remove_procfs_entries(void) | |
306 | { | |
307 | if (qeth_procfile) | |
308 | remove_proc_entry(QETH_PROCFILE_NAME, NULL); | |
309 | if (qeth_perf_procfile) | |
310 | remove_proc_entry(QETH_PERF_PROCFILE_NAME, NULL); | |
1da177e4 LT |
311 | } |
312 |