Subversion Repositories planix.SVN

Rev

Details | Last modification | View Log | RSS feed

Rev Author Line No. Line
2 - 1
/*
2
 * cached-worm device
3
 */
4
#include "all.h"
5
 
6
#define	CDEV(d)		((d)->cw.c)
7
#define	WDEV(d)		((d)->cw.w)
8
#define	RDEV(d)		((d)->cw.ro)
9
 
10
enum {
11
	DEBUG		= 0,
12
	FIRST		= SUPER_ADDR,
13
 
14
	ADDFREE		= 100,
15
	CACHE_ADDR	= SUPER_ADDR,
16
	MAXAGE		= 10000,
17
};
18
 
19
/* cache state */
20
enum
21
{
22
	/* states -- beware these are recorded on the cache */
23
				/*    cache    worm	*/
24
	Cnone = 0,		/*	0	?	*/
25
	Cdirty,			/*	1	0	*/
26
	Cdump,			/*	1	0->1	*/
27
	Cread,			/*	1	1	*/
28
	Cwrite,			/*	2	1	*/
29
	Cdump1,			/* inactive form of dump */
30
	Cerror,
31
 
32
	/* opcodes -- these are not recorded */
33
	Onone,
34
	Oread,
35
	Owrite,
36
	Ogrow,
37
	Odump,
38
	Orele,
39
	Ofree,
40
};
41
 
42
typedef	struct	Cw	Cw;
43
struct	Cw
44
{
45
	Device*	dev;
46
	Device*	cdev;
47
	Device*	wdev;
48
	Device*	rodev;
49
	Cw*	link;
50
 
51
	int	dbucket;	/* last bucket dumped */
52
	Off	daddr;		/* last block dumped */
53
	Off	ncopy;
54
	int	nodump;
55
/*
56
 * following are cached variables for dumps
57
 */
58
	Off	fsize;
59
	Off	ndump;
60
	int	depth;
61
	int	all;		/* local flag to recur on modified dirs */
62
	int	allflag;	/* global flag to recur on modified dirs */
63
	Off	falsehits;	/* times recur found modified blocks */
64
	struct {
65
		char	name[500];
66
		char	namepad[NAMELEN+10];
67
	};
68
};
69
 
70
static char* cwnames[] =
71
{
72
	[Cnone]		"none",
73
	[Cdirty]	"dirty",
74
	[Cdump]		"dump",
75
	[Cread]		"read",
76
	[Cwrite]	"write",
77
	[Cdump1]	"dump1",
78
	[Cerror]	"error",
79
 
80
	[Onone]		"none",
81
	[Oread]		"read",
82
	[Owrite]	"write",
83
	[Ogrow]		"grow",
84
	[Odump]		"dump",
85
	[Orele]		"rele",
86
};
87
 
88
int oldcachefmt = 1;
89
 
90
Centry*	getcentry(Bucket*, Off);
91
int	cwio(Device*, Off, void*, int);
92
void	cmd_cwcmd(int, char*[]);
93
 
94
/*
95
 * console command
96
 * initiate a dump
97
 */
98
void
99
cmd_dump(int argc, char *argv[])
100
{
101
	Filsys *fs;
102
 
103
	fs = cons.curfs;
104
	if(argc > 1)
105
		fs = fsstr(argv[1]);
106
	if(fs == 0) {
107
		print("%s: unknown file system\n", argv[1]);
108
		return;
109
	}
110
	cfsdump(fs);
111
}
112
 
113
/*
114
 * console command
115
 * worm stats
116
 */
117
static void
118
cmd_statw(int, char*[])
119
{
120
	Filsys *fs;
121
	Iobuf *p;
122
	Superb *sb;
123
	Cache *h;
124
	Bucket *b;
125
	Centry *c, *ce;
126
	Off m, nw, bw, state[Onone];
127
	Off sbfsize, sbcwraddr, sbroraddr, sblast, sbnext;
128
	Off hmsize, hmaddr, dsize, dsizepct;
129
	Device *dev;
130
	Cw *cw;
131
	int s;
132
 
133
	fs = cons.curfs;
134
	dev = fs->dev;
135
	if(dev->type != Devcw) {
136
		print("curfs not type cw\n");
137
		return;
138
	}
139
 
140
	cw = dev->private;
141
	if(cw == 0) {
142
		print("curfs not inited\n");
143
		return;
144
	}
145
 
146
	print("cwstats %s\n", fs->name);
147
 
148
	sbfsize = 0;
149
	sbcwraddr = 0;
150
	sbroraddr = 0;
151
	sblast = 0;
152
	sbnext = 0;
153
 
154
	print("\tfilesys %s\n", fs->name);
155
//	print("\tnio   =%7W%7W%7W\n", cw->ncwio+0, cw->ncwio+1, cw->ncwio+2);
156
	p = getbuf(dev, cwsaddr(dev), Brd);
157
	if(!p || checktag(p, Tsuper, QPSUPER)) {
158
		print("cwstats: checktag super\n");
159
		if(p) {
160
			putbuf(p);
161
			p = 0;
162
		}
163
	}
164
	if(p) {
165
		sb = (Superb*)p->iobuf;
166
		sbfsize = sb->fsize;
167
		sbcwraddr = sb->cwraddr;
168
		sbroraddr = sb->roraddr;
169
		sblast = sb->last;
170
		sbnext = sb->next;
171
		putbuf(p);
172
	}
173
 
174
	p = getbuf(cw->cdev, CACHE_ADDR, Brd|Bres);
175
	if(!p || checktag(p, Tcache, QPSUPER)) {
176
		print("cwstats: checktag c bucket\n");
177
		if(p)
178
			putbuf(p);
179
		return;
180
	}
181
	h = (Cache*)p->iobuf;
182
	hmaddr = h->maddr;
183
	hmsize = h->msize;
184
 
185
	print("\t\tmaddr  = %8lld\n", (Wideoff)hmaddr);
186
	print("\t\tmsize  = %8lld\n", (Wideoff)hmsize);
187
	print("\t\tcaddr  = %8lld\n", (Wideoff)h->caddr);
188
	print("\t\tcsize  = %8lld\n", (Wideoff)h->csize);
189
	print("\t\tsbaddr = %8lld\n", (Wideoff)h->sbaddr);
190
	print("\t\tcraddr = %8lld %8lld\n",
191
		(Wideoff)h->cwraddr, (Wideoff)sbcwraddr);
192
	print("\t\troaddr = %8lld %8lld\n",
193
		(Wideoff)h->roraddr, (Wideoff)sbroraddr);
194
	/* print stats in terms of (first-)disc sides */
195
	dsize = wormsizeside(dev, 0);
196
	if (dsize < 1) {
197
		if (DEBUG)
198
			print("wormsizeside returned size %lld for %Z side 0\n",
199
				(Wideoff)dsize, dev);
200
		dsize = h->wsize;	/* it's probably a fake worm */
201
		if (dsize < 1)
202
			dsize = 1000;	/* don't divide by zero */
203
	}
204
	dsizepct = dsize/100;
205
	print("\t\tfsize  = %8lld %8lld %2lld+%2lld%%\n", (Wideoff)h->fsize,
206
		(Wideoff)sbfsize, (Wideoff)h->fsize/dsize,
207
		(Wideoff)(h->fsize%dsize)/dsizepct);
208
	print("\t\tslast  =          %8lld\n", (Wideoff)sblast);
209
	print("\t\tsnext  =          %8lld\n", (Wideoff)sbnext);
210
	print("\t\twmax   = %8lld          %2lld+%2lld%%\n",
211
		(Wideoff)h->wmax, (Wideoff)h->wmax/dsize,
212
		(Wideoff)(h->wmax%dsize)/dsizepct);
213
	print("\t\twsize  = %8lld          %2lld+%2lld%%\n",
214
		(Wideoff)h->wsize, (Wideoff)h->wsize/dsize,
215
		(Wideoff)(h->wsize%dsize)/dsizepct);
216
	putbuf(p);
217
 
218
	bw = 0;			/* max filled bucket */
219
	memset(state, 0, sizeof(state));
220
	for(m = 0; m < hmsize; m++) {
221
		p = getbuf(cw->cdev, hmaddr + m/BKPERBLK, Brd);
222
		if(!p || checktag(p, Tbuck, hmaddr + m/BKPERBLK)) {
223
			print("cwstats: checktag c bucket\n");
224
			if(p)
225
				putbuf(p);
226
			return;
227
		}
228
		b = (Bucket*)p->iobuf + m%BKPERBLK;
229
		ce = b->entry + CEPERBK;
230
		nw = 0;
231
		for(c = b->entry; c < ce; c++) {
232
			s = c->state;
233
			state[s]++;
234
			if(s != Cnone && s != Cread)
235
				nw++;
236
		}
237
		putbuf(p);
238
		if(nw > bw)
239
			bw = nw;
240
	}
241
	for(s = Cnone; s < Cerror; s++)
242
		print("\t\t%6lld %s\n", (Wideoff)state[s], cwnames[s]);
243
	print("\t\tcache %2lld%% full\n", ((Wideoff)bw*100)/CEPERBK);
244
}
245
 
