cfs.c 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870
  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 = "il!emelie";
  122. mtpt = "/tmp";
  123. ARGBEGIN{
  124. case 'a':
  125. server = ARGF();
  126. if(server == 0)
  127. usage();
  128. break;
  129. case 'S':
  130. statson = 1;
  131. break;
  132. case 's':
  133. std = 1;
  134. break;
  135. case 'r':
  136. format = 1;
  137. break;
  138. case 'f':
  139. part = ARGF();
  140. if(part == 0)
  141. usage();
  142. break;
  143. case 'd':
  144. debug = 1;
  145. break;
  146. default:
  147. usage();
  148. }ARGEND
  149. if(argc && *argv)
  150. mtpt = *argv;
  151. if(debug)
  152. fmtinstall('F', fcallfmt);
  153. cachesetup(format, part);
  154. c.name = "client";
  155. s.name = "server";
  156. if(std){
  157. c.fd[0] = c.fd[1] = 1;
  158. s.fd[0] = s.fd[1] = 0;
  159. }else
  160. mountinit(server, mtpt);
  161. switch(fork()){
  162. case 0:
  163. io();
  164. exits("");
  165. case -1:
  166. error("fork");
  167. default:
  168. exits("");
  169. }
  170. }
  171. void
  172. cachesetup(int format, char *partition)
  173. {
  174. int f;
  175. int secsize;
  176. int inodes;
  177. int blocksize;
  178. secsize = 512;
  179. inodes = 1024;
  180. blocksize = 4*1024;
  181. f = open(partition, ORDWR);
  182. if(f < 0)
  183. error("opening partition");
  184. if(format || iinit(&ic, f, secsize)<0){
  185. if(iformat(&ic, f, inodes, "bootes", blocksize, secsize) < 0)
  186. error("formatting failed");
  187. }
  188. }
  189. void
  190. mountinit(char *server, char *mountpoint)
  191. {
  192. int p[2];
  193. /*
  194. * grab a channel and call up the file server
  195. */
  196. s.fd[0] = s.fd[1] = dial(netmkaddr(server, 0, "9fs"), 0, 0, 0);
  197. if(s.fd[0] < 0)
  198. error("opening data");
  199. /*
  200. * mount onto name space
  201. */
  202. if(pipe(p) < 0)
  203. error("pipe failed");
  204. switch(fork()){
  205. case 0:
  206. break;
  207. default:
  208. if(amount(p[1], mountpoint, MREPL|MCREATE, "") < 0)
  209. error("mount failed");
  210. exits(0);
  211. case -1:
  212. error("fork failed\n");
  213. /*BUG: no wait!*/
  214. }
  215. c.fd[0] = c.fd[1] = p[0];
  216. }
  217. void
  218. io(void)
  219. {
  220. int type;
  221. Mfile *mf;
  222. loop:
  223. rcvmsg(&c, &c.thdr);
  224. type = c.thdr.type;
  225. if(statson){
  226. cfsstat.cm[type].n++;
  227. cfsstat.cm[type].s = nsec();
  228. }
  229. mf = &mfile[c.thdr.fid];
  230. switch(type){
  231. default:
  232. error("type");
  233. break;
  234. case Tversion:
  235. rversion();
  236. break;
  237. case Tauth:
  238. mf = &mfile[c.thdr.afid];
  239. rauth(mf);
  240. break;
  241. case Tflush:
  242. rflush();
  243. break;
  244. case Tattach:
  245. rattach(mf);
  246. break;
  247. case Twalk:
  248. rwalk(mf);
  249. break;
  250. case Topen:
  251. ropen(mf);
  252. break;
  253. case Tcreate:
  254. rcreate(mf);
  255. break;
  256. case Tread:
  257. rread(mf);
  258. break;
  259. case Twrite:
  260. rwrite(mf);
  261. break;
  262. case Tclunk:
  263. rclunk(mf);
  264. break;
  265. case Tremove:
  266. rremove(mf);
  267. break;
  268. case Tstat:
  269. rstat(mf);
  270. break;
  271. case Twstat:
  272. rwstat(mf);
  273. break;
  274. }
  275. if(statson){
  276. cfsstat.cm[type].t += nsec() -cfsstat.cm[type].s;
  277. }
  278. goto loop;
  279. }
  280. void
  281. rversion(void)
  282. {
  283. if(messagesize > c.thdr.msize)
  284. messagesize = c.thdr.msize;
  285. c.thdr.msize = messagesize; /* set downstream size */
  286. delegate();
  287. }
  288. void
  289. rauth(Mfile *mf)
  290. {
  291. if(mf->busy)
  292. error("auth to used channel");
  293. if(delegate() == 0){
  294. mf->qid = s.rhdr.aqid;
  295. mf->busy = 1;
  296. }
  297. }
  298. void
  299. rflush(void) /* synchronous so easy */
  300. {
  301. sendreply(0);
  302. }
  303. void
  304. rattach(Mfile *mf)
  305. {
  306. if(delegate() == 0){
  307. mf->qid = s.rhdr.qid;
  308. mf->busy = 1;
  309. if (statson == 1){
  310. statson++;
  311. rootqid = mf->qid;
  312. }
  313. }
  314. }
  315. void
  316. rwalk(Mfile *mf)
  317. {
  318. Mfile *nmf;
  319. nmf = nil;
  320. if(statson
  321. && mf->qid.type == rootqid.type && mf->qid.path == rootqid.path
  322. && c.thdr.nwname == 1 && strcmp(c.thdr.wname[0], "cfsctl") == 0){
  323. /* This is the ctl file */
  324. nmf = &mfile[c.thdr.newfid];
  325. if(c.thdr.newfid != c.thdr.fid && nmf->busy)
  326. error("clone to used channel");
  327. nmf = &mfile[c.thdr.newfid];
  328. nmf->qid = ctlqid;
  329. nmf->busy = 1;
  330. c.rhdr.nwqid = 1;
  331. c.rhdr.wqid[0] = ctlqid;
  332. sendreply(0);
  333. return;
  334. }
  335. if(c.thdr.newfid != c.thdr.fid){
  336. if(c.thdr.newfid<0 || Nfid<=c.thdr.newfid)
  337. error("clone nfid out of range");
  338. nmf = &mfile[c.thdr.newfid];
  339. if(nmf->busy)
  340. error("clone to used channel");
  341. nmf = &mfile[c.thdr.newfid];
  342. nmf->qid = mf->qid;
  343. nmf->busy = 1;
  344. mf = nmf; /* Walk mf */
  345. }
  346. if(delegate() < 0){ /* complete failure */
  347. if(nmf)
  348. nmf->busy = 0;
  349. return;
  350. }
  351. if(s.rhdr.nwqid == c.thdr.nwname){ /* complete success */
  352. if(s.rhdr.nwqid > 0)
  353. mf->qid = s.rhdr.wqid[s.rhdr.nwqid-1];
  354. return;
  355. }
  356. /* partial success; release fid */
  357. if(nmf)
  358. nmf->busy = 0;
  359. }
  360. void
  361. ropen(Mfile *mf)
  362. {
  363. if(statson && ctltest(mf)){
  364. /* Opening ctl file */
  365. if(c.thdr.mode != OREAD){
  366. sendreply("does not exist");
  367. return;
  368. }
  369. c.rhdr.qid = ctlqid;
  370. c.rhdr.iounit = 0;
  371. sendreply(0);
  372. genstats();
  373. return;
  374. }
  375. if(delegate() == 0){
  376. mf->qid = s.rhdr.qid;
  377. if(c.thdr.mode & OTRUNC)
  378. iget(&ic, mf->qid);
  379. }
  380. }
  381. void
  382. rcreate(Mfile *mf)
  383. {
  384. if(statson && ctltest(mf)){
  385. sendreply("exists");
  386. return;
  387. }
  388. if(delegate() == 0){
  389. mf->qid = s.rhdr.qid;
  390. mf->qid.vers++;
  391. }
  392. }
  393. void
  394. rclunk(Mfile *mf)
  395. {
  396. if(!mf->busy){
  397. sendreply(0);
  398. return;
  399. }
  400. mf->busy = 0;
  401. delegate();
  402. }
  403. void
  404. rremove(Mfile *mf)
  405. {
  406. if(statson && ctltest(mf)){
  407. sendreply("not removed");
  408. return;
  409. }
  410. mf->busy = 0;
  411. delegate();
  412. }
  413. void
  414. rread(Mfile *mf)
  415. {
  416. int cnt;
  417. long off, first;
  418. char *cp;
  419. Ibuf *b;
  420. long n;
  421. char data[MAXFDATA];
  422. int done;
  423. first = off = c.thdr.offset;
  424. cnt = c.thdr.count;
  425. if(statson && ctltest(mf)){
  426. if(cnt > statlen-off)
  427. c.rhdr.count = statlen-off;
  428. else
  429. c.rhdr.count = cnt;
  430. if(c.rhdr.count < 0){
  431. sendreply("eof");
  432. return;
  433. }
  434. c.rhdr.data = statbuf + off;
  435. sendreply(0);
  436. return;
  437. }
  438. if(mf->qid.type & (QTDIR|QTAUTH)){
  439. delegate();
  440. if (statson) {
  441. cfsstat.ndirread++;
  442. if(c.rhdr.count > 0){
  443. cfsstat.bytesread += c.rhdr.count;
  444. cfsstat.bytesfromdirs += c.rhdr.count;
  445. }
  446. }
  447. return;
  448. }
  449. b = iget(&ic, mf->qid);
  450. if(b == 0){
  451. DPRINT(2, "delegating read\n");
  452. delegate();
  453. if (statson){
  454. cfsstat.ndelegateread++;
  455. if(c.rhdr.count > 0){
  456. cfsstat.bytesread += c.rhdr.count;
  457. cfsstat.bytesfromserver += c.rhdr.count;
  458. }
  459. }
  460. return;
  461. }
  462. cp = data;
  463. done = 0;
  464. while(cnt>0 && !done){
  465. if(off >= b->inode.length){
  466. DPRINT(2, "offset %ld greater than length %lld\n", off, b->inode.length);
  467. break;
  468. }
  469. n = fread(&ic, b, cp, off, cnt);
  470. if(n <= 0){
  471. n = -n;
  472. if(n==0 || n>cnt)
  473. n = cnt;
  474. DPRINT(2, "fetch %ld bytes of data from server at offset %ld\n", n, off);
  475. s.thdr.type = c.thdr.type;
  476. s.thdr.fid = c.thdr.fid;
  477. s.thdr.tag = c.thdr.tag;
  478. s.thdr.offset = off;
  479. s.thdr.count = n;
  480. if(statson){
  481. cfsstat.ndelegateread++;
  482. }
  483. if(askserver() < 0){
  484. sendreply(s.rhdr.ename);
  485. return;
  486. }
  487. if(s.rhdr.count != n)
  488. done = 1;
  489. n = s.rhdr.count;
  490. if(n == 0){
  491. /* end of file */
  492. if(b->inode.length > off){
  493. DPRINT(2, "file %llud.%ld, length %ld\n",
  494. b->inode.qid.path, b->inode.qid.vers, off);
  495. b->inode.length = off;
  496. }
  497. break;
  498. }
  499. memmove(cp, s.rhdr.data, n);
  500. fwrite(&ic, b, cp, off, n);
  501. if (statson){
  502. cfsstat.bytestocache += n;
  503. cfsstat.bytesfromserver += n;
  504. }
  505. }else{
  506. DPRINT(2, "fetched %ld bytes from cache\n", n);
  507. if(statson){
  508. cfsstat.bytesfromcache += n;
  509. }
  510. }
  511. cnt -= n;
  512. off += n;
  513. cp += n;
  514. }
  515. c.rhdr.data = data;
  516. c.rhdr.count = off - first;
  517. if(statson){
  518. cfsstat.bytesread += c.rhdr.count;
  519. }
  520. sendreply(0);
  521. }
  522. void
  523. rwrite(Mfile *mf)
  524. {
  525. Ibuf *b;
  526. char buf[MAXFDATA];
  527. if(statson && ctltest(mf)){
  528. sendreply("read only");
  529. return;
  530. }
  531. if(mf->qid.type & (QTDIR|QTAUTH)){
  532. delegate();
  533. if(statson && c.rhdr.count > 0)
  534. cfsstat.byteswritten += c.rhdr.count;
  535. return;
  536. }
  537. memmove(buf, c.thdr.data, c.thdr.count);
  538. if(delegate() < 0)
  539. return;
  540. if(s.rhdr.count > 0)
  541. cfsstat.byteswritten += s.rhdr.count;
  542. if(mf->qid.type & QTAPPEND) /* don't modify our cache for append-only data; always read from server*/
  543. return;
  544. b = iget(&ic, mf->qid);
  545. if(b == 0)
  546. return;
  547. if (b->inode.length < c.thdr.offset + s.rhdr.count)
  548. b->inode.length = c.thdr.offset + s.rhdr.count;
  549. mf->qid.vers++;
  550. if (s.rhdr.count != c.thdr.count)
  551. syslog(0, "cfslog", "rhdr.count %ud, thdr.count %ud\n",
  552. s.rhdr.count, c.thdr.count);
  553. if(fwrite(&ic, b, buf, c.thdr.offset, s.rhdr.count) == s.rhdr.count){
  554. iinc(&ic, b);
  555. if(statson)
  556. cfsstat.bytestocache += s.rhdr.count;
  557. }
  558. }
  559. void
  560. rstat(Mfile *mf)
  561. {
  562. Dir d;
  563. if(statson && ctltest(mf)){
  564. genstats();
  565. d.qid = ctlqid;
  566. d.mode = 0444;
  567. d.length = statlen; /* would be nice to do better */
  568. d.name = "cfsctl";
  569. d.uid = "none";
  570. d.gid = "none";
  571. d.muid = "none";
  572. d.atime = time(nil);
  573. d.mtime = d.atime;
  574. c.rhdr.nstat = convD2M(&d, c.rhdr.stat, sizeof(c.rhdr) - (c.rhdr.stat - (uchar*)&c.rhdr));
  575. sendreply(0);
  576. return;
  577. }
  578. if(delegate() == 0){
  579. Ibuf *b;
  580. convM2D(s.rhdr.stat, s.rhdr.nstat , &d, nil);
  581. mf->qid = d.qid;
  582. b = iget(&ic, mf->qid);
  583. if(b)
  584. b->inode.length = d.length;
  585. }
  586. }
  587. void
  588. rwstat(Mfile *mf)
  589. {
  590. Ibuf *b;
  591. if(statson && ctltest(mf)){
  592. sendreply("read only");
  593. return;
  594. }
  595. delegate();
  596. if(b = iget(&ic, mf->qid))
  597. b->inode.length = MAXLEN;
  598. }
  599. void
  600. error(char *fmt, ...) {
  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. }
  648. sendmsg(&c, &s.rhdr);
  649. return c.thdr.type+1 == s.rhdr.type ? 0 : -1;
  650. }
  651. /*
  652. * send a request to the server and get a reply
  653. */
  654. int
  655. askserver(void)
  656. {
  657. int type;
  658. s.thdr.tag = c.thdr.tag;
  659. type = s.thdr.type;
  660. if(statson){
  661. cfsstat.sm[type].n++;
  662. cfsstat.sm[type].s = nsec();
  663. }
  664. sendmsg(&s, &s.thdr);
  665. rcvmsg(&s, &s.rhdr);
  666. if(statson){
  667. cfsstat.sm[type].t += nsec() - cfsstat.sm[type].s;
  668. }
  669. return s.thdr.type+1 == s.rhdr.type ? 0 : -1;
  670. }
  671. /*
  672. * send/receive messages with logging
  673. */
  674. void
  675. sendmsg(P9fs *p, Fcall *f)
  676. {
  677. DPRINT(2, "->%s: %F\n", p->name, f);
  678. p->len = convS2M(f, datasnd, messagesize);
  679. if(p->len <= 0)
  680. error("convS2M");
  681. if(write(p->fd[1], datasnd, p->len)!=p->len)
  682. error("sendmsg");
  683. }
  684. void
  685. dump(uchar *p, int len)
  686. {
  687. fprint(2, "%d bytes", len);
  688. while(len > 0){
  689. fprint(2, " %.2ux", *p++);
  690. len--;
  691. }
  692. fprint(2, "\n");
  693. }
  694. void
  695. rcvmsg(P9fs *p, Fcall *f)
  696. {
  697. int olen, rlen;
  698. char buf[128];
  699. olen = p->len;
  700. p->len = read9pmsg(p->fd[0], datarcv, sizeof(datarcv));
  701. if(p->len <= 0){
  702. sprint(buf, "read9pmsg(%d)->%ld: %r", p->fd[0], p->len);
  703. error(buf);
  704. }
  705. if((rlen = convM2S(datarcv, p->len, f)) != p->len)
  706. error("rcvmsg format error, expected length %d, got %d", rlen, p->len);
  707. if(f->fid<0 || Nfid<=f->fid){
  708. fprint(2, "<-%s: %d %s on %d\n", p->name, f->type,
  709. mname[f->type]? mname[f->type] : "mystery",
  710. f->fid);
  711. dump((uchar*)datasnd, olen);
  712. dump((uchar*)datarcv, p->len);
  713. error("rcvmsg fid out of range");
  714. }
  715. DPRINT(2, "<-%s: %F\n", p->name, f);
  716. }
  717. int
  718. ctltest(Mfile *mf)
  719. {
  720. return mf->busy && mf->qid.type == ctlqid.type && mf->qid.path == ctlqid.path;
  721. }
  722. void
  723. genstats(void)
  724. {
  725. int i;
  726. char *p;
  727. p = statbuf;
  728. p += snprint(p, sizeof(statbuf)+statbuf-p, " Client Server\n");
  729. p += snprint(p, sizeof(statbuf)+statbuf-p, " #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, "%7lud %7lud ",
  733. cfsstat.cm[i].n, cfsstat.cm[i].n - cfsprev.cm[i].n);
  734. if (cfsstat.cm[i].n)
  735. p += snprint(p, sizeof(statbuf)+statbuf-p, "%7.3f ",
  736. 0.000001*cfsstat.cm[i].t/cfsstat.cm[i].n);
  737. else
  738. p += snprint(p, sizeof(statbuf)+statbuf-p, " ");
  739. if(cfsstat.cm[i].n - cfsprev.cm[i].n)
  740. p += snprint(p, sizeof(statbuf)+statbuf-p, "%7.3f ",
  741. 0.000001*(cfsstat.cm[i].t - cfsprev.cm[i].t)/(cfsstat.cm[i].n - cfsprev.cm[i].n));
  742. else
  743. p += snprint(p, sizeof(statbuf)+statbuf-p, " ");
  744. p += snprint(p, sizeof(statbuf)+statbuf-p, "%7lud %7lud ",
  745. cfsstat.sm[i].n, cfsstat.sm[i].n - cfsprev.sm[i].n);
  746. if (cfsstat.sm[i].n)
  747. p += snprint(p, sizeof(statbuf)+statbuf-p, "%7.3f ",
  748. 0.000001*cfsstat.sm[i].t/cfsstat.sm[i].n);
  749. else
  750. p += snprint(p, sizeof(statbuf)+statbuf-p, " ");
  751. if(cfsstat.sm[i].n - cfsprev.sm[i].n)
  752. p += snprint(p, sizeof(statbuf)+statbuf-p, "%7.3f ",
  753. 0.000001*(cfsstat.sm[i].t - cfsprev.sm[i].t)/(cfsstat.sm[i].n - cfsprev.sm[i].n));
  754. else
  755. p += snprint(p, sizeof(statbuf)+statbuf-p, " ");
  756. p += snprint(p, sizeof(statbuf)+statbuf-p, "%s\n", mname[i]);
  757. }
  758. p += snprint(p, sizeof(statbuf)+statbuf-p, "%7lud %7lud ndirread\n",
  759. cfsstat.ndirread, cfsstat.ndirread - cfsprev.ndirread);
  760. p += snprint(p, sizeof(statbuf)+statbuf-p, "%7lud %7lud ndelegateread\n",
  761. cfsstat.ndelegateread, cfsstat.ndelegateread - cfsprev.ndelegateread);
  762. p += snprint(p, sizeof(statbuf)+statbuf-p, "%7lud %7lud ninsert\n",
  763. cfsstat.ninsert, cfsstat.ninsert - cfsprev.ninsert);
  764. p += snprint(p, sizeof(statbuf)+statbuf-p, "%7lud %7lud ndelete\n",
  765. cfsstat.ndelete, cfsstat.ndelete - cfsprev.ndelete);
  766. p += snprint(p, sizeof(statbuf)+statbuf-p, "%7lud %7lud nupdate\n",
  767. cfsstat.nupdate, cfsstat.nupdate - cfsprev.nupdate);
  768. p += snprint(p, sizeof(statbuf)+statbuf-p, "%7lud %7lud bytesread\n",
  769. cfsstat.bytesread, cfsstat.bytesread - cfsprev.bytesread);
  770. p += snprint(p, sizeof(statbuf)+statbuf-p, "%7lud %7lud byteswritten\n",
  771. cfsstat.byteswritten, cfsstat.byteswritten - cfsprev.byteswritten);
  772. p += snprint(p, sizeof(statbuf)+statbuf-p, "%7lud %7lud bytesfromserver\n",
  773. cfsstat.bytesfromserver, cfsstat.bytesfromserver - cfsprev.bytesfromserver);
  774. p += snprint(p, sizeof(statbuf)+statbuf-p, "%7lud %7lud bytesfromdirs\n",
  775. cfsstat.bytesfromdirs, cfsstat.bytesfromdirs - cfsprev.bytesfromdirs);
  776. p += snprint(p, sizeof(statbuf)+statbuf-p, "%7lud %7lud bytesfromcache\n",
  777. cfsstat.bytesfromcache, cfsstat.bytesfromcache - cfsprev.bytesfromcache);
  778. p += snprint(p, sizeof(statbuf)+statbuf-p, "%7lud %7lud bytestocache\n",
  779. cfsstat.bytestocache, cfsstat.bytestocache - cfsprev.bytestocache);
  780. statlen = p - statbuf;
  781. cfsprev = cfsstat;
  782. }