| .. | .. |
|---|
| 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); |
|---|