246
int
247
dumpblock(Device *dev)
248
{
249
	Iobuf *p, *cb, *p1, *p2;
250
	Cache *h;
251
	Centry *c, *ce, *bc;
252
	Bucket *b;
253
	Off m, a, msize, maddr, wmax, caddr;
254
	int s1, s2, count;
255
	Cw *cw;
256
 
257
	cw = dev->private;
258
	if(cw == 0 || cw->nodump)
259
		return 0;
260
 
261
	cb = getbuf(cw->cdev, CACHE_ADDR, Brd|Bres);
262
	h = (Cache*)cb->iobuf;
263
	msize = h->msize;
264
	maddr = h->maddr;
265
	wmax = h->wmax;
266
	caddr = h->caddr;
267
	putbuf(cb);
268
 
269
	for(m=msize; m>=0; m--) {
270
		a = cw->dbucket + 1;
271
		if(a < 0 || a >= msize)
272
			a = 0;
273
		cw->dbucket = a;
274
		p = getbuf(cw->cdev, maddr + a/BKPERBLK, Brd);
275
		b = (Bucket*)p->iobuf + a%BKPERBLK;
276
		ce = b->entry + CEPERBK;
277
		bc = 0;
278
		for(c = b->entry; c < ce; c++)
279
			if(c->state == Cdump) {
280
				if(bc == 0) {
281
					bc = c;
282
					continue;
283
				}
284
				if(c->waddr < cw->daddr) {
285
					if(bc->waddr < cw->daddr &&
286
					   bc->waddr > c->waddr)
287
						bc = c;
288
					continue;
289
				}
290
				if(bc->waddr < cw->daddr ||
291
				   bc->waddr > c->waddr)
292
					bc = c;
293
			}
294
		if(bc) {
295
			c = bc;
296
			goto found;
297
		}
298
		putbuf(p);
299
	}
300
	if(cw->ncopy) {
301
		print("%lld blocks copied to worm\n", (Wideoff)cw->ncopy);
302
		cw->ncopy = 0;
303
	}
304
	cw->nodump = 1;
305
	return 0;
306
 
307
found:
308
	if (oldcachefmt)
309
		a = a*CEPERBK + (c - b->entry) + caddr;
310
	else
311
		a += (c - b->entry)*msize + caddr;
312
	p1 = getbuf(devnone, Cwdump1, 0);
313
	count = 0;
314
 
315
retry:
316
	count++;
317
	if(count > 10 || devread(cw->cdev, a, p1->iobuf))
318
		goto stop;
319
	m = c->waddr;
320
	cw->daddr = m;
321
	s1 = devwrite(cw->wdev, m, p1->iobuf);
322
	if(s1) {
323
		p2 = getbuf(devnone, Cwdump2, 0);
324
		s2 = devread(cw->wdev, m, p2->iobuf);
325
		if(s2) {
326
			if(s1 == 0x61 && s2 == 0x60) {
327
				putbuf(p2);
328
				goto retry;
329
			}
330
			goto stop1;
331
		}
332
		if(memcmp(p1->iobuf, p2->iobuf, RBUFSIZE))
333
			goto stop1;
334
		putbuf(p2);
335
	}
336
	/*
337
	 * reread and compare
338
	 */
339
	if(conf.dumpreread) {
340
		p2 = getbuf(devnone, Cwdump2, 0);
341
		s1 = devread(cw->wdev, m, p2->iobuf);
342
		if(s1)
343
			goto stop1;
344
		if(memcmp(p1->iobuf, p2->iobuf, RBUFSIZE)) {
345
			print("reread C%lld W%lld didnt compare\n",
346
				(Wideoff)a, (Wideoff)m);
347
			goto stop1;
348
		}
349
		putbuf(p2);
350
	}
351
 
352
	putbuf(p1);
353
	c->state = Cread;
354
	p->flags |= Bmod;
355
	putbuf(p);
356
 
357
	if(m > wmax) {
358
		cb = getbuf(cw->cdev, CACHE_ADDR, Brd|Bmod|Bres);
359
		h = (Cache*)cb->iobuf;
360
		if(m > h->wmax)
361
			h->wmax = m;
362
		putbuf(cb);
363
	}
364
	cw->ncopy++;
365
	return 1;
366
 
367
stop1:
368
	putbuf(p2);
369
	putbuf(p1);
370
	c->state = Cdump1;
371
	p->flags |= Bmod;
372
	putbuf(p);
373
	return 1;
374
 
375
stop:
376
	putbuf(p1);
377
	putbuf(p);
378
	print("stopping dump!!\n");
379
	cw->nodump = 1;
380
	return 0;
381
}
382
 
383
void
384
cwinit1(Device *dev)
385
{
386
	Cw *cw;
387
	static int first;
388
 
389
	cw = dev->private;
390
	if(cw)
391
		return;
392
 
393
	if(first == 0) {
394
		cmd_install("dump", "-- make dump backup to worm", cmd_dump);
395
		cmd_install("statw", "-- cache/worm stats", cmd_statw);
396
		cmd_install("cwcmd", "subcommand -- cache/worm errata", cmd_cwcmd);
397
		roflag = flag_install("ro", "-- ro reads and writes");
398
		first = 1;
399
	}
400
	cw = malloc(sizeof(Cw));
401
	dev->private = cw;
402
 
403
	cw->allflag = 0;
404
 
405
	cw->dev = dev;
406
	cw->cdev = CDEV(dev);
407
	cw->wdev = WDEV(dev);
408
	cw->rodev = RDEV(dev);
409
 
410
	devinit(cw->cdev);
411
	devinit(cw->wdev);
412
}
413
 
414
void
415
cwinit(Device *dev)
416
{
417
	Cw *cw;
418
	Cache *h;
419
	Iobuf *cb, *p;
420
	Off l, m;
421
 
422
	cwinit1(dev);
423
 
424
	cw = dev->private;
425
	l = devsize(cw->wdev);
426
	cb = getbuf(cw->cdev, CACHE_ADDR, Brd|Bmod|Bres);
427
	h = (Cache*)cb->iobuf;
428
	h->toytime = toytime() + SECOND(30);
429
	h->time = time(nil);
430
	m = h->wsize;
431
	if(l != m) {
432
		print("wdev changed size %lld to %lld\n",
433
			(Wideoff)m, (Wideoff)l);
434
		h->wsize = l;
435
		cb->flags |= Bmod;
436
	}
437
 
438
	for(m=0; m<h->msize; m++) {
439
		p = getbuf(cw->cdev, h->maddr + m/BKPERBLK, Brd);
440
		if(!p || checktag(p, Tbuck, h->maddr + m/BKPERBLK))
441
			panic("cwinit: checktag c bucket");
442
		putbuf(p);
443
	}
444
	putbuf(cb);
445
}
446
 
447
Off
448
cwsaddr(Device *dev)
449
{
450
	Iobuf *cb;
451
	Off sa;
452
 
453
	cb = getbuf(CDEV(dev), CACHE_ADDR, Brd|Bres);
454
	sa = ((Cache*)cb->iobuf)->sbaddr;
455
	putbuf(cb);
456
	return sa;
457
}
458
 
459
Off
460
cwraddr(Device *dev)
461
{
462
	Iobuf *cb;
463
	Off ra;
464
 
465
	switch(dev->type) {
466
	default:
467
		print("unknown dev in cwraddr %Z\n", dev);
468
		return 1;
469
 
470
	case Devcw:
471
		cb = getbuf(CDEV(dev), CACHE_ADDR, Brd|Bres);
472
		ra = ((Cache*)cb->iobuf)->cwraddr;
473
		break;
474
 
475
	case Devro:
476
		cb = getbuf(CDEV(dev->ro.parent), CACHE_ADDR, Brd|Bres);
477
		ra = ((Cache*)cb->iobuf)->roraddr;
478
		break;
479
	}
480
	putbuf(cb);
481
	return ra;
482
}
483
 
484
Devsize
485
cwsize(Device *dev)
486
{
487
	Iobuf *cb;
488
	Devsize fs;
489
 
490
	cb = getbuf(CDEV(dev), CACHE_ADDR, Brd|Bres);
491
	fs = ((Cache*)cb->iobuf)->fsize;
492
	putbuf(cb);
493
	return fs;
494
}
495
 
496
int
497
cwread(Device *dev, Off b, void *c)
498
{
499
	return cwio(dev, b, c, Oread) == Cerror;
500
}
501
 
502
int
503
cwwrite(Device *dev, Off b, void *c)
504
{
505
	return cwio(dev, b, c, Owrite) == Cerror;
506
}
507
 
508
int
509
roread(Device *dev, Off b, void *c)
510
{
511
	Device *d;
512
	int s;
513
 
514
	/*
515
	 * maybe better is to try buffer pool first
516
	 */
517
	d = dev->ro.parent;
518
	if(d == 0 || d->type != Devcw ||
519
	   d->private == 0 || RDEV(d) != dev) {
520
		print("bad rodev %Z\n", dev);
521
		return 1;
522
	}
523
	s = cwio(d, b, 0, Onone);
524
	if(s == Cdump || s == Cdump1 || s == Cread) {
525
		s = cwio(d, b, c, Oread);
526
		if(s == Cdump || s == Cdump1 || s == Cread) {
527
			if(cons.flags & roflag)
528
				print("roread: %Z %lld -> %Z(hit)\n",
529
					dev, (Wideoff)b, d);
530
			return 0;
531
		}
532
	}
533
	if(cons.flags & roflag)
534
		print("roread: %Z %lld -> %Z(miss)\n",
535
			dev, (Wideoff)b, WDEV(d));
536
	return devread(WDEV(d), b, c);
537
}
538
 
