aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/scsi/aic7xxx
diff options
context:
space:
mode:
authorHannes Reinecke <hare@suse.de>2006-10-23 09:22:37 -0400
committerJames Bottomley <jejb@mulgrave.il.steeleye.com>2006-10-25 18:14:38 -0400
commit8883c1f182fa88d2b8e0adb6ae90a42f67e5353e (patch)
tree4bed9c198d337e0a757eda5d609ce76530fca6ae /drivers/scsi/aic7xxx
parent4a531e8c79fe459e922347461ccc0f0c13de20d5 (diff)
[SCSI] aic79xx: Fixup external device reset
Whenever an external device is resetted we really have to take care to keep the channel in sync. Just notifying SCSI-ML and retry is not enough as we have to make sure the SCSI bus is not getting confused, either. So whenever we detect an external reset we rewrite the command to TUR, disable packetized command and notify the internal engine that an abort has happened. This way we trigger a proper bus reset sequence and all devices will be renegotiated properly. Kudos to Justin Gibbs and Luben Tuikov for this idea. Signed-off-by: Hannes Reinecke <hare@suse.de> Signed-off-by: James Bottomley <James.Bottomley@SteelEye.com>
Diffstat (limited to 'drivers/scsi/aic7xxx')
-rw-r--r--drivers/scsi/aic7xxx/aic79xx_core.c66
1 files changed, 56 insertions, 10 deletions
diff --git a/drivers/scsi/aic7xxx/aic79xx_core.c b/drivers/scsi/aic7xxx/aic79xx_core.c
index 4c2b5a817111..07a86a30f676 100644
--- a/drivers/scsi/aic7xxx/aic79xx_core.c
+++ b/drivers/scsi/aic7xxx/aic79xx_core.c
@@ -1154,10 +1154,12 @@ ahd_handle_seqint(struct ahd_softc *ahd, u_int intstat)
1154 * If a target takes us into the command phase 1154 * If a target takes us into the command phase
1155 * assume that it has been externally reset and 1155 * assume that it has been externally reset and
1156 * has thus lost our previous packetized negotiation 1156 * has thus lost our previous packetized negotiation
1157 * agreement. 1157 * agreement. Since we have not sent an identify
1158 * Revert to async/narrow transfers until we 1158 * message and may not have fully qualified the
1159 * can renegotiate with the device and notify 1159 * connection, we change our command to TUR, assert
1160 * the OSM about the reset. 1160 * ATN and ABORT the task when we go to message in
1161 * phase. The OSM will see the REQUEUE_REQUEST
1162 * status and retry the command.
1161 */ 1163 */
1162 scbid = ahd_get_scbptr(ahd); 1164 scbid = ahd_get_scbptr(ahd);
1163 scb = ahd_lookup_scb(ahd, scbid); 1165 scb = ahd_lookup_scb(ahd, scbid);
@@ -1184,7 +1186,28 @@ ahd_handle_seqint(struct ahd_softc *ahd, u_int intstat)
1184 ahd_set_syncrate(ahd, &devinfo, /*period*/0, 1186 ahd_set_syncrate(ahd, &devinfo, /*period*/0,
1185 /*offset*/0, /*ppr_options*/0, 1187 /*offset*/0, /*ppr_options*/0,
1186 AHD_TRANS_ACTIVE, /*paused*/TRUE); 1188 AHD_TRANS_ACTIVE, /*paused*/TRUE);
1187 scb->flags |= SCB_EXTERNAL_RESET; 1189 /* Hand-craft TUR command */
1190 ahd_outb(ahd, SCB_CDB_STORE, 0);
1191 ahd_outb(ahd, SCB_CDB_STORE+1, 0);
1192 ahd_outb(ahd, SCB_CDB_STORE+2, 0);
1193 ahd_outb(ahd, SCB_CDB_STORE+3, 0);
1194 ahd_outb(ahd, SCB_CDB_STORE+4, 0);
1195 ahd_outb(ahd, SCB_CDB_STORE+5, 0);
1196 ahd_outb(ahd, SCB_CDB_LEN, 6);
1197 scb->hscb->control &= ~(TAG_ENB|SCB_TAG_TYPE);
1198 scb->hscb->control |= MK_MESSAGE;
1199 ahd_outb(ahd, SCB_CONTROL, scb->hscb->control);
1200 ahd_outb(ahd, MSG_OUT, HOST_MSG);
1201 ahd_outb(ahd, SAVED_SCSIID, scb->hscb->scsiid);
1202 /*
1203 * The lun is 0, regardless of the SCB's lun
1204 * as we have not sent an identify message.
1205 */
1206 ahd_outb(ahd, SAVED_LUN, 0);
1207 ahd_outb(ahd, SEQ_FLAGS, 0);
1208 ahd_assert_atn(ahd);
1209 scb->flags &= ~SCB_PACKETIZED;
1210 scb->flags |= SCB_ABORT|SCB_EXTERNAL_RESET;
1188 ahd_freeze_devq(ahd, scb); 1211 ahd_freeze_devq(ahd, scb);
1189 ahd_set_transaction_status(scb, CAM_REQUEUE_REQ); 1212 ahd_set_transaction_status(scb, CAM_REQUEUE_REQ);
1190 ahd_freeze_scb(scb); 1213 ahd_freeze_scb(scb);
@@ -1620,8 +1643,10 @@ ahd_handle_scsiint(struct ahd_softc *ahd, u_int intstat)
1620 /* 1643 /*
1621 * Ignore external resets after a bus reset. 1644 * Ignore external resets after a bus reset.
1622 */ 1645 */
1623 if (((status & SCSIRSTI) != 0) && (ahd->flags & AHD_BUS_RESET_ACTIVE)) 1646 if (((status & SCSIRSTI) != 0) && (ahd->flags & AHD_BUS_RESET_ACTIVE)) {
1647 ahd_outb(ahd, CLRSINT1, CLRSCSIRSTI);
1624 return; 1648 return;
1649 }
1625 1650
1626 /* 1651 /*
1627 * Clear bus reset flag 1652 * Clear bus reset flag
@@ -2301,6 +2326,22 @@ ahd_handle_nonpkt_busfree(struct ahd_softc *ahd)
2301 if (sent_msg == MSG_ABORT_TAG) 2326 if (sent_msg == MSG_ABORT_TAG)
2302 tag = SCB_GET_TAG(scb); 2327 tag = SCB_GET_TAG(scb);
2303 2328
2329 if ((scb->flags & SCB_EXTERNAL_RESET) != 0) {
2330 /*
2331 * This abort is in response to an
2332 * unexpected switch to command phase
2333 * for a packetized connection. Since
2334 * the identify message was never sent,
2335 * "saved lun" is 0. We really want to
2336 * abort only the SCB that encountered
2337 * this error, which could have a different
2338 * lun. The SCB will be retried so the OS
2339 * will see the UA after renegotiating to
2340 * packetized.
2341 */
2342 tag = SCB_GET_TAG(scb);
2343 saved_lun = scb->hscb->lun;
2344 }
2304 found = ahd_abort_scbs(ahd, target, 'A', saved_lun, 2345 found = ahd_abort_scbs(ahd, target, 'A', saved_lun,
2305 tag, ROLE_INITIATOR, 2346 tag, ROLE_INITIATOR,
2306 CAM_REQ_ABORTED); 2347 CAM_REQ_ABORTED);
@@ -7985,6 +8026,11 @@ ahd_reset_channel(struct ahd_softc *ahd, char channel, int initiate_reset)
7985 ahd_clear_fifo(ahd, 1); 8026 ahd_clear_fifo(ahd, 1);
7986 8027
7987 /* 8028 /*
8029 * Clear SCSI interrupt status
8030 */
8031 ahd_outb(ahd, CLRSINT1, CLRSCSIRSTI);
8032
8033 /*
7988 * Reenable selections 8034 * Reenable selections
7989 */ 8035 */
7990 ahd_outb(ahd, SIMODE1, ahd_inb(ahd, SIMODE1) | ENSCSIRST); 8036 ahd_outb(ahd, SIMODE1, ahd_inb(ahd, SIMODE1) | ENSCSIRST);
@@ -8017,10 +8063,6 @@ ahd_reset_channel(struct ahd_softc *ahd, char channel, int initiate_reset)
8017 } 8063 }
8018 } 8064 }
8019#endif 8065#endif
8020 /* Notify the XPT that a bus reset occurred */
8021 ahd_send_async(ahd, devinfo.channel, CAM_TARGET_WILDCARD,
8022 CAM_LUN_WILDCARD, AC_BUS_RESET);
8023
8024 /* 8066 /*
8025 * Revert to async/narrow transfers until we renegotiate. 8067 * Revert to async/narrow transfers until we renegotiate.
8026 */ 8068 */
@@ -8042,6 +8084,10 @@ ahd_reset_channel(struct ahd_softc *ahd, char channel, int initiate_reset)
8042 } 8084 }
8043 } 8085 }
8044 8086
8087 /* Notify the XPT that a bus reset occurred */
8088 ahd_send_async(ahd, devinfo.channel, CAM_TARGET_WILDCARD,
8089 CAM_LUN_WILDCARD, AC_BUS_RESET);
8090
8045 ahd_restart(ahd); 8091 ahd_restart(ahd);
8046 8092
8047 return (found); 8093 return (found);