| .. | .. |
|---|
| 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) |
|---|
| .. | .. |
|---|
| 1940 | 1950 | */ |
|---|
| 1941 | 1951 | void iscsi_unblock_session(struct iscsi_cls_session *session) |
|---|
| 1942 | 1952 | { |
|---|
| 1953 | + flush_work(&session->block_work); |
|---|
| 1954 | + |
|---|
| 1943 | 1955 | queue_work(iscsi_eh_timer_workq, &session->unblock_work); |
|---|
| 1944 | 1956 | /* |
|---|
| 1945 | | - * make sure all the events have completed before tell the driver |
|---|
| 1946 | | - * it is safe |
|---|
| 1957 | + * Blocking the session can be done from any context so we only |
|---|
| 1958 | + * queue the block work. Make sure the unblock work has completed |
|---|
| 1959 | + * because it flushes/cancels the other works and updates the state. |
|---|
| 1947 | 1960 | */ |
|---|
| 1948 | | - flush_workqueue(iscsi_eh_timer_workq); |
|---|
| 1961 | + flush_work(&session->unblock_work); |
|---|
| 1949 | 1962 | } |
|---|
| 1950 | 1963 | EXPORT_SYMBOL_GPL(iscsi_unblock_session); |
|---|
| 1951 | 1964 | |
|---|
| .. | .. |
|---|
| 2000 | 2013 | spin_unlock_irqrestore(&session->lock, flags); |
|---|
| 2001 | 2014 | mutex_unlock(&ihost->mutex); |
|---|
| 2002 | 2015 | |
|---|
| 2016 | + scsi_remove_target(&session->dev); |
|---|
| 2017 | + |
|---|
| 2003 | 2018 | if (session->ida_used) |
|---|
| 2004 | 2019 | ida_simple_remove(&iscsi_sess_ida, target_id); |
|---|
| 2005 | | - |
|---|
| 2006 | | - scsi_remove_target(&session->dev); |
|---|
| 2007 | 2020 | |
|---|
| 2008 | 2021 | unbind_session_exit: |
|---|
| 2009 | 2022 | iscsi_session_event(session, ISCSI_KEVENT_UNBIND_SESSION); |
|---|
| 2010 | 2023 | ISCSI_DBG_TRANS_SESSION(session, "Completed target removal\n"); |
|---|
| 2024 | +} |
|---|
| 2025 | + |
|---|
| 2026 | +static void __iscsi_destroy_session(struct work_struct *work) |
|---|
| 2027 | +{ |
|---|
| 2028 | + struct iscsi_cls_session *session = |
|---|
| 2029 | + container_of(work, struct iscsi_cls_session, destroy_work); |
|---|
| 2030 | + |
|---|
| 2031 | + session->transport->destroy_session(session); |
|---|
| 2011 | 2032 | } |
|---|
| 2012 | 2033 | |
|---|
| 2013 | 2034 | struct iscsi_cls_session * |
|---|
| .. | .. |
|---|
| 2032 | 2053 | INIT_WORK(&session->block_work, __iscsi_block_session); |
|---|
| 2033 | 2054 | INIT_WORK(&session->unbind_work, __iscsi_unbind_session); |
|---|
| 2034 | 2055 | INIT_WORK(&session->scan_work, iscsi_scan_session); |
|---|
| 2056 | + INIT_WORK(&session->destroy_work, __iscsi_destroy_session); |
|---|
| 2035 | 2057 | spin_lock_init(&session->lock); |
|---|
| 2036 | 2058 | |
|---|
| 2037 | 2059 | /* this is released in the dev's release function */ |
|---|
| .. | .. |
|---|
| 2075 | 2097 | "could not register session's dev\n"); |
|---|
| 2076 | 2098 | goto release_ida; |
|---|
| 2077 | 2099 | } |
|---|
| 2078 | | - transport_register_device(&session->dev); |
|---|
| 2100 | + err = transport_register_device(&session->dev); |
|---|
| 2101 | + if (err) { |
|---|
| 2102 | + iscsi_cls_session_printk(KERN_ERR, session, |
|---|
| 2103 | + "could not register transport's dev\n"); |
|---|
| 2104 | + goto release_dev; |
|---|
| 2105 | + } |
|---|
| 2079 | 2106 | |
|---|
| 2080 | 2107 | spin_lock_irqsave(&sesslock, flags); |
|---|
| 2081 | 2108 | list_add(&session->sess_list, &sesslist); |
|---|
| .. | .. |
|---|
| 2085 | 2112 | ISCSI_DBG_TRANS_SESSION(session, "Completed session adding\n"); |
|---|
| 2086 | 2113 | return 0; |
|---|
| 2087 | 2114 | |
|---|
| 2115 | +release_dev: |
|---|
| 2116 | + device_del(&session->dev); |
|---|
| 2088 | 2117 | release_ida: |
|---|
| 2089 | 2118 | if (session->ida_used) |
|---|
| 2090 | 2119 | ida_simple_remove(&iscsi_sess_ida, session->target_id); |
|---|
| .. | .. |
|---|
| 2150 | 2179 | ISCSI_DBG_TRANS_SESSION(session, "Removing session\n"); |
|---|
| 2151 | 2180 | |
|---|
| 2152 | 2181 | spin_lock_irqsave(&sesslock, flags); |
|---|
| 2153 | | - list_del(&session->sess_list); |
|---|
| 2182 | + if (!list_empty(&session->sess_list)) |
|---|
| 2183 | + list_del(&session->sess_list); |
|---|
| 2154 | 2184 | spin_unlock_irqrestore(&sesslock, flags); |
|---|
| 2155 | 2185 | |
|---|
| 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); |
|---|
| 2186 | + flush_work(&session->block_work); |
|---|
| 2187 | + flush_work(&session->unblock_work); |
|---|
| 2188 | + cancel_delayed_work_sync(&session->recovery_work); |
|---|
| 2161 | 2189 | /* |
|---|
| 2162 | 2190 | * If we are blocked let commands flow again. The lld or iscsi |
|---|
| 2163 | 2191 | * layer should set up the queuecommand to fail commands. |
|---|
| .. | .. |
|---|
| 2189 | 2217 | device_del(&session->dev); |
|---|
| 2190 | 2218 | } |
|---|
| 2191 | 2219 | EXPORT_SYMBOL_GPL(iscsi_remove_session); |
|---|
| 2220 | + |
|---|
| 2221 | +static void iscsi_stop_conn(struct iscsi_cls_conn *conn, int flag) |
|---|
| 2222 | +{ |
|---|
| 2223 | + ISCSI_DBG_TRANS_CONN(conn, "Stopping conn.\n"); |
|---|
| 2224 | + |
|---|
| 2225 | + switch (flag) { |
|---|
| 2226 | + case STOP_CONN_RECOVER: |
|---|
| 2227 | + WRITE_ONCE(conn->state, ISCSI_CONN_FAILED); |
|---|
| 2228 | + break; |
|---|
| 2229 | + case STOP_CONN_TERM: |
|---|
| 2230 | + WRITE_ONCE(conn->state, ISCSI_CONN_DOWN); |
|---|
| 2231 | + break; |
|---|
| 2232 | + default: |
|---|
| 2233 | + iscsi_cls_conn_printk(KERN_ERR, conn, "invalid stop flag %d\n", |
|---|
| 2234 | + flag); |
|---|
| 2235 | + return; |
|---|
| 2236 | + } |
|---|
| 2237 | + |
|---|
| 2238 | + conn->transport->stop_conn(conn, flag); |
|---|
| 2239 | + ISCSI_DBG_TRANS_CONN(conn, "Stopping conn done.\n"); |
|---|
| 2240 | +} |
|---|
| 2241 | + |
|---|
| 2242 | +static void iscsi_ep_disconnect(struct iscsi_cls_conn *conn, bool is_active) |
|---|
| 2243 | +{ |
|---|
| 2244 | + struct iscsi_cls_session *session = iscsi_conn_to_session(conn); |
|---|
| 2245 | + struct iscsi_endpoint *ep; |
|---|
| 2246 | + |
|---|
| 2247 | + ISCSI_DBG_TRANS_CONN(conn, "disconnect ep.\n"); |
|---|
| 2248 | + WRITE_ONCE(conn->state, ISCSI_CONN_FAILED); |
|---|
| 2249 | + |
|---|
| 2250 | + if (!conn->ep || !session->transport->ep_disconnect) |
|---|
| 2251 | + return; |
|---|
| 2252 | + |
|---|
| 2253 | + ep = conn->ep; |
|---|
| 2254 | + conn->ep = NULL; |
|---|
| 2255 | + |
|---|
| 2256 | + session->transport->unbind_conn(conn, is_active); |
|---|
| 2257 | + session->transport->ep_disconnect(ep); |
|---|
| 2258 | + ISCSI_DBG_TRANS_CONN(conn, "disconnect ep done.\n"); |
|---|
| 2259 | +} |
|---|
| 2260 | + |
|---|
| 2261 | +static void iscsi_if_disconnect_bound_ep(struct iscsi_cls_conn *conn, |
|---|
| 2262 | + struct iscsi_endpoint *ep, |
|---|
| 2263 | + bool is_active) |
|---|
| 2264 | +{ |
|---|
| 2265 | + /* Check if this was a conn error and the kernel took ownership */ |
|---|
| 2266 | + spin_lock_irq(&conn->lock); |
|---|
| 2267 | + if (!test_bit(ISCSI_CLS_CONN_BIT_CLEANUP, &conn->flags)) { |
|---|
| 2268 | + spin_unlock_irq(&conn->lock); |
|---|
| 2269 | + iscsi_ep_disconnect(conn, is_active); |
|---|
| 2270 | + } else { |
|---|
| 2271 | + spin_unlock_irq(&conn->lock); |
|---|
| 2272 | + ISCSI_DBG_TRANS_CONN(conn, "flush kernel conn cleanup.\n"); |
|---|
| 2273 | + mutex_unlock(&conn->ep_mutex); |
|---|
| 2274 | + |
|---|
| 2275 | + flush_work(&conn->cleanup_work); |
|---|
| 2276 | + /* |
|---|
| 2277 | + * Userspace is now done with the EP so we can release the ref |
|---|
| 2278 | + * iscsi_cleanup_conn_work_fn took. |
|---|
| 2279 | + */ |
|---|
| 2280 | + iscsi_put_endpoint(ep); |
|---|
| 2281 | + mutex_lock(&conn->ep_mutex); |
|---|
| 2282 | + } |
|---|
| 2283 | +} |
|---|
| 2284 | + |
|---|
| 2285 | +static int iscsi_if_stop_conn(struct iscsi_transport *transport, |
|---|
| 2286 | + struct iscsi_uevent *ev) |
|---|
| 2287 | +{ |
|---|
| 2288 | + int flag = ev->u.stop_conn.flag; |
|---|
| 2289 | + struct iscsi_cls_conn *conn; |
|---|
| 2290 | + |
|---|
| 2291 | + conn = iscsi_conn_lookup(ev->u.stop_conn.sid, ev->u.stop_conn.cid); |
|---|
| 2292 | + if (!conn) |
|---|
| 2293 | + return -EINVAL; |
|---|
| 2294 | + |
|---|
| 2295 | + ISCSI_DBG_TRANS_CONN(conn, "iscsi if conn stop.\n"); |
|---|
| 2296 | + /* |
|---|
| 2297 | + * If this is a termination we have to call stop_conn with that flag |
|---|
| 2298 | + * so the correct states get set. If we haven't run the work yet try to |
|---|
| 2299 | + * avoid the extra run. |
|---|
| 2300 | + */ |
|---|
| 2301 | + if (flag == STOP_CONN_TERM) { |
|---|
| 2302 | + cancel_work_sync(&conn->cleanup_work); |
|---|
| 2303 | + iscsi_stop_conn(conn, flag); |
|---|
| 2304 | + } else { |
|---|
| 2305 | + /* |
|---|
| 2306 | + * For offload, when iscsid is restarted it won't know about |
|---|
| 2307 | + * existing endpoints so it can't do a ep_disconnect. We clean |
|---|
| 2308 | + * it up here for userspace. |
|---|
| 2309 | + */ |
|---|
| 2310 | + mutex_lock(&conn->ep_mutex); |
|---|
| 2311 | + if (conn->ep) |
|---|
| 2312 | + iscsi_if_disconnect_bound_ep(conn, conn->ep, true); |
|---|
| 2313 | + mutex_unlock(&conn->ep_mutex); |
|---|
| 2314 | + |
|---|
| 2315 | + /* |
|---|
| 2316 | + * Figure out if it was the kernel or userspace initiating this. |
|---|
| 2317 | + */ |
|---|
| 2318 | + spin_lock_irq(&conn->lock); |
|---|
| 2319 | + if (!test_and_set_bit(ISCSI_CLS_CONN_BIT_CLEANUP, &conn->flags)) { |
|---|
| 2320 | + spin_unlock_irq(&conn->lock); |
|---|
| 2321 | + iscsi_stop_conn(conn, flag); |
|---|
| 2322 | + } else { |
|---|
| 2323 | + spin_unlock_irq(&conn->lock); |
|---|
| 2324 | + ISCSI_DBG_TRANS_CONN(conn, |
|---|
| 2325 | + "flush kernel conn cleanup.\n"); |
|---|
| 2326 | + flush_work(&conn->cleanup_work); |
|---|
| 2327 | + } |
|---|
| 2328 | + /* |
|---|
| 2329 | + * Only clear for recovery to avoid extra cleanup runs during |
|---|
| 2330 | + * termination. |
|---|
| 2331 | + */ |
|---|
| 2332 | + spin_lock_irq(&conn->lock); |
|---|
| 2333 | + clear_bit(ISCSI_CLS_CONN_BIT_CLEANUP, &conn->flags); |
|---|
| 2334 | + spin_unlock_irq(&conn->lock); |
|---|
| 2335 | + } |
|---|
| 2336 | + ISCSI_DBG_TRANS_CONN(conn, "iscsi if conn stop done.\n"); |
|---|
| 2337 | + return 0; |
|---|
| 2338 | +} |
|---|
| 2339 | + |
|---|
| 2340 | +static void iscsi_cleanup_conn_work_fn(struct work_struct *work) |
|---|
| 2341 | +{ |
|---|
| 2342 | + struct iscsi_cls_conn *conn = container_of(work, struct iscsi_cls_conn, |
|---|
| 2343 | + cleanup_work); |
|---|
| 2344 | + struct iscsi_cls_session *session = iscsi_conn_to_session(conn); |
|---|
| 2345 | + |
|---|
| 2346 | + mutex_lock(&conn->ep_mutex); |
|---|
| 2347 | + /* |
|---|
| 2348 | + * Get a ref to the ep, so we don't release its ID until after |
|---|
| 2349 | + * userspace is done referencing it in iscsi_if_disconnect_bound_ep. |
|---|
| 2350 | + */ |
|---|
| 2351 | + if (conn->ep) |
|---|
| 2352 | + get_device(&conn->ep->dev); |
|---|
| 2353 | + iscsi_ep_disconnect(conn, false); |
|---|
| 2354 | + |
|---|
| 2355 | + if (system_state != SYSTEM_RUNNING) { |
|---|
| 2356 | + /* |
|---|
| 2357 | + * If the user has set up for the session to never timeout |
|---|
| 2358 | + * then hang like they wanted. For all other cases fail right |
|---|
| 2359 | + * away since userspace is not going to relogin. |
|---|
| 2360 | + */ |
|---|
| 2361 | + if (session->recovery_tmo > 0) |
|---|
| 2362 | + session->recovery_tmo = 0; |
|---|
| 2363 | + } |
|---|
| 2364 | + |
|---|
| 2365 | + iscsi_stop_conn(conn, STOP_CONN_RECOVER); |
|---|
| 2366 | + mutex_unlock(&conn->ep_mutex); |
|---|
| 2367 | + ISCSI_DBG_TRANS_CONN(conn, "cleanup done.\n"); |
|---|
| 2368 | +} |
|---|
| 2192 | 2369 | |
|---|
| 2193 | 2370 | void iscsi_free_session(struct iscsi_cls_session *session) |
|---|
| 2194 | 2371 | { |
|---|
| .. | .. |
|---|
| 2228 | 2405 | conn->dd_data = &conn[1]; |
|---|
| 2229 | 2406 | |
|---|
| 2230 | 2407 | mutex_init(&conn->ep_mutex); |
|---|
| 2408 | + spin_lock_init(&conn->lock); |
|---|
| 2231 | 2409 | INIT_LIST_HEAD(&conn->conn_list); |
|---|
| 2410 | + INIT_WORK(&conn->cleanup_work, iscsi_cleanup_conn_work_fn); |
|---|
| 2232 | 2411 | conn->transport = transport; |
|---|
| 2233 | 2412 | conn->cid = cid; |
|---|
| 2413 | + WRITE_ONCE(conn->state, ISCSI_CONN_DOWN); |
|---|
| 2234 | 2414 | |
|---|
| 2235 | 2415 | /* this is released in the dev's release function */ |
|---|
| 2236 | 2416 | if (!get_device(&session->dev)) |
|---|
| .. | .. |
|---|
| 2245 | 2425 | "register connection's dev\n"); |
|---|
| 2246 | 2426 | goto release_parent_ref; |
|---|
| 2247 | 2427 | } |
|---|
| 2248 | | - transport_register_device(&conn->dev); |
|---|
| 2428 | + err = transport_register_device(&conn->dev); |
|---|
| 2429 | + if (err) { |
|---|
| 2430 | + iscsi_cls_session_printk(KERN_ERR, session, "could not " |
|---|
| 2431 | + "register transport's dev\n"); |
|---|
| 2432 | + goto release_conn_ref; |
|---|
| 2433 | + } |
|---|
| 2249 | 2434 | |
|---|
| 2250 | 2435 | spin_lock_irqsave(&connlock, flags); |
|---|
| 2251 | 2436 | list_add(&conn->conn_list, &connlist); |
|---|
| .. | .. |
|---|
| 2254 | 2439 | ISCSI_DBG_TRANS_CONN(conn, "Completed conn creation\n"); |
|---|
| 2255 | 2440 | return conn; |
|---|
| 2256 | 2441 | |
|---|
| 2442 | +release_conn_ref: |
|---|
| 2443 | + device_unregister(&conn->dev); |
|---|
| 2444 | + put_device(&session->dev); |
|---|
| 2445 | + return NULL; |
|---|
| 2257 | 2446 | release_parent_ref: |
|---|
| 2258 | 2447 | put_device(&session->dev); |
|---|
| 2259 | 2448 | free_conn: |
|---|
| .. | .. |
|---|
| 2408 | 2597 | struct iscsi_uevent *ev; |
|---|
| 2409 | 2598 | struct iscsi_internal *priv; |
|---|
| 2410 | 2599 | int len = nlmsg_total_size(sizeof(*ev)); |
|---|
| 2600 | + unsigned long flags; |
|---|
| 2601 | + int state; |
|---|
| 2602 | + |
|---|
| 2603 | + spin_lock_irqsave(&conn->lock, flags); |
|---|
| 2604 | + /* |
|---|
| 2605 | + * Userspace will only do a stop call if we are at least bound. And, we |
|---|
| 2606 | + * only need to do the in kernel cleanup if in the UP state so cmds can |
|---|
| 2607 | + * be released to upper layers. If in other states just wait for |
|---|
| 2608 | + * userspace to avoid races that can leave the cleanup_work queued. |
|---|
| 2609 | + */ |
|---|
| 2610 | + state = READ_ONCE(conn->state); |
|---|
| 2611 | + switch (state) { |
|---|
| 2612 | + case ISCSI_CONN_BOUND: |
|---|
| 2613 | + case ISCSI_CONN_UP: |
|---|
| 2614 | + if (!test_and_set_bit(ISCSI_CLS_CONN_BIT_CLEANUP, |
|---|
| 2615 | + &conn->flags)) { |
|---|
| 2616 | + queue_work(iscsi_conn_cleanup_workq, |
|---|
| 2617 | + &conn->cleanup_work); |
|---|
| 2618 | + } |
|---|
| 2619 | + break; |
|---|
| 2620 | + default: |
|---|
| 2621 | + ISCSI_DBG_TRANS_CONN(conn, "Got conn error in state %d\n", |
|---|
| 2622 | + state); |
|---|
| 2623 | + break; |
|---|
| 2624 | + } |
|---|
| 2625 | + spin_unlock_irqrestore(&conn->lock, flags); |
|---|
| 2411 | 2626 | |
|---|
| 2412 | 2627 | priv = iscsi_if_transport_lookup(conn->transport); |
|---|
| 2413 | 2628 | if (!priv) |
|---|
| .. | .. |
|---|
| 2742 | 2957 | if (!conn) |
|---|
| 2743 | 2958 | return -EINVAL; |
|---|
| 2744 | 2959 | |
|---|
| 2960 | + ISCSI_DBG_TRANS_CONN(conn, "Flushing cleanup during destruction\n"); |
|---|
| 2961 | + flush_work(&conn->cleanup_work); |
|---|
| 2745 | 2962 | ISCSI_DBG_TRANS_CONN(conn, "Destroying transport conn\n"); |
|---|
| 2963 | + |
|---|
| 2746 | 2964 | if (transport->destroy_conn) |
|---|
| 2747 | 2965 | transport->destroy_conn(conn); |
|---|
| 2748 | | - |
|---|
| 2749 | 2966 | return 0; |
|---|
| 2750 | 2967 | } |
|---|
| 2751 | 2968 | |
|---|
| .. | .. |
|---|
| 2755 | 2972 | char *data = (char*)ev + sizeof(*ev); |
|---|
| 2756 | 2973 | struct iscsi_cls_conn *conn; |
|---|
| 2757 | 2974 | struct iscsi_cls_session *session; |
|---|
| 2758 | | - int err = 0, value = 0; |
|---|
| 2975 | + int err = 0, value = 0, state; |
|---|
| 2759 | 2976 | |
|---|
| 2760 | 2977 | if (ev->u.set_param.len > PAGE_SIZE) |
|---|
| 2761 | 2978 | return -EINVAL; |
|---|
| .. | .. |
|---|
| 2772 | 2989 | session->recovery_tmo = value; |
|---|
| 2773 | 2990 | break; |
|---|
| 2774 | 2991 | default: |
|---|
| 2775 | | - err = transport->set_param(conn, ev->u.set_param.param, |
|---|
| 2776 | | - data, ev->u.set_param.len); |
|---|
| 2992 | + state = READ_ONCE(conn->state); |
|---|
| 2993 | + if (state == ISCSI_CONN_BOUND || state == ISCSI_CONN_UP) { |
|---|
| 2994 | + err = transport->set_param(conn, ev->u.set_param.param, |
|---|
| 2995 | + data, ev->u.set_param.len); |
|---|
| 2996 | + } else { |
|---|
| 2997 | + return -ENOTCONN; |
|---|
| 2998 | + } |
|---|
| 2777 | 2999 | } |
|---|
| 2778 | 3000 | |
|---|
| 2779 | 3001 | return err; |
|---|
| .. | .. |
|---|
| 2828 | 3050 | ep = iscsi_lookup_endpoint(ep_handle); |
|---|
| 2829 | 3051 | if (!ep) |
|---|
| 2830 | 3052 | return -EINVAL; |
|---|
| 3053 | + |
|---|
| 2831 | 3054 | conn = ep->conn; |
|---|
| 2832 | | - if (conn) { |
|---|
| 2833 | | - mutex_lock(&conn->ep_mutex); |
|---|
| 2834 | | - conn->ep = NULL; |
|---|
| 2835 | | - mutex_unlock(&conn->ep_mutex); |
|---|
| 3055 | + if (!conn) { |
|---|
| 3056 | + /* |
|---|
| 3057 | + * conn was not even bound yet, so we can't get iscsi conn |
|---|
| 3058 | + * failures yet. |
|---|
| 3059 | + */ |
|---|
| 3060 | + transport->ep_disconnect(ep); |
|---|
| 3061 | + goto put_ep; |
|---|
| 2836 | 3062 | } |
|---|
| 2837 | 3063 | |
|---|
| 2838 | | - transport->ep_disconnect(ep); |
|---|
| 3064 | + mutex_lock(&conn->ep_mutex); |
|---|
| 3065 | + iscsi_if_disconnect_bound_ep(conn, ep, false); |
|---|
| 3066 | + mutex_unlock(&conn->ep_mutex); |
|---|
| 3067 | +put_ep: |
|---|
| 3068 | + iscsi_put_endpoint(ep); |
|---|
| 2839 | 3069 | return 0; |
|---|
| 2840 | 3070 | } |
|---|
| 2841 | 3071 | |
|---|
| .. | .. |
|---|
| 2861 | 3091 | |
|---|
| 2862 | 3092 | ev->r.retcode = transport->ep_poll(ep, |
|---|
| 2863 | 3093 | ev->u.ep_poll.timeout_ms); |
|---|
| 3094 | + iscsi_put_endpoint(ep); |
|---|
| 2864 | 3095 | break; |
|---|
| 2865 | 3096 | case ISCSI_UEVENT_TRANSPORT_EP_DISCONNECT: |
|---|
| 2866 | 3097 | rc = iscsi_if_ep_disconnect(transport, |
|---|
| .. | .. |
|---|
| 3491 | 3722 | return err; |
|---|
| 3492 | 3723 | } |
|---|
| 3493 | 3724 | |
|---|
| 3725 | +static int iscsi_if_transport_conn(struct iscsi_transport *transport, |
|---|
| 3726 | + struct nlmsghdr *nlh) |
|---|
| 3727 | +{ |
|---|
| 3728 | + struct iscsi_uevent *ev = nlmsg_data(nlh); |
|---|
| 3729 | + struct iscsi_cls_session *session; |
|---|
| 3730 | + struct iscsi_cls_conn *conn = NULL; |
|---|
| 3731 | + struct iscsi_endpoint *ep; |
|---|
| 3732 | + uint32_t pdu_len; |
|---|
| 3733 | + int err = 0; |
|---|
| 3734 | + |
|---|
| 3735 | + switch (nlh->nlmsg_type) { |
|---|
| 3736 | + case ISCSI_UEVENT_CREATE_CONN: |
|---|
| 3737 | + return iscsi_if_create_conn(transport, ev); |
|---|
| 3738 | + case ISCSI_UEVENT_DESTROY_CONN: |
|---|
| 3739 | + return iscsi_if_destroy_conn(transport, ev); |
|---|
| 3740 | + case ISCSI_UEVENT_STOP_CONN: |
|---|
| 3741 | + return iscsi_if_stop_conn(transport, ev); |
|---|
| 3742 | + } |
|---|
| 3743 | + |
|---|
| 3744 | + /* |
|---|
| 3745 | + * The following cmds need to be run under the ep_mutex so in kernel |
|---|
| 3746 | + * conn cleanup (ep_disconnect + unbind and conn) is not done while |
|---|
| 3747 | + * these are running. They also must not run if we have just run a conn |
|---|
| 3748 | + * cleanup because they would set the state in a way that might allow |
|---|
| 3749 | + * IO or send IO themselves. |
|---|
| 3750 | + */ |
|---|
| 3751 | + switch (nlh->nlmsg_type) { |
|---|
| 3752 | + case ISCSI_UEVENT_START_CONN: |
|---|
| 3753 | + conn = iscsi_conn_lookup(ev->u.start_conn.sid, |
|---|
| 3754 | + ev->u.start_conn.cid); |
|---|
| 3755 | + break; |
|---|
| 3756 | + case ISCSI_UEVENT_BIND_CONN: |
|---|
| 3757 | + conn = iscsi_conn_lookup(ev->u.b_conn.sid, ev->u.b_conn.cid); |
|---|
| 3758 | + break; |
|---|
| 3759 | + case ISCSI_UEVENT_SEND_PDU: |
|---|
| 3760 | + conn = iscsi_conn_lookup(ev->u.send_pdu.sid, ev->u.send_pdu.cid); |
|---|
| 3761 | + break; |
|---|
| 3762 | + } |
|---|
| 3763 | + |
|---|
| 3764 | + if (!conn) |
|---|
| 3765 | + return -EINVAL; |
|---|
| 3766 | + |
|---|
| 3767 | + mutex_lock(&conn->ep_mutex); |
|---|
| 3768 | + spin_lock_irq(&conn->lock); |
|---|
| 3769 | + if (test_bit(ISCSI_CLS_CONN_BIT_CLEANUP, &conn->flags)) { |
|---|
| 3770 | + spin_unlock_irq(&conn->lock); |
|---|
| 3771 | + mutex_unlock(&conn->ep_mutex); |
|---|
| 3772 | + ev->r.retcode = -ENOTCONN; |
|---|
| 3773 | + return 0; |
|---|
| 3774 | + } |
|---|
| 3775 | + spin_unlock_irq(&conn->lock); |
|---|
| 3776 | + |
|---|
| 3777 | + switch (nlh->nlmsg_type) { |
|---|
| 3778 | + case ISCSI_UEVENT_BIND_CONN: |
|---|
| 3779 | + session = iscsi_session_lookup(ev->u.b_conn.sid); |
|---|
| 3780 | + if (!session) { |
|---|
| 3781 | + err = -EINVAL; |
|---|
| 3782 | + break; |
|---|
| 3783 | + } |
|---|
| 3784 | + |
|---|
| 3785 | + ev->r.retcode = transport->bind_conn(session, conn, |
|---|
| 3786 | + ev->u.b_conn.transport_eph, |
|---|
| 3787 | + ev->u.b_conn.is_leading); |
|---|
| 3788 | + if (!ev->r.retcode) |
|---|
| 3789 | + WRITE_ONCE(conn->state, ISCSI_CONN_BOUND); |
|---|
| 3790 | + |
|---|
| 3791 | + if (ev->r.retcode || !transport->ep_connect) |
|---|
| 3792 | + break; |
|---|
| 3793 | + |
|---|
| 3794 | + ep = iscsi_lookup_endpoint(ev->u.b_conn.transport_eph); |
|---|
| 3795 | + if (ep) { |
|---|
| 3796 | + ep->conn = conn; |
|---|
| 3797 | + conn->ep = ep; |
|---|
| 3798 | + iscsi_put_endpoint(ep); |
|---|
| 3799 | + } else { |
|---|
| 3800 | + err = -ENOTCONN; |
|---|
| 3801 | + iscsi_cls_conn_printk(KERN_ERR, conn, |
|---|
| 3802 | + "Could not set ep conn binding\n"); |
|---|
| 3803 | + } |
|---|
| 3804 | + break; |
|---|
| 3805 | + case ISCSI_UEVENT_START_CONN: |
|---|
| 3806 | + ev->r.retcode = transport->start_conn(conn); |
|---|
| 3807 | + if (!ev->r.retcode) |
|---|
| 3808 | + WRITE_ONCE(conn->state, ISCSI_CONN_UP); |
|---|
| 3809 | + |
|---|
| 3810 | + break; |
|---|
| 3811 | + case ISCSI_UEVENT_SEND_PDU: |
|---|
| 3812 | + pdu_len = nlh->nlmsg_len - sizeof(*nlh) - sizeof(*ev); |
|---|
| 3813 | + |
|---|
| 3814 | + if ((ev->u.send_pdu.hdr_size > pdu_len) || |
|---|
| 3815 | + (ev->u.send_pdu.data_size > (pdu_len - ev->u.send_pdu.hdr_size))) { |
|---|
| 3816 | + err = -EINVAL; |
|---|
| 3817 | + break; |
|---|
| 3818 | + } |
|---|
| 3819 | + |
|---|
| 3820 | + ev->r.retcode = transport->send_pdu(conn, |
|---|
| 3821 | + (struct iscsi_hdr *)((char *)ev + sizeof(*ev)), |
|---|
| 3822 | + (char *)ev + sizeof(*ev) + ev->u.send_pdu.hdr_size, |
|---|
| 3823 | + ev->u.send_pdu.data_size); |
|---|
| 3824 | + break; |
|---|
| 3825 | + default: |
|---|
| 3826 | + err = -ENOSYS; |
|---|
| 3827 | + } |
|---|
| 3828 | + |
|---|
| 3829 | + mutex_unlock(&conn->ep_mutex); |
|---|
| 3830 | + return err; |
|---|
| 3831 | +} |
|---|
| 3494 | 3832 | |
|---|
| 3495 | 3833 | static int |
|---|
| 3496 | 3834 | iscsi_if_recv_msg(struct sk_buff *skb, struct nlmsghdr *nlh, uint32_t *group) |
|---|
| 3497 | 3835 | { |
|---|
| 3498 | 3836 | int err = 0; |
|---|
| 3499 | 3837 | u32 portid; |
|---|
| 3500 | | - u32 pdu_len; |
|---|
| 3501 | 3838 | struct iscsi_uevent *ev = nlmsg_data(nlh); |
|---|
| 3502 | 3839 | struct iscsi_transport *transport = NULL; |
|---|
| 3503 | 3840 | struct iscsi_internal *priv; |
|---|
| 3504 | 3841 | struct iscsi_cls_session *session; |
|---|
| 3505 | | - struct iscsi_cls_conn *conn; |
|---|
| 3506 | 3842 | struct iscsi_endpoint *ep = NULL; |
|---|
| 3507 | 3843 | |
|---|
| 3508 | 3844 | if (!netlink_capable(skb, CAP_SYS_ADMIN)) |
|---|
| .. | .. |
|---|
| 3543 | 3879 | ev->u.c_bound_session.initial_cmdsn, |
|---|
| 3544 | 3880 | ev->u.c_bound_session.cmds_max, |
|---|
| 3545 | 3881 | ev->u.c_bound_session.queue_depth); |
|---|
| 3882 | + iscsi_put_endpoint(ep); |
|---|
| 3546 | 3883 | break; |
|---|
| 3547 | 3884 | case ISCSI_UEVENT_DESTROY_SESSION: |
|---|
| 3548 | 3885 | session = iscsi_session_lookup(ev->u.d_session.sid); |
|---|
| .. | .. |
|---|
| 3553 | 3890 | else |
|---|
| 3554 | 3891 | transport->destroy_session(session); |
|---|
| 3555 | 3892 | break; |
|---|
| 3893 | + case ISCSI_UEVENT_DESTROY_SESSION_ASYNC: |
|---|
| 3894 | + session = iscsi_session_lookup(ev->u.d_session.sid); |
|---|
| 3895 | + if (!session) |
|---|
| 3896 | + err = -EINVAL; |
|---|
| 3897 | + else if (iscsi_session_has_conns(ev->u.d_session.sid)) |
|---|
| 3898 | + err = -EBUSY; |
|---|
| 3899 | + else { |
|---|
| 3900 | + unsigned long flags; |
|---|
| 3901 | + |
|---|
| 3902 | + /* Prevent this session from being found again */ |
|---|
| 3903 | + spin_lock_irqsave(&sesslock, flags); |
|---|
| 3904 | + list_del_init(&session->sess_list); |
|---|
| 3905 | + spin_unlock_irqrestore(&sesslock, flags); |
|---|
| 3906 | + |
|---|
| 3907 | + queue_work(system_unbound_wq, &session->destroy_work); |
|---|
| 3908 | + } |
|---|
| 3909 | + break; |
|---|
| 3556 | 3910 | case ISCSI_UEVENT_UNBIND_SESSION: |
|---|
| 3557 | 3911 | session = iscsi_session_lookup(ev->u.d_session.sid); |
|---|
| 3558 | 3912 | if (session) |
|---|
| .. | .. |
|---|
| 3561 | 3915 | else |
|---|
| 3562 | 3916 | err = -EINVAL; |
|---|
| 3563 | 3917 | 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 | 3918 | case ISCSI_UEVENT_SET_PARAM: |
|---|
| 3601 | 3919 | err = iscsi_set_param(transport, ev); |
|---|
| 3602 | 3920 | 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; |
|---|
| 3921 | + case ISCSI_UEVENT_CREATE_CONN: |
|---|
| 3922 | + case ISCSI_UEVENT_DESTROY_CONN: |
|---|
| 3610 | 3923 | 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; |
|---|
| 3924 | + case ISCSI_UEVENT_START_CONN: |
|---|
| 3925 | + case ISCSI_UEVENT_BIND_CONN: |
|---|
| 3617 | 3926 | 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; |
|---|
| 3927 | + err = iscsi_if_transport_conn(transport, nlh); |
|---|
| 3634 | 3928 | break; |
|---|
| 3635 | 3929 | case ISCSI_UEVENT_GET_STATS: |
|---|
| 3636 | 3930 | err = iscsi_if_get_stats(transport, nlh); |
|---|
| .. | .. |
|---|
| 3808 | 4102 | iscsi_conn_attr(tcp_recv_wsf, ISCSI_PARAM_TCP_RECV_WSF); |
|---|
| 3809 | 4103 | iscsi_conn_attr(local_ipaddr, ISCSI_PARAM_LOCAL_IPADDR); |
|---|
| 3810 | 4104 | |
|---|
| 4105 | +static const char *const connection_state_names[] = { |
|---|
| 4106 | + [ISCSI_CONN_UP] = "up", |
|---|
| 4107 | + [ISCSI_CONN_DOWN] = "down", |
|---|
| 4108 | + [ISCSI_CONN_FAILED] = "failed", |
|---|
| 4109 | + [ISCSI_CONN_BOUND] = "bound" |
|---|
| 4110 | +}; |
|---|
| 4111 | + |
|---|
| 4112 | +static ssize_t show_conn_state(struct device *dev, |
|---|
| 4113 | + struct device_attribute *attr, char *buf) |
|---|
| 4114 | +{ |
|---|
| 4115 | + struct iscsi_cls_conn *conn = iscsi_dev_to_conn(dev->parent); |
|---|
| 4116 | + const char *state = "unknown"; |
|---|
| 4117 | + int conn_state = READ_ONCE(conn->state); |
|---|
| 4118 | + |
|---|
| 4119 | + if (conn_state >= 0 && |
|---|
| 4120 | + conn_state < ARRAY_SIZE(connection_state_names)) |
|---|
| 4121 | + state = connection_state_names[conn_state]; |
|---|
| 4122 | + |
|---|
| 4123 | + return sysfs_emit(buf, "%s\n", state); |
|---|
| 4124 | +} |
|---|
| 4125 | +static ISCSI_CLASS_ATTR(conn, state, S_IRUGO, show_conn_state, |
|---|
| 4126 | + NULL); |
|---|
| 3811 | 4127 | |
|---|
| 3812 | 4128 | #define iscsi_conn_ep_attr_show(param) \ |
|---|
| 3813 | 4129 | static ssize_t show_conn_ep_param_##param(struct device *dev, \ |
|---|
| .. | .. |
|---|
| 3877 | 4193 | &dev_attr_conn_tcp_xmit_wsf.attr, |
|---|
| 3878 | 4194 | &dev_attr_conn_tcp_recv_wsf.attr, |
|---|
| 3879 | 4195 | &dev_attr_conn_local_ipaddr.attr, |
|---|
| 4196 | + &dev_attr_conn_state.attr, |
|---|
| 3880 | 4197 | NULL, |
|---|
| 3881 | 4198 | }; |
|---|
| 3882 | 4199 | |
|---|
| .. | .. |
|---|
| 3948 | 4265 | param = ISCSI_PARAM_TCP_RECV_WSF; |
|---|
| 3949 | 4266 | else if (attr == &dev_attr_conn_local_ipaddr.attr) |
|---|
| 3950 | 4267 | param = ISCSI_PARAM_LOCAL_IPADDR; |
|---|
| 4268 | + else if (attr == &dev_attr_conn_state.attr) |
|---|
| 4269 | + return S_IRUGO; |
|---|
| 3951 | 4270 | else { |
|---|
| 3952 | 4271 | WARN_ONCE(1, "Invalid conn attr"); |
|---|
| 3953 | 4272 | return 0; |
|---|
| .. | .. |
|---|
| 4454 | 4773 | int err; |
|---|
| 4455 | 4774 | |
|---|
| 4456 | 4775 | BUG_ON(!tt); |
|---|
| 4776 | + WARN_ON(tt->ep_disconnect && !tt->unbind_conn); |
|---|
| 4457 | 4777 | |
|---|
| 4458 | 4778 | priv = iscsi_if_transport_lookup(tt); |
|---|
| 4459 | 4779 | if (priv) |
|---|
| .. | .. |
|---|
| 4540 | 4860 | } |
|---|
| 4541 | 4861 | EXPORT_SYMBOL_GPL(iscsi_unregister_transport); |
|---|
| 4542 | 4862 | |
|---|
| 4863 | +void iscsi_dbg_trace(void (*trace)(struct device *dev, struct va_format *), |
|---|
| 4864 | + struct device *dev, const char *fmt, ...) |
|---|
| 4865 | +{ |
|---|
| 4866 | + struct va_format vaf; |
|---|
| 4867 | + va_list args; |
|---|
| 4868 | + |
|---|
| 4869 | + va_start(args, fmt); |
|---|
| 4870 | + vaf.fmt = fmt; |
|---|
| 4871 | + vaf.va = &args; |
|---|
| 4872 | + trace(dev, &vaf); |
|---|
| 4873 | + va_end(args); |
|---|
| 4874 | +} |
|---|
| 4875 | +EXPORT_SYMBOL_GPL(iscsi_dbg_trace); |
|---|
| 4876 | + |
|---|
| 4543 | 4877 | static __init int iscsi_transport_init(void) |
|---|
| 4544 | 4878 | { |
|---|
| 4545 | 4879 | int err; |
|---|
| .. | .. |
|---|
| 4586 | 4920 | goto unregister_flashnode_bus; |
|---|
| 4587 | 4921 | } |
|---|
| 4588 | 4922 | |
|---|
| 4589 | | - iscsi_eh_timer_workq = create_singlethread_workqueue("iscsi_eh"); |
|---|
| 4923 | + iscsi_eh_timer_workq = alloc_workqueue("%s", |
|---|
| 4924 | + WQ_SYSFS | __WQ_LEGACY | WQ_MEM_RECLAIM | WQ_UNBOUND, |
|---|
| 4925 | + 1, "iscsi_eh"); |
|---|
| 4590 | 4926 | if (!iscsi_eh_timer_workq) { |
|---|
| 4591 | 4927 | err = -ENOMEM; |
|---|
| 4592 | 4928 | goto release_nls; |
|---|
| 4593 | 4929 | } |
|---|
| 4594 | 4930 | |
|---|
| 4931 | + iscsi_conn_cleanup_workq = alloc_workqueue("%s", |
|---|
| 4932 | + WQ_SYSFS | WQ_MEM_RECLAIM | WQ_UNBOUND, 0, |
|---|
| 4933 | + "iscsi_conn_cleanup"); |
|---|
| 4934 | + if (!iscsi_conn_cleanup_workq) { |
|---|
| 4935 | + err = -ENOMEM; |
|---|
| 4936 | + goto destroy_wq; |
|---|
| 4937 | + } |
|---|
| 4938 | + |
|---|
| 4595 | 4939 | return 0; |
|---|
| 4596 | 4940 | |
|---|
| 4941 | +destroy_wq: |
|---|
| 4942 | + destroy_workqueue(iscsi_eh_timer_workq); |
|---|
| 4597 | 4943 | release_nls: |
|---|
| 4598 | 4944 | netlink_kernel_release(nls); |
|---|
| 4599 | 4945 | unregister_flashnode_bus: |
|---|
| .. | .. |
|---|
| 4615 | 4961 | |
|---|
| 4616 | 4962 | static void __exit iscsi_transport_exit(void) |
|---|
| 4617 | 4963 | { |
|---|
| 4964 | + destroy_workqueue(iscsi_conn_cleanup_workq); |
|---|
| 4618 | 4965 | destroy_workqueue(iscsi_eh_timer_workq); |
|---|
| 4619 | 4966 | netlink_kernel_release(nls); |
|---|
| 4620 | 4967 | bus_unregister(&iscsi_flashnode_bus); |
|---|