at v2.6.19 859 lines 22 kB view raw
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 22static struct proc_dir_entry *proc_afs; 23 24 25static int afs_proc_cells_open(struct inode *inode, struct file *file); 26static void *afs_proc_cells_start(struct seq_file *p, loff_t *pos); 27static void *afs_proc_cells_next(struct seq_file *p, void *v, loff_t *pos); 28static void afs_proc_cells_stop(struct seq_file *p, void *v); 29static int afs_proc_cells_show(struct seq_file *m, void *v); 30static ssize_t afs_proc_cells_write(struct file *file, const char __user *buf, 31 size_t size, loff_t *_pos); 32 33static 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 40static 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 48static int afs_proc_rootcell_open(struct inode *inode, struct file *file); 49static int afs_proc_rootcell_release(struct inode *inode, struct file *file); 50static ssize_t afs_proc_rootcell_read(struct file *file, char __user *buf, 51 size_t size, loff_t *_pos); 52static ssize_t afs_proc_rootcell_write(struct file *file, 53 const char __user *buf, 54 size_t size, loff_t *_pos); 55 56static 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 64static int afs_proc_cell_volumes_open(struct inode *inode, struct file *file); 65static int afs_proc_cell_volumes_release(struct inode *inode, 66 struct file *file); 67static void *afs_proc_cell_volumes_start(struct seq_file *p, loff_t *pos); 68static void *afs_proc_cell_volumes_next(struct seq_file *p, void *v, 69 loff_t *pos); 70static void afs_proc_cell_volumes_stop(struct seq_file *p, void *v); 71static int afs_proc_cell_volumes_show(struct seq_file *m, void *v); 72 73static 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 80static 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 87static int afs_proc_cell_vlservers_open(struct inode *inode, 88 struct file *file); 89static int afs_proc_cell_vlservers_release(struct inode *inode, 90 struct file *file); 91static void *afs_proc_cell_vlservers_start(struct seq_file *p, loff_t *pos); 92static void *afs_proc_cell_vlservers_next(struct seq_file *p, void *v, 93 loff_t *pos); 94static void afs_proc_cell_vlservers_stop(struct seq_file *p, void *v); 95static int afs_proc_cell_vlservers_show(struct seq_file *m, void *v); 96 97static 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 104static 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 111static int afs_proc_cell_servers_open(struct inode *inode, struct file *file); 112static int afs_proc_cell_servers_release(struct inode *inode, 113 struct file *file); 114static void *afs_proc_cell_servers_start(struct seq_file *p, loff_t *pos); 115static void *afs_proc_cell_servers_next(struct seq_file *p, void *v, 116 loff_t *pos); 117static void afs_proc_cell_servers_stop(struct seq_file *p, void *v); 118static int afs_proc_cell_servers_show(struct seq_file *m, void *v); 119 120static 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 127static 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 */ 138int 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 */ 178void 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 */ 190static 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 */ 210static 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 */ 235static 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 */ 251static 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 */ 261static 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 */ 282static 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 */ 357static int afs_proc_rootcell_open(struct inode *inode, struct file *file) 358{ 359 return 0; 360} 361 362static int afs_proc_rootcell_release(struct inode *inode, struct file *file) 363{ 364 return 0; 365} 366 367static 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 */ 378static 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 */ 422int 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 */ 470void 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 */ 486static 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 */ 510static 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 */ 527static 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 */ 555static 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 */ 575static 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 */ 587static 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 */ 615static 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 */ 639static 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 */ 657static 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 */ 682static 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 */ 702static 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 */ 714static 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 */ 735static 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 */ 759static 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 */ 777static void *afs_proc_cell_servers_start(struct seq_file *m, loff_t *_pos) 778 __acquires(m->private->sv_lock) 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 */ 806static 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 */ 826static void afs_proc_cell_servers_stop(struct seq_file *p, void *v) 827 __releases(p->private->sv_lock) 828{ 829 struct afs_cell *cell = p->private; 830 831 read_unlock(&cell->sv_lock); 832 833} /* end afs_proc_cell_servers_stop() */ 834 835/*****************************************************************************/ 836/* 837 * display a header line followed by a load of volume lines 838 */ 839static int afs_proc_cell_servers_show(struct seq_file *m, void *v) 840{ 841 struct afs_server *server = list_entry(v, struct afs_server, link); 842 char ipaddr[20]; 843 844 /* display header on line 1 */ 845 if (v == (void *) 1) { 846 seq_puts(m, "USE ADDR STATE\n"); 847 return 0; 848 } 849 850 /* display one cell per line on subsequent lines */ 851 sprintf(ipaddr, "%u.%u.%u.%u", NIPQUAD(server->addr)); 852 seq_printf(m, "%3d %-15.15s %5d\n", 853 atomic_read(&server->usage), 854 ipaddr, 855 server->fs_state 856 ); 857 858 return 0; 859} /* end afs_proc_cell_servers_show() */