📄 scsi_disk.c
字号:
/* * Copy in the write long data from user space. */ if(copyin( iox->io_addr, sc->sc_rzaddr[targid], iox->io_cnt) != 0){ reterr = EFAULT; break; } if ((reterr = rzcommand (dev, SZ_WRITEL, 1, 0)) == EINTR) { break; } if(sc->sc_c_status[targid] != SZ_GOOD) { mprintf("rzspecial: %s unit %d: write lond data failed\n", sc->sc_device[targid], unit); reterr = EIO; break; } break; case SCSI_READ_LONG: iox = (struct io_uxfer *)data; if( iox->io_cnt > sizeof(*dd) ){ reterr = EINVAL; break; } bcopy(data, sc->sc_rzparams[targid], sizeof(*iox)); if ((reterr = rzcommand (dev, SZ_READL, 1, 0)) == EINTR) { break; } if(sc->sc_c_status[targid] != SZ_GOOD) { mprintf("rzspecial: %s unit %d: read lond data failed\n", sc->sc_device[targid], unit); reterr = EIO; break; } /* * Copy out the read long data to user space. */ if(copyout(sc->sc_rzaddr[targid], iox->io_addr, iox->io_cnt) != 0) reterr = EFAULT; break; case SCSI_VERIFY_DATA: bcopy(data, sc->sc_rzparams[targid], sizeof(*vp)); if ((reterr = rzcommand (dev, SZ_VFY_DATA, 1, 0)) == EINTR) { break; } if(sc->sc_c_status[targid] != SZ_GOOD) { mprintf("rzspecial: %s unit %d: verify data failed\n", sc->sc_device[targid], unit); reterr = EIO; } break; case SCSI_MODE_SENSE: if ((reterr = rzcommand (dev, SZ_MODSNS, 1, 0)) == EINTR) { break; } if(sc->sc_c_status[targid] != SZ_GOOD) { mprintf("rzspecial: %s unit %d: mode sense failed\n", sc->sc_device[targid], unit); reterr = EIO; break; } /* * Copy out the mode sense data to user space. */ if(copyout(sc->sc_rzaddr[targid], msp->msp_addr, msp->msp_length) != 0) reterr = EFAULT; break; case SCSI_MODE_SELECT: /* * Must be super user to CHANGE the disk parameters. */ if (!suser()) { reterr = EACCES; break; } /* * Copy in the mode select data from user space. */ if(copyin(msp->msp_addr, sc->sc_rzaddr[targid], msp->msp_length) != 0) { reterr = EFAULT; break; } if ((reterr = rzcommand (dev, SZ_MODSEL, 1, 0)) == EINTR) { break; } if(sc->sc_c_status[targid] != SZ_GOOD) { mprintf("rzspecial: %s unit %d: mode select failed\n", sc->sc_device[targid], unit); reterr = EIO; } break; case SCSI_GET_SENSE: es = (struct extended_sense *)data; bcopy((char *)&sc->sc_rzsns[targid], data, sizeof(*es)); bzero((char *)&sc->sc_rzsns[targid], sizeof(sc->sc_rzsns[targid])); break; case SCSI_GET_INQUIRY_DATA: if ((reterr = rzcommand (dev, SZ_INQ, 1, 0)) == EINTR) { break; } if(sc->sc_c_status[targid] != SZ_GOOD) { mprintf("rzspecial: %s unit %d: inquiry failed\n", sc->sc_device[targid], unit); reterr = EIO; break; } if(copyout(sc->sc_rzaddr[targid], msp->msp_addr, msp->msp_length) != 0) reterr = EFAULT; break; default: reterr = ENXIO; break; } sc->sc_rzspecial[targid] = 0; KM_FREE(sc->sc_rzaddr[targid], KM_DEVBUF); return(reterr);}rzdump(){}/* * * Name: rz_bbr -Disk bad block replacement routine * * Abstract: This routine is called from the disk completion routine * to perform BBR on LBN contained in the sense info. * The LBN is read, reassigned, and written back. The * original request is repeated and it's status returned * to the user. * Inputs: * * sc Pointer to controller's sz_softc structure. * targid Device SCSI target ID (0 - 7). * cbp Buffer pointer of the current command. * * Outputs: Error messages depending on the success/fail of the BBR * progress. * * Return Values: A flag back to the completion routine signaling what * to do with the existing buffer on the front of the * queue. * * Side Effects: The buffer queue is changed to insert or remove the * bbr buffer on the front. The state machine is entered * many times. However the current operation is repeated * to get the necessary information back to the user. * The macros KM_ALLOC/KM_FREE are called. * * To Do: Make the BBR SM multipass, w/single pass it is a little * ugly. Work out a good handshake with rzdisk. If rzdisk * goes to replace a ECC block, this routine will do it * before the read data gets back to rzdisk. Workup a way * to turnoff error logging ? */rz_bbr( sc, targid, cbp ) register struct sz_softc *sc; register int targid; struct buf *cbp; /* bp for current operation */{ register struct buf *dp; register struct buf *bp; struct reassign_params *rp; int unit; int release; int flags; char *p; /* general purpose byte pointers */ char *q; int i; unit = minor(cbp->b_dev) >> 3; dp = &szutab[ unit ]; bp = &bszbuf[ targid ]; release = 1; /* Default: release front buffer */ /* Check to see if DBBR is disabled for this device. If the SCSI_NODBBR flag is set return to the completion routine. */ if( ((sc->sc_devtab[targid])->flags & SCSI_NODBBR ) != 0) return( release ); /* Check the active flag, is the target already in BBR state. */ if( !( sc->sc_bbr_active[ targid ] == 1 )) { /* Check out the additional sence codes. BBR will be done on ECC level errors. Read retry errors for now will be ignored. */ if( sc->sc_sns[targid].asb.rz_asb.asc == SZ_ASC_RRETRY ) { /* On a read retry, simply leave and allow the current buffer to complete. */ PRINTD( targid, 0x200, ("BBR Recovered error a read retry\n")); return( 1 ); /* free up the buffer */ /* FUTURE: save LBN for compare on next RRETRY */ } else if( sc->sc_sns[targid].asb.rz_asb.asc != SZ_ASC_RERROR ) { /* On a unhandled error code, simply leave and allow the current buffer to complete. */ PRINTD( targid, 0x200, ("BBR Recovered error not a read ECC\n")); return( 1 ); /* free up the buffer */ } /* On an error with ECC correction, go through the BBR process. */ /* Allocate a working data buffer for the read data. Two 512 chuncks are allocated one is for the read/write data the other is for the reassign data block containing the bad block info. *//* JAG remove magic ##'s and use defines */ KM_ALLOC( sc->sc_bbraddr[targid], char *, 1024, KM_DEVBUF, (KM_NOWAIT|KM_CLEAR) ); if(sc->sc_bbraddr[targid] == NULL) { mprintf("rz_bbr: Unable to alloc working buffer for DBBR\n" ); return( 1 ); /* free up the buffer */ } sc->sc_bbrparams[targid] = sc->sc_bbraddr[targid] + 512; /* Load the bp etal with what ever is needed. Then let the SCSI State Machine handle the work. The BBR states will handle "what just happened". */ bp->b_flags = (B_BUSY | B_READ ); /* read into ker mem */ bp->b_un.b_addr = sc->sc_bbraddr[ targid ]; /* where to read */ bp->b_bcount = 512; /* MAGIC # one block */ bp->b_retry = 0; /* no retry in SM */ bp->b_resid = 0; /* Setup the block # and dev entry for the bad block. The block is kept in the info bytes of the sence data. The block is "absolute" from the drives point of view. The b_dev entry has to be modified to fake the state machine out to use C partition */ bp->b_dev = (((cbp->b_dev) & ~(7)) | 2 ); /* C is #2 */ bp->b_blkno = sz_sbtol( &(sc->sc_sns[targid].infobyte3) ); /* LBN # */ /* Pre-load the reassign parameter part of the data block. Only the one block will be reassigned. This information will be use in the reassign part of the SM. */ rp = (struct reassign_params *)sc->sc_bbrparams[ targid ]; rp->rp_header.defect_len1 = 0; rp->rp_header.defect_len0 = 4; /* only 1 LBN = 4 bytes */ p = (char *)&(rp->rp_header.defect_len0); p++; /* inc to the defect desc section */ q = (char *)&(sc->sc_sns[ targid ].infobyte3); /* LBN # */ for( i = 0; i < 4; i++ ) /* xfer the bytes from sns to rp */ { *p++ = *q++; } /* Set the BBR active flag, and place the buffer onto the front of the queue. This will leave the bp that "failed" still on the queue and next in line. When the BBR has completed, this bp can be acted upon. ASSUMPTION: Playing with the queue. The priority level should still be high enough to block. */ sc->sc_bbr_active[ targid ] = 1; /* now ready to work */ bp->b_actf = dp->b_actf; /* copy forward link */ dp->b_actf = bp; /* add to the front of the queue */ sc->sc_bbr_state[targid] = BBR_READ; sc->sc_bbr_read[targid] = BBR_COUNT; /* counts decrement */ sc->sc_bbr_oper[targid] = SZ_RW_START; /* signal a normal cmd */ sz_retries[unit] = SZ_RW_RTCNT; /* preset cnt for no retries */ /* Setup the state for the SM and clear active. The SM will scan the queue and find the next available entry to startup. */ sc->sc_xstate[targid] = SZ_NEXT; sc->sc_xevent[targid] = SZ_BEGIN; dp->b_active = 0; /* no longer an active queue */ } else { /* The target is in BBR state. Using the bbr state variables in the sc structure work on what is needed. */ PRINTD( targid, 0x200, ("BBR state: %d\n", sc->sc_bbr_state[targid])); switch( sc->sc_bbr_state[ targid ] ) { case BBR_READ : /* Check the sence key(s) has the block gone from bad to good or worse ? */ if( sc->sc_sns[targid].snskey == SZ_NOSENSE ) { /* Stop BBR things appear to be back to normal. */ sc->sc_bbr_active[ targid ] = 0; /* all done */ break; } if( sc->sc_sns[targid].snskey == SZ_MEDIUMERR ) { /* The block has completly gone bad. Stop any BBR, report the error, logging was disabled in the SM during BBR. */ flags = SZ_HARDERR; scsi_logerr(sc, bp, targid, SZ_ET_DBBR, 0, 0, flags); sc->sc_bbr_active[ targid ] = 0; /* all done */ break; } /* Check the read counts for BBR_READ. If the count is not 0 try the read again. */ if( --sc->sc_bbr_read[targid] != 0 ) { /* Restart the current bp at the front of the queue. */ bp->b_flags = (B_BUSY | B_READ ); /* reload */ bp->b_bcount = 512; /* MAGIC # */ } else { /* Setup the BBR related stuff. The parameter and defect information has already been setup. */ sc->sc_bbr_state[targid] = BBR_REASSIGN; sc->sc_bbr_rawr[targid] = BBR_COUNT; /* counts dec */ sc->sc_bbr_oper[targid] = SZ_SP_START; /* special */ sz_retries[unit] = SZ_SP_RTCNT; /* preset: no retries */ /* Setup the buffer to contain the information needed for the reassign. */ bp->b_comand = SZ_REASSIGN; /* Use the rzspecial flag to fake out the SM and have it use some of the logic for reassign. */ sc->sc_rzspecial[ targid ] = 1; sc->sc_rzparams[ targid ] = sc->sc_bbrparams[ targid ]; } break; case BBR_REASSIGN : /* Clear the rzspecial flag it is used only during the Reassign and should not be around. */ sc->sc_rzspecial[ targid ] = 0; /* Was the reassign successfull? If not fail with an error message. Else go an try the write. */ if( sc->sc_sns[targid].snskey != SZ_NOSENSE ) { flags = SZ_HARDERR; scsi_logerr(sc, bp, targid, SZ_ET_DBBR, 1, 0, flags); sc->sc_bbr_active[ targid ] = 0; /* error out */ } else { /* Setup for the write of the buffered data. */ sc->sc_bbr_state[targid] = BBR_WRITE; sc->sc_bbr_write[targid] = BBR_COUNT; /* counts dec */ sc->sc_bbr_oper[targid] = SZ_RW_START; /* normal cmd */ sz_retries[unit] = SZ_RW_RTCNT; /* preset: 0 retries */ bp->b_flags = ( B_BUSY ); /* write from ker mem */ bp->b_bcount = 512; /* MAGIC # one block */ bp->b_retry = 0; /* no retry in SM */ bp->b_resid = 0; } break; case BBR_WRITE : /* Check the sence key(s) has the block successufully been written to the disk ?. */ if( sc->sc_sns[targid].snskey == SZ_NOSENSE ) { /* Stop BBR the write has completed. The previous buffer on the queue will be re-tried to allow the command to retry. */ flags = SZ_SOFTERR; scsi_logerr(sc, bp, targid, SZ_ET_DBBR, 2, 0, flags); sc->sc_bbr_active[ targid ] = 0; /* all done */ release = 0; /* repeat the command */ break; } /* Check the write counts for BBR_WRITE. If the count is not 0 try the write again. If the write counts have reached zero try to reassign the block again. */ if( --sc->sc_bbr_write[targid] != 0 ) { /* Restart the current bp at the front of the queue. */ bp->b_flags = ( B_BUSY ); /* reload */ bp->b_bcount = 512; /* MAGIC # */ } else { /* Check the reassign/write count. Reset the BBR state to reassign and try again. If the reassign/write counts have also reached zero, stop BBR and report the failure. */ if( --sc->sc_bbr_rawr[targid] != 0 ) { /* Setup the BBR related stuff. The parameter and defect information has already been setup. */ sc->sc_bbr_state[targid] = BBR_REASSIGN; sc->sc_bbr_oper[targid] = SZ_SP_START; /* special cmd */ sz_retries[unit] = SZ_SP_RTCNT; /* preset for 0 retry */ /* Setup the buffer to contain the information needed for the reassign. */ bp->b_comand = SZ_REASSIGN; /* Use the rzspecial flag to fake out the SM and have it use some of the logic for reassign. */ sc->sc_rzspecial[ targid ] = 1; sc->sc_rzparams[ targid ] = sc->sc_bbrparams[ targid ]; } else /* reassign-write count == 0 */ { /* The BBR has failed, report the error and terminate the BBR state. */ flags = SZ_HARDERR; scsi_logerr(sc, bp, targid, SZ_ET_DBBR, 3, 0, flags); sc->sc_bbr_active[targid] = 0; /* stop */ } } break; default: /* something is wrong release the buffer */ sc->sc_bbr_active[ targid ] = 0; break; } } /* Check out the BBR active flag. If the target is still in BBR mode leave the routine. If BBR is done free up the allocated memory and remove the BBR bp and allow the front buffer to be dequeued. If active is set setup the state for the SM. */ if( sc->sc_bbr_active[ targid ] == 0 ) { PRINTD( targid, 0x200, ("BBR done: cleaning up\n")); KM_FREE(sc->sc_bbraddr[targid], KM_DEVBUF); sc->sc_bbr_oper[targid] = 0; /* clear the opcode */ bp = dp->b_actf; /* pop off BBR buffer */ dp->b_actf = bp->b_actf; } else { /* Set the release flag to "no", the state for the command will be setup so that the SM will scan the queue and find the next available entry to startup. The snskey is pre-cleared, it is not cleared with in the SM. */ PRINTD( targid, 0x200, ("BBR active setup for SM\n")); sc->sc_sns[targid].snskey = SZ_NOSENSE; /* to make sure */ release = 0; /* do not remove front buffer */ } /* Return what the completion code needes to do with the existing buffer on the front of the queue. */ if( release == 0 ) { /* Setup the state for the SM and clear b_active. The SM will scan the queue and find the next available entry to startup. The snskey is pre-cleared, it is not cleared with in the SM. */ sc->sc_xstate[targid] = SZ_NEXT; sc->sc_xevent[targid] = SZ_BEGIN; dp->b_active = 0; /* no longer an active queue */ } return( release );}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -