1335 lines · 28.5 KB · C
RawDownload
/*  * This file is part of the UCB release of Plan 9. It is subject to the license * terms in the LICENSE file found in the top-level directory of this * distribution and at http://akaros.cs.berkeley.edu/files/Plan9License. No * part of the UCB release of Plan 9, including this file, may be copied, * modified, propagated, or distributed except according to the terms contained * in the LICENSE file. */ /* * drive HP optical-disc jukeboxes (e.g. HP 1200EX). * used to issue SCSI commands directly to the host adapter; * now (via scsi.c) it issues them via scsi(2) using * /dev/sdXX/raw to run the robotics, and uses normal i/o * on /dev/sdXX/data to run the drives. */#include "all.h"#include "io.h" enum {	SCSInone	= SCSIread,	MAXDRIVE	= 10,	MAXSIDE		= 500,		/* max. disc sides */ 	TWORM		= MINUTE(10),	THYSTER		= SECOND(10), 	Sectorsz	= 512,		/* usual disk sector size */ 	Jukemagic	= 0xbabfece2,}; typedef	struct	Side	Side;struct	Side{	QLock;			/* protects loading/unloading */	int	elem;		/* element number */	int	drive;		/* if loaded, where */	uchar	status;		/* Sunload, etc */	uchar	rot;		/* if backside */	int	ord;		/* ordinal number for labeling */ 	Timet	time;		/* time since last access, to unspin */	Timet	stime;		/* time since last spinup, for hysteresis */	long	nblock;		/* number of native blocks */	long	block;		/* bytes per native block */	long	mult;		/* multiplier to get plan9 blocks */	long	max;		/* max size in plan9 blocks */}; typedef	struct	Juke	Juke;struct	Juke{	QLock;				/* protects drive mechanism */	Side	side[MAXSIDE];	int	nside;			/* # of storage elements (*2 if rev) */	int	ndrive;			/* # of transfer elements */	Device*	juke;			/* devworm of changer */	Device*	drive[MAXDRIVE];	/* devworm for i/o */	uchar	offline[MAXDRIVE];	/* drives removed from service */	int	isfixedsize;		/* flag: one size fits all? */	long	fixedsize;		/* the one size that fits all */	int	probeok;		/* wait for init to probe */ 	Scsi*	robot;			/* scsi(2) interface to robotics */	char*	robotdir;		/* /dev/sdXX name */ 	/*	 * geometry returned by mode sense.	 * a *0 number (such as mt0) is the `element number' of the	 * first element of that type (e.g., mt, or motor transport).	 * an n* number is the quantity of them.	 */	int	mt0,	nmt;	/* motor transports (robot pickers) */	int	se0,	nse;	/* storage elements (discs, slots) */	int	ie0,	nie;	/* interchange elements (mailbox slots) */	int	dt0,	ndt;	/* drives (data transfer?) */	int	rot;		/* if true, discs are double-sided */ 	ulong	magic;	Juke*	link;};static	Juke*	jukelist; enum{	Sempty = 0,	/* does not exist */	Sunload,	/* on the shelf */	Sstart,		/* loaded and spinning */}; static	int	bestdrive(Juke*, int);static	void	element(Juke*, int);static	int	mmove(Juke*, int, int, int, int);static	void	shelves(void);static	int	waitready(Juke *, Device*);static	int	wormsense(Device*);static	Side*	wormunit(Device*); /* create a new label and try to write it */static voidnewlabel(Device *d, Off labelblk, char *labelbuf, unsigned vord){	Label *label = (Label *)labelbuf; 	memset(labelbuf, 0, RBUFSIZE);	label->magic = Labmagic;	label->ord = vord;	strncpy(label->service, service, sizeof label->service); 	if (!okay("write new label"))		print("NOT writing new label\n");	else if (wormwrite(d, labelblk, labelbuf))		/* wormwrite will have complained in detail */		print("can't write new label on side %d\n", vord);	else		print("wrote new label on side %d\n", vord);} /* check for label in last block.  call with v qlocked. */Side*wormlabel(Device *d, Side *v){	int vord;	Off labelblk = v->max - 1;	/* last block */	char labelbuf[RBUFSIZE];	Label *label = (Label *)labelbuf;	Juke *w = d->private; 	/* wormread calls wormunit, which locks v */	vord = v->ord;	qunlock(v); 	memset(label, 0, sizeof *label);	if (wormread(d, labelblk, labelbuf)) {		/*		 * wormread will have complained in detail about the error;		 * no need to repeat most of that detail.		 * probably an unwritten WORM-disc label; write a new one.		 */		print("error reading label block of side %d\n", vord);		newlabel(d, labelblk, labelbuf, vord);	} else if (label->magic != Labmagic) {		swab8(&label->magic);		if (label->magic == Labmagic) {			print("side %d's label magic byte-swapped; filsys should be configured with xD",				vord);			swab2(&label->ord);			/* could look for Devswab in Juke's filsys */		} else {			/*			 * magic # is wrong in both byte orders, thus			 * probably the label is empty on RW media,			 * so create a new one and try to write it.			 */			print("bad magic number in label of side %d\n", vord);			newlabel(d, labelblk, labelbuf, vord);		}	} 	qlock(v);	if (v->ord != vord)		panic("wormlabel: side %d switched ordinal to %d underfoot",			vord, v->ord);	if (label->ord != vord) {		print(	"labelled worm side %Z has wrong ordinal in label (%d, want %d)",			d, label->ord, vord);		qunlock(v);		cmd_wormreset(0, nil);			/* put discs away */		panic("wrong ordinal in label");	} 	print("label %Z ordinal %d\n", d, v->ord);	qunlock(v);	/*	 * wormunit should return without calling us again,	 * since v is now known.	 */	if (w != d->private)		panic("wormlabel: w != %Z->private", d);	return wormunit(d);} /* * mounts and spins up the device *	locks the structure */static Side*wormunit(Device *d)			/* d is l0 or r2 (e.g.) */{	int p, drive;	Device *dr;			/* w0 or w1.2.0 (e.g.) */	Side *v;	Juke *w;	Dir *dir; 	w = d->private;	if (w == nil)		panic("wormunit %Z nil juke", d);	if (w->magic != Jukemagic)		panic("bad magic in Juke for %Z", d);	p = d->wren.targ;	if(p < 0 || w && p >= w->nside) {		panic("wormunit: target %d out of range for %Z", p, d);		return 0;	} 	/*	 * if disk is unloaded, must load it	 * into next (circular) logical unit	 */	v = &w->side[p];	qlock(v);	if(v->status == Sunload) {		for(;;) {			qlock(w);			drive = bestdrive(w, p);			if(drive >= 0)				break;			qunlock(w);			delay(100);		}		print("\tload   r%ld drive %Z\n", v-w->side, w->drive[drive]);		if(mmove(w, w->mt0, v->elem, w->dt0+drive, v->rot)) {			qunlock(w);			goto sbad;		}		v->drive = drive;		v->status = Sstart;		v->stime = toytime();		qunlock(w);		dr = w->drive[drive];		if (!waitready(w, dr))			goto sbad;		v->stime = toytime();	} else		dr = w->drive[v->drive];	if(v->status != Sstart) {		if(v->status == Sempty)			print("worm: unit empty %Z\n", d);		else			print("worm: not started %Z\n", d);		goto sbad;	} 	v->time = toytime();	if(v->block)		/* side is known already */		return v; 	/*	 * load and record information about side	 */ 	if (dr->wren.file)		dr->wren.sddata = dataof(dr->wren.file);	else {		if (dr->wren.sddir == nil) {			if (dr->type == Devwren)				dr->wren.sddir = sdof(dr);			if (dr->wren.sddir == nil)				panic("wormunit: %Z for %Z not a wren", dr, d);		}		dr->wren.sddata = smprint("%s/data", dr->wren.sddir);	} 	if (dr->wren.fd == 0)		dr->wren.fd = open(dr->wren.sddata, ORDWR);	if (dr->wren.fd < 0) {		print("wormunit: can't open %s for %Z: %r\n", dr->wren.sddata, d);		goto sbad;	} 	v->block = inqsize(dr->wren.sddata);	if(v->block <= 0) {		print("\twormunit %Z block size %ld, setting to %d\n",			d, v->block, Sectorsz);		v->block = Sectorsz;	} 	dir = dirfstat(dr->wren.fd);	v->nblock = dir->length / v->block;	free(dir); 	v->mult = (RBUFSIZE + v->block - 1) / v->block;	v->max = (v->nblock + 1) / v->mult; 	print("\tworm %Z: drive %Z (juke drive %d)\n",		d, w->drive[v->drive], v->drive);	print("\t\t%,ld %ld-byte sectors, ", v->nblock, v->block);	print("%,ld %d-byte blocks\n", v->max, RBUFSIZE);	print("\t\t%ld multiplier\n", v->mult);	if(d->type == Devlworm)		return wormlabel(d, v);	else		return v; sbad:	qunlock(v);	return 0;} /* wait 10s for optical drive to spin up */static intwaitready(Juke *w, Device *d){	int p, e, rv;	char *datanm; 	if (w->magic != Jukemagic)		panic("waitready: bad magic in Juke (d->private) for %Z", d);	p = d->wren.targ;	if(p < 0 || p >= w->nside) {		print("waitready: target %d out of range for %Z\n", p, d);		return 0;	} 	if (d->type == Devwren && d->wren.file)		datanm = strdup(d->wren.file);	else {		if (d->wren.sddir)			free(d->wren.sddir);		if (d->type == Devwren)			d->wren.sddir = sdof(d);		if (d->wren.sddir == nil)			panic("waitready: d->wren.sddir not set for %Z", d); 		datanm = smprint("%s/data", d->wren.sddir);	} 	rv = 0;	for(e=0; e < 100; e++) {		if (e == 10)			print("waitready: waiting for %s to exist\n", datanm); // DEBUG		if (access(datanm, AEXIST) >= 0) {			rv = 1;			break;		}		delay(200);	}	if (rv == 0)		print("waitready: %s for %Z didn't come ready\n", datanm, d);	free(datanm);	return rv;} static intbestdrive(Juke *w, int side){	Side *v, *bv[MAXDRIVE];	int i, e, drive;	Timet t, t0; loop:	/* build table of what platters on what drives */	for(i=0; i<w->ndt; i++)		bv[i] = 0; 	v = &w->side[0];	for(i=0; i < w->nside; i++, v++)		if(v->status == Sstart) {			drive = v->drive;			if(drive >= 0 && drive < w->ndt)				bv[drive] = v;		} 	/*	 * find oldest drive, but must be	 * at least THYSTER old.	 */	e = w->side[side].elem;	t0 = toytime() - THYSTER;	t = t0;	drive = -1;	for(i=0; i<w->ndt; i++) {		v = bv[i];		if(v == 0) {		/* 2nd priority: empty drive */			if(w->offline[i])				continue;			if(w->drive[i] != devnone) {				drive = i;				t = 0;			}			continue;		}		if(v->elem == e) {	/* 1st priority: other side */			drive = -1;			if(v->stime < t0)				drive = i;			break;		}		if(v->stime < t) {	/* 3rd priority: by time */			drive = i;			t = v->stime;		}	} 	if(drive >= 0) {		v = bv[drive];		if(v) {			qlock(v);			if(v->status != Sstart) {				qunlock(v);				goto loop;			}			print("\tunload r%ld drive %Z\n",				v-w->side, w->drive[drive]);			if(mmove(w, w->mt0, w->dt0+drive, v->elem, v->rot)) {				qunlock(v);				goto loop;			}			v->status = Sunload;			qunlock(v);		}	}	return drive;} Devsizewormsize(Device *d){	Side *v;	Juke *w;	Devsize size; 	w = d->private;	if (w->magic != Jukemagic)		print("wormsize: bad magic in Juke (d->private) for %Z\n", d);	if(w->isfixedsize && w->fixedsize != 0)		size = w->fixedsize;	/* fixed size is now known */	else {		if (w != d->private)			panic("wormsize: w != %Z->private", d);		v = wormunit(d);		if(v == nil)			return 0;		size = v->max;		qunlock(v);		/*		 * set fixed size for whole Juke from		 * size of first disc examined.		 */		if(w->isfixedsize)			w->fixedsize = size;	}	if(d->type == Devlworm)		return size-1;		/* lie: last block is for label */	return size;} /* * return a Devjuke or an mcat (normally of sides) from within d (or nil). * if it's an mcat, the caller must walk it. */static Device *devtojuke(Device *d, Device *top){	while (d != nil)		switch(d->type) {		default:			print("devtojuke: type of device %Z of %Z unknown\n",				d, top);			return nil; 		case Devjuke:			/* jackpot!  d->private is a (Juke *) with nside, &c. */			/* FALL THROUGH */		case Devmcat:		case Devmlev:		case Devmirr:			/* squint hard & call an mlev or a mirr an mcat */			return d; 		case Devworm:		case Devlworm:			/*			 * d->private is a (Juke *) with nside, etc.,			 * but we're not supposed to get here.			 */			print("devtojuke: (l)worm %Z of %Z encountered\n",				d, top);			/* FALL THROUGH */		case Devwren:			return nil; 		case Devcw:			d = d->cw.w;			/* usually juke */			break;		case Devro:			d = d->ro.parent;		/* cw */			break;		case Devfworm:			d = d->fw.fw;			break;		case Devpart:			d = d->part.d;			break;		case Devswab:			d = d->swab.d;

Lines 1–500 of 1335.