1 #include <u.h> 2 #include <libc.h> 3 #include <auth.h> 4 #include <fcall.h> 5 6 #include "cformat.h" 7 #include "lru.h" 8 #include "bcache.h" 9 #include "disk.h" 10 #include "inode.h" 11 #include "file.h" 12 #include "stats.h" 13 14 enum 15 { 16 Nfid= 10240, 17 }; 18 19 /* maximum length of a file */ 20 #define MAXLEN 0x7fffffffffffffffLL 21 22 typedef struct Mfile Mfile; 23 typedef struct Ram Ram; 24 typedef struct P9fs P9fs; 25 typedef struct Clunkitem Clunkitem; 26 27 struct Mfile 28 { 29 Qid qid; 30 char busy; 31 }; 32 33 Mfile mfile[Nfid]; 34 Icache ic; 35 int debug, statson; 36 37 struct P9fs 38 { 39 int fd[2]; 40 Fcall rhdr; 41 Fcall thdr; 42 long len; 43 char *name; 44 }; 45 46 /* 47 * We do clunks asynchronously (we don't wait for the rclunk from 48 * the server). The fid, however, may not be reused until the Rcklunk 49 * is in. We therefore remember outstanding Rclunks in a linked 50 * list of Clunkitems. It is hard to imagine having more than one or two 51 * outstanding. We pretend clunks always succeed. 52 */ 53 struct Clunkitem 54 { 55 u32int fid; 56 ushort tag; 57 Clunkitem *next; 58 }; 59 Clunkitem *clunklist; 60 61 62 P9fs c; /* client conversation */ 63 P9fs s; /* server conversation */ 64 65 struct Cfsstat cfsstat, cfsprev; 66 char statbuf[2048]; 67 int statlen; 68 69 #define MAXFDATA 8192 /* i/o size for read/write */ 70 71 int messagesize = MAXFDATA+IOHDRSZ; 72 73 uchar datasnd[MAXFDATA + IOHDRSZ]; 74 uchar datarcv[MAXFDATA + IOHDRSZ]; 75 76 Qid rootqid; 77 Qid ctlqid = {0x5555555555555555LL, 0, 0}; 78 79 Clunkitem *getclunk(u32int, ushort); 80 void rversion(void); 81 void rauth(Mfile*); 82 void rflush(void); 83 void rattach(Mfile*); 84 void rwalk(Mfile*); 85 void ropen(Mfile*); 86 void rcreate(Mfile*); 87 void rread(Mfile*); 88 void rwrite(Mfile*); 89 void rclunk(Mfile*); 90 void rremove(Mfile*); 91 void rstat(Mfile*); 92 void rwstat(Mfile*); 93 void error(char*, ...); 94 void warning(char*); 95 void mountinit(char*, char*); 96 Clunkitem *notclunked(u32int, ushort); 97 void io(void); 98 void putclunk(u32int, ushort); 99 void sendreply(char*); 100 void sendmsg(P9fs*, Fcall*); 101 u32int rcvclunk(P9fs*, Fcall*); 102 void rcvmsg(P9fs*, Fcall*); 103 int delegate(void); 104 int askserver(void); 105 void cachesetup(int, char*); 106 int ctltest(Mfile*); 107 void genstats(void); 108 109 char *mname[]={ 110 [Tversion] "Tversion", 111 [Tauth] "Tauth", 112 [Tflush] "Tflush", 113 [Tattach] "Tattach", 114 [Twalk] "Twalk", 115 [Topen] "Topen", 116 [Tcreate] "Tcreate", 117 [Tclunk] "Tclunk", 118 [Tread] "Tread", 119 [Twrite] "Twrite", 120 [Tremove] "Tremove", 121 [Tstat] "Tstat", 122 [Twstat] "Twstat", 123 [Rversion] "Rversion", 124 [Rauth] "Rauth", 125 [Rerror] "Rerror", 126 [Rflush] "Rflush", 127 [Rattach] "Rattach", 128 [Rwalk] "Rwalk", 129 [Ropen] "Ropen", 130 [Rcreate] "Rcreate", 131 [Rclunk] "Rclunk", 132 [Rread] "Rread", 133 [Rwrite] "Rwrite", 134 [Rremove] "Rremove", 135 [Rstat] "Rstat", 136 [Rwstat] "Rwstat", 137 0, 138 }; 139 140 void 141 usage(void) 142 { 143 fprint(2, "usage:\tcfs -s [-rd] [-f partition]"); 144 fprint(2, "\tcfs [-rd] [-f partition] [-a netaddr] [mt-pt]\n"); 145 exits("usage"); 146 } 147 148 void 149 main(int argc, char *argv[]) 150 { 151 int std; 152 int format; 153 char *part; 154 char *server; 155 char *mtpt; 156 157 std = 0; 158 format = 0; 159 part = "/dev/sdC0/cache"; 160 server = "il!emelie"; 161 mtpt = "/tmp"; 162 163 ARGBEGIN{ 164 case 'a': 165 server = ARGF(); 166 if(server == 0) 167 usage(); 168 break; 169 case 'S': 170 statson = 1; 171 break; 172 case 's': 173 std = 1; 174 break; 175 case 'r': 176 format = 1; 177 break; 178 case 'f': 179 part = ARGF(); 180 if(part == 0) 181 usage(); 182 break; 183 case 'd': 184 debug = 1; 185 break; 186 default: 187 usage(); 188 }ARGEND 189 if(argc && *argv) 190 mtpt = *argv; 191 192 if(debug) 193 fmtinstall('F', fcallfmt); 194 195 cachesetup(format, part); 196 197 c.name = "client"; 198 s.name = "server"; 199 if(std){ 200 c.fd[0] = c.fd[1] = 1; 201 s.fd[0] = s.fd[1] = 0; 202 }else 203 mountinit(server, mtpt); 204 205 switch(fork()){ 206 case 0: 207 io(); 208 exits(""); 209 case -1: 210 error("fork"); 211 default: 212 exits(""); 213 } 214 } 215 216 void 217 cachesetup(int format, char *partition) 218 { 219 int f; 220 int secsize; 221 int inodes; 222 int blocksize; 223 224 secsize = 512; 225 inodes = 1024; 226 blocksize = 4*1024; 227 228 f = open(partition, ORDWR); 229 if(f < 0) 230 error("opening partition"); 231 232 if(format || iinit(&ic, f, secsize)<0){ 233 if(iformat(&ic, f, inodes, "bootes", blocksize, secsize) < 0) 234 error("formatting failed"); 235 } 236 } 237 238 void 239 mountinit(char *server, char *mountpoint) 240 { 241 int p[2]; 242 243 /* 244 * grab a channel and call up the file server 245 */ 246 s.fd[0] = s.fd[1] = dial(netmkaddr(server, 0, "9fs"), 0, 0, 0); 247 if(s.fd[0] < 0) 248 error("opening data"); 249 250 /* 251 * mount onto name space 252 */ 253 if(pipe(p) < 0) 254 error("pipe failed"); 255 switch(fork()){ 256 case 0: 257 break; 258 default: 259 if(amount(p[1], mountpoint, MREPL|MCREATE, "") < 0) 260 error("mount failed"); 261 exits(0); 262 case -1: 263 error("fork failed\n"); 264 /*BUG: no wait!*/ 265 } 266 c.fd[0] = c.fd[1] = p[0]; 267 } 268 269 void 270 io(void) 271 { 272 int type; 273 Mfile *mf; 274 loop: 275 rcvmsg(&c, &c.thdr); 276 277 type = c.thdr.type; 278 279 if(statson){ 280 cfsstat.cm[type].n++; 281 cfsstat.cm[type].s = nsec(); 282 } 283 284 mf = &mfile[c.thdr.fid]; 285 switch(type){ 286 default: 287 error("type"); 288 break; 289 case Tversion: 290 rversion(); 291 break; 292 case Tauth: 293 mf = &mfile[c.thdr.afid]; 294 rauth(mf); 295 break; 296 case Tflush: 297 rflush(); 298 break; 299 case Tattach: 300 rattach(mf); 301 break; 302 case Twalk: 303 rwalk(mf); 304 break; 305 case Topen: 306 ropen(mf); 307 break; 308 case Tcreate: 309 rcreate(mf); 310 break; 311 case Tread: 312 rread(mf); 313 break; 314 case Twrite: 315 rwrite(mf); 316 break; 317 case Tclunk: 318 rclunk(mf); 319 break; 320 case Tremove: 321 rremove(mf); 322 break; 323 case Tstat: 324 rstat(mf); 325 break; 326 case Twstat: 327 rwstat(mf); 328 break; 329 } 330 if(statson){ 331 cfsstat.cm[type].t += nsec() -cfsstat.cm[type].s; 332 } 333 goto loop; 334 } 335 336 void 337 rversion(void) 338 { 339 if(messagesize > c.thdr.msize) 340 messagesize = c.thdr.msize; 341 c.thdr.msize = messagesize; /* set downstream size */ 342 delegate(); 343 } 344 345 void 346 rauth(Mfile *mf) 347 { 348 Mfile *amf; 349 350 if(delegate() == 0){ 351 if (c.thdr.afid != NOFID){ 352 amf = &mfile[c.thdr.afid]; 353 if(amf->busy) 354 error("rauth afid on used channel"); 355 amf->qid = s.rhdr.aqid; 356 amf->busy = 1; 357 } 358 mf->qid = s.rhdr.qid; 359 mf->busy = 1; 360 } 361 } 362 363 void 364 rflush(void) /* synchronous so easy */ 365 { 366 sendreply(0); 367 } 368 369 void 370 rattach(Mfile *mf) 371 { 372 if(delegate() == 0){ 373 mf->qid = s.rhdr.qid; 374 mf->busy = 1; 375 if (statson == 1){ 376 statson++; 377 rootqid = mf->qid; 378 } 379 } 380 } 381 382 void 383 rwalk(Mfile *mf) 384 { 385 Mfile *nmf; 386 u32int fid; 387 388 nmf = nil; 389 if(statson 390 && mf->qid.type == rootqid.type && mf->qid.path == rootqid.path 391 && c.thdr.nwname == 1 && strcmp(c.thdr.wname[0], "cfsctl") == 0){ 392 /* This is the ctl file */ 393 nmf = &mfile[c.thdr.newfid]; 394 if(c.thdr.newfid != c.thdr.fid && nmf->busy) 395 error("clone to used channel"); 396 nmf = &mfile[c.thdr.newfid]; 397 nmf->qid = ctlqid; 398 nmf->busy = 1; 399 c.rhdr.nwqid = 1; 400 c.rhdr.wqid[0] = ctlqid; 401 sendreply(0); 402 return; 403 } 404 if(c.thdr.newfid != c.thdr.fid){ 405 if(c.thdr.newfid<0 || Nfid<=c.thdr.newfid) 406 error("clone nfid out of range"); 407 if(notclunked(c.thdr.newfid, NOTAG)){ 408 /* wait for any outstanding clunks on this new fid */ 409 do{ 410 fid = rcvclunk(&s, &s.rhdr); 411 }while(fid != c.thdr.newfid); 412 } 413 nmf = &mfile[c.thdr.newfid]; 414 if(nmf->busy) 415 error("clone to used channel"); 416 nmf = &mfile[c.thdr.newfid]; 417 nmf->qid = mf->qid; 418 nmf->busy = 1; 419 mf = nmf; /* Walk mf */ 420 } 421 422 if(delegate() < 0){ /* complete failure */ 423 if(nmf) 424 nmf->busy = 0; 425 return; 426 } 427 428 if(s.rhdr.nwqid == c.thdr.nwname){ /* complete success */ 429 if(s.rhdr.nwqid > 0) 430 mf->qid = s.rhdr.wqid[s.rhdr.nwqid-1]; 431 return; 432 } 433 434 /* partial success; release fid */ 435 if(nmf) 436 nmf->busy = 0; 437 } 438 439 void 440 ropen(Mfile *mf) 441 { 442 if(statson && ctltest(mf)){ 443 /* Opening ctl file */ 444 if(c.thdr.mode != OREAD){ 445 sendreply("does not exist"); 446 return; 447 } 448 c.rhdr.qid = ctlqid; 449 c.rhdr.iounit = 0; 450 sendreply(0); 451 genstats(); 452 return; 453 } 454 if(delegate() == 0){ 455 mf->qid = s.rhdr.qid; 456 if(c.thdr.mode & OTRUNC) 457 iget(&ic, mf->qid); 458 } 459 } 460 461 void 462 rcreate(Mfile *mf) 463 { 464 if(statson && ctltest(mf)){ 465 sendreply("exists"); 466 return; 467 } 468 if(delegate() == 0){ 469 mf->qid = s.rhdr.qid; 470 mf->qid.vers++; 471 } 472 } 473 474 void 475 rclunk(Mfile *mf) 476 { 477 if(!mf->busy){ 478 sendreply(0); 479 return; 480 } 481 mf->busy = 0; 482 if(statson && ctltest(mf)){ 483 sendreply(0); 484 return; 485 } 486 if(statson){ 487 cfsstat.sm[Tclunk].n++; 488 cfsstat.sm[Tclunk].s = nsec(); 489 } 490 /* forward clunk to server */ 491 sendmsg(&s, &c.thdr); 492 /* record outstanding rclunk from server */ 493 putclunk(c.thdr.fid, c.thdr.tag); 494 /* reply immediately */ 495 sendreply(0); 496 } 497 498 void 499 rremove(Mfile *mf) 500 { 501 if(statson && ctltest(mf)){ 502 sendreply("not removed"); 503 return; 504 } 505 mf->busy = 0; 506 delegate(); 507 } 508 509 void 510 rread(Mfile *mf) 511 { 512 int cnt; 513 long off, first; 514 char *cp; 515 Ibuf *b; 516 long n; 517 char data[MAXFDATA]; 518 int done; 519 520 first = off = c.thdr.offset; 521 cnt = c.thdr.count; 522 523 if(statson && ctltest(mf)){ 524 if(cnt > statlen-off) 525 c.rhdr.count = statlen-off; 526 else 527 c.rhdr.count = cnt; 528 if(c.rhdr.count < 0){ 529 sendreply("eof"); 530 return; 531 } 532 c.rhdr.data = statbuf + off; 533 sendreply(0); 534 return; 535 } 536 if(mf->qid.type & (QTDIR|QTAUTH)){ 537 delegate(); 538 if (statson) { 539 cfsstat.ndirread++; 540 if(c.rhdr.count > 0){ 541 cfsstat.bytesread += c.rhdr.count; 542 cfsstat.bytesfromdirs += c.rhdr.count; 543 } 544 } 545 return; 546 } 547 548 b = iget(&ic, mf->qid); 549 if(b == 0){ 550 DPRINT(2, "delegating read\n"); 551 delegate(); 552 if (statson){ 553 cfsstat.ndelegateread++; 554 if(c.rhdr.count > 0){ 555 cfsstat.bytesread += c.rhdr.count; 556 cfsstat.bytesfromserver += c.rhdr.count; 557 } 558 } 559 return; 560 } 561 562 cp = data; 563 done = 0; 564 while(cnt>0 && !done){ 565 if(off >= b->inode.length){ 566 DPRINT(2, "offset %ld greater than length %lld\n", off, b->inode.length); 567 break; 568 } 569 n = fread(&ic, b, cp, off, cnt); 570 if(n <= 0){ 571 n = -n; 572 if(n==0 || n>cnt) 573 n = cnt; 574 DPRINT(2, "fetch %ld bytes of data from server at offset %ld\n", n, off); 575 s.thdr.type = c.thdr.type; 576 s.thdr.fid = c.thdr.fid; 577 s.thdr.tag = c.thdr.tag; 578 s.thdr.offset = off; 579 s.thdr.count = n; 580 if(statson){ 581 cfsstat.ndelegateread++; 582 } 583 if(askserver() < 0){ 584 sendreply(s.rhdr.ename); 585 return; 586 } 587 if(s.rhdr.count != n) 588 done = 1; 589 n = s.rhdr.count; 590 if(n == 0){ 591 /* end of file */ 592 if(b->inode.length > off){ 593 DPRINT(2, "file %llud.%ld, length %ld\n", 594 b->inode.qid.path, b->inode.qid.vers, off); 595 b->inode.length = off; 596 } 597 break; 598 } 599 memmove(cp, s.rhdr.data, n); 600 fwrite(&ic, b, cp, off, n); 601 if (statson){ 602 cfsstat.bytestocache += n; 603 cfsstat.bytesfromserver += n; 604 } 605 }else{ 606 DPRINT(2, "fetched %ld bytes from cache\n", n); 607 if(statson){ 608 cfsstat.bytesfromcache += n; 609 } 610 } 611 cnt -= n; 612 off += n; 613 cp += n; 614 } 615 c.rhdr.data = data; 616 c.rhdr.count = off - first; 617 if(statson){ 618 cfsstat.bytesread += c.rhdr.count; 619 } 620 sendreply(0); 621 } 622 623 void 624 rwrite(Mfile *mf) 625 { 626 Ibuf *b; 627 char buf[MAXFDATA]; 628 629 if(statson && ctltest(mf)){ 630 sendreply("read only"); 631 return; 632 } 633 if(mf->qid.type & (QTDIR|QTAUTH)){ 634 delegate(); 635 if(statson && c.rhdr.count > 0) 636 cfsstat.byteswritten += c.rhdr.count; 637 return; 638 } 639 640 memmove(buf, c.thdr.data, c.thdr.count); 641 if(delegate() < 0) 642 return; 643 644 if(s.rhdr.count > 0) 645 cfsstat.byteswritten += s.rhdr.count; 646 if(mf->qid.type & QTAPPEND) /* don't modify our cache for append-only data; always read from server*/ 647 return; 648 b = iget(&ic, mf->qid); 649 if(b == 0) 650 return; 651 if (b->inode.length < c.thdr.offset + s.rhdr.count) 652 b->inode.length = c.thdr.offset + s.rhdr.count; 653 mf->qid.vers++; 654 if (s.rhdr.count != c.thdr.count) 655 syslog(0, "cfslog", "rhdr.count %ud, thdr.count %ud\n", 656 s.rhdr.count, c.thdr.count); 657 if(fwrite(&ic, b, buf, c.thdr.offset, s.rhdr.count) == s.rhdr.count){ 658 iinc(&ic, b); 659 if(statson) 660 cfsstat.bytestocache += s.rhdr.count; 661 } 662 } 663 664 void 665 rstat(Mfile *mf) 666 { 667 Dir d; 668 669 if(statson && ctltest(mf)){ 670 genstats(); 671 d.qid = ctlqid; 672 d.mode = 0444; 673 d.length = statlen; /* would be nice to do better */ 674 d.name = "cfsctl"; 675 d.uid = "none"; 676 d.gid = "none"; 677 d.muid = "none"; 678 d.atime = time(nil); 679 d.mtime = d.atime; 680 c.rhdr.nstat = convD2M(&d, c.rhdr.stat, sizeof(c.rhdr) - (c.rhdr.stat - (uchar*)&c.rhdr)); 681 sendreply(0); 682 return; 683 } 684 if(delegate() == 0){ 685 Ibuf *b; 686 687 convM2D(s.rhdr.stat, s.rhdr.nstat , &d, nil); 688 mf->qid = d.qid; 689 b = iget(&ic, mf->qid); 690 if(b) 691 b->inode.length = d.length; 692 } 693 } 694 695 void 696 rwstat(Mfile *mf) 697 { 698 Ibuf *b; 699 700 if(statson && ctltest(mf)){ 701 sendreply("read only"); 702 return; 703 } 704 delegate(); 705 if(b = iget(&ic, mf->qid)) 706 b->inode.length = MAXLEN; 707 } 708 709 void 710 error(char *fmt, ...) { 711 va_list arg; 712 static char buf[2048]; 713 714 va_start(arg, fmt); 715 vseprint(buf, buf+sizeof(buf), fmt, arg); 716 va_end(arg); 717 fprint(2, "%s: %s\n", argv0, buf); 718 exits("error"); 719 } 720 721 void 722 warning(char *s) 723 { 724 fprint(2, "cfs: %s: %r\n", s); 725 } 726 727 /* 728 * send a reply to the client 729 */ 730 void 731 sendreply(char *err) 732 { 733 734 if(err){ 735 c.rhdr.type = Rerror; 736 c.rhdr.ename = err; 737 }else{ 738 c.rhdr.type = c.thdr.type+1; 739 c.rhdr.fid = c.thdr.fid; 740 } 741 c.rhdr.tag = c.thdr.tag; 742 sendmsg(&c, &c.rhdr); 743 } 744 745 /* 746 * send a request to the server, get the reply, and send that to 747 * the client 748 */ 749 int 750 delegate(void) 751 { 752 int type; 753 754 type = c.thdr.type; 755 if(statson){ 756 cfsstat.sm[type].n++; 757 cfsstat.sm[type].s = nsec(); 758 } 759 760 sendmsg(&s, &c.thdr); 761 rcvmsg(&s, &s.rhdr); 762 763 if(statson){ 764 cfsstat.sm[type].t += nsec() - cfsstat.sm[type].s; 765 } 766 767 sendmsg(&c, &s.rhdr); 768 return c.thdr.type+1 == s.rhdr.type ? 0 : -1; 769 } 770 771 /* 772 * send a request to the server and get a reply 773 */ 774 int 775 askserver(void) 776 { 777 int type; 778 779 s.thdr.tag = c.thdr.tag; 780 781 type = s.thdr.type; 782 if(statson){ 783 cfsstat.sm[type].n++; 784 cfsstat.sm[type].s = nsec(); 785 } 786 787 sendmsg(&s, &s.thdr); 788 rcvmsg(&s, &s.rhdr); 789 790 if(statson){ 791 cfsstat.sm[type].t += nsec() - cfsstat.sm[type].s; 792 } 793 794 return s.thdr.type+1 == s.rhdr.type ? 0 : -1; 795 } 796 797 /* 798 * send/receive messages with logging 799 */ 800 void 801 sendmsg(P9fs *p, Fcall *f) 802 { 803 DPRINT(2, "->%s: %F\n", p->name, f); 804 805 p->len = convS2M(f, datasnd, messagesize); 806 if(p->len <= 0) 807 error("convS2M"); 808 if(write(p->fd[1], datasnd, p->len)!=p->len) 809 error("sendmsg"); 810 } 811 812 void 813 dump(uchar *p, int len) 814 { 815 fprint(2, "%d bytes", len); 816 while(len > 0){ 817 fprint(2, " %.2ux", *p++); 818 len--; 819 } 820 fprint(2, "\n"); 821 } 822 823 u32int 824 rcvclunk(P9fs *p, Fcall *f) 825 { 826 Clunkitem *ci; 827 int rlen; 828 char buf[128]; 829 u32int fid; 830 831 p->len = read9pmsg(p->fd[0], datarcv, sizeof(datarcv)); 832 if(p->len <= 0){ 833 sprint(buf, "read9pmsg(%d)->%ld: %r", p->fd[0], p->len); 834 error(buf); 835 } 836 837 if((rlen = convM2S(datarcv, p->len, f)) != p->len) 838 error("rcvmsg format error, expected length %d, got %d", rlen, p->len); 839 if(f->type != Rclunk && f->type != Rerror) 840 sysfatal("rcvclunk: unexpected message, type %d", f->type); 841 if((ci = getclunk(NOFID, f->tag)) == nil) 842 sysfatal("rcvclunk: unexpected clunk, %d", f->tag); 843 fid = ci->fid; 844 free(ci); 845 return fid; 846 } 847 848 void 849 rcvmsg(P9fs *p, Fcall *f) 850 { 851 Clunkitem *ci; 852 int olen, rlen; 853 char buf[128]; 854 855 olen = p->len; 856 again: 857 p->len = read9pmsg(p->fd[0], datarcv, sizeof(datarcv)); 858 if(p->len <= 0){ 859 sprint(buf, "read9pmsg(%d)->%ld: %r", p->fd[0], p->len); 860 error(buf); 861 } 862 863 if((rlen = convM2S(datarcv, p->len, f)) != p->len) 864 error("rcvmsg format error, expected length %d, got %d", rlen, p->len); 865 if(f->type == Rclunk){ 866 if(ci = getclunk(NOFID, f->tag)) 867 free(ci); 868 goto again; 869 } 870 if(f->type == Rerror && (ci = getclunk(NOFID, f->tag))){ 871 free(ci); 872 goto again; 873 } 874 if(f->fid<0 || Nfid<=f->fid){ 875 fprint(2, "<-%s: %d %s on %d\n", p->name, f->type, 876 mname[f->type]? mname[f->type] : "mystery", 877 f->fid); 878 dump((uchar*)datasnd, olen); 879 dump((uchar*)datarcv, p->len); 880 error("rcvmsg fid out of range"); 881 } 882 DPRINT(2, "<-%s: %F\n", p->name, f); 883 } 884 885 int 886 ctltest(Mfile *mf) 887 { 888 return mf->busy && mf->qid.type == ctlqid.type && mf->qid.path == ctlqid.path; 889 } 890 891 void 892 genstats(void) 893 { 894 int i; 895 char *p; 896 897 p = statbuf; 898 899 p += snprint(p, sizeof(statbuf)+statbuf-p, " Client Server\n"); 900 p += snprint(p, sizeof(statbuf)+statbuf-p, " #calls Δ ms/call Δ #calls Δ ms/call Δ\n"); 901 for (i = 0; i < nelem(cfsstat.cm); i++) 902 if(cfsstat.cm[i].n || cfsstat.sm[i].n) { 903 p += snprint(p, sizeof(statbuf)+statbuf-p, "%7lud %7lud ", 904 cfsstat.cm[i].n, cfsstat.cm[i].n - cfsprev.cm[i].n); 905 if (cfsstat.cm[i].n) 906 p += snprint(p, sizeof(statbuf)+statbuf-p, "%7.3f ", 907 0.000001*cfsstat.cm[i].t/cfsstat.cm[i].n); 908 else 909 p += snprint(p, sizeof(statbuf)+statbuf-p, " "); 910 if(cfsstat.cm[i].n - cfsprev.cm[i].n) 911 p += snprint(p, sizeof(statbuf)+statbuf-p, "%7.3f ", 912 0.000001*(cfsstat.cm[i].t - cfsprev.cm[i].t)/(cfsstat.cm[i].n - cfsprev.cm[i].n)); 913 else 914 p += snprint(p, sizeof(statbuf)+statbuf-p, " "); 915 p += snprint(p, sizeof(statbuf)+statbuf-p, "%7lud %7lud ", 916 cfsstat.sm[i].n, cfsstat.sm[i].n - cfsprev.sm[i].n); 917 if (cfsstat.sm[i].n) 918 p += snprint(p, sizeof(statbuf)+statbuf-p, "%7.3f ", 919 0.000001*cfsstat.sm[i].t/cfsstat.sm[i].n); 920 else 921 p += snprint(p, sizeof(statbuf)+statbuf-p, " "); 922 if(cfsstat.sm[i].n - cfsprev.sm[i].n) 923 p += snprint(p, sizeof(statbuf)+statbuf-p, "%7.3f ", 924 0.000001*(cfsstat.sm[i].t - cfsprev.sm[i].t)/(cfsstat.sm[i].n - cfsprev.sm[i].n)); 925 else 926 p += snprint(p, sizeof(statbuf)+statbuf-p, " "); 927 p += snprint(p, sizeof(statbuf)+statbuf-p, "%s\n", mname[i]); 928 } 929 p += snprint(p, sizeof(statbuf)+statbuf-p, "%7lud %7lud ndirread\n", 930 cfsstat.ndirread, cfsstat.ndirread - cfsprev.ndirread); 931 p += snprint(p, sizeof(statbuf)+statbuf-p, "%7lud %7lud ndelegateread\n", 932 cfsstat.ndelegateread, cfsstat.ndelegateread - cfsprev.ndelegateread); 933 p += snprint(p, sizeof(statbuf)+statbuf-p, "%7lud %7lud ninsert\n", 934 cfsstat.ninsert, cfsstat.ninsert - cfsprev.ninsert); 935 p += snprint(p, sizeof(statbuf)+statbuf-p, "%7lud %7lud ndelete\n", 936 cfsstat.ndelete, cfsstat.ndelete - cfsprev.ndelete); 937 p += snprint(p, sizeof(statbuf)+statbuf-p, "%7lud %7lud nupdate\n", 938 cfsstat.nupdate, cfsstat.nupdate - cfsprev.nupdate); 939 940 p += snprint(p, sizeof(statbuf)+statbuf-p, "%7lud %7lud bytesread\n", 941 cfsstat.bytesread, cfsstat.bytesread - cfsprev.bytesread); 942 p += snprint(p, sizeof(statbuf)+statbuf-p, "%7lud %7lud byteswritten\n", 943 cfsstat.byteswritten, cfsstat.byteswritten - cfsprev.byteswritten); 944 p += snprint(p, sizeof(statbuf)+statbuf-p, "%7lud %7lud bytesfromserver\n", 945 cfsstat.bytesfromserver, cfsstat.bytesfromserver - cfsprev.bytesfromserver); 946 p += snprint(p, sizeof(statbuf)+statbuf-p, "%7lud %7lud bytesfromdirs\n", 947 cfsstat.bytesfromdirs, cfsstat.bytesfromdirs - cfsprev.bytesfromdirs); 948 p += snprint(p, sizeof(statbuf)+statbuf-p, "%7lud %7lud bytesfromcache\n", 949 cfsstat.bytesfromcache, cfsstat.bytesfromcache - cfsprev.bytesfromcache); 950 p += snprint(p, sizeof(statbuf)+statbuf-p, "%7lud %7lud bytestocache\n", 951 cfsstat.bytestocache, cfsstat.bytestocache - cfsprev.bytestocache); 952 statlen = p - statbuf; 953 cfsprev = cfsstat; 954 } 955 956 void 957 putclunk(u32int fid, ushort tag) 958 { 959 Clunkitem *c; 960 961 c = malloc(sizeof(Clunkitem)); 962 if(c == nil) 963 sysfatal("putclunk: malloc: %r"); 964 c->fid = fid; 965 c->tag = tag; 966 c->next = clunklist; 967 clunklist = c; 968 } 969 970 Clunkitem * 971 getclunk(u32int fid, ushort tag) 972 { 973 Clunkitem **p, *c; 974 975 for(p = &clunklist; c = *p; p = &c->next){ 976 if(fid != c->fid && tag != c->tag) 977 continue; 978 *p = c->next; 979 return c; 980 } 981 return nil; 982 } 983 984 Clunkitem * 985 notclunked(u32int fid, ushort tag) 986 { 987 Clunkitem *c; 988 989 for(c = clunklist; c != nil; c = c->next) 990 if(fid == c->fid || tag == c->tag) 991 return c; 992 return nil; 993 } 994