Subversion Repositories planix.SVN

Rev

Rev 2 | Details | Compare with Previous | Last modification | View Log | RSS feed

Rev Author Line No. Line
2 - 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
}