539
int
540
cwio(Device *dev, Off addr, void *buf, int opcode)
541
{
542
	Iobuf *p, *p1, *p2, *cb;
543
	Cache *h;
544
	Bucket *b;
545
	Centry *c;
546
	Off bn, a1, a2, max, newmax;
547
	int state;
548
	Cw *cw;
549
 
550
	cw = dev->private;
551
 
552
	cb = getbuf(cw->cdev, CACHE_ADDR, Brd|Bres);
553
	h = (Cache*)cb->iobuf;
554
	if(toytime() >= h->toytime) {
555
		cb->flags |= Bmod;
556
		h->toytime = toytime() + SECOND(30);
557
		h->time = time(nil);
558
	}
559
 
560
	if(addr < 0) {
561
		putbuf(cb);
562
		return Cerror;
563
	}
564
 
565
	bn = addr % h->msize;
566
	a1 = h->maddr + bn/BKPERBLK;
567
	if (oldcachefmt)
568
		a2 = bn*CEPERBK + h->caddr;
569
	else
570
		a2 = bn + h->caddr;
571
	max = h->wmax;
572
 
573
	putbuf(cb);
574
	newmax = 0;
575
 
576
	p = getbuf(cw->cdev, a1, Brd|Bmod);
577
	if(!p || checktag(p, Tbuck, a1))
578
		panic("cwio: checktag c bucket");
579
	b = (Bucket*)p->iobuf + bn%BKPERBLK;
580
 
581
	c = getcentry(b, addr);
582
	if(c == 0) {
583
		putbuf(p);
584
		print("%Z disk cache bucket %lld is full\n",
585
			cw->cdev, (Wideoff)a1);
586
		return Cerror;
587
	}
588
	if (oldcachefmt)
589
		a2 += c - b->entry;
590
	else
591
		a2 += (c - b->entry) * h->msize;
592
 
593
	state = c->state;
594
	switch(opcode) {
595
	default:
596
		goto bad;
597
 
598
	case Onone:
599
		break;
600
 
601
	case Oread:
602
		switch(state) {
603
		default:
604
			goto bad;
605
 
606
		case Cread:
607
			if(!devread(cw->cdev, a2, buf))
608
				break;
609
			c->state = Cnone;
610
 
611
		case Cnone:
612
			if(devread(cw->wdev, addr, buf)) {
613
				state = Cerror;
614
				break;
615
			}
616
			if(addr > max)
617
				newmax = addr;
618
			if(!devwrite(cw->cdev, a2, buf))
619
				c->state = Cread;
620
			break;
621
 
622
		case Cdirty:
623
		case Cdump:
624
		case Cdump1:
625
		case Cwrite:
626
			if(devread(cw->cdev, a2, buf))
627
				state = Cerror;
628
			break;
629
		}
630
		break;
631
 
632
	case Owrite:
633
		switch(state) {
634
		default:
635
			goto bad;
636
 
637
		case Cdump:
638
		case Cdump1:
639
			/*
640
			 * this is hard part -- a dump block must be
641
			 * sent to the worm if it is rewritten.
642
			 * if this causes an error, there is no
643
			 * place to save the dump1 data. the block
644
			 * is just reclassified as 'dump1' (botch)
645
			 */
646
			p1 = getbuf(devnone, Cwio1, 0);
647
			if(devread(cw->cdev, a2, p1->iobuf)) {
648
				putbuf(p1);
649
				print("cwio: write induced dump error - r cache\n");
650
 
651
			casenone:
652
				if(devwrite(cw->cdev, a2, buf)) {
653
					state = Cerror;
654
					break;
655
				}
656
				c->state = Cdump1;
657
				break;
658
			}
659
			if(devwrite(cw->wdev, addr, p1->iobuf)) {
660
				p2 = getbuf(devnone, Cwio2, 0);
661
				if(devread(cw->wdev, addr, p2->iobuf)) {
662
					putbuf(p1);
663
					putbuf(p2);
664
					print("cwio: write induced dump error - r+w worm\n");
665
					goto casenone;
666
				}
667
				if(memcmp(p1->iobuf, p2->iobuf, RBUFSIZE)) {
668
					putbuf(p1);
669
					putbuf(p2);
670
					print("cwio: write induced dump error - w worm\n");
671
					goto casenone;
672
				}
673
				putbuf(p2);
674
			}
675
			putbuf(p1);
676
			c->state = Cread;
677
			if(addr > max)
678
				newmax = addr;
679
			cw->ncopy++;
680
 
681
		case Cnone:
682
		case Cread:
683
			if(devwrite(cw->cdev, a2, buf)) {
684
				state = Cerror;
685
				break;
686
			}
687
			c->state = Cwrite;
688
			break;
689
 
690
		case Cdirty:
691
		case Cwrite:
692
			if(devwrite(cw->cdev, a2, buf))
693
				state = Cerror;
694
			break;
695
		}
696
		break;
697
 
698
	case Ogrow:
699
		if(state != Cnone) {
700
			print("%Z for block %lld cwgrow with state = %s\n",
701
				cw->cdev, (Wideoff)addr, cwnames[state]);
702
			break;
703
		}
704
		c->state = Cdirty;
705
		break;
706
 
707
	case Odump:
708
		if(state != Cdirty) {	/* BOTCH */
709
			print("%Z for block %lld cwdump with state = %s\n",
710
				cw->cdev, (Wideoff)addr, cwnames[state]);
711
			break;
712
		}
713
		c->state = Cdump;
714
		cw->ndump++;	/* only called from dump command */
715
		break;
716
 
717
	case Orele:
718
		if(state != Cwrite) {
719
			if(state != Cdump1)
720
				print("%Z for block %lld cwrele with state = %s\n",
721
					cw->cdev, (Wideoff)addr, cwnames[state]);
722
			break;
723
		}
724
		c->state = Cnone;
725
		break;
726
 
727
	case Ofree:
728
		if(state == Cwrite || state == Cread)
729
			c->state = Cnone;
730
		break;
731
	}
732
	if(DEBUG)
733
		print("cwio: %Z %lld s=%s o=%s ns=%s\n",
734
			dev, (Wideoff)addr, cwnames[state],
735
			cwnames[opcode],
736
			cwnames[c->state]);
737
	putbuf(p);
738
	if(newmax) {
739
		cb = getbuf(cw->cdev, CACHE_ADDR, Brd|Bmod|Bres);
740
		h = (Cache*)cb->iobuf;
741
		if(newmax > h->wmax)
742
			h->wmax = newmax;
743
		putbuf(cb);
744
	}
745
	return state;
746
 
747
bad:
748
	print("%Z block %lld cw state = %s; cw opcode = %s",
749
		dev, (Wideoff)addr, cwnames[state], cwnames[opcode]);
750
	return Cerror;
751
}
752
 
753
 
754
int
755
cwgrow(Device *dev, Superb *sb, int uid)
756
{
757
	char str[NAMELEN];
758
	Iobuf *cb;
759
	Cache *h;
760
	Filsys *filsys;
761
	Off fs, nfs, ws;
762
 
763
	cb = getbuf(CDEV(dev), CACHE_ADDR, Brd|Bmod|Bres);
764
	h = (Cache*)cb->iobuf;
765
	ws = h->wsize;
766
	fs = h->fsize;
767
	if(fs >= ws)
768
		return 0;
769
	nfs = fs + ADDFREE;
770
	if(nfs >= ws)
771
		nfs = ws;
772
	h->fsize = nfs;
773
	putbuf(cb);
774
 
775
	sb->fsize = nfs;
776
	filsys = dev2fs(dev);
777
	if (filsys == nil)
778
		print("%Z", dev);
779
	else
780
		print("%s", filsys->name);
781
	uidtostr(str, uid, 1);
782
	print(" grow from %lld to %lld limit %lld by %s uid=%d\n",
783
		(Wideoff)fs, (Wideoff)nfs, (Wideoff)ws, str, uid);
784
	for(nfs--; nfs>=fs; nfs--)
785
		switch(cwio(dev, nfs, 0, Ogrow)) {
786
		case Cerror:
787
			return 0;
788
		case Cnone:
789
			addfree(dev, nfs, sb);
790
		}
791
	return 1;
792
}
793
 
794
int
795
cwfree(Device *dev, Off addr)
796
{
797
	int state;
798
 
799
	if(dev->type == Devcw) {
800
		state = cwio(dev, addr, 0, Ofree);
801
		if(state != Cdirty)
802
			return 1;	/* do not put in freelist */
803
	}
804
	return 0;			/* put in freelist */
805
}
806
 
807
#ifdef unused
808
int
809
bktcheck(Bucket *b)
810
{
811
	Centry *c, *c1, *c2, *ce;
812
	int err;
813
 
814
	err = 0;
815
	if(b->agegen < CEPERBK || b->agegen > MAXAGE) {
816
		print("agegen %ld\n", b->agegen);
817
		err = 1;
818
	}
819
 
820
	ce = b->entry + CEPERBK;
821
	c1 = 0;		/* lowest age last pass */
822
	for(;;) {
823
		c2 = 0;		/* lowest age this pass */
824
		for(c = b->entry; c < ce; c++) {
825
			if(c1 != 0 && c != c1) {
826
				if(c->age == c1->age) {
827
					print("same age %d\n", c->age);
828
					err = 1;
829
				}
830
				if(c1->waddr == c->waddr)
831
				if(c1->state != Cnone)
832
				if(c->state != Cnone) {
833
					print("same waddr %lld\n",
834
						(Wideoff)c->waddr);
835
					err = 1;
836
				}
837
			}
838
			if(c1 != 0 && c->age <= c1->age)
839
				continue;
840
			if(c2 == 0 || c->age < c2->age)
841
				c2 = c;
842
		}
843
		if(c2 == 0)
844
			break;
845
		c1 = c2;
846
		if(c1->age >= b->agegen) {
847
			print("age >= generator %d %ld\n", c1->age, b->agegen);
848
			err = 1;
849
		}
850
	}
851
	return err;
852
}
853
#endif
854
 
855
void
856
resequence(Bucket *b)
857
{
858
	Centry *c, *ce, *cr;
859
	int age, i;
860
 
861
	ce = b->entry + CEPERBK;
862
	for(c = b->entry; c < ce; c++) {
863
		c->age += CEPERBK;
864
		if(c->age < CEPERBK)
865
			c->age = MAXAGE;
866
	}
867
	b->agegen += CEPERBK;
868
 
869
	age = 0;
870
	for(i=0;; i++) {
871
		cr = 0;
872
		for(c = b->entry; c < ce; c++) {
873
			if(c->age < i)
874
				continue;
875
			if(cr == 0 || c->age < age) {
876
				cr = c;
877
				age = c->age;
878
			}
879
		}
880
		if(cr == 0)
881
			break;
882
		cr->age = i;
883
	}
884
	b->agegen = i;
885
	cons.nreseq++;
886
}
887
 
