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 */
43 struct 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 
51 struct sdp_iter_state {
52 	sa_family_t             family;
53 	int                     num;
54 	struct seq_operations   seq_ops;
55 };
56 
57 static 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 
72 static 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 
91 static 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 
111 static void sdp_seq_stop(struct seq_file *seq, void *v)
112 {
113 }
114 
115 #define TMPSZ 150
116 
117 static 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);
155 out:
156 	return 0;
157 }
158 
159 static 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;
187 out:
188 	return rc;
189 out_kfree:
190 	kfree(s);
191 	goto out;
192 }
193 
194 
195 static struct file_operations sdp_seq_fops;
196 static 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
205 DEFINE_PER_CPU(struct sdpstats, sdpstats);
206 
207 static 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 
263 static 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 
322 static 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 
334 static int sdpstats_seq_open(struct inode *inode, struct file *file)
335 {
336 	return single_open(file, sdpstats_seq_show, NULL);
337 }
338 
339 static 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
351 struct sdpprf_log sdpprf_log[SDPPRF_LOG_SIZE];
352 int sdpprf_log_count;
353 
354 static unsigned long long start_t;
355 
356 static 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);
374 out:
375 	return 0;
376 }
377 
378 static 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 
401 static 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 
415 static void sdpprf_stop(struct seq_file *p, void *v)
416 {
417 }
418 
419 static struct seq_operations sdpprf_ops = {
420 	.start = sdpprf_start,
421 	.stop = sdpprf_stop,
422 	.next = sdpprf_next,
423 	.show = sdpprf_show,
424 };
425 
426 static 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 
435 static 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 
444 static 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 
453 int __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
495 no_mem_prof:
496 #endif
497 
498 #ifdef SDPSTATS_ON
499 	proc_net_remove(&init_net, PROC_SDP_STATS);
500 
501 no_mem_stats:
502 #endif
503 	proc_net_remove(&init_net, sdp_seq_afinfo.name);
504 
505 no_mem:
506 	return -ENOMEM;
507 }
508 
509 void 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 
524 int __init sdp_proc_init(void)
525 {
526 	return 0;
527 }
528 
529 void sdp_proc_unregister(void)
530 {
531 
532 }
533 #endif /* CONFIG_PROC_FS */
534