Whamcloud - gitweb
LU-459 tests: quiet spurious console/test messages
[fs/lustre-release.git] / libcfs / libcfs / winnt / winnt-proc.c
1 /* -*- mode: c; c-basic-offset: 8; indent-tabs-mode: nil; -*-
2  * vim:expandtab:shiftwidth=8:tabstop=8:
3  *
4  * GPL HEADER START
5  *
6  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
7  *
8  * This program is free software; you can redistribute it and/or modify
9  * it under the terms of the GNU General Public License version 2 only,
10  * as published by the Free Software Foundation.
11  *
12  * This program is distributed in the hope that it will be useful, but
13  * WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15  * General Public License version 2 for more details (a copy is included
16  * in the LICENSE file that accompanied this code).
17  *
18  * You should have received a copy of the GNU General Public License
19  * version 2 along with this program; If not, see
20  * http://www.sun.com/software/products/lustre/docs/GPLv2.pdf
21  *
22  * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
23  * CA 95054 USA or visit www.sun.com if you need additional information or
24  * have any questions.
25  *
26  * GPL HEADER END
27  */
28 /*
29  * Copyright (c) 2008, 2010, Oracle and/or its affiliates. All rights reserved.
30  * Use is subject to license terms.
31  */
32 /*
33  * This file is part of Lustre, http://www.lustre.org/
34  * Lustre is a trademark of Sun Microsystems, Inc.
35  */
36
37
38 #ifndef EXPORT_SYMTAB
39 # define EXPORT_SYMTAB
40 #endif
41
42 # define DEBUG_SUBSYSTEM S_LNET
43
44 #include <libcfs/libcfs.h>
45 #include "tracefile.h"
46 #include <lustre_lib.h>
47
48 #ifdef __KERNEL__
49
50
51 /*
52  *  /proc emulator routines ...
53  */
54
55 /* The root node of the proc fs emulation: / */
56 cfs_proc_entry_t *              cfs_proc_root = NULL;
57
58 /* The root node of the proc fs emulation: /proc */
59 cfs_proc_entry_t *              cfs_proc_proc = NULL;
60
61 /* The fs sys directory: /proc/fs */
62 cfs_proc_entry_t *              cfs_proc_fs = NULL;
63
64 /* The sys root: /proc/sys */
65 cfs_proc_entry_t *              cfs_proc_sys = NULL;
66
67 /* The sys root: /proc/dev | to implement misc device */
68 cfs_proc_entry_t *              cfs_proc_dev = NULL;
69
70
71 /* SLAB object for cfs_proc_entry_t allocation */
72 cfs_mem_cache_t *               proc_entry_cache = NULL;
73
74 /* root node for sysctl table */
75 cfs_sysctl_table_header_t       root_table_header;
76
77 /* The global lock to protect all the access */
78
79 #if LIBCFS_PROCFS_SPINLOCK
80 cfs_spinlock_t                  proc_fs_lock;
81
82 #define INIT_PROCFS_LOCK()      cfs_spin_lock_init(&proc_fs_lock)
83 #define LOCK_PROCFS()           cfs_spin_lock(&proc_fs_lock)
84 #define UNLOCK_PROCFS()         cfs_spin_unlock(&proc_fs_lock)
85
86 #else
87
88 cfs_mutex_t                     proc_fs_lock;
89
90 #define INIT_PROCFS_LOCK()      cfs_init_mutex(&proc_fs_lock)
91 #define LOCK_PROCFS()           cfs_mutex_down(&proc_fs_lock)
92 #define UNLOCK_PROCFS()         cfs_mutex_up(&proc_fs_lock)
93
94 #endif
95
96 static ssize_t
97 proc_file_read(struct file * file, const char * buf, size_t nbytes, loff_t *ppos)
98 {
99     char    *page;
100     ssize_t retval=0;
101     int eof=0;
102     ssize_t n, count;
103     char    *start;
104     cfs_proc_entry_t * dp;
105
106     dp = (cfs_proc_entry_t  *) file->f_inode->i_priv;
107     if (!(page = (char*) cfs_alloc(CFS_PAGE_SIZE, 0)))
108         return -ENOMEM;
109
110     while ((nbytes > 0) && !eof) {
111
112         count = min_t(size_t, PROC_BLOCK_SIZE, nbytes);
113
114         start = NULL;
115         if (dp->read_proc) {
116             n = dp->read_proc( page, &start, (long)*ppos,
117                                count, &eof, dp->data);
118         } else
119             break;
120
121         if (!start) {
122             /*
123              * For proc files that are less than 4k
124              */
125             start = page + *ppos;
126             n -= (ssize_t)(*ppos);
127             if (n <= 0)
128                 break;
129             if (n > count)
130                 n = count;
131         }
132         if (n == 0)
133             break;  /* End of file */
134         if (n < 0) {
135             if (retval == 0)
136                 retval = n;
137             break;
138         }
139         
140         n -= cfs_copy_to_user((void *)buf, start, n);
141         if (n == 0) {
142             if (retval == 0)
143                 retval = -EFAULT;
144             break;
145         }
146
147         *ppos += n;
148         nbytes -= n;
149         buf += n;
150         retval += n;
151     }
152     cfs_free(page);
153
154     return retval;
155 }
156
157 static ssize_t
158 proc_file_write(struct file * file, const char * buffer,
159                 size_t count, loff_t *ppos)
160 {
161     cfs_proc_entry_t  * dp;
162     
163     dp = (cfs_proc_entry_t *) file->f_inode->i_priv;
164
165     if (!dp->write_proc)
166         return -EIO;
167
168     /* FIXME: does this routine need ppos?  probably... */
169     return dp->write_proc(file, buffer, count, dp->data);
170 }
171
172 struct file_operations proc_file_operations = {
173     /*owner*/       THIS_MODULE,
174     /*lseek:*/      NULL, //proc_file_lseek,
175     /*read:*/       proc_file_read,
176     /*write:*/      proc_file_write,
177     /*ioctl:*/      NULL,
178     /*open:*/       NULL,
179     /*release:*/    NULL
180 };
181
182 /* allocate proc entry block */
183
184 cfs_proc_entry_t *
185 proc_alloc_entry()
186 {
187     cfs_proc_entry_t * entry = NULL;
188
189     entry = cfs_mem_cache_alloc(proc_entry_cache, 0);
190     if (!entry) {
191         return NULL;
192     }
193
194     memset(entry, 0, sizeof(cfs_proc_entry_t));
195
196     entry->magic = CFS_PROC_ENTRY_MAGIC;
197     RtlInitializeSplayLinks(&(entry->s_link));
198     entry->proc_fops = &proc_file_operations;
199
200     return entry;
201 }
202
203 /* free the proc entry block */
204
205 void
206 proc_free_entry(cfs_proc_entry_t * entry)
207
208 {
209     ASSERT(entry->magic == CFS_PROC_ENTRY_MAGIC);
210
211     cfs_mem_cache_free(proc_entry_cache, entry);
212 }
213
214 /* dissect the path string for a given full proc path */
215
216 void
217 proc_dissect_name(
218     const char *path,
219     char **first,
220     int  *first_len,
221     char **remain
222     )
223 {
224     int i = 0, j = 0, len = 0;
225
226     *first = *remain = NULL;
227     *first_len = 0;
228
229     len = strlen(path);
230
231     while (i < len && (path[i] == '/')) i++;
232
233     if (i < len) {
234
235         *first = (char *)path + i;
236         while (i < len && (path[i] != '/')) i++;
237         *first_len = (int)(path + i - *first);
238
239         if (i + 1 < len) {
240             *remain = (char *)path + i + 1;
241         }
242     }
243 }
244
245 /* search the children entries of the parent entry */
246
247 cfs_proc_entry_t *
248 proc_search_splay (
249     cfs_proc_entry_t *  parent,
250     char *              name
251     )
252 {
253     cfs_proc_entry_t *  node;
254     PRTL_SPLAY_LINKS    link;
255
256     ASSERT(parent->magic == CFS_PROC_ENTRY_MAGIC);
257     ASSERT(cfs_is_flag_set(parent->flags, CFS_PROC_FLAG_DIRECTORY));
258
259     link = parent->root;
260
261     while (link) {
262
263         ANSI_STRING ename,nname;
264         long        result;
265
266         node = CONTAINING_RECORD(link, cfs_proc_entry_t, s_link);
267
268         ASSERT(node->magic == CFS_PROC_ENTRY_MAGIC);
269
270         /*  Compare the prefix in the tree with the full name */
271
272         RtlInitAnsiString(&ename, name);
273         RtlInitAnsiString(&nname, node->name);
274
275         result = RtlCompareString(&nname, &ename,TRUE);
276
277         if (result > 0) {
278
279             /*  The prefix is greater than the full name
280                 so we go down the left child          */
281
282             link = RtlLeftChild(link);
283
284         } else if (result < 0) {
285
286             /*  The prefix is less than the full name
287                 so we go down the right child      */
288
289             link = RtlRightChild(link);
290
291         } else {
292
293             /*  We got the entry in the splay tree and
294                 make it root node instead           */
295
296             parent->root = RtlSplay(link);
297
298             return node;
299         }
300
301         /* we need continue searching down the tree ... */
302     }
303
304     /*  There's no the exptected entry in the splay tree */
305
306     return NULL;
307 }
308
309 int
310 proc_insert_splay (
311     cfs_proc_entry_t * parent,
312     cfs_proc_entry_t * child
313     )
314 {
315     cfs_proc_entry_t * entry;
316
317     ASSERT(parent != NULL && child != NULL);
318     ASSERT(parent->magic == CFS_PROC_ENTRY_MAGIC);
319     ASSERT(child->magic == CFS_PROC_ENTRY_MAGIC);
320     ASSERT(cfs_is_flag_set(parent->flags, CFS_PROC_FLAG_DIRECTORY));
321
322     if (!parent->root) {
323         parent->root = &(child->s_link);
324     } else {
325         entry = CONTAINING_RECORD(parent->root, cfs_proc_entry_t, s_link);
326         while (TRUE) {
327             long        result;
328             ANSI_STRING ename, cname;
329
330             ASSERT(entry->magic == CFS_PROC_ENTRY_MAGIC);
331
332             RtlInitAnsiString(&ename, entry->name);
333             RtlInitAnsiString(&cname, child->name);
334
335             result = RtlCompareString(&ename, &cname,TRUE);
336
337             if (result == 0) {
338                 cfs_enter_debugger();
339                 if (entry == child) {
340                     break;
341                 }
342                 return FALSE;
343             }
344
345             if (result > 0) {
346                 if (RtlLeftChild(&entry->s_link) == NULL) {
347                     RtlInsertAsLeftChild(&entry->s_link, &child->s_link);
348                     break;
349                 } else {
350                     entry = CONTAINING_RECORD( RtlLeftChild(&entry->s_link),
351                                                cfs_proc_entry_t, s_link);
352                 }
353             } else {
354                 if (RtlRightChild(&entry->s_link) == NULL) {
355                     RtlInsertAsRightChild(&entry->s_link, &child->s_link);
356                     break;
357                 } else {
358                     entry = CONTAINING_RECORD( RtlRightChild(&entry->s_link),
359                                                cfs_proc_entry_t, s_link );
360                 }
361             }
362         }
363     }
364
365     cfs_set_flag(child->flags, CFS_PROC_FLAG_ATTACHED);
366     parent->nlink++;
367     child->parent = parent;
368
369     return TRUE;
370 }
371
372
373 /* remove a child entry from the splay tree */
374 int
375 proc_remove_splay (
376     cfs_proc_entry_t *  parent,
377     cfs_proc_entry_t *  child
378     )
379 {
380     cfs_proc_entry_t * entry = NULL;
381
382     ASSERT(parent != NULL && child != NULL);
383     ASSERT(parent->magic == CFS_PROC_ENTRY_MAGIC);
384     ASSERT(child->magic == CFS_PROC_ENTRY_MAGIC);
385     ASSERT(cfs_is_flag_set(parent->flags, CFS_PROC_FLAG_DIRECTORY));
386     ASSERT(cfs_is_flag_set(child->flags, CFS_PROC_FLAG_ATTACHED));
387     ASSERT(child->parent == parent);
388
389     entry = proc_search_splay(parent, child->name);
390
391     if (entry) {
392         ASSERT(entry == child);
393         parent->root = RtlDelete(&(entry->s_link));
394         parent->nlink--;
395     } else {
396         cfs_enter_debugger();
397         return FALSE;
398     }
399
400     return TRUE;
401 }
402
403
404 /* search a node inside the proc fs tree */
405
406 cfs_proc_entry_t *
407 proc_search_entry(
408     const char *        name,
409     cfs_proc_entry_t *  root
410     )
411 {
412     cfs_proc_entry_t *  entry;
413     cfs_proc_entry_t *  parent;
414     char *first, *remain;
415     int   flen;
416     char *ename = NULL;
417
418     parent = root;
419     entry = NULL;
420
421     ename = cfs_alloc(0x21, CFS_ALLOC_ZERO);
422
423     if (ename == NULL) {
424         goto errorout;
425     }
426
427 again:
428
429     /* dissect the file name string */
430     proc_dissect_name(name, &first, &flen, &remain);
431
432     if (first) {
433
434         if (flen >= 0x20) {
435             cfs_enter_debugger();
436             entry = NULL;
437             goto errorout;
438         }
439
440         memset(ename, 0, 0x20);
441         memcpy(ename, first, flen);
442
443         entry = proc_search_splay(parent, ename);
444
445         if (!entry) {
446             goto errorout;
447         }
448
449         if (remain) {
450             name = remain;
451             parent = entry;
452
453             goto again;
454         }
455     }
456
457 errorout:
458
459     if (ename) {
460         cfs_free(ename);
461     }
462
463     return entry;   
464 }
465
466 /* insert the path nodes to the proc fs tree */
467
468 cfs_proc_entry_t *
469 proc_insert_entry(
470     const char *        name,
471     cfs_proc_entry_t *  root
472     )
473 {
474     cfs_proc_entry_t *entry;
475     cfs_proc_entry_t *parent;
476     char *first, *remain;
477     int flen;
478     char ename[0x20];
479
480     parent = root;
481     entry = NULL;
482
483 again:
484
485     proc_dissect_name(name, &first, &flen, &remain);
486
487     if (first) {
488
489         if (flen >= 0x20) {
490             return NULL;
491         }
492
493         memset(ename, 0, 0x20);
494         memcpy(ename, first, flen);
495
496         entry = proc_search_splay(parent, ename);
497
498         if (!entry) {
499             entry = proc_alloc_entry();
500             memcpy(entry->name, ename, flen);
501
502             if (entry) {
503                 if(!proc_insert_splay(parent, entry)) {
504                     proc_free_entry(entry);
505                     entry = NULL;
506                 }
507             }
508         }
509
510         if (!entry) {
511             return NULL;
512         }
513
514         if (remain) {
515             entry->mode |= S_IFDIR | S_IRUGO | S_IXUGO;
516             cfs_set_flag(entry->flags, CFS_PROC_FLAG_DIRECTORY);
517             name = remain;
518             parent = entry;
519             goto again;
520         }
521     }
522
523     return entry;   
524 }
525
526 /* remove the path nodes from the proc fs tree */
527
528 void
529 proc_remove_entry(
530     const char *        name,
531     cfs_proc_entry_t *  root
532     )
533 {
534     cfs_proc_entry_t *entry;
535     char *first, *remain;
536     int  flen;
537     char ename[0x20];
538
539     entry  = NULL;
540
541     proc_dissect_name(name, &first, &flen, &remain);
542
543     if (first) {
544
545         memset(ename, 0, 0x20);
546         memcpy(ename, first, flen);
547
548         entry = proc_search_splay(root, ename);
549
550         if (entry) {
551
552             if (remain) {
553                 ASSERT(S_ISDIR(entry->mode));
554                 proc_remove_entry(remain, entry);
555             }
556
557             if (!entry->nlink) {
558                 proc_remove_splay(root, entry);
559                 proc_free_entry(entry);
560             }
561         }
562     } else {
563         cfs_enter_debugger();
564     }
565 }
566
567 /* create proc entry and insert it into the proc fs */
568
569 cfs_proc_entry_t *
570 create_proc_entry (
571     const char *        name,
572     mode_t              mode,
573     cfs_proc_entry_t *  parent
574     )
575 {
576     cfs_proc_entry_t *entry  = NULL;
577
578     if (S_ISDIR(mode)) {
579         if ((mode & S_IALLUGO) == 0)
580         mode |= S_IRUGO | S_IXUGO;
581     } else {
582         if ((mode & S_IFMT) == 0)
583             mode |= S_IFREG;
584         if ((mode & S_IALLUGO) == 0)
585             mode |= S_IRUGO;
586     }
587
588     LOCK_PROCFS();
589     ASSERT(NULL != cfs_proc_root);
590
591     if (!parent) {
592         if (name[0] == '/') {
593             parent = cfs_proc_root;
594         } else {
595             ASSERT(NULL != cfs_proc_proc);
596             parent = cfs_proc_proc;
597         }
598     }
599
600     entry = proc_search_entry(name, parent);
601
602     if (!entry) {
603         entry = proc_insert_entry(name, parent);
604         if (!entry) {
605             /* Failed to create/insert the splay node ... */
606             cfs_enter_debugger();
607             goto errorout;
608         }
609         /* Initializing entry ... */
610         entry->mode = mode;
611
612         if (S_ISDIR(mode)) {
613             cfs_set_flag(entry->flags, CFS_PROC_FLAG_DIRECTORY);
614         }
615     }
616
617 errorout:
618
619     UNLOCK_PROCFS();
620
621     return entry;
622 }
623
624
625 /* search the specified entry form the proc fs */
626
627 cfs_proc_entry_t *
628 search_proc_entry(
629     const char *        name,
630     cfs_proc_entry_t *  root
631     )
632 {
633     cfs_proc_entry_t * entry;
634
635     LOCK_PROCFS();
636     ASSERT(cfs_proc_root != NULL);
637     if (root == NULL) {
638         if (name[0] == '/') {
639             root = cfs_proc_root;
640         } else {
641             ASSERT(cfs_proc_proc != NULL);
642             root = cfs_proc_proc;
643         }
644     }
645     entry = proc_search_entry(name, root);
646     UNLOCK_PROCFS();
647
648     return entry;    
649 }
650
651 /* remove the entry from the proc fs */
652
653 void
654 remove_proc_entry(
655     const char *        name,
656     cfs_proc_entry_t *  parent
657     )
658 {
659     LOCK_PROCFS();
660     ASSERT(cfs_proc_root != NULL);
661     if (parent == NULL) {
662         if (name[0] == '/') {
663             parent = cfs_proc_root;
664         } else {
665             ASSERT(cfs_proc_proc != NULL);
666             parent = cfs_proc_proc;
667         }
668     }
669     proc_remove_entry(name, parent);
670     UNLOCK_PROCFS();
671 }
672
673
674 void proc_destroy_splay(cfs_proc_entry_t * entry)
675 {
676     cfs_proc_entry_t * node;
677
678     if (S_ISDIR(entry->mode)) {
679
680         while (entry->root) {
681             node = CONTAINING_RECORD(entry->root, cfs_proc_entry_t, s_link);
682             entry->root = RtlDelete(&(node->s_link));
683             proc_destroy_splay(node);
684         }
685     }
686
687     proc_free_entry(entry);
688 }
689
690 cfs_proc_entry_t *proc_symlink(
691     const char *name,
692         cfs_proc_entry_t *parent,
693     const char *dest
694     )
695 {
696     cfs_enter_debugger();
697     return NULL;
698 }
699
700 cfs_proc_entry_t *proc_mkdir(
701     const char *name,
702         cfs_proc_entry_t *parent)
703 {
704     return create_proc_entry((char *)name, S_IFDIR, parent);
705 }
706
707 void proc_destory_subtree(cfs_proc_entry_t *entry)
708 {
709     LOCK_PROCFS();
710     entry->root = NULL;
711     proc_destroy_splay(entry);
712     UNLOCK_PROCFS();
713 }
714
715 /* destory the whole proc fs tree */
716
717 void proc_destroy_fs()
718 {
719     LOCK_PROCFS();
720
721     if (cfs_proc_root) {
722         proc_destroy_splay(cfs_proc_root);
723     }
724
725     if (proc_entry_cache) {
726         cfs_mem_cache_destroy(proc_entry_cache);
727     }
728    
729     UNLOCK_PROCFS();
730 }
731
732 static char proc_item_path[512];
733
734
735 void proc_show_tree(cfs_proc_entry_t * node);
736 void proc_print_node(cfs_proc_entry_t * node)
737 {
738     if (node != cfs_proc_root) {
739         if (S_ISDIR(node->mode)) {
740             printk("%s/%s/\n", proc_item_path, node->name);
741         } else {
742             printk("%s/%s\n", proc_item_path, node->name);
743         }
744     } else {
745          printk("%s\n", node->name);
746     }
747
748     if (S_ISDIR(node->mode)) {
749         proc_show_tree(node);
750     }
751 }
752
753 void proc_show_child(PRTL_SPLAY_LINKS link)
754 {
755     cfs_proc_entry_t * entry  = NULL;
756
757     if (!link) {
758         return;
759     }
760
761     proc_show_child(link->LeftChild);
762     entry = CONTAINING_RECORD(link, cfs_proc_entry_t, s_link);
763     proc_print_node(entry);
764     proc_show_child(link->RightChild);
765 }
766
767 void proc_show_tree(cfs_proc_entry_t * node)
768 {
769     PRTL_SPLAY_LINKS link = NULL;
770     cfs_proc_entry_t * entry = NULL;
771     int i;
772
773     link = node->root;
774     i = strlen(proc_item_path);
775     ASSERT(S_ISDIR(node->mode));
776     if (node != cfs_proc_root) {
777         strcat(proc_item_path, "/");
778         strcat(proc_item_path, node->name);
779     }
780     proc_show_child(link);
781     proc_item_path[i] = 0;
782 }
783
784 void proc_print_splay()
785 {
786     printk("=================================================\n");
787     printk("Lustre virtual proc entries:\n");
788     printk("-------------------------------------------------\n");
789     LOCK_PROCFS();
790     proc_show_tree(cfs_proc_root);
791     UNLOCK_PROCFS();
792     printk("=================================================\n");
793 }
794
795
796 /* initilaize / build the proc fs tree */
797 int proc_init_fs()
798 {
799     cfs_proc_entry_t * root = NULL;
800
801     memset(&(root_table_header), 0, sizeof(struct ctl_table_header));
802     CFS_INIT_LIST_HEAD(&(root_table_header.ctl_entry));
803
804     INIT_PROCFS_LOCK();
805     proc_entry_cache = cfs_mem_cache_create(
806                             NULL,
807                             sizeof(cfs_proc_entry_t),
808                             0,
809                             0
810                             );
811
812     if (!proc_entry_cache) {
813         return (-ENOMEM);
814     }
815
816     root = proc_alloc_entry();
817     if (!root) {
818         proc_destroy_fs();
819         return (-ENOMEM);
820     }
821     root->magic = CFS_PROC_ENTRY_MAGIC;
822     root->flags = CFS_PROC_FLAG_DIRECTORY;
823     root->mode  = S_IFDIR | S_IRUGO | S_IXUGO;
824     root->nlink = 3; // root should never be deleted.
825     root->name[0]='/';
826     root->name[1]= 0;
827     cfs_proc_root = root;
828
829     cfs_proc_dev = create_proc_entry("dev", S_IFDIR, root);
830     if (!cfs_proc_dev) {
831         goto errorout;
832     }
833     cfs_proc_dev->nlink = 1;
834
835     cfs_proc_proc  = create_proc_entry("proc", S_IFDIR, root);
836     if (!cfs_proc_proc) {
837         goto errorout;
838     }
839     cfs_proc_proc->nlink = 1;
840
841     cfs_proc_fs = create_proc_entry("fs",  S_IFDIR, cfs_proc_proc);
842     if (!cfs_proc_fs) {
843         goto errorout;
844     }
845     cfs_proc_fs->nlink = 1;
846
847     cfs_proc_sys = create_proc_entry("sys",  S_IFDIR, cfs_proc_proc);
848     if (!cfs_proc_sys) {
849         goto errorout;
850     }
851     cfs_proc_sys->nlink = 1;
852
853   
854     return 0;
855
856 errorout:
857
858     proc_destroy_fs();
859     return (-ENOMEM);
860 }
861
862
863 static ssize_t do_rw_proc(int write, struct file * file, char * buf,
864               size_t count, loff_t *ppos)
865 {
866     int op;
867     cfs_proc_entry_t *de;
868     struct ctl_table *table;
869     size_t res;
870     ssize_t error;
871     
872     de = (cfs_proc_entry_t *) file->proc_dentry; 
873
874     if (!de || !de->data)
875         return -ENOTDIR;
876     table = (struct ctl_table *) de->data;
877     if (!table || !table->proc_handler)
878         return -ENOTDIR;
879     op = (write ? 002 : 004);
880
881     res = count;
882
883     /*
884      * FIXME: we need to pass on ppos to the handler.
885      */
886
887     error = (*table->proc_handler) (table, write, file, buf, &res);
888     if (error)
889         return error;
890     return res;
891 }
892
893 static ssize_t proc_readsys(struct file * file, char * buf,
894                 size_t count, loff_t *ppos)
895 {
896     return do_rw_proc(0, file, buf, count, ppos);
897 }
898
899 static ssize_t proc_writesys(struct file * file, const char * buf,
900                  size_t count, loff_t *ppos)
901 {
902     return do_rw_proc(1, file, (char *) buf, count, ppos);
903 }
904
905
906 struct file_operations proc_sys_file_operations = {
907     /*owner*/       THIS_MODULE,
908     /*lseek:*/      NULL,
909     /*read:*/       proc_readsys,
910     /*write:*/      proc_writesys,
911     /*ioctl:*/      NULL,
912     /*open:*/       NULL,
913     /*release:*/    NULL
914 };
915
916
917 /* Scan the sysctl entries in table and add them all into /proc */
918 void register_proc_table(cfs_sysctl_table_t * table, cfs_proc_entry_t * root)
919 {
920     cfs_proc_entry_t * de;
921     int len;
922     mode_t mode;
923     
924     for (; table->ctl_name; table++) {
925         /* Can't do anything without a proc name. */
926         if (!table->procname)
927             continue;
928         /* Maybe we can't do anything with it... */
929         if (!table->proc_handler && !table->child) {
930             printk(CFS_KERN_WARNING "SYSCTL: Can't register %s\n",
931                 table->procname);
932             continue;
933         }
934
935         len = strlen(table->procname);
936         mode = table->mode;
937
938         de = NULL;
939         if (table->proc_handler)
940             mode |= S_IFREG;
941         else {
942             de = search_proc_entry(table->procname, root);
943             if (de) {
944                 break;
945             }
946             /* If the subdir exists already, de is non-NULL */
947         }
948
949         if (!de) {
950
951             de = create_proc_entry((char *)table->procname, mode, root);
952             if (!de)
953                 continue;
954             de->data = (void *) table;
955             if (table->proc_handler) {
956                 de->proc_fops = &proc_sys_file_operations;
957             }
958         }
959         table->de = de;
960         if (de->mode & S_IFDIR)
961             register_proc_table(table->child, de);
962     }
963 }
964
965
966 /*
967  * Unregister a /proc sysctl table and any subdirectories.
968  */
969 void unregister_proc_table(cfs_sysctl_table_t * table, cfs_proc_entry_t *root)
970 {
971     cfs_proc_entry_t *de;
972     for (; table->ctl_name; table++) {
973         if (!(de = table->de))
974             continue;
975         if (de->mode & S_IFDIR) {
976             if (!table->child) {
977                 printk (CFS_KERN_ALERT "Help- malformed sysctl tree on free\n");
978                 continue;
979             }
980             unregister_proc_table(table->child, de);
981
982             /* Don't unregister directories which still have entries.. */
983             if (de->nlink)
984                 continue;
985         }
986
987         /* Don't unregister proc entries that are still being used.. */
988         if (de->nlink)
989             continue;
990
991         table->de = NULL;
992         remove_proc_entry((char *)table->procname, root);
993     }
994 }
995
996 /* The generic string strategy routine: */
997 int sysctl_string(cfs_sysctl_table_t *table, int *name, int nlen,
998           void *oldval, size_t *oldlenp,
999           void *newval, size_t newlen, void **context)
1000 {
1001     int l, len;
1002     
1003     if (!table->data || !table->maxlen) 
1004         return -ENOTDIR;
1005     
1006     if (oldval && oldlenp) {
1007         if(get_user(len, oldlenp))
1008             return -EFAULT;
1009         if (len) {
1010             l = strlen(table->data);
1011             if (len > l) len = l;
1012             if (len >= table->maxlen)
1013                 len = table->maxlen;
1014             if(cfs_copy_to_user(oldval, table->data, len))
1015                 return -EFAULT;
1016             if(put_user(0, ((char *) oldval) + len))
1017                 return -EFAULT;
1018             if(put_user(len, oldlenp))
1019                 return -EFAULT;
1020         }
1021     }
1022     if (newval && newlen) {
1023         len = newlen;
1024         if (len > table->maxlen)
1025             len = table->maxlen;
1026         if(cfs_copy_from_user(table->data, newval, len))
1027             return -EFAULT;
1028         if (len == table->maxlen)
1029             len--;
1030         ((char *) table->data)[len] = 0;
1031     }
1032     return 0;
1033 }
1034
1035 /**
1036  * simple_strtoul - convert a string to an unsigned long
1037  * @cp: The start of the string
1038  * @endp: A pointer to the end of the parsed string will be placed here
1039  * @base: The number base to use
1040  */
1041 unsigned long simple_strtoul(const char *cp,char **endp,unsigned int base)
1042 {
1043     unsigned long result = 0, value;
1044
1045     if (!base) {
1046         base = 10;
1047         if (*cp == '0') {
1048             base = 8;
1049             cp++;
1050             if ((*cp == 'x') && cfs_isxdigit(cp[1])) {
1051                 cp++;
1052                 base = 16;
1053             }
1054         }
1055     }
1056     while (cfs_isxdigit(*cp) &&
1057            (value = cfs_isdigit(*cp) ? *cp-'0' : toupper(*cp)-'A'+10) < base) {
1058         result = result*base + value;
1059         cp++;
1060     }
1061     if (endp)
1062         *endp = (char *)cp;
1063     return result;
1064 }
1065
1066 #define OP_SET  0
1067 #define OP_AND  1
1068 #define OP_OR   2
1069 #define OP_MAX  3
1070 #define OP_MIN  4
1071
1072
1073 static int do_proc_dointvec(cfs_sysctl_table_t *table, int write, struct file *filp,
1074           void *buffer, size_t *lenp, int conv, int op)
1075 {
1076     int *i, vleft, first=1, neg, val;
1077     size_t left, len;
1078     
1079     #define TMPBUFLEN 20
1080     char buf[TMPBUFLEN], *p;
1081     
1082     if (!table->data || !table->maxlen || !*lenp)
1083     {
1084         *lenp = 0;
1085         return 0;
1086     }
1087     
1088     i = (int *) table->data;
1089     vleft = table->maxlen / sizeof(int);
1090     left = *lenp;
1091     
1092     for (; left && vleft--; i++, first=0) {
1093         if (write) {
1094             while (left) {
1095                 char c;
1096                 if(get_user(c,(char *) buffer))
1097                     return -EFAULT;
1098                 if (!isspace(c))
1099                     break;
1100                 left--;
1101                 ((char *) buffer)++;
1102             }
1103             if (!left)
1104                 break;
1105             neg = 0;
1106             len = left;
1107             if (len > TMPBUFLEN-1)
1108                 len = TMPBUFLEN-1;
1109             if(cfs_copy_from_user(buf, buffer, len))
1110                 return -EFAULT;
1111             buf[len] = 0;
1112             p = buf;
1113             if (*p == '-' && left > 1) {
1114                 neg = 1;
1115                 left--, p++;
1116             }
1117             if (*p < '0' || *p > '9')
1118                 break;
1119             val = simple_strtoul(p, &p, 0) * conv;
1120             len = p-buf;
1121             if ((len < left) && *p && !isspace(*p))
1122                 break;
1123             if (neg)
1124                 val = -val;
1125             (char *)buffer += len;
1126             left -= len;
1127             switch(op) {
1128             case OP_SET:    *i = val; break;
1129             case OP_AND:    *i &= val; break;
1130             case OP_OR: *i |= val; break;
1131             case OP_MAX:    if(*i < val)
1132                         *i = val;
1133                     break;
1134             case OP_MIN:    if(*i > val)
1135                         *i = val;
1136                     break;
1137             }
1138         } else {
1139             p = buf;
1140             if (!first)
1141                 *p++ = '\t';
1142             sprintf(p, "%d", (*i) / conv);
1143             len = strlen(buf);
1144             if (len > left)
1145                 len = left;
1146             if(cfs_copy_to_user(buffer, buf, len))
1147                 return -EFAULT;
1148             left -= len;
1149             (char *)buffer += len;
1150         }
1151     }
1152
1153     if (!write && !first && left) {
1154         if(put_user('\n', (char *) buffer))
1155             return -EFAULT;
1156         left--, ((char *)buffer)++;
1157     }
1158     if (write) {
1159         p = (char *) buffer;
1160         while (left) {
1161             char c;
1162             if(get_user(c, p++))
1163                 return -EFAULT;
1164             if (!isspace(c))
1165                 break;
1166             left--;
1167         }
1168     }
1169     if (write && first)
1170         return -EINVAL;
1171     *lenp -= left;
1172     memset(&(filp->f_pos) , 0, sizeof(loff_t));
1173     filp->f_pos += (loff_t)(*lenp);
1174     return 0;
1175 }
1176
1177 /**
1178  * proc_dointvec - read a vector of integers
1179  * @table: the sysctl table
1180  * @write: %TRUE if this is a write to the sysctl file
1181  * @filp: the file structure
1182  * @buffer: the user buffer
1183  * @lenp: the size of the user buffer
1184  *
1185  * Reads/writes up to table->maxlen/sizeof(unsigned int) integer
1186  * values from/to the user buffer, treated as an ASCII string. 
1187  *
1188  * Returns 0 on success.
1189  */
1190 int proc_dointvec(cfs_sysctl_table_t *table, int write, struct file *filp,
1191              void *buffer, size_t *lenp)
1192 {
1193     return do_proc_dointvec(table,write,filp,buffer,lenp,1,OP_SET);
1194 }
1195
1196
1197 /**
1198  * proc_dostring - read a string sysctl
1199  * @table: the sysctl table
1200  * @write: %TRUE if this is a write to the sysctl file
1201  * @filp: the file structure
1202  * @buffer: the user buffer
1203  * @lenp: the size of the user buffer
1204  *
1205  * Reads/writes a string from/to the user buffer. If the kernel
1206  * buffer provided is not large enough to hold the string, the
1207  * string is truncated. The copied string is %NULL-terminated.
1208  * If the string is being read by the user process, it is copied
1209  * and a newline '\n' is added. It is truncated if the buffer is
1210  * not large enough.
1211  *
1212  * Returns 0 on success.
1213  */
1214 int proc_dostring(cfs_sysctl_table_t *table, int write, struct file *filp,
1215           void *buffer, size_t *lenp)
1216 {
1217     size_t len;
1218     char *p, c;
1219     
1220     if (!table->data || !table->maxlen || !*lenp ||
1221         (filp->f_pos && !write)) {
1222         *lenp = 0;
1223         return 0;
1224     }
1225     
1226     if (write) {
1227         len = 0;
1228         p = buffer;
1229         while (len < *lenp) {
1230             if(get_user(c, p++))
1231                 return -EFAULT;
1232             if (c == 0 || c == '\n')
1233                 break;
1234             len++;
1235         }
1236         if (len >= (size_t)table->maxlen)
1237             len = (size_t)table->maxlen-1;
1238         if(cfs_copy_from_user(table->data, buffer, len))
1239             return -EFAULT;
1240         ((char *) table->data)[len] = 0;
1241         filp->f_pos += *lenp;
1242     } else {
1243         len = (size_t)strlen(table->data);
1244         if (len > (size_t)table->maxlen)
1245             len = (size_t)table->maxlen;
1246         if (len > *lenp)
1247             len = *lenp;
1248         if (len)
1249             if(cfs_copy_to_user(buffer, table->data, len))
1250                 return -EFAULT;
1251         if (len < *lenp) {
1252             if(put_user('\n', ((char *) buffer) + len))
1253                 return -EFAULT;
1254             len++;
1255         }
1256         *lenp = len;
1257         filp->f_pos += len;
1258     }
1259     return 0;
1260 }
1261
1262 /* Perform the actual read/write of a sysctl table entry. */
1263 int do_sysctl_strategy (cfs_sysctl_table_t *table, 
1264             int *name, int nlen,
1265             void *oldval, size_t *oldlenp,
1266             void *newval, size_t newlen, void **context)
1267 {
1268     int op = 0, rc;
1269     size_t len;
1270
1271     if (oldval)
1272         op |= 004;
1273     if (newval) 
1274         op |= 002;
1275
1276     if (table->strategy) {
1277         rc = table->strategy(table, name, nlen, oldval, oldlenp,
1278                      newval, newlen, context);
1279         if (rc < 0)
1280             return rc;
1281         if (rc > 0)
1282             return 0;
1283     }
1284
1285     /* If there is no strategy routine, or if the strategy returns
1286      * zero, proceed with automatic r/w */
1287     if (table->data && table->maxlen) {
1288         if (oldval && oldlenp) {
1289             get_user(len, oldlenp);
1290             if (len) {
1291                 if (len > (size_t)table->maxlen)
1292                     len = (size_t)table->maxlen;
1293                 if(cfs_copy_to_user(oldval, table->data, len))
1294                     return -EFAULT;
1295                 if(put_user(len, oldlenp))
1296                     return -EFAULT;
1297             }
1298         }
1299         if (newval && newlen) {
1300             len = newlen;
1301             if (len > (size_t)table->maxlen)
1302                 len = (size_t)table->maxlen;
1303             if(cfs_copy_from_user(table->data, newval, len))
1304                 return -EFAULT;
1305         }
1306     }
1307     return 0;
1308 }
1309
1310 static int parse_table(int *name, int nlen,
1311                void *oldval, size_t *oldlenp,
1312                void *newval, size_t newlen,
1313                cfs_sysctl_table_t *table, void **context)
1314 {
1315     int n;
1316
1317 repeat:
1318
1319     if (!nlen)
1320         return -ENOTDIR;
1321     if (get_user(n, name))
1322         return -EFAULT;
1323     for ( ; table->ctl_name; table++) {
1324         if (n == table->ctl_name || table->ctl_name == CTL_ANY) {
1325             int error;
1326             if (table->child) {
1327 /*
1328                 if (ctl_perm(table, 001))
1329                     return -EPERM;
1330 */
1331                 if (table->strategy) {
1332                     error = table->strategy(
1333                         table, name, nlen,
1334                         oldval, oldlenp,
1335                         newval, newlen, context);
1336                     if (error)
1337                         return error;
1338                 }
1339                 name++;
1340                 nlen--;
1341                 table = table->child;
1342                 goto repeat;
1343             }
1344             error = do_sysctl_strategy(table, name, nlen,
1345                            oldval, oldlenp,
1346                            newval, newlen, context);
1347             return error;
1348         }
1349     }
1350     return -ENOTDIR;
1351 }
1352
1353 int do_sysctl(int *name, int nlen, void *oldval, size_t *oldlenp,
1354            void *newval, size_t newlen)
1355 {
1356     cfs_list_t *tmp;
1357
1358     if (nlen <= 0 || nlen >= CTL_MAXNAME)
1359         return -ENOTDIR;
1360     if (oldval) {
1361         int old_len;
1362         if (!oldlenp || get_user(old_len, oldlenp))
1363             return -EFAULT;
1364     }
1365     tmp = &root_table_header.ctl_entry;
1366     do {
1367         struct ctl_table_header *head =
1368             cfs_list_entry(tmp, struct ctl_table_header, ctl_entry);
1369         void *context = NULL;
1370         int error = parse_table(name, nlen, oldval, oldlenp, 
1371                     newval, newlen, head->ctl_table,
1372                     &context);
1373         if (context)
1374             cfs_free(context);
1375         if (error != -ENOTDIR)
1376             return error;
1377         tmp = tmp->next;
1378     } while (tmp != &root_table_header.ctl_entry);
1379     return -ENOTDIR;
1380 }
1381
1382 /**
1383  * register_sysctl_table - register a sysctl heirarchy
1384  * @table: the top-level table structure
1385  * @insert_at_head: whether the entry should be inserted in front or at the end
1386  *
1387  * Register a sysctl table heirarchy. @table should be a filled in ctl_table
1388  * array. An entry with a ctl_name of 0 terminates the table. 
1389  *
1390  * The members of the &ctl_table structure are used as follows:
1391  *
1392  * ctl_name - This is the numeric sysctl value used by sysctl(2). The number
1393  *            must be unique within that level of sysctl
1394  *
1395  * procname - the name of the sysctl file under /proc/sys. Set to %NULL to not
1396  *            enter a sysctl file
1397  *
1398  * data - a pointer to data for use by proc_handler
1399  *
1400  * maxlen - the maximum size in bytes of the data
1401  *
1402  * mode - the file permissions for the /proc/sys file, and for sysctl(2)
1403  *
1404  * child - a pointer to the child sysctl table if this entry is a directory, or
1405  *         %NULL.
1406  *
1407  * proc_handler - the text handler routine (described below)
1408  *
1409  * strategy - the strategy routine (described below)
1410  *
1411  * de - for internal use by the sysctl routines
1412  *
1413  * extra1, extra2 - extra pointers usable by the proc handler routines
1414  *
1415  * Leaf nodes in the sysctl tree will be represented by a single file
1416  * under /proc; non-leaf nodes will be represented by directories.
1417  *
1418  * sysctl(2) can automatically manage read and write requests through
1419  * the sysctl table.  The data and maxlen fields of the ctl_table
1420  * struct enable minimal validation of the values being written to be
1421  * performed, and the mode field allows minimal authentication.
1422  *
1423  * More sophisticated management can be enabled by the provision of a
1424  * strategy routine with the table entry.  This will be called before
1425  * any automatic read or write of the data is performed.
1426  *
1427  * The strategy routine may return
1428  *
1429  * < 0 - Error occurred (error is passed to user process)
1430  *
1431  * 0   - OK - proceed with automatic read or write.
1432  *
1433  * > 0 - OK - read or write has been done by the strategy routine, so
1434  *       return immediately.
1435  *
1436  * There must be a proc_handler routine for any terminal nodes
1437  * mirrored under /proc/sys (non-terminals are handled by a built-in
1438  * directory handler).  Several default handlers are available to
1439  * cover common cases -
1440  *
1441  * proc_dostring(), proc_dointvec(), proc_dointvec_jiffies(),
1442  * proc_dointvec_minmax(), proc_doulongvec_ms_jiffies_minmax(),
1443  * proc_doulongvec_minmax()
1444  *
1445  * It is the handler's job to read the input buffer from user memory
1446  * and process it. The handler should return 0 on success.
1447  *
1448  * This routine returns %NULL on a failure to register, and a pointer
1449  * to the table header on success.
1450  */
1451 struct ctl_table_header *register_sysctl_table(cfs_sysctl_table_t * table, 
1452                            int insert_at_head)
1453 {
1454     struct ctl_table_header *tmp;
1455     tmp = cfs_alloc(sizeof(struct ctl_table_header), 0);
1456     if (!tmp)
1457         return NULL;
1458     tmp->ctl_table = table;
1459
1460     CFS_INIT_LIST_HEAD(&tmp->ctl_entry);
1461     if (insert_at_head)
1462         cfs_list_add(&tmp->ctl_entry, &root_table_header.ctl_entry);
1463     else
1464         cfs_list_add_tail(&tmp->ctl_entry, &root_table_header.ctl_entry);
1465 #ifdef CONFIG_PROC_FS
1466     register_proc_table(table, cfs_proc_sys);
1467 #endif
1468     return tmp;
1469 }
1470
1471 /**
1472  * unregister_sysctl_table - unregister a sysctl table heirarchy
1473  * @header: the header returned from register_sysctl_table
1474  *
1475  * Unregisters the sysctl table and all children. proc entries may not
1476  * actually be removed until they are no longer used by anyone.
1477  */
1478 void unregister_sysctl_table(struct ctl_table_header * header)
1479 {
1480     cfs_list_del(&header->ctl_entry);
1481 #ifdef CONFIG_PROC_FS
1482     unregister_proc_table(header->ctl_table, cfs_proc_sys);
1483 #endif
1484     cfs_free(header);
1485 }
1486
1487
1488 int cfs_psdev_register(cfs_psdev_t * psdev)
1489 {
1490     cfs_proc_entry_t *  entry;
1491
1492     entry = create_proc_entry (
1493                 (char *)psdev->name,
1494                 S_IFREG,
1495                 cfs_proc_dev
1496             );
1497
1498     if (!entry) {
1499         return -ENOMEM;
1500     }
1501
1502     entry->flags |= CFS_PROC_FLAG_MISCDEV;
1503
1504     entry->proc_fops = psdev->fops;
1505     entry->data = (void *)psdev;
1506
1507     return 0;
1508 }
1509
1510 int cfs_psdev_deregister(cfs_psdev_t * psdev)
1511 {
1512     cfs_proc_entry_t *  entry;
1513
1514     entry = search_proc_entry (
1515                 (char *)psdev->name,
1516                 cfs_proc_dev
1517             );
1518
1519     if (entry) {
1520
1521         ASSERT(entry->data == (void *)psdev);
1522         ASSERT(entry->flags & CFS_PROC_FLAG_MISCDEV);
1523
1524         remove_proc_entry(
1525             (char *)psdev->name,
1526             cfs_proc_dev
1527             );
1528     }
1529
1530     return 0;
1531 }
1532
1533 #define PSDEV_LNET  (0x100)
1534 enum {
1535         PSDEV_DEBUG = 1,          /* control debugging */
1536         PSDEV_SUBSYSTEM_DEBUG,    /* control debugging */
1537         PSDEV_PRINTK,             /* force all messages to console */
1538         PSDEV_CONSOLE_RATELIMIT,  /* rate limit console messages */
1539         PSDEV_DEBUG_PATH,         /* crashdump log location */
1540         PSDEV_DEBUG_DUMP_PATH,    /* crashdump tracelog location */
1541         PSDEV_LIBCFS_MEMUSED,     /* bytes currently PORTAL_ALLOCated */
1542 };
1543
1544 static struct ctl_table lnet_table[] = {
1545         {PSDEV_DEBUG, "debug", &libcfs_debug, sizeof(int), 0644, NULL,
1546          &proc_dointvec},
1547         {PSDEV_SUBSYSTEM_DEBUG, "subsystem_debug", &libcfs_subsystem_debug,
1548          sizeof(int), 0644, NULL, &proc_dointvec},
1549         {PSDEV_PRINTK, "printk", &libcfs_printk, sizeof(int), 0644, NULL,
1550          &proc_dointvec},
1551         {PSDEV_CONSOLE_RATELIMIT, "console_ratelimit", &libcfs_console_ratelimit,
1552          sizeof(int), 0644, NULL, &proc_dointvec},
1553 /*
1554         {PSDEV_PORTALS_UPCALL, "upcall", portals_upcall,
1555          sizeof(portals_upcall), 0644, NULL, &proc_dostring,
1556          &sysctl_string},
1557 */
1558         {PSDEV_LIBCFS_MEMUSED, "memused", (int *)&libcfs_kmemory.counter,
1559          sizeof(int), 0644, NULL, &proc_dointvec},
1560         {0}
1561 };
1562
1563 static struct ctl_table top_table[2] = {
1564         {PSDEV_LNET, "lnet", NULL, 0, 0555, lnet_table},
1565         {0}
1566 };
1567
1568
1569 int trace_write_dump_kernel(struct file *file, const char *buffer,
1570                              unsigned long count, void *data)
1571 {
1572         int rc = cfs_trace_dump_debug_buffer_usrstr((void *)buffer, count);
1573         
1574         return (rc < 0) ? rc : count;
1575 }
1576
1577 int trace_write_daemon_file(struct file *file, const char *buffer,
1578                             unsigned long count, void *data)
1579 {
1580         int rc = cfs_trace_daemon_command_usrstr((void *)buffer, count);
1581
1582         return (rc < 0) ? rc : count;
1583 }
1584
1585 int trace_read_daemon_file(char *page, char **start, off_t off, int count,
1586                            int *eof, void *data)
1587 {
1588         int rc;
1589         cfs_tracefile_read_lock();
1590         rc = cfs_trace_copyout_string(page, count, cfs_tracefile, "\n");
1591         cfs_tracefile_read_unlock();
1592         return rc;
1593 }
1594
1595 int trace_write_debug_mb(struct file *file, const char *buffer,
1596                          unsigned long count, void *data)
1597 {
1598         int rc = 0; /*trace_set_debug_mb_userstr((void *)buffer, count);*/
1599         
1600         return (rc < 0) ? rc : count;
1601 }
1602
1603 int trace_read_debug_mb(char *page, char **start, off_t off, int count,
1604                         int *eof, void *data)
1605 {
1606         char   str[32];
1607
1608         snprintf(str, sizeof(str), "%d\n", cfs_trace_get_debug_mb());
1609
1610         return cfs_trace_copyout_string(page, count, str, NULL);
1611 }
1612
1613 int insert_proc(void)
1614 {
1615         cfs_proc_entry_t *ent;
1616
1617         ent = create_proc_entry("sys/lnet/dump_kernel", 0, NULL);
1618         if (ent == NULL) {
1619                 CERROR("couldn't register dump_kernel\n");
1620                 return -1;
1621         }
1622         ent->write_proc = trace_write_dump_kernel;
1623
1624         ent = create_proc_entry("sys/lnet/daemon_file", 0, NULL);
1625         if (ent == NULL) {
1626                 CERROR("couldn't register daemon_file\n");
1627                 return -1;
1628         }
1629         ent->write_proc = trace_write_daemon_file;
1630         ent->read_proc = trace_read_daemon_file;
1631
1632         ent = create_proc_entry("sys/lnet/debug_mb", 0, NULL);
1633         if (ent == NULL) {
1634                 CERROR("couldn't register debug_mb\n");
1635                 return -1;
1636         }
1637         ent->write_proc = trace_write_debug_mb;
1638         ent->read_proc = trace_read_debug_mb;
1639
1640         return 0;
1641 }
1642
1643 void remove_proc(void)
1644 {
1645         remove_proc_entry("sys/lnet/dump_kernel", NULL);
1646         remove_proc_entry("sys/lnet/daemon_file", NULL);
1647         remove_proc_entry("sys/lnet/debug_mb", NULL);
1648 }
1649
1650
1651 /*
1652  *  proc process routines of kernel space
1653  */
1654
1655 cfs_file_t *
1656 lustre_open_file(char * filename)
1657 {
1658     int rc = 0;
1659     cfs_file_t * fh = NULL;
1660     cfs_proc_entry_t * fp = NULL;
1661
1662     fp = search_proc_entry(filename, cfs_proc_root);
1663     if (!fp) {
1664         return NULL;
1665     }
1666
1667     fh = cfs_alloc(sizeof(cfs_file_t), CFS_ALLOC_ZERO);
1668     if (!fh) {
1669         return NULL;
1670     }
1671
1672     fh->f_inode = cfs_alloc(sizeof(struct inode), CFS_ALLOC_ZERO);
1673     if (!fh->f_inode) {
1674         cfs_free(fh);
1675         return NULL;
1676     }
1677
1678     fh->f_inode->i_priv = (void *)fp;
1679     fh->f_op = fp->proc_fops;
1680
1681     if (fh->f_op->open) {
1682         rc = (fh->f_op->open)(fh->f_inode, fh);
1683     } else {
1684         fp->nlink++;
1685     }
1686
1687     if (0 != rc) {
1688         cfs_free(fh->f_inode);
1689         cfs_free(fh);
1690         return NULL;
1691     }
1692
1693     return fh;
1694 }
1695
1696 int
1697 lustre_close_file(cfs_file_t * fh)
1698 {
1699     int rc = 0;
1700     cfs_proc_entry_t * fp = NULL;
1701
1702     fp = (cfs_proc_entry_t *) fh->f_inode->i_priv;
1703     if (fh->f_op->release) {
1704         rc = (fh->f_op->release)(fh->f_inode, fh);
1705     } else {
1706         fp->nlink--;
1707     }
1708
1709     cfs_free(fh->f_inode);
1710     cfs_free(fh);
1711
1712     return rc;
1713 }
1714
1715 int
1716 lustre_do_ioctl( cfs_file_t * fh,
1717                  unsigned long cmd,
1718                  ulong_ptr_t arg )
1719 {
1720     int rc = 0;
1721
1722     if (fh->f_op->ioctl) {
1723         rc = (fh->f_op->ioctl)(fh, cmd, arg);
1724     }
1725
1726     return rc;
1727 }
1728     
1729 int
1730 lustre_ioctl_file(cfs_file_t * fh, PCFS_PROC_IOCTL devctl)
1731 {
1732     int         rc = 0;
1733     ulong_ptr_t data;
1734
1735     data = (ulong_ptr_t)devctl + sizeof(CFS_PROC_IOCTL);
1736 #if defined(_X86_)    
1737     CLASSERT(sizeof(struct obd_ioctl_data) == 528);
1738 #else
1739     CLASSERT(sizeof(struct obd_ioctl_data) == 576);
1740 #endif
1741
1742     /* obd ioctl code */
1743     if (_IOC_TYPE(devctl->cmd) == 'f') {
1744
1745         struct obd_ioctl_data * obd = (struct obd_ioctl_data *) data;
1746
1747         if ( devctl->cmd != (ULONG)OBD_IOC_BRW_WRITE  &&
1748              devctl->cmd != (ULONG)OBD_IOC_BRW_READ ) {
1749
1750             unsigned long off = obd->ioc_len;
1751
1752             if (obd->ioc_plen1) {
1753                 obd->ioc_pbuf1 = (char *)(data + off);
1754                 off += cfs_size_round(obd->ioc_plen1);
1755             } else {
1756                 obd->ioc_pbuf1 = NULL;
1757             }
1758
1759             if (obd->ioc_plen2) {
1760                 obd->ioc_pbuf2 = (char *)(data + off);
1761                 off += cfs_size_round(obd->ioc_plen2);
1762             } else {
1763                 obd->ioc_pbuf2 = NULL;
1764             }
1765         }
1766     }
1767
1768     rc = lustre_do_ioctl(fh, devctl->cmd, data);
1769
1770     return rc;
1771
1772
1773
1774 size_t
1775 lustre_read_file(
1776     cfs_file_t *    fh,
1777     loff_t          off,
1778     size_t          size,
1779     char *          buf
1780     )
1781 {
1782     size_t  rc = 0;
1783     off_t   low, high;
1784
1785     low = (off_t) size;
1786     high = (off_t)(off >> 32);
1787
1788     if (fh->f_op->read) {
1789         rc = (fh->f_op->read) (fh, buf, size, &off);
1790     }
1791
1792     if (rc) {
1793         fh->f_pos = off + rc;
1794     }
1795
1796     return rc;
1797 }
1798  
1799
1800 size_t
1801 lustre_write_file(
1802     cfs_file_t *    fh,
1803     loff_t          off,
1804     size_t          size,
1805     char *          buf
1806     )
1807 {
1808     size_t rc = 0;
1809     off = 0;
1810     if (fh->f_op->write) {
1811         rc = (fh->f_op->write)(fh, buf, size, &off);
1812     }
1813
1814     return rc;
1815 }  
1816
1817
1818 /*
1819  *  seq file routines
1820  */
1821
1822 /**
1823  *      seq_open -      initialize sequential file
1824  *      @file: file we initialize
1825  *      @op: method table describing the sequence
1826  *
1827  *      seq_open() sets @file, associating it with a sequence described
1828  *      by @op.  @op->start() sets the iterator up and returns the first
1829  *      element of sequence. @op->stop() shuts it down.  @op->next()
1830  *      returns the next element of sequence.  @op->show() prints element
1831  *      into the buffer.  In case of error ->start() and ->next() return
1832  *      ERR_PTR(error).  In the end of sequence they return %NULL. ->show()
1833  *      returns 0 in case of success and negative number in case of error.
1834  */
1835 int seq_open(struct file *file, const struct seq_operations *op)
1836 {
1837         struct seq_file *p = file->private_data;
1838
1839         if (!p) {
1840                 p = kmalloc(sizeof(*p), GFP_KERNEL);
1841                 if (!p)
1842                         return -ENOMEM;
1843                 file->private_data = p;
1844         }
1845         memset(p, 0, sizeof(*p));
1846         cfs_mutex_init(&p->lock);
1847         p->op = op;
1848
1849         /*
1850          * Wrappers around seq_open(e.g. swaps_open) need to be
1851          * aware of this. If they set f_version themselves, they
1852          * should call seq_open first and then set f_version.
1853          */
1854         file->f_version = 0;
1855
1856         /* SEQ files support lseek, but not pread/pwrite */
1857         file->f_mode &= ~(FMODE_PREAD | FMODE_PWRITE);
1858         return 0;
1859 }
1860 EXPORT_SYMBOL(seq_open);
1861
1862 /**
1863  *      seq_read -      ->read() method for sequential files.
1864  *      @file: the file to read from
1865  *      @buf: the buffer to read to
1866  *      @size: the maximum number of bytes to read
1867  *      @ppos: the current position in the file
1868  *
1869  *      Ready-made ->f_op->read()
1870  */
1871 ssize_t seq_read(struct file *file, char __user *buf, size_t size, loff_t *ppos)
1872 {
1873         struct seq_file *m = (struct seq_file *)file->private_data;
1874         size_t copied = 0;
1875         loff_t pos;
1876         size_t n;
1877         void *p;
1878         int err = 0;
1879
1880         cfs_mutex_lock(&m->lock);
1881         /*
1882          * seq_file->op->..m_start/m_stop/m_next may do special actions
1883          * or optimisations based on the file->f_version, so we want to
1884          * pass the file->f_version to those methods.
1885          *
1886          * seq_file->version is just copy of f_version, and seq_file
1887          * methods can treat it simply as file version.
1888          * It is copied in first and copied out after all operations.
1889          * It is convenient to have it as  part of structure to avoid the
1890          * need of passing another argument to all the seq_file methods.
1891          */
1892         m->version = file->f_version;
1893         /* grab buffer if we didn't have one */
1894         if (!m->buf) {
1895                 m->buf = kmalloc(m->size = PAGE_SIZE, GFP_KERNEL);
1896                 if (!m->buf)
1897                         goto Enomem;
1898         }
1899         /* if not empty - flush it first */
1900         if (m->count) {
1901                 n = min(m->count, size);
1902                 err = cfs_copy_to_user(buf, m->buf + m->from, n);
1903                 if (err)
1904                         goto Efault;
1905                 m->count -= n;
1906                 m->from += n;
1907                 size -= n;
1908                 buf += n;
1909                 copied += n;
1910                 if (!m->count)
1911                         m->index++;
1912                 if (!size)
1913                         goto Done;
1914         }
1915         /* we need at least one record in buffer */
1916         while (1) {
1917                 pos = m->index;
1918                 p = m->op->start(m, &pos);
1919                 err = PTR_ERR(p);
1920                 if (!p || IS_ERR(p))
1921                         break;
1922                 err = m->op->show(m, p);
1923                 if (err)
1924                         break;
1925                 if (m->count < m->size)
1926                         goto Fill;
1927                 m->op->stop(m, p);
1928                 cfs_free(m->buf);
1929                 m->buf = kmalloc(m->size <<= 1, GFP_KERNEL);
1930                 if (!m->buf)
1931                         goto Enomem;
1932                 m->count = 0;
1933                 m->version = 0;
1934         }
1935         m->op->stop(m, p);
1936         m->count = 0;
1937         goto Done;
1938 Fill:
1939         /* they want more? let's try to get some more */
1940         while (m->count < size) {
1941                 size_t offs = m->count;
1942                 loff_t next = pos;
1943                 p = m->op->next(m, p, &next);
1944                 if (!p || IS_ERR(p)) {
1945                         err = PTR_ERR(p);
1946                         break;
1947                 }
1948                 err = m->op->show(m, p);
1949                 if (err || m->count == m->size) {
1950                         m->count = offs;
1951                         break;
1952                 }
1953                 pos = next;
1954         }
1955         m->op->stop(m, p);
1956         n = min(m->count, size);
1957         err = cfs_copy_to_user(buf, m->buf, n);
1958         if (err)
1959                 goto Efault;
1960         copied += n;
1961         m->count -= n;
1962         if (m->count)
1963                 m->from = n;
1964         else
1965                 pos++;
1966         m->index = pos;
1967 Done:
1968         if (!copied)
1969                 copied = err;
1970         else
1971                 *ppos += copied;
1972         file->f_version = m->version;
1973         cfs_mutex_unlock(&m->lock);
1974         return copied;
1975 Enomem:
1976         err = -ENOMEM;
1977         goto Done;
1978 Efault:
1979         err = -EFAULT;
1980         goto Done;
1981 }
1982 EXPORT_SYMBOL(seq_read);
1983
1984 static int traverse(struct seq_file *m, loff_t offset)
1985 {
1986         loff_t pos = 0, index;
1987         int error = 0;
1988         void *p;
1989
1990         m->version = 0;
1991         index = 0;
1992         m->count = m->from = 0;
1993         if (!offset) {
1994                 m->index = index;
1995                 return 0;
1996         }
1997         if (!m->buf) {
1998                 m->buf = kmalloc(m->size = PAGE_SIZE, GFP_KERNEL);
1999                 if (!m->buf)
2000                         return -ENOMEM;
2001         }
2002         p = m->op->start(m, &index);
2003         while (p) {
2004                 error = PTR_ERR(p);
2005                 if (IS_ERR(p))
2006                         break;
2007                 error = m->op->show(m, p);
2008                 if (error)
2009                         break;
2010                 if (m->count == m->size)
2011                         goto Eoverflow;
2012                 if (pos + (loff_t)(m->count) > offset) {
2013                         m->from = (size_t)(offset - pos);
2014                         m->count -= m->from;
2015                         m->index = index;
2016                         break;
2017                 }
2018                 pos += m->count;
2019                 m->count = 0;
2020                 if (pos == offset) {
2021                         index++;
2022                         m->index = index;
2023                         break;
2024                 }
2025                 p = m->op->next(m, p, &index);
2026         }
2027         m->op->stop(m, p);
2028         return error;
2029
2030 Eoverflow:
2031         m->op->stop(m, p);
2032         cfs_free(m->buf);
2033         m->buf = cfs_alloc(m->size <<= 1, GFP_KERNEL | CFS_ALLOC_ZERO);
2034         return !m->buf ? -ENOMEM : -EAGAIN;
2035 }
2036
2037 /**
2038  *      seq_lseek -     ->llseek() method for sequential files.
2039  *      @file: the file in question
2040  *      @offset: new position
2041  *      @origin: 0 for absolute, 1 for relative position
2042  *
2043  *      Ready-made ->f_op->llseek()
2044  */
2045 loff_t seq_lseek(struct file *file, loff_t offset, int origin)
2046 {
2047         struct seq_file *m = (struct seq_file *)file->private_data;
2048         long long retval = -EINVAL;
2049
2050         cfs_mutex_lock(&m->lock);
2051         m->version = file->f_version;
2052         switch (origin) {
2053                 case 1:
2054                         offset += file->f_pos;
2055                 case 0:
2056                         if (offset < 0)
2057                                 break;
2058                         retval = offset;
2059                         if (offset != file->f_pos) {
2060                                 while ((retval=traverse(m, offset)) == -EAGAIN)
2061                                         ;
2062                                 if (retval) {
2063                                         /* with extreme prejudice... */
2064                                         file->f_pos = 0;
2065                                         m->version = 0;
2066                                         m->index = 0;
2067                                         m->count = 0;
2068                                 } else {
2069                                         retval = file->f_pos = offset;
2070                                 }
2071                         }
2072         }
2073         file->f_version = m->version;
2074         cfs_mutex_unlock(&m->lock);
2075         return retval;
2076 }
2077 EXPORT_SYMBOL(seq_lseek);
2078
2079 /**
2080  *      seq_release -   free the structures associated with sequential file.
2081  *      @file: file in question
2082  *      @inode: file->f_path.dentry->d_inode
2083  *
2084  *      Frees the structures associated with sequential file; can be used
2085  *      as ->f_op->release() if you don't have private data to destroy.
2086  */
2087 int seq_release(struct inode *inode, struct file *file)
2088 {
2089         struct seq_file *m = (struct seq_file *)file->private_data;
2090     if (m) {
2091         if (m->buf)
2092                 cfs_free(m->buf);
2093             cfs_free(m);
2094     }
2095         return 0;
2096 }
2097 EXPORT_SYMBOL(seq_release);
2098
2099 /**
2100  *      seq_escape -    print string into buffer, escaping some characters
2101  *      @m:     target buffer
2102  *      @s:     string
2103  *      @esc:   set of characters that need escaping
2104  *
2105  *      Puts string into buffer, replacing each occurrence of character from
2106  *      @esc with usual octal escape.  Returns 0 in case of success, -1 - in
2107  *      case of overflow.
2108  */
2109 int seq_escape(struct seq_file *m, const char *s, const char *esc)
2110 {
2111         char *end = m->buf + m->size;
2112         char *p;
2113         char c;
2114
2115         for (p = m->buf + m->count; (c = *s) != '\0' && p < end; s++) {
2116                 if (!strchr(esc, c)) {
2117                         *p++ = c;
2118                         continue;
2119                 }
2120                 if (p + 3 < end) {
2121                         *p++ = '\\';
2122                         *p++ = '0' + ((c & 0300) >> 6);
2123                         *p++ = '0' + ((c & 070) >> 3);
2124                         *p++ = '0' + (c & 07);
2125                         continue;
2126                 }
2127                 m->count = m->size;
2128                 return -1;
2129         }
2130         m->count = p - m->buf;
2131         return 0;
2132 }
2133 EXPORT_SYMBOL(seq_escape);
2134
2135 int seq_printf(struct seq_file *m, const char *f, ...)
2136 {
2137         va_list args;
2138         int len;
2139
2140         if (m->count < m->size) {
2141                 va_start(args, f);
2142                 len = vsnprintf(m->buf + m->count, m->size - m->count, f, args);
2143                 va_end(args);
2144                 if (m->count + len < m->size) {
2145                         m->count += len;
2146                         return 0;
2147                 }
2148         }
2149         m->count = m->size;
2150         return -1;
2151 }
2152 EXPORT_SYMBOL(seq_printf);
2153
2154 char *d_path(struct path *p, char *buffer, int buflen)
2155 {
2156         cfs_enter_debugger();
2157         return ERR_PTR(-ENAMETOOLONG);
2158 }
2159
2160 int seq_path(struct seq_file *m, struct path *path, char *esc)
2161 {
2162         if (m->count < m->size) {
2163                 char *s = m->buf + m->count;
2164                 char *p = d_path(path, s, m->size - m->count);
2165                 if (!IS_ERR(p)) {
2166                         while (s <= p) {
2167                                 char c = *p++;
2168                                 if (!c) {
2169                                         p = m->buf + m->count;
2170                                         m->count = s - m->buf;
2171                                         return (int)(s - p);
2172                                 } else if (!strchr(esc, c)) {
2173                                         *s++ = c;
2174                                 } else if (s + 4 > p) {
2175                                         break;
2176                                 } else {
2177                                         *s++ = '\\';
2178                                         *s++ = '0' + ((c & 0300) >> 6);
2179                                         *s++ = '0' + ((c & 070) >> 3);
2180                                         *s++ = '0' + (c & 07);
2181                                 }
2182                         }
2183                 }
2184         }
2185         m->count = m->size;
2186         return -1;
2187 }
2188 EXPORT_SYMBOL(seq_path);
2189
2190 static void *single_start(struct seq_file *p, loff_t *pos)
2191 {
2192         return (void *) (INT_PTR) (*pos == 0);
2193 }
2194
2195 static void *single_next(struct seq_file *p, void *v, loff_t *pos)
2196 {
2197         ++*pos;
2198         return NULL;
2199 }
2200
2201 static void single_stop(struct seq_file *p, void *v)
2202 {
2203 }
2204
2205 int single_open(struct file *file, int (*show)(struct seq_file *, void *),
2206                 void *data)
2207 {
2208         struct seq_operations *op = kmalloc(sizeof(*op), GFP_KERNEL);
2209         int res = -ENOMEM;
2210
2211         if (op) {
2212                 op->start = single_start;
2213                 op->next = single_next;
2214                 op->stop = single_stop;
2215                 op->show = show;
2216                 res = seq_open(file, op);
2217                 if (!res)
2218                         ((struct seq_file *)file->private_data)->private = data;
2219                 else
2220                         cfs_free(op);
2221         }
2222         return res;
2223 }
2224 EXPORT_SYMBOL(single_open);
2225
2226 int single_release(struct inode *inode, struct file *file)
2227 {
2228         const struct seq_operations *op = ((struct seq_file *)file->private_data)->op;
2229         int res = seq_release(inode, file);
2230         cfs_free((void *)op);
2231         return res;
2232 }
2233 EXPORT_SYMBOL(single_release);
2234
2235 int seq_release_private(struct inode *inode, struct file *file)
2236 {
2237         struct seq_file *seq = file->private_data;
2238
2239         cfs_free(seq->private);
2240         seq->private = NULL;
2241         return seq_release(inode, file);
2242 }
2243 EXPORT_SYMBOL(seq_release_private);
2244
2245 void *__seq_open_private(struct file *f, const struct seq_operations *ops,
2246                 int psize)
2247 {
2248         int rc;
2249         void *private;
2250         struct seq_file *seq;
2251
2252         private = cfs_alloc(psize, GFP_KERNEL | CFS_ALLOC_ZERO);
2253         if (private == NULL)
2254                 goto out;
2255
2256         rc = seq_open(f, ops);
2257         if (rc < 0)
2258                 goto out_free;
2259
2260         seq = f->private_data;
2261         seq->private = private;
2262         return private;
2263
2264 out_free:
2265         cfs_free(private);
2266 out:
2267         return NULL;
2268 }
2269 EXPORT_SYMBOL(__seq_open_private);
2270
2271 int seq_open_private(struct file *filp, const struct seq_operations *ops,
2272                 int psize)
2273 {
2274         return __seq_open_private(filp, ops, psize) ? 0 : -ENOMEM;
2275 }
2276 EXPORT_SYMBOL(seq_open_private);
2277
2278 int seq_putc(struct seq_file *m, char c)
2279 {
2280         if (m->count < m->size) {
2281                 m->buf[m->count++] = c;
2282                 return 0;
2283         }
2284         return -1;
2285 }
2286 EXPORT_SYMBOL(seq_putc);
2287
2288 int seq_puts(struct seq_file *m, const char *s)
2289 {
2290         int len = strlen(s);
2291         if (m->count + len < m->size) {
2292                 memcpy(m->buf + m->count, s, len);
2293                 m->count += len;
2294                 return 0;
2295         }
2296         m->count = m->size;
2297         return -1;
2298 }
2299 EXPORT_SYMBOL(seq_puts);
2300
2301 cfs_list_t *seq_list_start(cfs_list_t *head, loff_t pos)
2302 {
2303         cfs_list_t *lh;
2304
2305         cfs_list_for_each(lh, head)
2306                 if (pos-- == 0)
2307                         return lh;
2308
2309         return NULL;
2310 }
2311
2312 EXPORT_SYMBOL(seq_list_start);
2313
2314 cfs_list_t *seq_list_start_head(cfs_list_t *head,
2315                                 loff_t pos)
2316 {
2317         if (!pos)
2318                 return head;
2319
2320         return seq_list_start(head, pos - 1);
2321 }
2322
2323 EXPORT_SYMBOL(seq_list_start_head);
2324
2325 cfs_list_t *seq_list_next(void *v, cfs_list_t *head,
2326                           loff_t *ppos)
2327 {
2328         cfs_list_t *lh;
2329
2330         lh = ((cfs_list_t *)v)->next;
2331         ++*ppos;
2332         return lh == head ? NULL : lh;
2333 }
2334
2335 EXPORT_SYMBOL(seq_list_next);
2336
2337 struct proc_dir_entry *PDE(const struct inode *inode)
2338 {
2339         return (struct proc_dir_entry *)inode->i_priv;
2340 }
2341
2342
2343 #endif /* __KERNEL__ */