888
Centry*
889
getcentry(Bucket *b, Off addr)
890
{
891
	Centry *c, *ce, *cr;
892
	int s, age;
893
 
894
	/*
895
	 * search for cache hit
896
	 * find oldest block as byproduct
897
	 */
898
	ce = b->entry + CEPERBK;
899
	age = 0;
900
	cr = 0;
901
	for(c = b->entry; c < ce; c++) {
902
		s = c->state;
903
		if(s == Cnone) {
904
			cr = c;
905
			age = 0;
906
			continue;
907
		}
908
		if(c->waddr == addr)
909
			goto found;
910
		if(s == Cread)
911
			if(cr == 0 || c->age < age) {
912
				cr = c;
913
				age = c->age;
914
			}
915
	}
916
 
917
	/*
918
	 * remap entry
919
	 */
920
	c = cr;
921
	if(c == 0)
922
		return 0;	/* bucket is full */
923
 
924
	c->state = Cnone;
925
	c->waddr = addr;
926
 
927
found:
928
	/*
929
	 * update the age to get filo cache.
930
	 * small number in age means old
931
	 */
932
	if(!cons.noage || c->state == Cnone) {
933
		age = b->agegen;
934
		c->age = age;
935
		age++;
936
		b->agegen = age;
937
		if(age < 0 || age >= MAXAGE)
938
			resequence(b);
939
	}
940
	return c;
941
}
942
 
943
/*
944
 * ream the cache
945
 * calculate new buckets
946
 */
947
Iobuf*
948
cacheinit(Device *dev)
949
{
950
	Iobuf *cb, *p;
951
	Cache *h;
952
	Device *cdev;
953
	Off m;
954
 
955
	print("cache init %Z\n", dev);
956
	cdev = CDEV(dev);
957
	devinit(cdev);
958
 
959
	cb = getbuf(cdev, CACHE_ADDR, Bmod|Bres);
960
	memset(cb->iobuf, 0, RBUFSIZE);
961
	settag(cb, Tcache, QPSUPER);
962
	h = (Cache*)cb->iobuf;
963
 
964
	/*
965
	 * calculate csize such that
966
	 * tsize = msize/BKPERBLK + csize and
967
	 * msize = csize/CEPERBK
968
	 */
969
	h->maddr = CACHE_ADDR + 1;
970
	m = devsize(cdev) - h->maddr;
971
	h->csize = ((Devsize)(m-1) * CEPERBK*BKPERBLK) / (CEPERBK*BKPERBLK+1);
972
	h->msize = h->csize/CEPERBK - 5;
973
	while(!prime(h->msize))
974
		h->msize--;
975
	h->csize = h->msize*CEPERBK;
976
	h->caddr = h->maddr + (h->msize+BKPERBLK-1)/BKPERBLK;
977
	h->wsize = devsize(WDEV(dev));
978
 
979
	if(h->msize <= 0)
980
		panic("cache too small");
981
	if(h->caddr + h->csize > m)
982
		panic("cache size error");
983
 
984
	/*
985
	 * setup cache map
986
	 */
987
	for(m=h->maddr; m<h->caddr; m++) {
988
		p = getbuf(cdev, m, Bmod);
989
		memset(p->iobuf, 0, RBUFSIZE);
990
		settag(p, Tbuck, m);
991
		putbuf(p);
992
	}
993
	print("done cacheinit\n");
994
	return cb;
995
}
996
 
997
Off
998
getstartsb(Device *dev)
999
{
1000
	Filsys *f;
1001
	Startsb *s;
1002
 
1003
	for(f=filsys; f->name; f++)
1004
		if(devcmpr(f->dev, dev) == 0) {
1005
			for(s=startsb; s->name; s++)
1006
				if(strcmp(f->name, s->name) == 0)
1007
					return s->startsb;
1008
			print(
1009
		"getstartsb: no special starting superblock for %Z %s\n",
1010
				dev, f->name);
1011
			return FIRST;
1012
		}
1013
	print("getstartsb: no filsys for device %Z\n", dev);
1014
	return FIRST;
1015
}
1016
 
1017
/*
1018
 * ream the cache
1019
 * calculate new buckets
1020
 * get superblock from
1021
 * last worm dump block.
1022
 */
1023
void
1024
cwrecover(Device *dev)
1025
{
1026
	Iobuf *p, *cb;
1027
	Cache *h;
1028
	Superb *s;
1029
	Off m, baddr;
1030
	Device *wdev;
1031
 
1032
//	print("cwrecover %Z\n", dev);	// DEBUG
1033
	cwinit1(dev);
1034
	wdev = WDEV(dev);
1035
 
1036
	p = getbuf(devnone, Cwxx1, 0);
1037
	s = (Superb*)p->iobuf;
1038
	baddr = 0;
1039
	m = getstartsb(dev);
1040
	localconfinit();
1041
	if(conf.firstsb)
1042
		m = conf.firstsb;
1043
	for(;;) {
1044
		memset(p->iobuf, 0, RBUFSIZE);
1045
		if(devread(wdev, m, p->iobuf) ||
1046
		   checktag(p, Tsuper, QPSUPER))
1047
			break;
1048
		baddr = m;
1049
		m = s->next;
1050
		print("dump %lld is good; %lld next\n", (Wideoff)baddr, (Wideoff)m);
1051
		if(baddr == conf.recovsb)
1052
			break;
1053
	}
1054
	putbuf(p);
1055
	if(!baddr)
1056
		panic("recover: no superblock");
1057
 
1058
	p = getbuf(wdev, baddr, Brd);
1059
	s = (Superb*)p->iobuf;
1060
 
1061
	cb = cacheinit(dev);
1062
	h = (Cache*)cb->iobuf;
1063
	h->sbaddr = baddr;
1064
	h->cwraddr = s->cwraddr;
1065
	h->roraddr = s->roraddr;
1066
	h->fsize = s->fsize + 100;		/* this must be conservative */
1067
	if(conf.recovcw)
1068
		h->cwraddr = conf.recovcw;
1069
	if(conf.recovro)
1070
		h->roraddr = conf.recovro;
1071
 
1072
	putbuf(cb);
1073
	putbuf(p);
1074
 
1075
	p = getbuf(dev, baddr, Brd|Bmod);
1076
	s = (Superb*)p->iobuf;
1077
 
1078
	memset(&s->fbuf, 0, sizeof(s->fbuf));
1079
	s->fbuf.free[0] = 0;
1080
	s->fbuf.nfree = 1;
1081
	s->tfree = 0;
1082
	if(conf.recovcw)
1083
		s->cwraddr = conf.recovcw;
1084
	if(conf.recovro)
1085
		s->roraddr = conf.recovro;
1086
 
1087
	putbuf(p);
1088
	print("done recover\n");
1089
}
1090
 
1091
/*
1092
 * ream the cache
1093
 * calculate new buckets
1094
 * initialize superblock.
1095
 */
1096
void
1097
cwream(Device *dev)
1098
{
1099
	Iobuf *p, *cb;
1100
	Cache *h;
1101
	Superb *s;
1102
	Off m, baddr;
1103
	Device *cdev;
1104
 
1105
	print("cwream %Z\n", dev);
1106
	cwinit1(dev);
1107
	cdev = CDEV(dev);
1108
	devinit(cdev);
1109
 
1110
	baddr = FIRST;	/*	baddr   = super addr
1111
				baddr+1 = cw root
1112
				baddr+2 = ro root
1113
				baddr+3 = reserved next superblock */
1114
 
1115
	cb = cacheinit(dev);
1116
	h = (Cache*)cb->iobuf;
1117
 
1118
	h->sbaddr = baddr;
1119
	h->cwraddr = baddr+1;
1120
	h->roraddr = baddr+2;
1121
	h->fsize = 0;	/* prevents superream from freeing */
1122
 
1123
	putbuf(cb);
1124
 
1125
	for(m=0; m<3; m++)
1126
		cwio(dev, baddr+m, 0, Ogrow);
1127
	superream(dev, baddr);
1128
	rootream(dev, baddr+1);			/* cw root */
1129
	rootream(dev, baddr+2);			/* ro root */
1130
 
1131
	cb = getbuf(cdev, CACHE_ADDR, Brd|Bmod|Bres);
1132
	h = (Cache*)cb->iobuf;
1133
	h->fsize = baddr+4;
1134
	putbuf(cb);
1135
 
1136
	p = getbuf(dev, baddr, Brd|Bmod|Bimm);
1137
	s = (Superb*)p->iobuf;
1138
	s->last = baddr;
1139
	s->cwraddr = baddr+1;
1140
	s->roraddr = baddr+2;
1141
	s->next = baddr+3;
1142
	s->fsize = baddr+4;
1143
	putbuf(p);
1144
 
1145
	for(m=0; m<3; m++)
1146
		cwio(dev, baddr+m, 0, Odump);
1147
}
1148
 
1149
Off
1150
rewalk1(Cw *cw, Off addr, int slot, Wpath *up)
1151
{
1152
	Iobuf *p, *p1;
1153
	Dentry *d;
1154
 
1155
	if(up == 0)
1156
		return cwraddr(cw->dev);
1157
	up->addr = rewalk1(cw, up->addr, up->slot, up->up);
1158
	p = getbuf(cw->dev, up->addr, Brd|Bmod);
1159
	d = getdir(p, up->slot);
1160
	if(!d || !(d->mode & DALLOC)) {
1161
		print("rewalk1 1\n");
1162
		if(p)
1163
			putbuf(p);
1164
		return addr;
1165
	}
1166
	p1 = dnodebuf(p, d, slot/DIRPERBUF, 0, 0);
1167
	if(!p1) {
1168
		print("rewalk1 2\n");
1169
		if(p)
1170
			putbuf(p);
1171
		return addr;
1172
	}
1173
	if(DEBUG)
1174
		print("rewalk1 %lld to %lld \"%s\"\n",
1175
			(Wideoff)addr, (Wideoff)p1->addr, d->name);
1176
	addr = p1->addr;
1177
	p1->flags |= Bmod;
1178
	putbuf(p1);
1179
	putbuf(p);
1180
	return addr;
1181
}
1182
 
