.. | .. |
---|
| 1 | +// SPDX-License-Identifier: GPL-2.0-or-later |
---|
1 | 2 | /* |
---|
2 | 3 | * iSCSI lib functions |
---|
3 | 4 | * |
---|
.. | .. |
---|
6 | 7 | * Copyright (C) 2004 - 2005 Dmitry Yusupov |
---|
7 | 8 | * Copyright (C) 2004 - 2005 Alex Aizman |
---|
8 | 9 | * maintained by open-iscsi@googlegroups.com |
---|
9 | | - * |
---|
10 | | - * This program is free software; you can redistribute it and/or modify |
---|
11 | | - * it under the terms of the GNU General Public License as published by |
---|
12 | | - * the Free Software Foundation; either version 2 of the License, or |
---|
13 | | - * (at your option) any later version. |
---|
14 | | - * |
---|
15 | | - * This program is distributed in the hope that it will be useful, |
---|
16 | | - * but WITHOUT ANY WARRANTY; without even the implied warranty of |
---|
17 | | - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
---|
18 | | - * GNU General Public License for more details. |
---|
19 | | - * |
---|
20 | | - * You should have received a copy of the GNU General Public License |
---|
21 | | - * along with this program; if not, write to the Free Software |
---|
22 | | - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. |
---|
23 | 10 | */ |
---|
24 | 11 | #include <linux/types.h> |
---|
25 | 12 | #include <linux/kfifo.h> |
---|
.. | .. |
---|
40 | 27 | #include <scsi/scsi_transport.h> |
---|
41 | 28 | #include <scsi/scsi_transport_iscsi.h> |
---|
42 | 29 | #include <scsi/libiscsi.h> |
---|
| 30 | +#include <trace/events/iscsi.h> |
---|
43 | 31 | |
---|
44 | 32 | static int iscsi_dbg_lib_conn; |
---|
45 | 33 | module_param_named(debug_libiscsi_conn, iscsi_dbg_lib_conn, int, |
---|
.. | .. |
---|
68 | 56 | iscsi_conn_printk(KERN_INFO, _conn, \ |
---|
69 | 57 | "%s " dbg_fmt, \ |
---|
70 | 58 | __func__, ##arg); \ |
---|
| 59 | + iscsi_dbg_trace(trace_iscsi_dbg_conn, \ |
---|
| 60 | + &(_conn)->cls_conn->dev, \ |
---|
| 61 | + "%s " dbg_fmt, __func__, ##arg);\ |
---|
71 | 62 | } while (0); |
---|
72 | 63 | |
---|
73 | 64 | #define ISCSI_DBG_SESSION(_session, dbg_fmt, arg...) \ |
---|
.. | .. |
---|
76 | 67 | iscsi_session_printk(KERN_INFO, _session, \ |
---|
77 | 68 | "%s " dbg_fmt, \ |
---|
78 | 69 | __func__, ##arg); \ |
---|
| 70 | + iscsi_dbg_trace(trace_iscsi_dbg_session, \ |
---|
| 71 | + &(_session)->cls_session->dev, \ |
---|
| 72 | + "%s " dbg_fmt, __func__, ##arg); \ |
---|
79 | 73 | } while (0); |
---|
80 | 74 | |
---|
81 | 75 | #define ISCSI_DBG_EH(_session, dbg_fmt, arg...) \ |
---|
.. | .. |
---|
84 | 78 | iscsi_session_printk(KERN_INFO, _session, \ |
---|
85 | 79 | "%s " dbg_fmt, \ |
---|
86 | 80 | __func__, ##arg); \ |
---|
| 81 | + iscsi_dbg_trace(trace_iscsi_dbg_eh, \ |
---|
| 82 | + &(_session)->cls_session->dev, \ |
---|
| 83 | + "%s " dbg_fmt, __func__, ##arg); \ |
---|
87 | 84 | } while (0); |
---|
88 | 85 | |
---|
89 | 86 | inline void iscsi_conn_queue_work(struct iscsi_conn *conn) |
---|
.. | .. |
---|
218 | 215 | return 0; |
---|
219 | 216 | } |
---|
220 | 217 | |
---|
221 | | -static int iscsi_prep_bidi_ahs(struct iscsi_task *task) |
---|
222 | | -{ |
---|
223 | | - struct scsi_cmnd *sc = task->sc; |
---|
224 | | - struct iscsi_rlength_ahdr *rlen_ahdr; |
---|
225 | | - int rc; |
---|
226 | | - |
---|
227 | | - rlen_ahdr = iscsi_next_hdr(task); |
---|
228 | | - rc = iscsi_add_hdr(task, sizeof(*rlen_ahdr)); |
---|
229 | | - if (rc) |
---|
230 | | - return rc; |
---|
231 | | - |
---|
232 | | - rlen_ahdr->ahslength = |
---|
233 | | - cpu_to_be16(sizeof(rlen_ahdr->read_length) + |
---|
234 | | - sizeof(rlen_ahdr->reserved)); |
---|
235 | | - rlen_ahdr->ahstype = ISCSI_AHSTYPE_RLENGTH; |
---|
236 | | - rlen_ahdr->reserved = 0; |
---|
237 | | - rlen_ahdr->read_length = cpu_to_be32(scsi_in(sc)->length); |
---|
238 | | - |
---|
239 | | - ISCSI_DBG_SESSION(task->conn->session, |
---|
240 | | - "bidi-in rlen_ahdr->read_length(%d) " |
---|
241 | | - "rlen_ahdr->ahslength(%d)\n", |
---|
242 | | - be32_to_cpu(rlen_ahdr->read_length), |
---|
243 | | - be16_to_cpu(rlen_ahdr->ahslength)); |
---|
244 | | - return 0; |
---|
245 | | -} |
---|
246 | | - |
---|
247 | 218 | /** |
---|
248 | 219 | * iscsi_check_tmf_restrictions - check if a task is affected by TMF |
---|
249 | 220 | * @task: iscsi task |
---|
.. | .. |
---|
277 | 248 | hdr_lun = scsilun_to_int(&tmf->lun); |
---|
278 | 249 | if (hdr_lun != task->sc->device->lun) |
---|
279 | 250 | return 0; |
---|
280 | | - /* fall through */ |
---|
| 251 | + fallthrough; |
---|
281 | 252 | case ISCSI_TM_FUNC_TARGET_WARM_RESET: |
---|
282 | 253 | /* |
---|
283 | 254 | * Fail all SCSI cmd PDUs |
---|
.. | .. |
---|
377 | 348 | memcpy(hdr->cdb, sc->cmnd, cmd_len); |
---|
378 | 349 | |
---|
379 | 350 | task->imm_count = 0; |
---|
380 | | - if (scsi_bidi_cmnd(sc)) { |
---|
381 | | - hdr->flags |= ISCSI_FLAG_CMD_READ; |
---|
382 | | - rc = iscsi_prep_bidi_ahs(task); |
---|
383 | | - if (rc) |
---|
384 | | - return rc; |
---|
385 | | - } |
---|
386 | | - |
---|
387 | 351 | if (scsi_get_prot_op(sc) != SCSI_PROT_NORMAL) |
---|
388 | 352 | task->protected = true; |
---|
389 | 353 | |
---|
.. | .. |
---|
458 | 422 | |
---|
459 | 423 | conn->scsicmd_pdus_cnt++; |
---|
460 | 424 | ISCSI_DBG_SESSION(session, "iscsi prep [%s cid %d sc %p cdb 0x%x " |
---|
461 | | - "itt 0x%x len %d bidi_len %d cmdsn %d win %d]\n", |
---|
462 | | - scsi_bidi_cmnd(sc) ? "bidirectional" : |
---|
| 425 | + "itt 0x%x len %d cmdsn %d win %d]\n", |
---|
463 | 426 | sc->sc_data_direction == DMA_TO_DEVICE ? |
---|
464 | 427 | "write" : "read", conn->id, sc, sc->cmnd[0], |
---|
465 | 428 | task->itt, transfer_length, |
---|
466 | | - scsi_bidi_cmnd(sc) ? scsi_in(sc)->length : 0, |
---|
467 | 429 | session->cmdsn, |
---|
468 | 430 | session->max_cmdsn - session->exp_cmdsn + 1); |
---|
469 | 431 | return 0; |
---|
.. | .. |
---|
632 | 594 | state = ISCSI_TASK_ABRT_TMF; |
---|
633 | 595 | |
---|
634 | 596 | sc->result = err << 16; |
---|
635 | | - if (!scsi_bidi_cmnd(sc)) |
---|
636 | | - scsi_set_resid(sc, scsi_bufflen(sc)); |
---|
637 | | - else { |
---|
638 | | - scsi_out(sc)->resid = scsi_out(sc)->length; |
---|
639 | | - scsi_in(sc)->resid = scsi_in(sc)->length; |
---|
640 | | - } |
---|
| 597 | + scsi_set_resid(sc, scsi_bufflen(sc)); |
---|
641 | 598 | |
---|
642 | 599 | /* regular RX path uses back_lock */ |
---|
643 | 600 | spin_lock_bh(&conn->session->back_lock); |
---|
.. | .. |
---|
826 | 783 | * @datalen: len of buffer |
---|
827 | 784 | * |
---|
828 | 785 | * iscsi_cmd_rsp sets up the scsi_cmnd fields based on the PDU and |
---|
829 | | - * then completes the command and task. |
---|
| 786 | + * then completes the command and task. called under back_lock |
---|
830 | 787 | **/ |
---|
831 | 788 | static void iscsi_scsi_cmd_rsp(struct iscsi_conn *conn, struct iscsi_hdr *hdr, |
---|
832 | 789 | struct iscsi_task *task, char *data, |
---|
.. | .. |
---|
895 | 852 | |
---|
896 | 853 | if (rhdr->flags & (ISCSI_FLAG_CMD_BIDI_UNDERFLOW | |
---|
897 | 854 | ISCSI_FLAG_CMD_BIDI_OVERFLOW)) { |
---|
898 | | - int res_count = be32_to_cpu(rhdr->bi_residual_count); |
---|
899 | | - |
---|
900 | | - if (scsi_bidi_cmnd(sc) && res_count > 0 && |
---|
901 | | - (rhdr->flags & ISCSI_FLAG_CMD_BIDI_OVERFLOW || |
---|
902 | | - res_count <= scsi_in(sc)->length)) |
---|
903 | | - scsi_in(sc)->resid = res_count; |
---|
904 | | - else |
---|
905 | | - sc->result = (DID_BAD_TARGET << 16) | rhdr->cmd_status; |
---|
| 855 | + sc->result = (DID_BAD_TARGET << 16) | rhdr->cmd_status; |
---|
906 | 856 | } |
---|
907 | 857 | |
---|
908 | 858 | if (rhdr->flags & (ISCSI_FLAG_CMD_UNDERFLOW | |
---|
.. | .. |
---|
929 | 879 | * @conn: iscsi connection |
---|
930 | 880 | * @hdr: iscsi pdu |
---|
931 | 881 | * @task: scsi command task |
---|
| 882 | + * |
---|
| 883 | + * iscsi_data_in_rsp sets up the scsi_cmnd fields based on the data received |
---|
| 884 | + * then completes the command and task. called under back_lock |
---|
932 | 885 | **/ |
---|
933 | 886 | static void |
---|
934 | 887 | iscsi_data_in_rsp(struct iscsi_conn *conn, struct iscsi_hdr *hdr, |
---|
.. | .. |
---|
949 | 902 | |
---|
950 | 903 | if (res_count > 0 && |
---|
951 | 904 | (rhdr->flags & ISCSI_FLAG_CMD_OVERFLOW || |
---|
952 | | - res_count <= scsi_in(sc)->length)) |
---|
953 | | - scsi_in(sc)->resid = res_count; |
---|
| 905 | + res_count <= sc->sdb.length)) |
---|
| 906 | + scsi_set_resid(sc, res_count); |
---|
954 | 907 | else |
---|
955 | 908 | sc->result = (DID_BAD_TARGET << 16) | rhdr->cmd_status; |
---|
956 | 909 | } |
---|
.. | .. |
---|
1018 | 971 | return 0; |
---|
1019 | 972 | } |
---|
1020 | 973 | |
---|
| 974 | +/** |
---|
| 975 | + * iscsi_nop_out_rsp - SCSI NOP Response processing |
---|
| 976 | + * @task: scsi command task |
---|
| 977 | + * @nop: the nop structure |
---|
| 978 | + * @data: where to put the data |
---|
| 979 | + * @datalen: length of data |
---|
| 980 | + * |
---|
| 981 | + * iscsi_nop_out_rsp handles nop response from use or |
---|
| 982 | + * from user space. called under back_lock |
---|
| 983 | + **/ |
---|
1021 | 984 | static int iscsi_nop_out_rsp(struct iscsi_task *task, |
---|
1022 | 985 | struct iscsi_nopin *nop, char *data, int datalen) |
---|
1023 | 986 | { |
---|
.. | .. |
---|
1404 | 1367 | } |
---|
1405 | 1368 | EXPORT_SYMBOL_GPL(iscsi_session_failure); |
---|
1406 | 1369 | |
---|
1407 | | -void iscsi_conn_failure(struct iscsi_conn *conn, enum iscsi_err err) |
---|
| 1370 | +static bool iscsi_set_conn_failed(struct iscsi_conn *conn) |
---|
1408 | 1371 | { |
---|
1409 | 1372 | struct iscsi_session *session = conn->session; |
---|
1410 | 1373 | |
---|
1411 | | - spin_lock_bh(&session->frwd_lock); |
---|
1412 | | - if (session->state == ISCSI_STATE_FAILED) { |
---|
1413 | | - spin_unlock_bh(&session->frwd_lock); |
---|
1414 | | - return; |
---|
1415 | | - } |
---|
| 1374 | + if (session->state == ISCSI_STATE_FAILED) |
---|
| 1375 | + return false; |
---|
1416 | 1376 | |
---|
1417 | 1377 | if (conn->stop_stage == 0) |
---|
1418 | 1378 | session->state = ISCSI_STATE_FAILED; |
---|
1419 | | - spin_unlock_bh(&session->frwd_lock); |
---|
1420 | 1379 | |
---|
1421 | 1380 | set_bit(ISCSI_SUSPEND_BIT, &conn->suspend_tx); |
---|
1422 | 1381 | set_bit(ISCSI_SUSPEND_BIT, &conn->suspend_rx); |
---|
1423 | | - iscsi_conn_error_event(conn->cls_conn, err); |
---|
| 1382 | + return true; |
---|
| 1383 | +} |
---|
| 1384 | + |
---|
| 1385 | +void iscsi_conn_failure(struct iscsi_conn *conn, enum iscsi_err err) |
---|
| 1386 | +{ |
---|
| 1387 | + struct iscsi_session *session = conn->session; |
---|
| 1388 | + bool needs_evt; |
---|
| 1389 | + |
---|
| 1390 | + spin_lock_bh(&session->frwd_lock); |
---|
| 1391 | + needs_evt = iscsi_set_conn_failed(conn); |
---|
| 1392 | + spin_unlock_bh(&session->frwd_lock); |
---|
| 1393 | + |
---|
| 1394 | + if (needs_evt) |
---|
| 1395 | + iscsi_conn_error_event(conn->cls_conn, err); |
---|
1424 | 1396 | } |
---|
1425 | 1397 | EXPORT_SYMBOL_GPL(iscsi_conn_failure); |
---|
1426 | 1398 | |
---|
.. | .. |
---|
1706 | 1678 | sc->result = DID_NO_CONNECT << 16; |
---|
1707 | 1679 | break; |
---|
1708 | 1680 | } |
---|
1709 | | - /* fall through */ |
---|
| 1681 | + fallthrough; |
---|
1710 | 1682 | case ISCSI_STATE_IN_RECOVERY: |
---|
1711 | 1683 | reason = FAILURE_SESSION_IN_RECOVERY; |
---|
1712 | 1684 | sc->result = DID_IMM_RETRY << 16; |
---|
.. | .. |
---|
1782 | 1754 | return 0; |
---|
1783 | 1755 | |
---|
1784 | 1756 | prepd_reject: |
---|
| 1757 | + spin_lock_bh(&session->back_lock); |
---|
1785 | 1758 | iscsi_complete_task(task, ISCSI_TASK_REQUEUE_SCSIQ); |
---|
| 1759 | + spin_unlock_bh(&session->back_lock); |
---|
1786 | 1760 | reject: |
---|
1787 | 1761 | spin_unlock_bh(&session->frwd_lock); |
---|
1788 | 1762 | ISCSI_DBG_SESSION(session, "cmd 0x%x rejected (%d)\n", |
---|
.. | .. |
---|
1790 | 1764 | return SCSI_MLQUEUE_TARGET_BUSY; |
---|
1791 | 1765 | |
---|
1792 | 1766 | prepd_fault: |
---|
| 1767 | + spin_lock_bh(&session->back_lock); |
---|
1793 | 1768 | iscsi_complete_task(task, ISCSI_TASK_REQUEUE_SCSIQ); |
---|
| 1769 | + spin_unlock_bh(&session->back_lock); |
---|
1794 | 1770 | fault: |
---|
1795 | 1771 | spin_unlock_bh(&session->frwd_lock); |
---|
1796 | 1772 | ISCSI_DBG_SESSION(session, "iscsi: cmd 0x%x is not queued (%d)\n", |
---|
1797 | 1773 | sc->cmnd[0], reason); |
---|
1798 | | - if (!scsi_bidi_cmnd(sc)) |
---|
1799 | | - scsi_set_resid(sc, scsi_bufflen(sc)); |
---|
1800 | | - else { |
---|
1801 | | - scsi_out(sc)->resid = scsi_out(sc)->length; |
---|
1802 | | - scsi_in(sc)->resid = scsi_in(sc)->length; |
---|
1803 | | - } |
---|
| 1774 | + scsi_set_resid(sc, scsi_bufflen(sc)); |
---|
1804 | 1775 | sc->scsi_done(sc); |
---|
1805 | 1776 | return 0; |
---|
1806 | 1777 | } |
---|
.. | .. |
---|
2155 | 2126 | spin_unlock(&session->frwd_lock); |
---|
2156 | 2127 | } |
---|
2157 | 2128 | |
---|
| 2129 | +/** |
---|
| 2130 | + * iscsi_conn_unbind - prevent queueing to conn. |
---|
| 2131 | + * @cls_conn: iscsi conn ep is bound to. |
---|
| 2132 | + * @is_active: is the conn in use for boot or is this for EH/termination |
---|
| 2133 | + * |
---|
| 2134 | + * This must be called by drivers implementing the ep_disconnect callout. |
---|
| 2135 | + * It disables queueing to the connection from libiscsi in preparation for |
---|
| 2136 | + * an ep_disconnect call. |
---|
| 2137 | + */ |
---|
| 2138 | +void iscsi_conn_unbind(struct iscsi_cls_conn *cls_conn, bool is_active) |
---|
| 2139 | +{ |
---|
| 2140 | + struct iscsi_session *session; |
---|
| 2141 | + struct iscsi_conn *conn; |
---|
| 2142 | + |
---|
| 2143 | + if (!cls_conn) |
---|
| 2144 | + return; |
---|
| 2145 | + |
---|
| 2146 | + conn = cls_conn->dd_data; |
---|
| 2147 | + session = conn->session; |
---|
| 2148 | + /* |
---|
| 2149 | + * Wait for iscsi_eh calls to exit. We don't wait for the tmf to |
---|
| 2150 | + * complete or timeout. The caller just wants to know what's running |
---|
| 2151 | + * is everything that needs to be cleaned up, and no cmds will be |
---|
| 2152 | + * queued. |
---|
| 2153 | + */ |
---|
| 2154 | + mutex_lock(&session->eh_mutex); |
---|
| 2155 | + |
---|
| 2156 | + iscsi_suspend_queue(conn); |
---|
| 2157 | + iscsi_suspend_tx(conn); |
---|
| 2158 | + |
---|
| 2159 | + spin_lock_bh(&session->frwd_lock); |
---|
| 2160 | + if (!is_active) { |
---|
| 2161 | + /* |
---|
| 2162 | + * if logout timed out before userspace could even send a PDU |
---|
| 2163 | + * the state might still be in ISCSI_STATE_LOGGED_IN and |
---|
| 2164 | + * allowing new cmds and TMFs. |
---|
| 2165 | + */ |
---|
| 2166 | + if (session->state == ISCSI_STATE_LOGGED_IN) |
---|
| 2167 | + iscsi_set_conn_failed(conn); |
---|
| 2168 | + } |
---|
| 2169 | + spin_unlock_bh(&session->frwd_lock); |
---|
| 2170 | + mutex_unlock(&session->eh_mutex); |
---|
| 2171 | +} |
---|
| 2172 | +EXPORT_SYMBOL_GPL(iscsi_conn_unbind); |
---|
| 2173 | + |
---|
2158 | 2174 | static void iscsi_prep_abort_task_pdu(struct iscsi_task *task, |
---|
2159 | 2175 | struct iscsi_tm *hdr) |
---|
2160 | 2176 | { |
---|
.. | .. |
---|
2271 | 2287 | "progress\n"); |
---|
2272 | 2288 | goto success; |
---|
2273 | 2289 | } |
---|
2274 | | - /* fall through */ |
---|
| 2290 | + fallthrough; |
---|
2275 | 2291 | default: |
---|
2276 | 2292 | session->tmf_state = TMF_INITIAL; |
---|
2277 | 2293 | goto failed; |
---|
.. | .. |
---|
2658 | 2674 | if (xmit_can_sleep) { |
---|
2659 | 2675 | snprintf(ihost->workq_name, sizeof(ihost->workq_name), |
---|
2660 | 2676 | "iscsi_q_%d", shost->host_no); |
---|
2661 | | - ihost->workq = create_singlethread_workqueue(ihost->workq_name); |
---|
| 2677 | + ihost->workq = alloc_workqueue("%s", |
---|
| 2678 | + WQ_SYSFS | __WQ_LEGACY | WQ_MEM_RECLAIM | WQ_UNBOUND, |
---|
| 2679 | + 1, ihost->workq_name); |
---|
2662 | 2680 | if (!ihost->workq) |
---|
2663 | 2681 | goto free_host; |
---|
2664 | 2682 | } |
---|
.. | .. |
---|
2802 | 2820 | "must be a power of 2.\n", total_cmds); |
---|
2803 | 2821 | total_cmds = rounddown_pow_of_two(total_cmds); |
---|
2804 | 2822 | if (total_cmds < ISCSI_TOTAL_CMDS_MIN) |
---|
2805 | | - return NULL; |
---|
| 2823 | + goto dec_session_count; |
---|
2806 | 2824 | printk(KERN_INFO "iscsi: Rounding can_queue to %d.\n", |
---|
2807 | 2825 | total_cmds); |
---|
2808 | 2826 | } |
---|
.. | .. |
---|
3113 | 3131 | state = ISCSI_TASK_ABRT_SESS_RECOV; |
---|
3114 | 3132 | if (task->state == ISCSI_TASK_PENDING) |
---|
3115 | 3133 | state = ISCSI_TASK_COMPLETED; |
---|
| 3134 | + spin_lock_bh(&session->back_lock); |
---|
3116 | 3135 | iscsi_complete_task(task, state); |
---|
3117 | | - |
---|
| 3136 | + spin_unlock_bh(&session->back_lock); |
---|
3118 | 3137 | } |
---|
3119 | 3138 | } |
---|
3120 | 3139 | |
---|
3121 | | -static void iscsi_start_session_recovery(struct iscsi_session *session, |
---|
3122 | | - struct iscsi_conn *conn, int flag) |
---|
| 3140 | +void iscsi_conn_stop(struct iscsi_cls_conn *cls_conn, int flag) |
---|
3123 | 3141 | { |
---|
| 3142 | + struct iscsi_conn *conn = cls_conn->dd_data; |
---|
| 3143 | + struct iscsi_session *session = conn->session; |
---|
3124 | 3144 | int old_stop_stage; |
---|
3125 | 3145 | |
---|
3126 | 3146 | mutex_lock(&session->eh_mutex); |
---|
.. | .. |
---|
3177 | 3197 | memset(&session->tmhdr, 0, sizeof(session->tmhdr)); |
---|
3178 | 3198 | spin_unlock_bh(&session->frwd_lock); |
---|
3179 | 3199 | mutex_unlock(&session->eh_mutex); |
---|
3180 | | -} |
---|
3181 | | - |
---|
3182 | | -void iscsi_conn_stop(struct iscsi_cls_conn *cls_conn, int flag) |
---|
3183 | | -{ |
---|
3184 | | - struct iscsi_conn *conn = cls_conn->dd_data; |
---|
3185 | | - struct iscsi_session *session = conn->session; |
---|
3186 | | - |
---|
3187 | | - switch (flag) { |
---|
3188 | | - case STOP_CONN_RECOVER: |
---|
3189 | | - case STOP_CONN_TERM: |
---|
3190 | | - iscsi_start_session_recovery(session, conn, flag); |
---|
3191 | | - break; |
---|
3192 | | - default: |
---|
3193 | | - iscsi_conn_printk(KERN_ERR, conn, |
---|
3194 | | - "invalid stop flag %d\n", flag); |
---|
3195 | | - } |
---|
3196 | 3200 | } |
---|
3197 | 3201 | EXPORT_SYMBOL_GPL(iscsi_conn_stop); |
---|
3198 | 3202 | |
---|