1 /* cached-worm file server */
2 #include "all.h"
3 #include "io.h"
4 #include "9p1.h"
5
6 extern int oldcachefmt;
7
8 Map *devmap;
9
10 Biobuf bin;
11
12 void
machinit(void)13 machinit(void)
14 {
15 active.exiting = 0;
16 }
17
18 /*
19 * Put a string on the console.
20 */
21 void
puts(char * s,int n)22 puts(char *s, int n)
23 {
24 print("%.*s", n, s);
25 }
26
27 void
prflush(void)28 prflush(void)
29 {
30 }
31
32 /*
33 * Print a string on the console.
34 */
35 void
putstrn(char * str,int n)36 putstrn(char *str, int n)
37 {
38 puts(str, n);
39 }
40
41 /*
42 * get a character from the console
43 */
44 int
getc(void)45 getc(void)
46 {
47 return Bgetrune(&bin);
48 }
49
50 void
panic(char * fmt,...)51 panic(char *fmt, ...)
52 {
53 int n;
54 va_list arg;
55 char buf[PRINTSIZE];
56
57 va_start(arg, fmt);
58 n = vseprint(buf, buf + sizeof buf, fmt, arg) - buf;
59 va_end(arg);
60 buf[n] = '\0';
61 print("panic: %s\n", buf);
62 exit();
63 }
64
65 int
okay(char * quest)66 okay(char *quest)
67 {
68 char *ln;
69
70 print("okay to %s? ", quest);
71 if ((ln = Brdline(&bin, '\n')) == nil)
72 return 0;
73 ln[Blinelen(&bin)-1] = '\0';
74 if (isascii(*ln) && isupper(*ln))
75 *ln = tolower(*ln);
76 return *ln == 'y';
77 }
78
79 static void
mapinit(char * mapfile)80 mapinit(char *mapfile)
81 {
82 int nf;
83 char *ln;
84 char *fields[2];
85 Biobuf *bp;
86 Map *map;
87
88 if (mapfile == nil)
89 return;
90 bp = Bopen(mapfile, OREAD);
91 if (bp == nil)
92 sysfatal("can't read %s", mapfile);
93 devmap = nil;
94 while ((ln = Brdline(bp, '\n')) != nil) {
95 ln[Blinelen(bp)-1] = '\0';
96 if (*ln == '\0' || *ln == '#')
97 continue;
98 nf = tokenize(ln, fields, nelem(fields));
99 if (nf != 2)
100 continue;
101 if(testconfig(fields[0]) != 0) {
102 print("bad `from' device %s in %s\n",
103 fields[0], mapfile);
104 continue;
105 }
106 map = malloc(sizeof *map);
107 map->from = strdup(fields[0]);
108 map->to = strdup(fields[1]);
109 map->fdev = iconfig(fields[0]);
110 map->tdev = nil;
111 if (access(map->to, AEXIST) < 0) {
112 /*
113 * map->to isn't an existing file, so it had better be
114 * a config string for a device.
115 */
116 if(testconfig(fields[1]) == 0)
117 map->tdev = iconfig(fields[1]);
118 }
119 /* else map->to is the replacement file name */
120 map->next = devmap;
121 devmap = map;
122 }
123 Bterm(bp);
124 }
125
126 static void
confinit(void)127 confinit(void)
128 {
129 conf.nmach = 1;
130
131 conf.mem = meminit();
132
133 conf.nuid = 1000;
134 conf.nserve = 15; /* tunable */
135 conf.nfile = 30000;
136 conf.nlgmsg = 100;
137 conf.nsmmsg = 500;
138
139 localconfinit();
140
141 conf.nwpath = conf.nfile*8;
142 conf.nauth = conf.nfile/10;
143 conf.gidspace = conf.nuid*3;
144
145 cons.flags = 0;
146
147 if (conf.devmap)
148 mapinit(conf.devmap);
149 }
150
151 /*
152 * compute BUFSIZE*(NDBLOCK+INDPERBUF+INDPERBUF+INDPERBUF+INDPERBUF⁴)
153 * while watching for overflow; in that case, return 0.
154 */
155
156 static uvlong
adduvlongov(uvlong a,uvlong b)157 adduvlongov(uvlong a, uvlong b)
158 {
159 uvlong r = a + b;
160
161 if (r < a || r < b)
162 return 0;
163 return r;
164 }
165
166 static uvlong
muluvlongov(uvlong a,uvlong b)167 muluvlongov(uvlong a, uvlong b)
168 {
169 uvlong r = a * b;
170
171 if (a != 0 && r/a != b || r < a || r < b)
172 return 0;
173 return r;
174 }
175
176 static uvlong
maxsize(void)177 maxsize(void)
178 {
179 int i;
180 uvlong max = NDBLOCK, ind = 1;
181
182 for (i = 0; i < NIBLOCK; i++) {
183 ind = muluvlongov(ind, INDPERBUF); /* power of INDPERBUF */
184 if (ind == 0)
185 return 0;
186 max = adduvlongov(max, ind);
187 if (max == 0)
188 return 0;
189 }
190 return muluvlongov(max, BUFSIZE);
191 }
192
193 enum {
194 INDPERBUF = ((uvlong)INDPERBUF *INDPERBUF),
195 INDPERBUF⁴ = ((uvlong)INDPERBUF*INDPERBUF),
196 };
197
198 static void
printsizes(void)199 printsizes(void)
200 {
201 uvlong max = maxsize();
202
203 print("\tblock size = %d; ", RBUFSIZE);
204 if (max == 0)
205 print("max file size exceeds 2⁶⁴ bytes\n");
206 else {
207 uvlong offlim = 1ULL << (sizeof(Off)*8 - 1);
208
209 if (max >= offlim)
210 max = offlim - 1;
211 print("max file size = %,llud\n", (Wideoff)max);
212 }
213 if (INDPERBUF/INDPERBUF != INDPERBUF)
214 print("overflow computing INDPERBUF\n");
215 if (INDPERBUF⁴/INDPERBUF != INDPERBUF)
216 print("overflow computing INDPERBUF⁴\n");
217 print("\tINDPERBUF = %d, INDPERBUF^4 = %,lld, ", INDPERBUF,
218 (Wideoff)INDPERBUF⁴);
219 print("CEPERBK = %d\n", CEPERBK);
220 print("\tsizeofs: Dentry = %d, Cache = %d\n",
221 sizeof(Dentry), sizeof(Cache));
222 }
223
224 void
usage(void)225 usage(void)
226 {
227 fprint(2, "usage: %s [-cf][-a ann-str][-m dev-map] config-dev\n",
228 argv0);
229 exits("usage");
230 }
231
232 void
main(int argc,char ** argv)233 main(int argc, char **argv)
234 {
235 int i, nets = 0;
236 char *ann;
237
238 rfork(RFNOTEG);
239 formatinit();
240 machinit();
241 conf.confdev = "n"; /* Devnone */
242
243 ARGBEGIN{
244 case 'a': /* announce on this net */
245 ann = EARGF(usage());
246 if (nets >= Maxnets) {
247 fprint(2, "%s: too many networks to announce: %s\n",
248 argv0, ann);
249 exits("too many nets");
250 }
251 annstrs[nets++] = ann;
252 break;
253 case 'c': /* use new, faster cache layout */
254 oldcachefmt = 0;
255 break;
256 case 'f': /* enter configuration mode first */
257 conf.configfirst++;
258 break;
259 case 'm': /* name device-map file */
260 conf.devmap = EARGF(usage());
261 break;
262 default:
263 usage();
264 break;
265 }ARGEND
266
267 if (argc != 1)
268 usage();
269 conf.confdev = argv[0]; /* config string for dev holding full config */
270
271 Binit(&bin, 0, OREAD);
272 confinit();
273
274 print("\nPlan 9 %d-bit cached-worm file server with %d-deep indir blks\n",
275 sizeof(Off)*8 - 1, NIBLOCK);
276 printsizes();
277
278 qlock(&reflock);
279 qunlock(&reflock);
280 serveq = newqueue(1000, "9P service"); /* tunable */
281 raheadq = newqueue(1000, "readahead"); /* tunable */
282
283 mbinit();
284 netinit();
285 scsiinit();
286
287 files = malloc(conf.nfile * sizeof *files);
288 for(i=0; i < conf.nfile; i++) {
289 qlock(&files[i]);
290 qunlock(&files[i]);
291 }
292
293 wpaths = malloc(conf.nwpath * sizeof(*wpaths));
294 uid = malloc(conf.nuid * sizeof(*uid));
295 gidspace = malloc(conf.gidspace * sizeof(*gidspace));
296 authinit();
297
298 print("iobufinit\n");
299 iobufinit();
300
301 arginit();
302 boottime = time(nil);
303
304 print("sysinit\n");
305 sysinit();
306
307 /*
308 * Ethernet i/o processes
309 */
310 netstart();
311
312 /*
313 * read ahead processes
314 */
315 newproc(rahead, 0, "rah");
316
317 /*
318 * server processes
319 */
320 for(i=0; i < conf.nserve; i++)
321 newproc(serve, 0, "srv");
322
323 /*
324 * worm "dump" copy process
325 */
326 newproc(wormcopy, 0, "wcp");
327
328 /*
329 * processes to read the console
330 */
331 consserve();
332
333 /*
334 * "sync" copy process
335 * this doesn't return.
336 */
337 procsetname("scp");
338 synccopy();
339 }
340
341 /*
342 * read ahead processes.
343 * read message from q and then
344 * read the device.
345 */
346 int
rbcmp(void * va,void * vb)347 rbcmp(void *va, void *vb)
348 {
349 Rabuf *ra, *rb;
350
351 ra = *(Rabuf**)va;
352 rb = *(Rabuf**)vb;
353 if(rb == 0)
354 return 1;
355 if(ra == 0)
356 return -1;
357 if(ra->dev > rb->dev)
358 return 1;
359 if(ra->dev < rb->dev)
360 return -1;
361 if(ra->addr > rb->addr)
362 return 1;
363 if(ra->addr < rb->addr)
364 return -1;
365 return 0;
366 }
367
368 void
rahead(void *)369 rahead(void *)
370 {
371 Rabuf *rb[50];
372 Iobuf *p;
373 int i, n;
374
375 for (;;) {
376 rb[0] = fs_recv(raheadq, 0);
377 for(n = 1; n < nelem(rb); n++) {
378 if(raheadq->count <= 0)
379 break;
380 rb[n] = fs_recv(raheadq, 0);
381 }
382 qsort(rb, n, sizeof rb[0], rbcmp);
383 for(i = 0; i < n; i++) {
384 if(rb[i] == 0)
385 continue;
386 p = getbuf(rb[i]->dev, rb[i]->addr, Brd);
387 if(p)
388 putbuf(p);
389 lock(&rabuflock);
390 rb[i]->link = rabuffree;
391 rabuffree = rb[i];
392 unlock(&rabuflock);
393 }
394 }
395 }
396
397 /*
398 * main filesystem server loop.
399 * entered by many processes.
400 * they wait for message buffers and
401 * then process them.
402 */
403 void
serve(void *)404 serve(void *)
405 {
406 int i;
407 Chan *cp;
408 Msgbuf *mb;
409
410 for (;;) {
411 qlock(&reflock);
412 /* read 9P request from a network input process */
413 mb = fs_recv(serveq, 0);
414 assert(mb->magic == Mbmagic);
415 /* fs kernel sets chan in /sys/src/fs/ip/il.c:/^getchan */
416 cp = mb->chan;
417 if (cp == nil)
418 panic("serve: nil mb->chan");
419 rlock(&cp->reflock);
420 qunlock(&reflock);
421
422 rlock(&mainlock);
423
424 if (mb->data == nil)
425 panic("serve: nil mb->data");
426 /* better sniffing code in /sys/src/cmd/disk/kfs/9p12.c */
427 if(cp->protocol == nil){
428 /* do we recognise the protocol in this packet? */
429 /* better sniffing code: /sys/src/cmd/disk/kfs/9p12.c */
430 for(i = 0; fsprotocol[i] != nil; i++)
431 if(fsprotocol[i](mb) != 0) {
432 cp->protocol = fsprotocol[i];
433 break;
434 }
435 if(cp->protocol == nil){
436 print("no protocol for message\n");
437 for(i = 0; i < 12; i++)
438 print(" %2.2uX", mb->data[i]);
439 print("\n");
440 }
441 } else
442 /* process the request, generate an answer and reply */
443 cp->protocol(mb);
444
445 mbfree(mb);
446 runlock(&mainlock);
447 runlock(&cp->reflock);
448 }
449 }
450
451 void
exit(void)452 exit(void)
453 {
454 lock(&active);
455 active.exiting = 1;
456 unlock(&active);
457
458 print("halted at %T.\n", time(nil));
459 postnote(PNGROUP, getpid(), "die");
460 exits(nil);
461 }
462
463 enum {
464 DUMPTIME = 5, /* 5 am */
465 WEEKMASK = 0, /* every day (1=sun, 2=mon, 4=tue, etc.) */
466 };
467
468 /*
469 * calculate the next dump time.
470 * minimum delay is 100 minutes.
471 */
472 Timet
nextdump(Timet t)473 nextdump(Timet t)
474 {
475 Timet nddate = nextime(t+MINUTE(100), DUMPTIME, WEEKMASK);
476
477 if(!conf.nodump)
478 print("next dump at %T\n", nddate);
479 return nddate;
480 }
481
482 /*
483 * process to copy dump blocks from
484 * cache to worm. it runs flat out when
485 * it gets work, but only looks for
486 * work every 10 seconds.
487 */
488 void
wormcopy(void *)489 wormcopy(void *)
490 {
491 int f, dorecalc = 1;
492 Timet dt, t = 0, nddate = 0, ntoytime = 0;
493 Filsys *fs;
494
495 for (;;) {
496 if (dorecalc) {
497 dorecalc = 0;
498 t = time(nil);
499 nddate = nextdump(t); /* chatters */
500 ntoytime = time(nil);
501 }
502 dt = time(nil) - t;
503 if(dt < 0 || dt > MINUTE(100)) {
504 if(dt < 0)
505 print("time went back\n");
506 else
507 print("time jumped ahead\n");
508 dorecalc = 1;
509 continue;
510 }
511 t += dt;
512 f = 0;
513 if(t > ntoytime)
514 ntoytime = time(nil) + HOUR(1);
515 else if(t > nddate) {
516 if(!conf.nodump) {
517 print("automatic dump %T\n", t);
518 for(fs=filsys; fs->name; fs++)
519 if(fs->dev->type == Devcw)
520 cfsdump(fs);
521 }
522 dorecalc = 1;
523 } else {
524 rlock(&mainlock);
525 for(fs=filsys; fs->name; fs++)
526 if(fs->dev->type == Devcw)
527 f |= dumpblock(fs->dev);
528 runlock(&mainlock);
529 if(!f)
530 delay(10000);
531 wormprobe();
532 }
533 }
534 }
535
536 /*
537 * process to synch blocks
538 * it puts out a block/cache-line every second
539 * it waits 10 seconds if caught up.
540 * in both cases, it takes about 10 seconds
541 * to get up-to-date.
542 */
543 void
synccopy(void)544 synccopy(void)
545 {
546 int f;
547
548 for (;;) {
549 rlock(&mainlock);
550 f = syncblock();
551 runlock(&mainlock);
552 if(!f)
553 delay(10000);
554 else
555 delay(1000);
556 }
557 }
558
559 Devsize
inqsize(char * file)560 inqsize(char *file)
561 {
562 int nf;
563 char *ln, *end, *data = malloc(strlen(file) + 5 + 1);
564 char *fields[4];
565 Devsize rv = -1;
566 Biobuf *bp;
567
568 strcpy(data, file);
569 end = strstr(data, "/data");
570 if (end == nil)
571 strcat(data, "/ctl");
572 else
573 strcpy(end, "/ctl");
574 bp = Bopen(data, OREAD);
575 if (bp) {
576 while (rv < 0 && (ln = Brdline(bp, '\n')) != nil) {
577 ln[Blinelen(bp)-1] = '\0';
578 nf = tokenize(ln, fields, nelem(fields));
579 if (nf == 3 && strcmp(fields[0], "geometry") == 0)
580 rv = atoi(fields[2]);
581 }
582 Bterm(bp);
583 }
584 free(data);
585 return rv;
586 }
587