1183
Off
1184
rewalk2(Cw *cw, Off addr, int slot, Wpath *up)
1185
{
1186
	Iobuf *p, *p1;
1187
	Dentry *d;
1188
 
1189
	if(up == 0)
1190
		return cwraddr(cw->rodev);
1191
	up->addr = rewalk2(cw, up->addr, up->slot, up->up);
1192
	p = getbuf(cw->rodev, up->addr, Brd);
1193
	d = getdir(p, up->slot);
1194
	if(!d || !(d->mode & DALLOC)) {
1195
		print("rewalk2 1\n");
1196
		if(p)
1197
			putbuf(p);
1198
		return addr;
1199
	}
1200
	p1 = dnodebuf(p, d, slot/DIRPERBUF, 0, 0);
1201
	if(!p1) {
1202
		print("rewalk2 2\n");
1203
		if(p)
1204
			putbuf(p);
1205
		return addr;
1206
	}
1207
	if(DEBUG)
1208
		print("rewalk2 %lld to %lld \"%s\"\n",
1209
			(Wideoff)addr, (Wideoff)p1->addr, d->name);
1210
	addr = p1->addr;
1211
	putbuf(p1);
1212
	putbuf(p);
1213
	return addr;
1214
}
1215
 
1216
void
1217
rewalk(Cw *cw)
1218
{
1219
	int h;
1220
	File *f;
1221
 
1222
	for(h=0; h<nelem(flist); h++)
1223
		for(f=flist[h]; f; f=f->next) {
1224
			if(!f->fs)
1225
				continue;
1226
			if(cw->dev == f->fs->dev)
1227
				f->addr = rewalk1(cw, f->addr, f->slot, f->wpath);
1228
			else
1229
			if(cw->rodev == f->fs->dev)
1230
				f->addr = rewalk2(cw, f->addr, f->slot, f->wpath);
1231
		}
1232
}
1233
 
1234
Off
1235
split(Cw *cw, Iobuf *p, Off addr)
1236
{
1237
	Off na;
1238
	int state;
1239
 
1240
	na = 0;
1241
	if(p && (p->flags & Bmod)) {
1242
		p->flags |= Bimm;
1243
		putbuf(p);
1244
		p = 0;
1245
	}
1246
	state = cwio(cw->dev, addr, 0, Onone);	/* read the state (twice?) */
1247
	switch(state) {
1248
	default:
1249
		panic("split: unknown state %s", cwnames[state]);
1250
 
1251
	case Cerror:
1252
	case Cnone:
1253
	case Cdump:
1254
	case Cread:
1255
		break;
1256
 
1257
	case Cdump1:
1258
	case Cwrite:
1259
		/*
1260
		 * botch.. could be done by relabeling
1261
		 */
1262
		if(!p) {
1263
			p = getbuf(cw->dev, addr, Brd);
1264
			if(!p) {
1265
				print("split: null getbuf\n");
1266
				break;
1267
			}
1268
		}
1269
		na = cw->fsize;
1270
		cw->fsize = na+1;
1271
		cwio(cw->dev, na, 0, Ogrow);
1272
		cwio(cw->dev, na, p->iobuf, Owrite);
1273
		cwio(cw->dev, na, 0, Odump);
1274
		cwio(cw->dev, addr, 0, Orele);
1275
		break;
1276
 
1277
	case Cdirty:
1278
		cwio(cw->dev, addr, 0, Odump);
1279
		break;
1280
	}
1281
	if(p)
1282
		putbuf(p);
1283
	return na;
1284
}
1285
 
1286
int
1287
isdirty(Cw *cw, Iobuf *p, Off addr, int tag)
1288
{
1289
	int s;
1290
 
1291
	if(p && (p->flags & Bmod))
1292
		return 1;
1293
	s = cwio(cw->dev, addr, 0, Onone);
1294
	if(s == Cdirty || s == Cwrite)
1295
		return 1;
1296
	if(tag >= Tind1 && tag <= Tmaxind)
1297
		/* botch, get these modified */
1298
		if(s != Cnone)
1299
			return 1;
1300
	return 0;
1301
}
1302
 
1303
Off
1304
cwrecur(Cw *cw, Off addr, int tag, int tag1, long qp)
1305
{
1306
	Iobuf *p;
1307
	Dentry *d;
1308
	int i, j, shouldstop;
1309
	Off na;
1310
	char *np;
1311
 
1312
	shouldstop = 0;
1313
	p = getbuf(cw->dev, addr, Bprobe);
1314
	if(!isdirty(cw, p, addr, tag)) {
1315
		if(!cw->all) {
1316
			if(DEBUG)
1317
				print("cwrecur: %lld t=%s not dirty %s\n",
1318
					(Wideoff)addr, tagnames[tag], cw->name);
1319
			if(p)
1320
				putbuf(p);
1321
			return 0;
1322
		}
1323
		shouldstop = 1;
1324
	}
1325
	if(DEBUG)
1326
		print("cwrecur: %lld t=%s %s\n",
1327
			(Wideoff)addr, tagnames[tag], cw->name);
1328
	if(cw->depth >= 100) {
1329
		print("dump depth too great %s\n", cw->name);
1330
		if(p)
1331
			putbuf(p);
1332
		return 0;
1333
	}
1334
	cw->depth++;
1335
 
1336
	switch(tag) {
1337
	default:
1338
		print("cwrecur: unknown tag %d %s\n", tag, cw->name);
1339
 
1340
	case Tfile:
1341
		break;
1342
 
1343
	case Tsuper:
1344
	case Tdir:
1345
		if(!p) {
1346
			p = getbuf(cw->dev, addr, Brd);
1347
			if(!p) {
1348
				print("cwrecur: Tdir p null %s\n",
1349
					cw->name);
1350
				break;
1351
			}
1352
		}
1353
		if(tag == Tdir) {
1354
			cw->namepad[0] = 0;	/* force room */
1355
			np = strchr(cw->name, 0);
1356
			*np++ = '/';
1357
		} else {
1358
			np = 0;	/* set */
1359
			cw->name[0] = 0;
1360
		}
1361
 
1362
		for(i=0; i<DIRPERBUF; i++) {
1363
			d = getdir(p, i);
1364
			if(!(d->mode & DALLOC))
1365
				continue;
1366
			qp = d->qid.path & ~QPDIR;
1367
			if(tag == Tdir)
1368
				strncpy(np, d->name, NAMELEN);
1369
			else
1370
			if(i > 0)
1371
				print("cwrecur: root with >1 directory\n");
1372
			tag1 = Tfile;
1373
			if(d->mode & DDIR)
1374
				tag1 = Tdir;
1375
			for(j=0; j<NDBLOCK; j++) {
1376
				na = d->dblock[j];
1377
				if(na) {
1378
					na = cwrecur(cw, na, tag1, 0, qp);
1379
					if(na) {
1380
						d->dblock[j] = na;
1381
						p->flags |= Bmod;
1382
					}
1383
				}
1384
			}
1385
			for (j = 0; j < NIBLOCK; j++) {
1386
				na = d->iblocks[j];
1387
				if(na) {
1388
					na = cwrecur(cw, na, Tind1+j, tag1, qp);
1389
					if(na) {
1390
						d->iblocks[j] = na;
1391
						p->flags |= Bmod;
1392
					}
1393
				}
1394
			}
1395
		}
1396
		break;
1397
 
1398
	case Tind1:
1399
		j = tag1;
1400
		tag1 = 0;
1401
		goto tind;
1402
 
1403
	case Tind2:
1404
#ifndef COMPAT32
1405
	case Tind3:
1406
	case Tind4:
1407
	/* add more Tind tags here ... */
1408
#endif
1409
		j = tag-1;
1410
	tind:
1411
		if(!p) {
1412
			p = getbuf(cw->dev, addr, Brd);
1413
			if(!p) {
1414
				print("cwrecur: Tind p null %s\n", cw->name);
1415
				break;
1416
			}
1417
		}
1418
		for(i=0; i<INDPERBUF; i++) {
1419
			na = ((Off *)p->iobuf)[i];
1420
			if(na) {
1421
				na = cwrecur(cw, na, j, tag1, qp);
1422
				if(na) {
1423
					((Off *)p->iobuf)[i] = na;
1424
					p->flags |= Bmod;
1425
				}
1426
			}
1427
		}
1428
		break;
1429
	}
1430
	na = split(cw, p, addr);
1431
	cw->depth--;
1432
	if(na && shouldstop) {
1433
		if(cw->falsehits < 10)
1434
			print("shouldstop %lld %lld t=%s %s\n",
1435
				(Wideoff)addr, (Wideoff)na,
1436
				tagnames[tag], cw->name);
1437
		cw->falsehits++;
1438
	}
1439
	return na;
1440
}
1441
 
1442
Timet	nextdump(Timet t);
1443
 
