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 13 machinit(void) 14 { 15 active.exiting = 0; 16 } 17 18 /* 19 * Put a string on the console. 20 */ 21 void 22 puts(char *s, int n) 23 { 24 print("%.*s", n, s); 25 } 26 27 void 28 prflush(void) 29 { 30 } 31 32 /* 33 * Print a string on the console. 34 */ 35 void 36 putstrn(char *str, int n) 37 { 38 puts(str, n); 39 } 40 41 /* 42 * get a character from the console 43 */ 44 int 45 getc(void) 46 { 47 return Bgetrune(&bin); 48 } 49 50 void 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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