Merge to Fedora kernel-2.6.18-1.2224_FC5 patched with stable patch-2.6.18.1-vs2.0...
[linux-2.6.git] / fs / afs / proc.c
1 /* proc.c: /proc interface for AFS
2  *
3  * Copyright (C) 2002 Red Hat, Inc. All Rights Reserved.
4  * Written by David Howells (dhowells@redhat.com)
5  *
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.
10  */
11
12 #include <linux/sched.h>
13 #include <linux/slab.h>
14 #include <linux/module.h>
15 #include <linux/proc_fs.h>
16 #include <linux/seq_file.h>
17 #include "cell.h"
18 #include "volume.h"
19 #include <asm/uaccess.h>
20 #include "internal.h"
21
22 static struct proc_dir_entry *proc_afs;
23
24
25 static int afs_proc_cells_open(struct inode *inode, struct file *file);
26 static void *afs_proc_cells_start(struct seq_file *p, loff_t *pos);
27 static void *afs_proc_cells_next(struct seq_file *p, void *v, loff_t *pos);
28 static void afs_proc_cells_stop(struct seq_file *p, void *v);
29 static int afs_proc_cells_show(struct seq_file *m, void *v);
30 static ssize_t afs_proc_cells_write(struct file *file, const char __user *buf,
31                                     size_t size, loff_t *_pos);
32
33 static struct seq_operations afs_proc_cells_ops = {
34         .start  = afs_proc_cells_start,
35         .next   = afs_proc_cells_next,
36         .stop   = afs_proc_cells_stop,
37         .show   = afs_proc_cells_show,
38 };
39
40 static const struct file_operations afs_proc_cells_fops = {
41         .open           = afs_proc_cells_open,
42         .read           = seq_read,
43         .write          = afs_proc_cells_write,
44         .llseek         = seq_lseek,
45         .release        = seq_release,
46 };
47
48 static int afs_proc_rootcell_open(struct inode *inode, struct file *file);
49 static int afs_proc_rootcell_release(struct inode *inode, struct file *file);
50 static ssize_t afs_proc_rootcell_read(struct file *file, char __user *buf,
51                                       size_t size, loff_t *_pos);
52 static ssize_t afs_proc_rootcell_write(struct file *file,
53                                        const char __user *buf,
54                                        size_t size, loff_t *_pos);
55
56 static const struct file_operations afs_proc_rootcell_fops = {
57         .open           = afs_proc_rootcell_open,
58         .read           = afs_proc_rootcell_read,
59         .write          = afs_proc_rootcell_write,
60         .llseek         = no_llseek,
61         .release        = afs_proc_rootcell_release
62 };
63
64 static int afs_proc_cell_volumes_open(struct inode *inode, struct file *file);
65 static int afs_proc_cell_volumes_release(struct inode *inode,
66                                          struct file *file);
67 static void *afs_proc_cell_volumes_start(struct seq_file *p, loff_t *pos);
68 static void *afs_proc_cell_volumes_next(struct seq_file *p, void *v,
69                                         loff_t *pos);
70 static void afs_proc_cell_volumes_stop(struct seq_file *p, void *v);
71 static int afs_proc_cell_volumes_show(struct seq_file *m, void *v);
72
73 static struct seq_operations afs_proc_cell_volumes_ops = {
74         .start  = afs_proc_cell_volumes_start,
75         .next   = afs_proc_cell_volumes_next,
76         .stop   = afs_proc_cell_volumes_stop,
77         .show   = afs_proc_cell_volumes_show,
78 };
79
80 static const struct file_operations afs_proc_cell_volumes_fops = {
81         .open           = afs_proc_cell_volumes_open,
82         .read           = seq_read,
83         .llseek         = seq_lseek,
84         .release        = afs_proc_cell_volumes_release,
85 };
86
87 static int afs_proc_cell_vlservers_open(struct inode *inode,
88                                         struct file *file);
89 static int afs_proc_cell_vlservers_release(struct inode *inode,
90                                            struct file *file);
91 static void *afs_proc_cell_vlservers_start(struct seq_file *p, loff_t *pos);
92 static void *afs_proc_cell_vlservers_next(struct seq_file *p, void *v,
93                                           loff_t *pos);
94 static void afs_proc_cell_vlservers_stop(struct seq_file *p, void *v);
95 static int afs_proc_cell_vlservers_show(struct seq_file *m, void *v);
96
97 static struct seq_operations afs_proc_cell_vlservers_ops = {
98         .start  = afs_proc_cell_vlservers_start,
99         .next   = afs_proc_cell_vlservers_next,
100         .stop   = afs_proc_cell_vlservers_stop,
101         .show   = afs_proc_cell_vlservers_show,
102 };
103
104 static const struct file_operations afs_proc_cell_vlservers_fops = {
105         .open           = afs_proc_cell_vlservers_open,
106         .read           = seq_read,
107         .llseek         = seq_lseek,
108         .release        = afs_proc_cell_vlservers_release,
109 };
110
111 static int afs_proc_cell_servers_open(struct inode *inode, struct file *file);
112 static int afs_proc_cell_servers_release(struct inode *inode,
113                                          struct file *file);
114 static void *afs_proc_cell_servers_start(struct seq_file *p, loff_t *pos);
115 static void *afs_proc_cell_servers_next(struct seq_file *p, void *v,
116                                         loff_t *pos);
117 static void afs_proc_cell_servers_stop(struct seq_file *p, void *v);
118 static int afs_proc_cell_servers_show(struct seq_file *m, void *v);
119
120 static struct seq_operations afs_proc_cell_servers_ops = {
121         .start  = afs_proc_cell_servers_start,
122         .next   = afs_proc_cell_servers_next,
123         .stop   = afs_proc_cell_servers_stop,
124         .show   = afs_proc_cell_servers_show,
125 };
126
127 static const struct file_operations afs_proc_cell_servers_fops = {
128         .open           = afs_proc_cell_servers_open,
129         .read           = seq_read,
130         .llseek         = seq_lseek,
131         .release        = afs_proc_cell_servers_release,
132 };
133
134 /*****************************************************************************/
135 /*
136  * initialise the /proc/fs/afs/ directory
137  */
138 int afs_proc_init(void)
139 {
140         struct proc_dir_entry *p;
141
142         _enter("");
143
144         proc_afs = proc_mkdir("fs/afs", NULL);
145         if (!proc_afs)
146                 goto error;
147         proc_afs->owner = THIS_MODULE;
148
149         p = create_proc_entry("cells", 0, proc_afs);
150         if (!p)
151                 goto error_proc;
152         p->proc_fops = &afs_proc_cells_fops;
153         p->owner = THIS_MODULE;
154
155         p = create_proc_entry("rootcell", 0, proc_afs);
156         if (!p)
157                 goto error_cells;
158         p->proc_fops = &afs_proc_rootcell_fops;
159         p->owner = THIS_MODULE;
160
161         _leave(" = 0");
162         return 0;
163
164  error_cells:
165         remove_proc_entry("cells", proc_afs);
166  error_proc:
167         remove_proc_entry("fs/afs", NULL);
168  error:
169         _leave(" = -ENOMEM");
170         return -ENOMEM;
171
172 } /* end afs_proc_init() */
173
174 /*****************************************************************************/
175 /*
176  * clean up the /proc/fs/afs/ directory
177  */
178 void afs_proc_cleanup(void)
179 {
180         remove_proc_entry("rootcell", proc_afs);
181         remove_proc_entry("cells", proc_afs);
182
183         remove_proc_entry("fs/afs", NULL);
184
185 } /* end afs_proc_cleanup() */
186
187 /*****************************************************************************/
188 /*
189  * open "/proc/fs/afs/cells" which provides a summary of extant cells
190  */
191 static int afs_proc_cells_open(struct inode *inode, struct file *file)
192 {
193         struct seq_file *m;
194         int ret;
195
196         ret = seq_open(file, &afs_proc_cells_ops);
197         if (ret < 0)
198                 return ret;
199
200         m = file->private_data;
201         m->private = PDE(inode)->data;
202
203         return 0;
204 } /* end afs_proc_cells_open() */
205
206 /*****************************************************************************/
207 /*
208  * set up the iterator to start reading from the cells list and return the
209  * first item
210  */
211 static void *afs_proc_cells_start(struct seq_file *m, loff_t *_pos)
212 {
213         struct list_head *_p;
214         loff_t pos = *_pos;
215
216         /* lock the list against modification */
217         down_read(&afs_proc_cells_sem);
218
219         /* allow for the header line */
220         if (!pos)
221                 return (void *) 1;
222         pos--;
223
224         /* find the n'th element in the list */
225         list_for_each(_p, &afs_proc_cells)
226                 if (!pos--)
227                         break;
228
229         return _p != &afs_proc_cells ? _p : NULL;
230 } /* end afs_proc_cells_start() */
231
232 /*****************************************************************************/
233 /*
234  * move to next cell in cells list
235  */
236 static void *afs_proc_cells_next(struct seq_file *p, void *v, loff_t *pos)
237 {
238         struct list_head *_p;
239
240         (*pos)++;
241
242         _p = v;
243         _p = v == (void *) 1 ? afs_proc_cells.next : _p->next;
244
245         return _p != &afs_proc_cells ? _p : NULL;
246 } /* end afs_proc_cells_next() */
247
248 /*****************************************************************************/
249 /*
250  * clean up after reading from the cells list
251  */
252 static void afs_proc_cells_stop(struct seq_file *p, void *v)
253 {
254         up_read(&afs_proc_cells_sem);
255
256 } /* end afs_proc_cells_stop() */
257
258 /*****************************************************************************/
259 /*
260  * display a header line followed by a load of cell lines
261  */
262 static int afs_proc_cells_show(struct seq_file *m, void *v)
263 {
264         struct afs_cell *cell = list_entry(v, struct afs_cell, proc_link);
265
266         /* display header on line 1 */
267         if (v == (void *) 1) {
268                 seq_puts(m, "USE NAME\n");
269                 return 0;
270         }
271
272         /* display one cell per line on subsequent lines */
273         seq_printf(m, "%3d %s\n", atomic_read(&cell->usage), cell->name);
274
275         return 0;
276 } /* end afs_proc_cells_show() */
277
278 /*****************************************************************************/
279 /*
280  * handle writes to /proc/fs/afs/cells
281  * - to add cells: echo "add <cellname> <IP>[:<IP>][:<IP>]"
282  */
283 static ssize_t afs_proc_cells_write(struct file *file, const char __user *buf,
284                                     size_t size, loff_t *_pos)
285 {
286         char *kbuf, *name, *args;
287         int ret;
288
289         /* start by dragging the command into memory */
290         if (size <= 1 || size >= PAGE_SIZE)
291                 return -EINVAL;
292
293         kbuf = kmalloc(size + 1, GFP_KERNEL);
294         if (!kbuf)
295                 return -ENOMEM;
296
297         ret = -EFAULT;
298         if (copy_from_user(kbuf, buf, size) != 0)
299                 goto done;
300         kbuf[size] = 0;
301
302         /* trim to first NL */
303         name = memchr(kbuf, '\n', size);
304         if (name)
305                 *name = 0;
306
307         /* split into command, name and argslist */
308         name = strchr(kbuf, ' ');
309         if (!name)
310                 goto inval;
311         do {
312                 *name++ = 0;
313         } while(*name == ' ');
314         if (!*name)
315                 goto inval;
316
317         args = strchr(name, ' ');
318         if (!args)
319                 goto inval;
320         do {
321                 *args++ = 0;
322         } while(*args == ' ');
323         if (!*args)
324                 goto inval;
325
326         /* determine command to perform */
327         _debug("cmd=%s name=%s args=%s", kbuf, name, args);
328
329         if (strcmp(kbuf, "add") == 0) {
330                 struct afs_cell *cell;
331                 ret = afs_cell_create(name, args, &cell);
332                 if (ret < 0)
333                         goto done;
334
335                 printk("kAFS: Added new cell '%s'\n", name);
336         }
337         else {
338                 goto inval;
339         }
340
341         ret = size;
342
343  done:
344         kfree(kbuf);
345         _leave(" = %d", ret);
346         return ret;
347
348  inval:
349         ret = -EINVAL;
350         printk("kAFS: Invalid Command on /proc/fs/afs/cells file\n");
351         goto done;
352 } /* end afs_proc_cells_write() */
353
354 /*****************************************************************************/
355 /*
356  * Stubs for /proc/fs/afs/rootcell
357  */
358 static int afs_proc_rootcell_open(struct inode *inode, struct file *file)
359 {
360         return 0;
361 }
362
363 static int afs_proc_rootcell_release(struct inode *inode, struct file *file)
364 {
365         return 0;
366 }
367
368 static ssize_t afs_proc_rootcell_read(struct file *file, char __user *buf,
369                                       size_t size, loff_t *_pos)
370 {
371         return 0;
372 }
373
374 /*****************************************************************************/
375 /*
376  * handle writes to /proc/fs/afs/rootcell
377  * - to initialize rootcell: echo "cell.name:192.168.231.14"
378  */
379 static ssize_t afs_proc_rootcell_write(struct file *file,
380                                        const char __user *buf,
381                                        size_t size, loff_t *_pos)
382 {
383         char *kbuf, *s;
384         int ret;
385
386         /* start by dragging the command into memory */
387         if (size <= 1 || size >= PAGE_SIZE)
388                 return -EINVAL;
389
390         ret = -ENOMEM;
391         kbuf = kmalloc(size + 1, GFP_KERNEL);
392         if (!kbuf)
393                 goto nomem;
394
395         ret = -EFAULT;
396         if (copy_from_user(kbuf, buf, size) != 0)
397                 goto infault;
398         kbuf[size] = 0;
399
400         /* trim to first NL */
401         s = memchr(kbuf, '\n', size);
402         if (s)
403                 *s = 0;
404
405         /* determine command to perform */
406         _debug("rootcell=%s", kbuf);
407
408         ret = afs_cell_init(kbuf);
409         if (ret >= 0)
410                 ret = size;     /* consume everything, always */
411
412  infault:
413         kfree(kbuf);
414  nomem:
415         _leave(" = %d", ret);
416         return ret;
417 } /* end afs_proc_rootcell_write() */
418
419 /*****************************************************************************/
420 /*
421  * initialise /proc/fs/afs/<cell>/
422  */
423 int afs_proc_cell_setup(struct afs_cell *cell)
424 {
425         struct proc_dir_entry *p;
426
427         _enter("%p{%s}", cell, cell->name);
428
429         cell->proc_dir = proc_mkdir(cell->name, proc_afs);
430         if (!cell->proc_dir)
431                 return -ENOMEM;
432
433         p = create_proc_entry("servers", 0, cell->proc_dir);
434         if (!p)
435                 goto error_proc;
436         p->proc_fops = &afs_proc_cell_servers_fops;
437         p->owner = THIS_MODULE;
438         p->data = cell;
439
440         p = create_proc_entry("vlservers", 0, cell->proc_dir);
441         if (!p)
442                 goto error_servers;
443         p->proc_fops = &afs_proc_cell_vlservers_fops;
444         p->owner = THIS_MODULE;
445         p->data = cell;
446
447         p = create_proc_entry("volumes", 0, cell->proc_dir);
448         if (!p)
449                 goto error_vlservers;
450         p->proc_fops = &afs_proc_cell_volumes_fops;
451         p->owner = THIS_MODULE;
452         p->data = cell;
453
454         _leave(" = 0");
455         return 0;
456
457  error_vlservers:
458         remove_proc_entry("vlservers", cell->proc_dir);
459  error_servers:
460         remove_proc_entry("servers", cell->proc_dir);
461  error_proc:
462         remove_proc_entry(cell->name, proc_afs);
463         _leave(" = -ENOMEM");
464         return -ENOMEM;
465 } /* end afs_proc_cell_setup() */
466
467 /*****************************************************************************/
468 /*
469  * remove /proc/fs/afs/<cell>/
470  */
471 void afs_proc_cell_remove(struct afs_cell *cell)
472 {
473         _enter("");
474
475         remove_proc_entry("volumes", cell->proc_dir);
476         remove_proc_entry("vlservers", cell->proc_dir);
477         remove_proc_entry("servers", cell->proc_dir);
478         remove_proc_entry(cell->name, proc_afs);
479
480         _leave("");
481 } /* end afs_proc_cell_remove() */
482
483 /*****************************************************************************/
484 /*
485  * open "/proc/fs/afs/<cell>/volumes" which provides a summary of extant cells
486  */
487 static int afs_proc_cell_volumes_open(struct inode *inode, struct file *file)
488 {
489         struct afs_cell *cell;
490         struct seq_file *m;
491         int ret;
492
493         cell = afs_get_cell_maybe((struct afs_cell **) &PDE(inode)->data);
494         if (!cell)
495                 return -ENOENT;
496
497         ret = seq_open(file, &afs_proc_cell_volumes_ops);
498         if (ret < 0)
499                 return ret;
500
501         m = file->private_data;
502         m->private = cell;
503
504         return 0;
505 } /* end afs_proc_cell_volumes_open() */
506
507 /*****************************************************************************/
508 /*
509  * close the file and release the ref to the cell
510  */
511 static int afs_proc_cell_volumes_release(struct inode *inode, struct file *file)
512 {
513         struct afs_cell *cell = PDE(inode)->data;
514         int ret;
515
516         ret = seq_release(inode,file);
517
518         afs_put_cell(cell);
519
520         return ret;
521 } /* end afs_proc_cell_volumes_release() */
522
523 /*****************************************************************************/
524 /*
525  * set up the iterator to start reading from the cells list and return the
526  * first item
527  */
528 static void *afs_proc_cell_volumes_start(struct seq_file *m, loff_t *_pos)
529 {
530         struct list_head *_p;
531         struct afs_cell *cell = m->private;
532         loff_t pos = *_pos;
533
534         _enter("cell=%p pos=%Ld", cell, *_pos);
535
536         /* lock the list against modification */
537         down_read(&cell->vl_sem);
538
539         /* allow for the header line */
540         if (!pos)
541                 return (void *) 1;
542         pos--;
543
544         /* find the n'th element in the list */
545         list_for_each(_p, &cell->vl_list)
546                 if (!pos--)
547                         break;
548
549         return _p != &cell->vl_list ? _p : NULL;
550 } /* end afs_proc_cell_volumes_start() */
551
552 /*****************************************************************************/
553 /*
554  * move to next cell in cells list
555  */
556 static void *afs_proc_cell_volumes_next(struct seq_file *p, void *v,
557                                         loff_t *_pos)
558 {
559         struct list_head *_p;
560         struct afs_cell *cell = p->private;
561
562         _enter("cell=%p pos=%Ld", cell, *_pos);
563
564         (*_pos)++;
565
566         _p = v;
567         _p = v == (void *) 1 ? cell->vl_list.next : _p->next;
568
569         return _p != &cell->vl_list ? _p : NULL;
570 } /* end afs_proc_cell_volumes_next() */
571
572 /*****************************************************************************/
573 /*
574  * clean up after reading from the cells list
575  */
576 static void afs_proc_cell_volumes_stop(struct seq_file *p, void *v)
577 {
578         struct afs_cell *cell = p->private;
579
580         up_read(&cell->vl_sem);
581
582 } /* end afs_proc_cell_volumes_stop() */
583
584 /*****************************************************************************/
585 /*
586  * display a header line followed by a load of volume lines
587  */
588 static int afs_proc_cell_volumes_show(struct seq_file *m, void *v)
589 {
590         struct afs_vlocation *vlocation =
591                 list_entry(v, struct afs_vlocation, link);
592
593         /* display header on line 1 */
594         if (v == (void *) 1) {
595                 seq_puts(m, "USE VLID[0]  VLID[1]  VLID[2]  NAME\n");
596                 return 0;
597         }
598
599         /* display one cell per line on subsequent lines */
600         seq_printf(m, "%3d %08x %08x %08x %s\n",
601                    atomic_read(&vlocation->usage),
602                    vlocation->vldb.vid[0],
603                    vlocation->vldb.vid[1],
604                    vlocation->vldb.vid[2],
605                    vlocation->vldb.name
606                    );
607
608         return 0;
609 } /* end afs_proc_cell_volumes_show() */
610
611 /*****************************************************************************/
612 /*
613  * open "/proc/fs/afs/<cell>/vlservers" which provides a list of volume
614  * location server
615  */
616 static int afs_proc_cell_vlservers_open(struct inode *inode, struct file *file)
617 {
618         struct afs_cell *cell;
619         struct seq_file *m;
620         int ret;
621
622         cell = afs_get_cell_maybe((struct afs_cell**)&PDE(inode)->data);
623         if (!cell)
624                 return -ENOENT;
625
626         ret = seq_open(file,&afs_proc_cell_vlservers_ops);
627         if (ret<0)
628                 return ret;
629
630         m = file->private_data;
631         m->private = cell;
632
633         return 0;
634 } /* end afs_proc_cell_vlservers_open() */
635
636 /*****************************************************************************/
637 /*
638  * close the file and release the ref to the cell
639  */
640 static int afs_proc_cell_vlservers_release(struct inode *inode,
641                                            struct file *file)
642 {
643         struct afs_cell *cell = PDE(inode)->data;
644         int ret;
645
646         ret = seq_release(inode,file);
647
648         afs_put_cell(cell);
649
650         return ret;
651 } /* end afs_proc_cell_vlservers_release() */
652
653 /*****************************************************************************/
654 /*
655  * set up the iterator to start reading from the cells list and return the
656  * first item
657  */
658 static void *afs_proc_cell_vlservers_start(struct seq_file *m, loff_t *_pos)
659 {
660         struct afs_cell *cell = m->private;
661         loff_t pos = *_pos;
662
663         _enter("cell=%p pos=%Ld", cell, *_pos);
664
665         /* lock the list against modification */
666         down_read(&cell->vl_sem);
667
668         /* allow for the header line */
669         if (!pos)
670                 return (void *) 1;
671         pos--;
672
673         if (pos >= cell->vl_naddrs)
674                 return NULL;
675
676         return &cell->vl_addrs[pos];
677 } /* end afs_proc_cell_vlservers_start() */
678
679 /*****************************************************************************/
680 /*
681  * move to next cell in cells list
682  */
683 static void *afs_proc_cell_vlservers_next(struct seq_file *p, void *v,
684                                           loff_t *_pos)
685 {
686         struct afs_cell *cell = p->private;
687         loff_t pos;
688
689         _enter("cell=%p{nad=%u} pos=%Ld", cell, cell->vl_naddrs, *_pos);
690
691         pos = *_pos;
692         (*_pos)++;
693         if (pos >= cell->vl_naddrs)
694                 return NULL;
695
696         return &cell->vl_addrs[pos];
697 } /* end afs_proc_cell_vlservers_next() */
698
699 /*****************************************************************************/
700 /*
701  * clean up after reading from the cells list
702  */
703 static void afs_proc_cell_vlservers_stop(struct seq_file *p, void *v)
704 {
705         struct afs_cell *cell = p->private;
706
707         up_read(&cell->vl_sem);
708
709 } /* end afs_proc_cell_vlservers_stop() */
710
711 /*****************************************************************************/
712 /*
713  * display a header line followed by a load of volume lines
714  */
715 static int afs_proc_cell_vlservers_show(struct seq_file *m, void *v)
716 {
717         struct in_addr *addr = v;
718
719         /* display header on line 1 */
720         if (v == (struct in_addr *) 1) {
721                 seq_puts(m, "ADDRESS\n");
722                 return 0;
723         }
724
725         /* display one cell per line on subsequent lines */
726         seq_printf(m, "%u.%u.%u.%u\n", NIPQUAD(addr->s_addr));
727
728         return 0;
729 } /* end afs_proc_cell_vlservers_show() */
730
731 /*****************************************************************************/
732 /*
733  * open "/proc/fs/afs/<cell>/servers" which provides a summary of active
734  * servers
735  */
736 static int afs_proc_cell_servers_open(struct inode *inode, struct file *file)
737 {
738         struct afs_cell *cell;
739         struct seq_file *m;
740         int ret;
741
742         cell = afs_get_cell_maybe((struct afs_cell **) &PDE(inode)->data);
743         if (!cell)
744                 return -ENOENT;
745
746         ret = seq_open(file, &afs_proc_cell_servers_ops);
747         if (ret < 0)
748                 return ret;
749
750         m = file->private_data;
751         m->private = cell;
752
753         return 0;
754 } /* end afs_proc_cell_servers_open() */
755
756 /*****************************************************************************/
757 /*
758  * close the file and release the ref to the cell
759  */
760 static int afs_proc_cell_servers_release(struct inode *inode,
761                                          struct file *file)
762 {
763         struct afs_cell *cell = PDE(inode)->data;
764         int ret;
765
766         ret = seq_release(inode, file);
767
768         afs_put_cell(cell);
769
770         return ret;
771 } /* end afs_proc_cell_servers_release() */
772
773 /*****************************************************************************/
774 /*
775  * set up the iterator to start reading from the cells list and return the
776  * first item
777  */
778 static void *afs_proc_cell_servers_start(struct seq_file *m, loff_t *_pos)
779 {
780         struct list_head *_p;
781         struct afs_cell *cell = m->private;
782         loff_t pos = *_pos;
783
784         _enter("cell=%p pos=%Ld", cell, *_pos);
785
786         /* lock the list against modification */
787         read_lock(&cell->sv_lock);
788
789         /* allow for the header line */
790         if (!pos)
791                 return (void *) 1;
792         pos--;
793
794         /* find the n'th element in the list */
795         list_for_each(_p, &cell->sv_list)
796                 if (!pos--)
797                         break;
798
799         return _p != &cell->sv_list ? _p : NULL;
800 } /* end afs_proc_cell_servers_start() */
801
802 /*****************************************************************************/
803 /*
804  * move to next cell in cells list
805  */
806 static void *afs_proc_cell_servers_next(struct seq_file *p, void *v,
807                                         loff_t *_pos)
808 {
809         struct list_head *_p;
810         struct afs_cell *cell = p->private;
811
812         _enter("cell=%p pos=%Ld", cell, *_pos);
813
814         (*_pos)++;
815
816         _p = v;
817         _p = v == (void *) 1 ? cell->sv_list.next : _p->next;
818
819         return _p != &cell->sv_list ? _p : NULL;
820 } /* end afs_proc_cell_servers_next() */
821
822 /*****************************************************************************/
823 /*
824  * clean up after reading from the cells list
825  */
826 static void afs_proc_cell_servers_stop(struct seq_file *p, void *v)
827 {
828         struct afs_cell *cell = p->private;
829
830         read_unlock(&cell->sv_lock);
831
832 } /* end afs_proc_cell_servers_stop() */
833
834 /*****************************************************************************/
835 /*
836  * display a header line followed by a load of volume lines
837  */
838 static int afs_proc_cell_servers_show(struct seq_file *m, void *v)
839 {
840         struct afs_server *server = list_entry(v, struct afs_server, link);
841         char ipaddr[20];
842
843         /* display header on line 1 */
844         if (v == (void *) 1) {
845                 seq_puts(m, "USE ADDR            STATE\n");
846                 return 0;
847         }
848
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),
853                    ipaddr,
854                    server->fs_state
855                    );
856
857         return 0;
858 } /* end afs_proc_cell_servers_show() */