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