xref: /plan9/sys/src/cmd/cwfs/cw.c (revision e6d20ddfa7d12e75efbb79fca45df9e14315b5f6)
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
cmd_dump(int argc,char * argv[])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
cmd_statw(int,char * [])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
dumpblock(Device * dev)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
cwinit1(Device * dev)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
cwinit(Device * dev)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
cwsaddr(Device * dev)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
cwraddr(Device * dev)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
cwsize(Device * dev)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
cwread(Device * dev,Off b,void * c)497 cwread(Device *dev, Off b, void *c)
498 {
499 	return cwio(dev, b, c, Oread) == Cerror;
500 }
501 
502 int
cwwrite(Device * dev,Off b,void * c)503 cwwrite(Device *dev, Off b, void *c)
504 {
505 	return cwio(dev, b, c, Owrite) == Cerror;
506 }
507 
508 int
roread(Device * dev,Off b,void * c)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
cwio(Device * dev,Off addr,void * buf,int opcode)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
cwgrow(Device * dev,Superb * sb,int uid)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
cwfree(Device * dev,Off addr)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
bktcheck(Bucket * b)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
resequence(Bucket * b)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*
getcentry(Bucket * b,Off addr)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*
cacheinit(Device * dev)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
getstartsb(Device * dev)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
cwrecover(Device * dev)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
cwream(Device * dev)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
rewalk1(Cw * cw,Off addr,int slot,Wpath * up)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
rewalk2(Cw * cw,Off addr,int slot,Wpath * up)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
rewalk(Cw * cw)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
split(Cw * cw,Iobuf * p,Off addr)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
isdirty(Cw * cw,Iobuf * p,Off addr,int tag)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
cwrecur(Cw * cw,Off addr,int tag,int tag1,long qp)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
cfsdump(Filsys * fs)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
mvstates(Device * dev,int s1,int s2,int side)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
prchain(Device * dev,Off m,int flg)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
touchsb(Device * dev)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
storesb(Device * dev,Off last,int doit)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
savecache(Device * dev)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
loadcache(Device * dev,int dskno)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
morecache(Device * dev,int dskno,Off size)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
blockcmp(Device * dev,Off wa,Off ca)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
wblock(Device * dev,Off addr)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
cwtest(Device *)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
convstate(char * name)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
searchtag(Device * d,Off a,int tag,int n)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
cmd_cwcmd(int argc,char * argv[])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 }
2269