diff options
author | Kenneth R Westerback <krw@cvs.openbsd.org> | 2005-10-10 16:27:25 +0000 |
---|---|---|
committer | Kenneth R Westerback <krw@cvs.openbsd.org> | 2005-10-10 16:27:25 +0000 |
commit | 4d7328af0d8545d1c5e98867adc169655e14f971 (patch) | |
tree | f59e1da363bf6e99c31ec1ea3170ba585eecbc09 /sys/dev/ic | |
parent | 77c0482e5a60827b70e8e722468bd328f0b82193 (diff) |
Add handling for IGNORE WIDE RESIDUE messages. Actually calculate
resid instead of assuming it is always 0. From bouyer via NetBSD. Note
we are now sync'd with the latest (1.78 siop.c, 1.37 siop_common.c)
NetBSD code.
Tested by various people over the last year+. Didn't solve any of
their problems but didn't cause any regression either.
Diffstat (limited to 'sys/dev/ic')
-rw-r--r-- | sys/dev/ic/siop.c | 93 | ||||
-rw-r--r-- | sys/dev/ic/siop_common.c | 171 | ||||
-rw-r--r-- | sys/dev/ic/siopvar_common.h | 9 |
3 files changed, 222 insertions, 51 deletions
diff --git a/sys/dev/ic/siop.c b/sys/dev/ic/siop.c index aa8f81b00ea..15b14e60696 100644 --- a/sys/dev/ic/siop.c +++ b/sys/dev/ic/siop.c @@ -1,5 +1,5 @@ -/* $OpenBSD: siop.c,v 1.39 2005/10/08 19:48:42 krw Exp $ */ -/* $NetBSD: siop.c,v 1.65 2002/11/08 22:04:41 bouyer Exp $ */ +/* $OpenBSD: siop.c,v 1.40 2005/10/10 16:27:23 krw Exp $ */ +/* $NetBSD: siop.c,v 1.78 2005/02/27 00:27:02 perry Exp $ */ /* * Copyright (c) 2000 Manuel Bouyer. @@ -343,7 +343,7 @@ siop_intr(v) struct siop_cmd *siop_cmd; struct siop_lun *siop_lun; struct scsi_xfer *xs; - int istat, sist, sstat1, dstat; + int istat, sist, sstat1, dstat = 0; u_int32_t irqcode; int need_reset = 0; int offset, target, lun, tag; @@ -518,31 +518,31 @@ siop_intr(v) /* * previous phase may be aborted for any reason * ( for example, the target has less data to - * transfer than requested). Just go to status - * and the command should terminate. + * transfer than requested). Compute resid and + * just go to status, the command should + * terminate. */ INCSTAT(siop_stat_intr_shortxfer); - if ((dstat & DSTAT_DFE) == 0) + if (scratcha0 & A_flag_data) + siop_ma(&siop_cmd->cmd_c); + else if ((dstat & DSTAT_DFE) == 0) siop_clearfifo(&sc->sc_c); - /* no table to flush here */ CALL_SCRIPT(Ent_status); return 1; case SSTAT1_PHASE_MSGIN: - /* - * target may be ready to disconnect - * Save data pointers just in case. - */ + /* + * target may be ready to disconnect + * Compute resid which would be used later + * if a save data pointer is needed. + */ INCSTAT(siop_stat_intr_xferdisc); if (scratcha0 & A_flag_data) - siop_sdp(&siop_cmd->cmd_c); + siop_ma(&siop_cmd->cmd_c); else if ((dstat & DSTAT_DFE) == 0) siop_clearfifo(&sc->sc_c); bus_space_write_1(sc->sc_c.sc_rt, sc->sc_c.sc_rh, SIOP_SCRATCHA, scratcha0 & ~A_flag_data); - siop_table_sync(siop_cmd, - BUS_DMASYNC_PREREAD | - BUS_DMASYNC_PREWRITE); CALL_SCRIPT(Ent_msgin); return 1; } @@ -816,6 +816,15 @@ scintr: CALL_SCRIPT(Ent_msgin_ack); return 1; } + if (msgin == MSG_IGN_WIDE_RESIDUE) { + /* use the extmsgdata table to get the second byte */ + siop_cmd->cmd_tables->t_extmsgdata.count = + htole32(1); + siop_table_sync(siop_cmd, + BUS_DMASYNC_PREREAD | BUS_DMASYNC_PREWRITE); + CALL_SCRIPT(Ent_get_extmsgdata); + return 1; + } if (xs) sc_print_addr(xs->sc_link); else @@ -859,6 +868,29 @@ scintr: printf("\n"); } #endif + if (siop_cmd->cmd_tables->msg_in[0] == + MSG_IGN_WIDE_RESIDUE) { + /* we got the second byte of MSG_IGN_WIDE_RESIDUE */ + if (siop_cmd->cmd_tables->msg_in[3] != 1) + printf("MSG_IGN_WIDE_RESIDUE: " + "bad len %d\n", + siop_cmd->cmd_tables->msg_in[3]); + switch (siop_iwr(&siop_cmd->cmd_c)) { + case SIOP_NEG_MSGOUT: + siop_table_sync(siop_cmd, + BUS_DMASYNC_PREREAD | + BUS_DMASYNC_PREWRITE); + CALL_SCRIPT(Ent_send_msgout); + return(1); + case SIOP_NEG_ACK: + CALL_SCRIPT(Ent_msgin_ack); + return(1); + default: + panic("invalid retval from " + "siop_iwr()"); + } + return(1); + } if (siop_cmd->cmd_tables->msg_in[2] == MSG_EXT_WDTR) { switch (siop_wdtr_neg(&siop_cmd->cmd_c)) { case SIOP_NEG_MSGOUT: @@ -936,25 +968,9 @@ scintr: #ifdef SIOP_DEBUG_DR printf("disconnect offset %d\n", offset); #endif - if (offset > SIOP_NSG) { - printf("%s: bad offset for disconnect (%d)\n", - sc->sc_c.sc_dev.dv_xname, offset); - goto reset; - } - /* - * offset == SIOP_NSG may be a valid condition if - * we get a sdp when the xfer is done. - * Don't call bcopy in this case. - */ - if (offset < SIOP_NSG) { - bcopy(&siop_cmd->cmd_tables->data[offset], - &siop_cmd->cmd_tables->data[0], - (SIOP_NSG - offset) * sizeof(scr_table_t)); - siop_table_sync(siop_cmd, - BUS_DMASYNC_PREREAD | BUS_DMASYNC_PREWRITE); - } - /* check if we can put some command in scheduler */ - siop_start(sc); + siop_sdp(&siop_cmd->cmd_c, offset); + siop_table_sync(siop_cmd, + BUS_DMASYNC_PREREAD | BUS_DMASYNC_PREWRITE); CALL_SCRIPT(Ent_script_sched); return 1; case A_int_resfail: @@ -981,6 +997,10 @@ scintr: letoh32(siop_cmd->cmd_tables->status)); #endif INCSTAT(siop_stat_intr_done); + /* update resid. */ + offset = bus_space_read_1(sc->sc_c.sc_rt, + sc->sc_c.sc_rh, SIOP_SCRATCHA + 1); + siop_update_resid(&siop_cmd->cmd_c, offset); if (siop_cmd->cmd_c.status == CMDST_SENSE_ACTIVE) siop_cmd->cmd_c.status = CMDST_SENSE_DONE; else @@ -1145,7 +1165,10 @@ out: xs->flags |= ITSDONE; siop_cmd->cmd_c.status = CMDST_FREE; TAILQ_INSERT_TAIL(&sc->free_list, siop_cmd, next); - xs->resid = 0; +#if 0 + if (xs->resid != 0) + printf("resid %d datalen %d\n", xs->resid, xs->datalen); +#endif scsi_done(xs); } diff --git a/sys/dev/ic/siop_common.c b/sys/dev/ic/siop_common.c index 5c3fd53cf11..8813b78462a 100644 --- a/sys/dev/ic/siop_common.c +++ b/sys/dev/ic/siop_common.c @@ -1,5 +1,5 @@ -/* $OpenBSD: siop_common.c,v 1.23 2005/10/08 23:46:11 krw Exp $ */ -/* $NetBSD: siop_common.c,v 1.31 2002/09/27 15:37:18 provos Exp $ */ +/* $OpenBSD: siop_common.c,v 1.24 2005/10/10 16:27:23 krw Exp $ */ +/* $NetBSD: siop_common.c,v 1.37 2005/02/27 00:27:02 perry Exp $ */ /* * Copyright (c) 2000, 2002 Manuel Bouyer. @@ -714,17 +714,21 @@ siop_minphys(bp) } void -siop_sdp(siop_cmd) +siop_ma(siop_cmd) struct siop_common_cmd *siop_cmd; { - /* save data pointer. Handle async only for now */ int offset, dbc, sstat; struct siop_common_softc *sc = siop_cmd->siop_sc; - scr_table_t *table; /* table to patch */ + scr_table_t *table; /* table with partial xfer */ + /* + * compute how much of the current table didn't get handled when + * a phase mismatch occurs + */ if ((siop_cmd->xs->flags & (SCSI_DATA_OUT | SCSI_DATA_IN)) == 0) - return; /* no data pointers to save */ + return; /* no valid data transfer */ + offset = bus_space_read_1(sc->sc_rt, sc->sc_rh, SIOP_SCRATCHA + 1); if (offset >= SIOP_NSG) { printf("%s: bad offset in siop_sdp (%d)\n", @@ -733,8 +737,8 @@ siop_sdp(siop_cmd) } table = &siop_cmd->siop_tables->data[offset]; #ifdef DEBUG_DR - printf("sdp: offset %d count=%d addr=0x%x ", offset, - letoh32(table->count), letoh32(table->addr)); + printf("siop_ma: offset %d count=%d addr=0x%x ", offset, + table->count, table->addr); #endif dbc = bus_space_read_4(sc->sc_rt, sc->sc_rh, SIOP_DBC) & 0x00ffffff; if (siop_cmd->xs->flags & SCSI_DATA_OUT) { @@ -772,13 +776,152 @@ siop_sdp(siop_cmd) bus_space_read_1(sc->sc_rt, sc->sc_rh, SIOP_CTEST3) | CTEST3_CLF); } - table->addr = - htole32(letoh32(table->addr) + letoh32(table->count) - dbc); - table->count = htole32(dbc); -#ifdef DEBUG_DR - printf("now count=%d addr=0x%x\n", - letoh32(table->count), letoh32(table->addr)); + siop_cmd->flags |= CMDFL_RESID; + siop_cmd->resid = dbc; +} + +void +siop_sdp(siop_cmd, offset) + struct siop_common_cmd *siop_cmd; + int offset; +{ + scr_table_t *table; + + if ((siop_cmd->xs->flags & (SCSI_DATA_OUT | SCSI_DATA_IN)) + == 0) + return; /* no data pointers to save */ + + /* + * offset == SIOP_NSG may be a valid condition if we get a Save data + * pointer when the xfer is done. Just ignore the Save data pointer + * in this case + */ + if (offset == SIOP_NSG) + return; +#ifdef DIAGNOSTIC + if (offset > SIOP_NSG) { + sc_print_addr(siop_cmd->xs->sc_link); + printf(": offset %d > %d\n", offset, SIOP_NSG); + panic("siop_sdp: offset"); + } #endif + /* + * Save data pointer. We do this by adjusting the tables to point + * at the begginning of the data not yet transfered. + * offset points to the first table with untransfered data. + */ + + /* + * before doing that we decrease resid from the ammount of data which + * has been transfered. + */ + siop_update_resid(siop_cmd, offset); + + /* + * First let see if we have a resid from a phase mismatch. If so, + * we have to adjst the table at offset to remove transfered data. + */ + if (siop_cmd->flags & CMDFL_RESID) { + siop_cmd->flags &= ~CMDFL_RESID; + table = &siop_cmd->siop_tables->data[offset]; + /* "cut" already transfered data from this table */ + table->addr = + htole32(letoh32(table->addr) + + letoh32(table->count) - siop_cmd->resid); + table->count = htole32(siop_cmd->resid); + } + + /* + * now we can remove entries which have been transfered. + * We just move the entries with data left at the beggining of the + * tables + */ + bcopy(&siop_cmd->siop_tables->data[offset], + &siop_cmd->siop_tables->data[0], + (SIOP_NSG - offset) * sizeof(scr_table_t)); +} + +void +siop_update_resid(siop_cmd, offset) + struct siop_common_cmd *siop_cmd; + int offset; +{ + scr_table_t *table; + int i; + + if ((siop_cmd->xs->flags & (SCSI_DATA_OUT | SCSI_DATA_IN)) + == 0) + return; /* no data to transfer */ + + /* + * update resid. First account for the table entries which have + * been fully completed. + */ + for (i = 0; i < offset; i++) + siop_cmd->xs->resid -= + letoh32(siop_cmd->siop_tables->data[i].count); + /* + * if CMDFL_RESID is set, the last table (pointed by offset) is a + * partial transfers. If not, offset points to the entry folloing + * the last full transfer. + */ + if (siop_cmd->flags & CMDFL_RESID) { + table = &siop_cmd->siop_tables->data[offset]; + siop_cmd->xs->resid -= letoh32(table->count) - siop_cmd->resid; + } +} + +int +siop_iwr(siop_cmd) + struct siop_common_cmd *siop_cmd; +{ + int offset; + scr_table_t *table; /* table with IWR */ + struct siop_common_softc *sc = siop_cmd->siop_sc; + /* handle ignore wide residue messages */ + + /* if target isn't wide, reject */ + if ((siop_cmd->siop_target->flags & TARF_ISWIDE) == 0) { + siop_cmd->siop_tables->t_msgout.count= htole32(1); + siop_cmd->siop_tables->msg_out[0] = MSG_MESSAGE_REJECT; + return SIOP_NEG_MSGOUT; + } + /* get index of current command in table */ + offset = bus_space_read_1(sc->sc_rt, sc->sc_rh, SIOP_SCRATCHA + 1); + /* + * if the current table did complete, we're now pointing at the + * next one. Go back one if we didn't see a phase mismatch. + */ + if ((siop_cmd->flags & CMDFL_RESID) == 0) + offset--; + table = &siop_cmd->siop_tables->data[offset]; + + if ((siop_cmd->flags & CMDFL_RESID) == 0) { + if (letoh32(table->count) & 1) { + /* we really got the number of bytes we expected */ + return SIOP_NEG_ACK; + } else { + /* + * now we really had a short xfer, by one byte. + * handle it just as if we had a phase mistmatch + * (there is a resid of one for this table). + * Update scratcha1 to reflect the fact that + * this xfer isn't complete. + */ + siop_cmd->flags |= CMDFL_RESID; + siop_cmd->resid = 1; + bus_space_write_1(sc->sc_rt, sc->sc_rh, + SIOP_SCRATCHA + 1, offset); + return SIOP_NEG_ACK; + } + } else { + /* + * we already have a short xfer for this table; it's + * just one byte less than we though it was + */ + siop_cmd->resid--; + return SIOP_NEG_ACK; + } } void diff --git a/sys/dev/ic/siopvar_common.h b/sys/dev/ic/siopvar_common.h index 6ed6c6a63e1..c9db45647d3 100644 --- a/sys/dev/ic/siopvar_common.h +++ b/sys/dev/ic/siopvar_common.h @@ -1,4 +1,4 @@ -/* $OpenBSD: siopvar_common.h,v 1.20 2005/10/08 17:30:18 krw Exp $ */ +/* $OpenBSD: siopvar_common.h,v 1.21 2005/10/10 16:27:23 krw Exp $ */ /* $NetBSD: siopvar_common.h,v 1.32 2005/02/27 00:27:02 perry Exp $ */ /* @@ -86,6 +86,7 @@ struct siop_common_cmd { int status; int flags; int tag; /* tag used for tagged command queuing */ + int resid; /* valid when CMDFL_RESID is set */ }; /* status defs */ @@ -99,6 +100,7 @@ struct siop_common_cmd { /* flags defs */ #define CMDFL_TIMEOUT 0x0001 /* cmd timed out */ #define CMDFL_TAG 0x0002 /* tagged cmd */ +#define CMDFL_RESID 0x0004 /* current offset in table is partial */ /* per-target struct */ struct siop_common_target { @@ -192,12 +194,15 @@ void siop_sdtr_msg(struct siop_common_cmd *, int, int, int); void siop_wdtr_msg(struct siop_common_cmd *, int, int); void siop_ppr_msg(struct siop_common_cmd *, int, int, int); void siop_update_xfer_mode(struct siop_common_softc *, int); +int siop_iwr(struct siop_common_cmd *); /* actions to take at return of siop_wdtr_neg() and siop_sdtr_neg() */ #define SIOP_NEG_NOP 0x0 #define SIOP_NEG_MSGOUT 0x1 #define SIOP_NEG_ACK 0x2 void siop_minphys(struct buf *); -void siop_sdp(struct siop_common_cmd *); +void siop_ma(struct siop_common_cmd *); +void siop_sdp(struct siop_common_cmd *, int); +void siop_update_resid(struct siop_common_cmd *, int); void siop_clearfifo(struct siop_common_softc *); void siop_resetbus(struct siop_common_softc *); |