1219820Sjeff/*
2219820Sjeff * Copyright (c) 2008 Mellanox Technologies Ltd.  All rights reserved.
3219820Sjeff *
4219820Sjeff * This software is available to you under a choice of one of two
5219820Sjeff * licenses.  You may choose to be licensed under the terms of the GNU
6219820Sjeff * General Public License (GPL) Version 2, available from the file
7219820Sjeff * COPYING in the main directory of this source tree, or the
8219820Sjeff * OpenIB.org BSD license below:
9219820Sjeff *
10219820Sjeff *     Redistribution and use in source and binary forms, with or
11219820Sjeff *     without modification, are permitted provided that the following
12219820Sjeff *     conditions are met:
13219820Sjeff *
14219820Sjeff *      - Redistributions of source code must retain the above
15219820Sjeff *        copyright notice, this list of conditions and the following
16219820Sjeff *        disclaimer.
17219820Sjeff *
18219820Sjeff *      - Redistributions in binary form must reproduce the above
19219820Sjeff *        copyright notice, this list of conditions and the following
20219820Sjeff *        disclaimer in the documentation and/or other materials
21219820Sjeff *        provided with the distribution.
22219820Sjeff *
23219820Sjeff * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
24219820Sjeff * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
25219820Sjeff * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
26219820Sjeff * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
27219820Sjeff * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
28219820Sjeff * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
29219820Sjeff * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
30219820Sjeff * SOFTWARE.
31219820Sjeff */
32219820Sjeff
33219820Sjeff#include <linux/proc_fs.h>
34219820Sjeff#include <rdma/sdp_socket.h>
35219820Sjeff#include "sdp.h"
36219820Sjeff
37219820Sjeff#ifdef CONFIG_PROC_FS
38219820Sjeff
39219820Sjeff#define PROC_SDP_STATS "sdpstats"
40219820Sjeff#define PROC_SDP_PERF "sdpprf"
41219820Sjeff
42219820Sjeff/* just like TCP fs */
43219820Sjeffstruct sdp_seq_afinfo {
44219820Sjeff	struct module           *owner;
45219820Sjeff	char                    *name;
46219820Sjeff	sa_family_t             family;
47219820Sjeff	int                     (*seq_show) (struct seq_file *m, void *v);
48219820Sjeff	struct file_operations  *seq_fops;
49219820Sjeff};
50219820Sjeff
51219820Sjeffstruct sdp_iter_state {
52219820Sjeff	sa_family_t             family;
53219820Sjeff	int                     num;
54219820Sjeff	struct seq_operations   seq_ops;
55219820Sjeff};
56219820Sjeff
57219820Sjeffstatic void *sdp_get_idx(struct seq_file *seq, loff_t pos)
58219820Sjeff{
59219820Sjeff	int i = 0;
60219820Sjeff	struct sdp_sock *ssk;
61219820Sjeff
62219820Sjeff	if (!list_empty(&sock_list))
63219820Sjeff		list_for_each_entry(ssk, &sock_list, sock_list) {
64219820Sjeff			if (i == pos)
65219820Sjeff				return ssk;
66219820Sjeff			i++;
67219820Sjeff		}
68219820Sjeff
69219820Sjeff	return NULL;
70219820Sjeff}
71219820Sjeff
72219820Sjeffstatic void *sdp_seq_start(struct seq_file *seq, loff_t *pos)
73219820Sjeff{
74219820Sjeff	void *start = NULL;
75219820Sjeff	struct sdp_iter_state *st = seq->private;
76219820Sjeff
77219820Sjeff	st->num = 0;
78219820Sjeff
79219820Sjeff	if (!*pos)
80219820Sjeff		return SEQ_START_TOKEN;
81219820Sjeff
82219820Sjeff	spin_lock_irq(&sock_list_lock);
83219820Sjeff	start = sdp_get_idx(seq, *pos - 1);
84219820Sjeff	if (start)
85219820Sjeff		sock_hold((struct socket *)start, SOCK_REF_SEQ);
86219820Sjeff	spin_unlock_irq(&sock_list_lock);
87219820Sjeff
88219820Sjeff	return start;
89219820Sjeff}
90219820Sjeff
91219820Sjeffstatic void *sdp_seq_next(struct seq_file *seq, void *v, loff_t *pos)
92219820Sjeff{
93219820Sjeff	struct sdp_iter_state *st = seq->private;
94219820Sjeff	void *next = NULL;
95219820Sjeff
96219820Sjeff	spin_lock_irq(&sock_list_lock);
97219820Sjeff	if (v == SEQ_START_TOKEN)
98219820Sjeff		next = sdp_get_idx(seq, 0);
99219820Sjeff	else
100219820Sjeff		next = sdp_get_idx(seq, *pos);
101219820Sjeff	if (next)
102219820Sjeff		sock_hold((struct socket *)next, SOCK_REF_SEQ);
103219820Sjeff	spin_unlock_irq(&sock_list_lock);
104219820Sjeff
105219820Sjeff	*pos += 1;
106219820Sjeff	st->num++;
107219820Sjeff
108219820Sjeff	return next;
109219820Sjeff}
110219820Sjeff
111219820Sjeffstatic void sdp_seq_stop(struct seq_file *seq, void *v)
112219820Sjeff{
113219820Sjeff}
114219820Sjeff
115219820Sjeff#define TMPSZ 150
116219820Sjeff
117219820Sjeffstatic int sdp_seq_show(struct seq_file *seq, void *v)
118219820Sjeff{
119219820Sjeff	struct sdp_iter_state *st;
120219820Sjeff	struct socket *sk = v;
121219820Sjeff	char tmpbuf[TMPSZ + 1];
122219820Sjeff	unsigned int dest;
123219820Sjeff	unsigned int src;
124219820Sjeff	int uid;
125219820Sjeff	unsigned long inode;
126219820Sjeff	__u16 destp;
127219820Sjeff	__u16 srcp;
128219820Sjeff	__u32 rx_queue, tx_queue;
129219820Sjeff
130219820Sjeff	if (v == SEQ_START_TOKEN) {
131219820Sjeff		seq_printf(seq, "%-*s\n", TMPSZ - 1,
132219820Sjeff				"  sl  local_address rem_address        "
133219820Sjeff				"uid inode   rx_queue tx_queue state");
134219820Sjeff		goto out;
135219820Sjeff	}
136219820Sjeff
137219820Sjeff	st = seq->private;
138219820Sjeff
139219820Sjeff	dest = inet_sk(sk)->daddr;
140219820Sjeff	src = inet_sk(sk)->rcv_saddr;
141219820Sjeff	destp = ntohs(inet_sk(sk)->dport);
142219820Sjeff	srcp = ntohs(inet_sk(sk)->sport);
143219820Sjeff	uid = sock_i_uid(sk);
144219820Sjeff	inode = sock_i_ino(sk);
145219820Sjeff	rx_queue = rcv_nxt(sdp_sk(sk)) - sdp_sk(sk)->copied_seq;
146219820Sjeff	tx_queue = sdp_sk(sk)->write_seq - sdp_sk(sk)->tx_ring.una_seq;
147219820Sjeff
148219820Sjeff	sprintf(tmpbuf, "%4d: %08X:%04X %08X:%04X %5d %lu	%08X:%08X %X",
149219820Sjeff		st->num, src, srcp, dest, destp, uid, inode,
150219820Sjeff		rx_queue, tx_queue, sk->sk_state);
151219820Sjeff
152219820Sjeff	seq_printf(seq, "%-*s\n", TMPSZ - 1, tmpbuf);
153219820Sjeff
154219820Sjeff	sock_put(sk, SOCK_REF_SEQ);
155219820Sjeffout:
156219820Sjeff	return 0;
157219820Sjeff}
158219820Sjeff
159219820Sjeffstatic int sdp_seq_open(struct inode *inode, struct file *file)
160219820Sjeff{
161219820Sjeff	struct sdp_seq_afinfo *afinfo = PDE(inode)->data;
162219820Sjeff	struct seq_file *seq;
163219820Sjeff	struct sdp_iter_state *s;
164219820Sjeff	int rc;
165219820Sjeff
166219820Sjeff	if (unlikely(afinfo == NULL))
167219820Sjeff		return -EINVAL;
168219820Sjeff
169219820Sjeff/* Workaround bogus warning by memtrack */
170219820Sjeff#define _kzalloc(size,flags) kzalloc(size,flags)
171219820Sjeff#undef kzalloc
172219820Sjeff	s = kzalloc(sizeof(*s), GFP_KERNEL);
173219820Sjeff#define kzalloc(s,f) _kzalloc(s,f)
174219820Sjeff	if (!s)
175219820Sjeff		return -ENOMEM;
176219820Sjeff	s->family               = afinfo->family;
177219820Sjeff	s->seq_ops.start        = sdp_seq_start;
178219820Sjeff	s->seq_ops.next         = sdp_seq_next;
179219820Sjeff	s->seq_ops.show         = afinfo->seq_show;
180219820Sjeff	s->seq_ops.stop         = sdp_seq_stop;
181219820Sjeff
182219820Sjeff	rc = seq_open(file, &s->seq_ops);
183219820Sjeff	if (rc)
184219820Sjeff		goto out_kfree;
185219820Sjeff	seq          = file->private_data;
186219820Sjeff	seq->private = s;
187219820Sjeffout:
188219820Sjeff	return rc;
189219820Sjeffout_kfree:
190219820Sjeff	kfree(s);
191219820Sjeff	goto out;
192219820Sjeff}
193219820Sjeff
194219820Sjeff
195219820Sjeffstatic struct file_operations sdp_seq_fops;
196219820Sjeffstatic struct sdp_seq_afinfo sdp_seq_afinfo = {
197219820Sjeff	.owner          = THIS_MODULE,
198219820Sjeff	.name           = "sdp",
199219820Sjeff	.family         = AF_INET_SDP,
200219820Sjeff	.seq_show       = sdp_seq_show,
201219820Sjeff	.seq_fops       = &sdp_seq_fops,
202219820Sjeff};
203219820Sjeff
204219820Sjeff#ifdef SDPSTATS_ON
205219820SjeffDEFINE_PER_CPU(struct sdpstats, sdpstats);
206219820Sjeff
207219820Sjeffstatic void sdpstats_seq_hist(struct seq_file *seq, char *str, u32 *h, int n,
208219820Sjeff		int is_log)
209219820Sjeff{
210219820Sjeff	int i;
211219820Sjeff	u32 max = 0;
212219820Sjeff
213219820Sjeff	seq_printf(seq, "%s:\n", str);
214219820Sjeff
215219820Sjeff	for (i = 0; i < n; i++) {
216219820Sjeff		if (h[i] > max)
217219820Sjeff			max = h[i];
218219820Sjeff	}
219219820Sjeff
220219820Sjeff	if (max == 0) {
221219820Sjeff		seq_printf(seq, " - all values are 0\n");
222219820Sjeff		return;
223219820Sjeff	}
224219820Sjeff
225219820Sjeff	for (i = 0; i < n; i++) {
226219820Sjeff		char s[51];
227219820Sjeff		int j = 50 * h[i] / max;
228219820Sjeff		int val = is_log ? (i == n-1 ? 0 : 1<<i) : i;
229219820Sjeff		memset(s, '*', j);
230219820Sjeff		s[j] = '\0';
231219820Sjeff
232219820Sjeff		seq_printf(seq, "%10d | %-50s - %d\n", val, s, h[i]);
233219820Sjeff	}
234219820Sjeff}
235219820Sjeff
236219820Sjeff#define SDPSTATS_COUNTER_GET(var) ({ \
237219820Sjeff	u32 __val = 0;						\
238219820Sjeff	unsigned int __i;                                       \
239219820Sjeff	for_each_possible_cpu(__i)                              \
240219820Sjeff		__val += per_cpu(sdpstats, __i).var;		\
241219820Sjeff	__val;							\
242219820Sjeff})
243219820Sjeff
244219820Sjeff#define SDPSTATS_HIST_GET(hist, hist_len, sum) ({ \
245219820Sjeff	unsigned int __i;                                       \
246219820Sjeff	for_each_possible_cpu(__i) {                            \
247219820Sjeff		unsigned int __j;				\
248219820Sjeff		u32 *h = per_cpu(sdpstats, __i).hist;		\
249219820Sjeff		for (__j = 0; __j < hist_len; __j++) { 		\
250219820Sjeff			sum[__j] += h[__j];			\
251219820Sjeff		} \
252219820Sjeff	} 							\
253219820Sjeff})
254219820Sjeff
255219820Sjeff#define __sdpstats_seq_hist(seq, msg, hist, is_log) ({		\
256219820Sjeff	u32 tmp_hist[SDPSTATS_MAX_HIST_SIZE];			\
257219820Sjeff	int hist_len = ARRAY_SIZE(__get_cpu_var(sdpstats).hist);\
258219820Sjeff	memset(tmp_hist, 0, sizeof(tmp_hist));			\
259219820Sjeff	SDPSTATS_HIST_GET(hist, hist_len, tmp_hist);	\
260219820Sjeff	sdpstats_seq_hist(seq, msg, tmp_hist, hist_len, is_log);\
261219820Sjeff})
262219820Sjeff
263219820Sjeffstatic int sdpstats_seq_show(struct seq_file *seq, void *v)
264219820Sjeff{
265219820Sjeff	int i;
266219820Sjeff
267219820Sjeff	seq_printf(seq, "SDP statistics:\n");
268219820Sjeff
269219820Sjeff	__sdpstats_seq_hist(seq, "sendmsg_seglen", sendmsg_seglen, 1);
270219820Sjeff	__sdpstats_seq_hist(seq, "send_size", send_size, 1);
271219820Sjeff	__sdpstats_seq_hist(seq, "credits_before_update",
272219820Sjeff		credits_before_update, 0);
273219820Sjeff
274219820Sjeff	seq_printf(seq, "sdp_sendmsg() calls\t\t: %d\n",
275219820Sjeff		SDPSTATS_COUNTER_GET(sendmsg));
276219820Sjeff	seq_printf(seq, "bcopy segments     \t\t: %d\n",
277219820Sjeff		SDPSTATS_COUNTER_GET(sendmsg_bcopy_segment));
278219820Sjeff	seq_printf(seq, "bzcopy segments    \t\t: %d\n",
279219820Sjeff		SDPSTATS_COUNTER_GET(sendmsg_bzcopy_segment));
280219820Sjeff	seq_printf(seq, "zcopy segments    \t\t: %d\n",
281219820Sjeff		SDPSTATS_COUNTER_GET(sendmsg_zcopy_segment));
282219820Sjeff	seq_printf(seq, "post_send_credits  \t\t: %d\n",
283219820Sjeff		SDPSTATS_COUNTER_GET(post_send_credits));
284219820Sjeff	seq_printf(seq, "memcpy_count       \t\t: %u\n",
285219820Sjeff		SDPSTATS_COUNTER_GET(memcpy_count));
286219820Sjeff
287219820Sjeff        for (i = 0; i < ARRAY_SIZE(__get_cpu_var(sdpstats).post_send); i++) {
288219820Sjeff                if (mid2str(i)) {
289219820Sjeff                        seq_printf(seq, "post_send %-20s\t: %d\n",
290219820Sjeff                                        mid2str(i),
291219820Sjeff					SDPSTATS_COUNTER_GET(post_send[i]));
292219820Sjeff                }
293219820Sjeff        }
294219820Sjeff
295219820Sjeff	seq_printf(seq, "\n");
296219820Sjeff	seq_printf(seq, "post_recv         \t\t: %d\n",
297219820Sjeff		SDPSTATS_COUNTER_GET(post_recv));
298219820Sjeff	seq_printf(seq, "BZCopy poll miss  \t\t: %d\n",
299219820Sjeff		SDPSTATS_COUNTER_GET(bzcopy_poll_miss));
300219820Sjeff	seq_printf(seq, "send_wait_for_mem \t\t: %d\n",
301219820Sjeff		SDPSTATS_COUNTER_GET(send_wait_for_mem));
302219820Sjeff	seq_printf(seq, "send_miss_no_credits\t\t: %d\n",
303219820Sjeff		SDPSTATS_COUNTER_GET(send_miss_no_credits));
304219820Sjeff
305219820Sjeff	seq_printf(seq, "rx_poll_miss      \t\t: %d\n", SDPSTATS_COUNTER_GET(rx_poll_miss));
306219820Sjeff	seq_printf(seq, "tx_poll_miss      \t\t: %d\n", SDPSTATS_COUNTER_GET(tx_poll_miss));
307219820Sjeff	seq_printf(seq, "tx_poll_busy      \t\t: %d\n", SDPSTATS_COUNTER_GET(tx_poll_busy));
308219820Sjeff	seq_printf(seq, "tx_poll_hit       \t\t: %d\n", SDPSTATS_COUNTER_GET(tx_poll_hit));
309219820Sjeff
310219820Sjeff	seq_printf(seq, "CQ stats:\n");
311219820Sjeff	seq_printf(seq, "- RX interrupts\t\t: %d\n", SDPSTATS_COUNTER_GET(rx_int_count));
312219820Sjeff	seq_printf(seq, "- TX interrupts\t\t: %d\n", SDPSTATS_COUNTER_GET(tx_int_count));
313219820Sjeff
314219820Sjeff	seq_printf(seq, "ZCopy stats:\n");
315219820Sjeff	seq_printf(seq, "- TX timeout\t\t: %d\n", SDPSTATS_COUNTER_GET(zcopy_tx_timeout));
316219820Sjeff	seq_printf(seq, "- TX cross send\t\t: %d\n", SDPSTATS_COUNTER_GET(zcopy_cross_send));
317219820Sjeff	seq_printf(seq, "- TX aborted by peer\t: %d\n", SDPSTATS_COUNTER_GET(zcopy_tx_aborted));
318219820Sjeff	seq_printf(seq, "- TX error\t\t: %d\n", SDPSTATS_COUNTER_GET(zcopy_tx_error));
319219820Sjeff	return 0;
320219820Sjeff}
321219820Sjeff
322219820Sjeffstatic ssize_t sdpstats_write(struct file *file, const char __user *buf,
323219820Sjeff			    size_t count, loff_t *offs)
324219820Sjeff{
325219820Sjeff	int i;
326219820Sjeff
327219820Sjeff	for_each_possible_cpu(i)
328219820Sjeff		memset(&per_cpu(sdpstats, i), 0, sizeof(struct sdpstats));
329219820Sjeff	printk(KERN_WARNING "Cleared sdp statistics\n");
330219820Sjeff
331219820Sjeff	return count;
332219820Sjeff}
333219820Sjeff
334219820Sjeffstatic int sdpstats_seq_open(struct inode *inode, struct file *file)
335219820Sjeff{
336219820Sjeff	return single_open(file, sdpstats_seq_show, NULL);
337219820Sjeff}
338219820Sjeff
339219820Sjeffstatic struct file_operations sdpstats_fops = {
340219820Sjeff	.owner		= THIS_MODULE,
341219820Sjeff	.open		= sdpstats_seq_open,
342219820Sjeff	.read		= seq_read,
343219820Sjeff	.write		= sdpstats_write,
344219820Sjeff	.llseek		= seq_lseek,
345219820Sjeff	.release	= single_release,
346219820Sjeff};
347219820Sjeff
348219820Sjeff#endif
349219820Sjeff
350219820Sjeff#ifdef SDP_PROFILING
351219820Sjeffstruct sdpprf_log sdpprf_log[SDPPRF_LOG_SIZE];
352219820Sjeffint sdpprf_log_count;
353219820Sjeff
354219820Sjeffstatic unsigned long long start_t;
355219820Sjeff
356219820Sjeffstatic int sdpprf_show(struct seq_file *m, void *v)
357219820Sjeff{
358219820Sjeff	struct sdpprf_log *l = v;
359219820Sjeff	unsigned long nsec_rem, t;
360219820Sjeff
361219820Sjeff	if (!sdpprf_log_count) {
362219820Sjeff		seq_printf(m, "No performance logs\n");
363219820Sjeff		goto out;
364219820Sjeff	}
365219820Sjeff
366219820Sjeff	t = l->time - start_t;
367219820Sjeff	nsec_rem = do_div(t, 1000000000);
368219820Sjeff
369219820Sjeff	seq_printf(m, "%-6d: [%5lu.%06lu] %-50s - [%d{%d} %d:%d] "
370219820Sjeff			"mb: %p %s:%d\n",
371219820Sjeff			l->idx, (unsigned long)t, nsec_rem/1000,
372219820Sjeff			l->msg, l->pid, l->cpu, l->sk_num, l->sk_dport,
373219820Sjeff			l->mb, l->func, l->line);
374219820Sjeffout:
375219820Sjeff	return 0;
376219820Sjeff}
377219820Sjeff
378219820Sjeffstatic void *sdpprf_start(struct seq_file *p, loff_t *pos)
379219820Sjeff{
380219820Sjeff	int idx = *pos;
381219820Sjeff
382219820Sjeff	if (!*pos) {
383219820Sjeff		if (!sdpprf_log_count)
384219820Sjeff			return SEQ_START_TOKEN;
385219820Sjeff	}
386219820Sjeff
387219820Sjeff	if (*pos >= MIN(sdpprf_log_count, SDPPRF_LOG_SIZE - 1))
388219820Sjeff		return NULL;
389219820Sjeff
390219820Sjeff	if (sdpprf_log_count >= SDPPRF_LOG_SIZE - 1) {
391219820Sjeff		int off = sdpprf_log_count & (SDPPRF_LOG_SIZE - 1);
392219820Sjeff		idx = (idx + off) & (SDPPRF_LOG_SIZE - 1);
393219820Sjeff
394219820Sjeff	}
395219820Sjeff
396219820Sjeff	if (!start_t)
397219820Sjeff		start_t = sdpprf_log[idx].time;
398219820Sjeff	return &sdpprf_log[idx];
399219820Sjeff}
400219820Sjeff
401219820Sjeffstatic void *sdpprf_next(struct seq_file *p, void *v, loff_t *pos)
402219820Sjeff{
403219820Sjeff	struct sdpprf_log *l = v;
404219820Sjeff
405219820Sjeff	if (++*pos >= MIN(sdpprf_log_count, SDPPRF_LOG_SIZE - 1))
406219820Sjeff		return NULL;
407219820Sjeff
408219820Sjeff	++l;
409219820Sjeff	if (l - &sdpprf_log[0] >= SDPPRF_LOG_SIZE - 1)
410219820Sjeff		return &sdpprf_log[0];
411219820Sjeff
412219820Sjeff	return l;
413219820Sjeff}
414219820Sjeff
415219820Sjeffstatic void sdpprf_stop(struct seq_file *p, void *v)
416219820Sjeff{
417219820Sjeff}
418219820Sjeff
419219820Sjeffstatic struct seq_operations sdpprf_ops = {
420219820Sjeff	.start = sdpprf_start,
421219820Sjeff	.stop = sdpprf_stop,
422219820Sjeff	.next = sdpprf_next,
423219820Sjeff	.show = sdpprf_show,
424219820Sjeff};
425219820Sjeff
426219820Sjeffstatic int sdpprf_open(struct inode *inode, struct file *file)
427219820Sjeff{
428219820Sjeff	int res;
429219820Sjeff
430219820Sjeff	res = seq_open(file, &sdpprf_ops);
431219820Sjeff
432219820Sjeff	return res;
433219820Sjeff}
434219820Sjeff
435219820Sjeffstatic ssize_t sdpprf_write(struct file *file, const char __user *buf,
436219820Sjeff			    size_t count, loff_t *offs)
437219820Sjeff{
438219820Sjeff	sdpprf_log_count = 0;
439219820Sjeff	printk(KERN_INFO "Cleared sdpprf statistics\n");
440219820Sjeff
441219820Sjeff	return count;
442219820Sjeff}
443219820Sjeff
444219820Sjeffstatic struct file_operations sdpprf_fops = {
445219820Sjeff	.open           = sdpprf_open,
446219820Sjeff	.read           = seq_read,
447219820Sjeff	.llseek         = seq_lseek,
448219820Sjeff	.release        = seq_release,
449219820Sjeff	.write		= sdpprf_write,
450219820Sjeff};
451219820Sjeff#endif /* SDP_PROFILING */
452219820Sjeff
453219820Sjeffint __init sdp_proc_init(void)
454219820Sjeff{
455219820Sjeff	struct proc_dir_entry *p = NULL;
456219820Sjeff#ifdef SDPSTATS_ON
457219820Sjeff	struct proc_dir_entry *stats = NULL;
458219820Sjeff#endif
459219820Sjeff#ifdef SDP_PROFILING
460219820Sjeff	struct proc_dir_entry *prof = NULL;
461219820Sjeff#endif
462219820Sjeff
463219820Sjeff	sdp_seq_afinfo.seq_fops->owner         = sdp_seq_afinfo.owner;
464219820Sjeff	sdp_seq_afinfo.seq_fops->open          = sdp_seq_open;
465219820Sjeff	sdp_seq_afinfo.seq_fops->read          = seq_read;
466219820Sjeff	sdp_seq_afinfo.seq_fops->llseek        = seq_lseek;
467219820Sjeff	sdp_seq_afinfo.seq_fops->release       = seq_release_private;
468219820Sjeff
469219820Sjeff	p = proc_net_fops_create(&init_net, sdp_seq_afinfo.name, S_IRUGO,
470219820Sjeff				 sdp_seq_afinfo.seq_fops);
471219820Sjeff	if (p)
472219820Sjeff		p->data = &sdp_seq_afinfo;
473219820Sjeff	else
474219820Sjeff		goto no_mem;
475219820Sjeff
476219820Sjeff#ifdef SDPSTATS_ON
477219820Sjeff
478219820Sjeff	stats = proc_net_fops_create(&init_net, PROC_SDP_STATS,
479219820Sjeff			S_IRUGO | S_IWUGO, &sdpstats_fops);
480219820Sjeff	if (!stats)
481219820Sjeff		goto no_mem_stats;
482219820Sjeff
483219820Sjeff#endif
484219820Sjeff
485219820Sjeff#ifdef SDP_PROFILING
486219820Sjeff	prof = proc_net_fops_create(&init_net, PROC_SDP_PERF,
487219820Sjeff			S_IRUGO | S_IWUGO, &sdpprf_fops);
488219820Sjeff	if (!prof)
489219820Sjeff		goto no_mem_prof;
490219820Sjeff#endif
491219820Sjeff
492219820Sjeff	return 0;
493219820Sjeff
494219820Sjeff#ifdef SDP_PROFILING
495219820Sjeffno_mem_prof:
496219820Sjeff#endif
497219820Sjeff
498219820Sjeff#ifdef SDPSTATS_ON
499219820Sjeff	proc_net_remove(&init_net, PROC_SDP_STATS);
500219820Sjeff
501219820Sjeffno_mem_stats:
502219820Sjeff#endif
503219820Sjeff	proc_net_remove(&init_net, sdp_seq_afinfo.name);
504219820Sjeff
505219820Sjeffno_mem:
506219820Sjeff	return -ENOMEM;
507219820Sjeff}
508219820Sjeff
509219820Sjeffvoid sdp_proc_unregister(void)
510219820Sjeff{
511219820Sjeff	proc_net_remove(&init_net, sdp_seq_afinfo.name);
512219820Sjeff	memset(sdp_seq_afinfo.seq_fops, 0, sizeof(*sdp_seq_afinfo.seq_fops));
513219820Sjeff
514219820Sjeff#ifdef SDPSTATS_ON
515219820Sjeff	proc_net_remove(&init_net, PROC_SDP_STATS);
516219820Sjeff#endif
517219820Sjeff#ifdef SDP_PROFILING
518219820Sjeff	proc_net_remove(&init_net, PROC_SDP_PERF);
519219820Sjeff#endif
520219820Sjeff}
521219820Sjeff
522219820Sjeff#else /* CONFIG_PROC_FS */
523219820Sjeff
524219820Sjeffint __init sdp_proc_init(void)
525219820Sjeff{
526219820Sjeff	return 0;
527219820Sjeff}
528219820Sjeff
529219820Sjeffvoid sdp_proc_unregister(void)
530219820Sjeff{
531219820Sjeff
532219820Sjeff}
533219820Sjeff#endif /* CONFIG_PROC_FS */
534