/* * 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. Load more
/* * 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 void newlabel(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 int waitready(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 int bestdrive(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; } Devsize wormsize(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;