1/*
2 * Copyright (c) 2008 Mellanox Technologies Ltd.  All rights reserved.
3 *
4 * This software is available to you under a choice of one of two
5 * licenses.  You may choose to be licensed under the terms of the GNU
6 * General Public License (GPL) Version 2, available from the file
7 * COPYING in the main directory of this source tree, or the
8 * OpenIB.org BSD license below:
9 *
10 *     Redistribution and use in source and binary forms, with or
11 *     without modification, are permitted provided that the following
12 *     conditions are met:
13 *
14 *      - Redistributions of source code must retain the above
15 *        copyright notice, this list of conditions and the following
16 *        disclaimer.
17 *
18 *      - Redistributions in binary form must reproduce the above
19 *        copyright notice, this list of conditions and the following
20 *        disclaimer in the documentation and/or other materials
21 *        provided with the distribution.
22 *
23 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
24 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
25 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
26 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
27 * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
28 * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
29 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
30 * SOFTWARE.
31 */
32
33#include <linux/proc_fs.h>
34#include <rdma/sdp_socket.h>
35#include "sdp.h"
36
37#ifdef CONFIG_PROC_FS
38
39#define PROC_SDP_STATS "sdpstats"
40#define PROC_SDP_PERF "sdpprf"
41
42/* just like TCP fs */
43struct sdp_seq_afinfo {
44	struct module           *owner;
45	char                    *name;
46	sa_family_t             family;
47	int                     (*seq_show) (struct seq_file *m, void *v);
48	struct file_operations  *seq_fops;
49};
50
51struct sdp_iter_state {
52	sa_family_t             family;
53	int                     num;
54	struct seq_operations   seq_ops;
55};
56
57static void *sdp_get_idx(struct seq_file *seq, loff_t pos)
58{
59	int i = 0;
60	struct sdp_sock *ssk;
61
62	if (!list_empty(&sock_list))
63		list_for_each_entry(ssk, &sock_list, sock_list) {
64			if (i == pos)
65				return ssk;
66			i++;
67		}
68
69	return NULL;
70}
71
72static void *sdp_seq_start(struct seq_file *seq, loff_t *pos)
73{
74	void *start = NULL;
75	struct sdp_iter_state *st = seq->private;
76
77	st->num = 0;
78
79	if (!*pos)
80		return SEQ_START_TOKEN;
81
82	spin_lock_irq(&sock_list_lock);
83	start = sdp_get_idx(seq, *pos - 1);
84	if (start)
85		sock_hold((struct socket *)start, SOCK_REF_SEQ);
86	spin_unlock_irq(&sock_list_lock);
87
88	return start;
89}
90
91static void *sdp_seq_next(struct seq_file *seq, void *v, loff_t *pos)
92{
93	struct sdp_iter_state *st = seq->private;
94	void *next = NULL;
95
96	spin_lock_irq(&sock_list_lock);
97	if (v == SEQ_START_TOKEN)
98		next = sdp_get_idx(seq, 0);
99	else
100		next = sdp_get_idx(seq, *pos);
101	if (next)
102		sock_hold((struct socket *)next, SOCK_REF_SEQ);
103	spin_unlock_irq(&sock_list_lock);
104
105	*pos += 1;
106	st->num++;
107
108	return next;
109}
110
111static void sdp_seq_stop(struct seq_file *seq, void *v)
112{
113}
114
115#define TMPSZ 150
116
117static int sdp_seq_show(struct seq_file *seq, void *v)
118{
119	struct sdp_iter_state *st;
120	struct socket *sk = v;
121	char tmpbuf[TMPSZ + 1];
122	unsigned int dest;
123	unsigned int src;
124	int uid;
125	unsigned long inode;
126	__u16 destp;
127	__u16 srcp;
128	__u32 rx_queue, tx_queue;
129
130	if (v == SEQ_START_TOKEN) {
131		seq_printf(seq, "%-*s\n", TMPSZ - 1,
132				"  sl  local_address rem_address        "
133				"uid inode   rx_queue tx_queue state");
134		goto out;
135	}
136
137	st = seq->private;
138
139	dest = inet_sk(sk)->daddr;
140	src = inet_sk(sk)->rcv_saddr;
141	destp = ntohs(inet_sk(sk)->dport);
142	srcp = ntohs(inet_sk(sk)->sport);
143	uid = sock_i_uid(sk);
144	inode = sock_i_ino(sk);
145	rx_queue = rcv_nxt(sdp_sk(sk)) - sdp_sk(sk)->copied_seq;
146	tx_queue = sdp_sk(sk)->write_seq - sdp_sk(sk)->tx_ring.una_seq;
147
148	sprintf(tmpbuf, "%4d: %08X:%04X %08X:%04X %5d %lu	%08X:%08X %X",
149		st->num, src, srcp, dest, destp, uid, inode,
150		rx_queue, tx_queue, sk->sk_state);
151
152	seq_printf(seq, "%-*s\n", TMPSZ - 1, tmpbuf);
153
154	sock_put(sk, SOCK_REF_SEQ);
155out:
156	return 0;
157}
158
159static int sdp_seq_open(struct inode *inode, struct file *file)
160{
161	struct sdp_seq_afinfo *afinfo = PDE(inode)->data;
162	struct seq_file *seq;
163	struct sdp_iter_state *s;
164	int rc;
165
166	if (unlikely(afinfo == NULL))
167		return -EINVAL;
168
169/* Workaround bogus warning by memtrack */
170#define _kzalloc(size,flags) kzalloc(size,flags)
171#undef kzalloc
172	s = kzalloc(sizeof(*s), GFP_KERNEL);
173#define kzalloc(s,f) _kzalloc(s,f)
174	if (!s)
175		return -ENOMEM;
176	s->family               = afinfo->family;
177	s->seq_ops.start        = sdp_seq_start;
178	s->seq_ops.next         = sdp_seq_next;
179	s->seq_ops.show         = afinfo->seq_show;
180	s->seq_ops.stop         = sdp_seq_stop;
181
182	rc = seq_open(file, &s->seq_ops);
183	if (rc)
184		goto out_kfree;
185	seq          = file->private_data;
186	seq->private = s;
187out:
188	return rc;
189out_kfree:
190	kfree(s);
191	goto out;
192}
193
194
195static struct file_operations sdp_seq_fops;
196static struct sdp_seq_afinfo sdp_seq_afinfo = {
197	.owner          = THIS_MODULE,
198	.name           = "sdp",
199	.family         = AF_INET_SDP,
200	.seq_show       = sdp_seq_show,
201	.seq_fops       = &sdp_seq_fops,
202};
203
204#ifdef SDPSTATS_ON
205DEFINE_PER_CPU(struct sdpstats, sdpstats);
206
207static void sdpstats_seq_hist(struct seq_file *seq, char *str, u32 *h, int n,
208		int is_log)
209{
210	int i;
211	u32 max = 0;
212
213	seq_printf(seq, "%s:\n", str);
214
215	for (i = 0; i < n; i++) {
216		if (h[i] > max)
217			max = h[i];
218	}
219
220	if (max == 0) {
221		seq_printf(seq, " - all values are 0\n");
222		return;
223	}
224
225	for (i = 0; i < n; i++) {
226		char s[51];
227		int j = 50 * h[i] / max;
228		int val = is_log ? (i == n-1 ? 0 : 1<<i) : i;
229		memset(s, '*', j);
230		s[j] = '\0';
231
232		seq_printf(seq, "%10d | %-50s - %d\n", val, s, h[i]);
233	}
234}
235
236#define SDPSTATS_COUNTER_GET(var) ({ \
237	u32 __val = 0;						\
238	unsigned int __i;                                       \
239	for_each_possible_cpu(__i)                              \
240		__val += per_cpu(sdpstats, __i).var;		\
241	__val;							\
242})
243
244#define SDPSTATS_HIST_GET(hist, hist_len, sum) ({ \
245	unsigned int __i;                                       \
246	for_each_possible_cpu(__i) {                            \
247		unsigned int __j;				\
248		u32 *h = per_cpu(sdpstats, __i).hist;		\
249		for (__j = 0; __j < hist_len; __j++) { 		\
250			sum[__j] += h[__j];			\
251		} \
252	} 							\
253})
254
255#define __sdpstats_seq_hist(seq, msg, hist, is_log) ({		\
256	u32 tmp_hist[SDPSTATS_MAX_HIST_SIZE];			\
257	int hist_len = ARRAY_SIZE(__get_cpu_var(sdpstats).hist);\
258	memset(tmp_hist, 0, sizeof(tmp_hist));			\
259	SDPSTATS_HIST_GET(hist, hist_len, tmp_hist);	\
260	sdpstats_seq_hist(seq, msg, tmp_hist, hist_len, is_log);\
261})
262
263static int sdpstats_seq_show(struct seq_file *seq, void *v)
264{
265	int i;
266
267	seq_printf(seq, "SDP statistics:\n");
268
269	__sdpstats_seq_hist(seq, "sendmsg_seglen", sendmsg_seglen, 1);
270	__sdpstats_seq_hist(seq, "send_size", send_size, 1);
271	__sdpstats_seq_hist(seq, "credits_before_update",
272		credits_before_update, 0);
273
274	seq_printf(seq, "sdp_sendmsg() calls\t\t: %d\n",
275		SDPSTATS_COUNTER_GET(sendmsg));
276	seq_printf(seq, "bcopy segments     \t\t: %d\n",
277		SDPSTATS_COUNTER_GET(sendmsg_bcopy_segment));
278	seq_printf(seq, "bzcopy segments    \t\t: %d\n",
279		SDPSTATS_COUNTER_GET(sendmsg_bzcopy_segment));
280	seq_printf(seq, "zcopy segments    \t\t: %d\n",
281		SDPSTATS_COUNTER_GET(sendmsg_zcopy_segment));
282	seq_printf(seq, "post_send_credits  \t\t: %d\n",
283		SDPSTATS_COUNTER_GET(post_send_credits));
284	seq_printf(seq, "memcpy_count       \t\t: %u\n",
285		SDPSTATS_COUNTER_GET(memcpy_count));
286
287        for (i = 0; i < ARRAY_SIZE(__get_cpu_var(sdpstats).post_send); i++) {
288                if (mid2str(i)) {
289                        seq_printf(seq, "post_send %-20s\t: %d\n",
290                                        mid2str(i),
291					SDPSTATS_COUNTER_GET(post_send[i]));
292                }
293        }
294
295	seq_printf(seq, "\n");
296	seq_printf(seq, "post_recv         \t\t: %d\n",
297		SDPSTATS_COUNTER_GET(post_recv));
298	seq_printf(seq, "BZCopy poll miss  \t\t: %d\n",
299		SDPSTATS_COUNTER_GET(bzcopy_poll_miss));
300	seq_printf(seq, "send_wait_for_mem \t\t: %d\n",
301		SDPSTATS_COUNTER_GET(send_wait_for_mem));
302	seq_printf(seq, "send_miss_no_credits\t\t: %d\n",
303		SDPSTATS_COUNTER_GET(send_miss_no_credits));
304
305	seq_printf(seq, "rx_poll_miss      \t\t: %d\n", SDPSTATS_COUNTER_GET(rx_poll_miss));
306	seq_printf(seq, "tx_poll_miss      \t\t: %d\n", SDPSTATS_COUNTER_GET(tx_poll_miss));
307	seq_printf(seq, "tx_poll_busy      \t\t: %d\n", SDPSTATS_COUNTER_GET(tx_poll_busy));
308	seq_printf(seq, "tx_poll_hit       \t\t: %d\n", SDPSTATS_COUNTER_GET(tx_poll_hit));
309
310	seq_printf(seq, "CQ stats:\n");
311	seq_printf(seq, "- RX interrupts\t\t: %d\n", SDPSTATS_COUNTER_GET(rx_int_count));
312	seq_printf(seq, "- TX interrupts\t\t: %d\n", SDPSTATS_COUNTER_GET(tx_int_count));
313
314	seq_printf(seq, "ZCopy stats:\n");
315	seq_printf(seq, "- TX timeout\t\t: %d\n", SDPSTATS_COUNTER_GET(zcopy_tx_timeout));
316	seq_printf(seq, "- TX cross send\t\t: %d\n", SDPSTATS_COUNTER_GET(zcopy_cross_send));
317	seq_printf(seq, "- TX aborted by peer\t: %d\n", SDPSTATS_COUNTER_GET(zcopy_tx_aborted));
318	seq_printf(seq, "- TX error\t\t: %d\n", SDPSTATS_COUNTER_GET(zcopy_tx_error));
319	return 0;
320}
321
322static ssize_t sdpstats_write(struct file *file, const char __user *buf,
323			    size_t count, loff_t *offs)
324{
325	int i;
326
327	for_each_possible_cpu(i)
328		memset(&per_cpu(sdpstats, i), 0, sizeof(struct sdpstats));
329	printk(KERN_WARNING "Cleared sdp statistics\n");
330
331	return count;
332}
333
334static int sdpstats_seq_open(struct inode *inode, struct file *file)
335{
336	return single_open(file, sdpstats_seq_show, NULL);
337}
338
339static struct file_operations sdpstats_fops = {
340	.owner		= THIS_MODULE,
341	.open		= sdpstats_seq_open,
342	.read		= seq_read,
343	.write		= sdpstats_write,
344	.llseek		= seq_lseek,
345	.release	= single_release,
346};
347
348#endif
349
350#ifdef SDP_PROFILING
351struct sdpprf_log sdpprf_log[SDPPRF_LOG_SIZE];
352int sdpprf_log_count;
353
354static unsigned long long start_t;
355
356static int sdpprf_show(struct seq_file *m, void *v)
357{
358	struct sdpprf_log *l = v;
359	unsigned long nsec_rem, t;
360
361	if (!sdpprf_log_count) {
362		seq_printf(m, "No performance logs\n");
363		goto out;
364	}
365
366	t = l->time - start_t;
367	nsec_rem = do_div(t, 1000000000);
368
369	seq_printf(m, "%-6d: [%5lu.%06lu] %-50s - [%d{%d} %d:%d] "
370			"mb: %p %s:%d\n",
371			l->idx, (unsigned long)t, nsec_rem/1000,
372			l->msg, l->pid, l->cpu, l->sk_num, l->sk_dport,
373			l->mb, l->func, l->line);
374out:
375	return 0;
376}
377
378static void *sdpprf_start(struct seq_file *p, loff_t *pos)
379{
380	int idx = *pos;
381
382	if (!*pos) {
383		if (!sdpprf_log_count)
384			return SEQ_START_TOKEN;
385	}
386
387	if (*pos >= MIN(sdpprf_log_count, SDPPRF_LOG_SIZE - 1))
388		return NULL;
389
390	if (sdpprf_log_count >= SDPPRF_LOG_SIZE - 1) {
391		int off = sdpprf_log_count & (SDPPRF_LOG_SIZE - 1);
392		idx = (idx + off) & (SDPPRF_LOG_SIZE - 1);
393
394	}
395
396	if (!start_t)
397		start_t = sdpprf_log[idx].time;
398	return &sdpprf_log[idx];
399}
400
401static void *sdpprf_next(struct seq_file *p, void *v, loff_t *pos)
402{
403	struct sdpprf_log *l = v;
404
405	if (++*pos >= MIN(sdpprf_log_count, SDPPRF_LOG_SIZE - 1))
406		return NULL;
407
408	++l;
409	if (l - &sdpprf_log[0] >= SDPPRF_LOG_SIZE - 1)
410		return &sdpprf_log[0];
411
412	return l;
413}
414
415static void sdpprf_stop(struct seq_file *p, void *v)
416{
417}
418
419static struct seq_operations sdpprf_ops = {
420	.start = sdpprf_start,
421	.stop = sdpprf_stop,
422	.next = sdpprf_next,
423	.show = sdpprf_show,
424};
425
426static int sdpprf_open(struct inode *inode, struct file *file)
427{
428	int res;
429
430	res = seq_open(file, &sdpprf_ops);
431
432	return res;
433}
434
435static ssize_t sdpprf_write(struct file *file, const char __user *buf,
436			    size_t count, loff_t *offs)
437{
438	sdpprf_log_count = 0;
439	printk(KERN_INFO "Cleared sdpprf statistics\n");
440
441	return count;
442}
443
444static struct file_operations sdpprf_fops = {
445	.open           = sdpprf_open,
446	.read           = seq_read,
447	.llseek         = seq_lseek,
448	.release        = seq_release,
449	.write		= sdpprf_write,
450};
451#endif /* SDP_PROFILING */
452
453int __init sdp_proc_init(void)
454{
455	struct proc_dir_entry *p = NULL;
456#ifdef SDPSTATS_ON
457	struct proc_dir_entry *stats = NULL;
458#endif
459#ifdef SDP_PROFILING
460	struct proc_dir_entry *prof = NULL;
461#endif
462
463	sdp_seq_afinfo.seq_fops->owner         = sdp_seq_afinfo.owner;
464	sdp_seq_afinfo.seq_fops->open          = sdp_seq_open;
465	sdp_seq_afinfo.seq_fops->read          = seq_read;
466	sdp_seq_afinfo.seq_fops->llseek        = seq_lseek;
467	sdp_seq_afinfo.seq_fops->release       = seq_release_private;
468
469	p = proc_net_fops_create(&init_net, sdp_seq_afinfo.name, S_IRUGO,
470				 sdp_seq_afinfo.seq_fops);
471	if (p)
472		p->data = &sdp_seq_afinfo;
473	else
474		goto no_mem;
475
476#ifdef SDPSTATS_ON
477
478	stats = proc_net_fops_create(&init_net, PROC_SDP_STATS,
479			S_IRUGO | S_IWUGO, &sdpstats_fops);
480	if (!stats)
481		goto no_mem_stats;
482
483#endif
484
485#ifdef SDP_PROFILING
486	prof = proc_net_fops_create(&init_net, PROC_SDP_PERF,
487			S_IRUGO | S_IWUGO, &sdpprf_fops);
488	if (!prof)
489		goto no_mem_prof;
490#endif
491
492	return 0;
493
494#ifdef SDP_PROFILING
495no_mem_prof:
496#endif
497
498#ifdef SDPSTATS_ON
499	proc_net_remove(&init_net, PROC_SDP_STATS);
500
501no_mem_stats:
502#endif
503	proc_net_remove(&init_net, sdp_seq_afinfo.name);
504
505no_mem:
506	return -ENOMEM;
507}
508
509void sdp_proc_unregister(void)
510{
511	proc_net_remove(&init_net, sdp_seq_afinfo.name);
512	memset(sdp_seq_afinfo.seq_fops, 0, sizeof(*sdp_seq_afinfo.seq_fops));
513
514#ifdef SDPSTATS_ON
515	proc_net_remove(&init_net, PROC_SDP_STATS);
516#endif
517#ifdef SDP_PROFILING
518	proc_net_remove(&init_net, PROC_SDP_PERF);
519#endif
520}
521
522#else /* CONFIG_PROC_FS */
523
524int __init sdp_proc_init(void)
525{
526	return 0;
527}
528
529void sdp_proc_unregister(void)
530{
531
532}
533#endif /* CONFIG_PROC_FS */
534