Whamcloud - gitweb
70f4059ee5a7948e35d13bb9e574f3acbda289e9
[fs/lustre-release.git] / lnet / libcfs / linux / linux-proc.c
1 /* -*- mode: c; c-basic-offset: 8; indent-tabs-mode: nil; -*-
2  * vim:expandtab:shiftwidth=8:tabstop=8:
3  *
4  * Copyright (C) 2001, 2002 Cluster File Systems, Inc.
5  *   Author: Zach Brown <zab@zabbo.net>
6  *   Author: Peter J. Braam <braam@clusterfs.com>
7  *   Author: Phil Schwan <phil@clusterfs.com>
8  *
9  *   This file is part of Lustre, http://www.lustre.org.
10  *
11  *   Lustre is free software; you can redistribute it and/or
12  *   modify it under the terms of version 2 of the GNU General Public
13  *   License as published by the Free Software Foundation.
14  *
15  *   Lustre is distributed in the hope that it will be useful,
16  *   but WITHOUT ANY WARRANTY; without even the implied warranty of
17  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18  *   GNU General Public License for more details.
19  *
20  *   You should have received a copy of the GNU General Public License
21  *   along with Lustre; if not, write to the Free Software
22  *   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
23  */
24
25 #ifndef EXPORT_SYMTAB
26 # define EXPORT_SYMTAB
27 #endif
28
29 #include <linux/config.h>
30 #include <linux/module.h>
31 #include <linux/kernel.h>
32 #include <linux/mm.h>
33 #include <linux/string.h>
34 #include <linux/stat.h>
35 #include <linux/errno.h>
36 #include <linux/smp_lock.h>
37 #include <linux/unistd.h>
38 #include <net/sock.h>
39 #include <linux/uio.h>
40
41 #include <asm/system.h>
42 #include <asm/uaccess.h>
43
44 #include <linux/fs.h>
45 #include <linux/file.h>
46 #include <linux/stat.h>
47 #include <linux/list.h>
48 #include <asm/uaccess.h>
49 #include <asm/segment.h>
50
51 #include <linux/proc_fs.h>
52 #include <linux/sysctl.h>
53
54 # define DEBUG_SUBSYSTEM S_PORTALS
55
56 #include <libcfs/kp30.h>
57 #include <asm/div64.h>
58 #include "tracefile.h"
59
60 static struct ctl_table_header *portals_table_header = NULL;
61 extern char debug_file_path[1024];
62 extern char portals_upcall[1024];
63
64 #define PSDEV_PORTALS  (0x100)
65 enum {
66         PSDEV_DEBUG = 1,          /* control debugging */
67         PSDEV_SUBSYSTEM_DEBUG,    /* control debugging */
68         PSDEV_PRINTK,             /* force all errors to console */
69         PSDEV_CONSOLE,            /* allow _any_ messages to console */
70         PSDEV_DEBUG_PATH,         /* crashdump log location */
71         PSDEV_DEBUG_DUMP_PATH,    /* crashdump tracelog location */
72         PSDEV_PORTALS_UPCALL,     /* User mode upcall script  */
73         PSDEV_PORTALS_MEMUSED,    /* bytes currently PORTAL_ALLOCated */
74 };
75
76 static struct ctl_table portals_table[] = {
77         {PSDEV_DEBUG, "debug", &portal_debug, sizeof(int), 0644, NULL,
78          &proc_dointvec},
79         {PSDEV_SUBSYSTEM_DEBUG, "subsystem_debug", &portal_subsystem_debug,
80          sizeof(int), 0644, NULL, &proc_dointvec},
81         {PSDEV_PRINTK, "printk", &portal_printk, sizeof(int), 0644, NULL,
82          &proc_dointvec},
83         {PSDEV_DEBUG_PATH, "debug_path", debug_file_path,
84          sizeof(debug_file_path), 0644, NULL, &proc_dostring, &sysctl_string},
85         {PSDEV_PORTALS_UPCALL, "upcall", portals_upcall,
86          sizeof(portals_upcall), 0644, NULL, &proc_dostring,
87          &sysctl_string},
88         {PSDEV_PORTALS_MEMUSED, "memused", (int *)&portal_kmemory.counter,
89          sizeof(int), 0644, NULL, &proc_dointvec},
90         {0}
91 };
92
93 static struct ctl_table top_table[2] = {
94         {PSDEV_PORTALS, "portals", NULL, 0, 0555, portals_table},
95         {0}
96 };
97
98
99 #ifdef PORTALS_PROFILING
100 /*
101  * profiling stuff.  we do this statically for now 'cause its simple,
102  * but we could do some tricks with elf sections to have this array
103  * automatically built.
104  */
105 #define def_prof(FOO) [PROF__##FOO] = {#FOO, 0, }
106
107 struct prof_ent prof_ents[] = {
108         def_prof(our_recvmsg),
109         def_prof(our_sendmsg),
110         def_prof(socknal_recv),
111         def_prof(lib_parse),
112         def_prof(conn_list_walk),
113         def_prof(memcpy),
114         def_prof(lib_finalize),
115         def_prof(pingcli_time),
116         def_prof(gmnal_send),
117         def_prof(gmnal_recv),
118 };
119
120 EXPORT_SYMBOL(prof_ents);
121
122 /*
123  * this function is as crazy as the proc filling api
124  * requires.
125  *
126  * buffer: page allocated for us to scribble in.  the
127  *  data returned to the user will be taken from here.
128  * *start: address of the pointer that will tell the 
129  *  caller where in buffer the data the user wants is.
130  * ppos: offset in the entire /proc file that the user
131  *  currently wants.
132  * wanted: the amount of data the user wants.
133  *
134  * while going, 'curpos' is the offset in the entire
135  * file where we currently are.  We only actually
136  * start filling buffer when we get to a place in
137  * the file that the user cares about.
138  *
139  * we take care to only sprintf when the user cares because
140  * we're holding a lock while we do this.
141  *
142  * we're smart and know that we generate fixed size lines.
143  * we only start writing to the buffer when the user cares.
144  * This is unpredictable because we don't snapshot the
145  * list between calls that are filling in a file from
146  * the list.  The list could change mid read and the
147  * output will look very weird indeed.  oh well.
148  */
149
150 static int prof_read_proc(char *buffer, char **start, off_t ppos, int wanted,
151                           int *eof, void *data)
152 {
153         int len = 0, i;
154         int curpos;
155         char *header = "Interval        Cycles_per (Starts Finishes Total)\n";
156         int header_len = strlen(header);
157         char *format = "%-15s %.12Ld (%.12d %.12d %.12Ld)";
158         int line_len = (15 + 1 + 12 + 2 + 12 + 1 + 12 + 1 + 12 + 1);
159
160         *start = buffer;
161
162         if (ppos < header_len) {
163                 int diff = MIN(header_len, wanted);
164                 memcpy(buffer, header + ppos, diff);
165                 len += diff;
166                 ppos += diff;
167         }
168
169         if (len >= wanted)
170                 goto out;
171
172         curpos = header_len;
173
174         for ( i = 0; i < MAX_PROFS ; i++) {
175                 int copied;
176                 struct prof_ent *pe = &prof_ents[i];
177                 long long cycles_per;
178                 /*
179                  * find the part of the array that the buffer wants
180                  */
181                 if (ppos >= (curpos + line_len))  {
182                         curpos += line_len;
183                         continue;
184                 }
185                 /* the clever caller split a line */
186                 if (ppos > curpos) {
187                         *start = buffer + (ppos - curpos);
188                 }
189
190                 if (pe->finishes == 0)
191                         cycles_per = 0;
192                 else
193                 {
194                         cycles_per = pe->total_cycles;
195                         do_div (cycles_per, pe->finishes);
196                 }
197
198                 copied = sprintf(buffer + len, format, pe->str, cycles_per,
199                                  pe->starts, pe->finishes, pe->total_cycles);
200
201                 len += copied;
202
203                 /* pad to line len, -1 for \n */
204                 if ((copied < line_len-1)) {
205                         int diff = (line_len-1) - copied;
206                         memset(buffer + len, ' ', diff);
207                         len += diff;
208                         copied += diff;
209                 }
210
211                 buffer[len++]= '\n';
212
213                 /* bail if we have enough */
214                 if (((buffer + len) - *start) >= wanted)
215                         break;
216
217                 curpos += line_len;
218         }
219
220         /* lameness */
221         if (i == MAX_PROFS)
222                 *eof = 1;
223  out:
224
225         return MIN(((buffer + len) - *start), wanted);
226 }
227
228 /*
229  * all kids love /proc :/
230  */
231 static unsigned char basedir[]="net/portals";
232 #endif /* PORTALS_PROFILING */
233
234 int insert_proc(void)
235 {
236         struct proc_dir_entry *ent;
237 #if PORTALS_PROFILING
238         unsigned char dir[128];
239
240         if (ARRAY_SIZE(prof_ents) != MAX_PROFS) {
241                 CERROR("profiling enum and array are out of sync.\n");
242                 return -1;
243         }
244
245         /*
246          * This is pretty lame.  assuming that failure just
247          * means that they already existed.
248          */
249         strcat(dir, basedir);
250         create_proc_entry(dir, S_IFDIR, 0);
251
252         strcat(dir, "/cycles");
253         ent = create_proc_entry(dir, 0, 0);
254         if (!ent) {
255                 CERROR("couldn't register %s?\n", dir);
256                 return -1;
257         }
258
259         ent->data = NULL;
260         ent->read_proc = prof_read_proc;
261 #endif /* PORTALS_PROFILING */
262
263 #ifdef CONFIG_SYSCTL
264         if (!portals_table_header)
265                 portals_table_header = register_sysctl_table(top_table, 0);
266 #endif
267
268         ent = create_proc_entry("sys/portals/dump_kernel", 0, NULL);
269         if (ent == NULL) {
270                 CERROR("couldn't register dump_kernel\n");
271                 return -1;
272         }
273         ent->write_proc = trace_dk;
274
275         ent = create_proc_entry("sys/portals/daemon_file", 0, NULL);
276         if (ent == NULL) {
277                 CERROR("couldn't register daemon_file\n");
278                 return -1;
279         }
280         ent->write_proc = trace_write_daemon_file;
281         ent->read_proc = trace_read_daemon_file;
282
283         ent = create_proc_entry("sys/portals/debug_mb", 0, NULL);
284         if (ent == NULL) {
285                 CERROR("couldn't register debug_mb\n");
286                 return -1;
287         }
288         ent->write_proc = trace_write_debug_mb;
289         ent->read_proc = trace_read_debug_mb;
290
291         return 0;
292 }
293
294 void remove_proc(void)
295 {
296 #if PORTALS_PROFILING
297         unsigned char dir[128];
298         int end;
299
300         dir[0]='\0';
301         strcat(dir, basedir);
302
303         end = strlen(dir);
304
305         strcat(dir, "/cycles");
306         remove_proc_entry(dir, 0);
307
308         dir[end] = '\0';
309         remove_proc_entry(dir, 0);
310 #endif /* PORTALS_PROFILING */
311
312         remove_proc_entry("sys/portals/dump_kernel", NULL);
313         remove_proc_entry("sys/portals/daemon_file", NULL);
314         remove_proc_entry("sys/portals/debug_mb", NULL);
315
316 #ifdef CONFIG_SYSCTL
317         if (portals_table_header)
318                 unregister_sysctl_table(portals_table_header);
319         portals_table_header = NULL;
320 #endif
321 }