1 /* proc.c: /proc interface for AFS
3 * Copyright (C) 2002 Red Hat, Inc. All Rights Reserved.
4 * Written by David Howells (dhowells@redhat.com)
6 * This program is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU General Public License
8 * as published by the Free Software Foundation; either version
9 * 2 of the License, or (at your option) any later version.
12 #include <linux/slab.h>
13 #include <linux/module.h>
14 #include <linux/proc_fs.h>
15 #include <linux/seq_file.h>
18 #include <asm/uaccess.h>
21 static struct proc_dir_entry
*proc_afs
;
24 static int afs_proc_cells_open(struct inode
*inode
, struct file
*file
);
25 static void *afs_proc_cells_start(struct seq_file
*p
, loff_t
*pos
);
26 static void *afs_proc_cells_next(struct seq_file
*p
, void *v
, loff_t
*pos
);
27 static void afs_proc_cells_stop(struct seq_file
*p
, void *v
);
28 static int afs_proc_cells_show(struct seq_file
*m
, void *v
);
29 static ssize_t
afs_proc_cells_write(struct file
*file
, const char __user
*buf
,
30 size_t size
, loff_t
*_pos
);
32 static struct seq_operations afs_proc_cells_ops
= {
33 .start
= afs_proc_cells_start
,
34 .next
= afs_proc_cells_next
,
35 .stop
= afs_proc_cells_stop
,
36 .show
= afs_proc_cells_show
,
39 static const struct file_operations afs_proc_cells_fops
= {
40 .open
= afs_proc_cells_open
,
42 .write
= afs_proc_cells_write
,
44 .release
= seq_release
,
47 static int afs_proc_rootcell_open(struct inode
*inode
, struct file
*file
);
48 static int afs_proc_rootcell_release(struct inode
*inode
, struct file
*file
);
49 static ssize_t
afs_proc_rootcell_read(struct file
*file
, char __user
*buf
,
50 size_t size
, loff_t
*_pos
);
51 static ssize_t
afs_proc_rootcell_write(struct file
*file
,
52 const char __user
*buf
,
53 size_t size
, loff_t
*_pos
);
55 static const struct file_operations afs_proc_rootcell_fops
= {
56 .open
= afs_proc_rootcell_open
,
57 .read
= afs_proc_rootcell_read
,
58 .write
= afs_proc_rootcell_write
,
60 .release
= afs_proc_rootcell_release
63 static int afs_proc_cell_volumes_open(struct inode
*inode
, struct file
*file
);
64 static int afs_proc_cell_volumes_release(struct inode
*inode
,
66 static void *afs_proc_cell_volumes_start(struct seq_file
*p
, loff_t
*pos
);
67 static void *afs_proc_cell_volumes_next(struct seq_file
*p
, void *v
,
69 static void afs_proc_cell_volumes_stop(struct seq_file
*p
, void *v
);
70 static int afs_proc_cell_volumes_show(struct seq_file
*m
, void *v
);
72 static struct seq_operations afs_proc_cell_volumes_ops
= {
73 .start
= afs_proc_cell_volumes_start
,
74 .next
= afs_proc_cell_volumes_next
,
75 .stop
= afs_proc_cell_volumes_stop
,
76 .show
= afs_proc_cell_volumes_show
,
79 static const struct file_operations afs_proc_cell_volumes_fops
= {
80 .open
= afs_proc_cell_volumes_open
,
83 .release
= afs_proc_cell_volumes_release
,
86 static int afs_proc_cell_vlservers_open(struct inode
*inode
,
88 static int afs_proc_cell_vlservers_release(struct inode
*inode
,
90 static void *afs_proc_cell_vlservers_start(struct seq_file
*p
, loff_t
*pos
);
91 static void *afs_proc_cell_vlservers_next(struct seq_file
*p
, void *v
,
93 static void afs_proc_cell_vlservers_stop(struct seq_file
*p
, void *v
);
94 static int afs_proc_cell_vlservers_show(struct seq_file
*m
, void *v
);
96 static struct seq_operations afs_proc_cell_vlservers_ops
= {
97 .start
= afs_proc_cell_vlservers_start
,
98 .next
= afs_proc_cell_vlservers_next
,
99 .stop
= afs_proc_cell_vlservers_stop
,
100 .show
= afs_proc_cell_vlservers_show
,
103 static const struct file_operations afs_proc_cell_vlservers_fops
= {
104 .open
= afs_proc_cell_vlservers_open
,
107 .release
= afs_proc_cell_vlservers_release
,
110 static int afs_proc_cell_servers_open(struct inode
*inode
, struct file
*file
);
111 static int afs_proc_cell_servers_release(struct inode
*inode
,
113 static void *afs_proc_cell_servers_start(struct seq_file
*p
, loff_t
*pos
);
114 static void *afs_proc_cell_servers_next(struct seq_file
*p
, void *v
,
116 static void afs_proc_cell_servers_stop(struct seq_file
*p
, void *v
);
117 static int afs_proc_cell_servers_show(struct seq_file
*m
, void *v
);
119 static struct seq_operations afs_proc_cell_servers_ops
= {
120 .start
= afs_proc_cell_servers_start
,
121 .next
= afs_proc_cell_servers_next
,
122 .stop
= afs_proc_cell_servers_stop
,
123 .show
= afs_proc_cell_servers_show
,
126 static const struct file_operations afs_proc_cell_servers_fops
= {
127 .open
= afs_proc_cell_servers_open
,
130 .release
= afs_proc_cell_servers_release
,
133 /*****************************************************************************/
135 * initialise the /proc/fs/afs/ directory
137 int afs_proc_init(void)
139 struct proc_dir_entry
*p
;
143 proc_afs
= proc_mkdir("fs/afs", NULL
);
146 proc_afs
->owner
= THIS_MODULE
;
148 p
= create_proc_entry("cells", 0, proc_afs
);
151 p
->proc_fops
= &afs_proc_cells_fops
;
152 p
->owner
= THIS_MODULE
;
154 p
= create_proc_entry("rootcell", 0, proc_afs
);
157 p
->proc_fops
= &afs_proc_rootcell_fops
;
158 p
->owner
= THIS_MODULE
;
164 remove_proc_entry("cells", proc_afs
);
166 remove_proc_entry("fs/afs", NULL
);
168 _leave(" = -ENOMEM");
171 } /* end afs_proc_init() */
173 /*****************************************************************************/
175 * clean up the /proc/fs/afs/ directory
177 void afs_proc_cleanup(void)
179 remove_proc_entry("cells", proc_afs
);
181 remove_proc_entry("fs/afs", NULL
);
183 } /* end afs_proc_cleanup() */
185 /*****************************************************************************/
187 * open "/proc/fs/afs/cells" which provides a summary of extant cells
189 static int afs_proc_cells_open(struct inode
*inode
, struct file
*file
)
194 ret
= seq_open(file
, &afs_proc_cells_ops
);
198 m
= file
->private_data
;
199 m
->private = PDE(inode
)->data
;
202 } /* end afs_proc_cells_open() */
204 /*****************************************************************************/
206 * set up the iterator to start reading from the cells list and return the
209 static void *afs_proc_cells_start(struct seq_file
*m
, loff_t
*_pos
)
211 struct list_head
*_p
;
214 /* lock the list against modification */
215 down_read(&afs_proc_cells_sem
);
217 /* allow for the header line */
222 /* find the n'th element in the list */
223 list_for_each(_p
, &afs_proc_cells
)
227 return _p
!= &afs_proc_cells
? _p
: NULL
;
228 } /* end afs_proc_cells_start() */
230 /*****************************************************************************/
232 * move to next cell in cells list
234 static void *afs_proc_cells_next(struct seq_file
*p
, void *v
, loff_t
*pos
)
236 struct list_head
*_p
;
241 _p
= v
== (void *) 1 ? afs_proc_cells
.next
: _p
->next
;
243 return _p
!= &afs_proc_cells
? _p
: NULL
;
244 } /* end afs_proc_cells_next() */
246 /*****************************************************************************/
248 * clean up after reading from the cells list
250 static void afs_proc_cells_stop(struct seq_file
*p
, void *v
)
252 up_read(&afs_proc_cells_sem
);
254 } /* end afs_proc_cells_stop() */
256 /*****************************************************************************/
258 * display a header line followed by a load of cell lines
260 static int afs_proc_cells_show(struct seq_file
*m
, void *v
)
262 struct afs_cell
*cell
= list_entry(v
, struct afs_cell
, proc_link
);
264 /* display header on line 1 */
265 if (v
== (void *) 1) {
266 seq_puts(m
, "USE NAME\n");
270 /* display one cell per line on subsequent lines */
271 seq_printf(m
, "%3d %s\n", atomic_read(&cell
->usage
), cell
->name
);
274 } /* end afs_proc_cells_show() */
276 /*****************************************************************************/
278 * handle writes to /proc/fs/afs/cells
279 * - to add cells: echo "add <cellname> <IP>[:<IP>][:<IP>]"
281 static ssize_t
afs_proc_cells_write(struct file
*file
, const char __user
*buf
,
282 size_t size
, loff_t
*_pos
)
284 char *kbuf
, *name
, *args
;
287 /* start by dragging the command into memory */
288 if (size
<= 1 || size
>= PAGE_SIZE
)
291 kbuf
= kmalloc(size
+ 1, GFP_KERNEL
);
296 if (copy_from_user(kbuf
, buf
, size
) != 0)
300 /* trim to first NL */
301 name
= memchr(kbuf
, '\n', size
);
305 /* split into command, name and argslist */
306 name
= strchr(kbuf
, ' ');
311 } while(*name
== ' ');
315 args
= strchr(name
, ' ');
320 } while(*args
== ' ');
324 /* determine command to perform */
325 _debug("cmd=%s name=%s args=%s", kbuf
, name
, args
);
327 if (strcmp(kbuf
, "add") == 0) {
328 struct afs_cell
*cell
;
329 ret
= afs_cell_create(name
, args
, &cell
);
333 printk("kAFS: Added new cell '%s'\n", name
);
343 _leave(" = %d", ret
);
348 printk("kAFS: Invalid Command on /proc/fs/afs/cells file\n");
350 } /* end afs_proc_cells_write() */
352 /*****************************************************************************/
354 * Stubs for /proc/fs/afs/rootcell
356 static int afs_proc_rootcell_open(struct inode
*inode
, struct file
*file
)
361 static int afs_proc_rootcell_release(struct inode
*inode
, struct file
*file
)
366 static ssize_t
afs_proc_rootcell_read(struct file
*file
, char __user
*buf
,
367 size_t size
, loff_t
*_pos
)
372 /*****************************************************************************/
374 * handle writes to /proc/fs/afs/rootcell
375 * - to initialize rootcell: echo "cell.name:192.168.231.14"
377 static ssize_t
afs_proc_rootcell_write(struct file
*file
,
378 const char __user
*buf
,
379 size_t size
, loff_t
*_pos
)
384 /* start by dragging the command into memory */
385 if (size
<= 1 || size
>= PAGE_SIZE
)
389 kbuf
= kmalloc(size
+ 1, GFP_KERNEL
);
394 if (copy_from_user(kbuf
, buf
, size
) != 0)
398 /* trim to first NL */
399 s
= memchr(kbuf
, '\n', size
);
403 /* determine command to perform */
404 _debug("rootcell=%s", kbuf
);
406 ret
= afs_cell_init(kbuf
);
408 ret
= size
; /* consume everything, always */
413 _leave(" = %d", ret
);
415 } /* end afs_proc_rootcell_write() */
417 /*****************************************************************************/
419 * initialise /proc/fs/afs/<cell>/
421 int afs_proc_cell_setup(struct afs_cell
*cell
)
423 struct proc_dir_entry
*p
;
425 _enter("%p{%s}", cell
, cell
->name
);
427 cell
->proc_dir
= proc_mkdir(cell
->name
, proc_afs
);
431 p
= create_proc_entry("servers", 0, cell
->proc_dir
);
434 p
->proc_fops
= &afs_proc_cell_servers_fops
;
435 p
->owner
= THIS_MODULE
;
438 p
= create_proc_entry("vlservers", 0, cell
->proc_dir
);
441 p
->proc_fops
= &afs_proc_cell_vlservers_fops
;
442 p
->owner
= THIS_MODULE
;
445 p
= create_proc_entry("volumes", 0, cell
->proc_dir
);
447 goto error_vlservers
;
448 p
->proc_fops
= &afs_proc_cell_volumes_fops
;
449 p
->owner
= THIS_MODULE
;
456 remove_proc_entry("vlservers", cell
->proc_dir
);
458 remove_proc_entry("servers", cell
->proc_dir
);
460 remove_proc_entry(cell
->name
, proc_afs
);
461 _leave(" = -ENOMEM");
463 } /* end afs_proc_cell_setup() */
465 /*****************************************************************************/
467 * remove /proc/fs/afs/<cell>/
469 void afs_proc_cell_remove(struct afs_cell
*cell
)
473 remove_proc_entry("volumes", cell
->proc_dir
);
474 remove_proc_entry("vlservers", cell
->proc_dir
);
475 remove_proc_entry("servers", cell
->proc_dir
);
476 remove_proc_entry(cell
->name
, proc_afs
);
479 } /* end afs_proc_cell_remove() */
481 /*****************************************************************************/
483 * open "/proc/fs/afs/<cell>/volumes" which provides a summary of extant cells
485 static int afs_proc_cell_volumes_open(struct inode
*inode
, struct file
*file
)
487 struct afs_cell
*cell
;
491 cell
= afs_get_cell_maybe((struct afs_cell
**) &PDE(inode
)->data
);
495 ret
= seq_open(file
, &afs_proc_cell_volumes_ops
);
499 m
= file
->private_data
;
503 } /* end afs_proc_cell_volumes_open() */
505 /*****************************************************************************/
507 * close the file and release the ref to the cell
509 static int afs_proc_cell_volumes_release(struct inode
*inode
, struct file
*file
)
511 struct afs_cell
*cell
= PDE(inode
)->data
;
514 ret
= seq_release(inode
,file
);
519 } /* end afs_proc_cell_volumes_release() */
521 /*****************************************************************************/
523 * set up the iterator to start reading from the cells list and return the
526 static void *afs_proc_cell_volumes_start(struct seq_file
*m
, loff_t
*_pos
)
528 struct list_head
*_p
;
529 struct afs_cell
*cell
= m
->private;
532 _enter("cell=%p pos=%Ld", cell
, *_pos
);
534 /* lock the list against modification */
535 down_read(&cell
->vl_sem
);
537 /* allow for the header line */
542 /* find the n'th element in the list */
543 list_for_each(_p
, &cell
->vl_list
)
547 return _p
!= &cell
->vl_list
? _p
: NULL
;
548 } /* end afs_proc_cell_volumes_start() */
550 /*****************************************************************************/
552 * move to next cell in cells list
554 static void *afs_proc_cell_volumes_next(struct seq_file
*p
, void *v
,
557 struct list_head
*_p
;
558 struct afs_cell
*cell
= p
->private;
560 _enter("cell=%p pos=%Ld", cell
, *_pos
);
565 _p
= v
== (void *) 1 ? cell
->vl_list
.next
: _p
->next
;
567 return _p
!= &cell
->vl_list
? _p
: NULL
;
568 } /* end afs_proc_cell_volumes_next() */
570 /*****************************************************************************/
572 * clean up after reading from the cells list
574 static void afs_proc_cell_volumes_stop(struct seq_file
*p
, void *v
)
576 struct afs_cell
*cell
= p
->private;
578 up_read(&cell
->vl_sem
);
580 } /* end afs_proc_cell_volumes_stop() */
582 /*****************************************************************************/
584 * display a header line followed by a load of volume lines
586 static int afs_proc_cell_volumes_show(struct seq_file
*m
, void *v
)
588 struct afs_vlocation
*vlocation
=
589 list_entry(v
, struct afs_vlocation
, link
);
591 /* display header on line 1 */
592 if (v
== (void *) 1) {
593 seq_puts(m
, "USE VLID[0] VLID[1] VLID[2] NAME\n");
597 /* display one cell per line on subsequent lines */
598 seq_printf(m
, "%3d %08x %08x %08x %s\n",
599 atomic_read(&vlocation
->usage
),
600 vlocation
->vldb
.vid
[0],
601 vlocation
->vldb
.vid
[1],
602 vlocation
->vldb
.vid
[2],
607 } /* end afs_proc_cell_volumes_show() */
609 /*****************************************************************************/
611 * open "/proc/fs/afs/<cell>/vlservers" which provides a list of volume
614 static int afs_proc_cell_vlservers_open(struct inode
*inode
, struct file
*file
)
616 struct afs_cell
*cell
;
620 cell
= afs_get_cell_maybe((struct afs_cell
**)&PDE(inode
)->data
);
624 ret
= seq_open(file
,&afs_proc_cell_vlservers_ops
);
628 m
= file
->private_data
;
632 } /* end afs_proc_cell_vlservers_open() */
634 /*****************************************************************************/
636 * close the file and release the ref to the cell
638 static int afs_proc_cell_vlservers_release(struct inode
*inode
,
641 struct afs_cell
*cell
= PDE(inode
)->data
;
644 ret
= seq_release(inode
,file
);
649 } /* end afs_proc_cell_vlservers_release() */
651 /*****************************************************************************/
653 * set up the iterator to start reading from the cells list and return the
656 static void *afs_proc_cell_vlservers_start(struct seq_file
*m
, loff_t
*_pos
)
658 struct afs_cell
*cell
= m
->private;
661 _enter("cell=%p pos=%Ld", cell
, *_pos
);
663 /* lock the list against modification */
664 down_read(&cell
->vl_sem
);
666 /* allow for the header line */
671 if (pos
>= cell
->vl_naddrs
)
674 return &cell
->vl_addrs
[pos
];
675 } /* end afs_proc_cell_vlservers_start() */
677 /*****************************************************************************/
679 * move to next cell in cells list
681 static void *afs_proc_cell_vlservers_next(struct seq_file
*p
, void *v
,
684 struct afs_cell
*cell
= p
->private;
687 _enter("cell=%p{nad=%u} pos=%Ld", cell
, cell
->vl_naddrs
, *_pos
);
691 if (pos
>= cell
->vl_naddrs
)
694 return &cell
->vl_addrs
[pos
];
695 } /* end afs_proc_cell_vlservers_next() */
697 /*****************************************************************************/
699 * clean up after reading from the cells list
701 static void afs_proc_cell_vlservers_stop(struct seq_file
*p
, void *v
)
703 struct afs_cell
*cell
= p
->private;
705 up_read(&cell
->vl_sem
);
707 } /* end afs_proc_cell_vlservers_stop() */
709 /*****************************************************************************/
711 * display a header line followed by a load of volume lines
713 static int afs_proc_cell_vlservers_show(struct seq_file
*m
, void *v
)
715 struct in_addr
*addr
= v
;
717 /* display header on line 1 */
718 if (v
== (struct in_addr
*) 1) {
719 seq_puts(m
, "ADDRESS\n");
723 /* display one cell per line on subsequent lines */
724 seq_printf(m
, "%u.%u.%u.%u\n", NIPQUAD(addr
->s_addr
));
727 } /* end afs_proc_cell_vlservers_show() */
729 /*****************************************************************************/
731 * open "/proc/fs/afs/<cell>/servers" which provides a summary of active
734 static int afs_proc_cell_servers_open(struct inode
*inode
, struct file
*file
)
736 struct afs_cell
*cell
;
740 cell
= afs_get_cell_maybe((struct afs_cell
**) &PDE(inode
)->data
);
744 ret
= seq_open(file
, &afs_proc_cell_servers_ops
);
748 m
= file
->private_data
;
752 } /* end afs_proc_cell_servers_open() */
754 /*****************************************************************************/
756 * close the file and release the ref to the cell
758 static int afs_proc_cell_servers_release(struct inode
*inode
,
761 struct afs_cell
*cell
= PDE(inode
)->data
;
764 ret
= seq_release(inode
, file
);
769 } /* end afs_proc_cell_servers_release() */
771 /*****************************************************************************/
773 * set up the iterator to start reading from the cells list and return the
776 static void *afs_proc_cell_servers_start(struct seq_file
*m
, loff_t
*_pos
)
777 __acquires(m
->private->sv_lock
)
779 struct list_head
*_p
;
780 struct afs_cell
*cell
= m
->private;
783 _enter("cell=%p pos=%Ld", cell
, *_pos
);
785 /* lock the list against modification */
786 read_lock(&cell
->sv_lock
);
788 /* allow for the header line */
793 /* find the n'th element in the list */
794 list_for_each(_p
, &cell
->sv_list
)
798 return _p
!= &cell
->sv_list
? _p
: NULL
;
799 } /* end afs_proc_cell_servers_start() */
801 /*****************************************************************************/
803 * move to next cell in cells list
805 static void *afs_proc_cell_servers_next(struct seq_file
*p
, void *v
,
808 struct list_head
*_p
;
809 struct afs_cell
*cell
= p
->private;
811 _enter("cell=%p pos=%Ld", cell
, *_pos
);
816 _p
= v
== (void *) 1 ? cell
->sv_list
.next
: _p
->next
;
818 return _p
!= &cell
->sv_list
? _p
: NULL
;
819 } /* end afs_proc_cell_servers_next() */
821 /*****************************************************************************/
823 * clean up after reading from the cells list
825 static void afs_proc_cell_servers_stop(struct seq_file
*p
, void *v
)
826 __releases(p
->private->sv_lock
)
828 struct afs_cell
*cell
= p
->private;
830 read_unlock(&cell
->sv_lock
);
832 } /* end afs_proc_cell_servers_stop() */
834 /*****************************************************************************/
836 * display a header line followed by a load of volume lines
838 static int afs_proc_cell_servers_show(struct seq_file
*m
, void *v
)
840 struct afs_server
*server
= list_entry(v
, struct afs_server
, link
);
843 /* display header on line 1 */
844 if (v
== (void *) 1) {
845 seq_puts(m
, "USE ADDR STATE\n");
849 /* display one cell per line on subsequent lines */
850 sprintf(ipaddr
, "%u.%u.%u.%u", NIPQUAD(server
->addr
));
851 seq_printf(m
, "%3d %-15.15s %5d\n",
852 atomic_read(&server
->usage
),
858 } /* end afs_proc_cell_servers_show() */