Exemplo n.º 1
0
/*
 * Catch an interrupt from the adaptor
 */
static void
ahbintr(void *arg)
{
	struct	  ahb_softc *ahb;
	u_int	  intstat;
	u_int32_t mbox;

	ahb = (struct ahb_softc *)arg;

	while (ahb_inb(ahb, HOSTSTAT) & HOSTSTAT_INTPEND) {
		/*
		 * Fetch information about this interrupt.
		 */
		intstat = ahb_inb(ahb, INTSTAT);
		mbox = ahb_inl(ahb, MBOXIN0);

		/*
		 * Reset interrupt latch.
		 */
		ahb_outb(ahb, CONTROL, CNTRL_CLRINT);

		/*
		 * Process the completed operation
		 */
		switch (intstat & INTSTAT_MASK) {
		case INTSTAT_ECB_OK:
		case INTSTAT_ECB_CMPWRETRY:
		case INTSTAT_ECB_CMPWERR:
			ahbdone(ahb, mbox, intstat);
			break;
		case INTSTAT_AEN_OCCURED:
			if ((intstat & INTSTAT_TARGET_MASK) == ahb->scsi_id) {
				/* Bus Reset */
				xpt_print_path(ahb->path);
				switch (mbox) {
				case HS_SCSI_RESET_ADAPTER:
					printf("Host Adapter Initiated "
					       "Bus Reset occurred\n");
					break;
				case HS_SCSI_RESET_INCOMING:
					printf("Bus Reset Initiated "
					       "by another device occurred\n");
					break;
				}
				/* Notify the XPT */
				xpt_async(AC_BUS_RESET, ahb->path, NULL);
				break;
			}
			printf("Unsupported initiator selection AEN occured\n");
			break;
		case INTSTAT_IMMED_OK:
		case INTSTAT_IMMED_ERR:
			ahbhandleimmed(ahb, mbox, intstat);
			break;
		case INTSTAT_HW_ERR:
			panic("Unrecoverable hardware Error Occurred\n");
		}
	}
}
Exemplo n.º 2
0
static void
ptdtor(struct cam_periph *periph)
{
	struct pt_softc *softc;

	softc = (struct pt_softc *)periph->softc;

	devstat_remove_entry(&softc->device_stats);

	destroy_dev(softc->dev);

	xpt_print_path(periph->path);
	printf("removing device entry\n");
	free(softc, M_DEVBUF);
}
Exemplo n.º 3
0
static void
ptoninvalidate(struct cam_periph *periph)
{
	int s;
	struct pt_softc *softc;
	struct buf *q_bp;
	struct ccb_setasync csa;

	softc = (struct pt_softc *)periph->softc;

	/*
	 * De-register any async callbacks.
	 */
	xpt_setup_ccb(&csa.ccb_h, periph->path,
		      /* priority */ 5);
	csa.ccb_h.func_code = XPT_SASYNC_CB;
	csa.event_enable = 0;
	csa.callback = ptasync;
	csa.callback_arg = periph;
	xpt_action((union ccb *)&csa);

	softc->flags |= PT_FLAG_DEVICE_INVALID;

	/*
	 * Although the oninvalidate() routines are always called at
	 * splsoftcam, we need to be at splbio() here to keep the buffer
	 * queue from being modified while we traverse it.
	 */
	s = splbio();

	/*
	 * Return all queued I/O with ENXIO.
	 * XXX Handle any transactions queued to the card
	 *     with XPT_ABORT_CCB.
	 */
	while ((q_bp = bufq_first(&softc->buf_queue)) != NULL){
		bufq_remove(&softc->buf_queue, q_bp);
		q_bp->b_resid = q_bp->b_bcount;
		q_bp->b_error = ENXIO;
		q_bp->b_flags |= B_ERROR;
		biodone(q_bp);
	}

	splx(s);

	xpt_print_path(periph->path);
	printf("lost device\n");
}
Exemplo n.º 4
0
void
isp_done(struct ccb_scsiio *sccb)
{
	struct ispsoftc *isp = XS_ISP(sccb);

	if (XS_NOERR(sccb))
		XS_SETERR(sccb, CAM_REQ_CMP);
	sccb->ccb_h.status &= ~CAM_STATUS_MASK;
	sccb->ccb_h.status |= sccb->ccb_h.spriv_field0;
	if ((sccb->ccb_h.status & CAM_STATUS_MASK) == CAM_REQ_CMP &&
	    (sccb->scsi_status != SCSI_STATUS_OK)) {
		sccb->ccb_h.status &= ~CAM_STATUS_MASK;
		sccb->ccb_h.status |= CAM_SCSI_STATUS_ERROR;
	}
	if ((sccb->ccb_h.status & CAM_STATUS_MASK) != CAM_REQ_CMP) {
		if ((sccb->ccb_h.status & CAM_DEV_QFRZN) == 0) {
			IDPRINTF(3, ("%s: freeze devq %d.%d ccbstat 0x%x\n",
			    isp->isp_name, sccb->ccb_h.target_id,
			    sccb->ccb_h.target_lun, sccb->ccb_h.status));
			xpt_freeze_devq(sccb->ccb_h.path, 1);
			sccb->ccb_h.status |= CAM_DEV_QFRZN;
		}
	}
	if (isp->isp_osinfo.simqfrozen & SIMQFRZ_RESOURCE) {
		isp->isp_osinfo.simqfrozen &= ~SIMQFRZ_RESOURCE;
		sccb->ccb_h.status |= CAM_RELEASE_SIMQ;
		xpt_release_simq(isp->isp_sim, 1);
	}
	sccb->ccb_h.status &= ~CAM_SIM_QUEUED;
	if (CAM_DEBUGGED(sccb->ccb_h.path, ISPDDB) &&
	    (sccb->ccb_h.status & CAM_STATUS_MASK) != CAM_REQ_CMP) {
		xpt_print_path(sccb->ccb_h.path);
		printf("cam completion status 0x%x\n", sccb->ccb_h.status);
	}
	xpt_done((union ccb *) sccb);
}
Exemplo n.º 5
0
void
adv_timeout(void *arg)
{
	int s;
	union ccb *ccb;
	struct adv_softc *adv;
	struct adv_ccb_info *cinfo;

	ccb = (union ccb *)arg;
	adv = (struct adv_softc *)xpt_path_sim(ccb->ccb_h.path)->softc;
	cinfo = (struct adv_ccb_info *)ccb->ccb_h.ccb_cinfo_ptr;

	xpt_print_path(ccb->ccb_h.path);
	printf("Timed out\n");

	s = splcam();
	/* Have we been taken care of already?? */
	if (cinfo == NULL || cinfo->state == ACCB_FREE) {
		splx(s);
		return;
	}

	adv_stop_execution(adv);

	if ((cinfo->state & ACCB_ABORT_QUEUED) == 0) {
		struct ccb_hdr *ccb_h;

		/*
		 * In order to simplify the recovery process, we ask the XPT
		 * layer to halt the queue of new transactions and we traverse
		 * the list of pending CCBs and remove their timeouts. This
		 * means that the driver attempts to clear only one error
		 * condition at a time.  In general, timeouts that occur
		 * close together are related anyway, so there is no benefit
		 * in attempting to handle errors in parrallel.  Timeouts will
		 * be reinstated when the recovery process ends.
		 */
		adv_set_state(adv, ADV_IN_TIMEOUT);

		/* This CCB is the CCB representing our recovery actions */
		cinfo->state |= ACCB_RECOVERY_CCB|ACCB_ABORT_QUEUED;

		ccb_h = LIST_FIRST(&adv->pending_ccbs);
		while (ccb_h != NULL) {
			untimeout(adv_timeout, ccb_h, ccb_h->timeout_ch);
			ccb_h = LIST_NEXT(ccb_h, sim_links.le);
		}

		/* XXX Should send a BDR */
		/* Attempt an abort as our first tact */
		xpt_print_path(ccb->ccb_h.path);
		printf("Attempting abort\n");
		adv_abort_ccb(adv, ccb->ccb_h.target_id,
			      ccb->ccb_h.target_lun, ccb,
			      CAM_CMD_TIMEOUT, /*queued_only*/FALSE);
		ccb->ccb_h.timeout_ch =
		    timeout(adv_timeout, ccb, 2 * hz);
	} else {
		/* Our attempt to perform an abort failed, go for a reset */
		xpt_print_path(ccb->ccb_h.path);
		printf("Resetting bus\n");		
		ccb->ccb_h.status &= ~CAM_STATUS_MASK;
		ccb->ccb_h.status |= CAM_CMD_TIMEOUT;
		adv_reset_bus(adv, /*initiate_reset*/TRUE);
	}
	adv_start_execution(adv);
	splx(s);
}
Exemplo n.º 6
0
void
adv_done(struct adv_softc *adv, union ccb *ccb, u_int done_stat,
	 u_int host_stat, u_int scsi_status, u_int q_no)
{
	struct	   adv_ccb_info *cinfo;

	cinfo = (struct adv_ccb_info *)ccb->ccb_h.ccb_cinfo_ptr;
	LIST_REMOVE(&ccb->ccb_h, sim_links.le);
	untimeout(adv_timeout, ccb, ccb->ccb_h.timeout_ch);
	if ((ccb->ccb_h.flags & CAM_DIR_MASK) != CAM_DIR_NONE) {
		bus_dmasync_op_t op;

		if ((ccb->ccb_h.flags & CAM_DIR_MASK) == CAM_DIR_IN)
			op = BUS_DMASYNC_POSTREAD;
		else
			op = BUS_DMASYNC_POSTWRITE;
		bus_dmamap_sync(adv->buffer_dmat, cinfo->dmamap, op);
		bus_dmamap_unload(adv->buffer_dmat, cinfo->dmamap);
	}

	switch (done_stat) {
	case QD_NO_ERROR:
		if (host_stat == QHSTA_NO_ERROR) {
			ccb->ccb_h.status = CAM_REQ_CMP;
			break;
		}
		xpt_print_path(ccb->ccb_h.path);
		printf("adv_done - queue done without error, "
		       "but host status non-zero(%x)\n", host_stat);
		/*FALLTHROUGH*/
	case QD_WITH_ERROR:
		switch (host_stat) {
		case QHSTA_M_TARGET_STATUS_BUSY:
		case QHSTA_M_BAD_QUEUE_FULL_OR_BUSY:
			/*
			 * Assume that if we were a tagged transaction
			 * the target reported queue full.  Otherwise,
			 * report busy.  The firmware really should just
			 * pass the original status back up to us even
			 * if it thinks the target was in error for
			 * returning this status as no other transactions
			 * from this initiator are in effect, but this
			 * ignores multi-initiator setups and there is
			 * evidence that the firmware gets its per-device
			 * transaction counts screwed up occassionally.
			 */
			ccb->ccb_h.status |= CAM_SCSI_STATUS_ERROR;
			if ((ccb->ccb_h.flags & CAM_TAG_ACTION_VALID) != 0
			 && host_stat != QHSTA_M_TARGET_STATUS_BUSY)
				scsi_status = SCSI_STATUS_QUEUE_FULL;
			else
				scsi_status = SCSI_STATUS_BUSY;
			adv_abort_ccb(adv, ccb->ccb_h.target_id,
				      ccb->ccb_h.target_lun,
				      /*ccb*/NULL, CAM_REQUEUE_REQ,
				      /*queued_only*/TRUE);
			/*FALLTHROUGH*/
		case QHSTA_M_NO_AUTO_REQ_SENSE:
		case QHSTA_NO_ERROR:
			ccb->csio.scsi_status = scsi_status;
			switch (scsi_status) {
			case SCSI_STATUS_CHECK_COND:
			case SCSI_STATUS_CMD_TERMINATED:
				ccb->ccb_h.status |= CAM_AUTOSNS_VALID;
				/* Structure copy */
				ccb->csio.sense_data =
				    adv->sense_buffers[q_no - 1];
				/* FALLTHROUGH */
			case SCSI_STATUS_BUSY:
			case SCSI_STATUS_RESERV_CONFLICT:
			case SCSI_STATUS_QUEUE_FULL:
			case SCSI_STATUS_COND_MET:
			case SCSI_STATUS_INTERMED:
			case SCSI_STATUS_INTERMED_COND_MET:
				ccb->ccb_h.status |= CAM_SCSI_STATUS_ERROR;
				break;
			case SCSI_STATUS_OK:
				ccb->ccb_h.status |= CAM_REQ_CMP;
				break;
			}
			break;
		case QHSTA_M_SEL_TIMEOUT:
			ccb->ccb_h.status = CAM_SEL_TIMEOUT;
			break;
		case QHSTA_M_DATA_OVER_RUN:
			ccb->ccb_h.status = CAM_DATA_RUN_ERR;
			break;
		case QHSTA_M_UNEXPECTED_BUS_FREE:
			ccb->ccb_h.status = CAM_UNEXP_BUSFREE;
			break;
		case QHSTA_M_BAD_BUS_PHASE_SEQ:
			ccb->ccb_h.status = CAM_SEQUENCE_FAIL;
			break;
		case QHSTA_M_BAD_CMPL_STATUS_IN:
			/* No command complete after a status message */
			ccb->ccb_h.status = CAM_SEQUENCE_FAIL;
			break;
		case QHSTA_D_EXE_SCSI_Q_BUSY_TIMEOUT:
		case QHSTA_M_WTM_TIMEOUT:
		case QHSTA_M_HUNG_REQ_SCSI_BUS_RESET:
			/* The SCSI bus hung in a phase */
			ccb->ccb_h.status = CAM_SEQUENCE_FAIL;
			adv_reset_bus(adv, /*initiate_reset*/TRUE);
			break;
		case QHSTA_M_AUTO_REQ_SENSE_FAIL:
			ccb->ccb_h.status = CAM_AUTOSENSE_FAIL;
			break;
		case QHSTA_D_QDONE_SG_LIST_CORRUPTED:
		case QHSTA_D_ASC_DVC_ERROR_CODE_SET:
		case QHSTA_D_HOST_ABORT_FAILED:
		case QHSTA_D_EXE_SCSI_Q_FAILED:
		case QHSTA_D_ASPI_NO_BUF_POOL:
		case QHSTA_M_BAD_TAG_CODE:
		case QHSTA_D_LRAM_CMP_ERROR:
		case QHSTA_M_MICRO_CODE_ERROR_HALT:
		default:
			panic("%s: Unhandled Host status error %x",
			      adv_name(adv), host_stat);
			/* NOTREACHED */
		}
		break;

	case QD_ABORTED_BY_HOST:
		/* Don't clobber any, more explicit, error codes we've set */
		if ((ccb->ccb_h.status & CAM_STATUS_MASK) == CAM_REQ_INPROG)
			ccb->ccb_h.status = CAM_REQ_ABORTED;
		break;

	default:
		xpt_print_path(ccb->ccb_h.path);
		printf("adv_done - queue done with unknown status %x:%x\n",
		       done_stat, host_stat);
		ccb->ccb_h.status = CAM_REQ_CMP_ERR;
		break;
	}
	adv_clear_state(adv, ccb);
	if ((ccb->ccb_h.status & CAM_STATUS_MASK) != CAM_REQ_CMP
	 && (ccb->ccb_h.status & CAM_DEV_QFRZN) == 0) {
		xpt_freeze_devq(ccb->ccb_h.path, /*count*/1);
		ccb->ccb_h.status |= CAM_DEV_QFRZN;
	}
	adv_free_ccb_info(adv, cinfo);
	/*
	 * Null this out so that we catch driver bugs that cause a
	 * ccb to be completed twice.
	 */
	ccb->ccb_h.ccb_cinfo_ptr = NULL;
	ccb->ccb_h.status &= ~CAM_SIM_QUEUED;
	xpt_done(ccb);
}
Exemplo n.º 7
0
static void
ahatimeout(void *arg)
{
	struct aha_ccb	*accb;
	union  ccb	*ccb;
	struct aha_softc *aha;
	uint32_t	paddr;
	struct ccb_hdr *ccb_h;

	accb = (struct aha_ccb *)arg;
	ccb = accb->ccb;
	aha = (struct aha_softc *)ccb->ccb_h.ccb_aha_ptr;
	mtx_assert(&aha->lock, MA_OWNED);
	xpt_print_path(ccb->ccb_h.path);
	printf("CCB %p - timed out\n", (void *)accb);

	if ((accb->flags & ACCB_ACTIVE) == 0) {
		xpt_print_path(ccb->ccb_h.path);
		printf("CCB %p - timed out CCB already completed\n",
		    (void *)accb);
		return;
	}

	/*
	 * In order to simplify the recovery process, we ask the XPT
	 * layer to halt the queue of new transactions and we traverse
	 * the list of pending CCBs and remove their timeouts. This
	 * means that the driver attempts to clear only one error
	 * condition at a time.  In general, timeouts that occur
	 * close together are related anyway, so there is no benefit
	 * in attempting to handle errors in parallel.  Timeouts will
	 * be reinstated when the recovery process ends.
	 */
	if ((accb->flags & ACCB_DEVICE_RESET) == 0) {
		if ((accb->flags & ACCB_RELEASE_SIMQ) == 0) {
			xpt_freeze_simq(aha->sim, /*count*/1);
			accb->flags |= ACCB_RELEASE_SIMQ;
		}

		ccb_h = LIST_FIRST(&aha->pending_ccbs);
		while (ccb_h != NULL) {
			struct aha_ccb *pending_accb;

			pending_accb = (struct aha_ccb *)ccb_h->ccb_accb_ptr;
			callout_stop(&pending_accb->timer);
			ccb_h = LIST_NEXT(ccb_h, sim_links.le);
		}
	}

	if ((accb->flags & ACCB_DEVICE_RESET) != 0
	 || aha->cur_outbox->action_code != AMBO_FREE) {
		/*
		 * Try a full host adapter/SCSI bus reset.
		 * We do this only if we have already attempted
		 * to clear the condition with a BDR, or we cannot
		 * attempt a BDR for lack of mailbox resources.
		 */
		ccb->ccb_h.status = CAM_CMD_TIMEOUT;
		ahareset(aha, /*hardreset*/TRUE);
		device_printf(aha->dev, "No longer in timeout\n");
	} else {
		/*
		 * Send a Bus Device Reset message:
		 * The target that is holding up the bus may not
		 * be the same as the one that triggered this timeout
		 * (different commands have different timeout lengths),
		 * but we have no way of determining this from our
		 * timeout handler.  Our strategy here is to queue a
		 * BDR message to the target of the timed out command.
		 * If this fails, we'll get another timeout 2 seconds
		 * later which will attempt a bus reset.
		 */
		accb->flags |= ACCB_DEVICE_RESET;
		callout_reset(&accb->timer, 2 * hz, ahatimeout, accb);
		aha->recovery_accb->hccb.opcode = INITIATOR_BUS_DEV_RESET;

		/* No Data Transfer */
		aha->recovery_accb->hccb.datain = TRUE;
		aha->recovery_accb->hccb.dataout = TRUE;
		aha->recovery_accb->hccb.ahastat = 0;
		aha->recovery_accb->hccb.sdstat = 0;
		aha->recovery_accb->hccb.target = ccb->ccb_h.target_id;

		/* Tell the adapter about this command */
		paddr = ahaccbvtop(aha, aha->recovery_accb);
		ahautoa24(paddr, aha->cur_outbox->ccb_addr);
		aha->cur_outbox->action_code = AMBO_START;
		aha_outb(aha, COMMAND_REG, AOP_START_MBOX);
		ahanextoutbox(aha);
	}
}
Exemplo n.º 8
0
static void
ptdone(struct cam_periph *periph, union ccb *done_ccb)
{
	struct pt_softc *softc;
	struct ccb_scsiio *csio;

	softc = (struct pt_softc *)periph->softc;
	csio = &done_ccb->csio;
	switch (csio->ccb_h.ccb_state) {
	case PT_CCB_BUFFER_IO:
	case PT_CCB_BUFFER_IO_UA:
	{
		struct bio *bp;
		int    oldspl;

		bp = (struct bio *)done_ccb->ccb_h.ccb_bp;
		if ((done_ccb->ccb_h.status & CAM_STATUS_MASK) != CAM_REQ_CMP) {
			int error;
			int s;
			int sf;
			
			if ((csio->ccb_h.ccb_state & PT_CCB_RETRY_UA) != 0)
				sf = SF_RETRY_UA;
			else
				sf = 0;

			error = pterror(done_ccb, CAM_RETRY_SELTO, sf);
			if (error == ERESTART) {
				/*
				 * A retry was scheuled, so
				 * just return.
				 */
				return;
			}
			if (error != 0) {
				struct bio *q_bp;

				s = splbio();

				if (error == ENXIO) {
					/*
					 * Catastrophic error.  Mark our device
					 * as invalid.
					 */
					xpt_print_path(periph->path);
					printf("Invalidating device\n");
					softc->flags |= PT_FLAG_DEVICE_INVALID;
				}

				/*
				 * return all queued I/O with EIO, so that
				 * the client can retry these I/Os in the
				 * proper order should it attempt to recover.
				 */
				while ((q_bp = bioq_first(&softc->bio_queue))
					!= NULL) {
					bioq_remove(&softc->bio_queue, q_bp);
					q_bp->bio_resid = q_bp->bio_bcount;
					biofinish(q_bp, NULL, EIO);
				}
				splx(s);
				bp->bio_error = error;
				bp->bio_resid = bp->bio_bcount;
				bp->bio_flags |= BIO_ERROR;
			} else {
				bp->bio_resid = csio->resid;
				bp->bio_error = 0;
				if (bp->bio_resid != 0) {
					/* Short transfer ??? */
					bp->bio_flags |= BIO_ERROR;
				}
			}
			if ((done_ccb->ccb_h.status & CAM_DEV_QFRZN) != 0)
				cam_release_devq(done_ccb->ccb_h.path,
						 /*relsim_flags*/0,
						 /*reduction*/0,
						 /*timeout*/0,
						 /*getcount_only*/0);
		} else {
			bp->bio_resid = csio->resid;
			if (bp->bio_resid != 0)
				bp->bio_flags |= BIO_ERROR;
		}

		/*
		 * Block out any asyncronous callbacks
		 * while we touch the pending ccb list.
		 */
		oldspl = splcam();
		LIST_REMOVE(&done_ccb->ccb_h, periph_links.le);
		splx(oldspl);

		biofinish(bp, &softc->device_stats, 0);
		break;
	}
	case PT_CCB_WAITING:
		/* Caller will release the CCB */
		wakeup(&done_ccb->ccb_h.cbfcnp);
		return;
	}
	xpt_release_ccb(done_ccb);
}
Exemplo n.º 9
0
static void
ahbprocesserror(struct ahb_softc *ahb, struct ecb *ecb, union ccb *ccb)
{
	struct hardware_ecb *hecb;
	struct ecb_status *status;

	hecb = &ecb->hecb;
	status = &ecb->status;
	switch (status->ha_status) {
	case HS_OK:
		ccb->csio.scsi_status = status->scsi_status;
		if (status->scsi_status != 0) {
			ccb->ccb_h.status = CAM_SCSI_STATUS_ERROR;
			if (status->sense_stored) {
				ccb->ccb_h.status |= CAM_AUTOSNS_VALID;
				ccb->csio.sense_resid =
				    ccb->csio.sense_len - status->sense_len;
				bcopy(&ecb->sense, &ccb->csio.sense_data,
				      status->sense_len);
			}
		}
		break;
	case HS_TARGET_NOT_ASSIGNED:
		ccb->ccb_h.status = CAM_PATH_INVALID;
		break;
	case HS_SEL_TIMEOUT:
		ccb->ccb_h.status = CAM_SEL_TIMEOUT;
		break;
	case HS_DATA_RUN_ERR:
		ahbcalcresid(ahb, ecb, ccb);
		break;
	case HS_UNEXPECTED_BUSFREE:
		ccb->ccb_h.status = CAM_UNEXP_BUSFREE;
		break;
	case HS_INVALID_PHASE:
		ccb->ccb_h.status = CAM_SEQUENCE_FAIL;
		break;
	case HS_REQUEST_SENSE_FAILED:
		ccb->ccb_h.status = CAM_AUTOSENSE_FAIL;
		break;
	case HS_TAG_MSG_REJECTED:
	{
		struct ccb_trans_settings neg; 
		struct ccb_trans_settings_scsi *scsi = &neg.proto_specific.scsi;

		xpt_print_path(ccb->ccb_h.path);
		printf("refuses tagged commands.  Performing "
		       "non-tagged I/O\n");
		memset(&neg, 0, sizeof (neg));
		neg.protocol = PROTO_SCSI;
		neg.protocol_version = SCSI_REV_2;
		neg.transport = XPORT_SPI;
		neg.transport_version = 2;
		scsi->flags = CTS_SCSI_VALID_TQ;
		xpt_setup_ccb(&neg.ccb_h, ccb->ccb_h.path, /*priority*/1); 
		xpt_async(AC_TRANSFER_NEG, ccb->ccb_h.path, &neg);
		ahb->tags_permitted &= ~(0x01 << ccb->ccb_h.target_id);
		ccb->ccb_h.status = CAM_MSG_REJECT_REC;
		break;
	}
	case HS_FIRMWARE_LOAD_REQ:
	case HS_HARDWARE_ERR:
		/*
		 * Tell the system that the Adapter
		 * is no longer functional.
		 */
		ccb->ccb_h.status = CAM_NO_HBA;
		break;
	case HS_CMD_ABORTED_HOST:
	case HS_CMD_ABORTED_ADAPTER:
	case HS_ATN_TARGET_FAILED:
	case HS_SCSI_RESET_ADAPTER:
	case HS_SCSI_RESET_INCOMING:
		ccb->ccb_h.status = CAM_SCSI_BUS_RESET;
		break;
	case HS_INVALID_ECB_PARAM:
		printf("ahb%ld: opcode 0x%02x, flag_word1 0x%02x, flag_word2 0x%02x\n",
			ahb->unit, hecb->opcode, hecb->flag_word1, hecb->flag_word2);	
		ccb->ccb_h.status = CAM_SCSI_BUS_RESET;
		break;
	case HS_DUP_TCB_RECEIVED:
	case HS_INVALID_OPCODE:
	case HS_INVALID_CMD_LINK:
	case HS_PROGRAM_CKSUM_ERROR:
		panic("ahb%ld: Can't happen host status %x occurred",
		      ahb->unit, status->ha_status);
		break;
	}
	if (ccb->ccb_h.status != CAM_REQ_CMP) {
		xpt_freeze_devq(ccb->ccb_h.path, /*count*/1);
		ccb->ccb_h.status |= CAM_DEV_QFRZN;
	}
}
Exemplo n.º 10
0
static void
ahbtimeout(void *arg)
{
	struct ecb	 *ecb;
	union  ccb	 *ccb;
	struct ahb_softc *ahb;
	int		  s;

	ecb = (struct ecb *)arg;
	ccb = ecb->ccb;
	ahb = (struct ahb_softc *)ccb->ccb_h.ccb_ahb_ptr;
	xpt_print_path(ccb->ccb_h.path);
	printf("ECB %p - timed out\n", (void *)ecb);

	s = splcam();

	if ((ecb->state & ECB_ACTIVE) == 0) {
		xpt_print_path(ccb->ccb_h.path);
		printf("ECB %p - timed out ECB already completed\n",
		       (void *)ecb);
		splx(s);
		return;
	}
	/*
	 * In order to simplify the recovery process, we ask the XPT
	 * layer to halt the queue of new transactions and we traverse
	 * the list of pending CCBs and remove their timeouts. This
	 * means that the driver attempts to clear only one error
	 * condition at a time.  In general, timeouts that occur
	 * close together are related anyway, so there is no benefit
	 * in attempting to handle errors in parrallel.  Timeouts will
	 * be reinstated when the recovery process ends.
	 */
	if ((ecb->state & ECB_DEVICE_RESET) == 0) {
		struct ccb_hdr *ccb_h;

		if ((ecb->state & ECB_RELEASE_SIMQ) == 0) {
			xpt_freeze_simq(ahb->sim, /*count*/1);
			ecb->state |= ECB_RELEASE_SIMQ;
		}

		ccb_h = LIST_FIRST(&ahb->pending_ccbs);
		while (ccb_h != NULL) {
			struct ecb *pending_ecb;

			pending_ecb = (struct ecb *)ccb_h->ccb_ecb_ptr;
			untimeout(ahbtimeout, pending_ecb, ccb_h->timeout_ch);
			ccb_h = LIST_NEXT(ccb_h, sim_links.le);
		}

		/* Store for our interrupt handler */
		ahb->immed_ecb = ecb;

		/*    
		 * Send a Bus Device Reset message:
		 * The target that is holding up the bus may not
		 * be the same as the one that triggered this timeout
		 * (different commands have different timeout lengths),
		 * but we have no way of determining this from our
		 * timeout handler.  Our strategy here is to queue a
		 * BDR message to the target of the timed out command.
		 * If this fails, we'll get another timeout 2 seconds
		 * later which will attempt a bus reset.
		 */
		xpt_print_path(ccb->ccb_h.path);
		printf("Queuing BDR\n");
		ecb->state |= ECB_DEVICE_RESET;
		ccb->ccb_h.timeout_ch =
		    timeout(ahbtimeout, (caddr_t)ecb, 2 * hz);

		ahb->immed_cmd = IMMED_RESET;
		ahbqueuembox(ahb, IMMED_RESET, ATTN_IMMED|ccb->ccb_h.target_id);
	} else if ((ecb->state & ECB_SCSIBUS_RESET) != 0) {
		/*
		 * Try a SCSI bus reset.  We do this only if we
		 * have already attempted to clear the condition with a BDR.
		 */
		xpt_print_path(ccb->ccb_h.path);
		printf("Attempting SCSI Bus reset\n");
		ecb->state |= ECB_SCSIBUS_RESET;
		ccb->ccb_h.timeout_ch =
		    timeout(ahbtimeout, (caddr_t)ecb, 2 * hz);
		ahb->immed_cmd = IMMED_RESET;
		ahbqueuembox(ahb, IMMED_RESET, ATTN_IMMED|ahb->scsi_id);
	} else {
		/* Bring out the hammer... */
		ahbreset(ahb);

		/* Simulate the reset complete interrupt */
		ahbhandleimmed(ahb, 0, ahb->scsi_id|INTSTAT_IMMED_OK);
	}

	splx(s);
}