.. | .. |
---|
| 1 | +// SPDX-License-Identifier: GPL-2.0-or-later |
---|
1 | 2 | /* |
---|
2 | 3 | * iSCSI transport class definitions |
---|
3 | 4 | * |
---|
.. | .. |
---|
5 | 6 | * Copyright (C) Mike Christie, 2004 - 2005 |
---|
6 | 7 | * Copyright (C) Dmitry Yusupov, 2004 - 2005 |
---|
7 | 8 | * Copyright (C) Alex Aizman, 2004 - 2005 |
---|
8 | | - * |
---|
9 | | - * This program is free software; you can redistribute it and/or modify |
---|
10 | | - * it under the terms of the GNU General Public License as published by |
---|
11 | | - * the Free Software Foundation; either version 2 of the License, or |
---|
12 | | - * (at your option) any later version. |
---|
13 | | - * |
---|
14 | | - * This program is distributed in the hope that it will be useful, |
---|
15 | | - * but WITHOUT ANY WARRANTY; without even the implied warranty of |
---|
16 | | - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
---|
17 | | - * GNU General Public License for more details. |
---|
18 | | - * |
---|
19 | | - * You should have received a copy of the GNU General Public License |
---|
20 | | - * along with this program; if not, write to the Free Software |
---|
21 | | - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. |
---|
22 | 9 | */ |
---|
23 | 10 | #include <linux/module.h> |
---|
24 | 11 | #include <linux/mutex.h> |
---|
.. | .. |
---|
38 | 25 | #define ISCSI_TRANSPORT_VERSION "2.0-870" |
---|
39 | 26 | |
---|
40 | 27 | #define ISCSI_SEND_MAX_ALLOWED 10 |
---|
| 28 | + |
---|
| 29 | +#define CREATE_TRACE_POINTS |
---|
| 30 | +#include <trace/events/iscsi.h> |
---|
| 31 | + |
---|
| 32 | +/* |
---|
| 33 | + * Export tracepoint symbols to be used by other modules. |
---|
| 34 | + */ |
---|
| 35 | +EXPORT_TRACEPOINT_SYMBOL_GPL(iscsi_dbg_conn); |
---|
| 36 | +EXPORT_TRACEPOINT_SYMBOL_GPL(iscsi_dbg_eh); |
---|
| 37 | +EXPORT_TRACEPOINT_SYMBOL_GPL(iscsi_dbg_session); |
---|
| 38 | +EXPORT_TRACEPOINT_SYMBOL_GPL(iscsi_dbg_tcp); |
---|
| 39 | +EXPORT_TRACEPOINT_SYMBOL_GPL(iscsi_dbg_sw_tcp); |
---|
41 | 40 | |
---|
42 | 41 | static int dbg_session; |
---|
43 | 42 | module_param_named(debug_session, dbg_session, int, |
---|
.. | .. |
---|
61 | 60 | iscsi_cls_session_printk(KERN_INFO, _session, \ |
---|
62 | 61 | "%s: " dbg_fmt, \ |
---|
63 | 62 | __func__, ##arg); \ |
---|
| 63 | + iscsi_dbg_trace(trace_iscsi_dbg_trans_session, \ |
---|
| 64 | + &(_session)->dev, \ |
---|
| 65 | + "%s " dbg_fmt, __func__, ##arg); \ |
---|
64 | 66 | } while (0); |
---|
65 | 67 | |
---|
66 | 68 | #define ISCSI_DBG_TRANS_CONN(_conn, dbg_fmt, arg...) \ |
---|
.. | .. |
---|
68 | 70 | if (dbg_conn) \ |
---|
69 | 71 | iscsi_cls_conn_printk(KERN_INFO, _conn, \ |
---|
70 | 72 | "%s: " dbg_fmt, \ |
---|
71 | | - __func__, ##arg); \ |
---|
| 73 | + __func__, ##arg); \ |
---|
| 74 | + iscsi_dbg_trace(trace_iscsi_dbg_trans_conn, \ |
---|
| 75 | + &(_conn)->dev, \ |
---|
| 76 | + "%s " dbg_fmt, __func__, ##arg); \ |
---|
72 | 77 | } while (0); |
---|
73 | 78 | |
---|
74 | 79 | struct iscsi_internal { |
---|
.. | .. |
---|
83 | 88 | |
---|
84 | 89 | static atomic_t iscsi_session_nr; /* sysfs session id for next new session */ |
---|
85 | 90 | static struct workqueue_struct *iscsi_eh_timer_workq; |
---|
| 91 | + |
---|
| 92 | +static struct workqueue_struct *iscsi_conn_cleanup_workq; |
---|
86 | 93 | |
---|
87 | 94 | static DEFINE_IDA(iscsi_sess_ida); |
---|
88 | 95 | /* |
---|
.. | .. |
---|
255 | 262 | } |
---|
256 | 263 | EXPORT_SYMBOL_GPL(iscsi_destroy_endpoint); |
---|
257 | 264 | |
---|
| 265 | +void iscsi_put_endpoint(struct iscsi_endpoint *ep) |
---|
| 266 | +{ |
---|
| 267 | + put_device(&ep->dev); |
---|
| 268 | +} |
---|
| 269 | +EXPORT_SYMBOL_GPL(iscsi_put_endpoint); |
---|
| 270 | + |
---|
| 271 | +/** |
---|
| 272 | + * iscsi_lookup_endpoint - get ep from handle |
---|
| 273 | + * @handle: endpoint handle |
---|
| 274 | + * |
---|
| 275 | + * Caller must do a iscsi_put_endpoint. |
---|
| 276 | + */ |
---|
258 | 277 | struct iscsi_endpoint *iscsi_lookup_endpoint(u64 handle) |
---|
259 | 278 | { |
---|
260 | | - struct iscsi_endpoint *ep; |
---|
261 | 279 | struct device *dev; |
---|
262 | 280 | |
---|
263 | 281 | dev = class_find_device(&iscsi_endpoint_class, NULL, &handle, |
---|
.. | .. |
---|
265 | 283 | if (!dev) |
---|
266 | 284 | return NULL; |
---|
267 | 285 | |
---|
268 | | - ep = iscsi_dev_to_endpoint(dev); |
---|
269 | | - /* |
---|
270 | | - * we can drop this now because the interface will prevent |
---|
271 | | - * removals and lookups from racing. |
---|
272 | | - */ |
---|
273 | | - put_device(dev); |
---|
274 | | - return ep; |
---|
| 286 | + return iscsi_dev_to_endpoint(dev); |
---|
275 | 287 | } |
---|
276 | 288 | EXPORT_SYMBOL_GPL(iscsi_lookup_endpoint); |
---|
277 | 289 | |
---|
.. | .. |
---|
1526 | 1538 | return -ENOTSUPP; |
---|
1527 | 1539 | |
---|
1528 | 1540 | snprintf(bsg_name, sizeof(bsg_name), "iscsi_host%d", shost->host_no); |
---|
1529 | | - q = bsg_setup_queue(dev, bsg_name, iscsi_bsg_host_dispatch, 0); |
---|
| 1541 | + q = bsg_setup_queue(dev, bsg_name, iscsi_bsg_host_dispatch, NULL, 0); |
---|
1530 | 1542 | if (IS_ERR(q)) { |
---|
1531 | 1543 | shost_printk(KERN_ERR, shost, "bsg interface failed to " |
---|
1532 | 1544 | "initialize - no request queue\n"); |
---|
.. | .. |
---|
1560 | 1572 | struct Scsi_Host *shost = dev_to_shost(dev); |
---|
1561 | 1573 | struct iscsi_cls_host *ihost = shost->shost_data; |
---|
1562 | 1574 | |
---|
1563 | | - if (ihost->bsg_q) { |
---|
1564 | | - bsg_unregister_queue(ihost->bsg_q); |
---|
1565 | | - blk_cleanup_queue(ihost->bsg_q); |
---|
1566 | | - } |
---|
| 1575 | + bsg_remove_queue(ihost->bsg_q); |
---|
1567 | 1576 | return 0; |
---|
1568 | 1577 | } |
---|
1569 | 1578 | |
---|
.. | .. |
---|
1591 | 1600 | static LIST_HEAD(sesslist); |
---|
1592 | 1601 | static DEFINE_SPINLOCK(sesslock); |
---|
1593 | 1602 | static LIST_HEAD(connlist); |
---|
| 1603 | +static LIST_HEAD(connlist_err); |
---|
1594 | 1604 | static DEFINE_SPINLOCK(connlock); |
---|
1595 | 1605 | |
---|
1596 | 1606 | static uint32_t iscsi_conn_get_sid(struct iscsi_cls_conn *conn) |
---|
.. | .. |
---|
1663 | 1673 | } |
---|
1664 | 1674 | return name; |
---|
1665 | 1675 | } |
---|
| 1676 | + |
---|
| 1677 | +static char *iscsi_session_target_state_name[] = { |
---|
| 1678 | + [ISCSI_SESSION_TARGET_UNBOUND] = "UNBOUND", |
---|
| 1679 | + [ISCSI_SESSION_TARGET_ALLOCATED] = "ALLOCATED", |
---|
| 1680 | + [ISCSI_SESSION_TARGET_SCANNED] = "SCANNED", |
---|
| 1681 | + [ISCSI_SESSION_TARGET_UNBINDING] = "UNBINDING", |
---|
| 1682 | +}; |
---|
1666 | 1683 | |
---|
1667 | 1684 | int iscsi_session_chkready(struct iscsi_cls_session *session) |
---|
1668 | 1685 | { |
---|
.. | .. |
---|
1795 | 1812 | if ((scan_data->channel == SCAN_WILD_CARD || |
---|
1796 | 1813 | scan_data->channel == 0) && |
---|
1797 | 1814 | (scan_data->id == SCAN_WILD_CARD || |
---|
1798 | | - scan_data->id == id)) |
---|
| 1815 | + scan_data->id == id)) { |
---|
1799 | 1816 | scsi_scan_target(&session->dev, 0, id, |
---|
1800 | 1817 | scan_data->lun, scan_data->rescan); |
---|
| 1818 | + spin_lock_irqsave(&session->lock, flags); |
---|
| 1819 | + session->target_state = ISCSI_SESSION_TARGET_SCANNED; |
---|
| 1820 | + spin_unlock_irqrestore(&session->lock, flags); |
---|
| 1821 | + } |
---|
1801 | 1822 | } |
---|
1802 | 1823 | |
---|
1803 | 1824 | user_scan_exit: |
---|
.. | .. |
---|
1940 | 1961 | */ |
---|
1941 | 1962 | void iscsi_unblock_session(struct iscsi_cls_session *session) |
---|
1942 | 1963 | { |
---|
| 1964 | + flush_work(&session->block_work); |
---|
| 1965 | + |
---|
1943 | 1966 | queue_work(iscsi_eh_timer_workq, &session->unblock_work); |
---|
1944 | 1967 | /* |
---|
1945 | | - * make sure all the events have completed before tell the driver |
---|
1946 | | - * it is safe |
---|
| 1968 | + * Blocking the session can be done from any context so we only |
---|
| 1969 | + * queue the block work. Make sure the unblock work has completed |
---|
| 1970 | + * because it flushes/cancels the other works and updates the state. |
---|
1947 | 1971 | */ |
---|
1948 | | - flush_workqueue(iscsi_eh_timer_workq); |
---|
| 1972 | + flush_work(&session->unblock_work); |
---|
1949 | 1973 | } |
---|
1950 | 1974 | EXPORT_SYMBOL_GPL(iscsi_unblock_session); |
---|
1951 | 1975 | |
---|
.. | .. |
---|
1983 | 2007 | struct iscsi_cls_host *ihost = shost->shost_data; |
---|
1984 | 2008 | unsigned long flags; |
---|
1985 | 2009 | unsigned int target_id; |
---|
| 2010 | + bool remove_target = true; |
---|
1986 | 2011 | |
---|
1987 | 2012 | ISCSI_DBG_TRANS_SESSION(session, "Unbinding session\n"); |
---|
1988 | 2013 | |
---|
1989 | 2014 | /* Prevent new scans and make sure scanning is not in progress */ |
---|
1990 | 2015 | mutex_lock(&ihost->mutex); |
---|
1991 | 2016 | spin_lock_irqsave(&session->lock, flags); |
---|
1992 | | - if (session->target_id == ISCSI_MAX_TARGET) { |
---|
| 2017 | + if (session->target_state == ISCSI_SESSION_TARGET_ALLOCATED) { |
---|
| 2018 | + remove_target = false; |
---|
| 2019 | + } else if (session->target_state != ISCSI_SESSION_TARGET_SCANNED) { |
---|
1993 | 2020 | spin_unlock_irqrestore(&session->lock, flags); |
---|
1994 | 2021 | mutex_unlock(&ihost->mutex); |
---|
1995 | | - goto unbind_session_exit; |
---|
| 2022 | + ISCSI_DBG_TRANS_SESSION(session, |
---|
| 2023 | + "Skipping target unbinding: Session is unbound/unbinding.\n"); |
---|
| 2024 | + return; |
---|
1996 | 2025 | } |
---|
1997 | 2026 | |
---|
| 2027 | + session->target_state = ISCSI_SESSION_TARGET_UNBINDING; |
---|
1998 | 2028 | target_id = session->target_id; |
---|
1999 | 2029 | session->target_id = ISCSI_MAX_TARGET; |
---|
2000 | 2030 | spin_unlock_irqrestore(&session->lock, flags); |
---|
2001 | 2031 | mutex_unlock(&ihost->mutex); |
---|
2002 | 2032 | |
---|
| 2033 | + if (remove_target) |
---|
| 2034 | + scsi_remove_target(&session->dev); |
---|
| 2035 | + |
---|
2003 | 2036 | if (session->ida_used) |
---|
2004 | 2037 | ida_simple_remove(&iscsi_sess_ida, target_id); |
---|
2005 | 2038 | |
---|
2006 | | - scsi_remove_target(&session->dev); |
---|
2007 | | - |
---|
2008 | | -unbind_session_exit: |
---|
2009 | 2039 | iscsi_session_event(session, ISCSI_KEVENT_UNBIND_SESSION); |
---|
2010 | 2040 | ISCSI_DBG_TRANS_SESSION(session, "Completed target removal\n"); |
---|
| 2041 | + |
---|
| 2042 | + spin_lock_irqsave(&session->lock, flags); |
---|
| 2043 | + session->target_state = ISCSI_SESSION_TARGET_UNBOUND; |
---|
| 2044 | + spin_unlock_irqrestore(&session->lock, flags); |
---|
| 2045 | +} |
---|
| 2046 | + |
---|
| 2047 | +static void __iscsi_destroy_session(struct work_struct *work) |
---|
| 2048 | +{ |
---|
| 2049 | + struct iscsi_cls_session *session = |
---|
| 2050 | + container_of(work, struct iscsi_cls_session, destroy_work); |
---|
| 2051 | + |
---|
| 2052 | + session->transport->destroy_session(session); |
---|
2011 | 2053 | } |
---|
2012 | 2054 | |
---|
2013 | 2055 | struct iscsi_cls_session * |
---|
.. | .. |
---|
2032 | 2074 | INIT_WORK(&session->block_work, __iscsi_block_session); |
---|
2033 | 2075 | INIT_WORK(&session->unbind_work, __iscsi_unbind_session); |
---|
2034 | 2076 | INIT_WORK(&session->scan_work, iscsi_scan_session); |
---|
| 2077 | + INIT_WORK(&session->destroy_work, __iscsi_destroy_session); |
---|
2035 | 2078 | spin_lock_init(&session->lock); |
---|
2036 | 2079 | |
---|
2037 | 2080 | /* this is released in the dev's release function */ |
---|
.. | .. |
---|
2067 | 2110 | session->ida_used = true; |
---|
2068 | 2111 | } else |
---|
2069 | 2112 | session->target_id = target_id; |
---|
| 2113 | + spin_lock_irqsave(&session->lock, flags); |
---|
| 2114 | + session->target_state = ISCSI_SESSION_TARGET_ALLOCATED; |
---|
| 2115 | + spin_unlock_irqrestore(&session->lock, flags); |
---|
2070 | 2116 | |
---|
2071 | 2117 | dev_set_name(&session->dev, "session%u", session->sid); |
---|
2072 | 2118 | err = device_add(&session->dev); |
---|
.. | .. |
---|
2075 | 2121 | "could not register session's dev\n"); |
---|
2076 | 2122 | goto release_ida; |
---|
2077 | 2123 | } |
---|
2078 | | - transport_register_device(&session->dev); |
---|
| 2124 | + err = transport_register_device(&session->dev); |
---|
| 2125 | + if (err) { |
---|
| 2126 | + iscsi_cls_session_printk(KERN_ERR, session, |
---|
| 2127 | + "could not register transport's dev\n"); |
---|
| 2128 | + goto release_dev; |
---|
| 2129 | + } |
---|
2079 | 2130 | |
---|
2080 | 2131 | spin_lock_irqsave(&sesslock, flags); |
---|
2081 | 2132 | list_add(&session->sess_list, &sesslist); |
---|
.. | .. |
---|
2085 | 2136 | ISCSI_DBG_TRANS_SESSION(session, "Completed session adding\n"); |
---|
2086 | 2137 | return 0; |
---|
2087 | 2138 | |
---|
| 2139 | +release_dev: |
---|
| 2140 | + device_del(&session->dev); |
---|
2088 | 2141 | release_ida: |
---|
2089 | 2142 | if (session->ida_used) |
---|
2090 | 2143 | ida_simple_remove(&iscsi_sess_ida, session->target_id); |
---|
.. | .. |
---|
2150 | 2203 | ISCSI_DBG_TRANS_SESSION(session, "Removing session\n"); |
---|
2151 | 2204 | |
---|
2152 | 2205 | spin_lock_irqsave(&sesslock, flags); |
---|
2153 | | - list_del(&session->sess_list); |
---|
| 2206 | + if (!list_empty(&session->sess_list)) |
---|
| 2207 | + list_del(&session->sess_list); |
---|
2154 | 2208 | spin_unlock_irqrestore(&sesslock, flags); |
---|
2155 | 2209 | |
---|
2156 | | - /* make sure there are no blocks/unblocks queued */ |
---|
2157 | | - flush_workqueue(iscsi_eh_timer_workq); |
---|
2158 | | - /* make sure the timedout callout is not running */ |
---|
2159 | | - if (!cancel_delayed_work(&session->recovery_work)) |
---|
2160 | | - flush_workqueue(iscsi_eh_timer_workq); |
---|
| 2210 | + flush_work(&session->block_work); |
---|
| 2211 | + flush_work(&session->unblock_work); |
---|
| 2212 | + cancel_delayed_work_sync(&session->recovery_work); |
---|
2161 | 2213 | /* |
---|
2162 | 2214 | * If we are blocked let commands flow again. The lld or iscsi |
---|
2163 | 2215 | * layer should set up the queuecommand to fail commands. |
---|
.. | .. |
---|
2189 | 2241 | device_del(&session->dev); |
---|
2190 | 2242 | } |
---|
2191 | 2243 | EXPORT_SYMBOL_GPL(iscsi_remove_session); |
---|
| 2244 | + |
---|
| 2245 | +static void iscsi_stop_conn(struct iscsi_cls_conn *conn, int flag) |
---|
| 2246 | +{ |
---|
| 2247 | + ISCSI_DBG_TRANS_CONN(conn, "Stopping conn.\n"); |
---|
| 2248 | + |
---|
| 2249 | + switch (flag) { |
---|
| 2250 | + case STOP_CONN_RECOVER: |
---|
| 2251 | + WRITE_ONCE(conn->state, ISCSI_CONN_FAILED); |
---|
| 2252 | + break; |
---|
| 2253 | + case STOP_CONN_TERM: |
---|
| 2254 | + WRITE_ONCE(conn->state, ISCSI_CONN_DOWN); |
---|
| 2255 | + break; |
---|
| 2256 | + default: |
---|
| 2257 | + iscsi_cls_conn_printk(KERN_ERR, conn, "invalid stop flag %d\n", |
---|
| 2258 | + flag); |
---|
| 2259 | + return; |
---|
| 2260 | + } |
---|
| 2261 | + |
---|
| 2262 | + conn->transport->stop_conn(conn, flag); |
---|
| 2263 | + ISCSI_DBG_TRANS_CONN(conn, "Stopping conn done.\n"); |
---|
| 2264 | +} |
---|
| 2265 | + |
---|
| 2266 | +static void iscsi_ep_disconnect(struct iscsi_cls_conn *conn, bool is_active) |
---|
| 2267 | +{ |
---|
| 2268 | + struct iscsi_cls_session *session = iscsi_conn_to_session(conn); |
---|
| 2269 | + struct iscsi_endpoint *ep; |
---|
| 2270 | + |
---|
| 2271 | + ISCSI_DBG_TRANS_CONN(conn, "disconnect ep.\n"); |
---|
| 2272 | + WRITE_ONCE(conn->state, ISCSI_CONN_FAILED); |
---|
| 2273 | + |
---|
| 2274 | + if (!conn->ep || !session->transport->ep_disconnect) |
---|
| 2275 | + return; |
---|
| 2276 | + |
---|
| 2277 | + ep = conn->ep; |
---|
| 2278 | + conn->ep = NULL; |
---|
| 2279 | + |
---|
| 2280 | + session->transport->unbind_conn(conn, is_active); |
---|
| 2281 | + session->transport->ep_disconnect(ep); |
---|
| 2282 | + ISCSI_DBG_TRANS_CONN(conn, "disconnect ep done.\n"); |
---|
| 2283 | +} |
---|
| 2284 | + |
---|
| 2285 | +static void iscsi_if_disconnect_bound_ep(struct iscsi_cls_conn *conn, |
---|
| 2286 | + struct iscsi_endpoint *ep, |
---|
| 2287 | + bool is_active) |
---|
| 2288 | +{ |
---|
| 2289 | + /* Check if this was a conn error and the kernel took ownership */ |
---|
| 2290 | + spin_lock_irq(&conn->lock); |
---|
| 2291 | + if (!test_bit(ISCSI_CLS_CONN_BIT_CLEANUP, &conn->flags)) { |
---|
| 2292 | + spin_unlock_irq(&conn->lock); |
---|
| 2293 | + iscsi_ep_disconnect(conn, is_active); |
---|
| 2294 | + } else { |
---|
| 2295 | + spin_unlock_irq(&conn->lock); |
---|
| 2296 | + ISCSI_DBG_TRANS_CONN(conn, "flush kernel conn cleanup.\n"); |
---|
| 2297 | + mutex_unlock(&conn->ep_mutex); |
---|
| 2298 | + |
---|
| 2299 | + flush_work(&conn->cleanup_work); |
---|
| 2300 | + /* |
---|
| 2301 | + * Userspace is now done with the EP so we can release the ref |
---|
| 2302 | + * iscsi_cleanup_conn_work_fn took. |
---|
| 2303 | + */ |
---|
| 2304 | + iscsi_put_endpoint(ep); |
---|
| 2305 | + mutex_lock(&conn->ep_mutex); |
---|
| 2306 | + } |
---|
| 2307 | +} |
---|
| 2308 | + |
---|
| 2309 | +static int iscsi_if_stop_conn(struct iscsi_transport *transport, |
---|
| 2310 | + struct iscsi_uevent *ev) |
---|
| 2311 | +{ |
---|
| 2312 | + int flag = ev->u.stop_conn.flag; |
---|
| 2313 | + struct iscsi_cls_conn *conn; |
---|
| 2314 | + |
---|
| 2315 | + conn = iscsi_conn_lookup(ev->u.stop_conn.sid, ev->u.stop_conn.cid); |
---|
| 2316 | + if (!conn) |
---|
| 2317 | + return -EINVAL; |
---|
| 2318 | + |
---|
| 2319 | + ISCSI_DBG_TRANS_CONN(conn, "iscsi if conn stop.\n"); |
---|
| 2320 | + /* |
---|
| 2321 | + * If this is a termination we have to call stop_conn with that flag |
---|
| 2322 | + * so the correct states get set. If we haven't run the work yet try to |
---|
| 2323 | + * avoid the extra run. |
---|
| 2324 | + */ |
---|
| 2325 | + if (flag == STOP_CONN_TERM) { |
---|
| 2326 | + cancel_work_sync(&conn->cleanup_work); |
---|
| 2327 | + iscsi_stop_conn(conn, flag); |
---|
| 2328 | + } else { |
---|
| 2329 | + /* |
---|
| 2330 | + * For offload, when iscsid is restarted it won't know about |
---|
| 2331 | + * existing endpoints so it can't do a ep_disconnect. We clean |
---|
| 2332 | + * it up here for userspace. |
---|
| 2333 | + */ |
---|
| 2334 | + mutex_lock(&conn->ep_mutex); |
---|
| 2335 | + if (conn->ep) |
---|
| 2336 | + iscsi_if_disconnect_bound_ep(conn, conn->ep, true); |
---|
| 2337 | + mutex_unlock(&conn->ep_mutex); |
---|
| 2338 | + |
---|
| 2339 | + /* |
---|
| 2340 | + * Figure out if it was the kernel or userspace initiating this. |
---|
| 2341 | + */ |
---|
| 2342 | + spin_lock_irq(&conn->lock); |
---|
| 2343 | + if (!test_and_set_bit(ISCSI_CLS_CONN_BIT_CLEANUP, &conn->flags)) { |
---|
| 2344 | + spin_unlock_irq(&conn->lock); |
---|
| 2345 | + iscsi_stop_conn(conn, flag); |
---|
| 2346 | + } else { |
---|
| 2347 | + spin_unlock_irq(&conn->lock); |
---|
| 2348 | + ISCSI_DBG_TRANS_CONN(conn, |
---|
| 2349 | + "flush kernel conn cleanup.\n"); |
---|
| 2350 | + flush_work(&conn->cleanup_work); |
---|
| 2351 | + } |
---|
| 2352 | + /* |
---|
| 2353 | + * Only clear for recovery to avoid extra cleanup runs during |
---|
| 2354 | + * termination. |
---|
| 2355 | + */ |
---|
| 2356 | + spin_lock_irq(&conn->lock); |
---|
| 2357 | + clear_bit(ISCSI_CLS_CONN_BIT_CLEANUP, &conn->flags); |
---|
| 2358 | + spin_unlock_irq(&conn->lock); |
---|
| 2359 | + } |
---|
| 2360 | + ISCSI_DBG_TRANS_CONN(conn, "iscsi if conn stop done.\n"); |
---|
| 2361 | + return 0; |
---|
| 2362 | +} |
---|
| 2363 | + |
---|
| 2364 | +static void iscsi_cleanup_conn_work_fn(struct work_struct *work) |
---|
| 2365 | +{ |
---|
| 2366 | + struct iscsi_cls_conn *conn = container_of(work, struct iscsi_cls_conn, |
---|
| 2367 | + cleanup_work); |
---|
| 2368 | + struct iscsi_cls_session *session = iscsi_conn_to_session(conn); |
---|
| 2369 | + |
---|
| 2370 | + mutex_lock(&conn->ep_mutex); |
---|
| 2371 | + /* |
---|
| 2372 | + * Get a ref to the ep, so we don't release its ID until after |
---|
| 2373 | + * userspace is done referencing it in iscsi_if_disconnect_bound_ep. |
---|
| 2374 | + */ |
---|
| 2375 | + if (conn->ep) |
---|
| 2376 | + get_device(&conn->ep->dev); |
---|
| 2377 | + iscsi_ep_disconnect(conn, false); |
---|
| 2378 | + |
---|
| 2379 | + if (system_state != SYSTEM_RUNNING) { |
---|
| 2380 | + /* |
---|
| 2381 | + * If the user has set up for the session to never timeout |
---|
| 2382 | + * then hang like they wanted. For all other cases fail right |
---|
| 2383 | + * away since userspace is not going to relogin. |
---|
| 2384 | + */ |
---|
| 2385 | + if (session->recovery_tmo > 0) |
---|
| 2386 | + session->recovery_tmo = 0; |
---|
| 2387 | + } |
---|
| 2388 | + |
---|
| 2389 | + iscsi_stop_conn(conn, STOP_CONN_RECOVER); |
---|
| 2390 | + mutex_unlock(&conn->ep_mutex); |
---|
| 2391 | + ISCSI_DBG_TRANS_CONN(conn, "cleanup done.\n"); |
---|
| 2392 | +} |
---|
2192 | 2393 | |
---|
2193 | 2394 | void iscsi_free_session(struct iscsi_cls_session *session) |
---|
2194 | 2395 | { |
---|
.. | .. |
---|
2228 | 2429 | conn->dd_data = &conn[1]; |
---|
2229 | 2430 | |
---|
2230 | 2431 | mutex_init(&conn->ep_mutex); |
---|
| 2432 | + spin_lock_init(&conn->lock); |
---|
2231 | 2433 | INIT_LIST_HEAD(&conn->conn_list); |
---|
| 2434 | + INIT_WORK(&conn->cleanup_work, iscsi_cleanup_conn_work_fn); |
---|
2232 | 2435 | conn->transport = transport; |
---|
2233 | 2436 | conn->cid = cid; |
---|
| 2437 | + WRITE_ONCE(conn->state, ISCSI_CONN_DOWN); |
---|
2234 | 2438 | |
---|
2235 | 2439 | /* this is released in the dev's release function */ |
---|
2236 | 2440 | if (!get_device(&session->dev)) |
---|
.. | .. |
---|
2245 | 2449 | "register connection's dev\n"); |
---|
2246 | 2450 | goto release_parent_ref; |
---|
2247 | 2451 | } |
---|
2248 | | - transport_register_device(&conn->dev); |
---|
| 2452 | + err = transport_register_device(&conn->dev); |
---|
| 2453 | + if (err) { |
---|
| 2454 | + iscsi_cls_session_printk(KERN_ERR, session, "could not " |
---|
| 2455 | + "register transport's dev\n"); |
---|
| 2456 | + goto release_conn_ref; |
---|
| 2457 | + } |
---|
2249 | 2458 | |
---|
2250 | 2459 | spin_lock_irqsave(&connlock, flags); |
---|
2251 | 2460 | list_add(&conn->conn_list, &connlist); |
---|
.. | .. |
---|
2254 | 2463 | ISCSI_DBG_TRANS_CONN(conn, "Completed conn creation\n"); |
---|
2255 | 2464 | return conn; |
---|
2256 | 2465 | |
---|
| 2466 | +release_conn_ref: |
---|
| 2467 | + device_unregister(&conn->dev); |
---|
| 2468 | + put_device(&session->dev); |
---|
| 2469 | + return NULL; |
---|
2257 | 2470 | release_parent_ref: |
---|
2258 | 2471 | put_device(&session->dev); |
---|
2259 | 2472 | free_conn: |
---|
.. | .. |
---|
2408 | 2621 | struct iscsi_uevent *ev; |
---|
2409 | 2622 | struct iscsi_internal *priv; |
---|
2410 | 2623 | int len = nlmsg_total_size(sizeof(*ev)); |
---|
| 2624 | + unsigned long flags; |
---|
| 2625 | + int state; |
---|
| 2626 | + |
---|
| 2627 | + spin_lock_irqsave(&conn->lock, flags); |
---|
| 2628 | + /* |
---|
| 2629 | + * Userspace will only do a stop call if we are at least bound. And, we |
---|
| 2630 | + * only need to do the in kernel cleanup if in the UP state so cmds can |
---|
| 2631 | + * be released to upper layers. If in other states just wait for |
---|
| 2632 | + * userspace to avoid races that can leave the cleanup_work queued. |
---|
| 2633 | + */ |
---|
| 2634 | + state = READ_ONCE(conn->state); |
---|
| 2635 | + switch (state) { |
---|
| 2636 | + case ISCSI_CONN_BOUND: |
---|
| 2637 | + case ISCSI_CONN_UP: |
---|
| 2638 | + if (!test_and_set_bit(ISCSI_CLS_CONN_BIT_CLEANUP, |
---|
| 2639 | + &conn->flags)) { |
---|
| 2640 | + queue_work(iscsi_conn_cleanup_workq, |
---|
| 2641 | + &conn->cleanup_work); |
---|
| 2642 | + } |
---|
| 2643 | + break; |
---|
| 2644 | + default: |
---|
| 2645 | + ISCSI_DBG_TRANS_CONN(conn, "Got conn error in state %d\n", |
---|
| 2646 | + state); |
---|
| 2647 | + break; |
---|
| 2648 | + } |
---|
| 2649 | + spin_unlock_irqrestore(&conn->lock, flags); |
---|
2411 | 2650 | |
---|
2412 | 2651 | priv = iscsi_if_transport_lookup(conn->transport); |
---|
2413 | 2652 | if (!priv) |
---|
.. | .. |
---|
2742 | 2981 | if (!conn) |
---|
2743 | 2982 | return -EINVAL; |
---|
2744 | 2983 | |
---|
| 2984 | + ISCSI_DBG_TRANS_CONN(conn, "Flushing cleanup during destruction\n"); |
---|
| 2985 | + flush_work(&conn->cleanup_work); |
---|
2745 | 2986 | ISCSI_DBG_TRANS_CONN(conn, "Destroying transport conn\n"); |
---|
| 2987 | + |
---|
2746 | 2988 | if (transport->destroy_conn) |
---|
2747 | 2989 | transport->destroy_conn(conn); |
---|
2748 | | - |
---|
2749 | 2990 | return 0; |
---|
2750 | 2991 | } |
---|
2751 | 2992 | |
---|
2752 | 2993 | static int |
---|
2753 | | -iscsi_set_param(struct iscsi_transport *transport, struct iscsi_uevent *ev) |
---|
| 2994 | +iscsi_if_set_param(struct iscsi_transport *transport, struct iscsi_uevent *ev, u32 rlen) |
---|
2754 | 2995 | { |
---|
2755 | 2996 | char *data = (char*)ev + sizeof(*ev); |
---|
2756 | 2997 | struct iscsi_cls_conn *conn; |
---|
2757 | 2998 | struct iscsi_cls_session *session; |
---|
2758 | | - int err = 0, value = 0; |
---|
| 2999 | + int err = 0, value = 0, state; |
---|
2759 | 3000 | |
---|
2760 | | - if (ev->u.set_param.len > PAGE_SIZE) |
---|
| 3001 | + if (ev->u.set_param.len > rlen || |
---|
| 3002 | + ev->u.set_param.len > PAGE_SIZE) |
---|
2761 | 3003 | return -EINVAL; |
---|
2762 | 3004 | |
---|
2763 | 3005 | session = iscsi_session_lookup(ev->u.set_param.sid); |
---|
2764 | 3006 | conn = iscsi_conn_lookup(ev->u.set_param.sid, ev->u.set_param.cid); |
---|
2765 | 3007 | if (!conn || !session) |
---|
| 3008 | + return -EINVAL; |
---|
| 3009 | + |
---|
| 3010 | + /* data will be regarded as NULL-ended string, do length check */ |
---|
| 3011 | + if (strlen(data) > ev->u.set_param.len) |
---|
2766 | 3012 | return -EINVAL; |
---|
2767 | 3013 | |
---|
2768 | 3014 | switch (ev->u.set_param.param) { |
---|
.. | .. |
---|
2772 | 3018 | session->recovery_tmo = value; |
---|
2773 | 3019 | break; |
---|
2774 | 3020 | default: |
---|
2775 | | - err = transport->set_param(conn, ev->u.set_param.param, |
---|
2776 | | - data, ev->u.set_param.len); |
---|
| 3021 | + state = READ_ONCE(conn->state); |
---|
| 3022 | + if (state == ISCSI_CONN_BOUND || state == ISCSI_CONN_UP) { |
---|
| 3023 | + err = transport->set_param(conn, ev->u.set_param.param, |
---|
| 3024 | + data, ev->u.set_param.len); |
---|
| 3025 | + } else { |
---|
| 3026 | + return -ENOTCONN; |
---|
| 3027 | + } |
---|
2777 | 3028 | } |
---|
2778 | 3029 | |
---|
2779 | 3030 | return err; |
---|
.. | .. |
---|
2828 | 3079 | ep = iscsi_lookup_endpoint(ep_handle); |
---|
2829 | 3080 | if (!ep) |
---|
2830 | 3081 | return -EINVAL; |
---|
| 3082 | + |
---|
2831 | 3083 | conn = ep->conn; |
---|
2832 | | - if (conn) { |
---|
2833 | | - mutex_lock(&conn->ep_mutex); |
---|
2834 | | - conn->ep = NULL; |
---|
2835 | | - mutex_unlock(&conn->ep_mutex); |
---|
| 3084 | + if (!conn) { |
---|
| 3085 | + /* |
---|
| 3086 | + * conn was not even bound yet, so we can't get iscsi conn |
---|
| 3087 | + * failures yet. |
---|
| 3088 | + */ |
---|
| 3089 | + transport->ep_disconnect(ep); |
---|
| 3090 | + goto put_ep; |
---|
2836 | 3091 | } |
---|
2837 | 3092 | |
---|
2838 | | - transport->ep_disconnect(ep); |
---|
| 3093 | + mutex_lock(&conn->ep_mutex); |
---|
| 3094 | + iscsi_if_disconnect_bound_ep(conn, ep, false); |
---|
| 3095 | + mutex_unlock(&conn->ep_mutex); |
---|
| 3096 | +put_ep: |
---|
| 3097 | + iscsi_put_endpoint(ep); |
---|
2839 | 3098 | return 0; |
---|
2840 | 3099 | } |
---|
2841 | 3100 | |
---|
2842 | 3101 | static int |
---|
2843 | 3102 | iscsi_if_transport_ep(struct iscsi_transport *transport, |
---|
2844 | | - struct iscsi_uevent *ev, int msg_type) |
---|
| 3103 | + struct iscsi_uevent *ev, int msg_type, u32 rlen) |
---|
2845 | 3104 | { |
---|
2846 | 3105 | struct iscsi_endpoint *ep; |
---|
2847 | 3106 | int rc = 0; |
---|
.. | .. |
---|
2849 | 3108 | switch (msg_type) { |
---|
2850 | 3109 | case ISCSI_UEVENT_TRANSPORT_EP_CONNECT_THROUGH_HOST: |
---|
2851 | 3110 | case ISCSI_UEVENT_TRANSPORT_EP_CONNECT: |
---|
2852 | | - rc = iscsi_if_ep_connect(transport, ev, msg_type); |
---|
| 3111 | + if (rlen < sizeof(struct sockaddr)) |
---|
| 3112 | + rc = -EINVAL; |
---|
| 3113 | + else |
---|
| 3114 | + rc = iscsi_if_ep_connect(transport, ev, msg_type); |
---|
2853 | 3115 | break; |
---|
2854 | 3116 | case ISCSI_UEVENT_TRANSPORT_EP_POLL: |
---|
2855 | 3117 | if (!transport->ep_poll) |
---|
.. | .. |
---|
2861 | 3123 | |
---|
2862 | 3124 | ev->r.retcode = transport->ep_poll(ep, |
---|
2863 | 3125 | ev->u.ep_poll.timeout_ms); |
---|
| 3126 | + iscsi_put_endpoint(ep); |
---|
2864 | 3127 | break; |
---|
2865 | 3128 | case ISCSI_UEVENT_TRANSPORT_EP_DISCONNECT: |
---|
2866 | 3129 | rc = iscsi_if_ep_disconnect(transport, |
---|
.. | .. |
---|
2872 | 3135 | |
---|
2873 | 3136 | static int |
---|
2874 | 3137 | iscsi_tgt_dscvr(struct iscsi_transport *transport, |
---|
2875 | | - struct iscsi_uevent *ev) |
---|
| 3138 | + struct iscsi_uevent *ev, u32 rlen) |
---|
2876 | 3139 | { |
---|
2877 | 3140 | struct Scsi_Host *shost; |
---|
2878 | 3141 | struct sockaddr *dst_addr; |
---|
2879 | 3142 | int err; |
---|
| 3143 | + |
---|
| 3144 | + if (rlen < sizeof(*dst_addr)) |
---|
| 3145 | + return -EINVAL; |
---|
2880 | 3146 | |
---|
2881 | 3147 | if (!transport->tgt_dscvr) |
---|
2882 | 3148 | return -EINVAL; |
---|
.. | .. |
---|
2898 | 3164 | |
---|
2899 | 3165 | static int |
---|
2900 | 3166 | iscsi_set_host_param(struct iscsi_transport *transport, |
---|
2901 | | - struct iscsi_uevent *ev) |
---|
| 3167 | + struct iscsi_uevent *ev, u32 rlen) |
---|
2902 | 3168 | { |
---|
2903 | 3169 | char *data = (char*)ev + sizeof(*ev); |
---|
2904 | 3170 | struct Scsi_Host *shost; |
---|
.. | .. |
---|
2907 | 3173 | if (!transport->set_host_param) |
---|
2908 | 3174 | return -ENOSYS; |
---|
2909 | 3175 | |
---|
2910 | | - if (ev->u.set_host_param.len > PAGE_SIZE) |
---|
| 3176 | + if (ev->u.set_host_param.len > rlen || |
---|
| 3177 | + ev->u.set_host_param.len > PAGE_SIZE) |
---|
2911 | 3178 | return -EINVAL; |
---|
2912 | 3179 | |
---|
2913 | 3180 | shost = scsi_host_lookup(ev->u.set_host_param.host_no); |
---|
.. | .. |
---|
2917 | 3184 | return -ENODEV; |
---|
2918 | 3185 | } |
---|
2919 | 3186 | |
---|
| 3187 | + /* see similar check in iscsi_if_set_param() */ |
---|
| 3188 | + if (strlen(data) > ev->u.set_host_param.len) |
---|
| 3189 | + return -EINVAL; |
---|
| 3190 | + |
---|
2920 | 3191 | err = transport->set_host_param(shost, ev->u.set_host_param.param, |
---|
2921 | 3192 | data, ev->u.set_host_param.len); |
---|
2922 | 3193 | scsi_host_put(shost); |
---|
.. | .. |
---|
2924 | 3195 | } |
---|
2925 | 3196 | |
---|
2926 | 3197 | static int |
---|
2927 | | -iscsi_set_path(struct iscsi_transport *transport, struct iscsi_uevent *ev) |
---|
| 3198 | +iscsi_set_path(struct iscsi_transport *transport, struct iscsi_uevent *ev, u32 rlen) |
---|
2928 | 3199 | { |
---|
2929 | 3200 | struct Scsi_Host *shost; |
---|
2930 | 3201 | struct iscsi_path *params; |
---|
2931 | 3202 | int err; |
---|
| 3203 | + |
---|
| 3204 | + if (rlen < sizeof(*params)) |
---|
| 3205 | + return -EINVAL; |
---|
2932 | 3206 | |
---|
2933 | 3207 | if (!transport->set_path) |
---|
2934 | 3208 | return -ENOSYS; |
---|
.. | .. |
---|
2989 | 3263 | } |
---|
2990 | 3264 | |
---|
2991 | 3265 | static int |
---|
2992 | | -iscsi_send_ping(struct iscsi_transport *transport, struct iscsi_uevent *ev) |
---|
| 3266 | +iscsi_send_ping(struct iscsi_transport *transport, struct iscsi_uevent *ev, u32 rlen) |
---|
2993 | 3267 | { |
---|
2994 | 3268 | struct Scsi_Host *shost; |
---|
2995 | 3269 | struct sockaddr *dst_addr; |
---|
2996 | 3270 | int err; |
---|
| 3271 | + |
---|
| 3272 | + if (rlen < sizeof(*dst_addr)) |
---|
| 3273 | + return -EINVAL; |
---|
2997 | 3274 | |
---|
2998 | 3275 | if (!transport->send_ping) |
---|
2999 | 3276 | return -ENOSYS; |
---|
.. | .. |
---|
3491 | 3768 | return err; |
---|
3492 | 3769 | } |
---|
3493 | 3770 | |
---|
| 3771 | +static int iscsi_if_transport_conn(struct iscsi_transport *transport, |
---|
| 3772 | + struct nlmsghdr *nlh, u32 pdu_len) |
---|
| 3773 | +{ |
---|
| 3774 | + struct iscsi_uevent *ev = nlmsg_data(nlh); |
---|
| 3775 | + struct iscsi_cls_session *session; |
---|
| 3776 | + struct iscsi_cls_conn *conn = NULL; |
---|
| 3777 | + struct iscsi_endpoint *ep; |
---|
| 3778 | + int err = 0; |
---|
| 3779 | + |
---|
| 3780 | + switch (nlh->nlmsg_type) { |
---|
| 3781 | + case ISCSI_UEVENT_CREATE_CONN: |
---|
| 3782 | + return iscsi_if_create_conn(transport, ev); |
---|
| 3783 | + case ISCSI_UEVENT_DESTROY_CONN: |
---|
| 3784 | + return iscsi_if_destroy_conn(transport, ev); |
---|
| 3785 | + case ISCSI_UEVENT_STOP_CONN: |
---|
| 3786 | + return iscsi_if_stop_conn(transport, ev); |
---|
| 3787 | + } |
---|
| 3788 | + |
---|
| 3789 | + /* |
---|
| 3790 | + * The following cmds need to be run under the ep_mutex so in kernel |
---|
| 3791 | + * conn cleanup (ep_disconnect + unbind and conn) is not done while |
---|
| 3792 | + * these are running. They also must not run if we have just run a conn |
---|
| 3793 | + * cleanup because they would set the state in a way that might allow |
---|
| 3794 | + * IO or send IO themselves. |
---|
| 3795 | + */ |
---|
| 3796 | + switch (nlh->nlmsg_type) { |
---|
| 3797 | + case ISCSI_UEVENT_START_CONN: |
---|
| 3798 | + conn = iscsi_conn_lookup(ev->u.start_conn.sid, |
---|
| 3799 | + ev->u.start_conn.cid); |
---|
| 3800 | + break; |
---|
| 3801 | + case ISCSI_UEVENT_BIND_CONN: |
---|
| 3802 | + conn = iscsi_conn_lookup(ev->u.b_conn.sid, ev->u.b_conn.cid); |
---|
| 3803 | + break; |
---|
| 3804 | + case ISCSI_UEVENT_SEND_PDU: |
---|
| 3805 | + conn = iscsi_conn_lookup(ev->u.send_pdu.sid, ev->u.send_pdu.cid); |
---|
| 3806 | + break; |
---|
| 3807 | + } |
---|
| 3808 | + |
---|
| 3809 | + if (!conn) |
---|
| 3810 | + return -EINVAL; |
---|
| 3811 | + |
---|
| 3812 | + mutex_lock(&conn->ep_mutex); |
---|
| 3813 | + spin_lock_irq(&conn->lock); |
---|
| 3814 | + if (test_bit(ISCSI_CLS_CONN_BIT_CLEANUP, &conn->flags)) { |
---|
| 3815 | + spin_unlock_irq(&conn->lock); |
---|
| 3816 | + mutex_unlock(&conn->ep_mutex); |
---|
| 3817 | + ev->r.retcode = -ENOTCONN; |
---|
| 3818 | + return 0; |
---|
| 3819 | + } |
---|
| 3820 | + spin_unlock_irq(&conn->lock); |
---|
| 3821 | + |
---|
| 3822 | + switch (nlh->nlmsg_type) { |
---|
| 3823 | + case ISCSI_UEVENT_BIND_CONN: |
---|
| 3824 | + session = iscsi_session_lookup(ev->u.b_conn.sid); |
---|
| 3825 | + if (!session) { |
---|
| 3826 | + err = -EINVAL; |
---|
| 3827 | + break; |
---|
| 3828 | + } |
---|
| 3829 | + |
---|
| 3830 | + ev->r.retcode = transport->bind_conn(session, conn, |
---|
| 3831 | + ev->u.b_conn.transport_eph, |
---|
| 3832 | + ev->u.b_conn.is_leading); |
---|
| 3833 | + if (!ev->r.retcode) |
---|
| 3834 | + WRITE_ONCE(conn->state, ISCSI_CONN_BOUND); |
---|
| 3835 | + |
---|
| 3836 | + if (ev->r.retcode || !transport->ep_connect) |
---|
| 3837 | + break; |
---|
| 3838 | + |
---|
| 3839 | + ep = iscsi_lookup_endpoint(ev->u.b_conn.transport_eph); |
---|
| 3840 | + if (ep) { |
---|
| 3841 | + ep->conn = conn; |
---|
| 3842 | + conn->ep = ep; |
---|
| 3843 | + iscsi_put_endpoint(ep); |
---|
| 3844 | + } else { |
---|
| 3845 | + err = -ENOTCONN; |
---|
| 3846 | + iscsi_cls_conn_printk(KERN_ERR, conn, |
---|
| 3847 | + "Could not set ep conn binding\n"); |
---|
| 3848 | + } |
---|
| 3849 | + break; |
---|
| 3850 | + case ISCSI_UEVENT_START_CONN: |
---|
| 3851 | + ev->r.retcode = transport->start_conn(conn); |
---|
| 3852 | + if (!ev->r.retcode) |
---|
| 3853 | + WRITE_ONCE(conn->state, ISCSI_CONN_UP); |
---|
| 3854 | + |
---|
| 3855 | + break; |
---|
| 3856 | + case ISCSI_UEVENT_SEND_PDU: |
---|
| 3857 | + if ((ev->u.send_pdu.hdr_size > pdu_len) || |
---|
| 3858 | + (ev->u.send_pdu.data_size > (pdu_len - ev->u.send_pdu.hdr_size))) { |
---|
| 3859 | + err = -EINVAL; |
---|
| 3860 | + break; |
---|
| 3861 | + } |
---|
| 3862 | + |
---|
| 3863 | + ev->r.retcode = transport->send_pdu(conn, |
---|
| 3864 | + (struct iscsi_hdr *)((char *)ev + sizeof(*ev)), |
---|
| 3865 | + (char *)ev + sizeof(*ev) + ev->u.send_pdu.hdr_size, |
---|
| 3866 | + ev->u.send_pdu.data_size); |
---|
| 3867 | + break; |
---|
| 3868 | + default: |
---|
| 3869 | + err = -ENOSYS; |
---|
| 3870 | + } |
---|
| 3871 | + |
---|
| 3872 | + mutex_unlock(&conn->ep_mutex); |
---|
| 3873 | + return err; |
---|
| 3874 | +} |
---|
3494 | 3875 | |
---|
3495 | 3876 | static int |
---|
3496 | 3877 | iscsi_if_recv_msg(struct sk_buff *skb, struct nlmsghdr *nlh, uint32_t *group) |
---|
3497 | 3878 | { |
---|
3498 | 3879 | int err = 0; |
---|
3499 | 3880 | u32 portid; |
---|
3500 | | - u32 pdu_len; |
---|
3501 | 3881 | struct iscsi_uevent *ev = nlmsg_data(nlh); |
---|
3502 | 3882 | struct iscsi_transport *transport = NULL; |
---|
3503 | 3883 | struct iscsi_internal *priv; |
---|
3504 | 3884 | struct iscsi_cls_session *session; |
---|
3505 | | - struct iscsi_cls_conn *conn; |
---|
3506 | 3885 | struct iscsi_endpoint *ep = NULL; |
---|
| 3886 | + u32 rlen; |
---|
3507 | 3887 | |
---|
3508 | 3888 | if (!netlink_capable(skb, CAP_SYS_ADMIN)) |
---|
3509 | 3889 | return -EPERM; |
---|
.. | .. |
---|
3522 | 3902 | return -EINVAL; |
---|
3523 | 3903 | |
---|
3524 | 3904 | portid = NETLINK_CB(skb).portid; |
---|
| 3905 | + |
---|
| 3906 | + /* |
---|
| 3907 | + * Even though the remaining payload may not be regarded as nlattr, |
---|
| 3908 | + * (like address or something else), calculate the remaining length |
---|
| 3909 | + * here to ease following length checks. |
---|
| 3910 | + */ |
---|
| 3911 | + rlen = nlmsg_attrlen(nlh, sizeof(*ev)); |
---|
3525 | 3912 | |
---|
3526 | 3913 | switch (nlh->nlmsg_type) { |
---|
3527 | 3914 | case ISCSI_UEVENT_CREATE_SESSION: |
---|
.. | .. |
---|
3543 | 3930 | ev->u.c_bound_session.initial_cmdsn, |
---|
3544 | 3931 | ev->u.c_bound_session.cmds_max, |
---|
3545 | 3932 | ev->u.c_bound_session.queue_depth); |
---|
| 3933 | + iscsi_put_endpoint(ep); |
---|
3546 | 3934 | break; |
---|
3547 | 3935 | case ISCSI_UEVENT_DESTROY_SESSION: |
---|
3548 | 3936 | session = iscsi_session_lookup(ev->u.d_session.sid); |
---|
.. | .. |
---|
3553 | 3941 | else |
---|
3554 | 3942 | transport->destroy_session(session); |
---|
3555 | 3943 | break; |
---|
| 3944 | + case ISCSI_UEVENT_DESTROY_SESSION_ASYNC: |
---|
| 3945 | + session = iscsi_session_lookup(ev->u.d_session.sid); |
---|
| 3946 | + if (!session) |
---|
| 3947 | + err = -EINVAL; |
---|
| 3948 | + else if (iscsi_session_has_conns(ev->u.d_session.sid)) |
---|
| 3949 | + err = -EBUSY; |
---|
| 3950 | + else { |
---|
| 3951 | + unsigned long flags; |
---|
| 3952 | + |
---|
| 3953 | + /* Prevent this session from being found again */ |
---|
| 3954 | + spin_lock_irqsave(&sesslock, flags); |
---|
| 3955 | + list_del_init(&session->sess_list); |
---|
| 3956 | + spin_unlock_irqrestore(&sesslock, flags); |
---|
| 3957 | + |
---|
| 3958 | + queue_work(system_unbound_wq, &session->destroy_work); |
---|
| 3959 | + } |
---|
| 3960 | + break; |
---|
3556 | 3961 | case ISCSI_UEVENT_UNBIND_SESSION: |
---|
3557 | 3962 | session = iscsi_session_lookup(ev->u.d_session.sid); |
---|
3558 | 3963 | if (session) |
---|
.. | .. |
---|
3561 | 3966 | else |
---|
3562 | 3967 | err = -EINVAL; |
---|
3563 | 3968 | break; |
---|
3564 | | - case ISCSI_UEVENT_CREATE_CONN: |
---|
3565 | | - err = iscsi_if_create_conn(transport, ev); |
---|
3566 | | - break; |
---|
3567 | | - case ISCSI_UEVENT_DESTROY_CONN: |
---|
3568 | | - err = iscsi_if_destroy_conn(transport, ev); |
---|
3569 | | - break; |
---|
3570 | | - case ISCSI_UEVENT_BIND_CONN: |
---|
3571 | | - session = iscsi_session_lookup(ev->u.b_conn.sid); |
---|
3572 | | - conn = iscsi_conn_lookup(ev->u.b_conn.sid, ev->u.b_conn.cid); |
---|
3573 | | - |
---|
3574 | | - if (conn && conn->ep) |
---|
3575 | | - iscsi_if_ep_disconnect(transport, conn->ep->id); |
---|
3576 | | - |
---|
3577 | | - if (!session || !conn) { |
---|
3578 | | - err = -EINVAL; |
---|
3579 | | - break; |
---|
3580 | | - } |
---|
3581 | | - |
---|
3582 | | - ev->r.retcode = transport->bind_conn(session, conn, |
---|
3583 | | - ev->u.b_conn.transport_eph, |
---|
3584 | | - ev->u.b_conn.is_leading); |
---|
3585 | | - if (ev->r.retcode || !transport->ep_connect) |
---|
3586 | | - break; |
---|
3587 | | - |
---|
3588 | | - ep = iscsi_lookup_endpoint(ev->u.b_conn.transport_eph); |
---|
3589 | | - if (ep) { |
---|
3590 | | - ep->conn = conn; |
---|
3591 | | - |
---|
3592 | | - mutex_lock(&conn->ep_mutex); |
---|
3593 | | - conn->ep = ep; |
---|
3594 | | - mutex_unlock(&conn->ep_mutex); |
---|
3595 | | - } else |
---|
3596 | | - iscsi_cls_conn_printk(KERN_ERR, conn, |
---|
3597 | | - "Could not set ep conn " |
---|
3598 | | - "binding\n"); |
---|
3599 | | - break; |
---|
3600 | 3969 | case ISCSI_UEVENT_SET_PARAM: |
---|
3601 | | - err = iscsi_set_param(transport, ev); |
---|
| 3970 | + err = iscsi_if_set_param(transport, ev, rlen); |
---|
3602 | 3971 | break; |
---|
3603 | | - case ISCSI_UEVENT_START_CONN: |
---|
3604 | | - conn = iscsi_conn_lookup(ev->u.start_conn.sid, ev->u.start_conn.cid); |
---|
3605 | | - if (conn) |
---|
3606 | | - ev->r.retcode = transport->start_conn(conn); |
---|
3607 | | - else |
---|
3608 | | - err = -EINVAL; |
---|
3609 | | - break; |
---|
| 3972 | + case ISCSI_UEVENT_CREATE_CONN: |
---|
| 3973 | + case ISCSI_UEVENT_DESTROY_CONN: |
---|
3610 | 3974 | case ISCSI_UEVENT_STOP_CONN: |
---|
3611 | | - conn = iscsi_conn_lookup(ev->u.stop_conn.sid, ev->u.stop_conn.cid); |
---|
3612 | | - if (conn) |
---|
3613 | | - transport->stop_conn(conn, ev->u.stop_conn.flag); |
---|
3614 | | - else |
---|
3615 | | - err = -EINVAL; |
---|
3616 | | - break; |
---|
| 3975 | + case ISCSI_UEVENT_START_CONN: |
---|
| 3976 | + case ISCSI_UEVENT_BIND_CONN: |
---|
3617 | 3977 | case ISCSI_UEVENT_SEND_PDU: |
---|
3618 | | - pdu_len = nlh->nlmsg_len - sizeof(*nlh) - sizeof(*ev); |
---|
3619 | | - |
---|
3620 | | - if ((ev->u.send_pdu.hdr_size > pdu_len) || |
---|
3621 | | - (ev->u.send_pdu.data_size > (pdu_len - ev->u.send_pdu.hdr_size))) { |
---|
3622 | | - err = -EINVAL; |
---|
3623 | | - break; |
---|
3624 | | - } |
---|
3625 | | - |
---|
3626 | | - conn = iscsi_conn_lookup(ev->u.send_pdu.sid, ev->u.send_pdu.cid); |
---|
3627 | | - if (conn) |
---|
3628 | | - ev->r.retcode = transport->send_pdu(conn, |
---|
3629 | | - (struct iscsi_hdr*)((char*)ev + sizeof(*ev)), |
---|
3630 | | - (char*)ev + sizeof(*ev) + ev->u.send_pdu.hdr_size, |
---|
3631 | | - ev->u.send_pdu.data_size); |
---|
3632 | | - else |
---|
3633 | | - err = -EINVAL; |
---|
| 3978 | + err = iscsi_if_transport_conn(transport, nlh, rlen); |
---|
3634 | 3979 | break; |
---|
3635 | 3980 | case ISCSI_UEVENT_GET_STATS: |
---|
3636 | 3981 | err = iscsi_if_get_stats(transport, nlh); |
---|
.. | .. |
---|
3639 | 3984 | case ISCSI_UEVENT_TRANSPORT_EP_POLL: |
---|
3640 | 3985 | case ISCSI_UEVENT_TRANSPORT_EP_DISCONNECT: |
---|
3641 | 3986 | case ISCSI_UEVENT_TRANSPORT_EP_CONNECT_THROUGH_HOST: |
---|
3642 | | - err = iscsi_if_transport_ep(transport, ev, nlh->nlmsg_type); |
---|
| 3987 | + err = iscsi_if_transport_ep(transport, ev, nlh->nlmsg_type, rlen); |
---|
3643 | 3988 | break; |
---|
3644 | 3989 | case ISCSI_UEVENT_TGT_DSCVR: |
---|
3645 | | - err = iscsi_tgt_dscvr(transport, ev); |
---|
| 3990 | + err = iscsi_tgt_dscvr(transport, ev, rlen); |
---|
3646 | 3991 | break; |
---|
3647 | 3992 | case ISCSI_UEVENT_SET_HOST_PARAM: |
---|
3648 | | - err = iscsi_set_host_param(transport, ev); |
---|
| 3993 | + err = iscsi_set_host_param(transport, ev, rlen); |
---|
3649 | 3994 | break; |
---|
3650 | 3995 | case ISCSI_UEVENT_PATH_UPDATE: |
---|
3651 | | - err = iscsi_set_path(transport, ev); |
---|
| 3996 | + err = iscsi_set_path(transport, ev, rlen); |
---|
3652 | 3997 | break; |
---|
3653 | 3998 | case ISCSI_UEVENT_SET_IFACE_PARAMS: |
---|
3654 | | - err = iscsi_set_iface_params(transport, ev, |
---|
3655 | | - nlmsg_attrlen(nlh, sizeof(*ev))); |
---|
| 3999 | + err = iscsi_set_iface_params(transport, ev, rlen); |
---|
3656 | 4000 | break; |
---|
3657 | 4001 | case ISCSI_UEVENT_PING: |
---|
3658 | | - err = iscsi_send_ping(transport, ev); |
---|
| 4002 | + err = iscsi_send_ping(transport, ev, rlen); |
---|
3659 | 4003 | break; |
---|
3660 | 4004 | case ISCSI_UEVENT_GET_CHAP: |
---|
3661 | 4005 | err = iscsi_get_chap(transport, nlh); |
---|
.. | .. |
---|
3664 | 4008 | err = iscsi_delete_chap(transport, ev); |
---|
3665 | 4009 | break; |
---|
3666 | 4010 | case ISCSI_UEVENT_SET_FLASHNODE_PARAMS: |
---|
3667 | | - err = iscsi_set_flashnode_param(transport, ev, |
---|
3668 | | - nlmsg_attrlen(nlh, |
---|
3669 | | - sizeof(*ev))); |
---|
| 4011 | + err = iscsi_set_flashnode_param(transport, ev, rlen); |
---|
3670 | 4012 | break; |
---|
3671 | 4013 | case ISCSI_UEVENT_NEW_FLASHNODE: |
---|
3672 | | - err = iscsi_new_flashnode(transport, ev, |
---|
3673 | | - nlmsg_attrlen(nlh, sizeof(*ev))); |
---|
| 4014 | + err = iscsi_new_flashnode(transport, ev, rlen); |
---|
3674 | 4015 | break; |
---|
3675 | 4016 | case ISCSI_UEVENT_DEL_FLASHNODE: |
---|
3676 | 4017 | err = iscsi_del_flashnode(transport, ev); |
---|
.. | .. |
---|
3685 | 4026 | err = iscsi_logout_flashnode_sid(transport, ev); |
---|
3686 | 4027 | break; |
---|
3687 | 4028 | case ISCSI_UEVENT_SET_CHAP: |
---|
3688 | | - err = iscsi_set_chap(transport, ev, |
---|
3689 | | - nlmsg_attrlen(nlh, sizeof(*ev))); |
---|
| 4029 | + err = iscsi_set_chap(transport, ev, rlen); |
---|
3690 | 4030 | break; |
---|
3691 | 4031 | case ISCSI_UEVENT_GET_HOST_STATS: |
---|
3692 | 4032 | err = iscsi_get_host_stats(transport, nlh); |
---|
.. | .. |
---|
3808 | 4148 | iscsi_conn_attr(tcp_recv_wsf, ISCSI_PARAM_TCP_RECV_WSF); |
---|
3809 | 4149 | iscsi_conn_attr(local_ipaddr, ISCSI_PARAM_LOCAL_IPADDR); |
---|
3810 | 4150 | |
---|
| 4151 | +static const char *const connection_state_names[] = { |
---|
| 4152 | + [ISCSI_CONN_UP] = "up", |
---|
| 4153 | + [ISCSI_CONN_DOWN] = "down", |
---|
| 4154 | + [ISCSI_CONN_FAILED] = "failed", |
---|
| 4155 | + [ISCSI_CONN_BOUND] = "bound" |
---|
| 4156 | +}; |
---|
| 4157 | + |
---|
| 4158 | +static ssize_t show_conn_state(struct device *dev, |
---|
| 4159 | + struct device_attribute *attr, char *buf) |
---|
| 4160 | +{ |
---|
| 4161 | + struct iscsi_cls_conn *conn = iscsi_dev_to_conn(dev->parent); |
---|
| 4162 | + const char *state = "unknown"; |
---|
| 4163 | + int conn_state = READ_ONCE(conn->state); |
---|
| 4164 | + |
---|
| 4165 | + if (conn_state >= 0 && |
---|
| 4166 | + conn_state < ARRAY_SIZE(connection_state_names)) |
---|
| 4167 | + state = connection_state_names[conn_state]; |
---|
| 4168 | + |
---|
| 4169 | + return sysfs_emit(buf, "%s\n", state); |
---|
| 4170 | +} |
---|
| 4171 | +static ISCSI_CLASS_ATTR(conn, state, S_IRUGO, show_conn_state, |
---|
| 4172 | + NULL); |
---|
3811 | 4173 | |
---|
3812 | 4174 | #define iscsi_conn_ep_attr_show(param) \ |
---|
3813 | 4175 | static ssize_t show_conn_ep_param_##param(struct device *dev, \ |
---|
.. | .. |
---|
3877 | 4239 | &dev_attr_conn_tcp_xmit_wsf.attr, |
---|
3878 | 4240 | &dev_attr_conn_tcp_recv_wsf.attr, |
---|
3879 | 4241 | &dev_attr_conn_local_ipaddr.attr, |
---|
| 4242 | + &dev_attr_conn_state.attr, |
---|
3880 | 4243 | NULL, |
---|
3881 | 4244 | }; |
---|
3882 | 4245 | |
---|
.. | .. |
---|
3948 | 4311 | param = ISCSI_PARAM_TCP_RECV_WSF; |
---|
3949 | 4312 | else if (attr == &dev_attr_conn_local_ipaddr.attr) |
---|
3950 | 4313 | param = ISCSI_PARAM_LOCAL_IPADDR; |
---|
| 4314 | + else if (attr == &dev_attr_conn_state.attr) |
---|
| 4315 | + return S_IRUGO; |
---|
3951 | 4316 | else { |
---|
3952 | 4317 | WARN_ONCE(1, "Invalid conn attr"); |
---|
3953 | 4318 | return 0; |
---|
.. | .. |
---|
4023 | 4388 | iscsi_session_attr(def_taskmgmt_tmo, ISCSI_PARAM_DEF_TASKMGMT_TMO, 0); |
---|
4024 | 4389 | iscsi_session_attr(discovery_parent_idx, ISCSI_PARAM_DISCOVERY_PARENT_IDX, 0); |
---|
4025 | 4390 | iscsi_session_attr(discovery_parent_type, ISCSI_PARAM_DISCOVERY_PARENT_TYPE, 0); |
---|
| 4391 | + |
---|
| 4392 | +static ssize_t |
---|
| 4393 | +show_priv_session_target_state(struct device *dev, struct device_attribute *attr, |
---|
| 4394 | + char *buf) |
---|
| 4395 | +{ |
---|
| 4396 | + struct iscsi_cls_session *session = iscsi_dev_to_session(dev->parent); |
---|
| 4397 | + |
---|
| 4398 | + return sysfs_emit(buf, "%s\n", |
---|
| 4399 | + iscsi_session_target_state_name[session->target_state]); |
---|
| 4400 | +} |
---|
| 4401 | + |
---|
| 4402 | +static ISCSI_CLASS_ATTR(priv_sess, target_state, S_IRUGO, |
---|
| 4403 | + show_priv_session_target_state, NULL); |
---|
4026 | 4404 | |
---|
4027 | 4405 | static ssize_t |
---|
4028 | 4406 | show_priv_session_state(struct device *dev, struct device_attribute *attr, |
---|
.. | .. |
---|
4126 | 4504 | &dev_attr_sess_boot_target.attr, |
---|
4127 | 4505 | &dev_attr_priv_sess_recovery_tmo.attr, |
---|
4128 | 4506 | &dev_attr_priv_sess_state.attr, |
---|
| 4507 | + &dev_attr_priv_sess_target_state.attr, |
---|
4129 | 4508 | &dev_attr_priv_sess_creator.attr, |
---|
4130 | 4509 | &dev_attr_sess_chap_out_idx.attr, |
---|
4131 | 4510 | &dev_attr_sess_chap_in_idx.attr, |
---|
.. | .. |
---|
4238 | 4617 | else if (attr == &dev_attr_priv_sess_recovery_tmo.attr) |
---|
4239 | 4618 | return S_IRUGO | S_IWUSR; |
---|
4240 | 4619 | else if (attr == &dev_attr_priv_sess_state.attr) |
---|
| 4620 | + return S_IRUGO; |
---|
| 4621 | + else if (attr == &dev_attr_priv_sess_target_state.attr) |
---|
4241 | 4622 | return S_IRUGO; |
---|
4242 | 4623 | else if (attr == &dev_attr_priv_sess_creator.attr) |
---|
4243 | 4624 | return S_IRUGO; |
---|
.. | .. |
---|
4454 | 4835 | int err; |
---|
4455 | 4836 | |
---|
4456 | 4837 | BUG_ON(!tt); |
---|
| 4838 | + WARN_ON(tt->ep_disconnect && !tt->unbind_conn); |
---|
4457 | 4839 | |
---|
4458 | 4840 | priv = iscsi_if_transport_lookup(tt); |
---|
4459 | 4841 | if (priv) |
---|
.. | .. |
---|
4540 | 4922 | } |
---|
4541 | 4923 | EXPORT_SYMBOL_GPL(iscsi_unregister_transport); |
---|
4542 | 4924 | |
---|
| 4925 | +void iscsi_dbg_trace(void (*trace)(struct device *dev, struct va_format *), |
---|
| 4926 | + struct device *dev, const char *fmt, ...) |
---|
| 4927 | +{ |
---|
| 4928 | + struct va_format vaf; |
---|
| 4929 | + va_list args; |
---|
| 4930 | + |
---|
| 4931 | + va_start(args, fmt); |
---|
| 4932 | + vaf.fmt = fmt; |
---|
| 4933 | + vaf.va = &args; |
---|
| 4934 | + trace(dev, &vaf); |
---|
| 4935 | + va_end(args); |
---|
| 4936 | +} |
---|
| 4937 | +EXPORT_SYMBOL_GPL(iscsi_dbg_trace); |
---|
| 4938 | + |
---|
4543 | 4939 | static __init int iscsi_transport_init(void) |
---|
4544 | 4940 | { |
---|
4545 | 4941 | int err; |
---|
.. | .. |
---|
4586 | 4982 | goto unregister_flashnode_bus; |
---|
4587 | 4983 | } |
---|
4588 | 4984 | |
---|
4589 | | - iscsi_eh_timer_workq = create_singlethread_workqueue("iscsi_eh"); |
---|
| 4985 | + iscsi_eh_timer_workq = alloc_workqueue("%s", |
---|
| 4986 | + WQ_SYSFS | __WQ_LEGACY | WQ_MEM_RECLAIM | WQ_UNBOUND, |
---|
| 4987 | + 1, "iscsi_eh"); |
---|
4590 | 4988 | if (!iscsi_eh_timer_workq) { |
---|
4591 | 4989 | err = -ENOMEM; |
---|
4592 | 4990 | goto release_nls; |
---|
4593 | 4991 | } |
---|
4594 | 4992 | |
---|
| 4993 | + iscsi_conn_cleanup_workq = alloc_workqueue("%s", |
---|
| 4994 | + WQ_SYSFS | WQ_MEM_RECLAIM | WQ_UNBOUND, 0, |
---|
| 4995 | + "iscsi_conn_cleanup"); |
---|
| 4996 | + if (!iscsi_conn_cleanup_workq) { |
---|
| 4997 | + err = -ENOMEM; |
---|
| 4998 | + goto destroy_wq; |
---|
| 4999 | + } |
---|
| 5000 | + |
---|
4595 | 5001 | return 0; |
---|
4596 | 5002 | |
---|
| 5003 | +destroy_wq: |
---|
| 5004 | + destroy_workqueue(iscsi_eh_timer_workq); |
---|
4597 | 5005 | release_nls: |
---|
4598 | 5006 | netlink_kernel_release(nls); |
---|
4599 | 5007 | unregister_flashnode_bus: |
---|
.. | .. |
---|
4615 | 5023 | |
---|
4616 | 5024 | static void __exit iscsi_transport_exit(void) |
---|
4617 | 5025 | { |
---|
| 5026 | + destroy_workqueue(iscsi_conn_cleanup_workq); |
---|
4618 | 5027 | destroy_workqueue(iscsi_eh_timer_workq); |
---|
4619 | 5028 | netlink_kernel_release(nls); |
---|
4620 | 5029 | bus_unregister(&iscsi_flashnode_bus); |
---|