1444
void
1445
cfsdump(Filsys *fs)
1446
{
1447
	long m, n, i;
1448
	Off orba, rba, oroa, roa, sba, a;
1449
	Timet tim;
1450
	char tstr[20];
1451
	Iobuf *pr, *p1, *p;
1452
	Dentry *dr, *d1, *d;
1453
	Cache *h;
1454
	Superb *s;
1455
	Cw *cw;
1456
 
1457
	if(fs->dev->type != Devcw) {
1458
		print("cant dump; not cw device: %Z\n", fs->dev);
1459
		return;
1460
	}
1461
	cw = fs->dev->private;
1462
	if(cw == 0) {
1463
		print("cant dump: has not been inited: %Z\n", fs->dev);
1464
		return;
1465
	}
1466
 
1467
	tim = toytime();
1468
	wlock(&mainlock);		/* dump */
1469
 
1470
	/*
1471
	 * set up static structure
1472
	 * with frequent variables
1473
	 */
1474
	cw->ndump = 0;
1475
	cw->name[0] = 0;
1476
	cw->depth = 0;
1477
 
1478
	/*
1479
	 * cw root
1480
	 */
1481
	sync("before dump");
1482
	cw->fsize = cwsize(cw->dev);
1483
	orba = cwraddr(cw->dev);
1484
	print("cwroot %lld", (Wideoff)orba);
1485
	cons.noage = 1;
1486
	cw->all = cw->allflag;
1487
	rba = cwrecur(cw, orba, Tsuper, 0, QPROOT);
1488
	if(rba == 0)
1489
		rba = orba;
1490
	print("->%lld\n", (Wideoff)rba);
1491
	sync("after cw");
1492
 
1493
	/*
1494
	 * partial super block
1495
	 */
1496
	p = getbuf(cw->dev, cwsaddr(cw->dev), Brd|Bmod|Bimm);
1497
	s = (Superb*)p->iobuf;
1498
	s->fsize = cw->fsize;
1499
	s->cwraddr = rba;
1500
	putbuf(p);
1501
 
1502
	/*
1503
	 * partial cache block
1504
	 */
1505
	p = getbuf(cw->cdev, CACHE_ADDR, Brd|Bmod|Bimm|Bres);
1506
	h = (Cache*)p->iobuf;
1507
	h->fsize = cw->fsize;
1508
	h->cwraddr = rba;
1509
	putbuf(p);
1510
 
1511
	/*
1512
	 * ro root
1513
	 */
1514
	oroa = cwraddr(cw->rodev);
1515
	pr = getbuf(cw->dev, oroa, Brd|Bmod);
1516
	dr = getdir(pr, 0);
1517
 
1518
	datestr(tstr, time(nil));	/* tstr = "yyyymmdd" */
1519
	n = 0;
1520
	for(a=0;; a++) {
1521
		p1 = dnodebuf(pr, dr, a, Tdir, 0);
1522
		if(!p1)
1523
			goto bad;
1524
		n++;
1525
		for(i=0; i<DIRPERBUF; i++) {
1526
			d1 = getdir(p1, i);
1527
			if(!d1)
1528
				goto bad;
1529
			if(!(d1->mode & DALLOC))
1530
				goto found1;
1531
			if(!memcmp(d1->name, tstr, 4))
1532
				goto found2;	/* found entry */
1533
		}
1534
		putbuf(p1);
1535
	}
1536
 
1537
	/*
1538
	 * no year directory, create one
1539
	 */
1540
found1:
1541
	p = getbuf(cw->dev, rba, Brd);
1542
	d = getdir(p, 0);
1543
	d1->qid = d->qid;
1544
	d1->qid.version += n;
1545
	memmove(d1->name, tstr, 4);
1546
	d1->mode = d->mode;
1547
	d1->uid = d->uid;
1548
	d1->gid = d->gid;
1549
	putbuf(p);
1550
	accessdir(p1, d1, FWRITE, 0);
1551
 
1552
	/*
1553
	 * put mmdd[count] in year directory
1554
	 */
1555
found2:
1556
	accessdir(p1, d1, FREAD, 0);
1557
	putbuf(pr);
1558
	pr = p1;
1559
	dr = d1;
1560
 
1561
	n = 0;
1562
	m = 0;
1563
	for(a=0;; a++) {
1564
		p1 = dnodebuf(pr, dr, a, Tdir, 0);
1565
		if(!p1)
1566
			goto bad;
1567
		n++;
1568
		for(i=0; i<DIRPERBUF; i++) {
1569
			d1 = getdir(p1, i);
1570
			if(!d1)
1571
				goto bad;
1572
			if(!(d1->mode & DALLOC))
1573
				goto found;
1574
			if(!memcmp(d1->name, tstr+4, 4))
1575
				m++;
1576
		}
1577
		putbuf(p1);
1578
	}
1579
 
1580
	/*
1581
	 * empty slot put in root
1582
	 */
1583
found:
1584
	if(m)	/* how many dumps this date */
1585
		sprint(tstr+8, "%ld", m);
1586
 
1587
	p = getbuf(cw->dev, rba, Brd);
1588
	d = getdir(p, 0);
1589
	*d1 = *d;				/* qid is QPROOT */
1590
	putbuf(p);
1591
	strcpy(d1->name, tstr+4);
1592
	d1->qid.version += n;
1593
	accessdir(p1, d1, FWRITE, 0);
1594
	putbuf(p1);
1595
	putbuf(pr);
1596
 
1597
	cw->fsize = cwsize(cw->dev);
1598
	oroa = cwraddr(cw->rodev);		/* probably redundant */
1599
	print("roroot %lld", (Wideoff)oroa);
1600
 
1601
	cons.noage = 0;
1602
	cw->all = 0;
1603
	roa = cwrecur(cw, oroa, Tsuper, 0, QPROOT);
1604
	if(roa == 0) {
1605
		print("[same]");
1606
		roa = oroa;
1607
	}
1608
	print("->%lld /%.4s/%s\n", (Wideoff)roa, tstr, tstr+4);
1609
	sync("after ro");
1610
 
1611
	/*
1612
	 * final super block
1613
	 */
1614
	a = cwsaddr(cw->dev);
1615
	print("sblock %lld", (Wideoff)a);
1616
	p = getbuf(cw->dev, a, Brd|Bmod|Bimm);
1617
	s = (Superb*)p->iobuf;
1618
	s->last = a;
1619
	sba = s->next;
1620
	s->next = cw->fsize;
1621
	cw->fsize++;
1622
	s->fsize = cw->fsize;
1623
	s->roraddr = roa;
1624
 
1625
	cwio(cw->dev, sba, 0, Ogrow);
1626
	cwio(cw->dev, sba, p->iobuf, Owrite);
1627
	cwio(cw->dev, sba, 0, Odump);
1628
	print("->%lld (->%lld)\n", (Wideoff)sba, (Wideoff)s->next);
1629
 
1630
	putbuf(p);
1631
 
1632
	/*
1633
	 * final cache block
1634
	 */
1635
	p = getbuf(cw->cdev, CACHE_ADDR, Brd|Bmod|Bimm|Bres);
1636
	h = (Cache*)p->iobuf;
1637
	h->fsize = cw->fsize;
1638
	h->roraddr = roa;
1639
	h->sbaddr = sba;
1640
	putbuf(p);
1641
 
1642
	rewalk(cw);
1643
	sync("all done");
1644
 
1645
	print("%lld blocks queued for worm\n", (Wideoff)cw->ndump);
1646
	print("%lld falsehits\n", (Wideoff)cw->falsehits);
1647
	cw->nodump = 0;
1648
 
1649
	/*
1650
	 * extend all of the locks
1651
	 */
1652
	tim = toytime() - tim;
1653
	for(i=0; i<NTLOCK; i++)
1654
		if(tlocks[i].time > 0)
1655
			tlocks[i].time += tim;
1656
 
1657
	wunlock(&mainlock);
1658
	nextdump(time(nil));
1659
	return;
1660
 
1661
bad:
1662
	panic("dump: bad");
1663
}
1664
 
1665
void
1666
mvstates(Device *dev, int s1, int s2, int side)
1667
{
1668
	Iobuf *p, *cb;
1669
	Cache *h;
1670
	Bucket *b;
1671
	Centry *c, *ce;
1672
	Off m, lo, hi, msize, maddr;
1673
	Cw *cw;
1674
 
1675
	cw = dev->private;
1676
	lo = 0;
1677
	hi = lo + devsize(dev->cw.w);	/* size of all sides totalled */
1678
	if(side >= 0) {
1679
		/* operate on only a single disc side */
1680
		Sidestarts ss;
1681
 
1682
		wormsidestarts(dev, side, &ss);
1683
		lo = ss.sstart;
1684
		hi = ss.s1start;
1685
	}
1686
	cb = getbuf(cw->cdev, CACHE_ADDR, Brd|Bres);
1687
	if(!cb || checktag(cb, Tcache, QPSUPER))
1688
		panic("cwstats: checktag c bucket");
1689
	h = (Cache*)cb->iobuf;
1690
	msize = h->msize;
1691
	maddr = h->maddr;
1692
	putbuf(cb);
1693
 
1694
	for(m=0; m<msize; m++) {
1695
		p = getbuf(cw->cdev, maddr + m/BKPERBLK, Brd|Bmod);
1696
		if(!p || checktag(p, Tbuck, maddr + m/BKPERBLK))
1697
			panic("cwtest: checktag c bucket");
1698
		b = (Bucket*)p->iobuf + m%BKPERBLK;
1699
		ce = b->entry + CEPERBK;
1700
		for(c=b->entry; c<ce; c++)
1701
			if(c->state == s1 && c->waddr >= lo && c->waddr < hi)
1702
				c->state = s2;
1703
		putbuf(p);
1704
	}
1705
}
1706
 
1707
void
1708
prchain(Device *dev, Off m, int flg)
1709
{
1710
	Iobuf *p;
1711
	Superb *s;
1712
 
1713
	if(m == 0) {
1714
		if(flg)
1715
			m = cwsaddr(dev);
1716
		else
1717
			m = getstartsb(dev);
1718
	}
1719
	p = getbuf(devnone, Cwxx2, 0);
1720
	s = (Superb*)p->iobuf;
1721
	for(;;) {
1722
		memset(p->iobuf, 0, RBUFSIZE);
1723
		if(devread(WDEV(dev), m, p->iobuf) ||
1724
		   checktag(p, Tsuper, QPSUPER))
1725
			break;
1726
		if(flg) {
1727
			print("dump %lld is good; %lld prev\n", (Wideoff)m,
1728
				(Wideoff)s->last);
1729
			print("\t%lld cwroot; %lld roroot\n",
1730
				(Wideoff)s->cwraddr, (Wideoff)s->roraddr);
1731
			if(m <= s->last)
1732
				break;
1733
			m = s->last;
1734
		} else {
1735
			print("dump %lld is good; %lld next\n", (Wideoff)m,
1736
				(Wideoff)s->next);
1737
			print("\t%lld cwroot; %lld roroot\n",
1738
				(Wideoff)s->cwraddr, (Wideoff)s->roraddr);
1739
			if(m >= s->next)
1740
				break;
1741
			m = s->next;
1742
		}
1743
	}
1744
	putbuf(p);
1745
}
1746
 
