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