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