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