aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/scsi/libiscsi.c
diff options
context:
space:
mode:
authorMike Christie <michaelc@cs.wisc.edu>2009-11-11 17:34:33 -0500
committerJames Bottomley <James.Bottomley@suse.de>2009-12-04 13:01:36 -0500
commit3fe5ae8b4c4d3a82c755074878da7ddb9dde381e (patch)
tree2565bc75ba1ab288cff50d4290eca8312f87bb04 /drivers/scsi/libiscsi.c
parent5d12c05e29fc8715e3e32f57a8cced9290d87c55 (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.c251
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}
1758EXPORT_SYMBOL_GPL(iscsi_target_alloc); 1758EXPORT_SYMBOL_GPL(iscsi_target_alloc);
1759 1759
1760void 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}
1772EXPORT_SYMBOL_GPL(iscsi_session_recovery_timedout);
1773
1774int 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) {
1787failed:
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}
1824EXPORT_SYMBOL_GPL(iscsi_eh_target_reset);
1825
1826static void iscsi_tmf_timedout(unsigned long data) 1760static 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}
2330EXPORT_SYMBOL_GPL(iscsi_eh_device_reset); 2264EXPORT_SYMBOL_GPL(iscsi_eh_device_reset);
2331 2265
2266void 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}
2278EXPORT_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 */
2287static 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) {
2300failed:
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
2339static 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 */
2355int 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
2419unlock:
2420 spin_unlock_bh(&session->lock);
2421done:
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}
2430EXPORT_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;