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