cfs.c 16 KB

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