📄 juke.c
字号:
#include "all.h"#define SCSInone SCSIread#define MAXDRIVE 10#define MAXSIDE 500#define TWORM MINUTE(10)#define THYSTER SECOND(10)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; /* how many storage elements (*2 if rev) */ int ndrive; /* number of transfer elements */ Device* juke; /* devworm of changer */ Device* drive[MAXDRIVE]; /* devworm for i/o */ uchar offline[MAXDRIVE]; /* drives removed from service */ long fixedsize; /* one size fits all */ int probeok; /* wait for init to probe */ /* * 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 */ Juke* link;};static Juke* jukelist;enum{ Sempty = 0, /* does not exist */ Sunload, /* on the shelf */ Sstart, /* loaded and spinning */};extern int FIXEDSIZE;static int wormsense(Device*);static Side* wormunit(Device*);static void shelves(void);static int mmove(Juke*, int, int, int, int);static int bestdrive(Juke*, int);static void waitready(Device*);static void element(Juke*, int);/* * mounts and spins up the device * locks the structure */staticSide*wormunit(Device *d){ int p, s, drive; Side *v; Juke *w; uchar cmd[10], buf[8]; w = d->private; p = d->wren.targ; if(p < 0 || p >= w->nside) {// panic("wormunit partition %Z\n", 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); waitsec(100); } print(" load 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); waitready(w->drive[drive]); v->stime = toytime(); } 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) return v; /* * capacity command */ memset(cmd, 0, sizeof(cmd)); memset(buf, 0, sizeof(buf)); cmd[0] = 0x25; /* read capacity */ s = scsiio(w->drive[v->drive], SCSIread, cmd, sizeof(cmd), buf, sizeof(buf)); if(s) goto sbad; v->nblock = (buf[0]<<24) | (buf[1]<<16) | (buf[2]<<8) | (buf[3]<<0); v->block = (buf[4]<<24) | (buf[5]<<16) | (buf[6]<<8) | (buf[7]<<0); v->mult = (RBUFSIZE + v->block - 1) / v->block; v->max = (v->nblock + 1) / v->mult; print(" worm %Z: drive %Z\n", d, w->drive[v->drive]); print(" %ld blocks at %ld bytes each\n", v->nblock, v->block); print(" %ld logical blocks at %d bytes each\n", v->max, RBUFSIZE); print(" %ld multiplier\n", v->mult); if(d->type != Devlworm) return v; /* check for label */ print("label %Z ordinal %d\n", d, v->ord); qunlock(v); return wormunit(d);sbad: qunlock(v);// panic("wormunit sbad"); return 0;}staticvoidwaitready(Device *d){ uchar cmd[6]; int s, e; for(e=0;e<100;e++) { memset(cmd, 0, sizeof(cmd)); s = scsiio(d, SCSInone, cmd, sizeof(cmd), cmd, 0); if(s == 0) break; waitsec(100); }}staticintbestdrive(Juke *w, int side){ Side *v, *bv[MAXDRIVE]; int i, s, 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++) { s = v->status; if(s == 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(" unload 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->fixedsize) size = w->fixedsize; else { v = wormunit(d); if(v == 0) return 0; size = v->max; qunlock(v); if(FIXEDSIZE) // TODO? push FIXEDSIZE into Device or Juke struct w->fixedsize = size; } if(d->type == Devlworm) return size-1; 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: case Devide: 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; break; } return d;}static intdevisside(Device *d){ return d->type == Devworm || d->type == Devlworm;}static Device *findside(Device *juke, int side, Device *top){ int i = 0; Device *mcat = juke->j.m, *x; Juke *w = juke->private; for (x = mcat->cat.first; x != nil; x = x->link) { if (!devisside(x)) { print("wormsizeside: %Z of %Z of %Z type not (l)worm\n", x, mcat, top); return nil; } i = x->wren.targ; if (i < 0 || i >= w->nside) panic("wormsizeside: side %d in %Z out of range", i, mcat); if (i == side) break; } if (x == nil) return nil; if (w->side[i].time == 0) { print("wormsizeside: side %d not in jukebox %Z\n", i, juke); return nil; } return x;}typedef struct { int sleft; /* sides still to visit to reach desired side */ int starget; /* side of topdev we want */ Device *topdev; int sawjuke; /* passed by a jukebox */ int sized; /* flag: asked wormsize for size of starget */} Visit;/* * walk the Device tree from d looking for Devjukes, counting sides. * the main complication is mcats and the like with Devjukes in them. * use Devjuke's d->private as Juke* and see sides. */static Offvisitsides(Device *d, Device *parentj, Visit *vp){ Off size = 0; Device *x; Juke *w; /* * find the first juke or mcat. * d==nil means we couldn't find one; typically harmless, due to a * mirror of dissimilar devices. */ d = devtojuke(d, vp->topdev); if (d == nil || vp->sleft < 0) return 0; if (d->type == Devjuke) { /* jackpot! d->private is a (Juke *) */ vp->sawjuke = 1; w = d->private; /* * if there aren't enough sides in this jukebox to reach * the desired one, subtract these sides and pass. */ if (vp->sleft >= w->nside) { vp->sleft -= w->nside; return 0; } /* else this is the right juke, paw through mcat of sides */ return visitsides(d->j.m, d, vp); } /* * d will usually be an mcat of sides, but it could be an mcat of * jukes, for example. in that case, we need to walk the mcat, * recursing as needed, until we find the right juke, then stop at * the right side within its mcat of sides, by comparing side * numbers, not just by counting (to allow for unused slots). */ x = d->cat.first; if (x == nil) { print("visitsides: %Z of %Z: empty mcat\n", d, vp->topdev); return 0; } if (!devisside(x)) { for (; x != nil && !vp->sized; x = x->link) size = visitsides(x, parentj, vp); return size; } /* the side we want is in this jukebox, thus this mcat (d) */ if (parentj == nil) { print("visitsides: no parent juke for sides mcat %Z\n", d); vp->sleft = -1; return 0; } if (d != parentj->j.m) panic("visitsides: mcat mismatch %Z vs %Z", d, parentj->j.m); x = findside(parentj, vp->sleft, vp->topdev); if (x == nil) { vp->sleft = -1; return 0; } /* we've turned vp->starget into the right Device* */ vp->sleft = 0; vp->sized = 1; return wormsize(x);}/* * d must be, or be within, a filesystem config that also contains * the jukebox that `side' resides on. * d is normally a Devcw, but could be Devwren, Devide, Devpart, Devfworm, * etc. if called from chk.c Ctouch code. Note too that the worm part of * the Devcw might be other than a Devjuke. */Devsizewormsizeside(Device *d, int side){ Devsize size; Visit visit; memset(&visit, 0, sizeof visit); visit.starget = visit.sleft = side; visit.topdev = d; size = visitsides(d, nil, &visit); if (visit.sawjuke && (visit.sleft != 0 || !visit.sized)) { print("wormsizeside: fewer than %d sides in %Z\n", side, d); return 0; } return size;}/* * returns starts (in blocks) of side #side and #(side+1) of dev in *stp. * dev should be a Devcw. */voidwormsidestarts(Device *dev, int side, Sidestarts *stp){ int s; Devsize dstart; for (dstart = s = 0; s < side; s++) dstart += wormsizeside(dev, s); stp->sstart = dstart; stp->s1start = dstart + wormsizeside(dev, side);}staticintwormiocmd(Device *d, int io, Off b, void *c){ Side *v; Juke *w; Off l; int s; long m; uchar cmd[10]; w = d->private; v = wormunit(d); if(v == 0) return 0x71; if(b >= v->max) { qunlock(v); print("worm: wormiocmd out of range %Z(%lld)\n", d, (Wideoff)b); return 0x071; } memset(cmd, 0, sizeof(cmd)); cmd[0] = 0x28; /* extended read */ if(io != SCSIread) cmd[0] = 0x2a; /* extended write */ m = v->mult; l = b * m; cmd[2] = l>>24; cmd[3] = l>>16; cmd[4] = l>>8; cmd[5] = l; cmd[7] = m>>8; cmd[8] = m; s = scsiio(w->drive[v->drive], io, cmd, sizeof(cmd), c, RBUFSIZE); qunlock(v); return s;}intwormread(Device *d, Off b, void *c){ int s; s = wormiocmd(d, SCSIread, b, c); if(s) { print("wormread: %Z(%lld) bad status #%x\n", d, (Wideoff)b, s); cons.nwormre++; return s; } return 0;}intwormwrite(Device *d, Off b, void *c){ int s; s = wormiocmd(d, SCSIwrite, b, c); if(s) { print("wormwrite: %Z(%lld) bad status #%x\n", d, (Wideoff)b, s); cons.nwormwe++; return s; } return 0;}staticintmmove(Juke *w, int trans, int from, int to, int rot)
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -