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