From 7457cf2d463eee53035012c38b34afa40bf3087a Mon Sep 17 00:00:00 2001 From: "Justin T. Gibbs" Date: Mon, 16 Aug 1999 22:49:29 +0000 Subject: [PATCH] Add support for issuing immediate notify event ccbs for bus resets, bdr messages, abort messages, and abort tag messages. Fix a bug in how default transfer negotiations are handled if the user had disabled initial bus resets. Support multi-targetid on the aic7895C. --- sys/dev/aic7xxx/aic7xxx.c | 564 +++++++++++++++++++++++++++++--------- sys/dev/aic7xxx/aic7xxx.h | 24 +- 2 files changed, 451 insertions(+), 137 deletions(-) diff --git a/sys/dev/aic7xxx/aic7xxx.c b/sys/dev/aic7xxx/aic7xxx.c index 9f050a73526c..7acab866b39d 100644 --- a/sys/dev/aic7xxx/aic7xxx.c +++ b/sys/dev/aic7xxx/aic7xxx.c @@ -36,7 +36,7 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * $Id: aic7xxx.c,v 1.30 1999/05/22 22:04:07 gibbs Exp $ + * $Id: aic7xxx.c,v 1.31 1999/05/23 18:55:58 gibbs Exp $ */ /* * A few notes on features of the driver. @@ -171,7 +171,7 @@ typedef enum { ROLE_UNKNOWN, ROLE_INITIATOR, - ROLE_TARGET, + ROLE_TARGET } role_t; struct ahc_devinfo { @@ -260,8 +260,13 @@ static void ahc_clear_msg_state(struct ahc_softc *ahc); static void ahc_handle_message_phase(struct ahc_softc *ahc, struct cam_path *path); static int ahc_sent_msg(struct ahc_softc *ahc, u_int msgtype, int full); -static int ahc_parse_msg(struct ahc_softc *ahc, struct cam_path *path, - struct ahc_devinfo *devinfo); +typedef enum { + MSGLOOP_IN_PROG, + MSGLOOP_MSGCOMPLETE, + MSGLOOP_TERMINATED +} msg_loop_stat; +static int ahc_parse_msg(struct ahc_softc *ahc, struct cam_path *path, + struct ahc_devinfo *devinfo); static void ahc_handle_ign_wide_residue(struct ahc_softc *ahc, struct ahc_devinfo *devinfo); static void ahc_handle_devreset(struct ahc_softc *ahc, @@ -276,19 +281,20 @@ static int ahc_check_patch(struct ahc_softc *ahc, static void ahc_download_instr(struct ahc_softc *ahc, int instrptr, u_int8_t *dconsts); static int ahc_match_scb(struct scb *scb, int target, char channel, - int lun, u_int tag); + int lun, u_int tag, role_t role); #ifdef AHC_DEBUG static void ahc_print_scb(struct scb *scb); #endif static int ahc_search_qinfifo(struct ahc_softc *ahc, int target, char channel, int lun, u_int tag, - u_int32_t status, ahc_search_action action); + role_t role, u_int32_t status, + ahc_search_action action); static void ahc_abort_ccb(struct ahc_softc *ahc, struct cam_sim *sim, union ccb *ccb); static int ahc_reset_channel(struct ahc_softc *ahc, char channel, int initiate_reset); static int ahc_abort_scbs(struct ahc_softc *ahc, int target, - char channel, int lun, u_int tag, + char channel, int lun, u_int tag, role_t role, u_int32_t status); static int ahc_search_disc_list(struct ahc_softc *ahc, int target, char channel, int lun, u_int tag); @@ -340,6 +346,12 @@ static void ahc_set_recoveryscb(struct ahc_softc *ahc, struct scb *scb); static timeout_t ahc_timeout; +static void ahc_queue_lstate_event(struct ahc_softc *ahc, + struct tmode_lstate *lstate, + u_int initiator_id, u_int event_type, + u_int event_arg); +static void ahc_send_lstate_events(struct ahc_softc *ahc, + struct tmode_lstate *lstate); static __inline int sequencer_paused(struct ahc_softc *ahc); static __inline void pause_sequencer(struct ahc_softc *ahc); static __inline void unpause_sequencer(struct ahc_softc *ahc, @@ -355,6 +367,7 @@ static __inline cam_status ahc_ccb_status(union ccb* ccb); static __inline void ahcsetccbstatus(union ccb* ccb, cam_status status); static __inline void ahc_run_tqinfifo(struct ahc_softc *ahc); +static __inline void ahc_run_qoutfifo(struct ahc_softc *ahc); static __inline struct ahc_initiator_tinfo * ahc_fetch_transinfo(struct ahc_softc *ahc, @@ -498,6 +511,39 @@ ahc_run_tqinfifo(struct ahc_softc *ahc) } } +static __inline void +ahc_run_qoutfifo(struct ahc_softc *ahc) +{ + struct scb *scb; + u_int scb_index; + + while (ahc->qoutfifo[ahc->qoutfifonext] != SCB_LIST_NULL) { + scb_index = ahc->qoutfifo[ahc->qoutfifonext]; + ahc->qoutfifo[ahc->qoutfifonext++] = SCB_LIST_NULL; + + scb = &ahc->scb_data->scbarray[scb_index]; + if (scb_index >= ahc->scb_data->numscbs + || (scb->flags & SCB_ACTIVE) == 0) { + printf("%s: WARNING no command for scb %d " + "(cmdcmplt)\nQOUTPOS = %d\n", + ahc_name(ahc), scb_index, + ahc->qoutfifonext - 1); + continue; + } + + /* + * Save off the residual + * if there is one. + */ + if (scb->hscb->residual_SG_count != 0) + ahc_calc_residual(scb); + else + scb->ccb->csio.resid = 0; + ahc_done(ahc, scb); + } +} + + /* * An scb (and hence an scb entry on the board) is put onto the * free list. @@ -779,7 +825,7 @@ ahcinitscbdata(struct ahc_softc *ahc) */ /* DMA tag for our hardware scb structures */ - if (bus_dma_tag_create(ahc->parent_dmat, /*alignment*/0, /*boundary*/0, + if (bus_dma_tag_create(ahc->parent_dmat, /*alignment*/1, /*boundary*/0, /*lowaddr*/BUS_SPACE_MAXADDR, /*highaddr*/BUS_SPACE_MAXADDR, /*filter*/NULL, /*filterarg*/NULL, @@ -809,7 +855,7 @@ ahcinitscbdata(struct ahc_softc *ahc) scb_data->init_level++; /* DMA tag for our sense buffers */ - if (bus_dma_tag_create(ahc->parent_dmat, /*alignment*/0, /*boundary*/0, + if (bus_dma_tag_create(ahc->parent_dmat, /*alignment*/1, /*boundary*/0, /*lowaddr*/BUS_SPACE_MAXADDR, /*highaddr*/BUS_SPACE_MAXADDR, /*filter*/NULL, /*filterarg*/NULL, @@ -839,7 +885,7 @@ ahcinitscbdata(struct ahc_softc *ahc) scb_data->init_level++; /* DMA tag for our S/G structures. We allocate in page sized chunks */ - if (bus_dma_tag_create(ahc->parent_dmat, /*alignment*/0, /*boundary*/0, + if (bus_dma_tag_create(ahc->parent_dmat, /*alignment*/1, /*boundary*/0, /*lowaddr*/BUS_SPACE_MAXADDR, /*highaddr*/BUS_SPACE_MAXADDR, /*filter*/NULL, /*filterarg*/NULL, @@ -1600,33 +1646,8 @@ ahc_intr(void *arg) #endif if (intstat & CMDCMPLT) { - struct scb *scb; - u_int scb_index; - ahc_outb(ahc, CLRINT, CLRCMDINT); - while (ahc->qoutfifo[ahc->qoutfifonext] != SCB_LIST_NULL) { - scb_index = ahc->qoutfifo[ahc->qoutfifonext]; - ahc->qoutfifo[ahc->qoutfifonext++] = SCB_LIST_NULL; - - scb = &ahc->scb_data->scbarray[scb_index]; - if (scb_index >= ahc->scb_data->numscbs - || (scb->flags & SCB_ACTIVE) == 0) { - printf("%s: WARNING no command for scb %d " - "(cmdcmplt)\nQOUTPOS = %d\n", - ahc_name(ahc), scb_index, - ahc->qoutfifonext - 1); - continue; - } - - /* - * Save off the residual - * if there is one. - */ - if (scb->hscb->residual_SG_count != 0) - ahc_calc_residual(scb); - ahc_done(ahc, scb); - } - + ahc_run_qoutfifo(ahc); if ((ahc->flags & AHC_TARGETMODE) != 0) { ahc_run_tqinfifo(ahc); } @@ -1649,7 +1670,8 @@ ahc_intr(void *arg) /* Tell everyone that this HBA is no longer availible */ ahc_abort_scbs(ahc, CAM_TARGET_WILDCARD, ALL_CHANNELS, - CAM_LUN_WILDCARD, SCB_LIST_NULL, CAM_NO_HBA); + CAM_LUN_WILDCARD, SCB_LIST_NULL, ROLE_UNKNOWN, + CAM_NO_HBA); } if (intstat & SEQINT) ahc_handle_seqint(ahc, intstat); @@ -1756,6 +1778,7 @@ ahc_handle_en_lun(struct ahc_softc *ahc, struct cam_sim *sim, union ccb *ccb) /* Are we already enabled?? */ if (lstate != NULL) { + xpt_print_path(ccb->ccb_h.path); printf("Lun already enabled\n"); ccb->ccb_h.status = CAM_LUN_ALRDY_ENA; return; @@ -1779,6 +1802,7 @@ ahc_handle_en_lun(struct ahc_softc *ahc, struct cam_sim *sim, union ccb *ccb) if (target != CAM_TARGET_WILDCARD && tstate == NULL) { tstate = ahc_alloc_tstate(ahc, target, channel); if (tstate == NULL) { + xpt_print_path(ccb->ccb_h.path); printf("Couldn't allocate tstate\n"); ccb->ccb_h.status = CAM_RESRC_UNAVAIL; return; @@ -1786,11 +1810,23 @@ ahc_handle_en_lun(struct ahc_softc *ahc, struct cam_sim *sim, union ccb *ccb) } lstate = malloc(sizeof(*lstate), M_DEVBUF, M_NOWAIT); if (lstate == NULL) { + xpt_print_path(ccb->ccb_h.path); printf("Couldn't allocate lstate\n"); ccb->ccb_h.status = CAM_RESRC_UNAVAIL; return; } bzero(lstate, sizeof(*lstate)); + status = xpt_create_path(&lstate->path, /*periph*/NULL, + xpt_path_path_id(ccb->ccb_h.path), + xpt_path_target_id(ccb->ccb_h.path), + xpt_path_lun_id(ccb->ccb_h.path)); + if (status != CAM_REQ_CMP) { + free(lstate, M_DEVBUF); + xpt_print_path(ccb->ccb_h.path); + printf("Couldn't allocate path\n"); + ccb->ccb_h.status = CAM_RESRC_UNAVAIL; + return; + } SLIST_INIT(&lstate->accept_tios); SLIST_INIT(&lstate->immed_notifies); s = splcam(); @@ -1896,6 +1932,7 @@ ahc_handle_en_lun(struct ahc_softc *ahc, struct cam_sim *sim, union ccb *ccb) xpt_print_path(ccb->ccb_h.path); printf("Target mode disabled\n"); + xpt_free_path(lstate->path); free(lstate, M_DEVBUF); pause_sequencer(ahc); @@ -2224,6 +2261,8 @@ ahc_handle_seqint(struct ahc_softc *ahc, u_int intstat) */ if (hscb->residual_SG_count != 0) ahc_calc_residual(scb); + else + scb->ccb->csio.resid = 0; #ifdef AHC_DEBUG if (ahc_debug & AHC_SHOWSENSE) { @@ -2313,14 +2352,6 @@ ahc_handle_seqint(struct ahc_softc *ahc, u_int intstat) ahc_inb(ahc, SCB_DATACNT)); break; } - case TARGET_MSG_HELP: - { - /* - * XXX Handle BDR, Abort, Abort Tag, and transfer negotiations. - */ - restart_sequencer(ahc); - return; - } case HOST_MSG_LOOP: { /* @@ -2337,9 +2368,18 @@ ahc_handle_seqint(struct ahc_softc *ahc, u_int intstat) u_int bus_phase; bus_phase = ahc_inb(ahc, SCSISIGI) & PHASE_MASK; - if (bus_phase != P_MESGIN && bus_phase != P_MESGOUT) - panic("ahc_intr: HOST_MSG_LOOP bad phase 0x%x", + if (bus_phase != P_MESGIN + && bus_phase != P_MESGOUT) { + printf("ahc_intr: HOST_MSG_LOOP bad " + "phase 0x%x\n", bus_phase); + /* + * Probably transitioned to bus free before + * we got here. Just punt the message. + */ + ahc_clear_intstat(ahc); + restart_sequencer(ahc); + } if (devinfo.role == ROLE_INITIATOR) { struct scb *scb; @@ -2572,10 +2612,12 @@ ahc_handle_scsiint(struct ahc_softc *ahc, u_int intstat) tag = SCB_LIST_NULL; ahc_abort_scbs(ahc, target, channel, SCB_LUN(scb), tag, + ROLE_INITIATOR, CAM_UNEXP_BUSFREE); } else { ahc_abort_scbs(ahc, target, channel, CAM_LUN_WILDCARD, SCB_LIST_NULL, + ROLE_INITIATOR, CAM_UNEXP_BUSFREE); printf("%s: ", ahc_name(ahc)); } @@ -2923,7 +2965,8 @@ ahc_handle_msg_reject(struct ahc_softc *ahc, struct ahc_devinfo *devinfo) */ ahc_search_qinfifo(ahc, SCB_TARGET(scb), SCB_CHANNEL(scb), SCB_LUN(scb), /*tag*/SCB_LIST_NULL, - CAM_REQUEUE_REQ, SEARCH_COMPLETE); + ROLE_INITIATOR, CAM_REQUEUE_REQ, + SEARCH_COMPLETE); } else { /* * Otherwise, we ignore it. @@ -3137,13 +3180,23 @@ ahc_handle_message_phase(struct ahc_softc *ahc, struct cam_path *path) ahc_outb(ahc, SXFRCTL0, ahc_inb(ahc, SXFRCTL0) & ~SPIOEN); ahc->msgin_buf[ahc->msgin_index] = ahc_inb(ahc, SCSIDATL); msgdone = ahc_parse_msg(ahc, path, &devinfo); + if (msgdone == MSGLOOP_TERMINATED) { + /* + * The message is *really* done in that it caused + * us to go to bus free. The sequencer has already + * been reset at this point, so pull the ejection + * handle. + */ + return; + } + ahc->msgin_index++; /* * XXX Read spec about initiator dropping ATN too soon * and use msgdone to detect it. */ - if (msgdone) { + if (msgdone == MSGLOOP_MSGCOMPLETE) { ahc->msgin_index = 0; /* @@ -3236,7 +3289,7 @@ ahc_parse_msg(struct ahc_softc *ahc, struct cam_path *path, int response; u_int targ_scsirate; - done = FALSE; + done = MSGLOOP_IN_PROG; response = FALSE; reject = FALSE; tinfo = ahc_fetch_transinfo(ahc, devinfo->channel, devinfo->our_scsiid, @@ -3259,7 +3312,7 @@ ahc_parse_msg(struct ahc_softc *ahc, struct cam_path *path, response = ahc_handle_msg_reject(ahc, devinfo); /* FALLTHROUGH */ case MSG_NOOP: - done = TRUE; + done = MSGLOOP_MSGCOMPLETE; break; case MSG_IGN_WIDE_RESIDUE: { @@ -3268,7 +3321,7 @@ ahc_parse_msg(struct ahc_softc *ahc, struct cam_path *path, if (ahc->msgin_buf[1] != 1 || tinfo->current.width == MSG_EXT_WDTR_BUS_8_BIT) { reject = TRUE; - done = TRUE; + done = MSGLOOP_MSGCOMPLETE; } else ahc_handle_ign_wide_residue(ahc, devinfo); } @@ -3335,7 +3388,7 @@ ahc_parse_msg(struct ahc_softc *ahc, struct cam_path *path, ahc->msgout_index = 0; response = TRUE; } - done = TRUE; + done = MSGLOOP_MSGCOMPLETE; break; } case MSG_EXT_WDTR: @@ -3451,7 +3504,7 @@ ahc_parse_msg(struct ahc_softc *ahc, struct cam_path *path, response = TRUE; } } - done = TRUE; + done = MSGLOOP_MSGCOMPLETE; break; } default: @@ -3461,15 +3514,45 @@ ahc_parse_msg(struct ahc_softc *ahc, struct cam_path *path, } break; } - case MSG_ABORT: - case MSG_ABORT_TAG: case MSG_BUS_DEV_RESET: - case MSG_CLEAR_QUEUE: - case MSG_TERM_IO_PROC: - /* Target mode messages */ - if (devinfo->role != ROLE_TARGET) - reject = TRUE; + ahc_handle_devreset(ahc, devinfo, + CAM_BDR_SENT, AC_SENT_BDR, + "Bus Device Reset Received", + /*verbose_only*/FALSE); + restart_sequencer(ahc); + done = MSGLOOP_TERMINATED; break; + case MSG_ABORT_TAG: + case MSG_ABORT: + case MSG_CLEAR_QUEUE: + /* Target mode messages */ + if (devinfo->role != ROLE_TARGET) { + reject = TRUE; + break; + } + ahc_abort_scbs(ahc, devinfo->target, devinfo->channel, + devinfo->lun, + ahc->msgin_buf[0] == MSG_ABORT_TAG + ? SCB_LIST_NULL + : ahc_inb(ahc, INITIATOR_TAG), + ROLE_TARGET, CAM_REQ_ABORTED); + + tstate = ahc->enabled_targets[devinfo->our_scsiid]; + if (tstate != NULL) { + struct tmode_lstate* lstate; + + lstate = tstate->enabled_luns[devinfo->lun]; + if (lstate != NULL) { + ahc_queue_lstate_event(ahc, lstate, + devinfo->our_scsiid, + ahc->msgin_buf[0], + /*arg*/0); + ahc_send_lstate_events(ahc, lstate); + } + } + done = MSGLOOP_MSGCOMPLETE; + break; + case MSG_TERM_IO_PROC: default: reject = TRUE; break; @@ -3477,17 +3560,16 @@ ahc_parse_msg(struct ahc_softc *ahc, struct cam_path *path, if (reject) { /* - * Assert attention and setup to - * reject the message. + * Setup to reject the message. */ ahc->msgout_index = 0; ahc->msgout_len = 1; ahc->msgout_buf[0] = MSG_MESSAGE_REJECT; - done = TRUE; + done = MSGLOOP_MSGCOMPLETE; response = TRUE; } - if (done && !response) + if (done != MSGLOOP_IN_PROG && !response) /* Clear the outgoing message buffer */ ahc->msgout_len = 0; @@ -3585,9 +3667,34 @@ ahc_handle_devreset(struct ahc_softc *ahc, struct ahc_devinfo *devinfo, struct cam_path *path; int found; int error; + struct tmode_tstate* tstate; + u_int lun; + error = ahc_create_path(ahc, devinfo, &path); + found = ahc_abort_scbs(ahc, devinfo->target, devinfo->channel, + CAM_LUN_WILDCARD, SCB_LIST_NULL, devinfo->role, + status); + /* + * Send an immediate notify ccb to all target more peripheral + * drivers affected by this action. + */ + tstate = ahc->enabled_targets[devinfo->our_scsiid]; + if (tstate != NULL) { + for (lun = 0; lun <= 7; lun++) { + struct tmode_lstate* lstate; + + lstate = tstate->enabled_luns[lun]; + if (lstate == NULL) + continue; + + ahc_queue_lstate_event(ahc, lstate, devinfo->our_scsiid, + MSG_BUS_DEV_RESET, /*arg*/0); + ahc_send_lstate_events(ahc, lstate); + } + } + /* * Go back to async/narrow transfers and renegotiate. * ahc_set_width and ahc_set_syncrate can cope with NULL @@ -3598,8 +3705,6 @@ ahc_handle_devreset(struct ahc_softc *ahc, struct ahc_devinfo *devinfo, ahc_set_syncrate(ahc, devinfo, path, /*syncrate*/NULL, /*period*/0, /*offset*/0, AHC_TRANS_CUR, /*paused*/TRUE); - found = ahc_abort_scbs(ahc, devinfo->target, devinfo->channel, - CAM_LUN_WILDCARD, SCB_LIST_NULL, status); if (error == CAM_REQ_CMP && acode != 0) xpt_async(AC_SENT_BDR, path, NULL); @@ -3650,7 +3755,9 @@ ahc_done(struct ahc_softc *ahc, struct scb *scb) ahc_index_busy_tcl(ahc, scb->hscb->tcl, /*unbusy*/TRUE); if (ccb->ccb_h.func_code == XPT_CONT_TARGET_IO) { - ccb->ccb_h.status = CAM_REQ_CMP; + if (ahc_ccb_status(ccb) == CAM_REQ_INPROG) + ccb->ccb_h.status |= CAM_REQ_CMP; + ccb->ccb_h.status &= ~CAM_SIM_QUEUED; ahcfreescb(ahc, scb); xpt_done(ccb); return; @@ -3685,7 +3792,8 @@ ahc_done(struct ahc_softc *ahc, struct scb *scb) * SCB into the QINFIFO. */ ahc_search_qinfifo(ahc, SCB_TARGET(scb), SCB_CHANNEL(scb), - SCB_LUN(scb), scb->hscb->tag, /*status*/0, + SCB_LUN(scb), scb->hscb->tag, + ROLE_INITIATOR, /*status*/0, SEARCH_REMOVE); if (ahc_ccb_status(ccb) == CAM_BDR_SENT) ahcsetccbstatus(ccb, CAM_CMD_TIMEOUT); @@ -3756,6 +3864,9 @@ ahc_init(struct ahc_softc *ahc) size_t driver_data_size; u_int32_t physaddr; + printf("SBLKCTL = 0x%x\n", ahc_inb(ahc, SBLKCTL)); + printf("SSTAT0 = 0x%x\n", ahc_inb(ahc, SSTAT0)); + printf("SFUNCT = 0x%x\n", ahc_inb(ahc, SFUNCT)); #ifdef AHC_PRINT_SRAM printf("Scratch Ram:"); for (i = 0x20; i < 0x5f; i++) { @@ -3806,7 +3917,7 @@ ahc_init(struct ahc_softc *ahc) } /* DMA tag for mapping buffers into device visible space. */ - if (bus_dma_tag_create(ahc->parent_dmat, /*alignment*/0, /*boundary*/0, + if (bus_dma_tag_create(ahc->parent_dmat, /*alignment*/1, /*boundary*/0, /*lowaddr*/BUS_SPACE_MAXADDR, /*highaddr*/BUS_SPACE_MAXADDR, /*filter*/NULL, /*filterarg*/NULL, @@ -3831,7 +3942,7 @@ ahc_init(struct ahc_softc *ahc) driver_data_size = 3 * 256 * sizeof(u_int8_t); if ((ahc->flags & AHC_TARGETMODE) != 0) driver_data_size += AHC_TMODE_CMDS * sizeof(struct target_cmd); - if (bus_dma_tag_create(ahc->parent_dmat, /*alignment*/0, /*boundary*/0, + if (bus_dma_tag_create(ahc->parent_dmat, /*alignment*/1, /*boundary*/0, /*lowaddr*/BUS_SPACE_MAXADDR, /*highaddr*/BUS_SPACE_MAXADDR, /*filter*/NULL, /*filterarg*/NULL, @@ -4080,8 +4191,10 @@ ahc_init(struct ahc_softc *ahc) } tstate->ultraenb = ultraenb; tstate->discenable = discenable; - tstate->tagenable = tagenable; + tstate->tagenable = 0; /* Wait until the XPT says its okay */ } + ahc->user_discenable = discenable; + ahc->user_tagenable = tagenable; /* * Tell the sequencer where it can find the our arrays in memory. @@ -4430,6 +4543,7 @@ ahc_action(struct cam_sim *sim, union ccb *ccb) SLIST_INSERT_HEAD(&lstate->immed_notifies, &ccb->ccb_h, sim_links.sle); ccb->ccb_h.status = CAM_REQ_INPROG; + ahc_send_lstate_events(ahc, lstate); break; } case XPT_EN_LUN: /* Enable LUN as a target */ @@ -4447,6 +4561,8 @@ ahc_action(struct cam_sim *sim, union ccb *ccb) struct ccb_trans_settings *cts; struct ahc_initiator_tinfo *tinfo; struct tmode_tstate *tstate; + u_int16_t *discenable; + u_int16_t *tagenable; u_int update_type; int s; @@ -4460,25 +4576,34 @@ ahc_action(struct cam_sim *sim, union ccb *ccb) devinfo.our_scsiid, devinfo.target, &tstate); update_type = 0; - if ((cts->flags & CCB_TRANS_CURRENT_SETTINGS) != 0) + if ((cts->flags & CCB_TRANS_CURRENT_SETTINGS) != 0) { update_type |= AHC_TRANS_GOAL; - if ((cts->flags & CCB_TRANS_USER_SETTINGS) != 0) + discenable = &tstate->discenable; + tagenable = &tstate->tagenable; + } else if ((cts->flags & CCB_TRANS_USER_SETTINGS) != 0) { update_type |= AHC_TRANS_USER; + discenable = &ahc->user_discenable; + tagenable = &ahc->user_tagenable; + } else { + ccb->ccb_h.status = CAM_REQ_INVALID; + xpt_done(ccb); + break; + } s = splcam(); if ((cts->valid & CCB_TRANS_DISC_VALID) != 0) { if ((cts->flags & CCB_TRANS_DISC_ENB) != 0) - tstate->discenable |= devinfo.target_mask; + *discenable |= devinfo.target_mask; else - tstate->discenable &= ~devinfo.target_mask; + *discenable &= ~devinfo.target_mask; } if ((cts->valid & CCB_TRANS_TQ_VALID) != 0) { if ((cts->flags & CCB_TRANS_TAG_ENB) != 0) - tstate->tagenable |= devinfo.target_mask; + *tagenable |= devinfo.target_mask; else - tstate->tagenable &= ~devinfo.target_mask; + *tagenable &= ~devinfo.target_mask; } if ((cts->valid & CCB_TRANS_BUS_WIDTH_VALID) != 0) { @@ -4571,11 +4696,19 @@ ahc_action(struct cam_sim *sim, union ccb *ccb) s = splcam(); cts->flags &= ~(CCB_TRANS_DISC_ENB|CCB_TRANS_TAG_ENB); - if ((tstate->discenable & devinfo.target_mask) != 0) - cts->flags |= CCB_TRANS_DISC_ENB; + if ((cts->flags & CCB_TRANS_USER_SETTINGS) != 0) { + if ((ahc->user_discenable & devinfo.target_mask) != 0) + cts->flags |= CCB_TRANS_DISC_ENB; - if ((tstate->tagenable & devinfo.target_mask) != 0) - cts->flags |= CCB_TRANS_TAG_ENB; + if ((ahc->user_tagenable & devinfo.target_mask) != 0) + cts->flags |= CCB_TRANS_TAG_ENB; + } else { + if ((tstate->discenable & devinfo.target_mask) != 0) + cts->flags |= CCB_TRANS_DISC_ENB; + + if ((tstate->tagenable & devinfo.target_mask) != 0) + cts->flags |= CCB_TRANS_TAG_ENB; + } cts->sync_period = tinfo->period; cts->sync_offset = tinfo->offset; @@ -4958,8 +5091,8 @@ ahc_freeze_devq(struct ahc_softc *ahc, struct cam_path *path) channel = xpt_path_sim(path)->bus_id == 0 ? 'A' : 'B'; ahc_search_qinfifo(ahc, target, channel, lun, - /*tag*/SCB_LIST_NULL, CAM_REQUEUE_REQ, - SEARCH_COMPLETE); + /*tag*/SCB_LIST_NULL, ROLE_UNKNOWN, + CAM_REQUEUE_REQ, SEARCH_COMPLETE); } static void @@ -5433,11 +5566,13 @@ ahc_timeout(void *arg) } else { int disconnected; + /* XXX Shouldn't panic. Just punt instead */ if ((scb->hscb->control & TARGET_SCB) != 0) panic("Timed-out target SCB but bus idle"); if (bus_state != P_BUSFREE && (ahc_inb(ahc, SSTAT0) & TARGET) != 0) { + /* XXX What happened to the SCB? */ /* Hung target selection. Goto busfree */ printf("%s: Hung target selection\n", ahc_name(ahc)); @@ -5446,8 +5581,8 @@ ahc_timeout(void *arg) } if (ahc_search_qinfifo(ahc, target, channel, lun, - scb->hscb->tag, /*status*/0, - SEARCH_COUNT) > 0) { + scb->hscb->tag, ROLE_INITIATOR, + /*status*/0, SEARCH_COUNT) > 0) { disconnected = FALSE; } else { disconnected = TRUE; @@ -5484,6 +5619,7 @@ ahc_timeout(void *arg) channel, SCB_LUN(scb), SCB_LIST_NULL, CAM_REQUEUE_REQ, + ROLE_INITIATOR, SEARCH_COMPLETE); xpt_print_path(scb->ccb->ccb_h.path); printf("Queuing a BDR SCB\n"); @@ -5516,7 +5652,7 @@ ahc_timeout(void *arg) static int ahc_search_qinfifo(struct ahc_softc *ahc, int target, char channel, - int lun, u_int tag, u_int32_t status, + int lun, u_int tag, role_t role, u_int32_t status, ahc_search_action action) { struct scb *scbp; @@ -5536,7 +5672,7 @@ ahc_search_qinfifo(struct ahc_softc *ahc, int target, char channel, while (qinpos != qintail) { scbp = &ahc->scb_data->scbarray[ahc->qinfifo[qinpos]]; - if (ahc_match_scb(scbp, target, channel, lun, tag)) { + if (ahc_match_scb(scbp, target, channel, lun, tag, role)) { /* * We found an scb that needs to be removed. */ @@ -5661,7 +5797,7 @@ ahc_abort_ccb(struct ahc_softc *ahc, struct cam_sim *sim, union ccb *ccb) */ static int ahc_abort_scbs(struct ahc_softc *ahc, int target, char channel, - int lun, u_int tag, u_int32_t status) + int lun, u_int tag, role_t role, u_int32_t status) { struct scb *scbp; u_int active_scb; @@ -5672,7 +5808,7 @@ ahc_abort_scbs(struct ahc_softc *ahc, int target, char channel, active_scb = ahc_inb(ahc, SCBPTR); found = ahc_search_qinfifo(ahc, target, channel, lun, tag, - CAM_REQUEUE_REQ, SEARCH_COMPLETE); + CAM_REQUEUE_REQ, role, SEARCH_COMPLETE); /* * Search waiting for selection list. @@ -5694,7 +5830,8 @@ ahc_abort_scbs(struct ahc_softc *ahc, int target, char channel, scb_index, ahc->scb_data->numscbs); } scbp = &ahc->scb_data->scbarray[scb_index]; - if (ahc_match_scb(scbp, target, channel, lun, tag)) { + if (ahc_match_scb(scbp, target, channel, + lun, tag, role)) { next = ahc_abort_wscb(ahc, next, prev); } else { @@ -5721,7 +5858,8 @@ ahc_abort_scbs(struct ahc_softc *ahc, int target, char channel, scbid = ahc_inb(ahc, SCB_TAG); if (scbid < ahc->scb_data->numscbs) { scbp = &ahc->scb_data->scbarray[scbid]; - if (ahc_match_scb(scbp, target, channel, lun, tag)) { + if (ahc_match_scb(scbp, target, channel, + lun, tag, role)) { ahc_add_curscb_to_free_list(ahc); } } @@ -5741,7 +5879,8 @@ ahc_abort_scbs(struct ahc_softc *ahc, int target, char channel, while (ccb_h != NULL) { scbp = (struct scb *)ccb_h->ccb_scb_ptr; ccb_h = ccb_h->sim_links.le.le_next; - if (ahc_match_scb(scbp, target, channel, lun, tag)) { + if (ahc_match_scb(scbp, target, channel, + lun, tag, role)) { if (ahc_ccb_status(scbp->ccb) == CAM_REQ_INPROG) ahcsetccbstatus(scbp->ccb, status); ahc_freeze_ccb(scbp->ccb); @@ -5782,9 +5921,9 @@ ahc_search_disc_list(struct ahc_softc *ahc, int target, char channel, scb_index, ahc->scb_data->numscbs); } scbp = &ahc->scb_data->scbarray[scb_index]; - if (ahc_match_scb(scbp, target, channel, lun, tag)) { - next = ahc_rem_scb_from_disc_list(ahc, prev, - next); + if (ahc_match_scb(scbp, target, channel, lun, + tag, ROLE_INITIATOR)) { + next = ahc_rem_scb_from_disc_list(ahc, prev, next); count++; } else { prev = next; @@ -5813,7 +5952,7 @@ ahc_rem_scb_from_disc_list(struct ahc_softc *ahc, u_int prev, u_int scbptr) } else ahc_outb(ahc, DISCONNECTED_SCBH, next); - return next; + return (next); } static void @@ -5911,18 +6050,80 @@ ahc_reset_channel(struct ahc_softc *ahc, char channel, int initiate_reset) u_int sblkctl; u_int our_id; int found; + int restart_needed; char cur_channel; ahc->pending_device = NULL; pause_sequencer(ahc); + + /* + * Run our command complete fifos to ensure that we perform + * completion processing on any commands that 'completed' + * before the reset occurred. + */ + ahc_run_qoutfifo(ahc); + if ((ahc->flags & AHC_TARGETMODE) != 0) { + ahc_run_tqinfifo(ahc); + } + + /* + * Reset the bus if we are initiating this reset + */ + sblkctl = ahc_inb(ahc, SBLKCTL); + cur_channel = 'A'; + if ((ahc->features & AHC_TWIN) != 0 + && ((sblkctl & SELBUSB) != 0)) + cur_channel = 'B'; + if (cur_channel != channel) { + /* Case 1: Command for another bus is active + * Stealthily reset the other bus without + * upsetting the current bus. + */ + ahc_outb(ahc, SBLKCTL, sblkctl ^ SELBUSB); + ahc_outb(ahc, SIMODE1, ahc_inb(ahc, SIMODE1) & ~ENBUSFREE); + ahc_outb(ahc, SCSISEQ, + ahc_inb(ahc, SCSISEQ) & (ENSELI|ENRSELI|ENAUTOATNP)); + if (initiate_reset) + ahc_reset_current_bus(ahc); + ahc_clear_intstat(ahc); + ahc_outb(ahc, SBLKCTL, sblkctl); + restart_needed = FALSE; + } else { + /* Case 2: A command from this bus is active or we're idle */ + ahc_clear_msg_state(ahc); + ahc_outb(ahc, SIMODE1, ahc_inb(ahc, SIMODE1) & ~ENBUSFREE); + ahc_outb(ahc, SCSISEQ, + ahc_inb(ahc, SCSISEQ) & (ENSELI|ENRSELI|ENAUTOATNP)); + if (initiate_reset) + ahc_reset_current_bus(ahc); + ahc_clear_intstat(ahc); + + /* + * Since we are going to restart the sequencer, avoid + * a race in the sequencer that could cause corruption + * of our Q pointers by starting over from index 0. + */ + ahc->qoutfifonext = 0; + if ((ahc->features & AHC_QUEUE_REGS) != 0) + ahc_outb(ahc, SDSCB_QOFF, 0); + else + ahc_outb(ahc, QOUTPOS, 0); + if ((ahc->flags & AHC_TARGETMODE) != 0) { + ahc->tqinfifonext = 0; + ahc_outb(ahc, KERNEL_TQINPOS, ahc->tqinfifonext - 1); + ahc_outb(ahc, TQINPOS, 0); + } + restart_needed = TRUE; + } + /* * Clean up all the state information for the * pending transactions on this bus. */ found = ahc_abort_scbs(ahc, CAM_TARGET_WILDCARD, channel, CAM_LUN_WILDCARD, SCB_LIST_NULL, - CAM_SCSI_BUS_RESET); + ROLE_UNKNOWN, CAM_SCSI_BUS_RESET); if (channel == 'B') { path = ahc->path_b; our_id = ahc->our_id_b; @@ -5931,13 +6132,38 @@ ahc_reset_channel(struct ahc_softc *ahc, char channel, int initiate_reset) our_id = ahc->our_id; } + max_scsiid = (ahc->features & AHC_WIDE) ? 15 : 7; + + /* + * Send an immediate notify ccb to all target more peripheral + * drivers affected by this action. + */ + for (target = 0; target <= max_scsiid; target++) { + struct tmode_tstate* tstate; + u_int lun; + + tstate = ahc->enabled_targets[target]; + if (tstate == NULL) + continue; + for (lun = 0; lun <= 7; lun++) { + struct tmode_lstate* lstate; + + lstate = tstate->enabled_luns[lun]; + if (lstate == NULL) + continue; + + ahc_queue_lstate_event(ahc, lstate, CAM_TARGET_WILDCARD, + EVENT_TYPE_BUS_RESET, /*arg*/0); + ahc_send_lstate_events(ahc, lstate); + } + } + /* Notify the XPT that a bus reset occurred */ xpt_async(AC_BUS_RESET, path, NULL); /* * Revert to async/narrow transfers until we renegotiate. */ - max_scsiid = (ahc->features & AHC_WIDE) ? 15 : 7; for (target = 0; target <= max_scsiid; target++) { if (ahc->enabled_targets[target] == NULL) @@ -5958,45 +6184,16 @@ ahc_reset_channel(struct ahc_softc *ahc, char channel, int initiate_reset) } } - /* - * Reset the bus if we are initiating this reset and - * restart/unpause the sequencer - */ - sblkctl = ahc_inb(ahc, SBLKCTL); - cur_channel = 'A'; - if ((ahc->features & AHC_TWIN) != 0 - && ((sblkctl & SELBUSB) != 0)) - cur_channel = 'B'; - if (cur_channel != channel) { - /* Case 1: Command for another bus is active - * Stealthily reset the other bus without - * upsetting the current bus. - */ - ahc_outb(ahc, SBLKCTL, sblkctl ^ SELBUSB); - ahc_outb(ahc, SIMODE1, ahc_inb(ahc, SIMODE1) & ~ENBUSFREE); - ahc_outb(ahc, SCSISEQ, - ahc_inb(ahc, SCSISEQ) & (ENSELI|ENRSELI|ENAUTOATNP)); - if (initiate_reset) - ahc_reset_current_bus(ahc); - ahc_clear_intstat(ahc); - ahc_outb(ahc, SBLKCTL, sblkctl); - unpause_sequencer(ahc, /*unpause_always*/FALSE); - } else { - /* Case 2: A command from this bus is active or we're idle */ - ahc_clear_msg_state(ahc); - ahc_outb(ahc, SIMODE1, ahc_inb(ahc, SIMODE1) & ~ENBUSFREE); - ahc_outb(ahc, SCSISEQ, - ahc_inb(ahc, SCSISEQ) & (ENSELI|ENRSELI|ENAUTOATNP)); - if (initiate_reset) - ahc_reset_current_bus(ahc); - ahc_clear_intstat(ahc); + if (restart_needed) restart_sequencer(ahc); - } + else + unpause_sequencer(ahc, /*unpause_always*/FALSE); return found; } static int -ahc_match_scb (struct scb *scb, int target, char channel, int lun, u_int tag) +ahc_match_scb(struct scb *scb, int target, char channel, + int lun, role_t role, u_int tag) { int targ = SCB_TARGET(scb); char chan = SCB_CHANNEL(scb); @@ -6008,9 +6205,20 @@ ahc_match_scb (struct scb *scb, int target, char channel, int lun, u_int tag) match = ((targ == target) || (target == CAM_TARGET_WILDCARD)); if (match != 0) match = ((lun == slun) || (lun == CAM_LUN_WILDCARD)); - if (match != 0) - match = ((tag == scb->hscb->tag) || (tag == SCB_LIST_NULL)); + if (match != 0) { + int group; + group = XPT_FC_GROUP(scb->ccb->ccb_h.func_code); + if (role == ROLE_INITIATOR) { + match = (group == XPT_FC_GROUP_COMMON) + && ((tag == scb->ccb->csio.tag_id) + || (tag == SCB_LIST_NULL)); + } else if (role == ROLE_TARGET) { + match = (group == XPT_FC_GROUP_TMODE) + && ((tag == scb->ccb->csio.tag_id) + || (tag == SCB_LIST_NULL)); + } + } return match; } @@ -6276,3 +6484,89 @@ ahc_shutdown(int howto, void *arg) for (i = TARG_SCSIRATE; i < HA_274_BIOSCTRL; i++) ahc_outb(ahc, i, 0); } + +/* + * Add a target mode event to this lun's queue + */ +static void +ahc_queue_lstate_event(struct ahc_softc *ahc, struct tmode_lstate *lstate, + u_int initiator_id, u_int event_type, u_int event_arg) +{ + struct ahc_tmode_event *event; + int pending; + + xpt_freeze_devq(lstate->path, /*count*/1); + if (lstate->event_w_idx >= lstate->event_r_idx) + pending = lstate->event_w_idx - lstate->event_r_idx; + else + pending = AHC_TMODE_EVENT_BUFFER_SIZE + 1 + - (lstate->event_r_idx - lstate->event_w_idx); + + if (event_type == EVENT_TYPE_BUS_RESET + || event_type == MSG_BUS_DEV_RESET) { + /* + * Any earlier events are irrelevant, so reset our buffer. + * This has the effect of allowing us to deal with reset + * floods (an external device holding down the reset line) + * without losing the event that is really interesting. + */ + lstate->event_r_idx = 0; + lstate->event_w_idx = 0; + xpt_release_devq(lstate->path, pending, /*runqueue*/FALSE); + } + + if (pending == AHC_TMODE_EVENT_BUFFER_SIZE) { + xpt_print_path(lstate->path); + printf("immediate event %x:%x lost\n", + lstate->event_buffer[lstate->event_r_idx].event_type, + lstate->event_buffer[lstate->event_r_idx].event_arg); + lstate->event_r_idx++; + if (lstate->event_r_idx == AHC_TMODE_EVENT_BUFFER_SIZE) + lstate->event_r_idx = 0; + xpt_release_devq(lstate->path, /*count*/1, /*runqueue*/FALSE); + } + + event = &lstate->event_buffer[lstate->event_w_idx]; + event->initiator_id = initiator_id; + event->event_type = event_type; + event->event_arg = event_arg; + lstate->event_w_idx++; + if (lstate->event_w_idx == AHC_TMODE_EVENT_BUFFER_SIZE) + lstate->event_w_idx = 0; +} + +/* + * Send any target mode events queued up waiting + * for immediate notify resources. + */ +static void +ahc_send_lstate_events(struct ahc_softc *ahc, struct tmode_lstate *lstate) +{ + struct ccb_hdr *ccbh; + struct ccb_immed_notify *inot; + + while (lstate->event_r_idx != lstate->event_w_idx + && (ccbh = SLIST_FIRST(&lstate->immed_notifies)) != NULL) { + struct ahc_tmode_event *event; + + event = &lstate->event_buffer[lstate->event_r_idx]; + SLIST_REMOVE_HEAD(&lstate->immed_notifies, sim_links.sle); + inot = (struct ccb_immed_notify *)ccbh; + switch (event->event_type) { + case EVENT_TYPE_BUS_RESET: + ccbh->status = CAM_SCSI_BUS_RESET|CAM_DEV_QFRZN; + break; + default: + ccbh->status = CAM_MESSAGE_RECV|CAM_DEV_QFRZN; + inot->message_args[0] = event->event_type; + inot->message_args[1] = event->event_arg; + break; + } + inot->initiator_id = event->initiator_id; + inot->sense_len = 0; + xpt_done((union ccb *)inot); + lstate->event_r_idx++; + if (lstate->event_r_idx == AHC_TMODE_EVENT_BUFFER_SIZE) + lstate->event_r_idx = 0; + } +} diff --git a/sys/dev/aic7xxx/aic7xxx.h b/sys/dev/aic7xxx/aic7xxx.h index c4243660883d..a4a1cf5ac298 100644 --- a/sys/dev/aic7xxx/aic7xxx.h +++ b/sys/dev/aic7xxx/aic7xxx.h @@ -34,7 +34,7 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * $Id: aic7xxx.h,v 1.9 1999/05/17 21:53:51 gibbs Exp $ + * $Id: aic7xxx.h,v 1.10 1999/05/22 22:04:10 gibbs Exp $ */ #ifndef _AIC7XXX_H_ @@ -126,8 +126,9 @@ typedef enum { AHC_AIC7890_FE = AHC_MORE_SRAM|AHC_CMD_CHAN|AHC_ULTRA2|AHC_QUEUE_REGS |AHC_SG_PRELOAD|AHC_MULTI_TID|AHC_HS_MAILBOX, AHC_AIC7895_FE = AHC_MORE_SRAM|AHC_CMD_CHAN|AHC_ULTRA, + AHC_AIC7895C_FE = AHC_MORE_SRAM|AHC_CMD_CHAN|AHC_ULTRA|AHC_MULTI_TID, AHC_AIC7896_FE = AHC_MORE_SRAM|AHC_CMD_CHAN|AHC_ULTRA2|AHC_QUEUE_REGS - |AHC_SG_PRELOAD|AHC_MULTI_TID|AHC_HS_MAILBOX, + |AHC_SG_PRELOAD|AHC_MULTI_TID|AHC_HS_MAILBOX } ahc_feature; typedef enum { @@ -259,13 +260,29 @@ struct target_cmd { u_int8_t pad[7]; }; +/* + * Number of events we can buffer up if we run out + * of immediate notify ccbs. + */ +#define AHC_TMODE_EVENT_BUFFER_SIZE 8 +struct ahc_tmode_event { + u_int8_t initiator_id; + u_int8_t event_type; /* MSG type or EVENT_TYPE_BUS_RESET */ +#define EVENT_TYPE_BUS_RESET 0xFF + u_int8_t event_arg; +}; + /* * Per lun target mode state including accept TIO CCB * and immediate notify CCB pools. */ struct tmode_lstate { + struct cam_path *path; struct ccb_hdr_slist accept_tios; struct ccb_hdr_slist immed_notifies; + struct ahc_tmode_event event_buffer[AHC_TMODE_EVENT_BUFFER_SIZE]; + u_int8_t event_r_idx; + u_int8_t event_w_idx; }; #define AHC_TRANS_CUR 0x01 /* Modify current neogtiation status */ @@ -542,6 +559,9 @@ struct ahc_softc { /* Initialization level of this data structure */ u_int init_level; + + u_int16_t user_discenable;/* Disconnection allowed */ + u_int16_t user_tagenable;/* Tagged Queuing allowed */ }; struct full_ahc_softc {