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