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