1 /* 2 * drive HP optical-disc jukeboxes (e.g. HP 1200EX). 3 * used to issue SCSI commands directly to the host adapter; 4 * now (via scsi.c) it issues them via scsi(2) using 5 * /dev/sdXX/raw to run the robotics, and uses normal i/o 6 * on /dev/sdXX/data to run the drives. 7 */ 8 #include "all.h" 9 #include "io.h" 10 11 enum { 12 SCSInone = SCSIread, 13 MAXDRIVE = 10, 14 MAXSIDE = 500, /* max. disc sides */ 15 16 TWORM = MINUTE(10), 17 THYSTER = SECOND(10), 18 19 Sectorsz = 512, /* usual disk sector size */ 20 21 Jukemagic = 0xbabfece2, 22 }; 23 24 typedef struct Side Side; 25 struct Side 26 { 27 QLock; /* protects loading/unloading */ 28 int elem; /* element number */ 29 int drive; /* if loaded, where */ 30 uchar status; /* Sunload, etc */ 31 uchar rot; /* if backside */ 32 int ord; /* ordinal number for labeling */ 33 34 Timet time; /* time since last access, to unspin */ 35 Timet stime; /* time since last spinup, for hysteresis */ 36 long nblock; /* number of native blocks */ 37 long block; /* bytes per native block */ 38 long mult; /* multiplier to get plan9 blocks */ 39 long max; /* max size in plan9 blocks */ 40 }; 41 42 typedef struct Juke Juke; 43 struct Juke 44 { 45 QLock; /* protects drive mechanism */ 46 Side side[MAXSIDE]; 47 int nside; /* # of storage elements (*2 if rev) */ 48 int ndrive; /* # of transfer elements */ 49 Device* juke; /* devworm of changer */ 50 Device* drive[MAXDRIVE]; /* devworm for i/o */ 51 uchar offline[MAXDRIVE]; /* drives removed from service */ 52 int isfixedsize; /* flag: one size fits all? */ 53 long fixedsize; /* the one size that fits all */ 54 int probeok; /* wait for init to probe */ 55 56 Scsi* robot; /* scsi(2) interface to robotics */ 57 char* robotdir; /* /dev/sdXX name */ 58 59 /* 60 * geometry returned by mode sense. 61 * a *0 number (such as mt0) is the `element number' of the 62 * first element of that type (e.g., mt, or motor transport). 63 * an n* number is the quantity of them. 64 */ 65 int mt0, nmt; /* motor transports (robot pickers) */ 66 int se0, nse; /* storage elements (discs, slots) */ 67 int ie0, nie; /* interchange elements (mailbox slots) */ 68 int dt0, ndt; /* drives (data transfer?) */ 69 int rot; /* if true, discs are double-sided */ 70 71 ulong magic; 72 Juke* link; 73 }; 74 static Juke* jukelist; 75 76 enum 77 { 78 Sempty = 0, /* does not exist */ 79 Sunload, /* on the shelf */ 80 Sstart, /* loaded and spinning */ 81 }; 82 83 static int bestdrive(Juke*, int); 84 static void element(Juke*, int); 85 static int mmove(Juke*, int, int, int, int); 86 static void shelves(void); 87 static int waitready(Juke *, Device*); 88 static int wormsense(Device*); 89 static Side* wormunit(Device*); 90 91 /* create a new label and try to write it */ 92 static void 93 newlabel(Device *d, Off labelblk, char *labelbuf, unsigned vord) 94 { 95 Label *label = (Label *)labelbuf; 96 97 memset(labelbuf, 0, RBUFSIZE); 98 label->magic = Labmagic; 99 label->ord = vord; 100 strncpy(label->service, service, sizeof label->service); 101 102 if (!okay("write new label")) 103 print("NOT writing new label\n"); 104 else if (wormwrite(d, labelblk, labelbuf)) 105 /* wormwrite will have complained in detail */ 106 print("can't write new label on side %d\n", vord); 107 else 108 print("wrote new label on side %d\n", vord); 109 } 110 111 /* check for label in last block. call with v qlocked. */ 112 Side* 113 wormlabel(Device *d, Side *v) 114 { 115 int vord; 116 Off labelblk = v->max - 1; /* last block */ 117 char labelbuf[RBUFSIZE]; 118 Label *label = (Label *)labelbuf; 119 Juke *w = d->private; 120 121 /* wormread calls wormunit, which locks v */ 122 vord = v->ord; 123 qunlock(v); 124 125 memset(label, 0, sizeof *label); 126 if (wormread(d, labelblk, labelbuf)) { 127 /* 128 * wormread will have complained in detail about the error; 129 * no need to repeat most of that detail. 130 * probably an unwritten WORM-disc label; write a new one. 131 */ 132 print("error reading label block of side %d\n", vord); 133 newlabel(d, labelblk, labelbuf, vord); 134 } else if (label->magic != Labmagic) { 135 swab8(&label->magic); 136 if (label->magic == Labmagic) { 137 print( 138 "side %d's label magic byte-swapped; filsys should be configured with xD", 139 vord); 140 swab2(&label->ord); 141 /* could look for Devswab in Juke's filsys */ 142 } else { 143 /* 144 * magic # is wrong in both byte orders, thus 145 * probably the label is empty on RW media, 146 * so create a new one and try to write it. 147 */ 148 print("bad magic number in label of side %d\n", vord); 149 newlabel(d, labelblk, labelbuf, vord); 150 } 151 } 152 153 qlock(v); 154 if (v->ord != vord) 155 panic("wormlabel: side %d switched ordinal to %d underfoot", 156 vord, v->ord); 157 if (label->ord != vord) { 158 print( 159 "labelled worm side %Z has wrong ordinal in label (%d, want %d)", 160 d, label->ord, vord); 161 qunlock(v); 162 cmd_wormreset(0, nil); /* put discs away */ 163 panic("wrong ordinal in label"); 164 } 165 166 print("label %Z ordinal %d\n", d, v->ord); 167 qunlock(v); 168 /* 169 * wormunit should return without calling us again, 170 * since v is now known. 171 */ 172 if (w != d->private) 173 panic("wormlabel: w != %Z->private", d); 174 return wormunit(d); 175 } 176 177 /* 178 * mounts and spins up the device 179 * locks the structure 180 */ 181 static Side* 182 wormunit(Device *d) /* d is l0 or r2 (e.g.) */ 183 { 184 int p, drive; 185 Device *dr; /* w0 or w1.2.0 (e.g.) */ 186 Side *v; 187 Juke *w; 188 Dir *dir; 189 190 w = d->private; 191 if (w == nil) 192 panic("wormunit %Z nil juke", d); 193 if (w->magic != Jukemagic) 194 panic("bad magic in Juke for %Z", d); 195 p = d->wren.targ; 196 if(p < 0 || w && p >= w->nside) { 197 panic("wormunit: target %d out of range for %Z", p, d); 198 return 0; 199 } 200 201 /* 202 * if disk is unloaded, must load it 203 * into next (circular) logical unit 204 */ 205 v = &w->side[p]; 206 qlock(v); 207 if(v->status == Sunload) { 208 for(;;) { 209 qlock(w); 210 drive = bestdrive(w, p); 211 if(drive >= 0) 212 break; 213 qunlock(w); 214 delay(100); 215 } 216 print("\tload r%ld drive %Z\n", v-w->side, w->drive[drive]); 217 if(mmove(w, w->mt0, v->elem, w->dt0+drive, v->rot)) { 218 qunlock(w); 219 goto sbad; 220 } 221 v->drive = drive; 222 v->status = Sstart; 223 v->stime = toytime(); 224 qunlock(w); 225 dr = w->drive[drive]; 226 if (!waitready(w, dr)) 227 goto sbad; 228 v->stime = toytime(); 229 } else 230 dr = w->drive[v->drive]; 231 if(v->status != Sstart) { 232 if(v->status == Sempty) 233 print("worm: unit empty %Z\n", d); 234 else 235 print("worm: not started %Z\n", d); 236 goto sbad; 237 } 238 239 v->time = toytime(); 240 if(v->block) /* side is known already */ 241 return v; 242 243 /* 244 * load and record information about side 245 */ 246 247 if (dr->wren.file) 248 dr->wren.sddata = dataof(dr->wren.file); 249 else { 250 if (dr->wren.sddir == nil) { 251 if (dr->type == Devwren) 252 dr->wren.sddir = sdof(dr); 253 if (dr->wren.sddir == nil) 254 panic("wormunit: %Z for %Z not a wren", dr, d); 255 } 256 dr->wren.sddata = smprint("%s/data", dr->wren.sddir); 257 } 258 259 if (dr->wren.fd == 0) 260 dr->wren.fd = open(dr->wren.sddata, ORDWR); 261 if (dr->wren.fd < 0) { 262 print("wormunit: can't open %s for %Z: %r\n", dr->wren.sddata, d); 263 goto sbad; 264 } 265 266 v->block = inqsize(dr->wren.sddata); 267 if(v->block <= 0) { 268 print("\twormunit %Z block size %ld, setting to %d\n", 269 d, v->block, Sectorsz); 270 v->block = Sectorsz; 271 } 272 273 dir = dirfstat(dr->wren.fd); 274 v->nblock = dir->length / v->block; 275 free(dir); 276 277 v->mult = (RBUFSIZE + v->block - 1) / v->block; 278 v->max = (v->nblock + 1) / v->mult; 279 280 print("\tworm %Z: drive %Z (juke drive %d)\n", 281 d, w->drive[v->drive], v->drive); 282 print("\t\t%,ld %ld-byte sectors, ", v->nblock, v->block); 283 print("%,ld %d-byte blocks\n", v->max, RBUFSIZE); 284 print("\t\t%ld multiplier\n", v->mult); 285 if(d->type == Devlworm) 286 return wormlabel(d, v); 287 else 288 return v; 289 290 sbad: 291 qunlock(v); 292 return 0; 293 } 294 295 /* wait 10s for optical drive to spin up */ 296 static int 297 waitready(Juke *w, Device *d) 298 { 299 int p, e, rv; 300 char *datanm; 301 302 if (w->magic != Jukemagic) 303 panic("waitready: bad magic in Juke (d->private) for %Z", d); 304 p = d->wren.targ; 305 if(p < 0 || p >= w->nside) { 306 print("waitready: target %d out of range for %Z\n", p, d); 307 return 0; 308 } 309 310 if (d->type == Devwren && d->wren.file) 311 datanm = strdup(d->wren.file); 312 else { 313 if (d->wren.sddir) 314 free(d->wren.sddir); 315 if (d->type == Devwren) 316 d->wren.sddir = sdof(d); 317 if (d->wren.sddir == nil) 318 panic("waitready: d->wren.sddir not set for %Z", d); 319 320 datanm = smprint("%s/data", d->wren.sddir); 321 } 322 323 rv = 0; 324 for(e=0; e < 100; e++) { 325 if (e == 10) 326 print("waitready: waiting for %s to exist\n", datanm); // DEBUG 327 if (access(datanm, AEXIST) >= 0) { 328 rv = 1; 329 break; 330 } 331 delay(200); 332 } 333 if (rv == 0) 334 print("waitready: %s for %Z didn't come ready\n", datanm, d); 335 free(datanm); 336 return rv; 337 } 338 339 static int 340 bestdrive(Juke *w, int side) 341 { 342 Side *v, *bv[MAXDRIVE]; 343 int i, e, drive; 344 Timet t, t0; 345 346 loop: 347 /* build table of what platters on what drives */ 348 for(i=0; i<w->ndt; i++) 349 bv[i] = 0; 350 351 v = &w->side[0]; 352 for(i=0; i < w->nside; i++, v++) 353 if(v->status == Sstart) { 354 drive = v->drive; 355 if(drive >= 0 && drive < w->ndt) 356 bv[drive] = v; 357 } 358 359 /* 360 * find oldest drive, but must be 361 * at least THYSTER old. 362 */ 363 e = w->side[side].elem; 364 t0 = toytime() - THYSTER; 365 t = t0; 366 drive = -1; 367 for(i=0; i<w->ndt; i++) { 368 v = bv[i]; 369 if(v == 0) { /* 2nd priority: empty drive */ 370 if(w->offline[i]) 371 continue; 372 if(w->drive[i] != devnone) { 373 drive = i; 374 t = 0; 375 } 376 continue; 377 } 378 if(v->elem == e) { /* 1st priority: other side */ 379 drive = -1; 380 if(v->stime < t0) 381 drive = i; 382 break; 383 } 384 if(v->stime < t) { /* 3rd priority: by time */ 385 drive = i; 386 t = v->stime; 387 } 388 } 389 390 if(drive >= 0) { 391 v = bv[drive]; 392 if(v) { 393 qlock(v); 394 if(v->status != Sstart) { 395 qunlock(v); 396 goto loop; 397 } 398 print("\tunload r%ld drive %Z\n", 399 v-w->side, w->drive[drive]); 400 if(mmove(w, w->mt0, w->dt0+drive, v->elem, v->rot)) { 401 qunlock(v); 402 goto loop; 403 } 404 v->status = Sunload; 405 qunlock(v); 406 } 407 } 408 return drive; 409 } 410 411 Devsize 412 wormsize(Device *d) 413 { 414 Side *v; 415 Juke *w; 416 Devsize size; 417 418 w = d->private; 419 if (w->magic != Jukemagic) 420 print("wormsize: bad magic in Juke (d->private) for %Z\n", d); 421 if(w->isfixedsize && w->fixedsize != 0) 422 size = w->fixedsize; /* fixed size is now known */ 423 else { 424 if (w != d->private) 425 panic("wormsize: w != %Z->private", d); 426 v = wormunit(d); 427 if(v == nil) 428 return 0; 429 size = v->max; 430 qunlock(v); 431 /* 432 * set fixed size for whole Juke from 433 * size of first disc examined. 434 */ 435 if(w->isfixedsize) 436 w->fixedsize = size; 437 } 438 if(d->type == Devlworm) 439 return size-1; /* lie: last block is for label */ 440 return size; 441 } 442 443 /* 444 * return a Devjuke or an mcat (normally of sides) from within d (or nil). 445 * if it's an mcat, the caller must walk it. 446 */ 447 static Device * 448 devtojuke(Device *d, Device *top) 449 { 450 while (d != nil) 451 switch(d->type) { 452 default: 453 print("devtojuke: type of device %Z of %Z unknown\n", 454 d, top); 455 return nil; 456 457 case Devjuke: 458 /* jackpot! d->private is a (Juke *) with nside, &c. */ 459 /* FALL THROUGH */ 460 case Devmcat: 461 case Devmlev: 462 case Devmirr: 463 /* squint hard & call an mlev or a mirr an mcat */ 464 return d; 465 466 case Devworm: 467 case Devlworm: 468 /* 469 * d->private is a (Juke *) with nside, etc., 470 * but we're not supposed to get here. 471 */ 472 print("devtojuke: (l)worm %Z of %Z encountered\n", 473 d, top); 474 /* FALL THROUGH */ 475 case Devwren: 476 return nil; 477 478 case Devcw: 479 d = d->cw.w; /* usually juke */ 480 break; 481 case Devro: 482 d = d->ro.parent; /* cw */ 483 break; 484 case Devfworm: 485 d = d->fw.fw; 486 break; 487 case Devpart: 488 d = d->part.d; 489 break; 490 case Devswab: 491 d = d->swab.d; 492 break; 493 } 494 return d; 495 } 496 497 static int 498 devisside(Device *d) 499 { 500 return d->type == Devworm || d->type == Devlworm; 501 } 502 503 static Device * 504 findside(Device *juke, int side, Device *top) 505 { 506 int i = 0; 507 Device *mcat = juke->j.m, *x; 508 Juke *w = juke->private; 509 510 for (x = mcat->cat.first; x != nil; x = x->link) { 511 if (!devisside(x)) { 512 print("wormsizeside: %Z of %Z of %Z type not (l)worm\n", 513 x, mcat, top); 514 return nil; 515 } 516 i = x->wren.targ; 517 if (i < 0 || i >= w->nside) 518 panic("wormsizeside: side %d in %Z out of range", 519 i, mcat); 520 if (i == side) 521 break; 522 } 523 if (x == nil) 524 return nil; 525 if (w->side[i].time == 0) { 526 print("wormsizeside: side %d not in jukebox %Z\n", i, juke); 527 return nil; 528 } 529 return x; 530 } 531 532 typedef struct { 533 int sleft; /* sides still to visit to reach desired side */ 534 int starget; /* side of topdev we want */ 535 Device *topdev; 536 int sawjuke; /* passed by a jukebox */ 537 int sized; /* flag: asked wormsize for size of starget */ 538 } Visit; 539 540 /* 541 * walk the Device tree from d looking for Devjukes, counting sides. 542 * the main complication is mcats and the like with Devjukes in them. 543 * use Devjuke's d->private as Juke* and see sides. 544 */ 545 static Off 546 visitsides(Device *d, Device *parentj, Visit *vp) 547 { 548 Off size = 0; 549 Device *x; 550 Juke *w; 551 552 /* 553 * find the first juke or mcat. 554 * d==nil means we couldn't find one; typically harmless, due to a 555 * mirror of dissimilar devices. 556 */ 557 d = devtojuke(d, vp->topdev); 558 if (d == nil || vp->sleft < 0) 559 return 0; 560 if (d->type == Devjuke) { /* jackpot! d->private is a (Juke *) */ 561 vp->sawjuke = 1; 562 w = d->private; 563 /* 564 * if there aren't enough sides in this jukebox to reach 565 * the desired one, subtract these sides and pass. 566 */ 567 if (vp->sleft >= w->nside) { 568 vp->sleft -= w->nside; 569 return 0; 570 } 571 /* else this is the right juke, paw through mcat of sides */ 572 return visitsides(d->j.m, d, vp); 573 } 574 575 /* 576 * d will usually be an mcat of sides, but it could be an mcat of 577 * jukes, for example. in that case, we need to walk the mcat, 578 * recursing as needed, until we find the right juke, then stop at 579 * the right side within its mcat of sides, by comparing side 580 * numbers, not just by counting (to allow for unused slots). 581 */ 582 x = d->cat.first; 583 if (x == nil) { 584 print("visitsides: %Z of %Z: empty mcat\n", d, vp->topdev); 585 return 0; 586 } 587 if (!devisside(x)) { 588 for (; x != nil && !vp->sized; x = x->link) 589 size = visitsides(x, parentj, vp); 590 return size; 591 } 592 593 /* the side we want is in this jukebox, thus this mcat (d) */ 594 if (parentj == nil) { 595 print("visitsides: no parent juke for sides mcat %Z\n", d); 596 vp->sleft = -1; 597 return 0; 598 } 599 if (d != parentj->j.m) 600 panic("visitsides: mcat mismatch %Z vs %Z", d, parentj->j.m); 601 x = findside(parentj, vp->sleft, vp->topdev); 602 if (x == nil) { 603 vp->sleft = -1; 604 return 0; 605 } 606 607 /* we've turned vp->starget into the right Device* */ 608 vp->sleft = 0; 609 vp->sized = 1; 610 return wormsize(x); 611 } 612 613 /* 614 * d must be, or be within, a filesystem config that also contains 615 * the jukebox that `side' resides on. 616 * d is normally a Devcw, but could be Devwren, Devide, Devpart, Devfworm, 617 * etc. if called from chk.c Ctouch code. Note too that the worm part of 618 * the Devcw might be other than a Devjuke. 619 */ 620 Devsize 621 wormsizeside(Device *d, int side) 622 { 623 Devsize size; 624 Visit visit; 625 626 memset(&visit, 0, sizeof visit); 627 visit.starget = visit.sleft = side; 628 visit.topdev = d; 629 size = visitsides(d, nil, &visit); 630 if (visit.sawjuke && (visit.sleft != 0 || !visit.sized)) { 631 print("wormsizeside: fewer than %d sides in %Z\n", side, d); 632 return 0; 633 } 634 return size; 635 } 636 637 /* 638 * returns starts (in blocks) of side #side and #(side+1) of dev in *stp. 639 * dev should be a Devcw. 640 */ 641 void 642 wormsidestarts(Device *dev, int side, Sidestarts *stp) 643 { 644 int s; 645 Devsize dstart; 646 647 for (dstart = s = 0; s < side; s++) 648 dstart += wormsizeside(dev, s); 649 stp->sstart = dstart; 650 stp->s1start = dstart + wormsizeside(dev, side); 651 } 652 653 int 654 wormread(Device *d, Off b, void *c) 655 { 656 int r = 0; 657 long max; 658 char name[128]; 659 Side *v = wormunit(d); 660 Juke *w = d->private; 661 Device *dr; 662 663 if (v == nil) 664 panic("wormread: nil wormunit(%Z)", d); 665 dr = w->drive[v->drive]; 666 if (dr->wren.fd < 0) 667 panic("wormread: unopened fd for %Z", d); 668 max = (d->type == Devlworm? v->max + 1: v->max); 669 if(b >= max) { 670 print("wormread: block out of range %Z(%lld)\n", d, (Wideoff)b); 671 r = 0x071; 672 } else if (pread(dr->wren.fd, c, RBUFSIZE, (vlong)b*RBUFSIZE) != RBUFSIZE) { 673 fd2path(dr->wren.fd, name, sizeof name); 674 print("wormread: error on %Z(%lld) on %s in %s: %r\n", 675 d, (Wideoff)b, name, dr->wren.sddir); 676 cons.nwormre++; 677 r = 1; 678 } 679 qunlock(v); 680 return r; 681 } 682 683 int 684 wormwrite(Device *d, Off b, void *c) 685 { 686 int r = 0; 687 long max; 688 char name[128]; 689 Side *v = wormunit(d); 690 Juke *w = d->private; 691 Device *dr; 692 693 if (v == nil) 694 panic("wormwrite: nil wormunit(%Z)", d); 695 dr = w->drive[v->drive]; 696 if (dr->wren.fd < 0) 697 panic("wormwrite: unopened fd for %Z", d); 698 max = (d->type == Devlworm? v->max + 1: v->max); 699 if(b >= max) { 700 print("wormwrite: block out of range %Z(%lld)\n", 701 d, (Wideoff)b); 702 r = 0x071; 703 } else if (pwrite(dr->wren.fd, c, RBUFSIZE, (vlong)b*RBUFSIZE) != RBUFSIZE) { 704 fd2path(dr->wren.fd, name, sizeof name); 705 print("wormwrwite: error on %Z(%lld) on %s in %s: %r\n", 706 d, (Wideoff)b, name, dr->wren.sddir); 707 cons.nwormwe++; 708 r = 1; 709 } 710 qunlock(v); 711 return r; 712 } 713 714 static int 715 mmove(Juke *w, int trans, int from, int to, int rot) 716 { 717 int s; 718 uchar cmd[12], buf[4]; 719 static int recur = 0; 720 721 memset(cmd, 0, sizeof cmd); 722 cmd[0] = 0xa5; /* move medium */ 723 cmd[2] = trans>>8; 724 cmd[3] = trans; 725 cmd[4] = from>>8; 726 cmd[5] = from; 727 cmd[6] = to>>8; 728 cmd[7] = to; 729 if(rot) 730 cmd[10] = 1; 731 s = scsiio(w->juke, SCSInone, cmd, sizeof cmd, buf, 0); /* mmove */ 732 if(s) { 733 print("scsio status #%x\n", s); 734 print("move medium t=%d fr=%d to=%d rot=%d\n", 735 trans, from, to, rot); 736 // panic("mmove"); 737 if(recur == 0) { 738 recur = 1; 739 print("element from=%d\n", from); 740 element(w, from); 741 print("element to=%d\n", to); 742 element(w, to); 743 print("element trans=%d\n", trans); 744 element(w, trans); 745 recur = 0; 746 } 747 return 1; 748 } 749 return 0; 750 } 751 752 static void 753 geometry(Juke *w) 754 { 755 int s; 756 uchar cmd[6], buf[4+20]; 757 758 memset(cmd, 0, sizeof cmd); 759 memset(buf, 0, sizeof buf); 760 cmd[0] = 0x1a; /* mode sense */ 761 cmd[2] = 0x1d; /* element address assignment */ 762 cmd[4] = sizeof buf; /* allocation length */ 763 764 s = scsiio(w->juke, SCSIread, cmd, sizeof cmd, buf, sizeof buf); /* mode sense elem addrs */ 765 if(s) 766 panic("geometry #%x", s); 767 768 w->mt0 = (buf[4+2]<<8) | buf[4+3]; 769 w->nmt = (buf[4+4]<<8) | buf[4+5]; 770 w->se0 = (buf[4+6]<<8) | buf[4+7]; 771 w->nse = (buf[4+8]<<8) | buf[4+9]; 772 w->ie0 = (buf[4+10]<<8) | buf[4+11]; 773 w->nie = (buf[4+12]<<8) | buf[4+13]; 774 w->dt0 = (buf[4+14]<<8) | buf[4+15]; 775 w->ndt = (buf[4+16]<<8) | buf[4+17]; 776 777 memset(cmd, 0, 6); 778 memset(buf, 0, sizeof buf); 779 cmd[0] = 0x1a; /* mode sense */ 780 cmd[2] = 0x1e; /* transport geometry */ 781 cmd[4] = sizeof buf; /* allocation length */ 782 783 s = scsiio(w->juke, SCSIread, cmd, sizeof cmd, buf, sizeof buf); /* mode sense geometry */ 784 if(s) 785 panic("geometry #%x", s); 786 787 w->rot = buf[4+2] & 1; 788 789 print("\tmt %d %d\n", w->mt0, w->nmt); 790 print("\tse %d %d\n", w->se0, w->nse); 791 print("\tie %d %d\n", w->ie0, w->nie); 792 print("\tdt %d %d\n", w->dt0, w->ndt); 793 print("\trot %d\n", w->rot); 794 prflush(); 795 } 796 797 /* 798 * read element e's status from jukebox w, move any disc in drive back to its 799 * slot, and update and print software status. 800 */ 801 static void 802 element(Juke *w, int e) 803 { 804 uchar cmd[12], buf[8+8+88]; 805 int s, t; 806 807 memset(cmd, 0, sizeof cmd); 808 memset(buf, 0, sizeof buf); 809 cmd[0] = 0xb8; /* read element status */ 810 cmd[2] = e>>8; /* starting element */ 811 cmd[3] = e; 812 cmd[5] = 1; /* number of elements */ 813 cmd[9] = sizeof buf; /* allocation length */ 814 815 s = scsiio(w->juke, SCSIread, cmd, sizeof cmd, buf, sizeof buf); /* read elem sts */ 816 if(s) { 817 print("scsiio #%x\n", s); 818 goto bad; 819 } 820 821 s = (buf[0]<<8) | buf[1]; 822 if(s != e) { 823 print("element = %d\n", s); 824 goto bad; 825 } 826 if(buf[3] != 1) { 827 print("number reported = %d\n", buf[3]); 828 goto bad; 829 } 830 s = (buf[8+8+0]<<8) | buf[8+8+1]; 831 if(s != e) { 832 print("element1 = %d\n", s); 833 goto bad; 834 } 835 836 switch(buf[8+0]) { /* element type */ 837 default: 838 print("unknown element %d: %d\n", e, buf[8+0]); 839 goto bad; 840 case 1: /* transport */ 841 s = e - w->mt0; 842 if(s < 0 || s >= w->nmt) 843 goto bad; 844 if(buf[8+8+2] & 1) 845 print("transport %d full %d.%d\n", s, 846 (buf[8+8+10]<<8) | buf[8+8+11], 847 (buf[8+8+9]>>6) & 1); 848 break; 849 case 2: /* storage */ 850 s = e - w->se0; 851 if(s < 0 || s >= w->nse) 852 goto bad; 853 w->side[s].status = Sempty; 854 if(buf[8+8+2] & 1) 855 w->side[s].status = Sunload; 856 if(w->rot) 857 w->side[w->nse+s].status = w->side[s].status; 858 break; 859 case 3: /* import/export */ 860 s = e - w->ie0; 861 if(s < 0 || s >= w->nie) 862 goto bad; 863 print("import/export %d #%.2x %d.%d\n", s, 864 buf[8+8+2], 865 (buf[8+8+10]<<8) | buf[8+8+11], 866 (buf[8+8+9]>>6) & 1); 867 break; 868 case 4: /* data transfer */ 869 s = e - w->dt0; 870 if(s < 0 || s >= w->ndt) 871 goto bad; 872 print("data transfer %d #%.2x %d.%d\n", s, 873 buf[8+8+2], 874 (buf[8+8+10]<<8) | buf[8+8+11], 875 (buf[8+8+9]>>6) & 1); 876 if(buf[8+8+2] & 1) { 877 t = ((buf[8+8+10]<<8) | buf[8+8+11]) - w->se0; 878 if (t < 0 || t >= w->nse || t >= MAXSIDE || 879 s >= MAXDRIVE) { 880 print( 881 "element: juke %Z lies; claims side %d is in drive %d\n", 882 w->juke, t, s); /* lying sack of ... */ 883 /* 884 * at minimum, we've avoided corrupting our 885 * data structures. if we know that numbers 886 * like w->nside are valid here, we could use 887 * them in more stringent tests. 888 * perhaps should whack the jukebox upside the 889 * head here to knock some sense into it. 890 */ 891 goto bad; 892 } 893 print("r%d in drive %d\n", t, s); 894 if(mmove(w, w->mt0, w->dt0+s, w->se0+t, 895 (buf[8+8+9]>>6) & 1)) { 896 print("mmove initial unload\n"); 897 goto bad; 898 } 899 w->side[t].status = Sunload; 900 if(w->rot) 901 w->side[w->nse+t].status = Sunload; 902 } 903 if(buf[8+8+2] & 4) { 904 print("drive w%d has exception #%.2x #%.2x\n", s, 905 buf[8+8+4], buf[8+8+5]); 906 goto bad; 907 } 908 break; 909 } 910 return; 911 bad: 912 /* panic("element") */ ; 913 } 914 915 /* 916 * read all elements' status from jukebox w, move any discs in drives back 917 * to their slots, and update and print software status. 918 */ 919 static void 920 positions(Juke *w) 921 { 922 int i, f; 923 924 /* mark empty shelves */ 925 for(i=0; i<w->nse; i++) 926 element(w, w->se0+i); 927 for(i=0; i<w->nmt; i++) 928 element(w, w->mt0+i); 929 for(i=0; i<w->nie; i++) 930 element(w, w->ie0+i); 931 for(i=0; i<w->ndt; i++) 932 element(w, w->dt0+i); 933 934 f = 0; 935 for(i=0; i<w->nse; i++) 936 if(w->side[i].status == Sempty) { 937 if(f) { 938 print("r%d\n", i-1); 939 f = 0; 940 } 941 } else { 942 if(!f) { 943 print("\tshelves r%d-", i); 944 f = 1; 945 } 946 } 947 if(f) 948 print("r%d\n", i-1); 949 } 950 951 static void 952 jinit(Juke *w, Device *d, int o) 953 { 954 int p; 955 Device *dev = d; 956 957 switch(d->type) { 958 default: 959 print("juke platter not (devmcat of) dev(l)worm: %Z\n", d); 960 panic("jinit: type"); 961 962 case Devmcat: 963 /* 964 * we don't call mcatinit(d) here, so we have to set d->cat.ndev 965 * ourselves. 966 */ 967 for(d=d->cat.first; d; d=d->link) 968 jinit(w, d, o++); 969 dev->cat.ndev = o; 970 break; 971 972 case Devlworm: 973 p = d->wren.targ; 974 if(p < 0 || p >= w->nside) 975 panic("jinit partition %Z", d); 976 w->side[p].ord = o; 977 /* FALL THROUGH */ 978 case Devworm: 979 if(d->private) { 980 print("juke platter private pointer set %p\n", 981 d->private); 982 panic("jinit: private"); 983 } 984 d->private = w; 985 break; 986 } 987 } 988 989 Side* 990 wormi(char *arg) 991 { 992 int i, j; 993 Juke *w; 994 Side *v; 995 996 i = number(arg, -1, 10) - 1; 997 w = jukelist; 998 if(i < 0 || i >= w->nside) { 999 print("bad unit number %s (%d)\n", arg, i+1); 1000 return 0; 1001 } 1002 j = i; 1003 if(j >= w->nse) 1004 j -= w->nse; 1005 if(j < w->nside) { 1006 v = &w->side[j]; 1007 qlock(v); 1008 if(v->status == Sstart) { 1009 if(mmove(w, w->mt0, w->dt0+v->drive, v->elem, v->rot)) { 1010 qunlock(v); 1011 return 0; 1012 } 1013 v->status = Sunload; 1014 } 1015 qunlock(v); 1016 } 1017 j += w->nse; 1018 if(j < w->nside) { 1019 v = &w->side[j]; 1020 qlock(v); 1021 if(v->status == Sstart) { 1022 if(mmove(w, w->mt0, w->dt0+v->drive, v->elem, v->rot)) { 1023 qunlock(v); 1024 return 0; 1025 } 1026 v->status = Sunload; 1027 } 1028 qunlock(v); 1029 } 1030 v = &w->side[i]; 1031 qlock(v); 1032 return v; 1033 } 1034 1035 static void 1036 cmd_wormoffline(int argc, char *argv[]) 1037 { 1038 int u, i; 1039 Juke *w; 1040 1041 if(argc <= 1) { 1042 print("usage: wormoffline drive\n"); 1043 return; 1044 } 1045 u = number(argv[1], -1, 10); 1046 w = jukelist; 1047 if(u < 0 || u >= w->ndrive) { 1048 print("bad drive %s (0<=%d<%d)\n", argv[1], u, w->ndrive); 1049 return; 1050 } 1051 if(w->offline[u]) 1052 print("drive %d already offline\n", u); 1053 w->offline[u] = 1; 1054 for(i=0; i<w->ndrive; i++) 1055 if(w->offline[i] == 0) 1056 return; 1057 print("that would take all drives offline\n"); 1058 w->offline[u] = 0; 1059 } 1060 1061 static void 1062 cmd_wormonline(int argc, char *argv[]) 1063 { 1064 int u; 1065 Juke *w; 1066 1067 if(argc <= 1) { 1068 print("usage: wormonline drive\n"); 1069 return; 1070 } 1071 u = number(argv[1], -1, 10); 1072 w = jukelist; 1073 if(u < 0 || u >= w->ndrive) { 1074 print("bad drive %s (0<=%d<%d)\n", argv[1], u, w->ndrive); 1075 return; 1076 } 1077 if(w->offline[u] == 0) 1078 print("drive %d already online\n", u); 1079 w->offline[u] = 0; 1080 } 1081 1082 void 1083 cmd_wormreset(int, char *[]) 1084 { 1085 Juke *w; 1086 1087 for(w=jukelist; w; w=w->link) { 1088 qlock(w); 1089 positions(w); 1090 qunlock(w); 1091 } 1092 } 1093 1094 static void 1095 cmd_wormeject(int argc, char *argv[]) 1096 { 1097 Juke *w; 1098 Side *v; 1099 1100 if(argc <= 1) { 1101 print("usage: wormeject unit\n"); 1102 return; 1103 } 1104 v = wormi(argv[1]); 1105 if(v == 0) 1106 return; 1107 w = jukelist; 1108 mmove(w, w->mt0, v->elem, w->ie0, 0); 1109 qunlock(v); 1110 } 1111 1112 static void 1113 cmd_wormingest(int argc, char *argv[]) 1114 { 1115 Juke *w; 1116 Side *v; 1117 1118 if(argc <= 1) { 1119 print("usage: wormingest unit\n"); 1120 return; 1121 } 1122 v = wormi(argv[1]); 1123 if(v == 0) 1124 return; 1125 w = jukelist; 1126 mmove(w, w->mt0, w->ie0, v->elem, 0); 1127 qunlock(v); 1128 } 1129 1130 static void 1131 newside(Side *v, int rot, int elem) 1132 { 1133 qlock(v); 1134 qunlock(v); 1135 // v->name = "shelf"; 1136 v->elem = elem; 1137 v->rot = rot; 1138 v->status = Sempty; 1139 v->time = toytime(); 1140 } 1141 1142 /* 1143 * query jukebox robotics for geometry; 1144 * argument is the wren dev of the changer. 1145 * result is actually Juke*, but that type is only known in this file. 1146 */ 1147 void * 1148 querychanger(Device *xdev) 1149 { 1150 Juke *w; 1151 Side *v; 1152 int i; 1153 1154 if (xdev == nil) 1155 panic("querychanger: nil Device"); 1156 if(xdev->type != Devwren) { 1157 print("juke changer not wren %Z\n", xdev); 1158 goto bad; 1159 } 1160 for(w=jukelist; w; w=w->link) 1161 if(xdev == w->juke) 1162 return w; 1163 1164 /* 1165 * allocate a juke structure 1166 * no locking problems. 1167 */ 1168 w = malloc(sizeof(Juke)); 1169 w->magic = Jukemagic; 1170 w->isfixedsize = FIXEDSIZE; 1171 w->link = jukelist; 1172 jukelist = w; 1173 1174 print("alloc juke %Z\n", xdev); 1175 qlock(w); 1176 qunlock(w); 1177 // w->name = "juke"; 1178 w->juke = xdev; 1179 w->robotdir = sdof(xdev); 1180 w->robot = openscsi(w->robotdir); 1181 if (w->robot == nil) 1182 panic("can't openscsi(%s): %r", w->robotdir); 1183 newscsi(xdev, w->robot); 1184 geometry(w); 1185 1186 /* 1187 * pick up each side 1188 */ 1189 w->nside = w->nse; 1190 if(w->rot) 1191 w->nside += w->nside; 1192 if(w->nside > MAXSIDE) { 1193 print("too many sides: %d max %d\n", w->nside, MAXSIDE); 1194 goto bad; 1195 } 1196 for(i=0; i < w->nse; i++) { 1197 v = &w->side[i]; 1198 newside(v, 0, w->se0 + i); 1199 if(w->rot) 1200 newside(v + w->nse, 1, w->se0 + i); 1201 } 1202 positions(w); 1203 1204 w->ndrive = w->ndt; 1205 if(w->ndrive > MAXDRIVE) { 1206 print("ndrives truncated to %d\n", MAXDRIVE); 1207 w->ndrive = MAXDRIVE; 1208 } 1209 1210 /* 1211 * pick up each drive 1212 */ 1213 for(i=0; i<w->ndrive; i++) 1214 w->drive[i] = devnone; 1215 return w; 1216 bad: 1217 panic("querychanger: %Z", xdev); 1218 return nil; 1219 } 1220 1221 void 1222 jukeinit(Device *d) 1223 { 1224 Juke *w; 1225 Device *xdev; 1226 int i; 1227 static int beenhere = 0; 1228 1229 /* j(w<changer>w<station0>...)(r<platters>) */ 1230 if (d == nil) 1231 panic("jukeinit: nil Device"); 1232 xdev = d->j.j; 1233 if(xdev == nil || xdev->type != Devmcat) { 1234 print("juke union not mcat\n"); 1235 goto bad; 1236 } 1237 1238 /* 1239 * pick up the changer device 1240 */ 1241 xdev = xdev->cat.first; 1242 w = querychanger(xdev); 1243 1244 if (!beenhere) { 1245 beenhere = 1; 1246 cmd_install("wormreset", 1247 "-- put drives back where jukebox thinks they belong", 1248 cmd_wormreset); 1249 cmd_install("wormeject", "unit -- shelf to outside", 1250 cmd_wormeject); 1251 cmd_install("wormingest", "unit -- outside to shelf", 1252 cmd_wormingest); 1253 cmd_install("wormoffline", "unit -- disable drive", 1254 cmd_wormoffline); 1255 cmd_install("wormonline", "unit -- enable drive", 1256 cmd_wormonline); 1257 } 1258 1259 /* walk through the worm drives */ 1260 i = 0; 1261 while(xdev = xdev->link) { 1262 if(xdev->type != Devwren) { 1263 print("drive not devwren: %Z\n", xdev); 1264 goto bad; 1265 } 1266 if(w->drive[i]->type != Devnone && 1267 xdev != w->drive[i]) { 1268 print("double init drive %d %Z %Z\n", 1269 i, w->drive[i], xdev); 1270 goto bad; 1271 } 1272 if(i >= w->ndrive) { 1273 print("too many drives %Z\n", xdev); 1274 goto bad; 1275 } 1276 w->drive[i++] = xdev; 1277 } 1278 1279 if(i <= 0) { 1280 print("no drives\n"); 1281 goto bad; 1282 } 1283 1284 /* 1285 * put w pointer in each platter 1286 */ 1287 d->private = w; 1288 jinit(w, d->j.m, 0); 1289 w->probeok = 1; 1290 return; 1291 1292 bad: 1293 panic("juke init"); 1294 } 1295 1296 /* 1297 * called periodically 1298 */ 1299 void 1300 wormprobe(void) 1301 { 1302 int i, drive; 1303 Timet t; 1304 Side *v; 1305 Juke *w; 1306 1307 t = toytime() - TWORM; 1308 for(w=jukelist; w; w=w->link) { 1309 if(w->probeok == 0 || !canqlock(w)) 1310 continue; 1311 for(i=0; i<w->nside; i++) { 1312 v = &w->side[i]; 1313 if(!canqlock(v)) 1314 continue; 1315 if(v->status == Sstart && t > v->time) { 1316 drive = v->drive; 1317 print("\ttime r%ld drive %Z\n", 1318 v-w->side, w->drive[drive]); 1319 mmove(w, w->mt0, w->dt0+drive, v->elem, v->rot); 1320 v->status = Sunload; 1321 } 1322 qunlock(v); 1323 } 1324 qunlock(w); 1325 } 1326 } 1327