1747
void
1748
touchsb(Device *dev)
1749
{
1750
	Iobuf *p;
1751
	Off m;
1752
 
1753
	m = cwsaddr(dev);
1754
	p = getbuf(devnone, Cwxx2, 0);
1755
 
1756
	memset(p->iobuf, 0, RBUFSIZE);
1757
	if(devread(WDEV(dev), m, p->iobuf) ||
1758
	   checktag(p, Tsuper, QPSUPER))
1759
		print("%Z block %lld WORM SUPER BLOCK READ FAILED\n",
1760
			WDEV(dev), (Wideoff)m);
1761
	else
1762
		print("%Z touch superblock %lld\n", WDEV(dev), (Wideoff)m);
1763
	putbuf(p);
1764
}
1765
 
1766
void
1767
storesb(Device *dev, Off last, int doit)
1768
{
1769
	Iobuf *ph, *ps;
1770
	Cache *h;
1771
	Superb *s;
1772
	Off sbaddr, qidgen;
1773
 
1774
	sbaddr = cwsaddr(dev);
1775
 
1776
	ps = getbuf(devnone, Cwxx2, 0);
1777
	if(!ps) {
1778
		print("sbstore: getbuf\n");
1779
		return;
1780
	}
1781
 
1782
	/*
1783
	 * try to read last sb
1784
	 */
1785
	memset(ps->iobuf, 0, RBUFSIZE);
1786
	if(devread(WDEV(dev), last, ps->iobuf) ||
1787
	   checktag(ps, Tsuper, QPSUPER))
1788
		print("read last failed\n");
1789
	else
1790
		print("read last succeeded\n");
1791
 
1792
	s = (Superb*)ps->iobuf;
1793
	qidgen = s->qidgen;
1794
	if(qidgen == 0)
1795
		qidgen = 0x31415;
1796
	qidgen += 1000;
1797
	if(s->next != sbaddr)
1798
		print("next(last) is not sbaddr %lld %lld\n",
1799
			(Wideoff)s->next, (Wideoff)sbaddr);
1800
	else
1801
		print("next(last) is sbaddr\n");
1802
 
1803
	/*
1804
	 * read cached superblock
1805
	 */
1806
	ph = getbuf(CDEV(dev), CACHE_ADDR, Brd|Bres);
1807
	if(!ph || checktag(ph, Tcache, QPSUPER)) {
1808
		print("cwstats: checktag c bucket\n");
1809
		if(ph)
1810
			putbuf(ph);
1811
		putbuf(ps);
1812
		return;
1813
	} else
1814
		print("read cached sb succeeded\n");
1815
 
1816
	h = (Cache*)ph->iobuf;
1817
 
1818
	memset(ps->iobuf, 0, RBUFSIZE);
1819
	settag(ps, Tsuper, QPSUPER);
1820
	ps->flags = 0;
1821
	s = (Superb*)ps->iobuf;
1822
 
1823
	s->cwraddr = h->cwraddr;
1824
	s->roraddr = h->roraddr;
1825
	s->fsize = h->fsize;
1826
	s->fstart = 2;
1827
	s->last = last;
1828
	s->next = h->roraddr+1;
1829
 
1830
	s->qidgen = qidgen;
1831
	putbuf(ph);
1832
 
1833
	if(s->fsize-1 != s->next ||
1834
	   s->fsize-2 != s->roraddr ||
1835
	   s->fsize-5 != s->cwraddr) {
1836
		print("addrs not in relationship %lld %lld %lld %lld\n",
1837
			(Wideoff)s->cwraddr, (Wideoff)s->roraddr,
1838
			(Wideoff)s->next, (Wideoff)s->fsize);
1839
		putbuf(ps);
1840
		return;
1841
	} else
1842
		print("addresses in relation\n");
1843
 
1844
	if(doit)
1845
	if(devwrite(WDEV(dev), sbaddr, ps->iobuf))
1846
		print("%Z block %lld WORM SUPER BLOCK WRITE FAILED\n",
1847
			WDEV(dev), (Wideoff)sbaddr);
1848
	ps->flags = 0;
1849
	putbuf(ps);
1850
}
1851
 
1852
void
1853
savecache(Device *dev)
1854
{
1855
	Iobuf *p, *cb;
1856
	Cache *h;
1857
	Bucket *b;
1858
	Centry *c, *ce;
1859
	long n, left;
1860
	Off m, maddr, msize, *longp, nbyte;
1861
	Device *cdev;
1862
 
1863
	if(walkto("/adm/cache") || con_open(FID2, OWRITE|OTRUNC)) {
1864
		print("cant open /adm/cache\n");
1865
		return;
1866
	}
1867
	cdev = CDEV(dev);
1868
	cb = getbuf(cdev, CACHE_ADDR, Brd|Bres);
1869
	if(!cb || checktag(cb, Tcache, QPSUPER))
1870
		panic("savecache: checktag c bucket");
1871
	h = (Cache*)cb->iobuf;
1872
	msize = h->msize;
1873
	maddr = h->maddr;
1874
	putbuf(cb);
1875
 
1876
	n = BUFSIZE;			/* calculate write size */
1877
	if(n > MAXDAT)
1878
		n = MAXDAT;
1879
 
1880
	cb = getbuf(devnone, Cwxx4, 0);
1881
	longp = (Off *)cb->iobuf;
1882
	left = n/sizeof(Off);
1883
	cons.offset = 0;
1884
 
1885
	for(m=0; m<msize; m++) {
1886
		if(left < BKPERBLK) {
1887
			nbyte = (n/sizeof(Off) - left) * sizeof(Off);
1888
			con_write(FID2, cb->iobuf, cons.offset, nbyte);
1889
			cons.offset += nbyte;
1890
			longp = (Off *)cb->iobuf;
1891
			left = n/sizeof(Off);
1892
		}
1893
		p = getbuf(cdev, maddr + m/BKPERBLK, Brd);
1894
		if(!p || checktag(p, Tbuck, maddr + m/BKPERBLK))
1895
			panic("cwtest: checktag c bucket");
1896
		b = (Bucket*)p->iobuf + m%BKPERBLK;
1897
		ce = b->entry + CEPERBK;
1898
		for(c = b->entry; c < ce; c++)
1899
			if(c->state == Cread) {
1900
				*longp++ = c->waddr;
1901
				left--;
1902
			}
1903
		putbuf(p);
1904
	}
1905
	nbyte = (n/sizeof(Off) - left) * sizeof(Off);
1906
	con_write(FID2, cb->iobuf, cons.offset, nbyte);
1907
	putbuf(cb);
1908
}
1909
 
1910
void
1911
loadcache(Device *dev, int dskno)
1912
{
1913
	Iobuf *p, *cb;
1914
	Off m, nbyte, *longp, count;
1915
	Sidestarts ss;
1916
 
1917
	if(walkto("/adm/cache") || con_open(FID2, OREAD)) {
1918
		print("cant open /adm/cache\n");
1919
		return;
1920
	}
1921
 
1922
	cb = getbuf(devnone, Cwxx4, 0);
1923
	cons.offset = 0;
1924
	count = 0;
1925
 
1926
	if (dskno >= 0)
1927
		wormsidestarts(dev, dskno, &ss);
1928
	for(;;) {
1929
		memset(cb->iobuf, 0, BUFSIZE);
1930
		nbyte = con_read(FID2, cb->iobuf, cons.offset, 100) / sizeof(Off);
1931
		if(nbyte <= 0)
1932
			break;
1933
		cons.offset += nbyte * sizeof(Off);
1934
		longp = (Off *)cb->iobuf;
1935
		while(nbyte > 0) {
1936
			m = *longp++;
1937
			nbyte--;
1938
			if(m == 0)
1939
				continue;
1940
			/* if given a diskno, restrict to just that disc side */
1941
			if(dskno < 0 || m >= ss.sstart && m < ss.s1start) {
1942
				p = getbuf(dev, m, Brd);
1943
				if(p)
1944
					putbuf(p);
1945
				count++;
1946
			}
1947
		}
1948
	}
1949
	putbuf(cb);
1950
	print("%lld blocks loaded from worm %d\n", (Wideoff)count, dskno);
1951
}
1952
 
1953
void
1954
morecache(Device *dev, int dskno, Off size)
1955
{
1956
	Iobuf *p;
1957
	Off m, ml, mh, mm, count;
1958
	Cache *h;
1959
	Sidestarts ss;
1960
 
1961
	p = getbuf(CDEV(dev), CACHE_ADDR, Brd|Bres);
1962
	if(!p || checktag(p, Tcache, QPSUPER))
1963
		panic("savecache: checktag c bucket");
1964
	h = (Cache*)p->iobuf;
1965
	mm = h->wmax;
1966
	putbuf(p);
1967
 
1968
	wormsidestarts(dev, dskno, &ss);
1969
	ml = ss.sstart;		/* start at beginning of disc side #dskno */
1970
	mh = ml + size;
1971
	if(mh > mm) {
1972
		mh = mm;
1973
		print("limited to %lld\n", (Wideoff)mh-ml);
1974
	}
1975
 
1976
	count = 0;
1977
	for(m=ml; m < mh; m++) {
1978
		p = getbuf(dev, m, Brd);
1979
		if(p)
1980
			putbuf(p);
1981
		count++;
1982
	}
1983
	print("%lld blocks loaded from worm %d\n", (Wideoff)count, dskno);
1984
}
1985
 
1986
void
1987
blockcmp(Device *dev, Off wa, Off ca)
1988
{
1989
	Iobuf *p1, *p2;
1990
	int i, c;
1991
 
1992
	p1 = getbuf(WDEV(dev), wa, Brd);
1993
	if(!p1) {
1994
		print("blockcmp: wdev error\n");
1995
		return;
1996
	}
1997
 
1998
	p2 = getbuf(CDEV(dev), ca, Brd);
1999
	if(!p2) {
2000
		print("blockcmp: cdev error\n");
2001
		putbuf(p1);
2002
		return;
2003
	}
2004
 
2005
	c = 0;
2006
	for(i=0; i<RBUFSIZE; i++)
2007
		if(p1->iobuf[i] != p2->iobuf[i]) {
2008
			print("%4d: %.2x %.2x\n",
2009
				i,
2010
				p1->iobuf[i]&0xff,
2011
				p2->iobuf[i]&0xff);
2012
			c++;
2013
			if(c >= 10)
2014
				break;
2015
		}
2016
 
2017
	if(c == 0)
2018
		print("no error\n");
2019
	putbuf(p1);
2020
	putbuf(p2);
2021
}
2022
 
