diff options
author | Mike Christie <michaelc@cs.wisc.edu> | 2009-11-11 17:34:33 -0500 |
---|---|---|
committer | James Bottomley <James.Bottomley@suse.de> | 2009-12-04 13:01:36 -0500 |
commit | 3fe5ae8b4c4d3a82c755074878da7ddb9dde381e (patch) | |
tree | 2565bc75ba1ab288cff50d4290eca8312f87bb04 /drivers/scsi/libiscsi.c | |
parent | 5d12c05e29fc8715e3e32f57a8cced9290d87c55 (diff) |
[SCSI] libiscsi: add warm target reset tmf support
This implements warm target reset tmf support for
the scsi-ml target reset callback. Previously we would
just drop the session in that callback. This patch will
now try a target reset and if that fails drop the session.
Signed-off-by: Mike Christie <michaelc@cs.wisc.edu>
Signed-off-by: James Bottomley <James.Bottomley@suse.de>
Diffstat (limited to 'drivers/scsi/libiscsi.c')
-rw-r--r-- | drivers/scsi/libiscsi.c | 251 |
1 files changed, 179 insertions, 72 deletions
diff --git a/drivers/scsi/libiscsi.c b/drivers/scsi/libiscsi.c index b6ffdc5512cd..07ec997c5d4f 100644 --- a/drivers/scsi/libiscsi.c +++ b/drivers/scsi/libiscsi.c | |||
@@ -298,17 +298,18 @@ static int iscsi_check_tmf_restrictions(struct iscsi_task *task, int opcode) | |||
298 | hdr_lun = scsilun_to_int((struct scsi_lun *)tmf->lun); | 298 | hdr_lun = scsilun_to_int((struct scsi_lun *)tmf->lun); |
299 | if (hdr_lun != task->sc->device->lun) | 299 | if (hdr_lun != task->sc->device->lun) |
300 | return 0; | 300 | return 0; |
301 | 301 | /* fall through */ | |
302 | case ISCSI_TM_FUNC_TARGET_WARM_RESET: | ||
302 | /* | 303 | /* |
303 | * Fail all SCSI cmd PDUs | 304 | * Fail all SCSI cmd PDUs |
304 | */ | 305 | */ |
305 | if (opcode != ISCSI_OP_SCSI_DATA_OUT) { | 306 | if (opcode != ISCSI_OP_SCSI_DATA_OUT) { |
306 | iscsi_conn_printk(KERN_INFO, conn, | 307 | iscsi_conn_printk(KERN_INFO, conn, |
307 | "task [op %x/%x itt " | 308 | "task [op %x/%x itt " |
308 | "0x%x/0x%x lun %u] " | 309 | "0x%x/0x%x] " |
309 | "rejected.\n", | 310 | "rejected.\n", |
310 | task->hdr->opcode, opcode, | 311 | task->hdr->opcode, opcode, |
311 | task->itt, task->hdr_itt, hdr_lun); | 312 | task->itt, task->hdr_itt); |
312 | return -EACCES; | 313 | return -EACCES; |
313 | } | 314 | } |
314 | /* | 315 | /* |
@@ -318,10 +319,9 @@ static int iscsi_check_tmf_restrictions(struct iscsi_task *task, int opcode) | |||
318 | if (conn->session->fast_abort) { | 319 | if (conn->session->fast_abort) { |
319 | iscsi_conn_printk(KERN_INFO, conn, | 320 | iscsi_conn_printk(KERN_INFO, conn, |
320 | "task [op %x/%x itt " | 321 | "task [op %x/%x itt " |
321 | "0x%x/0x%x lun %u] " | 322 | "0x%x/0x%x] fast abort.\n", |
322 | "fast abort.\n", | ||
323 | task->hdr->opcode, opcode, | 323 | task->hdr->opcode, opcode, |
324 | task->itt, task->hdr_itt, hdr_lun); | 324 | task->itt, task->hdr_itt); |
325 | return -EACCES; | 325 | return -EACCES; |
326 | } | 326 | } |
327 | break; | 327 | break; |
@@ -1757,72 +1757,6 @@ int iscsi_target_alloc(struct scsi_target *starget) | |||
1757 | } | 1757 | } |
1758 | EXPORT_SYMBOL_GPL(iscsi_target_alloc); | 1758 | EXPORT_SYMBOL_GPL(iscsi_target_alloc); |
1759 | 1759 | ||
1760 | void iscsi_session_recovery_timedout(struct iscsi_cls_session *cls_session) | ||
1761 | { | ||
1762 | struct iscsi_session *session = cls_session->dd_data; | ||
1763 | |||
1764 | spin_lock_bh(&session->lock); | ||
1765 | if (session->state != ISCSI_STATE_LOGGED_IN) { | ||
1766 | session->state = ISCSI_STATE_RECOVERY_FAILED; | ||
1767 | if (session->leadconn) | ||
1768 | wake_up(&session->leadconn->ehwait); | ||
1769 | } | ||
1770 | spin_unlock_bh(&session->lock); | ||
1771 | } | ||
1772 | EXPORT_SYMBOL_GPL(iscsi_session_recovery_timedout); | ||
1773 | |||
1774 | int iscsi_eh_target_reset(struct scsi_cmnd *sc) | ||
1775 | { | ||
1776 | struct iscsi_cls_session *cls_session; | ||
1777 | struct iscsi_session *session; | ||
1778 | struct iscsi_conn *conn; | ||
1779 | |||
1780 | cls_session = starget_to_session(scsi_target(sc->device)); | ||
1781 | session = cls_session->dd_data; | ||
1782 | conn = session->leadconn; | ||
1783 | |||
1784 | mutex_lock(&session->eh_mutex); | ||
1785 | spin_lock_bh(&session->lock); | ||
1786 | if (session->state == ISCSI_STATE_TERMINATE) { | ||
1787 | failed: | ||
1788 | ISCSI_DBG_EH(session, | ||
1789 | "failing target reset: Could not log back into " | ||
1790 | "target [age %d]\n", | ||
1791 | session->age); | ||
1792 | spin_unlock_bh(&session->lock); | ||
1793 | mutex_unlock(&session->eh_mutex); | ||
1794 | return FAILED; | ||
1795 | } | ||
1796 | |||
1797 | spin_unlock_bh(&session->lock); | ||
1798 | mutex_unlock(&session->eh_mutex); | ||
1799 | /* | ||
1800 | * we drop the lock here but the leadconn cannot be destoyed while | ||
1801 | * we are in the scsi eh | ||
1802 | */ | ||
1803 | iscsi_conn_failure(conn, ISCSI_ERR_CONN_FAILED); | ||
1804 | |||
1805 | ISCSI_DBG_EH(session, "wait for relogin\n"); | ||
1806 | wait_event_interruptible(conn->ehwait, | ||
1807 | session->state == ISCSI_STATE_TERMINATE || | ||
1808 | session->state == ISCSI_STATE_LOGGED_IN || | ||
1809 | session->state == ISCSI_STATE_RECOVERY_FAILED); | ||
1810 | if (signal_pending(current)) | ||
1811 | flush_signals(current); | ||
1812 | |||
1813 | mutex_lock(&session->eh_mutex); | ||
1814 | spin_lock_bh(&session->lock); | ||
1815 | if (session->state == ISCSI_STATE_LOGGED_IN) { | ||
1816 | ISCSI_DBG_EH(session, | ||
1817 | "target reset succeeded\n"); | ||
1818 | } else | ||
1819 | goto failed; | ||
1820 | spin_unlock_bh(&session->lock); | ||
1821 | mutex_unlock(&session->eh_mutex); | ||
1822 | return SUCCESS; | ||
1823 | } | ||
1824 | EXPORT_SYMBOL_GPL(iscsi_eh_target_reset); | ||
1825 | |||
1826 | static void iscsi_tmf_timedout(unsigned long data) | 1760 | static void iscsi_tmf_timedout(unsigned long data) |
1827 | { | 1761 | { |
1828 | struct iscsi_conn *conn = (struct iscsi_conn *)data; | 1762 | struct iscsi_conn *conn = (struct iscsi_conn *)data; |
@@ -2329,6 +2263,172 @@ done: | |||
2329 | } | 2263 | } |
2330 | EXPORT_SYMBOL_GPL(iscsi_eh_device_reset); | 2264 | EXPORT_SYMBOL_GPL(iscsi_eh_device_reset); |
2331 | 2265 | ||
2266 | void iscsi_session_recovery_timedout(struct iscsi_cls_session *cls_session) | ||
2267 | { | ||
2268 | struct iscsi_session *session = cls_session->dd_data; | ||
2269 | |||
2270 | spin_lock_bh(&session->lock); | ||
2271 | if (session->state != ISCSI_STATE_LOGGED_IN) { | ||
2272 | session->state = ISCSI_STATE_RECOVERY_FAILED; | ||
2273 | if (session->leadconn) | ||
2274 | wake_up(&session->leadconn->ehwait); | ||
2275 | } | ||
2276 | spin_unlock_bh(&session->lock); | ||
2277 | } | ||
2278 | EXPORT_SYMBOL_GPL(iscsi_session_recovery_timedout); | ||
2279 | |||
2280 | /** | ||
2281 | * iscsi_eh_session_reset - drop session and attempt relogin | ||
2282 | * @sc: scsi command | ||
2283 | * | ||
2284 | * This function will wait for a relogin, session termination from | ||
2285 | * userspace, or a recovery/replacement timeout. | ||
2286 | */ | ||
2287 | static int iscsi_eh_session_reset(struct scsi_cmnd *sc) | ||
2288 | { | ||
2289 | struct iscsi_cls_session *cls_session; | ||
2290 | struct iscsi_session *session; | ||
2291 | struct iscsi_conn *conn; | ||
2292 | |||
2293 | cls_session = starget_to_session(scsi_target(sc->device)); | ||
2294 | session = cls_session->dd_data; | ||
2295 | conn = session->leadconn; | ||
2296 | |||
2297 | mutex_lock(&session->eh_mutex); | ||
2298 | spin_lock_bh(&session->lock); | ||
2299 | if (session->state == ISCSI_STATE_TERMINATE) { | ||
2300 | failed: | ||
2301 | ISCSI_DBG_EH(session, | ||
2302 | "failing session reset: Could not log back into " | ||
2303 | "%s, %s [age %d]\n", session->targetname, | ||
2304 | conn->persistent_address, session->age); | ||
2305 | spin_unlock_bh(&session->lock); | ||
2306 | mutex_unlock(&session->eh_mutex); | ||
2307 | return FAILED; | ||
2308 | } | ||
2309 | |||
2310 | spin_unlock_bh(&session->lock); | ||
2311 | mutex_unlock(&session->eh_mutex); | ||
2312 | /* | ||
2313 | * we drop the lock here but the leadconn cannot be destoyed while | ||
2314 | * we are in the scsi eh | ||
2315 | */ | ||
2316 | iscsi_conn_failure(conn, ISCSI_ERR_CONN_FAILED); | ||
2317 | |||
2318 | ISCSI_DBG_EH(session, "wait for relogin\n"); | ||
2319 | wait_event_interruptible(conn->ehwait, | ||
2320 | session->state == ISCSI_STATE_TERMINATE || | ||
2321 | session->state == ISCSI_STATE_LOGGED_IN || | ||
2322 | session->state == ISCSI_STATE_RECOVERY_FAILED); | ||
2323 | if (signal_pending(current)) | ||
2324 | flush_signals(current); | ||
2325 | |||
2326 | mutex_lock(&session->eh_mutex); | ||
2327 | spin_lock_bh(&session->lock); | ||
2328 | if (session->state == ISCSI_STATE_LOGGED_IN) { | ||
2329 | ISCSI_DBG_EH(session, | ||
2330 | "session reset succeeded for %s,%s\n", | ||
2331 | session->targetname, conn->persistent_address); | ||
2332 | } else | ||
2333 | goto failed; | ||
2334 | spin_unlock_bh(&session->lock); | ||
2335 | mutex_unlock(&session->eh_mutex); | ||
2336 | return SUCCESS; | ||
2337 | } | ||
2338 | |||
2339 | static void iscsi_prep_tgt_reset_pdu(struct scsi_cmnd *sc, struct iscsi_tm *hdr) | ||
2340 | { | ||
2341 | memset(hdr, 0, sizeof(*hdr)); | ||
2342 | hdr->opcode = ISCSI_OP_SCSI_TMFUNC | ISCSI_OP_IMMEDIATE; | ||
2343 | hdr->flags = ISCSI_TM_FUNC_TARGET_WARM_RESET & ISCSI_FLAG_TM_FUNC_MASK; | ||
2344 | hdr->flags |= ISCSI_FLAG_CMD_FINAL; | ||
2345 | hdr->rtt = RESERVED_ITT; | ||
2346 | } | ||
2347 | |||
2348 | /** | ||
2349 | * iscsi_eh_target_reset - reset target | ||
2350 | * @sc: scsi command | ||
2351 | * | ||
2352 | * This will attempt to send a warm target reset. If that fails | ||
2353 | * then we will drop the session and attempt ERL0 recovery. | ||
2354 | */ | ||
2355 | int iscsi_eh_target_reset(struct scsi_cmnd *sc) | ||
2356 | { | ||
2357 | struct iscsi_cls_session *cls_session; | ||
2358 | struct iscsi_session *session; | ||
2359 | struct iscsi_conn *conn; | ||
2360 | struct iscsi_tm *hdr; | ||
2361 | int rc = FAILED; | ||
2362 | |||
2363 | cls_session = starget_to_session(scsi_target(sc->device)); | ||
2364 | session = cls_session->dd_data; | ||
2365 | |||
2366 | ISCSI_DBG_EH(session, "tgt Reset [sc %p tgt %s]\n", sc, | ||
2367 | session->targetname); | ||
2368 | |||
2369 | mutex_lock(&session->eh_mutex); | ||
2370 | spin_lock_bh(&session->lock); | ||
2371 | /* | ||
2372 | * Just check if we are not logged in. We cannot check for | ||
2373 | * the phase because the reset could come from a ioctl. | ||
2374 | */ | ||
2375 | if (!session->leadconn || session->state != ISCSI_STATE_LOGGED_IN) | ||
2376 | goto unlock; | ||
2377 | conn = session->leadconn; | ||
2378 | |||
2379 | /* only have one tmf outstanding at a time */ | ||
2380 | if (conn->tmf_state != TMF_INITIAL) | ||
2381 | goto unlock; | ||
2382 | conn->tmf_state = TMF_QUEUED; | ||
2383 | |||
2384 | hdr = &conn->tmhdr; | ||
2385 | iscsi_prep_tgt_reset_pdu(sc, hdr); | ||
2386 | |||
2387 | if (iscsi_exec_task_mgmt_fn(conn, hdr, session->age, | ||
2388 | session->tgt_reset_timeout)) { | ||
2389 | rc = FAILED; | ||
2390 | goto unlock; | ||
2391 | } | ||
2392 | |||
2393 | switch (conn->tmf_state) { | ||
2394 | case TMF_SUCCESS: | ||
2395 | break; | ||
2396 | case TMF_TIMEDOUT: | ||
2397 | spin_unlock_bh(&session->lock); | ||
2398 | iscsi_conn_failure(conn, ISCSI_ERR_CONN_FAILED); | ||
2399 | goto done; | ||
2400 | default: | ||
2401 | conn->tmf_state = TMF_INITIAL; | ||
2402 | goto unlock; | ||
2403 | } | ||
2404 | |||
2405 | rc = SUCCESS; | ||
2406 | spin_unlock_bh(&session->lock); | ||
2407 | |||
2408 | iscsi_suspend_tx(conn); | ||
2409 | |||
2410 | spin_lock_bh(&session->lock); | ||
2411 | memset(hdr, 0, sizeof(*hdr)); | ||
2412 | fail_scsi_tasks(conn, -1, DID_ERROR); | ||
2413 | conn->tmf_state = TMF_INITIAL; | ||
2414 | spin_unlock_bh(&session->lock); | ||
2415 | |||
2416 | iscsi_start_tx(conn); | ||
2417 | goto done; | ||
2418 | |||
2419 | unlock: | ||
2420 | spin_unlock_bh(&session->lock); | ||
2421 | done: | ||
2422 | ISCSI_DBG_EH(session, "tgt %s reset result = %s\n", session->targetname, | ||
2423 | rc == SUCCESS ? "SUCCESS" : "FAILED"); | ||
2424 | mutex_unlock(&session->eh_mutex); | ||
2425 | |||
2426 | if (rc == FAILED) | ||
2427 | rc = iscsi_eh_session_reset(sc); | ||
2428 | return rc; | ||
2429 | } | ||
2430 | EXPORT_SYMBOL_GPL(iscsi_eh_target_reset); | ||
2431 | |||
2332 | /* | 2432 | /* |
2333 | * Pre-allocate a pool of @max items of @item_size. By default, the pool | 2433 | * Pre-allocate a pool of @max items of @item_size. By default, the pool |
2334 | * should be accessed via kfifo_{get,put} on q->queue. | 2434 | * should be accessed via kfifo_{get,put} on q->queue. |
@@ -2595,6 +2695,7 @@ iscsi_session_setup(struct iscsi_transport *iscsit, struct Scsi_Host *shost, | |||
2595 | session->host = shost; | 2695 | session->host = shost; |
2596 | session->state = ISCSI_STATE_FREE; | 2696 | session->state = ISCSI_STATE_FREE; |
2597 | session->fast_abort = 1; | 2697 | session->fast_abort = 1; |
2698 | session->tgt_reset_timeout = 30; | ||
2598 | session->lu_reset_timeout = 15; | 2699 | session->lu_reset_timeout = 15; |
2599 | session->abort_timeout = 10; | 2700 | session->abort_timeout = 10; |
2600 | session->scsi_cmds_max = scsi_cmds; | 2701 | session->scsi_cmds_max = scsi_cmds; |
@@ -3033,6 +3134,9 @@ int iscsi_set_param(struct iscsi_cls_conn *cls_conn, | |||
3033 | case ISCSI_PARAM_LU_RESET_TMO: | 3134 | case ISCSI_PARAM_LU_RESET_TMO: |
3034 | sscanf(buf, "%d", &session->lu_reset_timeout); | 3135 | sscanf(buf, "%d", &session->lu_reset_timeout); |
3035 | break; | 3136 | break; |
3137 | case ISCSI_PARAM_TGT_RESET_TMO: | ||
3138 | sscanf(buf, "%d", &session->tgt_reset_timeout); | ||
3139 | break; | ||
3036 | case ISCSI_PARAM_PING_TMO: | 3140 | case ISCSI_PARAM_PING_TMO: |
3037 | sscanf(buf, "%d", &conn->ping_timeout); | 3141 | sscanf(buf, "%d", &conn->ping_timeout); |
3038 | break; | 3142 | break; |
@@ -3132,6 +3236,9 @@ int iscsi_session_get_param(struct iscsi_cls_session *cls_session, | |||
3132 | case ISCSI_PARAM_LU_RESET_TMO: | 3236 | case ISCSI_PARAM_LU_RESET_TMO: |
3133 | len = sprintf(buf, "%d\n", session->lu_reset_timeout); | 3237 | len = sprintf(buf, "%d\n", session->lu_reset_timeout); |
3134 | break; | 3238 | break; |
3239 | case ISCSI_PARAM_TGT_RESET_TMO: | ||
3240 | len = sprintf(buf, "%d\n", session->tgt_reset_timeout); | ||
3241 | break; | ||
3135 | case ISCSI_PARAM_INITIAL_R2T_EN: | 3242 | case ISCSI_PARAM_INITIAL_R2T_EN: |
3136 | len = sprintf(buf, "%d\n", session->initial_r2t_en); | 3243 | len = sprintf(buf, "%d\n", session->initial_r2t_en); |
3137 | break; | 3244 | break; |