Subject: TMSCP fixes plus TQK70 support (#409) Index: pdpuba/tmscp.c,pdp/mscp_common.h 2.11BSD Description: Due to a bug in the TMSCP driver a TQK70+TK70 would not work with 2.11BSD. Enabling the cache has no effect and possibly leaves the driver in a state where no further accesses to the drive will be allowed. Repeat-By: Add a TK70 to the system. Try writing to the tape. One of two things will happen: 1) an immediate "end of tape" error will be produced, 2) the program will hang and a reboot will be probably be needed to regain control of the terminal (and free the tape drive). Attempting to read a TK70 tape will produce an error message from the driver saying that the read failed with a status of 4 (the drive went "available" during the read command!). If the diagnostic output of the TMSCP driver is enabled with "sysctl -w machdep.tmscp.printf=9" it will be seen that an 'endcode' of 0x00 (actually 0x80 with the high bit indicating command complete) is being returned by the controller (with the status field set to 1 which indicates an invalid parameter in the previous command). The TQK70 reports (via a flag in the 'get unit characteristics' response) that it is 'cache capable' but enabling the cache with either "mt -f /dev/rmtxx cacheon" or the sysctl command "sysctl -w machdep.tmscp.cache=xxx" has no effect. All in all the TK70 was not working right at all. Fix: The one basic problem was that the TQK70 is much more picky about the content of the 'density' (m_format) field in the TMSCP command packet. Earlier controllers (TK50 for example) had 0 in the high byte of this field. The TQK70 insists that a nonzero (0x2) value be present. That field being in error in the 'set unit characteristics' command was causing almost all of the problems. The density selection code was fixed to preserve the high byte. It is a bug on the TQK70's part that 0x80 is being returned in response to a command of 0x0A! The proper endcode is 0x8A! During the debugging and testing of the driver it was noticed that the drive would be left "busy" and not useable without a reboot. At one time the 'tmscpcommand()' function was only used to perform a non-blocking rewind when the drive was close. Since that time the tmscpcommand() function was used much more widely and those newer uses require timeout logic to prevent the drive from being left permanently busy (hardware going catatonic). Another section of code never did feel quite right. The special treatment of 'available and offline' status codes had been carried over from previous versions of the driver. This section of code was removed because it was failing to perform an iodone() call when a read or write command completed with an offline or available error return. Other minor changes included shortening some error message strings to save D space and adding one or two extra debug statements to help with future debugging. NOTE: When writing a TK70 cartridge it is necessary to use the "high density" device number. Instead of "/dev/rmt0" use "/dev/rmt24" (created with "mknod /dev/rmt24 c 23 24"). This is because while a TQK70 presents two density choices (TK50 and TK70) only one of them can be used for writing (but the driver doesn't necessarily know which one - it didn't feel "right" to hard code that into the driver). Apply the following patch and rebuild the kernel. Cut where indicated and save to a file (/tmp/409), then: patch < /tmp/409 cd /sys/YOUR_KERNEL make mv /unix /ounix mv unix /unix chmod 744 /unix If you are using a networking kernel then: mv /netnix /onetnix mv netnix /netnix chmod 744 /netnix and finally: reboot It is also a very good idea to keep an updated GENERIC kernel present on the system for use in emergencies. Often this is /genunix. cd /sys/GENERIC make mv unix /genunix chmod 744 /genunix As always this and previous updates to 2.11BSD are available via anonymous FTP to either FTP.IIPO.GTEGSC.COM or MOE.2BSD.COM in the directory /pub/2.11BSD. --------------------------cut here--------------------- *** /usr/src/sys/pdpuba/tmscp.c.old Fri Apr 24 19:50:11 1998 --- /usr/src/sys/pdpuba/tmscp.c Fri Feb 26 21:15:19 1999 *************** *** 1,6 **** #define TMSDEBUG 1 ! /* @(#)tmscp.c 1.11 (2.11BSD GTE) 1998/4/24 */ #if !defined(lint) && defined(DOSCCS) static char *sccsid = "@(#)tmscp.c 1.24 (ULTRIX) 1/21/86"; --- 1,6 ---- #define TMSDEBUG 1 ! /* @(#)tmscp.c 1.12 (2.11BSD) 1999/2/25 */ #if !defined(lint) && defined(DOSCCS) static char *sccsid = "@(#)tmscp.c 1.24 (ULTRIX) 1/21/86"; *************** *** 31,37 **** --- 31,56 ---- * tmscp.c - TMSCP (TK50/TU81) tape device driver * * Modification History: + * 25-Feb-99 - sms + * Fix density selection to preserve the high byte of m_format. This + * is required devices (such as the TK70) which have a nonzero value + * in this field. * + * 22-Feb-99 - sms + * Add timeout logic to tmscpcommand to catch hardware going catatonic. + * When tmscpcommand() was first created the only use was to issue a + * nonblocking rewind upon close. Tmscpcommand is now called for many + * other functions some of which can leave a process (and the drive) hung + * unless a timeout is done. + * + * Remove special treatment of OFFLINE and AVLBL status codes in + * tms_iodone(). The code is suspected of having been a bug even in + * its old location but is definitely causing problems now - if a read + * completes with a 'AVLBL' status the drive is hung because an iodone() + * is never performed. + * + * Minor cleanup done thruout (reduce D space consumption ,etc). + * * 24-Apr-98 - sms * An incorrect pointer was being passed to tms_iodone() from tmscprsp() * when dealing with the 'invalid endcode' case. While that value should *************** *** 318,323 **** --- 337,343 ---- int tmscp_cp_wait = 0; /* Something to wait on for command */ /* packets and or credits. */ int wakeup(); + int tmswatchdog(); extern int hz; /* Should find the right include */ extern long _iomap(); extern u_int tmscp_cache; /* See pdp/kern_pdp.c */ *************** *** 528,534 **** break; default: ! log(LOG_INFO, "tms%d: state %d\n", dev, sc->sc_state); return; } /* end switch */ --- 548,554 ---- break; default: ! log(LOG_INFO, "tms%d: ST %d\n", dev, sc->sc_state); return; } /* end switch */ *************** *** 604,609 **** --- 624,630 ---- register struct tms_info *tms; register struct mscp *mp; struct tmscpdevice *tmscpaddr; + u_short cmdref; int s,i; if (ctlr >= NTMSCP) *************** *** 628,634 **** if (sc->sc_state == S_IDLE) if (!tkini(sc)) { ! log(LOG_INFO, "tms%d init fail\n", ctlr); sc->sc_drives[unit] = NULL; tms->Tflags = 0; tms->tms_type = 0; --- 649,655 ---- if (sc->sc_state == S_IDLE) if (!tkini(sc)) { ! log(LOG_INFO, "tms%d init\n", ctlr); sc->sc_drives[unit] = NULL; tms->Tflags = 0; tms->tms_type = 0; *************** *** 671,678 **** mp->mscp_opcode = M_OP_ONLIN; mp->mscp_unit = unit; /* unit? */ tms_clrerr(tms, mp); ! mp->mscp_cmdref = (u_short)&tms->tms_type; ! /* need to sleep on something */ ((Trl *)mp->mscp_dscptr)->hsh |= (TMSCP_OWN | TMSCP_INT); normalseg5(); i = tmscpaddr->tmscpip; --- 692,700 ---- mp->mscp_opcode = M_OP_ONLIN; mp->mscp_unit = unit; /* unit? */ tms_clrerr(tms, mp); ! /* calculate this once instead of 4 times */ ! cmdref = (u_short)&tms->tms_type; ! mp->mscp_cmdref = cmdref; /* need to sleep on something */ ((Trl *)mp->mscp_dscptr)->hsh |= (TMSCP_OWN | TMSCP_INT); normalseg5(); i = tmscpaddr->tmscpip; *************** *** 682,690 **** * 240 seconds (4 minutes) is necessary since a rewind * can take a few minutes. */ ! timeout(wakeup,(caddr_t) &tms->tms_type,240 * hz); ! sleep((caddr_t) &tms->tms_type,PSWP+1); ! untimeout(wakeup, &tms->tms_type); } if (!(tms->Tflags & _ONLINE)) { --- 704,712 ---- * 240 seconds (4 minutes) is necessary since a rewind * can take a few minutes. */ ! timeout(wakeup,(caddr_t) cmdref,240 * hz); ! sleep((caddr_t)cmdref,PSWP+1); ! untimeout(wakeup, cmdref); } if (!(tms->Tflags & _ONLINE)) { *************** *** 754,761 **** * exception state, leave that alone for the non-rewind case so that further * operations fail. About all that can be done now is log the error. */ ! log(LOG_INFO, "tms%d,%d flush fail\n", ! TMSCTLR(dev), unit); tms->Tflags &= ~_CACHE_WRITTEN; } } --- 776,782 ---- * exception state, leave that alone for the non-rewind case so that further * operations fail. About all that can be done now is log the error. */ ! log(LOG_INFO, "tms%d,%d flsh\n", TMSCTLR(dev), unit); tms->Tflags &= ~_CACHE_WRITTEN; } } *************** *** 790,796 **** bp = &tmscp_softc[TMSCTLR(dev)].sc_cmdbuf; s = spl5(); ! while (bp->b_flags&B_BUSY) { bp->b_flags |= B_WANTED; sleep((caddr_t)bp, PRIBIO); --- 811,817 ---- bp = &tmscp_softc[TMSCTLR(dev)].sc_cmdbuf; s = spl5(); ! while (bp->b_flags&B_BUSY) { bp->b_flags |= B_WANTED; sleep((caddr_t)bp, PRIBIO); *************** *** 807,824 **** bp->b_bcount = count; bp->b_resid = com; bp->b_blkno = 0; - tmscpstrategy(bp); /* ! * It is safe to wait here because the rewind done on a close specifies ! * the modifier M_MD_IMMED which causes an immediate return. */ iowait(bp); ! if (bp->b_flags&B_WANTED) ! wakeup((caddr_t)bp); ! bp->b_flags &= B_ERROR; } /* * Init mscp communications area */ tmsginit(sc, com, msgs, offset, length, flags) --- 828,860 ---- bp->b_bcount = count; bp->b_resid = com; bp->b_blkno = 0; /* ! * Start the timer before entering the strategy routine. If it declares ! * an immediate error it will also perform an iodone which will cause us ! * to fall thru and cancel the timer. */ + timeout(tmswatchdog, bp, 240 * hz); + tmscpstrategy(bp); iowait(bp); ! untimeout(tmswatchdog, bp); ! if (bp->b_flags & B_WANTED) /* Anyone waiting above? */ ! wakeup(bp); ! bp->b_flags &= B_ERROR; /* Clears B_BUSY */ } /* + * If this routine is called then (after 4 minutes) something is hung. + * Set the I/O done flag, set error to be ETIMEDOUT, and issue the wakeup. + */ + tmswatchdog(bp) + struct buf *bp; + { + + bp->b_error = ETIMEDOUT; + biodone(bp); + } + + /* * Init mscp communications area */ tmsginit(sc, com, msgs, offset, length, flags) *************** *** 968,974 **** tms = sc->sc_drives[unit]; if ((tmscpaddr->tmscpsa&TMSCP_ERR) || sc->sc_state != S_RUN) { ! log(LOG_INFO, "tms%d,%d: sa %x state %d\n", sc->sc_unit, unit, tmscpaddr->tmscpsa, sc->sc_state); (void)tkini(sc); /* SHOULD REQUEUE OUTSTANDING REQUESTS, LIKE TMSCPRESET */ --- 1004,1010 ---- tms = sc->sc_drives[unit]; if ((tmscpaddr->tmscpsa&TMSCP_ERR) || sc->sc_state != S_RUN) { ! log(LOG_INFO, "tms%d,%d: sa %x st %d\n", sc->sc_unit, unit, tmscpaddr->tmscpsa, sc->sc_state); (void)tkini(sc); /* SHOULD REQUEUE OUTSTANDING REQUESTS, LIKE TMSCPRESET */ *************** *** 1062,1072 **** tms_repos_st(mp, sc, M_MD_OBJCT); break; case TMS_SETDENSITY: ! tms->tms_format = Dmatrix[TMSDENS(bp->b_dev)][tms->tms_fmtmenu & FMTMASK]; tms_stunt_st(mp, sc, 0); break; default: ! log(LOG_INFO, "tms ioctl %x\n", bp->b_resid); /* Need a no-op. Reposition no amount */ mp->mscp_opcode = M_OP_REPOS; break; --- 1098,1109 ---- tms_repos_st(mp, sc, M_MD_OBJCT); break; case TMS_SETDENSITY: ! tms->tms_format &= ~M_TF_MASK; ! tms->tms_format |= Dmatrix[TMSDENS(bp->b_dev)][tms->tms_fmtmenu & FMTMASK]; tms_stunt_st(mp, sc, 0); break; default: ! log(LOG_INFO, "ioctl %x\n", bp->b_resid); /* Need a no-op. Reposition no amount */ mp->mscp_opcode = M_OP_REPOS; break; *************** *** 1089,1094 **** --- 1126,1138 ---- mp->mscp_modifier |= M_MD_CLSEX; } + if (tmscpprintf & 0x8) + { + log(LOG_INFO, "tms%d,%d -> op %x fl %x mod %x\n", + sc->sc_unit, mp->mscp_unit, + mp->mscp_opcode, mp->mscp_flags, mp->mscp_modifier); + } + ((Trl *)mp->mscp_dscptr)->hsh |= (TMSCP_OWN|TMSCP_INT); i = tmscpaddr->tmscpip; /* initiate polling */ dp->b_qsize++; *************** *** 1242,1248 **** case M_OP_AVATN: tms->Tflags &= ~_ONLINE; return; ! case 0: if (tmscpprintf & 0x8) log(LOG_INFO, "tms%d,%d: inv end=%x st=%x\n", sc->sc_unit,mp->mscp_unit,em_endcode,em_status); --- 1286,1298 ---- case M_OP_AVATN: tms->Tflags &= ~_ONLINE; return; ! /* ! * An endcode with no opcode (0x80) is an invalid command. This is supposed ! * to indicate a protocol error (illegal opcode, parameter error, etc) but ! * without the real opcode we don't know which command (reposition, write, ! * read, ...) failed. So, just declare I/O done and hope for the best. ! */ ! case 0: if (tmscpprintf & 0x8) log(LOG_INFO, "tms%d,%d: inv end=%x st=%x\n", sc->sc_unit,mp->mscp_unit,em_endcode,em_status); *************** *** 1272,1278 **** return; default: if (tmscpprintf & 0x8) ! log(LOG_INFO, "tms%d,%d bad rsp: %x\n", sc->sc_unit, mp->mscp_unit, em_endcode); return; } /* end switch mp->mscp_opcode */ --- 1322,1328 ---- return; default: if (tmscpprintf & 0x8) ! log(LOG_INFO, "tms%d,%d rsp %x\n", sc->sc_unit, mp->mscp_unit, em_endcode); return; } /* end switch mp->mscp_opcode */ *************** *** 1590,1596 **** mp->mscp_modifier |= M_MD_CDATL; } else ! log(LOG_INFO, "tms%d,%d cache lost\n", sc->sc_unit, mp->mscp_unit); } } --- 1640,1646 ---- mp->mscp_modifier |= M_MD_CDATL; } else ! log(LOG_INFO, "tms%d,%d clost\n", sc->sc_unit, mp->mscp_unit); } } *************** *** 1675,1683 **** { register struct tms_info *tms = sc->sc_drives[mp->mscp_unit]; - tms->Tflags &= ~_INUSE; (void)tms_check_ret(mp, sc); ! tms->Tflags &= ~_ONLINE; tms->tms_position = 0; tms->tms_flags |= MTF_BOM; if (tms->tms_status == M_ST_SUCC) --- 1725,1732 ---- { register struct tms_info *tms = sc->sc_drives[mp->mscp_unit]; (void)tms_check_ret(mp, sc); ! tms->Tflags &= ~(_INUSE | _ONLINE); tms->tms_position = 0; tms->tms_flags |= MTF_BOM; if (tms->tms_status == M_ST_SUCC) *************** *** 1696,1702 **** if (em_status == M_ST_SUCC) tms->Tflags &= ~_CACHE_WRITTEN; else ! log(LOG_INFO, "tms%d,%d flush fail\n",sc->sc_unit,mp->mscp_unit); tms->tms_position = mp->mscp_position; (void)tms_check_ret(mp, sc); tms->tms_resid = 0; --- 1745,1751 ---- if (em_status == M_ST_SUCC) tms->Tflags &= ~_CACHE_WRITTEN; else ! log(LOG_INFO, "tms%d,%d Flush\n",sc->sc_unit,mp->mscp_unit); tms->tms_position = mp->mscp_position; (void)tms_check_ret(mp, sc); tms->tms_resid = 0; *************** *** 1771,1782 **** tms_iodone(mp, sc); } - /* - * This routine removes the buffer from the I/O wait queue, decrements the - * drive queue size, stores the residual value in the buffer header and - * calls iodone. - */ - tms_iodone(mp, sc) struct mscp *mp; struct tmscp_softc *sc; --- 1820,1825 ---- *************** *** 1784,1825 **** struct tms_info *tms = sc->sc_drives[mp->mscp_unit]; register struct buf *bp = (struct buf *)mp->mscp_cmdref; register struct buf *dp = &tms->tms_dtab; - u_short st = tms->tms_status; /* ! * Remove the buffer from the I/O wait queue. */ bp->av_back->av_forw = bp->av_forw; bp->av_forw->av_back = bp->av_back; dp->b_qsize--; - /* - * The weird (and not fully understood) treatment of the OFFLIN and AVLBL - * status values was moved intact. It is not clear why the error bit can't - * simply be set and iodone called as for other not successful status codes. - */ - if (st == M_ST_OFFLN || st == M_ST_AVLBL) - { - /* - * Link buffer on to the front of the drive queue. - */ - if ((bp->av_forw = dp->b_actf) == 0) - dp->b_actf = bp; - dp->b_actf = bp; - /* - * Link the drive onto the controller queue. - */ - if (dp->b_active == 0) - { - dp->b_forw = NULL; - if (sc->sc_ctab.b_actf == NULL) - sc->sc_ctab.b_actf = dp; - else - sc->sc_ctab.b_actl->b_forw = dp; - sc->sc_ctab.b_actl = dp; - dp->b_active = 1; - } - return; - } bp->b_resid = tms->tms_resid; iodone(bp); } --- 1827,1840 ---- struct tms_info *tms = sc->sc_drives[mp->mscp_unit]; register struct buf *bp = (struct buf *)mp->mscp_cmdref; register struct buf *dp = &tms->tms_dtab; /* ! * Remove the buffer from the I/O wait queue. Set the residual count and ! * declare the I/O done. */ bp->av_back->av_forw = bp->av_forw; bp->av_forw->av_back = bp->av_back; dp->b_qsize--; bp->b_resid = tms->tms_resid; iodone(bp); } *************** *** 1890,1896 **** tms->Tflags &= ~_ONLINE; if ((tms->Tflags & _CACHE_ON) && (tms->Tflags & _CACHE_WRITTEN)) ! log(LOG_INFO, "tms%d,%d cache loss2\n", sc->sc_unit, mp->mscp_unit); berr = ENXIO; break; --- 1905,1911 ---- tms->Tflags &= ~_ONLINE; if ((tms->Tflags & _CACHE_ON) && (tms->Tflags & _CACHE_WRITTEN)) ! log(LOG_INFO, "tms%d,%d closs2\n", sc->sc_unit, mp->mscp_unit); berr = ENXIO; break; *************** *** 1926,1932 **** else { tms->Tflags &= ~_CLSEREX; ! log(LOG_INFO, "tms%d,%d serex, subcode %x\n", sc->sc_unit, mp->mscp_unit, em_subcode); berr = EIO; } --- 1941,1947 ---- else { tms->Tflags &= ~_CLSEREX; ! log(LOG_INFO, "tms%d,%d serex sb %x\n", sc->sc_unit, mp->mscp_unit, em_subcode); berr = EIO; } *************** *** 1957,1963 **** break; } if (unkerr) ! log(LOG_INFO, "tms%d,%d: bad st/sb =%x/%x\n", sc->sc_unit, mp->mscp_unit, em_status, em_subcode); if (berr && bp) { --- 1972,1978 ---- break; } if (unkerr) ! log(LOG_INFO, "tms%d,%d: st/sb =%x/%x\n", sc->sc_unit, mp->mscp_unit, em_status, em_subcode); if (berr && bp) { *** /usr/src/sys/pdp/mscp_common.h.old Sun Feb 1 14:34:57 1998 --- /usr/src/sys/pdp/mscp_common.h Thu Feb 25 19:42:19 1999 *************** *** 1,5 **** /* ! * 1.2 (2.11BSD) 1998/2/31 * * Definitions common to both MSCP and TMSCP were moved here from tmscp.h. * Eventually the MSCP driver and include file will be modified to use these --- 1,5 ---- /* ! * 1.3 (2.11BSD) 1999/2/25 * * Definitions common to both MSCP and TMSCP were moved here from tmscp.h. * Eventually the MSCP driver and include file will be modified to use these *************** *** 336,344 **** --- 336,361 ---- /* * Tape Format Flag Values */ + #define M_TF_MASK 0x00ff /* Density bits */ + #define M_TF_CODE 0x0100 /* Format code multiplier */ #define M_TF_800 0x01 /* NRZI 800 bpi */ #define M_TF_PE 0x02 /* Phase Encoded 1600 bpi */ #define M_TF_GCR 0x04 /* Group Code Recording 6250 bpi */ #define M_TF_BLK 0x08 /* Cartridge Block Mode */ + + #ifdef notnow + /* + * Define a few of the common controller and drive types for reference but + * don't actually force the preprocessor to handle even more defines. + */ + #define M_CM_UDA50 2 + #define M_CM_TU81 5 + #define M_CM_UDA50A 6 + #define M_CM_TK50 9 + #define M_CM_TQK50 9 + #define M_CM_TK70 14 + #define M_CM_TQK70 14 + #define M_CM_RQDX3 19 + #endif #endif /* _MSCP_COMMON_H_ */ *** /VERSION.old Thu Dec 31 20:27:01 1998 --- /VERSION Fri Feb 26 20:09:03 1999 *************** *** 1,5 **** ! Current Patch Level: 408 ! Date: December 31, 1998 2.11 BSD ============ --- 1,5 ---- ! Current Patch Level: 409 ! Date: February 26, 1999 2.11 BSD ============