2023
void
2024
wblock(Device *dev, Off addr)
2025
{
2026
	Iobuf *p1;
2027
	int i;
2028
 
2029
	p1 = getbuf(dev, addr, Brd);
2030
	if(p1) {
2031
		i = devwrite(WDEV(dev), addr, p1->iobuf);
2032
		print("i = %d\n", i);
2033
		putbuf(p1);
2034
	}
2035
}
2036
 
2037
void
2038
cwtest(Device*)
2039
{
2040
}
2041
 
2042
#ifdef	XXX
2043
/* garbage to change sb size
2044
 * probably will need it someday
2045
 */
2046
	fsz = number(0, 0, 10);
2047
	count = 0;
2048
	if(fsz == number(0, -1, 10))
2049
		count = -1;		/* really do it */
2050
	print("fsize = %ld\n", fsz);
2051
	cdev = CDEV(dev);
2052
	cb = getbuf(cdev, CACHE_ADDR, Brd|Bres);
2053
	if(!cb || checktag(cb, Tcache, QPSUPER))
2054
		panic("cwstats: checktag c bucket");
2055
	h = (Cache*)cb->iobuf;
2056
	for(m=0; m<h->msize; m++) {
2057
		p = getbuf(cdev, h->maddr + m/BKPERBLK, Brd|Bmod);
2058
		if(!p || checktag(p, Tbuck, h->maddr + m/BKPERBLK))
2059
			panic("cwtest: checktag c bucket");
2060
		b = (Bucket*)p->iobuf + m%BKPERBLK;
2061
		ce = b->entry + CEPERBK;
2062
		for(c=b->entry; c<ce; c++) {
2063
			if(c->waddr < fsz)
2064
				continue;
2065
			if(count < 0) {
2066
				c->state = Cnone;
2067
				continue;
2068
			}
2069
			if(c->state != Cdirty)
2070
				count++;
2071
		}
2072
		putbuf(p);
2073
	}
2074
	if(count < 0) {
2075
		print("old cache hsize = %ld\n", h->fsize);
2076
		h->fsize = fsz;
2077
		cb->flags |= Bmod;
2078
		p = getbuf(dev, h->sbaddr, Brd|Bmod);
2079
		s = (Superb*)p->iobuf;
2080
		print("old super hsize = %ld\n", s->fsize);
2081
		s->fsize = fsz;
2082
		putbuf(p);
2083
	}
2084
	putbuf(cb);
2085
	print("count = %lld\n", (Wideoff)count);
2086
#endif
2087
 
2088
int
2089
convstate(char *name)
2090
{
2091
	int i;
2092
 
2093
	for(i=0; i<nelem(cwnames); i++)
2094
		if(cwnames[i])
2095
			if(strcmp(cwnames[i], name) == 0)
2096
				return i;
2097
	return -1;
2098
}
2099
 
2100
void
2101
searchtag(Device *d, Off a, int tag, int n)
2102
{
2103
	Iobuf *p;
2104
	Tag *t;
2105
	int i;
2106
 
2107
	if(a == 0)
2108
		a = getstartsb(d);
2109
	p = getbuf(devnone, Cwxx2, 0);
2110
	t = (Tag*)(p->iobuf+BUFSIZE);
2111
	for(i=0; i<n; i++) {
2112
		memset(p->iobuf, 0, RBUFSIZE);
2113
		if(devread(WDEV(d), a+i, p->iobuf)) {
2114
			if(n == 1000)
2115
				break;
2116
			continue;
2117
		}
2118
		if(t->tag == tag) {
2119
			print("tag %d found at %Z %lld\n", tag, d, (Wideoff)a+i);
2120
			break;
2121
		}
2122
	}
2123
	putbuf(p);
2124
}
2125
 
2126
void
2127
cmd_cwcmd(int argc, char *argv[])
2128
{
2129
	Device *dev;
2130
	char *arg;
2131
	char str[28];
2132
	Off s1, s2, a, b, n;
2133
	Cw *cw;
2134
 
2135
	if(argc <= 1) {
2136
		print("\tcwcmd mvstate state1 state2 [platter]\n");
2137
		print("\tcwcmd prchain [start] [bakflg]\n");
2138
		print("\tcwcmd searchtag [start] [tag] [blocks]\n");
2139
		print("\tcwcmd touchsb\n");
2140
		print("\tcwcmd savecache\n");
2141
		print("\tcwcmd loadcache [dskno]\n");
2142
		print("\tcwcmd morecache dskno [count]\n");
2143
		print("\tcwcmd blockcmp wbno cbno\n");
2144
		print("\tcwcmd startdump [01]\n");
2145
		print("\tcwcmd acct\n");
2146
		print("\tcwcmd clearacct\n");
2147
		return;
2148
	}
2149
	arg = argv[1];
2150
 
2151
	/*
2152
	 * items not depend on a cw filesystem
2153
	 */
2154
	if(strcmp(arg, "acct") == 0) {
2155
		for(a=0; a<nelem(growacct); a++) {
2156
			b = growacct[a];
2157
			if(b) {
2158
				uidtostr(str, a, 1);
2159
				print("%10lld %s\n",
2160
					((Wideoff)b*ADDFREE*RBUFSIZE+500000)/1000000,
2161
					str);
2162
			}
2163
		}
2164
		return;
2165
	}
2166
	if(strcmp(arg, "clearacct") == 0) {
2167
		memset(growacct, 0, sizeof(growacct));
2168
		return;
2169
	}
2170
 
2171
	/*
2172
	 * items depend on cw filesystem
2173
	 */
2174
	dev = cons.curfs->dev;
2175
	if(dev == 0 || dev->type != Devcw || dev->private == 0) {
2176
		print("cfs not a cw filesystem: %Z\n", dev);
2177
		return;
2178
	}
2179
	cw = dev->private;
2180
	if(strcmp(arg, "searchtag") == 0) {
2181
		a = 0;
2182
		if(argc > 2)
2183
			a = number(argv[2], 0, 10);
2184
		b = Tsuper;
2185
		if(argc > 3)
2186
			b = number(argv[3], 0, 10);
2187
		n = 1000;
2188
		if(argc > 4)
2189
			n = number(argv[4], 0, 10);
2190
		searchtag(dev, a, b, n);
2191
	} else if(strcmp(arg, "mvstate") == 0) {
2192
		if(argc < 4)
2193
			goto bad;
2194
		s1 = convstate(argv[2]);
2195
		s2 = convstate(argv[3]);
2196
		if(s1 < 0 || s2 < 0)
2197
			goto bad;
2198
		a = -1;
2199
		if(argc > 4)
2200
			a = number(argv[4], 0, 10);
2201
		mvstates(dev, s1, s2, a);
2202
		return;
2203
	bad:
2204
		print("cwcmd mvstate: bad args\n");
2205
	} else if(strcmp(arg, "prchain") == 0) {
2206
		a = 0;
2207
		if(argc > 2)
2208
			a = number(argv[2], 0, 10);
2209
		s1 = 0;
2210
		if(argc > 3)
2211
			s1 = number(argv[3], 0, 10);
2212
		prchain(dev, a, s1);
2213
	} else if(strcmp(arg, "touchsb") == 0)
2214
		touchsb(dev);
2215
	else if(strcmp(arg, "savecache") == 0)
2216
		savecache(dev);
2217
	else if(strcmp(arg, "loadcache") == 0) {
2218
		s1 = -1;
2219
		if(argc > 2)
2220
			s1 = number(argv[2], 0, 10);
2221
		loadcache(dev, s1);
2222
	} else if(strcmp(arg, "morecache") == 0) {
2223
		if(argc <= 2) {
2224
			print("arg count\n");
2225
			return;
2226
		}
2227
		s1 = number(argv[2], 0, 10);
2228
		if(argc > 3)
2229
			s2 = number(argv[3], 0, 10);
2230
		else
2231
			s2 = wormsizeside(dev, s1); /* default to 1 disc side */
2232
		morecache(dev, s1, s2);
2233
	} else if(strcmp(arg, "blockcmp") == 0) {
2234
		if(argc < 4) {
2235
			print("cannot arg count\n");
2236
			return;
2237
		}
2238
		s1 = number(argv[2], 0, 10);
2239
		s2 = number(argv[3], 0, 10);
2240
		blockcmp(dev, s1, s2);
2241
	} else if(strcmp(arg, "startdump") == 0) {
2242
		if(argc > 2)
2243
			cw->nodump = number(argv[2], 0, 10);
2244
		cw->nodump = !cw->nodump;
2245
		if(cw->nodump)
2246
			print("dump stopped\n");
2247
		else
2248
			print("dump allowed\n");
2249
	} else if(strcmp(arg, "allflag") == 0) {
2250
		if(argc > 2)
2251
			cw->allflag = number(argv[2], 0, 10);
2252
		else
2253
			cw->allflag = !cw->allflag;
2254
		print("allflag = %d; falsehits = %lld\n",
2255
			cw->allflag, (Wideoff)cw->falsehits);
2256
	} else if(strcmp(arg, "storesb") == 0) {
2257
		a = 4168344;
2258
		b = 0;
2259
		if(argc > 2)
2260
			a = number(argv[2], 4168344, 10);
2261
		if(argc > 3)
2262
			b = number(argv[3], 0, 10);
2263
		storesb(dev, a, b);
2264
	} else if(strcmp(arg, "test") == 0)
2265
		cwtest(dev);
2266
	else
2267
		print("unknown cwcmd %s\n", arg);
2268
}