diff options
author | James Smart <James.Smart@Emulex.Com> | 2008-06-14 22:52:38 -0400 |
---|---|---|
committer | James Bottomley <James.Bottomley@HansenPartnership.com> | 2008-07-12 09:22:27 -0400 |
commit | 915caaaf622172bd3451e7b76ba9cfcea80e87c7 (patch) | |
tree | ee336bd79be7cf81b6da86c278b45116a1b54f9f /drivers | |
parent | 235f7f25f4928f5075dbebdfb9ca2c5d90db882c (diff) |
[SCSI] lpfc 8.2.7 : Change device reset behavior
Prior handler was only waiting for I/O on one lun to finish before
returning completion. Now, wait for all LUNs on the target.
Also performed some rudimentary cleanup while in this code.
Signed-off-by: James Smart <james.smart@emulex.com>
Signed-off-by: James Bottomley <James.Bottomley@HansenPartnership.com>
Diffstat (limited to 'drivers')
-rw-r--r-- | drivers/scsi/lpfc/lpfc_scsi.c | 201 |
1 files changed, 80 insertions, 121 deletions
diff --git a/drivers/scsi/lpfc/lpfc_scsi.c b/drivers/scsi/lpfc/lpfc_scsi.c index 0910a9ab76a5..3926affaf727 100644 --- a/drivers/scsi/lpfc/lpfc_scsi.c +++ b/drivers/scsi/lpfc/lpfc_scsi.c | |||
@@ -849,14 +849,15 @@ lpfc_scsi_tgt_reset(struct lpfc_scsi_buf *lpfc_cmd, struct lpfc_vport *vport, | |||
849 | struct lpfc_iocbq *iocbq; | 849 | struct lpfc_iocbq *iocbq; |
850 | struct lpfc_iocbq *iocbqrsp; | 850 | struct lpfc_iocbq *iocbqrsp; |
851 | int ret; | 851 | int ret; |
852 | int status; | ||
852 | 853 | ||
853 | if (!rdata->pnode || !NLP_CHK_NODE_ACT(rdata->pnode)) | 854 | if (!rdata->pnode || !NLP_CHK_NODE_ACT(rdata->pnode)) |
854 | return FAILED; | 855 | return FAILED; |
855 | 856 | ||
856 | lpfc_cmd->rdata = rdata; | 857 | lpfc_cmd->rdata = rdata; |
857 | ret = lpfc_scsi_prep_task_mgmt_cmd(vport, lpfc_cmd, lun, | 858 | status = lpfc_scsi_prep_task_mgmt_cmd(vport, lpfc_cmd, lun, |
858 | FCP_TARGET_RESET); | 859 | FCP_TARGET_RESET); |
859 | if (!ret) | 860 | if (!status) |
860 | return FAILED; | 861 | return FAILED; |
861 | 862 | ||
862 | iocbq = &lpfc_cmd->cur_iocbq; | 863 | iocbq = &lpfc_cmd->cur_iocbq; |
@@ -869,12 +870,15 @@ lpfc_scsi_tgt_reset(struct lpfc_scsi_buf *lpfc_cmd, struct lpfc_vport *vport, | |||
869 | lpfc_printf_vlog(vport, KERN_INFO, LOG_FCP, | 870 | lpfc_printf_vlog(vport, KERN_INFO, LOG_FCP, |
870 | "0702 Issue Target Reset to TGT %d Data: x%x x%x\n", | 871 | "0702 Issue Target Reset to TGT %d Data: x%x x%x\n", |
871 | tgt_id, rdata->pnode->nlp_rpi, rdata->pnode->nlp_flag); | 872 | tgt_id, rdata->pnode->nlp_rpi, rdata->pnode->nlp_flag); |
872 | ret = lpfc_sli_issue_iocb_wait(phba, | 873 | status = lpfc_sli_issue_iocb_wait(phba, |
873 | &phba->sli.ring[phba->sli.fcp_ring], | 874 | &phba->sli.ring[phba->sli.fcp_ring], |
874 | iocbq, iocbqrsp, lpfc_cmd->timeout); | 875 | iocbq, iocbqrsp, lpfc_cmd->timeout); |
875 | if (ret != IOCB_SUCCESS) { | 876 | if (status != IOCB_SUCCESS) { |
876 | if (ret == IOCB_TIMEDOUT) | 877 | if (status == IOCB_TIMEDOUT) { |
877 | iocbq->iocb_cmpl = lpfc_tskmgmt_def_cmpl; | 878 | iocbq->iocb_cmpl = lpfc_tskmgmt_def_cmpl; |
879 | ret = TIMEOUT_ERROR; | ||
880 | } else | ||
881 | ret = FAILED; | ||
878 | lpfc_cmd->status = IOSTAT_DRIVER_REJECT; | 882 | lpfc_cmd->status = IOSTAT_DRIVER_REJECT; |
879 | } else { | 883 | } else { |
880 | ret = SUCCESS; | 884 | ret = SUCCESS; |
@@ -1142,121 +1146,96 @@ lpfc_device_reset_handler(struct scsi_cmnd *cmnd) | |||
1142 | struct lpfc_iocbq *iocbq, *iocbqrsp; | 1146 | struct lpfc_iocbq *iocbq, *iocbqrsp; |
1143 | struct lpfc_rport_data *rdata = cmnd->device->hostdata; | 1147 | struct lpfc_rport_data *rdata = cmnd->device->hostdata; |
1144 | struct lpfc_nodelist *pnode = rdata->pnode; | 1148 | struct lpfc_nodelist *pnode = rdata->pnode; |
1145 | uint32_t cmd_result = 0, cmd_status = 0; | 1149 | unsigned long later; |
1146 | int ret = FAILED; | 1150 | int ret = SUCCESS; |
1147 | int iocb_status = IOCB_SUCCESS; | 1151 | int status; |
1148 | int cnt, loopcnt; | 1152 | int cnt; |
1149 | 1153 | ||
1150 | lpfc_block_error_handler(cmnd); | 1154 | lpfc_block_error_handler(cmnd); |
1151 | loopcnt = 0; | ||
1152 | /* | 1155 | /* |
1153 | * If target is not in a MAPPED state, delay the reset until | 1156 | * If target is not in a MAPPED state, delay the reset until |
1154 | * target is rediscovered or devloss timeout expires. | 1157 | * target is rediscovered or devloss timeout expires. |
1155 | */ | 1158 | */ |
1156 | while (1) { | 1159 | later = msecs_to_jiffies(2 * vport->cfg_devloss_tmo * 1000) + jiffies; |
1160 | while (time_after(later, jiffies)) { | ||
1157 | if (!pnode || !NLP_CHK_NODE_ACT(pnode)) | 1161 | if (!pnode || !NLP_CHK_NODE_ACT(pnode)) |
1158 | goto out; | 1162 | return FAILED; |
1159 | |||
1160 | if (pnode->nlp_state != NLP_STE_MAPPED_NODE) { | ||
1161 | schedule_timeout_uninterruptible(msecs_to_jiffies(500)); | ||
1162 | loopcnt++; | ||
1163 | rdata = cmnd->device->hostdata; | ||
1164 | if (!rdata || | ||
1165 | (loopcnt > ((vport->cfg_devloss_tmo * 2) + 1))){ | ||
1166 | lpfc_printf_vlog(vport, KERN_ERR, LOG_FCP, | ||
1167 | "0721 LUN Reset rport " | ||
1168 | "failure: cnt x%x rdata x%p\n", | ||
1169 | loopcnt, rdata); | ||
1170 | goto out; | ||
1171 | } | ||
1172 | pnode = rdata->pnode; | ||
1173 | if (!pnode || !NLP_CHK_NODE_ACT(pnode)) | ||
1174 | goto out; | ||
1175 | } | ||
1176 | if (pnode->nlp_state == NLP_STE_MAPPED_NODE) | 1163 | if (pnode->nlp_state == NLP_STE_MAPPED_NODE) |
1177 | break; | 1164 | break; |
1165 | schedule_timeout_uninterruptible(msecs_to_jiffies(500)); | ||
1166 | rdata = cmnd->device->hostdata; | ||
1167 | if (!rdata) | ||
1168 | break; | ||
1169 | pnode = rdata->pnode; | ||
1170 | } | ||
1171 | if (!rdata || pnode->nlp_state != NLP_STE_MAPPED_NODE) { | ||
1172 | lpfc_printf_vlog(vport, KERN_ERR, LOG_FCP, | ||
1173 | "0721 LUN Reset rport " | ||
1174 | "failure: msec x%x rdata x%p\n", | ||
1175 | jiffies_to_msecs(jiffies - later), rdata); | ||
1176 | return FAILED; | ||
1178 | } | 1177 | } |
1179 | |||
1180 | lpfc_cmd = lpfc_get_scsi_buf(phba); | 1178 | lpfc_cmd = lpfc_get_scsi_buf(phba); |
1181 | if (lpfc_cmd == NULL) | 1179 | if (lpfc_cmd == NULL) |
1182 | goto out; | 1180 | return FAILED; |
1183 | |||
1184 | lpfc_cmd->timeout = 60; | 1181 | lpfc_cmd->timeout = 60; |
1185 | lpfc_cmd->rdata = rdata; | 1182 | lpfc_cmd->rdata = rdata; |
1186 | 1183 | ||
1187 | ret = lpfc_scsi_prep_task_mgmt_cmd(vport, lpfc_cmd, cmnd->device->lun, | 1184 | status = lpfc_scsi_prep_task_mgmt_cmd(vport, lpfc_cmd, |
1188 | FCP_TARGET_RESET); | 1185 | cmnd->device->lun, |
1189 | if (!ret) | 1186 | FCP_TARGET_RESET); |
1190 | goto out_free_scsi_buf; | 1187 | if (!status) { |
1191 | 1188 | lpfc_release_scsi_buf(phba, lpfc_cmd); | |
1189 | return FAILED; | ||
1190 | } | ||
1192 | iocbq = &lpfc_cmd->cur_iocbq; | 1191 | iocbq = &lpfc_cmd->cur_iocbq; |
1193 | 1192 | ||
1194 | /* get a buffer for this IOCB command response */ | 1193 | /* get a buffer for this IOCB command response */ |
1195 | iocbqrsp = lpfc_sli_get_iocbq(phba); | 1194 | iocbqrsp = lpfc_sli_get_iocbq(phba); |
1196 | if (iocbqrsp == NULL) | 1195 | if (iocbqrsp == NULL) { |
1197 | goto out_free_scsi_buf; | 1196 | lpfc_release_scsi_buf(phba, lpfc_cmd); |
1198 | 1197 | return FAILED; | |
1198 | } | ||
1199 | lpfc_printf_vlog(vport, KERN_INFO, LOG_FCP, | 1199 | lpfc_printf_vlog(vport, KERN_INFO, LOG_FCP, |
1200 | "0703 Issue target reset to TGT %d LUN %d " | 1200 | "0703 Issue target reset to TGT %d LUN %d " |
1201 | "rpi x%x nlp_flag x%x\n", cmnd->device->id, | 1201 | "rpi x%x nlp_flag x%x\n", cmnd->device->id, |
1202 | cmnd->device->lun, pnode->nlp_rpi, pnode->nlp_flag); | 1202 | cmnd->device->lun, pnode->nlp_rpi, pnode->nlp_flag); |
1203 | iocb_status = lpfc_sli_issue_iocb_wait(phba, | 1203 | status = lpfc_sli_issue_iocb_wait(phba, |
1204 | &phba->sli.ring[phba->sli.fcp_ring], | 1204 | &phba->sli.ring[phba->sli.fcp_ring], |
1205 | iocbq, iocbqrsp, lpfc_cmd->timeout); | 1205 | iocbq, iocbqrsp, lpfc_cmd->timeout); |
1206 | 1206 | if (status == IOCB_TIMEDOUT) { | |
1207 | if (iocb_status == IOCB_TIMEDOUT) | ||
1208 | iocbq->iocb_cmpl = lpfc_tskmgmt_def_cmpl; | 1207 | iocbq->iocb_cmpl = lpfc_tskmgmt_def_cmpl; |
1209 | 1208 | ret = TIMEOUT_ERROR; | |
1210 | if (iocb_status == IOCB_SUCCESS) | 1209 | } else { |
1211 | ret = SUCCESS; | 1210 | if (status != IOCB_SUCCESS) |
1212 | else | 1211 | ret = FAILED; |
1213 | ret = iocb_status; | 1212 | lpfc_release_scsi_buf(phba, lpfc_cmd); |
1214 | 1213 | } | |
1215 | cmd_result = iocbqrsp->iocb.un.ulpWord[4]; | 1214 | lpfc_printf_vlog(vport, KERN_ERR, LOG_FCP, |
1216 | cmd_status = iocbqrsp->iocb.ulpStatus; | 1215 | "0713 SCSI layer issued device reset (%d, %d) " |
1217 | 1216 | "return x%x status x%x result x%x\n", | |
1217 | cmnd->device->id, cmnd->device->lun, ret, | ||
1218 | iocbqrsp->iocb.ulpStatus, | ||
1219 | iocbqrsp->iocb.un.ulpWord[4]); | ||
1218 | lpfc_sli_release_iocbq(phba, iocbqrsp); | 1220 | lpfc_sli_release_iocbq(phba, iocbqrsp); |
1219 | |||
1220 | /* | ||
1221 | * All outstanding txcmplq I/Os should have been aborted by the device. | ||
1222 | * Unfortunately, some targets do not abide by this forcing the driver | ||
1223 | * to double check. | ||
1224 | */ | ||
1225 | cnt = lpfc_sli_sum_iocb(vport, cmnd->device->id, cmnd->device->lun, | 1221 | cnt = lpfc_sli_sum_iocb(vport, cmnd->device->id, cmnd->device->lun, |
1226 | LPFC_CTX_LUN); | 1222 | LPFC_CTX_TGT); |
1227 | if (cnt) | 1223 | if (cnt) |
1228 | lpfc_sli_abort_iocb(vport, &phba->sli.ring[phba->sli.fcp_ring], | 1224 | lpfc_sli_abort_iocb(vport, &phba->sli.ring[phba->sli.fcp_ring], |
1229 | cmnd->device->id, cmnd->device->lun, | 1225 | cmnd->device->id, cmnd->device->lun, |
1230 | LPFC_CTX_LUN); | 1226 | LPFC_CTX_TGT); |
1231 | loopcnt = 0; | 1227 | later = msecs_to_jiffies(2 * vport->cfg_devloss_tmo * 1000) + jiffies; |
1232 | while(cnt) { | 1228 | while (time_after(later, jiffies) && cnt) { |
1233 | schedule_timeout_uninterruptible(LPFC_RESET_WAIT*HZ); | 1229 | schedule_timeout_uninterruptible(msecs_to_jiffies(20)); |
1234 | |||
1235 | if (++loopcnt | ||
1236 | > (2 * vport->cfg_devloss_tmo)/LPFC_RESET_WAIT) | ||
1237 | break; | ||
1238 | |||
1239 | cnt = lpfc_sli_sum_iocb(vport, cmnd->device->id, | 1230 | cnt = lpfc_sli_sum_iocb(vport, cmnd->device->id, |
1240 | cmnd->device->lun, LPFC_CTX_LUN); | 1231 | cmnd->device->lun, LPFC_CTX_TGT); |
1241 | } | 1232 | } |
1242 | |||
1243 | if (cnt) { | 1233 | if (cnt) { |
1244 | lpfc_printf_vlog(vport, KERN_ERR, LOG_FCP, | 1234 | lpfc_printf_vlog(vport, KERN_ERR, LOG_FCP, |
1245 | "0719 device reset I/O flush failure: " | 1235 | "0719 device reset I/O flush failure: " |
1246 | "cnt x%x\n", cnt); | 1236 | "cnt x%x\n", cnt); |
1247 | ret = FAILED; | 1237 | ret = FAILED; |
1248 | } | 1238 | } |
1249 | |||
1250 | out_free_scsi_buf: | ||
1251 | if (iocb_status != IOCB_TIMEDOUT) { | ||
1252 | lpfc_release_scsi_buf(phba, lpfc_cmd); | ||
1253 | } | ||
1254 | lpfc_printf_vlog(vport, KERN_ERR, LOG_FCP, | ||
1255 | "0713 SCSI layer issued device reset (%d, %d) " | ||
1256 | "return x%x status x%x result x%x\n", | ||
1257 | cmnd->device->id, cmnd->device->lun, ret, | ||
1258 | cmd_status, cmd_result); | ||
1259 | out: | ||
1260 | return ret; | 1239 | return ret; |
1261 | } | 1240 | } |
1262 | 1241 | ||
@@ -1268,19 +1247,12 @@ lpfc_bus_reset_handler(struct scsi_cmnd *cmnd) | |||
1268 | struct lpfc_hba *phba = vport->phba; | 1247 | struct lpfc_hba *phba = vport->phba; |
1269 | struct lpfc_nodelist *ndlp = NULL; | 1248 | struct lpfc_nodelist *ndlp = NULL; |
1270 | int match; | 1249 | int match; |
1271 | int ret = FAILED, i, err_count = 0; | 1250 | int ret = SUCCESS, status, i; |
1272 | int cnt, loopcnt; | 1251 | int cnt; |
1273 | struct lpfc_scsi_buf * lpfc_cmd; | 1252 | struct lpfc_scsi_buf * lpfc_cmd; |
1253 | unsigned long later; | ||
1274 | 1254 | ||
1275 | lpfc_block_error_handler(cmnd); | 1255 | lpfc_block_error_handler(cmnd); |
1276 | |||
1277 | lpfc_cmd = lpfc_get_scsi_buf(phba); | ||
1278 | if (lpfc_cmd == NULL) | ||
1279 | goto out; | ||
1280 | |||
1281 | /* The lpfc_cmd storage is reused. Set all loop invariants. */ | ||
1282 | lpfc_cmd->timeout = 60; | ||
1283 | |||
1284 | /* | 1256 | /* |
1285 | * Since the driver manages a single bus device, reset all | 1257 | * Since the driver manages a single bus device, reset all |
1286 | * targets known to the driver. Should any target reset | 1258 | * targets known to the driver. Should any target reset |
@@ -1294,7 +1266,7 @@ lpfc_bus_reset_handler(struct scsi_cmnd *cmnd) | |||
1294 | if (!NLP_CHK_NODE_ACT(ndlp)) | 1266 | if (!NLP_CHK_NODE_ACT(ndlp)) |
1295 | continue; | 1267 | continue; |
1296 | if (ndlp->nlp_state == NLP_STE_MAPPED_NODE && | 1268 | if (ndlp->nlp_state == NLP_STE_MAPPED_NODE && |
1297 | i == ndlp->nlp_sid && | 1269 | ndlp->nlp_sid == i && |
1298 | ndlp->rport) { | 1270 | ndlp->rport) { |
1299 | match = 1; | 1271 | match = 1; |
1300 | break; | 1272 | break; |
@@ -1303,27 +1275,22 @@ lpfc_bus_reset_handler(struct scsi_cmnd *cmnd) | |||
1303 | spin_unlock_irq(shost->host_lock); | 1275 | spin_unlock_irq(shost->host_lock); |
1304 | if (!match) | 1276 | if (!match) |
1305 | continue; | 1277 | continue; |
1306 | 1278 | lpfc_cmd = lpfc_get_scsi_buf(phba); | |
1307 | ret = lpfc_scsi_tgt_reset(lpfc_cmd, vport, i, | 1279 | if (lpfc_cmd) { |
1308 | cmnd->device->lun, | 1280 | lpfc_cmd->timeout = 60; |
1309 | ndlp->rport->dd_data); | 1281 | status = lpfc_scsi_tgt_reset(lpfc_cmd, vport, i, |
1310 | if (ret != SUCCESS) { | 1282 | cmnd->device->lun, |
1283 | ndlp->rport->dd_data); | ||
1284 | if (status != TIMEOUT_ERROR) | ||
1285 | lpfc_release_scsi_buf(phba, lpfc_cmd); | ||
1286 | } | ||
1287 | if (!lpfc_cmd || status != SUCCESS) { | ||
1311 | lpfc_printf_vlog(vport, KERN_ERR, LOG_FCP, | 1288 | lpfc_printf_vlog(vport, KERN_ERR, LOG_FCP, |
1312 | "0700 Bus Reset on target %d failed\n", | 1289 | "0700 Bus Reset on target %d failed\n", |
1313 | i); | 1290 | i); |
1314 | err_count++; | 1291 | ret = FAILED; |
1315 | break; | ||
1316 | } | 1292 | } |
1317 | } | 1293 | } |
1318 | |||
1319 | if (ret != IOCB_TIMEDOUT) | ||
1320 | lpfc_release_scsi_buf(phba, lpfc_cmd); | ||
1321 | |||
1322 | if (err_count == 0) | ||
1323 | ret = SUCCESS; | ||
1324 | else | ||
1325 | ret = FAILED; | ||
1326 | |||
1327 | /* | 1294 | /* |
1328 | * All outstanding txcmplq I/Os should have been aborted by | 1295 | * All outstanding txcmplq I/Os should have been aborted by |
1329 | * the targets. Unfortunately, some targets do not abide by | 1296 | * the targets. Unfortunately, some targets do not abide by |
@@ -1333,27 +1300,19 @@ lpfc_bus_reset_handler(struct scsi_cmnd *cmnd) | |||
1333 | if (cnt) | 1300 | if (cnt) |
1334 | lpfc_sli_abort_iocb(vport, &phba->sli.ring[phba->sli.fcp_ring], | 1301 | lpfc_sli_abort_iocb(vport, &phba->sli.ring[phba->sli.fcp_ring], |
1335 | 0, 0, LPFC_CTX_HOST); | 1302 | 0, 0, LPFC_CTX_HOST); |
1336 | loopcnt = 0; | 1303 | later = msecs_to_jiffies(2 * vport->cfg_devloss_tmo * 1000) + jiffies; |
1337 | while(cnt) { | 1304 | while (time_after(later, jiffies) && cnt) { |
1338 | schedule_timeout_uninterruptible(LPFC_RESET_WAIT*HZ); | 1305 | schedule_timeout_uninterruptible(msecs_to_jiffies(20)); |
1339 | |||
1340 | if (++loopcnt | ||
1341 | > (2 * vport->cfg_devloss_tmo)/LPFC_RESET_WAIT) | ||
1342 | break; | ||
1343 | |||
1344 | cnt = lpfc_sli_sum_iocb(vport, 0, 0, LPFC_CTX_HOST); | 1306 | cnt = lpfc_sli_sum_iocb(vport, 0, 0, LPFC_CTX_HOST); |
1345 | } | 1307 | } |
1346 | |||
1347 | if (cnt) { | 1308 | if (cnt) { |
1348 | lpfc_printf_vlog(vport, KERN_ERR, LOG_FCP, | 1309 | lpfc_printf_vlog(vport, KERN_ERR, LOG_FCP, |
1349 | "0715 Bus Reset I/O flush failure: " | 1310 | "0715 Bus Reset I/O flush failure: " |
1350 | "cnt x%x left x%x\n", cnt, i); | 1311 | "cnt x%x left x%x\n", cnt, i); |
1351 | ret = FAILED; | 1312 | ret = FAILED; |
1352 | } | 1313 | } |
1353 | |||
1354 | lpfc_printf_vlog(vport, KERN_ERR, LOG_FCP, | 1314 | lpfc_printf_vlog(vport, KERN_ERR, LOG_FCP, |
1355 | "0714 SCSI layer issued Bus Reset Data: x%x\n", ret); | 1315 | "0714 SCSI layer issued Bus Reset Data: x%x\n", ret); |
1356 | out: | ||
1357 | return ret; | 1316 | return ret; |
1358 | } | 1317 | } |
1359 | 1318 | ||