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