blob: 2e9b68215593b397de1342e930854cd895bbb54a [file] [log] [blame]
Alex Aizman0896b752005-08-04 19:33:07 -07001/*
Linus Torvalds1da177e2005-04-16 15:20:36 -07002 * iSCSI transport class definitions
3 *
4 * Copyright (C) IBM Corporation, 2004
Alex Aizman0896b752005-08-04 19:33:07 -07005 * Copyright (C) Mike Christie, 2004 - 2005
6 * Copyright (C) Dmitry Yusupov, 2004 - 2005
7 * Copyright (C) Alex Aizman, 2004 - 2005
Linus Torvalds1da177e2005-04-16 15:20:36 -07008 *
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 */
23#include <linux/module.h>
Arjan van de Ven0b950672006-01-11 13:16:10 +010024#include <linux/mutex.h>
Tejun Heo5a0e3ad2010-03-24 17:04:11 +090025#include <linux/slab.h>
Alex Aizman0896b752005-08-04 19:33:07 -070026#include <net/tcp.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070027#include <scsi/scsi.h>
28#include <scsi/scsi_host.h>
29#include <scsi/scsi_device.h>
30#include <scsi/scsi_transport.h>
31#include <scsi/scsi_transport_iscsi.h>
Alex Aizman0896b752005-08-04 19:33:07 -070032#include <scsi/iscsi_if.h>
Mike Christiec01be6d2010-07-22 16:59:49 +053033#include <scsi/scsi_cmnd.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070034
Mike Christie4c2133c2008-06-16 10:11:34 -050035#define ISCSI_TRANSPORT_VERSION "2.0-870"
Linus Torvalds1da177e2005-04-16 15:20:36 -070036
Mike Christie632248a2009-08-20 15:11:01 -050037static int dbg_session;
38module_param_named(debug_session, dbg_session, int,
39 S_IRUGO | S_IWUSR);
40MODULE_PARM_DESC(debug_session,
41 "Turn on debugging for sessions in scsi_transport_iscsi "
42 "module. Set to 1 to turn on, and zero to turn off. Default "
43 "is off.");
44
45static int dbg_conn;
46module_param_named(debug_conn, dbg_conn, int,
47 S_IRUGO | S_IWUSR);
48MODULE_PARM_DESC(debug_conn,
49 "Turn on debugging for connections in scsi_transport_iscsi "
50 "module. Set to 1 to turn on, and zero to turn off. Default "
51 "is off.");
52
53#define ISCSI_DBG_TRANS_SESSION(_session, dbg_fmt, arg...) \
54 do { \
55 if (dbg_session) \
56 iscsi_cls_session_printk(KERN_INFO, _session, \
57 "%s: " dbg_fmt, \
58 __func__, ##arg); \
59 } while (0);
60
61#define ISCSI_DBG_TRANS_CONN(_conn, dbg_fmt, arg...) \
62 do { \
63 if (dbg_conn) \
64 iscsi_cls_conn_printk(KERN_INFO, _conn, \
65 "%s: " dbg_fmt, \
66 __func__, ##arg); \
67 } while (0);
68
Linus Torvalds1da177e2005-04-16 15:20:36 -070069struct iscsi_internal {
70 struct scsi_transport_template t;
Alex Aizman0896b752005-08-04 19:33:07 -070071 struct iscsi_transport *iscsi_transport;
72 struct list_head list;
Tony Jonesee959b02008-02-22 00:13:36 +010073 struct device dev;
Mike Christie30a6c652006-04-06 21:13:39 -050074
Alex Aizman0896b752005-08-04 19:33:07 -070075 struct transport_container conn_cont;
Alex Aizman0896b752005-08-04 19:33:07 -070076 struct transport_container session_cont;
Linus Torvalds1da177e2005-04-16 15:20:36 -070077};
78
Mike Christie41be1442007-02-28 17:32:18 -060079static atomic_t iscsi_session_nr; /* sysfs session id for next new session */
Mike Christied8bf5412007-12-13 12:43:27 -060080static struct workqueue_struct *iscsi_eh_timer_workq;
Mike Christieb5c7a122006-04-06 21:13:33 -050081
Alex Aizman0896b752005-08-04 19:33:07 -070082/*
83 * list of registered transports and lock that must
84 * be held while accessing list. The iscsi_transport_lock must
Arjan van de Ven0b950672006-01-11 13:16:10 +010085 * be acquired after the rx_queue_mutex.
Alex Aizman0896b752005-08-04 19:33:07 -070086 */
87static LIST_HEAD(iscsi_transports);
88static DEFINE_SPINLOCK(iscsi_transport_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -070089
Alex Aizman0896b752005-08-04 19:33:07 -070090#define to_iscsi_internal(tmpl) \
91 container_of(tmpl, struct iscsi_internal, t)
92
Tony Jonesee959b02008-02-22 00:13:36 +010093#define dev_to_iscsi_internal(_dev) \
94 container_of(_dev, struct iscsi_internal, dev)
Alex Aizman0896b752005-08-04 19:33:07 -070095
Tony Jonesee959b02008-02-22 00:13:36 +010096static void iscsi_transport_release(struct device *dev)
Alex Aizman0896b752005-08-04 19:33:07 -070097{
Tony Jonesee959b02008-02-22 00:13:36 +010098 struct iscsi_internal *priv = dev_to_iscsi_internal(dev);
Alex Aizman0896b752005-08-04 19:33:07 -070099 kfree(priv);
100}
101
102/*
103 * iscsi_transport_class represents the iscsi_transports that are
104 * registered.
105 */
106static struct class iscsi_transport_class = {
107 .name = "iscsi_transport",
Tony Jonesee959b02008-02-22 00:13:36 +0100108 .dev_release = iscsi_transport_release,
Alex Aizman0896b752005-08-04 19:33:07 -0700109};
110
111static ssize_t
Tony Jonesee959b02008-02-22 00:13:36 +0100112show_transport_handle(struct device *dev, struct device_attribute *attr,
113 char *buf)
Alex Aizman0896b752005-08-04 19:33:07 -0700114{
Tony Jonesee959b02008-02-22 00:13:36 +0100115 struct iscsi_internal *priv = dev_to_iscsi_internal(dev);
Mike Christie762e2bf2005-09-12 21:01:46 -0500116 return sprintf(buf, "%llu\n", (unsigned long long)iscsi_handle(priv->iscsi_transport));
Alex Aizman0896b752005-08-04 19:33:07 -0700117}
Tony Jonesee959b02008-02-22 00:13:36 +0100118static DEVICE_ATTR(handle, S_IRUGO, show_transport_handle, NULL);
Alex Aizman0896b752005-08-04 19:33:07 -0700119
120#define show_transport_attr(name, format) \
121static ssize_t \
Tony Jonesee959b02008-02-22 00:13:36 +0100122show_transport_##name(struct device *dev, \
123 struct device_attribute *attr,char *buf) \
Alex Aizman0896b752005-08-04 19:33:07 -0700124{ \
Tony Jonesee959b02008-02-22 00:13:36 +0100125 struct iscsi_internal *priv = dev_to_iscsi_internal(dev); \
Alex Aizman0896b752005-08-04 19:33:07 -0700126 return sprintf(buf, format"\n", priv->iscsi_transport->name); \
127} \
Tony Jonesee959b02008-02-22 00:13:36 +0100128static DEVICE_ATTR(name, S_IRUGO, show_transport_##name, NULL);
Alex Aizman0896b752005-08-04 19:33:07 -0700129
130show_transport_attr(caps, "0x%x");
Alex Aizman0896b752005-08-04 19:33:07 -0700131
132static struct attribute *iscsi_transport_attrs[] = {
Tony Jonesee959b02008-02-22 00:13:36 +0100133 &dev_attr_handle.attr,
134 &dev_attr_caps.attr,
Alex Aizman0896b752005-08-04 19:33:07 -0700135 NULL,
136};
137
138static struct attribute_group iscsi_transport_group = {
139 .attrs = iscsi_transport_attrs,
140};
141
Mike Christied82ff9be2008-05-21 15:54:13 -0500142/*
143 * iSCSI endpoint attrs
144 */
145#define iscsi_dev_to_endpoint(_dev) \
146 container_of(_dev, struct iscsi_endpoint, dev)
147
148#define ISCSI_ATTR(_prefix,_name,_mode,_show,_store) \
149struct device_attribute dev_attr_##_prefix##_##_name = \
150 __ATTR(_name,_mode,_show,_store)
151
152static void iscsi_endpoint_release(struct device *dev)
153{
154 struct iscsi_endpoint *ep = iscsi_dev_to_endpoint(dev);
155 kfree(ep);
156}
157
158static struct class iscsi_endpoint_class = {
159 .name = "iscsi_endpoint",
160 .dev_release = iscsi_endpoint_release,
161};
162
163static ssize_t
164show_ep_handle(struct device *dev, struct device_attribute *attr, char *buf)
165{
166 struct iscsi_endpoint *ep = iscsi_dev_to_endpoint(dev);
Mike Christie21536062008-09-24 11:46:11 -0500167 return sprintf(buf, "%llu\n", (unsigned long long) ep->id);
Mike Christied82ff9be2008-05-21 15:54:13 -0500168}
169static ISCSI_ATTR(ep, handle, S_IRUGO, show_ep_handle, NULL);
170
171static struct attribute *iscsi_endpoint_attrs[] = {
172 &dev_attr_ep_handle.attr,
173 NULL,
174};
175
176static struct attribute_group iscsi_endpoint_group = {
177 .attrs = iscsi_endpoint_attrs,
178};
179
180#define ISCSI_MAX_EPID -1
181
182static int iscsi_match_epid(struct device *dev, void *data)
183{
184 struct iscsi_endpoint *ep = iscsi_dev_to_endpoint(dev);
Mike Christie21536062008-09-24 11:46:11 -0500185 uint64_t *epid = (uint64_t *) data;
Mike Christied82ff9be2008-05-21 15:54:13 -0500186
187 return *epid == ep->id;
188}
189
190struct iscsi_endpoint *
191iscsi_create_endpoint(int dd_size)
192{
193 struct device *dev;
194 struct iscsi_endpoint *ep;
Mike Christie21536062008-09-24 11:46:11 -0500195 uint64_t id;
Mike Christied82ff9be2008-05-21 15:54:13 -0500196 int err;
197
198 for (id = 1; id < ISCSI_MAX_EPID; id++) {
Greg Kroah-Hartman695794a2008-05-22 17:21:08 -0400199 dev = class_find_device(&iscsi_endpoint_class, NULL, &id,
Mike Christied82ff9be2008-05-21 15:54:13 -0500200 iscsi_match_epid);
201 if (!dev)
202 break;
203 }
204 if (id == ISCSI_MAX_EPID) {
205 printk(KERN_ERR "Too many connections. Max supported %u\n",
206 ISCSI_MAX_EPID - 1);
207 return NULL;
208 }
209
210 ep = kzalloc(sizeof(*ep) + dd_size, GFP_KERNEL);
211 if (!ep)
212 return NULL;
213
214 ep->id = id;
215 ep->dev.class = &iscsi_endpoint_class;
Kay Sievers71610f52008-12-03 22:41:36 +0100216 dev_set_name(&ep->dev, "ep-%llu", (unsigned long long) id);
Mike Christied82ff9be2008-05-21 15:54:13 -0500217 err = device_register(&ep->dev);
218 if (err)
219 goto free_ep;
220
221 err = sysfs_create_group(&ep->dev.kobj, &iscsi_endpoint_group);
222 if (err)
223 goto unregister_dev;
224
225 if (dd_size)
226 ep->dd_data = &ep[1];
227 return ep;
228
229unregister_dev:
230 device_unregister(&ep->dev);
231 return NULL;
232
233free_ep:
234 kfree(ep);
235 return NULL;
236}
237EXPORT_SYMBOL_GPL(iscsi_create_endpoint);
238
239void iscsi_destroy_endpoint(struct iscsi_endpoint *ep)
240{
241 sysfs_remove_group(&ep->dev.kobj, &iscsi_endpoint_group);
242 device_unregister(&ep->dev);
243}
244EXPORT_SYMBOL_GPL(iscsi_destroy_endpoint);
245
246struct iscsi_endpoint *iscsi_lookup_endpoint(u64 handle)
247{
Mike Christief80f8682008-06-16 10:11:35 -0500248 struct iscsi_endpoint *ep;
Mike Christied82ff9be2008-05-21 15:54:13 -0500249 struct device *dev;
250
Greg Kroah-Hartman695794a2008-05-22 17:21:08 -0400251 dev = class_find_device(&iscsi_endpoint_class, NULL, &handle,
Mike Christied82ff9be2008-05-21 15:54:13 -0500252 iscsi_match_epid);
253 if (!dev)
254 return NULL;
255
Mike Christief80f8682008-06-16 10:11:35 -0500256 ep = iscsi_dev_to_endpoint(dev);
257 /*
258 * we can drop this now because the interface will prevent
259 * removals and lookups from racing.
260 */
261 put_device(dev);
262 return ep;
Mike Christied82ff9be2008-05-21 15:54:13 -0500263}
264EXPORT_SYMBOL_GPL(iscsi_lookup_endpoint);
265
Mike Christie8d079132011-07-25 13:48:40 -0500266/*
267 * Interface to display network param to sysfs
268 */
269
270static void iscsi_iface_release(struct device *dev)
271{
272 struct iscsi_iface *iface = iscsi_dev_to_iface(dev);
273 struct device *parent = iface->dev.parent;
274
275 kfree(iface);
276 put_device(parent);
277}
278
279
280static struct class iscsi_iface_class = {
281 .name = "iscsi_iface",
282 .dev_release = iscsi_iface_release,
283};
284
285#define ISCSI_IFACE_ATTR(_prefix, _name, _mode, _show, _store) \
286struct device_attribute dev_attr_##_prefix##_##_name = \
287 __ATTR(_name, _mode, _show, _store)
288
289/* iface attrs show */
290#define iscsi_iface_attr_show(type, name, param_type, param) \
291static ssize_t \
292show_##type##_##name(struct device *dev, struct device_attribute *attr, \
293 char *buf) \
294{ \
295 struct iscsi_iface *iface = iscsi_dev_to_iface(dev); \
296 struct iscsi_transport *t = iface->transport; \
297 return t->get_iface_param(iface, param_type, param, buf); \
298} \
299
300#define iscsi_iface_net_attr(type, name, param) \
301 iscsi_iface_attr_show(type, name, ISCSI_NET_PARAM, param) \
302static ISCSI_IFACE_ATTR(type, name, S_IRUGO, show_##type##_##name, NULL);
303
304/* generic read only ipvi4 attribute */
305iscsi_iface_net_attr(ipv4_iface, ipaddress, ISCSI_NET_PARAM_IPV4_ADDR);
306iscsi_iface_net_attr(ipv4_iface, gateway, ISCSI_NET_PARAM_IPV4_GW);
307iscsi_iface_net_attr(ipv4_iface, subnet, ISCSI_NET_PARAM_IPV4_SUBNET);
308iscsi_iface_net_attr(ipv4_iface, bootproto, ISCSI_NET_PARAM_IPV4_BOOTPROTO);
309
310/* generic read only ipv6 attribute */
311iscsi_iface_net_attr(ipv6_iface, ipaddress, ISCSI_NET_PARAM_IPV6_ADDR);
312iscsi_iface_net_attr(ipv6_iface, link_local_addr, ISCSI_NET_PARAM_IPV6_LINKLOCAL);
313iscsi_iface_net_attr(ipv6_iface, router_addr, ISCSI_NET_PARAM_IPV6_ROUTER);
314iscsi_iface_net_attr(ipv6_iface, ipaddr_autocfg,
315 ISCSI_NET_PARAM_IPV6_ADDR_AUTOCFG);
316iscsi_iface_net_attr(ipv6_iface, linklocal_autocfg,
317 ISCSI_NET_PARAM_IPV6_LINKLOCAL_AUTOCFG);
318
319/* common read only iface attribute */
320iscsi_iface_net_attr(iface, enabled, ISCSI_NET_PARAM_IFACE_ENABLE);
321iscsi_iface_net_attr(iface, vlan, ISCSI_NET_PARAM_VLAN_ID);
322
323static mode_t iscsi_iface_attr_is_visible(struct kobject *kobj,
324 struct attribute *attr, int i)
325{
326 struct device *dev = container_of(kobj, struct device, kobj);
327 struct iscsi_iface *iface = iscsi_dev_to_iface(dev);
328 struct iscsi_transport *t = iface->transport;
Mike Christieb78dbba2011-07-25 13:48:44 -0500329 int param;
Mike Christie8d079132011-07-25 13:48:40 -0500330
331 if (attr == &dev_attr_iface_enabled.attr)
Mike Christieb78dbba2011-07-25 13:48:44 -0500332 param = ISCSI_NET_PARAM_IFACE_ENABLE;
Mike Christie8d079132011-07-25 13:48:40 -0500333 else if (attr == &dev_attr_iface_vlan.attr)
Mike Christieb78dbba2011-07-25 13:48:44 -0500334 param = ISCSI_NET_PARAM_VLAN_ID;
335 else if (iface->iface_type == ISCSI_IFACE_TYPE_IPV4) {
Mike Christie8d079132011-07-25 13:48:40 -0500336 if (attr == &dev_attr_ipv4_iface_ipaddress.attr)
Mike Christieb78dbba2011-07-25 13:48:44 -0500337 param = ISCSI_NET_PARAM_IPV4_ADDR;
Mike Christie8d079132011-07-25 13:48:40 -0500338 else if (attr == &dev_attr_ipv4_iface_gateway.attr)
Mike Christieb78dbba2011-07-25 13:48:44 -0500339 param = ISCSI_NET_PARAM_IPV4_GW;
Mike Christie8d079132011-07-25 13:48:40 -0500340 else if (attr == &dev_attr_ipv4_iface_subnet.attr)
Mike Christieb78dbba2011-07-25 13:48:44 -0500341 param = ISCSI_NET_PARAM_IPV4_SUBNET;
Mike Christie8d079132011-07-25 13:48:40 -0500342 else if (attr == &dev_attr_ipv4_iface_bootproto.attr)
Mike Christieb78dbba2011-07-25 13:48:44 -0500343 param = ISCSI_NET_PARAM_IPV4_BOOTPROTO;
344 else
345 return 0;
Mike Christie8d079132011-07-25 13:48:40 -0500346 } else if (iface->iface_type == ISCSI_IFACE_TYPE_IPV6) {
347 if (attr == &dev_attr_ipv6_iface_ipaddress.attr)
Mike Christieb78dbba2011-07-25 13:48:44 -0500348 param = ISCSI_NET_PARAM_IPV6_ADDR;
Mike Christie8d079132011-07-25 13:48:40 -0500349 else if (attr == &dev_attr_ipv6_iface_link_local_addr.attr)
Mike Christieb78dbba2011-07-25 13:48:44 -0500350 param = ISCSI_NET_PARAM_IPV6_LINKLOCAL;
Mike Christie8d079132011-07-25 13:48:40 -0500351 else if (attr == &dev_attr_ipv6_iface_router_addr.attr)
Mike Christieb78dbba2011-07-25 13:48:44 -0500352 param = ISCSI_NET_PARAM_IPV6_ROUTER;
Mike Christie8d079132011-07-25 13:48:40 -0500353 else if (attr == &dev_attr_ipv6_iface_ipaddr_autocfg.attr)
Mike Christieb78dbba2011-07-25 13:48:44 -0500354 param = ISCSI_NET_PARAM_IPV6_ADDR_AUTOCFG;
Mike Christie8d079132011-07-25 13:48:40 -0500355 else if (attr == &dev_attr_ipv6_iface_linklocal_autocfg.attr)
Mike Christieb78dbba2011-07-25 13:48:44 -0500356 param = ISCSI_NET_PARAM_IPV6_LINKLOCAL_AUTOCFG;
357 else
358 return 0;
359 } else {
360 WARN_ONCE(1, "Invalid iface attr");
361 return 0;
Mike Christie8d079132011-07-25 13:48:40 -0500362 }
363
Mike Christieb78dbba2011-07-25 13:48:44 -0500364 return t->attr_is_visible(ISCSI_NET_PARAM, param);
Mike Christie8d079132011-07-25 13:48:40 -0500365}
366
367static struct attribute *iscsi_iface_attrs[] = {
368 &dev_attr_iface_enabled.attr,
369 &dev_attr_iface_vlan.attr,
370 &dev_attr_ipv4_iface_ipaddress.attr,
371 &dev_attr_ipv4_iface_gateway.attr,
372 &dev_attr_ipv4_iface_subnet.attr,
373 &dev_attr_ipv4_iface_bootproto.attr,
374 &dev_attr_ipv6_iface_ipaddress.attr,
375 &dev_attr_ipv6_iface_link_local_addr.attr,
376 &dev_attr_ipv6_iface_router_addr.attr,
377 &dev_attr_ipv6_iface_ipaddr_autocfg.attr,
378 &dev_attr_ipv6_iface_linklocal_autocfg.attr,
379 NULL,
380};
381
382static struct attribute_group iscsi_iface_group = {
383 .attrs = iscsi_iface_attrs,
384 .is_visible = iscsi_iface_attr_is_visible,
385};
386
387struct iscsi_iface *
388iscsi_create_iface(struct Scsi_Host *shost, struct iscsi_transport *transport,
389 uint32_t iface_type, uint32_t iface_num, int dd_size)
390{
391 struct iscsi_iface *iface;
392 int err;
393
394 iface = kzalloc(sizeof(*iface) + dd_size, GFP_KERNEL);
395 if (!iface)
396 return NULL;
397
398 iface->transport = transport;
399 iface->iface_type = iface_type;
400 iface->iface_num = iface_num;
401 iface->dev.release = iscsi_iface_release;
402 iface->dev.class = &iscsi_iface_class;
403 /* parent reference released in iscsi_iface_release */
404 iface->dev.parent = get_device(&shost->shost_gendev);
405 if (iface_type == ISCSI_IFACE_TYPE_IPV4)
406 dev_set_name(&iface->dev, "ipv4-iface-%u-%u", shost->host_no,
407 iface_num);
408 else
409 dev_set_name(&iface->dev, "ipv6-iface-%u-%u", shost->host_no,
410 iface_num);
411
412 err = device_register(&iface->dev);
413 if (err)
414 goto free_iface;
415
416 err = sysfs_create_group(&iface->dev.kobj, &iscsi_iface_group);
417 if (err)
418 goto unreg_iface;
419
420 if (dd_size)
421 iface->dd_data = &iface[1];
422 return iface;
423
424unreg_iface:
425 device_unregister(&iface->dev);
426 return NULL;
427
428free_iface:
429 put_device(iface->dev.parent);
430 kfree(iface);
431 return NULL;
432}
433EXPORT_SYMBOL_GPL(iscsi_create_iface);
434
435void iscsi_destroy_iface(struct iscsi_iface *iface)
436{
437 sysfs_remove_group(&iface->dev.kobj, &iscsi_iface_group);
438 device_unregister(&iface->dev);
439}
440EXPORT_SYMBOL_GPL(iscsi_destroy_iface);
441
Mike Christie30a6c652006-04-06 21:13:39 -0500442static int iscsi_setup_host(struct transport_container *tc, struct device *dev,
Tony Jonesee959b02008-02-22 00:13:36 +0100443 struct device *cdev)
Mike Christie30a6c652006-04-06 21:13:39 -0500444{
445 struct Scsi_Host *shost = dev_to_shost(dev);
Mike Christie32c6e1b2008-05-21 15:53:58 -0500446 struct iscsi_cls_host *ihost = shost->shost_data;
Mike Christie30a6c652006-04-06 21:13:39 -0500447
448 memset(ihost, 0, sizeof(*ihost));
Mike Christie8aae18a2008-01-31 13:36:48 -0600449 atomic_set(&ihost->nr_scans, 0);
Mike Christie79706342008-05-21 15:54:12 -0500450 mutex_init(&ihost->mutex);
Mike Christie30a6c652006-04-06 21:13:39 -0500451 return 0;
452}
453
454static DECLARE_TRANSPORT_CLASS(iscsi_host_class,
455 "iscsi_host",
456 iscsi_setup_host,
Mike Christie06d25af2009-03-05 14:46:02 -0600457 NULL,
Mike Christie30a6c652006-04-06 21:13:39 -0500458 NULL);
459
Alex Aizman0896b752005-08-04 19:33:07 -0700460static DECLARE_TRANSPORT_CLASS(iscsi_session_class,
461 "iscsi_session",
Linus Torvalds1da177e2005-04-16 15:20:36 -0700462 NULL,
463 NULL,
464 NULL);
465
Alex Aizman0896b752005-08-04 19:33:07 -0700466static DECLARE_TRANSPORT_CLASS(iscsi_connection_class,
467 "iscsi_connection",
Linus Torvalds1da177e2005-04-16 15:20:36 -0700468 NULL,
469 NULL,
470 NULL);
Alex Aizman0896b752005-08-04 19:33:07 -0700471
472static struct sock *nls;
Arjan van de Ven0b950672006-01-11 13:16:10 +0100473static DEFINE_MUTEX(rx_queue_mutex);
Alex Aizman0896b752005-08-04 19:33:07 -0700474
Mike Christie7b7232f2006-02-01 21:06:49 -0600475static LIST_HEAD(sesslist);
476static DEFINE_SPINLOCK(sesslock);
Alex Aizman0896b752005-08-04 19:33:07 -0700477static LIST_HEAD(connlist);
478static DEFINE_SPINLOCK(connlock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700479
Mike Christieb5c7a122006-04-06 21:13:33 -0500480static uint32_t iscsi_conn_get_sid(struct iscsi_cls_conn *conn)
481{
482 struct iscsi_cls_session *sess = iscsi_dev_to_session(conn->dev.parent);
483 return sess->sid;
484}
485
486/*
487 * Returns the matching session to a given sid
488 */
489static struct iscsi_cls_session *iscsi_session_lookup(uint32_t sid)
Mike Christie7b7232f2006-02-01 21:06:49 -0600490{
491 unsigned long flags;
492 struct iscsi_cls_session *sess;
493
494 spin_lock_irqsave(&sesslock, flags);
495 list_for_each_entry(sess, &sesslist, sess_list) {
Mike Christieb5c7a122006-04-06 21:13:33 -0500496 if (sess->sid == sid) {
Mike Christie7b7232f2006-02-01 21:06:49 -0600497 spin_unlock_irqrestore(&sesslock, flags);
498 return sess;
499 }
500 }
501 spin_unlock_irqrestore(&sesslock, flags);
502 return NULL;
503}
504
Mike Christieb5c7a122006-04-06 21:13:33 -0500505/*
506 * Returns the matching connection to a given sid / cid tuple
507 */
508static struct iscsi_cls_conn *iscsi_conn_lookup(uint32_t sid, uint32_t cid)
Mike Christie7b7232f2006-02-01 21:06:49 -0600509{
510 unsigned long flags;
511 struct iscsi_cls_conn *conn;
512
513 spin_lock_irqsave(&connlock, flags);
514 list_for_each_entry(conn, &connlist, conn_list) {
Mike Christieb5c7a122006-04-06 21:13:33 -0500515 if ((conn->cid == cid) && (iscsi_conn_get_sid(conn) == sid)) {
Mike Christie7b7232f2006-02-01 21:06:49 -0600516 spin_unlock_irqrestore(&connlock, flags);
517 return conn;
518 }
519 }
520 spin_unlock_irqrestore(&connlock, flags);
521 return NULL;
522}
523
Mike Christie7b8631b2006-01-13 18:05:50 -0600524/*
525 * The following functions can be used by LLDs that allocate
526 * their own scsi_hosts or by software iscsi LLDs
527 */
Mike Christie6eabafb2008-01-31 13:36:43 -0600528static struct {
529 int value;
530 char *name;
531} iscsi_session_state_names[] = {
532 { ISCSI_SESSION_LOGGED_IN, "LOGGED_IN" },
533 { ISCSI_SESSION_FAILED, "FAILED" },
534 { ISCSI_SESSION_FREE, "FREE" },
535};
536
Adrian Bunk3b0f2082008-02-13 23:30:17 +0200537static const char *iscsi_session_state_name(int state)
Mike Christie6eabafb2008-01-31 13:36:43 -0600538{
539 int i;
540 char *name = NULL;
541
542 for (i = 0; i < ARRAY_SIZE(iscsi_session_state_names); i++) {
543 if (iscsi_session_state_names[i].value == state) {
544 name = iscsi_session_state_names[i].name;
545 break;
546 }
547 }
548 return name;
549}
550
551int iscsi_session_chkready(struct iscsi_cls_session *session)
552{
553 unsigned long flags;
554 int err;
555
556 spin_lock_irqsave(&session->lock, flags);
557 switch (session->state) {
558 case ISCSI_SESSION_LOGGED_IN:
559 err = 0;
560 break;
561 case ISCSI_SESSION_FAILED:
Andrew Vasquez9a1a69a12009-04-29 13:12:39 -0500562 err = DID_IMM_RETRY << 16;
Mike Christie6eabafb2008-01-31 13:36:43 -0600563 break;
564 case ISCSI_SESSION_FREE:
Mike Christie56d7fcf2008-08-19 18:45:26 -0500565 err = DID_TRANSPORT_FAILFAST << 16;
Mike Christie6eabafb2008-01-31 13:36:43 -0600566 break;
567 default:
568 err = DID_NO_CONNECT << 16;
569 break;
570 }
571 spin_unlock_irqrestore(&session->lock, flags);
572 return err;
573}
574EXPORT_SYMBOL_GPL(iscsi_session_chkready);
575
Mike Christie7b8631b2006-01-13 18:05:50 -0600576static void iscsi_session_release(struct device *dev)
577{
578 struct iscsi_cls_session *session = iscsi_dev_to_session(dev);
Mike Christie7b8631b2006-01-13 18:05:50 -0600579 struct Scsi_Host *shost;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700580
Mike Christie7b8631b2006-01-13 18:05:50 -0600581 shost = iscsi_session_to_shost(session);
582 scsi_host_put(shost);
Mike Christie632248a2009-08-20 15:11:01 -0500583 ISCSI_DBG_TRANS_SESSION(session, "Completing session release\n");
Mike Christie7b8631b2006-01-13 18:05:50 -0600584 kfree(session);
Mike Christie7b8631b2006-01-13 18:05:50 -0600585}
Linus Torvalds1da177e2005-04-16 15:20:36 -0700586
Mike Christie7b8631b2006-01-13 18:05:50 -0600587static int iscsi_is_session_dev(const struct device *dev)
588{
589 return dev->release == iscsi_session_release;
590}
Linus Torvalds1da177e2005-04-16 15:20:36 -0700591
Mike Christiea4804cd2008-05-21 15:54:00 -0500592static int iscsi_iter_session_fn(struct device *dev, void *data)
593{
594 void (* fn) (struct iscsi_cls_session *) = data;
595
596 if (!iscsi_is_session_dev(dev))
597 return 0;
598 fn(iscsi_dev_to_session(dev));
599 return 0;
600}
601
602void iscsi_host_for_each_session(struct Scsi_Host *shost,
603 void (*fn)(struct iscsi_cls_session *))
604{
605 device_for_each_child(&shost->shost_gendev, fn,
606 iscsi_iter_session_fn);
607}
608EXPORT_SYMBOL_GPL(iscsi_host_for_each_session);
609
Mike Christie8aae18a2008-01-31 13:36:48 -0600610/**
611 * iscsi_scan_finished - helper to report when running scans are done
612 * @shost: scsi host
613 * @time: scan run time
614 *
615 * This function can be used by drives like qla4xxx to report to the scsi
616 * layer when the scans it kicked off at module load time are done.
617 */
618int iscsi_scan_finished(struct Scsi_Host *shost, unsigned long time)
619{
Mike Christie32c6e1b2008-05-21 15:53:58 -0500620 struct iscsi_cls_host *ihost = shost->shost_data;
Mike Christie8aae18a2008-01-31 13:36:48 -0600621 /*
622 * qla4xxx will have kicked off some session unblocks before calling
623 * scsi_scan_host, so just wait for them to complete.
624 */
625 return !atomic_read(&ihost->nr_scans);
626}
627EXPORT_SYMBOL_GPL(iscsi_scan_finished);
628
Mike Christie79706342008-05-21 15:54:12 -0500629struct iscsi_scan_data {
630 unsigned int channel;
631 unsigned int id;
632 unsigned int lun;
633};
634
635static int iscsi_user_scan_session(struct device *dev, void *data)
636{
637 struct iscsi_scan_data *scan_data = data;
638 struct iscsi_cls_session *session;
639 struct Scsi_Host *shost;
640 struct iscsi_cls_host *ihost;
641 unsigned long flags;
642 unsigned int id;
643
644 if (!iscsi_is_session_dev(dev))
645 return 0;
646
647 session = iscsi_dev_to_session(dev);
Mike Christie632248a2009-08-20 15:11:01 -0500648
649 ISCSI_DBG_TRANS_SESSION(session, "Scanning session\n");
650
Mike Christie79706342008-05-21 15:54:12 -0500651 shost = iscsi_session_to_shost(session);
652 ihost = shost->shost_data;
653
654 mutex_lock(&ihost->mutex);
655 spin_lock_irqsave(&session->lock, flags);
656 if (session->state != ISCSI_SESSION_LOGGED_IN) {
657 spin_unlock_irqrestore(&session->lock, flags);
Mike Christie632248a2009-08-20 15:11:01 -0500658 goto user_scan_exit;
Mike Christie79706342008-05-21 15:54:12 -0500659 }
660 id = session->target_id;
661 spin_unlock_irqrestore(&session->lock, flags);
662
663 if (id != ISCSI_MAX_TARGET) {
664 if ((scan_data->channel == SCAN_WILD_CARD ||
665 scan_data->channel == 0) &&
666 (scan_data->id == SCAN_WILD_CARD ||
667 scan_data->id == id))
668 scsi_scan_target(&session->dev, 0, id,
669 scan_data->lun, 1);
670 }
Mike Christie632248a2009-08-20 15:11:01 -0500671
672user_scan_exit:
Mike Christie79706342008-05-21 15:54:12 -0500673 mutex_unlock(&ihost->mutex);
Mike Christie632248a2009-08-20 15:11:01 -0500674 ISCSI_DBG_TRANS_SESSION(session, "Completed session scan\n");
Mike Christie79706342008-05-21 15:54:12 -0500675 return 0;
676}
677
Mike Christie30a6c652006-04-06 21:13:39 -0500678static int iscsi_user_scan(struct Scsi_Host *shost, uint channel,
679 uint id, uint lun)
680{
Mike Christie79706342008-05-21 15:54:12 -0500681 struct iscsi_scan_data scan_data;
Mike Christie30a6c652006-04-06 21:13:39 -0500682
Mike Christie79706342008-05-21 15:54:12 -0500683 scan_data.channel = channel;
684 scan_data.id = id;
685 scan_data.lun = lun;
Mike Christie30a6c652006-04-06 21:13:39 -0500686
Mike Christie79706342008-05-21 15:54:12 -0500687 return device_for_each_child(&shost->shost_gendev, &scan_data,
688 iscsi_user_scan_session);
Mike Christie30a6c652006-04-06 21:13:39 -0500689}
690
Mike Christiebd976f62008-01-31 13:36:46 -0600691static void iscsi_scan_session(struct work_struct *work)
692{
693 struct iscsi_cls_session *session =
694 container_of(work, struct iscsi_cls_session, scan_work);
Mike Christie8aae18a2008-01-31 13:36:48 -0600695 struct Scsi_Host *shost = iscsi_session_to_shost(session);
Mike Christie32c6e1b2008-05-21 15:53:58 -0500696 struct iscsi_cls_host *ihost = shost->shost_data;
Mike Christie79706342008-05-21 15:54:12 -0500697 struct iscsi_scan_data scan_data;
Mike Christiebd976f62008-01-31 13:36:46 -0600698
Mike Christie79706342008-05-21 15:54:12 -0500699 scan_data.channel = 0;
700 scan_data.id = SCAN_WILD_CARD;
701 scan_data.lun = SCAN_WILD_CARD;
Mike Christiebd976f62008-01-31 13:36:46 -0600702
Mike Christie79706342008-05-21 15:54:12 -0500703 iscsi_user_scan_session(&session->dev, &scan_data);
Mike Christie8aae18a2008-01-31 13:36:48 -0600704 atomic_dec(&ihost->nr_scans);
Mike Christiebd976f62008-01-31 13:36:46 -0600705}
706
Mike Christiec01be6d2010-07-22 16:59:49 +0530707/**
708 * iscsi_block_scsi_eh - block scsi eh until session state has transistioned
Randy Dunlape6d4ef42010-08-14 13:05:41 -0700709 * @cmd: scsi cmd passed to scsi eh handler
Mike Christiec01be6d2010-07-22 16:59:49 +0530710 *
711 * If the session is down this function will wait for the recovery
712 * timer to fire or for the session to be logged back in. If the
713 * recovery timer fires then FAST_IO_FAIL is returned. The caller
714 * should pass this error value to the scsi eh.
715 */
716int iscsi_block_scsi_eh(struct scsi_cmnd *cmd)
717{
718 struct iscsi_cls_session *session =
719 starget_to_session(scsi_target(cmd->device));
720 unsigned long flags;
721 int ret = 0;
722
723 spin_lock_irqsave(&session->lock, flags);
724 while (session->state != ISCSI_SESSION_LOGGED_IN) {
725 if (session->state == ISCSI_SESSION_FREE) {
726 ret = FAST_IO_FAIL;
727 break;
728 }
729 spin_unlock_irqrestore(&session->lock, flags);
730 msleep(1000);
731 spin_lock_irqsave(&session->lock, flags);
732 }
733 spin_unlock_irqrestore(&session->lock, flags);
734 return ret;
735}
736EXPORT_SYMBOL_GPL(iscsi_block_scsi_eh);
737
David Howellsc4028952006-11-22 14:57:56 +0000738static void session_recovery_timedout(struct work_struct *work)
Mike Christie30a6c652006-04-06 21:13:39 -0500739{
David Howellsc4028952006-11-22 14:57:56 +0000740 struct iscsi_cls_session *session =
741 container_of(work, struct iscsi_cls_session,
742 recovery_work.work);
Mike Christie6eabafb2008-01-31 13:36:43 -0600743 unsigned long flags;
Mike Christie30a6c652006-04-06 21:13:39 -0500744
Mike Christie322d7392008-01-31 13:36:52 -0600745 iscsi_cls_session_printk(KERN_INFO, session,
746 "session recovery timed out after %d secs\n",
747 session->recovery_tmo);
Mike Christie30a6c652006-04-06 21:13:39 -0500748
Mike Christie6eabafb2008-01-31 13:36:43 -0600749 spin_lock_irqsave(&session->lock, flags);
750 switch (session->state) {
751 case ISCSI_SESSION_FAILED:
752 session->state = ISCSI_SESSION_FREE;
753 break;
754 case ISCSI_SESSION_LOGGED_IN:
755 case ISCSI_SESSION_FREE:
756 /* we raced with the unblock's flush */
757 spin_unlock_irqrestore(&session->lock, flags);
758 return;
759 }
760 spin_unlock_irqrestore(&session->lock, flags);
761
Mike Christie30a6c652006-04-06 21:13:39 -0500762 if (session->transport->session_recovery_timedout)
763 session->transport->session_recovery_timedout(session);
764
Mike Christie632248a2009-08-20 15:11:01 -0500765 ISCSI_DBG_TRANS_SESSION(session, "Unblocking SCSI target\n");
Mike Christie30a6c652006-04-06 21:13:39 -0500766 scsi_target_unblock(&session->dev);
Mike Christie632248a2009-08-20 15:11:01 -0500767 ISCSI_DBG_TRANS_SESSION(session, "Completed unblocking SCSI target\n");
Mike Christie30a6c652006-04-06 21:13:39 -0500768}
769
Mike Christie45ab33b2008-03-04 13:26:55 -0600770static void __iscsi_unblock_session(struct work_struct *work)
Mike Christie30a6c652006-04-06 21:13:39 -0500771{
Mike Christie45ab33b2008-03-04 13:26:55 -0600772 struct iscsi_cls_session *session =
773 container_of(work, struct iscsi_cls_session,
774 unblock_work);
Mike Christiebd976f62008-01-31 13:36:46 -0600775 struct Scsi_Host *shost = iscsi_session_to_shost(session);
Mike Christie32c6e1b2008-05-21 15:53:58 -0500776 struct iscsi_cls_host *ihost = shost->shost_data;
Mike Christie6eabafb2008-01-31 13:36:43 -0600777 unsigned long flags;
778
Mike Christie632248a2009-08-20 15:11:01 -0500779 ISCSI_DBG_TRANS_SESSION(session, "Unblocking session\n");
Mike Christie45ab33b2008-03-04 13:26:55 -0600780 /*
781 * The recovery and unblock work get run from the same workqueue,
782 * so try to cancel it if it was going to run after this unblock.
783 */
784 cancel_delayed_work(&session->recovery_work);
Mike Christie6eabafb2008-01-31 13:36:43 -0600785 spin_lock_irqsave(&session->lock, flags);
786 session->state = ISCSI_SESSION_LOGGED_IN;
787 spin_unlock_irqrestore(&session->lock, flags);
Mike Christie45ab33b2008-03-04 13:26:55 -0600788 /* start IO */
789 scsi_target_unblock(&session->dev);
Mike Christie8aae18a2008-01-31 13:36:48 -0600790 /*
791 * Only do kernel scanning if the driver is properly hooked into
792 * the async scanning code (drivers like iscsi_tcp do login and
793 * scanning from userspace).
794 */
795 if (shost->hostt->scan_finished) {
Mike Christie06d25af2009-03-05 14:46:02 -0600796 if (scsi_queue_work(shost, &session->scan_work))
Mike Christie8aae18a2008-01-31 13:36:48 -0600797 atomic_inc(&ihost->nr_scans);
798 }
Mike Christie632248a2009-08-20 15:11:01 -0500799 ISCSI_DBG_TRANS_SESSION(session, "Completed unblocking session\n");
Mike Christie6eabafb2008-01-31 13:36:43 -0600800}
Mike Christie45ab33b2008-03-04 13:26:55 -0600801
802/**
803 * iscsi_unblock_session - set a session as logged in and start IO.
804 * @session: iscsi session
805 *
806 * Mark a session as ready to accept IO.
807 */
808void iscsi_unblock_session(struct iscsi_cls_session *session)
809{
810 queue_work(iscsi_eh_timer_workq, &session->unblock_work);
811 /*
812 * make sure all the events have completed before tell the driver
813 * it is safe
814 */
815 flush_workqueue(iscsi_eh_timer_workq);
816}
Mike Christie30a6c652006-04-06 21:13:39 -0500817EXPORT_SYMBOL_GPL(iscsi_unblock_session);
818
Mike Christie45ab33b2008-03-04 13:26:55 -0600819static void __iscsi_block_session(struct work_struct *work)
Mike Christie30a6c652006-04-06 21:13:39 -0500820{
Mike Christie45ab33b2008-03-04 13:26:55 -0600821 struct iscsi_cls_session *session =
822 container_of(work, struct iscsi_cls_session,
823 block_work);
Mike Christie6eabafb2008-01-31 13:36:43 -0600824 unsigned long flags;
825
Mike Christie632248a2009-08-20 15:11:01 -0500826 ISCSI_DBG_TRANS_SESSION(session, "Blocking session\n");
Mike Christie6eabafb2008-01-31 13:36:43 -0600827 spin_lock_irqsave(&session->lock, flags);
828 session->state = ISCSI_SESSION_FAILED;
829 spin_unlock_irqrestore(&session->lock, flags);
Mike Christie30a6c652006-04-06 21:13:39 -0500830 scsi_target_block(&session->dev);
Mike Christie632248a2009-08-20 15:11:01 -0500831 ISCSI_DBG_TRANS_SESSION(session, "Completed SCSI target blocking\n");
Mike Christiefdd46dc2009-11-11 16:34:34 -0600832 if (session->recovery_tmo >= 0)
833 queue_delayed_work(iscsi_eh_timer_workq,
834 &session->recovery_work,
835 session->recovery_tmo * HZ);
Mike Christie30a6c652006-04-06 21:13:39 -0500836}
Mike Christie45ab33b2008-03-04 13:26:55 -0600837
838void iscsi_block_session(struct iscsi_cls_session *session)
839{
840 queue_work(iscsi_eh_timer_workq, &session->block_work);
841}
Mike Christie30a6c652006-04-06 21:13:39 -0500842EXPORT_SYMBOL_GPL(iscsi_block_session);
843
Mike Christie26974782007-12-13 12:43:29 -0600844static void __iscsi_unbind_session(struct work_struct *work)
845{
846 struct iscsi_cls_session *session =
847 container_of(work, struct iscsi_cls_session,
848 unbind_work);
849 struct Scsi_Host *shost = iscsi_session_to_shost(session);
Mike Christie32c6e1b2008-05-21 15:53:58 -0500850 struct iscsi_cls_host *ihost = shost->shost_data;
Mike Christie79706342008-05-21 15:54:12 -0500851 unsigned long flags;
Mike Christie26974782007-12-13 12:43:29 -0600852
Mike Christie632248a2009-08-20 15:11:01 -0500853 ISCSI_DBG_TRANS_SESSION(session, "Unbinding session\n");
854
Mike Christie26974782007-12-13 12:43:29 -0600855 /* Prevent new scans and make sure scanning is not in progress */
856 mutex_lock(&ihost->mutex);
Mike Christie79706342008-05-21 15:54:12 -0500857 spin_lock_irqsave(&session->lock, flags);
858 if (session->target_id == ISCSI_MAX_TARGET) {
859 spin_unlock_irqrestore(&session->lock, flags);
Mike Christie26974782007-12-13 12:43:29 -0600860 mutex_unlock(&ihost->mutex);
861 return;
862 }
Mike Christie79706342008-05-21 15:54:12 -0500863 session->target_id = ISCSI_MAX_TARGET;
864 spin_unlock_irqrestore(&session->lock, flags);
Mike Christie26974782007-12-13 12:43:29 -0600865 mutex_unlock(&ihost->mutex);
866
867 scsi_remove_target(&session->dev);
868 iscsi_session_event(session, ISCSI_KEVENT_UNBIND_SESSION);
Mike Christie632248a2009-08-20 15:11:01 -0500869 ISCSI_DBG_TRANS_SESSION(session, "Completed target removal\n");
Mike Christie26974782007-12-13 12:43:29 -0600870}
871
Mike Christie7b8631b2006-01-13 18:05:50 -0600872struct iscsi_cls_session *
Mike Christie5d91e202008-05-21 15:54:01 -0500873iscsi_alloc_session(struct Scsi_Host *shost, struct iscsi_transport *transport,
874 int dd_size)
Mike Christie7b8631b2006-01-13 18:05:50 -0600875{
876 struct iscsi_cls_session *session;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700877
Mike Christie5d91e202008-05-21 15:54:01 -0500878 session = kzalloc(sizeof(*session) + dd_size,
Mike Christieb5c7a122006-04-06 21:13:33 -0500879 GFP_KERNEL);
Mike Christie7b8631b2006-01-13 18:05:50 -0600880 if (!session)
Mike Christief53a88d2006-06-28 12:00:27 -0500881 return NULL;
882
Mike Christie7b8631b2006-01-13 18:05:50 -0600883 session->transport = transport;
Mike Christie30a6c652006-04-06 21:13:39 -0500884 session->recovery_tmo = 120;
Mike Christie6eabafb2008-01-31 13:36:43 -0600885 session->state = ISCSI_SESSION_FREE;
David Howellsc4028952006-11-22 14:57:56 +0000886 INIT_DELAYED_WORK(&session->recovery_work, session_recovery_timedout);
Mike Christie30a6c652006-04-06 21:13:39 -0500887 INIT_LIST_HEAD(&session->sess_list);
Mike Christie45ab33b2008-03-04 13:26:55 -0600888 INIT_WORK(&session->unblock_work, __iscsi_unblock_session);
889 INIT_WORK(&session->block_work, __iscsi_block_session);
Mike Christie26974782007-12-13 12:43:29 -0600890 INIT_WORK(&session->unbind_work, __iscsi_unbind_session);
Mike Christiebd976f62008-01-31 13:36:46 -0600891 INIT_WORK(&session->scan_work, iscsi_scan_session);
Mike Christie6eabafb2008-01-31 13:36:43 -0600892 spin_lock_init(&session->lock);
Mike Christie7b8631b2006-01-13 18:05:50 -0600893
Mike Christie6a8a0d32006-06-28 12:00:31 -0500894 /* this is released in the dev's release function */
895 scsi_host_get(shost);
Mike Christie8434aa82006-06-28 12:00:30 -0500896 session->dev.parent = &shost->shost_gendev;
897 session->dev.release = iscsi_session_release;
898 device_initialize(&session->dev);
Mike Christie5d91e202008-05-21 15:54:01 -0500899 if (dd_size)
Mike Christieb5c7a122006-04-06 21:13:33 -0500900 session->dd_data = &session[1];
Mike Christie632248a2009-08-20 15:11:01 -0500901
902 ISCSI_DBG_TRANS_SESSION(session, "Completed session allocation\n");
Mike Christie8434aa82006-06-28 12:00:30 -0500903 return session;
904}
905EXPORT_SYMBOL_GPL(iscsi_alloc_session);
906
Mike Christie79706342008-05-21 15:54:12 -0500907static int iscsi_get_next_target_id(struct device *dev, void *data)
908{
909 struct iscsi_cls_session *session;
910 unsigned long flags;
911 int err = 0;
912
913 if (!iscsi_is_session_dev(dev))
914 return 0;
915
916 session = iscsi_dev_to_session(dev);
917 spin_lock_irqsave(&session->lock, flags);
918 if (*((unsigned int *) data) == session->target_id)
919 err = -EEXIST;
920 spin_unlock_irqrestore(&session->lock, flags);
921 return err;
922}
923
Mike Christie6a8a0d32006-06-28 12:00:31 -0500924int iscsi_add_session(struct iscsi_cls_session *session, unsigned int target_id)
Mike Christie8434aa82006-06-28 12:00:30 -0500925{
926 struct Scsi_Host *shost = iscsi_session_to_shost(session);
Mike Christie32c6e1b2008-05-21 15:53:58 -0500927 struct iscsi_cls_host *ihost;
Mike Christie26974782007-12-13 12:43:29 -0600928 unsigned long flags;
Mike Christie79706342008-05-21 15:54:12 -0500929 unsigned int id = target_id;
Mike Christie8434aa82006-06-28 12:00:30 -0500930 int err;
Mike Christieb5c7a122006-04-06 21:13:33 -0500931
Mike Christie30a6c652006-04-06 21:13:39 -0500932 ihost = shost->shost_data;
Mike Christie41be1442007-02-28 17:32:18 -0600933 session->sid = atomic_add_return(1, &iscsi_session_nr);
Mike Christie79706342008-05-21 15:54:12 -0500934
935 if (id == ISCSI_MAX_TARGET) {
936 for (id = 0; id < ISCSI_MAX_TARGET; id++) {
937 err = device_for_each_child(&shost->shost_gendev, &id,
938 iscsi_get_next_target_id);
939 if (!err)
940 break;
941 }
942
943 if (id == ISCSI_MAX_TARGET) {
944 iscsi_cls_session_printk(KERN_ERR, session,
945 "Too many iscsi targets. Max "
946 "number of targets is %d.\n",
947 ISCSI_MAX_TARGET - 1);
Jaswinder Singh Rajput24add1c2009-06-20 13:29:21 +0530948 err = -EOVERFLOW;
Mike Christie79706342008-05-21 15:54:12 -0500949 goto release_host;
950 }
951 }
952 session->target_id = id;
Mike Christie30a6c652006-04-06 21:13:39 -0500953
Kay Sievers71610f52008-12-03 22:41:36 +0100954 dev_set_name(&session->dev, "session%u", session->sid);
Mike Christie8434aa82006-06-28 12:00:30 -0500955 err = device_add(&session->dev);
Mike Christie7b8631b2006-01-13 18:05:50 -0600956 if (err) {
Mike Christie322d7392008-01-31 13:36:52 -0600957 iscsi_cls_session_printk(KERN_ERR, session,
958 "could not register session's dev\n");
Mike Christie8434aa82006-06-28 12:00:30 -0500959 goto release_host;
Mike Christie7b8631b2006-01-13 18:05:50 -0600960 }
961 transport_register_device(&session->dev);
962
Mike Christie26974782007-12-13 12:43:29 -0600963 spin_lock_irqsave(&sesslock, flags);
964 list_add(&session->sess_list, &sesslist);
965 spin_unlock_irqrestore(&sesslock, flags);
966
Mike Christie26974782007-12-13 12:43:29 -0600967 iscsi_session_event(session, ISCSI_KEVENT_CREATE_SESSION);
Mike Christie632248a2009-08-20 15:11:01 -0500968 ISCSI_DBG_TRANS_SESSION(session, "Completed session adding\n");
Mike Christie8434aa82006-06-28 12:00:30 -0500969 return 0;
Mike Christie30a6c652006-04-06 21:13:39 -0500970
Mike Christie8434aa82006-06-28 12:00:30 -0500971release_host:
972 scsi_host_put(shost);
973 return err;
Mike Christie7b8631b2006-01-13 18:05:50 -0600974}
Mike Christie8434aa82006-06-28 12:00:30 -0500975EXPORT_SYMBOL_GPL(iscsi_add_session);
Mike Christie7b8631b2006-01-13 18:05:50 -0600976
977/**
Mike Christie8434aa82006-06-28 12:00:30 -0500978 * iscsi_create_session - create iscsi class session
979 * @shost: scsi host
980 * @transport: iscsi transport
Mike Christie5d91e202008-05-21 15:54:01 -0500981 * @dd_size: private driver data size
Rob Landleyeb448202007-11-03 13:30:39 -0500982 * @target_id: which target
Mike Christie7b8631b2006-01-13 18:05:50 -0600983 *
Mike Christie8434aa82006-06-28 12:00:30 -0500984 * This can be called from a LLD or iscsi_transport.
Rob Landleyeb448202007-11-03 13:30:39 -0500985 */
Mike Christie8434aa82006-06-28 12:00:30 -0500986struct iscsi_cls_session *
Mike Christie5d91e202008-05-21 15:54:01 -0500987iscsi_create_session(struct Scsi_Host *shost, struct iscsi_transport *transport,
988 int dd_size, unsigned int target_id)
Mike Christie8434aa82006-06-28 12:00:30 -0500989{
990 struct iscsi_cls_session *session;
991
Mike Christie5d91e202008-05-21 15:54:01 -0500992 session = iscsi_alloc_session(shost, transport, dd_size);
Mike Christie8434aa82006-06-28 12:00:30 -0500993 if (!session)
994 return NULL;
995
Mike Christie6a8a0d32006-06-28 12:00:31 -0500996 if (iscsi_add_session(session, target_id)) {
Mike Christie8434aa82006-06-28 12:00:30 -0500997 iscsi_free_session(session);
998 return NULL;
999 }
1000 return session;
1001}
1002EXPORT_SYMBOL_GPL(iscsi_create_session);
1003
Mike Christie26974782007-12-13 12:43:29 -06001004static void iscsi_conn_release(struct device *dev)
1005{
1006 struct iscsi_cls_conn *conn = iscsi_dev_to_conn(dev);
1007 struct device *parent = conn->dev.parent;
1008
Mike Christie632248a2009-08-20 15:11:01 -05001009 ISCSI_DBG_TRANS_CONN(conn, "Releasing conn\n");
Mike Christie26974782007-12-13 12:43:29 -06001010 kfree(conn);
1011 put_device(parent);
1012}
1013
1014static int iscsi_is_conn_dev(const struct device *dev)
1015{
1016 return dev->release == iscsi_conn_release;
1017}
1018
1019static int iscsi_iter_destroy_conn_fn(struct device *dev, void *data)
1020{
1021 if (!iscsi_is_conn_dev(dev))
1022 return 0;
1023 return iscsi_destroy_conn(iscsi_dev_to_conn(dev));
1024}
1025
Mike Christie8434aa82006-06-28 12:00:30 -05001026void iscsi_remove_session(struct iscsi_cls_session *session)
Mike Christie7b8631b2006-01-13 18:05:50 -06001027{
Mike Christie30a6c652006-04-06 21:13:39 -05001028 struct Scsi_Host *shost = iscsi_session_to_shost(session);
Mike Christie26974782007-12-13 12:43:29 -06001029 unsigned long flags;
1030 int err;
Mike Christie30a6c652006-04-06 21:13:39 -05001031
Mike Christie632248a2009-08-20 15:11:01 -05001032 ISCSI_DBG_TRANS_SESSION(session, "Removing session\n");
1033
Mike Christie26974782007-12-13 12:43:29 -06001034 spin_lock_irqsave(&sesslock, flags);
1035 list_del(&session->sess_list);
1036 spin_unlock_irqrestore(&sesslock, flags);
1037
Mike Christie45ab33b2008-03-04 13:26:55 -06001038 /* make sure there are no blocks/unblocks queued */
1039 flush_workqueue(iscsi_eh_timer_workq);
1040 /* make sure the timedout callout is not running */
1041 if (!cancel_delayed_work(&session->recovery_work))
1042 flush_workqueue(iscsi_eh_timer_workq);
Mike Christie26974782007-12-13 12:43:29 -06001043 /*
1044 * If we are blocked let commands flow again. The lld or iscsi
1045 * layer should set up the queuecommand to fail commands.
Mike Christie45ab33b2008-03-04 13:26:55 -06001046 * We assume that LLD will not be calling block/unblock while
1047 * removing the session.
Mike Christie26974782007-12-13 12:43:29 -06001048 */
Mike Christie6eabafb2008-01-31 13:36:43 -06001049 spin_lock_irqsave(&session->lock, flags);
1050 session->state = ISCSI_SESSION_FREE;
1051 spin_unlock_irqrestore(&session->lock, flags);
Mike Christiebd976f62008-01-31 13:36:46 -06001052
Mike Christie45ab33b2008-03-04 13:26:55 -06001053 scsi_target_unblock(&session->dev);
1054 /* flush running scans then delete devices */
Mike Christie06d25af2009-03-05 14:46:02 -06001055 scsi_flush_work(shost);
Mike Christie45ab33b2008-03-04 13:26:55 -06001056 __iscsi_unbind_session(&session->unbind_work);
Mike Christie30a6c652006-04-06 21:13:39 -05001057
Mike Christie26974782007-12-13 12:43:29 -06001058 /* hw iscsi may not have removed all connections from session */
1059 err = device_for_each_child(&session->dev, NULL,
1060 iscsi_iter_destroy_conn_fn);
1061 if (err)
Mike Christie322d7392008-01-31 13:36:52 -06001062 iscsi_cls_session_printk(KERN_ERR, session,
1063 "Could not delete all connections "
1064 "for session. Error %d.\n", err);
Mike Christie8434aa82006-06-28 12:00:30 -05001065
Mike Christie7b8631b2006-01-13 18:05:50 -06001066 transport_unregister_device(&session->dev);
Mike Christie632248a2009-08-20 15:11:01 -05001067
1068 ISCSI_DBG_TRANS_SESSION(session, "Completing session removal\n");
Mike Christie8434aa82006-06-28 12:00:30 -05001069 device_del(&session->dev);
1070}
1071EXPORT_SYMBOL_GPL(iscsi_remove_session);
1072
1073void iscsi_free_session(struct iscsi_cls_session *session)
1074{
Mike Christie632248a2009-08-20 15:11:01 -05001075 ISCSI_DBG_TRANS_SESSION(session, "Freeing session\n");
Mike Christie26974782007-12-13 12:43:29 -06001076 iscsi_session_event(session, ISCSI_KEVENT_DESTROY_SESSION);
Mike Christie8434aa82006-06-28 12:00:30 -05001077 put_device(&session->dev);
Mike Christie7b8631b2006-01-13 18:05:50 -06001078}
Mike Christie8434aa82006-06-28 12:00:30 -05001079EXPORT_SYMBOL_GPL(iscsi_free_session);
1080
1081/**
1082 * iscsi_destroy_session - destroy iscsi session
1083 * @session: iscsi_session
1084 *
1085 * Can be called by a LLD or iscsi_transport. There must not be
1086 * any running connections.
Rob Landleyeb448202007-11-03 13:30:39 -05001087 */
Mike Christie8434aa82006-06-28 12:00:30 -05001088int iscsi_destroy_session(struct iscsi_cls_session *session)
1089{
1090 iscsi_remove_session(session);
Mike Christie632248a2009-08-20 15:11:01 -05001091 ISCSI_DBG_TRANS_SESSION(session, "Completing session destruction\n");
Mike Christie8434aa82006-06-28 12:00:30 -05001092 iscsi_free_session(session);
1093 return 0;
1094}
Mike Christie7b8631b2006-01-13 18:05:50 -06001095EXPORT_SYMBOL_GPL(iscsi_destroy_session);
1096
Mike Christie7b8631b2006-01-13 18:05:50 -06001097/**
1098 * iscsi_create_conn - create iscsi class connection
1099 * @session: iscsi cls session
Mike Christie5d91e202008-05-21 15:54:01 -05001100 * @dd_size: private driver data size
Mike Christie7b8631b2006-01-13 18:05:50 -06001101 * @cid: connection id
1102 *
1103 * This can be called from a LLD or iscsi_transport. The connection
1104 * is child of the session so cid must be unique for all connections
1105 * on the session.
Mike Christieb5c7a122006-04-06 21:13:33 -05001106 *
1107 * Since we do not support MCS, cid will normally be zero. In some cases
1108 * for software iscsi we could be trying to preallocate a connection struct
1109 * in which case there could be two connection structs and cid would be
1110 * non-zero.
Rob Landleyeb448202007-11-03 13:30:39 -05001111 */
Mike Christie7b8631b2006-01-13 18:05:50 -06001112struct iscsi_cls_conn *
Mike Christie5d91e202008-05-21 15:54:01 -05001113iscsi_create_conn(struct iscsi_cls_session *session, int dd_size, uint32_t cid)
Mike Christie7b8631b2006-01-13 18:05:50 -06001114{
1115 struct iscsi_transport *transport = session->transport;
Mike Christie7b8631b2006-01-13 18:05:50 -06001116 struct iscsi_cls_conn *conn;
Mike Christie26974782007-12-13 12:43:29 -06001117 unsigned long flags;
Mike Christie7b8631b2006-01-13 18:05:50 -06001118 int err;
1119
Mike Christie5d91e202008-05-21 15:54:01 -05001120 conn = kzalloc(sizeof(*conn) + dd_size, GFP_KERNEL);
Mike Christie7b8631b2006-01-13 18:05:50 -06001121 if (!conn)
1122 return NULL;
Mike Christie5d91e202008-05-21 15:54:01 -05001123 if (dd_size)
Mike Christie7b8631b2006-01-13 18:05:50 -06001124 conn->dd_data = &conn[1];
1125
Mike Christie22a39fb2011-02-16 15:04:33 -06001126 mutex_init(&conn->ep_mutex);
Mike Christie7b8631b2006-01-13 18:05:50 -06001127 INIT_LIST_HEAD(&conn->conn_list);
1128 conn->transport = transport;
Mike Christieb5c7a122006-04-06 21:13:33 -05001129 conn->cid = cid;
Mike Christie7b8631b2006-01-13 18:05:50 -06001130
1131 /* this is released in the dev's release function */
1132 if (!get_device(&session->dev))
Mike Christie43a145a2006-10-16 18:09:38 -04001133 goto free_conn;
Mike Christieb5c7a122006-04-06 21:13:33 -05001134
Kay Sievers71610f52008-12-03 22:41:36 +01001135 dev_set_name(&conn->dev, "connection%d:%u", session->sid, cid);
Mike Christie7b8631b2006-01-13 18:05:50 -06001136 conn->dev.parent = &session->dev;
1137 conn->dev.release = iscsi_conn_release;
1138 err = device_register(&conn->dev);
1139 if (err) {
Mike Christie322d7392008-01-31 13:36:52 -06001140 iscsi_cls_session_printk(KERN_ERR, session, "could not "
1141 "register connection's dev\n");
Mike Christie7b8631b2006-01-13 18:05:50 -06001142 goto release_parent_ref;
1143 }
1144 transport_register_device(&conn->dev);
Mike Christie26974782007-12-13 12:43:29 -06001145
1146 spin_lock_irqsave(&connlock, flags);
1147 list_add(&conn->conn_list, &connlist);
Mike Christie26974782007-12-13 12:43:29 -06001148 spin_unlock_irqrestore(&connlock, flags);
Mike Christie632248a2009-08-20 15:11:01 -05001149
1150 ISCSI_DBG_TRANS_CONN(conn, "Completed conn creation\n");
Mike Christie7b8631b2006-01-13 18:05:50 -06001151 return conn;
1152
1153release_parent_ref:
1154 put_device(&session->dev);
1155free_conn:
1156 kfree(conn);
1157 return NULL;
1158}
1159
1160EXPORT_SYMBOL_GPL(iscsi_create_conn);
1161
1162/**
1163 * iscsi_destroy_conn - destroy iscsi class connection
Rob Landleyeb448202007-11-03 13:30:39 -05001164 * @conn: iscsi cls session
Mike Christie7b8631b2006-01-13 18:05:50 -06001165 *
Mike Christie26974782007-12-13 12:43:29 -06001166 * This can be called from a LLD or iscsi_transport.
Rob Landleyeb448202007-11-03 13:30:39 -05001167 */
Mike Christie7b8631b2006-01-13 18:05:50 -06001168int iscsi_destroy_conn(struct iscsi_cls_conn *conn)
1169{
Mike Christie26974782007-12-13 12:43:29 -06001170 unsigned long flags;
1171
1172 spin_lock_irqsave(&connlock, flags);
Mike Christie26974782007-12-13 12:43:29 -06001173 list_del(&conn->conn_list);
1174 spin_unlock_irqrestore(&connlock, flags);
1175
Mike Christie7b8631b2006-01-13 18:05:50 -06001176 transport_unregister_device(&conn->dev);
Mike Christie632248a2009-08-20 15:11:01 -05001177 ISCSI_DBG_TRANS_CONN(conn, "Completing conn destruction\n");
Mike Christie7b8631b2006-01-13 18:05:50 -06001178 device_unregister(&conn->dev);
1179 return 0;
1180}
Mike Christie7b8631b2006-01-13 18:05:50 -06001181EXPORT_SYMBOL_GPL(iscsi_destroy_conn);
1182
1183/*
Mike Christie7b8631b2006-01-13 18:05:50 -06001184 * iscsi interface functions
1185 */
Alex Aizman0896b752005-08-04 19:33:07 -07001186static struct iscsi_internal *
1187iscsi_if_transport_lookup(struct iscsi_transport *tt)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001188{
Alex Aizman0896b752005-08-04 19:33:07 -07001189 struct iscsi_internal *priv;
1190 unsigned long flags;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001191
Alex Aizman0896b752005-08-04 19:33:07 -07001192 spin_lock_irqsave(&iscsi_transport_lock, flags);
1193 list_for_each_entry(priv, &iscsi_transports, list) {
1194 if (tt == priv->iscsi_transport) {
1195 spin_unlock_irqrestore(&iscsi_transport_lock, flags);
1196 return priv;
1197 }
1198 }
1199 spin_unlock_irqrestore(&iscsi_transport_lock, flags);
1200 return NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001201}
Linus Torvalds1da177e2005-04-16 15:20:36 -07001202
Alex Aizman0896b752005-08-04 19:33:07 -07001203static int
Michael Chan43514772009-06-08 18:14:41 -07001204iscsi_multicast_skb(struct sk_buff *skb, uint32_t group, gfp_t gfp)
Mike Christie53cb8a12006-06-28 12:00:32 -05001205{
Michael Chan43514772009-06-08 18:14:41 -07001206 return nlmsg_multicast(nls, skb, 0, group, gfp);
Alex Aizman0896b752005-08-04 19:33:07 -07001207}
1208
Mike Christie7b7232f2006-02-01 21:06:49 -06001209int iscsi_recv_pdu(struct iscsi_cls_conn *conn, struct iscsi_hdr *hdr,
Alex Aizman0896b752005-08-04 19:33:07 -07001210 char *data, uint32_t data_size)
1211{
1212 struct nlmsghdr *nlh;
1213 struct sk_buff *skb;
1214 struct iscsi_uevent *ev;
Alex Aizman0896b752005-08-04 19:33:07 -07001215 char *pdu;
Mike Christie790f39a2006-05-18 20:31:39 -05001216 struct iscsi_internal *priv;
Alex Aizman0896b752005-08-04 19:33:07 -07001217 int len = NLMSG_SPACE(sizeof(*ev) + sizeof(struct iscsi_hdr) +
1218 data_size);
1219
Mike Christie790f39a2006-05-18 20:31:39 -05001220 priv = iscsi_if_transport_lookup(conn->transport);
1221 if (!priv)
1222 return -EINVAL;
1223
Mike Christie43a145a2006-10-16 18:09:38 -04001224 skb = alloc_skb(len, GFP_ATOMIC);
Alex Aizman0896b752005-08-04 19:33:07 -07001225 if (!skb) {
Mike Christiee5bd7b52008-09-24 11:46:10 -05001226 iscsi_conn_error_event(conn, ISCSI_ERR_CONN_FAILED);
Mike Christie322d7392008-01-31 13:36:52 -06001227 iscsi_cls_conn_printk(KERN_ERR, conn, "can not deliver "
1228 "control PDU: OOM\n");
Alex Aizman0896b752005-08-04 19:33:07 -07001229 return -ENOMEM;
1230 }
1231
Michael Chan43514772009-06-08 18:14:41 -07001232 nlh = __nlmsg_put(skb, 0, 0, 0, (len - sizeof(*nlh)), 0);
Alex Aizman0896b752005-08-04 19:33:07 -07001233 ev = NLMSG_DATA(nlh);
1234 memset(ev, 0, sizeof(*ev));
1235 ev->transport_handle = iscsi_handle(conn->transport);
1236 ev->type = ISCSI_KEVENT_RECV_PDU;
Mike Christieb5c7a122006-04-06 21:13:33 -05001237 ev->r.recv_req.cid = conn->cid;
1238 ev->r.recv_req.sid = iscsi_conn_get_sid(conn);
Alex Aizman0896b752005-08-04 19:33:07 -07001239 pdu = (char*)ev + sizeof(*ev);
1240 memcpy(pdu, hdr, sizeof(struct iscsi_hdr));
1241 memcpy(pdu + sizeof(struct iscsi_hdr), data, data_size);
1242
Michael Chan43514772009-06-08 18:14:41 -07001243 return iscsi_multicast_skb(skb, ISCSI_NL_GRP_ISCSID, GFP_ATOMIC);
Alex Aizman0896b752005-08-04 19:33:07 -07001244}
1245EXPORT_SYMBOL_GPL(iscsi_recv_pdu);
1246
Michael Chan43514772009-06-08 18:14:41 -07001247int iscsi_offload_mesg(struct Scsi_Host *shost,
1248 struct iscsi_transport *transport, uint32_t type,
1249 char *data, uint16_t data_size)
1250{
1251 struct nlmsghdr *nlh;
1252 struct sk_buff *skb;
1253 struct iscsi_uevent *ev;
1254 int len = NLMSG_SPACE(sizeof(*ev) + data_size);
1255
Michael Chana541f842009-07-29 08:49:52 +00001256 skb = alloc_skb(len, GFP_ATOMIC);
Michael Chan43514772009-06-08 18:14:41 -07001257 if (!skb) {
1258 printk(KERN_ERR "can not deliver iscsi offload message:OOM\n");
1259 return -ENOMEM;
1260 }
1261
1262 nlh = __nlmsg_put(skb, 0, 0, 0, (len - sizeof(*nlh)), 0);
1263 ev = NLMSG_DATA(nlh);
1264 memset(ev, 0, sizeof(*ev));
1265 ev->type = type;
1266 ev->transport_handle = iscsi_handle(transport);
1267 switch (type) {
1268 case ISCSI_KEVENT_PATH_REQ:
1269 ev->r.req_path.host_no = shost->host_no;
1270 break;
1271 case ISCSI_KEVENT_IF_DOWN:
1272 ev->r.notify_if_down.host_no = shost->host_no;
1273 break;
1274 }
1275
1276 memcpy((char *)ev + sizeof(*ev), data, data_size);
1277
Michael Chana541f842009-07-29 08:49:52 +00001278 return iscsi_multicast_skb(skb, ISCSI_NL_GRP_UIP, GFP_ATOMIC);
Michael Chan43514772009-06-08 18:14:41 -07001279}
1280EXPORT_SYMBOL_GPL(iscsi_offload_mesg);
1281
Mike Christiee5bd7b52008-09-24 11:46:10 -05001282void iscsi_conn_error_event(struct iscsi_cls_conn *conn, enum iscsi_err error)
Alex Aizman0896b752005-08-04 19:33:07 -07001283{
1284 struct nlmsghdr *nlh;
1285 struct sk_buff *skb;
1286 struct iscsi_uevent *ev;
Mike Christie790f39a2006-05-18 20:31:39 -05001287 struct iscsi_internal *priv;
Alex Aizman0896b752005-08-04 19:33:07 -07001288 int len = NLMSG_SPACE(sizeof(*ev));
1289
Mike Christie790f39a2006-05-18 20:31:39 -05001290 priv = iscsi_if_transport_lookup(conn->transport);
1291 if (!priv)
1292 return;
1293
Mike Christie43a145a2006-10-16 18:09:38 -04001294 skb = alloc_skb(len, GFP_ATOMIC);
Alex Aizman0896b752005-08-04 19:33:07 -07001295 if (!skb) {
Mike Christie322d7392008-01-31 13:36:52 -06001296 iscsi_cls_conn_printk(KERN_ERR, conn, "gracefully ignored "
1297 "conn error (%d)\n", error);
Alex Aizman0896b752005-08-04 19:33:07 -07001298 return;
1299 }
1300
Michael Chan43514772009-06-08 18:14:41 -07001301 nlh = __nlmsg_put(skb, 0, 0, 0, (len - sizeof(*nlh)), 0);
Alex Aizman0896b752005-08-04 19:33:07 -07001302 ev = NLMSG_DATA(nlh);
1303 ev->transport_handle = iscsi_handle(conn->transport);
1304 ev->type = ISCSI_KEVENT_CONN_ERROR;
Alex Aizman0896b752005-08-04 19:33:07 -07001305 ev->r.connerror.error = error;
Mike Christieb5c7a122006-04-06 21:13:33 -05001306 ev->r.connerror.cid = conn->cid;
1307 ev->r.connerror.sid = iscsi_conn_get_sid(conn);
Alex Aizman0896b752005-08-04 19:33:07 -07001308
Michael Chan43514772009-06-08 18:14:41 -07001309 iscsi_multicast_skb(skb, ISCSI_NL_GRP_ISCSID, GFP_ATOMIC);
Alex Aizman0896b752005-08-04 19:33:07 -07001310
Mike Christie322d7392008-01-31 13:36:52 -06001311 iscsi_cls_conn_printk(KERN_INFO, conn, "detected conn error (%d)\n",
1312 error);
Alex Aizman0896b752005-08-04 19:33:07 -07001313}
Mike Christiee5bd7b52008-09-24 11:46:10 -05001314EXPORT_SYMBOL_GPL(iscsi_conn_error_event);
Alex Aizman0896b752005-08-04 19:33:07 -07001315
1316static int
Michael Chan43514772009-06-08 18:14:41 -07001317iscsi_if_send_reply(uint32_t group, int seq, int type, int done, int multi,
1318 void *payload, int size)
Alex Aizman0896b752005-08-04 19:33:07 -07001319{
1320 struct sk_buff *skb;
1321 struct nlmsghdr *nlh;
1322 int len = NLMSG_SPACE(size);
1323 int flags = multi ? NLM_F_MULTI : 0;
1324 int t = done ? NLMSG_DONE : type;
1325
Mike Christie43a145a2006-10-16 18:09:38 -04001326 skb = alloc_skb(len, GFP_ATOMIC);
Mike Christie239a7dc2007-05-30 12:57:07 -05001327 if (!skb) {
1328 printk(KERN_ERR "Could not allocate skb to send reply.\n");
1329 return -ENOMEM;
1330 }
Alex Aizman0896b752005-08-04 19:33:07 -07001331
Michael Chan43514772009-06-08 18:14:41 -07001332 nlh = __nlmsg_put(skb, 0, 0, t, (len - sizeof(*nlh)), 0);
Alex Aizman0896b752005-08-04 19:33:07 -07001333 nlh->nlmsg_flags = flags;
1334 memcpy(NLMSG_DATA(nlh), payload, size);
Michael Chan43514772009-06-08 18:14:41 -07001335 return iscsi_multicast_skb(skb, group, GFP_ATOMIC);
Alex Aizman0896b752005-08-04 19:33:07 -07001336}
1337
1338static int
Mike Christie5b940ad2006-02-01 21:06:56 -06001339iscsi_if_get_stats(struct iscsi_transport *transport, struct nlmsghdr *nlh)
Alex Aizman0896b752005-08-04 19:33:07 -07001340{
1341 struct iscsi_uevent *ev = NLMSG_DATA(nlh);
1342 struct iscsi_stats *stats;
1343 struct sk_buff *skbstat;
Mike Christie7b8631b2006-01-13 18:05:50 -06001344 struct iscsi_cls_conn *conn;
Alex Aizman0896b752005-08-04 19:33:07 -07001345 struct nlmsghdr *nlhstat;
1346 struct iscsi_uevent *evstat;
Mike Christie790f39a2006-05-18 20:31:39 -05001347 struct iscsi_internal *priv;
Alex Aizman0896b752005-08-04 19:33:07 -07001348 int len = NLMSG_SPACE(sizeof(*ev) +
1349 sizeof(struct iscsi_stats) +
1350 sizeof(struct iscsi_stats_custom) *
1351 ISCSI_STATS_CUSTOM_MAX);
1352 int err = 0;
1353
Mike Christie790f39a2006-05-18 20:31:39 -05001354 priv = iscsi_if_transport_lookup(transport);
1355 if (!priv)
1356 return -EINVAL;
1357
Mike Christieb5c7a122006-04-06 21:13:33 -05001358 conn = iscsi_conn_lookup(ev->u.get_stats.sid, ev->u.get_stats.cid);
Alex Aizman0896b752005-08-04 19:33:07 -07001359 if (!conn)
1360 return -EEXIST;
1361
1362 do {
1363 int actual_size;
1364
Mike Christie43a145a2006-10-16 18:09:38 -04001365 skbstat = alloc_skb(len, GFP_ATOMIC);
Alex Aizman0896b752005-08-04 19:33:07 -07001366 if (!skbstat) {
Mike Christie322d7392008-01-31 13:36:52 -06001367 iscsi_cls_conn_printk(KERN_ERR, conn, "can not "
1368 "deliver stats: OOM\n");
Alex Aizman0896b752005-08-04 19:33:07 -07001369 return -ENOMEM;
1370 }
1371
Michael Chan43514772009-06-08 18:14:41 -07001372 nlhstat = __nlmsg_put(skbstat, 0, 0, 0,
Alex Aizman0896b752005-08-04 19:33:07 -07001373 (len - sizeof(*nlhstat)), 0);
1374 evstat = NLMSG_DATA(nlhstat);
1375 memset(evstat, 0, sizeof(*evstat));
1376 evstat->transport_handle = iscsi_handle(conn->transport);
1377 evstat->type = nlh->nlmsg_type;
Mike Christieb5c7a122006-04-06 21:13:33 -05001378 evstat->u.get_stats.cid =
1379 ev->u.get_stats.cid;
1380 evstat->u.get_stats.sid =
1381 ev->u.get_stats.sid;
Alex Aizman0896b752005-08-04 19:33:07 -07001382 stats = (struct iscsi_stats *)
1383 ((char*)evstat + sizeof(*evstat));
1384 memset(stats, 0, sizeof(*stats));
1385
Mike Christie7b7232f2006-02-01 21:06:49 -06001386 transport->get_stats(conn, stats);
Alex Aizman0896b752005-08-04 19:33:07 -07001387 actual_size = NLMSG_SPACE(sizeof(struct iscsi_uevent) +
1388 sizeof(struct iscsi_stats) +
1389 sizeof(struct iscsi_stats_custom) *
1390 stats->custom_length);
1391 actual_size -= sizeof(*nlhstat);
1392 actual_size = NLMSG_LENGTH(actual_size);
Mike Christie5b940ad2006-02-01 21:06:56 -06001393 skb_trim(skbstat, NLMSG_ALIGN(actual_size));
Alex Aizman0896b752005-08-04 19:33:07 -07001394 nlhstat->nlmsg_len = actual_size;
1395
Michael Chan43514772009-06-08 18:14:41 -07001396 err = iscsi_multicast_skb(skbstat, ISCSI_NL_GRP_ISCSID,
1397 GFP_ATOMIC);
Alex Aizman0896b752005-08-04 19:33:07 -07001398 } while (err < 0 && err != -ECONNREFUSED);
1399
1400 return err;
1401}
1402
Mike Christie53cb8a12006-06-28 12:00:32 -05001403/**
Mike Christie26974782007-12-13 12:43:29 -06001404 * iscsi_session_event - send session destr. completion event
1405 * @session: iscsi class session
1406 * @event: type of event
Rob Landleyeb448202007-11-03 13:30:39 -05001407 */
Mike Christie26974782007-12-13 12:43:29 -06001408int iscsi_session_event(struct iscsi_cls_session *session,
1409 enum iscsi_uevent_e event)
Mike Christie53cb8a12006-06-28 12:00:32 -05001410{
1411 struct iscsi_internal *priv;
Mike Christie53cb8a12006-06-28 12:00:32 -05001412 struct Scsi_Host *shost;
1413 struct iscsi_uevent *ev;
1414 struct sk_buff *skb;
1415 struct nlmsghdr *nlh;
Mike Christie53cb8a12006-06-28 12:00:32 -05001416 int rc, len = NLMSG_SPACE(sizeof(*ev));
1417
Mike Christie26974782007-12-13 12:43:29 -06001418 priv = iscsi_if_transport_lookup(session->transport);
Mike Christie53cb8a12006-06-28 12:00:32 -05001419 if (!priv)
1420 return -EINVAL;
Mike Christie53cb8a12006-06-28 12:00:32 -05001421 shost = iscsi_session_to_shost(session);
1422
Mike Christie43a145a2006-10-16 18:09:38 -04001423 skb = alloc_skb(len, GFP_KERNEL);
Mike Christie53cb8a12006-06-28 12:00:32 -05001424 if (!skb) {
Mike Christie322d7392008-01-31 13:36:52 -06001425 iscsi_cls_session_printk(KERN_ERR, session,
1426 "Cannot notify userspace of session "
1427 "event %u\n", event);
Mike Christie53cb8a12006-06-28 12:00:32 -05001428 return -ENOMEM;
1429 }
1430
Michael Chan43514772009-06-08 18:14:41 -07001431 nlh = __nlmsg_put(skb, 0, 0, 0, (len - sizeof(*nlh)), 0);
Mike Christie53cb8a12006-06-28 12:00:32 -05001432 ev = NLMSG_DATA(nlh);
Mike Christie26974782007-12-13 12:43:29 -06001433 ev->transport_handle = iscsi_handle(session->transport);
1434
1435 ev->type = event;
1436 switch (event) {
1437 case ISCSI_KEVENT_DESTROY_SESSION:
1438 ev->r.d_session.host_no = shost->host_no;
1439 ev->r.d_session.sid = session->sid;
1440 break;
1441 case ISCSI_KEVENT_CREATE_SESSION:
1442 ev->r.c_session_ret.host_no = shost->host_no;
1443 ev->r.c_session_ret.sid = session->sid;
1444 break;
1445 case ISCSI_KEVENT_UNBIND_SESSION:
1446 ev->r.unbind_session.host_no = shost->host_no;
1447 ev->r.unbind_session.sid = session->sid;
1448 break;
1449 default:
Mike Christie322d7392008-01-31 13:36:52 -06001450 iscsi_cls_session_printk(KERN_ERR, session, "Invalid event "
1451 "%u.\n", event);
Mike Christie26974782007-12-13 12:43:29 -06001452 kfree_skb(skb);
1453 return -EINVAL;
1454 }
Mike Christie53cb8a12006-06-28 12:00:32 -05001455
1456 /*
1457 * this will occur if the daemon is not up, so we just warn
1458 * the user and when the daemon is restarted it will handle it
1459 */
Michael Chan43514772009-06-08 18:14:41 -07001460 rc = iscsi_multicast_skb(skb, ISCSI_NL_GRP_ISCSID, GFP_KERNEL);
Pablo Neira Ayusoff491a72009-02-05 23:56:36 -08001461 if (rc == -ESRCH)
Mike Christie322d7392008-01-31 13:36:52 -06001462 iscsi_cls_session_printk(KERN_ERR, session,
1463 "Cannot notify userspace of session "
1464 "event %u. Check iscsi daemon\n",
1465 event);
Mike Christie632248a2009-08-20 15:11:01 -05001466
1467 ISCSI_DBG_TRANS_SESSION(session, "Completed handling event %d rc %d\n",
1468 event, rc);
Mike Christie53cb8a12006-06-28 12:00:32 -05001469 return rc;
1470}
Mike Christie26974782007-12-13 12:43:29 -06001471EXPORT_SYMBOL_GPL(iscsi_session_event);
Mike Christie53cb8a12006-06-28 12:00:32 -05001472
Alex Aizman0896b752005-08-04 19:33:07 -07001473static int
Mike Christied82ff9be2008-05-21 15:54:13 -05001474iscsi_if_create_session(struct iscsi_internal *priv, struct iscsi_endpoint *ep,
1475 struct iscsi_uevent *ev, uint32_t initial_cmdsn,
Mike Christie40753ca2008-05-21 15:53:56 -05001476 uint16_t cmds_max, uint16_t queue_depth)
Mike Christie7b8631b2006-01-13 18:05:50 -06001477{
1478 struct iscsi_transport *transport = priv->iscsi_transport;
Mike Christie7b7232f2006-02-01 21:06:49 -06001479 struct iscsi_cls_session *session;
Mike Christie5e7facb2009-03-05 14:46:06 -06001480 struct Scsi_Host *shost;
Mike Christie7b8631b2006-01-13 18:05:50 -06001481
Mike Christied82ff9be2008-05-21 15:54:13 -05001482 session = transport->create_session(ep, cmds_max, queue_depth,
Mike Christie5e7facb2009-03-05 14:46:06 -06001483 initial_cmdsn);
Mike Christie7b7232f2006-02-01 21:06:49 -06001484 if (!session)
Mike Christie7b8631b2006-01-13 18:05:50 -06001485 return -ENOMEM;
1486
Mike Christie5e7facb2009-03-05 14:46:06 -06001487 shost = iscsi_session_to_shost(session);
1488 ev->r.c_session_ret.host_no = shost->host_no;
Mike Christieb5c7a122006-04-06 21:13:33 -05001489 ev->r.c_session_ret.sid = session->sid;
Mike Christie632248a2009-08-20 15:11:01 -05001490 ISCSI_DBG_TRANS_SESSION(session,
1491 "Completed creating transport session\n");
Mike Christie7b8631b2006-01-13 18:05:50 -06001492 return 0;
1493}
1494
1495static int
Mike Christie7b7232f2006-02-01 21:06:49 -06001496iscsi_if_create_conn(struct iscsi_transport *transport, struct iscsi_uevent *ev)
Mike Christie7b8631b2006-01-13 18:05:50 -06001497{
Mike Christie7b8631b2006-01-13 18:05:50 -06001498 struct iscsi_cls_conn *conn;
Mike Christie7b7232f2006-02-01 21:06:49 -06001499 struct iscsi_cls_session *session;
Mike Christie7b8631b2006-01-13 18:05:50 -06001500
Mike Christieb5c7a122006-04-06 21:13:33 -05001501 session = iscsi_session_lookup(ev->u.c_conn.sid);
1502 if (!session) {
Mike Christie322d7392008-01-31 13:36:52 -06001503 printk(KERN_ERR "iscsi: invalid session %d.\n",
Mike Christieb5c7a122006-04-06 21:13:33 -05001504 ev->u.c_conn.sid);
Mike Christie7b8631b2006-01-13 18:05:50 -06001505 return -EINVAL;
Mike Christieb5c7a122006-04-06 21:13:33 -05001506 }
Mike Christie7b8631b2006-01-13 18:05:50 -06001507
Mike Christie7b7232f2006-02-01 21:06:49 -06001508 conn = transport->create_conn(session, ev->u.c_conn.cid);
Mike Christieb5c7a122006-04-06 21:13:33 -05001509 if (!conn) {
Mike Christie322d7392008-01-31 13:36:52 -06001510 iscsi_cls_session_printk(KERN_ERR, session,
1511 "couldn't create a new connection.");
Mike Christie7b7232f2006-02-01 21:06:49 -06001512 return -ENOMEM;
Mike Christieb5c7a122006-04-06 21:13:33 -05001513 }
Mike Christie7b8631b2006-01-13 18:05:50 -06001514
Mike Christieb5c7a122006-04-06 21:13:33 -05001515 ev->r.c_conn_ret.sid = session->sid;
1516 ev->r.c_conn_ret.cid = conn->cid;
Mike Christie632248a2009-08-20 15:11:01 -05001517
1518 ISCSI_DBG_TRANS_CONN(conn, "Completed creating transport conn\n");
Mike Christie7b8631b2006-01-13 18:05:50 -06001519 return 0;
Mike Christie7b8631b2006-01-13 18:05:50 -06001520}
1521
1522static int
1523iscsi_if_destroy_conn(struct iscsi_transport *transport, struct iscsi_uevent *ev)
1524{
Mike Christie7b8631b2006-01-13 18:05:50 -06001525 struct iscsi_cls_conn *conn;
Mike Christie7b8631b2006-01-13 18:05:50 -06001526
Mike Christieb5c7a122006-04-06 21:13:33 -05001527 conn = iscsi_conn_lookup(ev->u.d_conn.sid, ev->u.d_conn.cid);
Mike Christie7b8631b2006-01-13 18:05:50 -06001528 if (!conn)
Mike Christie7b8631b2006-01-13 18:05:50 -06001529 return -EINVAL;
Mike Christie7b8631b2006-01-13 18:05:50 -06001530
Mike Christie632248a2009-08-20 15:11:01 -05001531 ISCSI_DBG_TRANS_CONN(conn, "Destroying transport conn\n");
Mike Christie7b8631b2006-01-13 18:05:50 -06001532 if (transport->destroy_conn)
1533 transport->destroy_conn(conn);
Mike Christie632248a2009-08-20 15:11:01 -05001534
Mike Christie7b8631b2006-01-13 18:05:50 -06001535 return 0;
1536}
1537
Mike Christiefd7255f2006-04-06 21:13:36 -05001538static int
1539iscsi_set_param(struct iscsi_transport *transport, struct iscsi_uevent *ev)
1540{
1541 char *data = (char*)ev + sizeof(*ev);
1542 struct iscsi_cls_conn *conn;
1543 struct iscsi_cls_session *session;
Mike Christiea54a52c2006-06-28 12:00:23 -05001544 int err = 0, value = 0;
Mike Christiefd7255f2006-04-06 21:13:36 -05001545
1546 session = iscsi_session_lookup(ev->u.set_param.sid);
1547 conn = iscsi_conn_lookup(ev->u.set_param.sid, ev->u.set_param.cid);
1548 if (!conn || !session)
1549 return -EINVAL;
1550
1551 switch (ev->u.set_param.param) {
Mike Christie30a6c652006-04-06 21:13:39 -05001552 case ISCSI_PARAM_SESS_RECOVERY_TMO:
Mike Christiea54a52c2006-06-28 12:00:23 -05001553 sscanf(data, "%d", &value);
Mike Christiefdd46dc2009-11-11 16:34:34 -06001554 session->recovery_tmo = value;
Mike Christie30a6c652006-04-06 21:13:39 -05001555 break;
Mike Christiefd7255f2006-04-06 21:13:36 -05001556 default:
Mike Christiea54a52c2006-06-28 12:00:23 -05001557 err = transport->set_param(conn, ev->u.set_param.param,
1558 data, ev->u.set_param.len);
Mike Christiefd7255f2006-04-06 21:13:36 -05001559 }
1560
1561 return err;
1562}
1563
Mike Christie10eb0f02009-05-13 17:57:38 -05001564static int iscsi_if_ep_connect(struct iscsi_transport *transport,
1565 struct iscsi_uevent *ev, int msg_type)
1566{
1567 struct iscsi_endpoint *ep;
1568 struct sockaddr *dst_addr;
1569 struct Scsi_Host *shost = NULL;
1570 int non_blocking, err = 0;
1571
1572 if (!transport->ep_connect)
1573 return -EINVAL;
1574
1575 if (msg_type == ISCSI_UEVENT_TRANSPORT_EP_CONNECT_THROUGH_HOST) {
1576 shost = scsi_host_lookup(ev->u.ep_connect_through_host.host_no);
1577 if (!shost) {
1578 printk(KERN_ERR "ep connect failed. Could not find "
1579 "host no %u\n",
1580 ev->u.ep_connect_through_host.host_no);
1581 return -ENODEV;
1582 }
1583 non_blocking = ev->u.ep_connect_through_host.non_blocking;
1584 } else
1585 non_blocking = ev->u.ep_connect.non_blocking;
1586
1587 dst_addr = (struct sockaddr *)((char*)ev + sizeof(*ev));
1588 ep = transport->ep_connect(shost, dst_addr, non_blocking);
1589 if (IS_ERR(ep)) {
1590 err = PTR_ERR(ep);
1591 goto release_host;
1592 }
1593
1594 ev->r.ep_connect_ret.handle = ep->id;
1595release_host:
1596 if (shost)
1597 scsi_host_put(shost);
1598 return err;
1599}
1600
Mike Christie22a39fb2011-02-16 15:04:33 -06001601static int iscsi_if_ep_disconnect(struct iscsi_transport *transport,
1602 u64 ep_handle)
1603{
1604 struct iscsi_cls_conn *conn;
1605 struct iscsi_endpoint *ep;
1606
1607 if (!transport->ep_disconnect)
1608 return -EINVAL;
1609
1610 ep = iscsi_lookup_endpoint(ep_handle);
1611 if (!ep)
1612 return -EINVAL;
1613 conn = ep->conn;
1614 if (conn) {
1615 mutex_lock(&conn->ep_mutex);
1616 conn->ep = NULL;
1617 mutex_unlock(&conn->ep_mutex);
1618 }
1619
1620 transport->ep_disconnect(ep);
1621 return 0;
1622}
1623
Mike Christie7b8631b2006-01-13 18:05:50 -06001624static int
Or Gerlitz264faaa2006-05-02 19:46:36 -05001625iscsi_if_transport_ep(struct iscsi_transport *transport,
1626 struct iscsi_uevent *ev, int msg_type)
1627{
Mike Christied82ff9be2008-05-21 15:54:13 -05001628 struct iscsi_endpoint *ep;
Or Gerlitz264faaa2006-05-02 19:46:36 -05001629 int rc = 0;
1630
1631 switch (msg_type) {
Mike Christie10eb0f02009-05-13 17:57:38 -05001632 case ISCSI_UEVENT_TRANSPORT_EP_CONNECT_THROUGH_HOST:
Or Gerlitz264faaa2006-05-02 19:46:36 -05001633 case ISCSI_UEVENT_TRANSPORT_EP_CONNECT:
Mike Christie10eb0f02009-05-13 17:57:38 -05001634 rc = iscsi_if_ep_connect(transport, ev, msg_type);
Or Gerlitz264faaa2006-05-02 19:46:36 -05001635 break;
1636 case ISCSI_UEVENT_TRANSPORT_EP_POLL:
1637 if (!transport->ep_poll)
1638 return -EINVAL;
1639
Mike Christied82ff9be2008-05-21 15:54:13 -05001640 ep = iscsi_lookup_endpoint(ev->u.ep_poll.ep_handle);
1641 if (!ep)
1642 return -EINVAL;
1643
1644 ev->r.retcode = transport->ep_poll(ep,
Or Gerlitz264faaa2006-05-02 19:46:36 -05001645 ev->u.ep_poll.timeout_ms);
1646 break;
1647 case ISCSI_UEVENT_TRANSPORT_EP_DISCONNECT:
Mike Christie22a39fb2011-02-16 15:04:33 -06001648 rc = iscsi_if_ep_disconnect(transport,
1649 ev->u.ep_disconnect.ep_handle);
Or Gerlitz264faaa2006-05-02 19:46:36 -05001650 break;
1651 }
1652 return rc;
1653}
1654
1655static int
Mike Christie01cb2252006-06-28 12:00:22 -05001656iscsi_tgt_dscvr(struct iscsi_transport *transport,
1657 struct iscsi_uevent *ev)
1658{
Mike Christie2174a042007-05-30 12:57:10 -05001659 struct Scsi_Host *shost;
Mike Christie01cb2252006-06-28 12:00:22 -05001660 struct sockaddr *dst_addr;
Mike Christie2174a042007-05-30 12:57:10 -05001661 int err;
Mike Christie01cb2252006-06-28 12:00:22 -05001662
1663 if (!transport->tgt_dscvr)
1664 return -EINVAL;
1665
Mike Christie2174a042007-05-30 12:57:10 -05001666 shost = scsi_host_lookup(ev->u.tgt_dscvr.host_no);
James Smart315cb0a2008-08-07 20:49:30 -04001667 if (!shost) {
Mike Christie2174a042007-05-30 12:57:10 -05001668 printk(KERN_ERR "target discovery could not find host no %u\n",
1669 ev->u.tgt_dscvr.host_no);
1670 return -ENODEV;
1671 }
1672
1673
Mike Christie01cb2252006-06-28 12:00:22 -05001674 dst_addr = (struct sockaddr *)((char*)ev + sizeof(*ev));
Mike Christie2174a042007-05-30 12:57:10 -05001675 err = transport->tgt_dscvr(shost, ev->u.tgt_dscvr.type,
1676 ev->u.tgt_dscvr.enable, dst_addr);
1677 scsi_host_put(shost);
1678 return err;
Mike Christie01cb2252006-06-28 12:00:22 -05001679}
1680
1681static int
Mike Christie1d9bf132007-05-30 12:57:11 -05001682iscsi_set_host_param(struct iscsi_transport *transport,
1683 struct iscsi_uevent *ev)
1684{
1685 char *data = (char*)ev + sizeof(*ev);
1686 struct Scsi_Host *shost;
1687 int err;
1688
1689 if (!transport->set_host_param)
1690 return -ENOSYS;
1691
1692 shost = scsi_host_lookup(ev->u.set_host_param.host_no);
James Smart315cb0a2008-08-07 20:49:30 -04001693 if (!shost) {
Mike Christie1d9bf132007-05-30 12:57:11 -05001694 printk(KERN_ERR "set_host_param could not find host no %u\n",
1695 ev->u.set_host_param.host_no);
1696 return -ENODEV;
1697 }
1698
1699 err = transport->set_host_param(shost, ev->u.set_host_param.param,
1700 data, ev->u.set_host_param.len);
1701 scsi_host_put(shost);
1702 return err;
1703}
1704
1705static int
Michael Chan43514772009-06-08 18:14:41 -07001706iscsi_set_path(struct iscsi_transport *transport, struct iscsi_uevent *ev)
1707{
1708 struct Scsi_Host *shost;
1709 struct iscsi_path *params;
1710 int err;
1711
1712 if (!transport->set_path)
1713 return -ENOSYS;
1714
1715 shost = scsi_host_lookup(ev->u.set_path.host_no);
1716 if (!shost) {
1717 printk(KERN_ERR "set path could not find host no %u\n",
1718 ev->u.set_path.host_no);
1719 return -ENODEV;
1720 }
1721
1722 params = (struct iscsi_path *)((char *)ev + sizeof(*ev));
1723 err = transport->set_path(shost, params);
1724
1725 scsi_host_put(shost);
1726 return err;
1727}
1728
1729static int
Mike Christie56c155b2011-07-25 13:48:37 -05001730iscsi_set_iface_params(struct iscsi_transport *transport,
1731 struct iscsi_uevent *ev)
1732{
1733 char *data = (char *)ev + sizeof(*ev);
1734 struct Scsi_Host *shost;
1735 int err;
1736
1737 if (!transport->set_iface_param)
1738 return -ENOSYS;
1739
1740 shost = scsi_host_lookup(ev->u.set_iface_params.host_no);
1741 if (!shost) {
1742 printk(KERN_ERR "set_iface_params could not find host no %u\n",
1743 ev->u.set_iface_params.host_no);
1744 return -ENODEV;
1745 }
1746
1747 err = transport->set_iface_param(shost, data,
1748 ev->u.set_iface_params.count);
1749 scsi_host_put(shost);
1750 return err;
1751}
1752
1753static int
Michael Chan43514772009-06-08 18:14:41 -07001754iscsi_if_recv_msg(struct sk_buff *skb, struct nlmsghdr *nlh, uint32_t *group)
Alex Aizman0896b752005-08-04 19:33:07 -07001755{
1756 int err = 0;
1757 struct iscsi_uevent *ev = NLMSG_DATA(nlh);
1758 struct iscsi_transport *transport = NULL;
1759 struct iscsi_internal *priv;
Mike Christie7b7232f2006-02-01 21:06:49 -06001760 struct iscsi_cls_session *session;
1761 struct iscsi_cls_conn *conn;
Mike Christied82ff9be2008-05-21 15:54:13 -05001762 struct iscsi_endpoint *ep = NULL;
Alex Aizman0896b752005-08-04 19:33:07 -07001763
Michael Chan43514772009-06-08 18:14:41 -07001764 if (nlh->nlmsg_type == ISCSI_UEVENT_PATH_UPDATE)
1765 *group = ISCSI_NL_GRP_UIP;
1766 else
1767 *group = ISCSI_NL_GRP_ISCSID;
1768
Alex Aizman0896b752005-08-04 19:33:07 -07001769 priv = iscsi_if_transport_lookup(iscsi_ptr(ev->transport_handle));
1770 if (!priv)
1771 return -EINVAL;
1772 transport = priv->iscsi_transport;
1773
Mike Christie7b7232f2006-02-01 21:06:49 -06001774 if (!try_module_get(transport->owner))
1775 return -EINVAL;
1776
Alex Aizman0896b752005-08-04 19:33:07 -07001777 switch (nlh->nlmsg_type) {
1778 case ISCSI_UEVENT_CREATE_SESSION:
Mike Christied82ff9be2008-05-21 15:54:13 -05001779 err = iscsi_if_create_session(priv, ep, ev,
Mike Christie40753ca2008-05-21 15:53:56 -05001780 ev->u.c_session.initial_cmdsn,
1781 ev->u.c_session.cmds_max,
1782 ev->u.c_session.queue_depth);
1783 break;
1784 case ISCSI_UEVENT_CREATE_BOUND_SESSION:
Mike Christied82ff9be2008-05-21 15:54:13 -05001785 ep = iscsi_lookup_endpoint(ev->u.c_bound_session.ep_handle);
Mike Christiec95fddc2008-06-16 10:11:32 -05001786 if (!ep) {
1787 err = -EINVAL;
1788 break;
1789 }
Mike Christied82ff9be2008-05-21 15:54:13 -05001790
1791 err = iscsi_if_create_session(priv, ep, ev,
Mike Christie40753ca2008-05-21 15:53:56 -05001792 ev->u.c_bound_session.initial_cmdsn,
1793 ev->u.c_bound_session.cmds_max,
1794 ev->u.c_bound_session.queue_depth);
Alex Aizman0896b752005-08-04 19:33:07 -07001795 break;
1796 case ISCSI_UEVENT_DESTROY_SESSION:
Mike Christieb5c7a122006-04-06 21:13:33 -05001797 session = iscsi_session_lookup(ev->u.d_session.sid);
Mike Christie26974782007-12-13 12:43:29 -06001798 if (session)
Mike Christie7b7232f2006-02-01 21:06:49 -06001799 transport->destroy_session(session);
Mike Christie26974782007-12-13 12:43:29 -06001800 else
1801 err = -EINVAL;
1802 break;
1803 case ISCSI_UEVENT_UNBIND_SESSION:
1804 session = iscsi_session_lookup(ev->u.d_session.sid);
1805 if (session)
Mike Christie06d25af2009-03-05 14:46:02 -06001806 scsi_queue_work(iscsi_session_to_shost(session),
1807 &session->unbind_work);
Mike Christie26974782007-12-13 12:43:29 -06001808 else
Mike Christie7b7232f2006-02-01 21:06:49 -06001809 err = -EINVAL;
Alex Aizman0896b752005-08-04 19:33:07 -07001810 break;
1811 case ISCSI_UEVENT_CREATE_CONN:
1812 err = iscsi_if_create_conn(transport, ev);
1813 break;
1814 case ISCSI_UEVENT_DESTROY_CONN:
1815 err = iscsi_if_destroy_conn(transport, ev);
1816 break;
1817 case ISCSI_UEVENT_BIND_CONN:
Mike Christieb5c7a122006-04-06 21:13:33 -05001818 session = iscsi_session_lookup(ev->u.b_conn.sid);
1819 conn = iscsi_conn_lookup(ev->u.b_conn.sid, ev->u.b_conn.cid);
Mike Christie7b7232f2006-02-01 21:06:49 -06001820
Mike Christie22a39fb2011-02-16 15:04:33 -06001821 if (conn && conn->ep)
1822 iscsi_if_ep_disconnect(transport, conn->ep->id);
1823
1824 if (!session || !conn) {
Mike Christie7b7232f2006-02-01 21:06:49 -06001825 err = -EINVAL;
Mike Christie22a39fb2011-02-16 15:04:33 -06001826 break;
1827 }
1828
1829 ev->r.retcode = transport->bind_conn(session, conn,
1830 ev->u.b_conn.transport_eph,
1831 ev->u.b_conn.is_leading);
1832 if (ev->r.retcode || !transport->ep_connect)
1833 break;
1834
1835 ep = iscsi_lookup_endpoint(ev->u.b_conn.transport_eph);
1836 if (ep) {
1837 ep->conn = conn;
1838
1839 mutex_lock(&conn->ep_mutex);
1840 conn->ep = ep;
1841 mutex_unlock(&conn->ep_mutex);
1842 } else
1843 iscsi_cls_conn_printk(KERN_ERR, conn,
1844 "Could not set ep conn "
1845 "binding\n");
Alex Aizman0896b752005-08-04 19:33:07 -07001846 break;
1847 case ISCSI_UEVENT_SET_PARAM:
Mike Christiefd7255f2006-04-06 21:13:36 -05001848 err = iscsi_set_param(transport, ev);
Alex Aizman0896b752005-08-04 19:33:07 -07001849 break;
1850 case ISCSI_UEVENT_START_CONN:
Mike Christieb5c7a122006-04-06 21:13:33 -05001851 conn = iscsi_conn_lookup(ev->u.start_conn.sid, ev->u.start_conn.cid);
Mike Christie7b7232f2006-02-01 21:06:49 -06001852 if (conn)
1853 ev->r.retcode = transport->start_conn(conn);
1854 else
1855 err = -EINVAL;
Alex Aizman0896b752005-08-04 19:33:07 -07001856 break;
1857 case ISCSI_UEVENT_STOP_CONN:
Mike Christieb5c7a122006-04-06 21:13:33 -05001858 conn = iscsi_conn_lookup(ev->u.stop_conn.sid, ev->u.stop_conn.cid);
Mike Christie7b7232f2006-02-01 21:06:49 -06001859 if (conn)
1860 transport->stop_conn(conn, ev->u.stop_conn.flag);
1861 else
1862 err = -EINVAL;
Alex Aizman0896b752005-08-04 19:33:07 -07001863 break;
1864 case ISCSI_UEVENT_SEND_PDU:
Mike Christieb5c7a122006-04-06 21:13:33 -05001865 conn = iscsi_conn_lookup(ev->u.send_pdu.sid, ev->u.send_pdu.cid);
Mike Christie7b7232f2006-02-01 21:06:49 -06001866 if (conn)
1867 ev->r.retcode = transport->send_pdu(conn,
1868 (struct iscsi_hdr*)((char*)ev + sizeof(*ev)),
1869 (char*)ev + sizeof(*ev) + ev->u.send_pdu.hdr_size,
1870 ev->u.send_pdu.data_size);
1871 else
1872 err = -EINVAL;
Alex Aizman0896b752005-08-04 19:33:07 -07001873 break;
1874 case ISCSI_UEVENT_GET_STATS:
Mike Christie5b940ad2006-02-01 21:06:56 -06001875 err = iscsi_if_get_stats(transport, nlh);
Alex Aizman0896b752005-08-04 19:33:07 -07001876 break;
Or Gerlitz264faaa2006-05-02 19:46:36 -05001877 case ISCSI_UEVENT_TRANSPORT_EP_CONNECT:
1878 case ISCSI_UEVENT_TRANSPORT_EP_POLL:
1879 case ISCSI_UEVENT_TRANSPORT_EP_DISCONNECT:
Mike Christie10eb0f02009-05-13 17:57:38 -05001880 case ISCSI_UEVENT_TRANSPORT_EP_CONNECT_THROUGH_HOST:
Or Gerlitz264faaa2006-05-02 19:46:36 -05001881 err = iscsi_if_transport_ep(transport, ev, nlh->nlmsg_type);
1882 break;
Mike Christie01cb2252006-06-28 12:00:22 -05001883 case ISCSI_UEVENT_TGT_DSCVR:
1884 err = iscsi_tgt_dscvr(transport, ev);
1885 break;
Mike Christie1d9bf132007-05-30 12:57:11 -05001886 case ISCSI_UEVENT_SET_HOST_PARAM:
1887 err = iscsi_set_host_param(transport, ev);
1888 break;
Michael Chan43514772009-06-08 18:14:41 -07001889 case ISCSI_UEVENT_PATH_UPDATE:
1890 err = iscsi_set_path(transport, ev);
1891 break;
Mike Christie56c155b2011-07-25 13:48:37 -05001892 case ISCSI_UEVENT_SET_IFACE_PARAMS:
1893 err = iscsi_set_iface_params(transport, ev);
1894 break;
Alex Aizman0896b752005-08-04 19:33:07 -07001895 default:
Mike Christie1d9bf132007-05-30 12:57:11 -05001896 err = -ENOSYS;
Alex Aizman0896b752005-08-04 19:33:07 -07001897 break;
1898 }
1899
Mike Christie7b7232f2006-02-01 21:06:49 -06001900 module_put(transport->owner);
Alex Aizman0896b752005-08-04 19:33:07 -07001901 return err;
1902}
1903
Mike Christieb5c7a122006-04-06 21:13:33 -05001904/*
Denis V. Lunevcd40b7d2007-10-10 21:15:29 -07001905 * Get message from skb. Each message is processed by iscsi_if_recv_msg.
1906 * Malformed skbs with wrong lengths or invalid creds are not processed.
Mike Christieb5c7a122006-04-06 21:13:33 -05001907 */
Alex Aizman0896b752005-08-04 19:33:07 -07001908static void
Denis V. Lunevcd40b7d2007-10-10 21:15:29 -07001909iscsi_if_rx(struct sk_buff *skb)
Alex Aizman0896b752005-08-04 19:33:07 -07001910{
Arjan van de Ven0b950672006-01-11 13:16:10 +01001911 mutex_lock(&rx_queue_mutex);
Denis V. Lunevcd40b7d2007-10-10 21:15:29 -07001912 while (skb->len >= NLMSG_SPACE(0)) {
1913 int err;
1914 uint32_t rlen;
1915 struct nlmsghdr *nlh;
1916 struct iscsi_uevent *ev;
Michael Chan43514772009-06-08 18:14:41 -07001917 uint32_t group;
Denis V. Lunevcd40b7d2007-10-10 21:15:29 -07001918
1919 nlh = nlmsg_hdr(skb);
1920 if (nlh->nlmsg_len < sizeof(*nlh) ||
1921 skb->len < nlh->nlmsg_len) {
1922 break;
Mike Christieee7f8e42006-02-01 21:07:01 -06001923 }
Mike Christieee7f8e42006-02-01 21:07:01 -06001924
Denis V. Lunevcd40b7d2007-10-10 21:15:29 -07001925 ev = NLMSG_DATA(nlh);
1926 rlen = NLMSG_ALIGN(nlh->nlmsg_len);
1927 if (rlen > skb->len)
1928 rlen = skb->len;
Alex Aizman0896b752005-08-04 19:33:07 -07001929
Michael Chan43514772009-06-08 18:14:41 -07001930 err = iscsi_if_recv_msg(skb, nlh, &group);
Denis V. Lunevcd40b7d2007-10-10 21:15:29 -07001931 if (err) {
1932 ev->type = ISCSI_KEVENT_IF_ERROR;
1933 ev->iferror = err;
1934 }
1935 do {
1936 /*
1937 * special case for GET_STATS:
1938 * on success - sending reply and stats from
1939 * inside of if_recv_msg(),
1940 * on error - fall through.
1941 */
1942 if (ev->type == ISCSI_UEVENT_GET_STATS && !err)
Alex Aizman0896b752005-08-04 19:33:07 -07001943 break;
Michael Chan43514772009-06-08 18:14:41 -07001944 err = iscsi_if_send_reply(group, nlh->nlmsg_seq,
Denis V. Lunevcd40b7d2007-10-10 21:15:29 -07001945 nlh->nlmsg_type, 0, 0, ev, sizeof(*ev));
1946 } while (err < 0 && err != -ECONNREFUSED);
1947 skb_pull(skb, rlen);
Alex Aizman0896b752005-08-04 19:33:07 -07001948 }
Arjan van de Ven0b950672006-01-11 13:16:10 +01001949 mutex_unlock(&rx_queue_mutex);
Alex Aizman0896b752005-08-04 19:33:07 -07001950}
1951
Mike Christiefd7255f2006-04-06 21:13:36 -05001952#define ISCSI_CLASS_ATTR(_prefix,_name,_mode,_show,_store) \
Tony Jonesee959b02008-02-22 00:13:36 +01001953struct device_attribute dev_attr_##_prefix##_##_name = \
Mike Christiefd7255f2006-04-06 21:13:36 -05001954 __ATTR(_name,_mode,_show,_store)
1955
Alex Aizman0896b752005-08-04 19:33:07 -07001956/*
1957 * iSCSI connection attrs
1958 */
Mike Christiea54a52c2006-06-28 12:00:23 -05001959#define iscsi_conn_attr_show(param) \
Linus Torvalds1da177e2005-04-16 15:20:36 -07001960static ssize_t \
Tony Jonesee959b02008-02-22 00:13:36 +01001961show_conn_param_##param(struct device *dev, \
1962 struct device_attribute *attr, char *buf) \
Mike Christiefd7255f2006-04-06 21:13:36 -05001963{ \
Tony Jonesee959b02008-02-22 00:13:36 +01001964 struct iscsi_cls_conn *conn = iscsi_dev_to_conn(dev->parent); \
Mike Christiefd7255f2006-04-06 21:13:36 -05001965 struct iscsi_transport *t = conn->transport; \
Mike Christiea54a52c2006-06-28 12:00:23 -05001966 return t->get_conn_param(conn, param, buf); \
Mike Christiefd7255f2006-04-06 21:13:36 -05001967}
1968
Mike Christiea54a52c2006-06-28 12:00:23 -05001969#define iscsi_conn_attr(field, param) \
1970 iscsi_conn_attr_show(param) \
1971static ISCSI_CLASS_ATTR(conn, field, S_IRUGO, show_conn_param_##param, \
Mike Christiefd7255f2006-04-06 21:13:36 -05001972 NULL);
1973
Mike Christiea54a52c2006-06-28 12:00:23 -05001974iscsi_conn_attr(max_recv_dlength, ISCSI_PARAM_MAX_RECV_DLENGTH);
1975iscsi_conn_attr(max_xmit_dlength, ISCSI_PARAM_MAX_XMIT_DLENGTH);
1976iscsi_conn_attr(header_digest, ISCSI_PARAM_HDRDGST_EN);
1977iscsi_conn_attr(data_digest, ISCSI_PARAM_DATADGST_EN);
1978iscsi_conn_attr(ifmarker, ISCSI_PARAM_IFMARKER_EN);
1979iscsi_conn_attr(ofmarker, ISCSI_PARAM_OFMARKER_EN);
1980iscsi_conn_attr(persistent_port, ISCSI_PARAM_PERSISTENT_PORT);
Mike Christiea54a52c2006-06-28 12:00:23 -05001981iscsi_conn_attr(exp_statsn, ISCSI_PARAM_EXP_STATSN);
1982iscsi_conn_attr(persistent_address, ISCSI_PARAM_PERSISTENT_ADDRESS);
Mike Christief6d51802007-12-13 12:43:30 -06001983iscsi_conn_attr(ping_tmo, ISCSI_PARAM_PING_TMO);
1984iscsi_conn_attr(recv_tmo, ISCSI_PARAM_RECV_TMO);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001985
Mike Christie289324b2011-02-16 15:04:37 -06001986#define iscsi_conn_ep_attr_show(param) \
1987static ssize_t show_conn_ep_param_##param(struct device *dev, \
1988 struct device_attribute *attr,\
1989 char *buf) \
1990{ \
1991 struct iscsi_cls_conn *conn = iscsi_dev_to_conn(dev->parent); \
1992 struct iscsi_transport *t = conn->transport; \
1993 struct iscsi_endpoint *ep; \
1994 ssize_t rc; \
1995 \
1996 /* \
1997 * Need to make sure ep_disconnect does not free the LLD's \
1998 * interconnect resources while we are trying to read them. \
1999 */ \
2000 mutex_lock(&conn->ep_mutex); \
2001 ep = conn->ep; \
2002 if (!ep && t->ep_connect) { \
2003 mutex_unlock(&conn->ep_mutex); \
2004 return -ENOTCONN; \
2005 } \
2006 \
2007 if (ep) \
2008 rc = t->get_ep_param(ep, param, buf); \
2009 else \
2010 rc = t->get_conn_param(conn, param, buf); \
2011 mutex_unlock(&conn->ep_mutex); \
2012 return rc; \
2013}
2014
2015#define iscsi_conn_ep_attr(field, param) \
2016 iscsi_conn_ep_attr_show(param) \
2017static ISCSI_CLASS_ATTR(conn, field, S_IRUGO, \
2018 show_conn_ep_param_##param, NULL);
2019
2020iscsi_conn_ep_attr(address, ISCSI_PARAM_CONN_ADDRESS);
2021iscsi_conn_ep_attr(port, ISCSI_PARAM_CONN_PORT);
2022
Mike Christie3128c6c2011-07-25 13:48:42 -05002023static struct attribute *iscsi_conn_attrs[] = {
2024 &dev_attr_conn_max_recv_dlength.attr,
2025 &dev_attr_conn_max_xmit_dlength.attr,
2026 &dev_attr_conn_header_digest.attr,
2027 &dev_attr_conn_data_digest.attr,
2028 &dev_attr_conn_ifmarker.attr,
2029 &dev_attr_conn_ofmarker.attr,
2030 &dev_attr_conn_address.attr,
2031 &dev_attr_conn_port.attr,
2032 &dev_attr_conn_exp_statsn.attr,
2033 &dev_attr_conn_persistent_address.attr,
2034 &dev_attr_conn_persistent_port.attr,
2035 &dev_attr_conn_ping_tmo.attr,
2036 &dev_attr_conn_recv_tmo.attr,
2037 NULL,
2038};
2039
2040static mode_t iscsi_conn_attr_is_visible(struct kobject *kobj,
2041 struct attribute *attr, int i)
2042{
2043 struct device *cdev = container_of(kobj, struct device, kobj);
2044 struct iscsi_cls_conn *conn = transport_class_to_conn(cdev);
2045 struct iscsi_transport *t = conn->transport;
2046 int param;
2047
2048 if (attr == &dev_attr_conn_max_recv_dlength.attr)
2049 param = ISCSI_PARAM_MAX_RECV_DLENGTH;
2050 else if (attr == &dev_attr_conn_max_xmit_dlength.attr)
2051 param = ISCSI_PARAM_MAX_XMIT_DLENGTH;
2052 else if (attr == &dev_attr_conn_header_digest.attr)
2053 param = ISCSI_PARAM_HDRDGST_EN;
2054 else if (attr == &dev_attr_conn_data_digest.attr)
2055 param = ISCSI_PARAM_DATADGST_EN;
2056 else if (attr == &dev_attr_conn_ifmarker.attr)
2057 param = ISCSI_PARAM_IFMARKER_EN;
2058 else if (attr == &dev_attr_conn_ofmarker.attr)
2059 param = ISCSI_PARAM_OFMARKER_EN;
2060 else if (attr == &dev_attr_conn_address.attr)
2061 param = ISCSI_PARAM_CONN_ADDRESS;
2062 else if (attr == &dev_attr_conn_port.attr)
2063 param = ISCSI_PARAM_CONN_PORT;
2064 else if (attr == &dev_attr_conn_exp_statsn.attr)
2065 param = ISCSI_PARAM_EXP_STATSN;
2066 else if (attr == &dev_attr_conn_persistent_address.attr)
2067 param = ISCSI_PARAM_PERSISTENT_ADDRESS;
2068 else if (attr == &dev_attr_conn_persistent_port.attr)
2069 param = ISCSI_PARAM_PERSISTENT_PORT;
2070 else if (attr == &dev_attr_conn_ping_tmo.attr)
2071 param = ISCSI_PARAM_PING_TMO;
2072 else if (attr == &dev_attr_conn_recv_tmo.attr)
2073 param = ISCSI_PARAM_RECV_TMO;
2074 else {
2075 WARN_ONCE(1, "Invalid conn attr");
2076 return 0;
2077 }
2078
2079 return t->attr_is_visible(ISCSI_PARAM, param);
2080}
2081
2082static struct attribute_group iscsi_conn_group = {
2083 .attrs = iscsi_conn_attrs,
2084 .is_visible = iscsi_conn_attr_is_visible,
2085};
2086
Linus Torvalds1da177e2005-04-16 15:20:36 -07002087/*
Alex Aizman0896b752005-08-04 19:33:07 -07002088 * iSCSI session attrs
Linus Torvalds1da177e2005-04-16 15:20:36 -07002089 */
Mike Christieb2c64162007-05-30 12:57:16 -05002090#define iscsi_session_attr_show(param, perm) \
Linus Torvalds1da177e2005-04-16 15:20:36 -07002091static ssize_t \
Tony Jonesee959b02008-02-22 00:13:36 +01002092show_session_param_##param(struct device *dev, \
2093 struct device_attribute *attr, char *buf) \
Mike Christiefd7255f2006-04-06 21:13:36 -05002094{ \
Tony Jonesee959b02008-02-22 00:13:36 +01002095 struct iscsi_cls_session *session = \
2096 iscsi_dev_to_session(dev->parent); \
Mike Christiefd7255f2006-04-06 21:13:36 -05002097 struct iscsi_transport *t = session->transport; \
Mike Christieb2c64162007-05-30 12:57:16 -05002098 \
2099 if (perm && !capable(CAP_SYS_ADMIN)) \
2100 return -EACCES; \
Mike Christiea54a52c2006-06-28 12:00:23 -05002101 return t->get_session_param(session, param, buf); \
Mike Christiefd7255f2006-04-06 21:13:36 -05002102}
Linus Torvalds1da177e2005-04-16 15:20:36 -07002103
Mike Christieb2c64162007-05-30 12:57:16 -05002104#define iscsi_session_attr(field, param, perm) \
2105 iscsi_session_attr_show(param, perm) \
Mike Christiea54a52c2006-06-28 12:00:23 -05002106static ISCSI_CLASS_ATTR(sess, field, S_IRUGO, show_session_param_##param, \
Mike Christiefd7255f2006-04-06 21:13:36 -05002107 NULL);
Mike Christieb2c64162007-05-30 12:57:16 -05002108iscsi_session_attr(targetname, ISCSI_PARAM_TARGET_NAME, 0);
2109iscsi_session_attr(initial_r2t, ISCSI_PARAM_INITIAL_R2T_EN, 0);
2110iscsi_session_attr(max_outstanding_r2t, ISCSI_PARAM_MAX_R2T, 0);
2111iscsi_session_attr(immediate_data, ISCSI_PARAM_IMM_DATA_EN, 0);
2112iscsi_session_attr(first_burst_len, ISCSI_PARAM_FIRST_BURST, 0);
2113iscsi_session_attr(max_burst_len, ISCSI_PARAM_MAX_BURST, 0);
2114iscsi_session_attr(data_pdu_in_order, ISCSI_PARAM_PDU_INORDER_EN, 0);
2115iscsi_session_attr(data_seq_in_order, ISCSI_PARAM_DATASEQ_INORDER_EN, 0);
2116iscsi_session_attr(erl, ISCSI_PARAM_ERL, 0);
2117iscsi_session_attr(tpgt, ISCSI_PARAM_TPGT, 0);
2118iscsi_session_attr(username, ISCSI_PARAM_USERNAME, 1);
2119iscsi_session_attr(username_in, ISCSI_PARAM_USERNAME_IN, 1);
2120iscsi_session_attr(password, ISCSI_PARAM_PASSWORD, 1);
2121iscsi_session_attr(password_in, ISCSI_PARAM_PASSWORD_IN, 1);
Mike Christie4cd49ea2007-12-13 12:43:38 -06002122iscsi_session_attr(fast_abort, ISCSI_PARAM_FAST_ABORT, 0);
2123iscsi_session_attr(abort_tmo, ISCSI_PARAM_ABORT_TMO, 0);
2124iscsi_session_attr(lu_reset_tmo, ISCSI_PARAM_LU_RESET_TMO, 0);
Mike Christie3fe5ae82009-11-11 16:34:33 -06002125iscsi_session_attr(tgt_reset_tmo, ISCSI_PARAM_TGT_RESET_TMO, 0);
Mike Christie88dfd342008-05-21 15:54:16 -05002126iscsi_session_attr(ifacename, ISCSI_PARAM_IFACE_NAME, 0);
Vikas Chaudhary3b2bef12010-07-10 14:51:30 +05302127iscsi_session_attr(initiatorname, ISCSI_PARAM_INITIATOR_NAME, 0);
2128iscsi_session_attr(targetalias, ISCSI_PARAM_TARGET_ALIAS, 0);
Mike Christiefd7255f2006-04-06 21:13:36 -05002129
Mike Christie6eabafb2008-01-31 13:36:43 -06002130static ssize_t
Tony Jonesee959b02008-02-22 00:13:36 +01002131show_priv_session_state(struct device *dev, struct device_attribute *attr,
2132 char *buf)
Mike Christie6eabafb2008-01-31 13:36:43 -06002133{
Tony Jonesee959b02008-02-22 00:13:36 +01002134 struct iscsi_cls_session *session = iscsi_dev_to_session(dev->parent);
Mike Christie6eabafb2008-01-31 13:36:43 -06002135 return sprintf(buf, "%s\n", iscsi_session_state_name(session->state));
2136}
2137static ISCSI_CLASS_ATTR(priv_sess, state, S_IRUGO, show_priv_session_state,
2138 NULL);
2139
Mike Christiefd7255f2006-04-06 21:13:36 -05002140#define iscsi_priv_session_attr_show(field, format) \
2141static ssize_t \
Tony Jonesee959b02008-02-22 00:13:36 +01002142show_priv_session_##field(struct device *dev, \
2143 struct device_attribute *attr, char *buf) \
Mike Christiefd7255f2006-04-06 21:13:36 -05002144{ \
Tony Jonesee959b02008-02-22 00:13:36 +01002145 struct iscsi_cls_session *session = \
2146 iscsi_dev_to_session(dev->parent); \
Vikas Chaudharyfe4f0bd2010-07-22 16:57:43 +05302147 if (session->field == -1) \
2148 return sprintf(buf, "off\n"); \
Mike Christiefd7255f2006-04-06 21:13:36 -05002149 return sprintf(buf, format"\n", session->field); \
2150}
2151
Vikas Chaudharyfe4f0bd2010-07-22 16:57:43 +05302152#define iscsi_priv_session_attr_store(field) \
2153static ssize_t \
2154store_priv_session_##field(struct device *dev, \
2155 struct device_attribute *attr, \
2156 const char *buf, size_t count) \
2157{ \
2158 int val; \
2159 char *cp; \
2160 struct iscsi_cls_session *session = \
2161 iscsi_dev_to_session(dev->parent); \
2162 if ((session->state == ISCSI_SESSION_FREE) || \
2163 (session->state == ISCSI_SESSION_FAILED)) \
2164 return -EBUSY; \
2165 if (strncmp(buf, "off", 3) == 0) \
2166 session->field = -1; \
2167 else { \
2168 val = simple_strtoul(buf, &cp, 0); \
2169 if (*cp != '\0' && *cp != '\n') \
2170 return -EINVAL; \
2171 session->field = val; \
2172 } \
2173 return count; \
2174}
2175
2176#define iscsi_priv_session_rw_attr(field, format) \
Mike Christiefd7255f2006-04-06 21:13:36 -05002177 iscsi_priv_session_attr_show(field, format) \
Vikas Chaudharyfe4f0bd2010-07-22 16:57:43 +05302178 iscsi_priv_session_attr_store(field) \
Vasiliy Kulikov523f3c82011-02-04 15:24:14 +03002179static ISCSI_CLASS_ATTR(priv_sess, field, S_IRUGO | S_IWUSR, \
Vikas Chaudharyfe4f0bd2010-07-22 16:57:43 +05302180 show_priv_session_##field, \
2181 store_priv_session_##field)
2182iscsi_priv_session_rw_attr(recovery_tmo, "%d");
Mike Christiefd7255f2006-04-06 21:13:36 -05002183
Mike Christie1d063c12011-07-25 13:48:43 -05002184static struct attribute *iscsi_session_attrs[] = {
2185 &dev_attr_sess_initial_r2t.attr,
2186 &dev_attr_sess_max_outstanding_r2t.attr,
2187 &dev_attr_sess_immediate_data.attr,
2188 &dev_attr_sess_first_burst_len.attr,
2189 &dev_attr_sess_max_burst_len.attr,
2190 &dev_attr_sess_data_pdu_in_order.attr,
2191 &dev_attr_sess_data_seq_in_order.attr,
2192 &dev_attr_sess_erl.attr,
2193 &dev_attr_sess_targetname.attr,
2194 &dev_attr_sess_tpgt.attr,
2195 &dev_attr_sess_password.attr,
2196 &dev_attr_sess_password_in.attr,
2197 &dev_attr_sess_username.attr,
2198 &dev_attr_sess_username_in.attr,
2199 &dev_attr_sess_fast_abort.attr,
2200 &dev_attr_sess_abort_tmo.attr,
2201 &dev_attr_sess_lu_reset_tmo.attr,
2202 &dev_attr_sess_tgt_reset_tmo.attr,
2203 &dev_attr_sess_ifacename.attr,
2204 &dev_attr_sess_initiatorname.attr,
2205 &dev_attr_sess_targetalias.attr,
2206 &dev_attr_priv_sess_recovery_tmo.attr,
2207 &dev_attr_priv_sess_state.attr,
2208 NULL,
2209};
2210
2211static mode_t iscsi_session_attr_is_visible(struct kobject *kobj,
2212 struct attribute *attr, int i)
2213{
2214 struct device *cdev = container_of(kobj, struct device, kobj);
2215 struct iscsi_cls_session *session = transport_class_to_session(cdev);
2216 struct iscsi_transport *t = session->transport;
2217 int param;
2218
2219 if (attr == &dev_attr_sess_initial_r2t.attr)
2220 param = ISCSI_PARAM_INITIAL_R2T_EN;
2221 else if (attr == &dev_attr_sess_max_outstanding_r2t.attr)
2222 param = ISCSI_PARAM_MAX_R2T;
2223 else if (attr == &dev_attr_sess_immediate_data.attr)
2224 param = ISCSI_PARAM_IMM_DATA_EN;
2225 else if (attr == &dev_attr_sess_first_burst_len.attr)
2226 param = ISCSI_PARAM_FIRST_BURST;
2227 else if (attr == &dev_attr_sess_max_burst_len.attr)
2228 param = ISCSI_PARAM_MAX_BURST;
2229 else if (attr == &dev_attr_sess_data_pdu_in_order.attr)
2230 param = ISCSI_PARAM_PDU_INORDER_EN;
2231 else if (attr == &dev_attr_sess_data_seq_in_order.attr)
2232 param = ISCSI_PARAM_DATASEQ_INORDER_EN;
2233 else if (attr == &dev_attr_sess_erl.attr)
2234 param = ISCSI_PARAM_ERL;
2235 else if (attr == &dev_attr_sess_targetname.attr)
2236 param = ISCSI_PARAM_TARGET_NAME;
2237 else if (attr == &dev_attr_sess_tpgt.attr)
2238 param = ISCSI_PARAM_TPGT;
2239 else if (attr == &dev_attr_sess_password.attr)
2240 param = ISCSI_PARAM_USERNAME;
2241 else if (attr == &dev_attr_sess_password_in.attr)
2242 param = ISCSI_PARAM_USERNAME_IN;
2243 else if (attr == &dev_attr_sess_username.attr)
2244 param = ISCSI_PARAM_PASSWORD;
2245 else if (attr == &dev_attr_sess_username_in.attr)
2246 param = ISCSI_PARAM_PASSWORD_IN;
2247 else if (attr == &dev_attr_sess_fast_abort.attr)
2248 param = ISCSI_PARAM_FAST_ABORT;
2249 else if (attr == &dev_attr_sess_abort_tmo.attr)
2250 param = ISCSI_PARAM_ABORT_TMO;
2251 else if (attr == &dev_attr_sess_lu_reset_tmo.attr)
2252 param = ISCSI_PARAM_LU_RESET_TMO;
2253 else if (attr == &dev_attr_sess_tgt_reset_tmo.attr)
2254 param = ISCSI_PARAM_TGT_RESET_TMO;
2255 else if (attr == &dev_attr_sess_ifacename.attr)
2256 param = ISCSI_PARAM_IFACE_NAME;
2257 else if (attr == &dev_attr_sess_initiatorname.attr)
2258 param = ISCSI_PARAM_INITIATOR_NAME;
2259 else if (attr == &dev_attr_sess_targetalias.attr)
2260 param = ISCSI_PARAM_TARGET_ALIAS;
2261 else if (attr == &dev_attr_priv_sess_recovery_tmo.attr)
2262 return S_IRUGO | S_IWUSR;
2263 else if (attr == &dev_attr_priv_sess_state.attr)
2264 return S_IRUGO;
2265 else {
2266 WARN_ONCE(1, "Invalid session attr");
2267 return 0;
2268 }
2269
2270 return t->attr_is_visible(ISCSI_PARAM, param);
2271}
2272
2273static struct attribute_group iscsi_session_group = {
2274 .attrs = iscsi_session_attrs,
2275 .is_visible = iscsi_session_attr_is_visible,
2276};
2277
Mike Christie1819dc82007-05-30 12:57:08 -05002278/*
2279 * iSCSI host attrs
2280 */
2281#define iscsi_host_attr_show(param) \
2282static ssize_t \
Tony Jonesee959b02008-02-22 00:13:36 +01002283show_host_param_##param(struct device *dev, \
2284 struct device_attribute *attr, char *buf) \
Mike Christie1819dc82007-05-30 12:57:08 -05002285{ \
Tony Jonesee959b02008-02-22 00:13:36 +01002286 struct Scsi_Host *shost = transport_class_to_shost(dev); \
Mike Christie1819dc82007-05-30 12:57:08 -05002287 struct iscsi_internal *priv = to_iscsi_internal(shost->transportt); \
2288 return priv->iscsi_transport->get_host_param(shost, param, buf); \
2289}
2290
2291#define iscsi_host_attr(field, param) \
2292 iscsi_host_attr_show(param) \
2293static ISCSI_CLASS_ATTR(host, field, S_IRUGO, show_host_param_##param, \
2294 NULL);
2295
Mike Christied8196ed2007-05-30 12:57:25 -05002296iscsi_host_attr(netdev, ISCSI_HOST_PARAM_NETDEV_NAME);
Mike Christie1819dc82007-05-30 12:57:08 -05002297iscsi_host_attr(hwaddress, ISCSI_HOST_PARAM_HWADDRESS);
Mike Christied8196ed2007-05-30 12:57:25 -05002298iscsi_host_attr(ipaddress, ISCSI_HOST_PARAM_IPADDRESS);
Mike Christie8ad57812007-05-30 12:57:13 -05002299iscsi_host_attr(initiatorname, ISCSI_HOST_PARAM_INITIATOR_NAME);
Mike Christie1819dc82007-05-30 12:57:08 -05002300
Mike Christief27fb2e2011-07-25 13:48:45 -05002301static struct attribute *iscsi_host_attrs[] = {
2302 &dev_attr_host_netdev.attr,
2303 &dev_attr_host_hwaddress.attr,
2304 &dev_attr_host_ipaddress.attr,
2305 &dev_attr_host_initiatorname.attr,
2306 NULL,
2307};
2308
2309static mode_t iscsi_host_attr_is_visible(struct kobject *kobj,
2310 struct attribute *attr, int i)
2311{
2312 struct device *cdev = container_of(kobj, struct device, kobj);
2313 struct Scsi_Host *shost = transport_class_to_shost(cdev);
2314 struct iscsi_internal *priv = to_iscsi_internal(shost->transportt);
2315 int param;
2316
2317 if (attr == &dev_attr_host_netdev.attr)
2318 param = ISCSI_HOST_PARAM_NETDEV_NAME;
2319 else if (attr == &dev_attr_host_hwaddress.attr)
2320 param = ISCSI_HOST_PARAM_HWADDRESS;
2321 else if (attr == &dev_attr_host_ipaddress.attr)
2322 param = ISCSI_HOST_PARAM_IPADDRESS;
2323 else if (attr == &dev_attr_host_initiatorname.attr)
2324 param = ISCSI_HOST_PARAM_INITIATOR_NAME;
2325 else {
2326 WARN_ONCE(1, "Invalid host attr");
2327 return 0;
2328 }
2329
2330 return priv->iscsi_transport->attr_is_visible(ISCSI_HOST_PARAM, param);
2331}
2332
2333static struct attribute_group iscsi_host_group = {
2334 .attrs = iscsi_host_attrs,
2335 .is_visible = iscsi_host_attr_is_visible,
2336};
Mike Christie1819dc82007-05-30 12:57:08 -05002337
Alex Aizman0896b752005-08-04 19:33:07 -07002338static int iscsi_session_match(struct attribute_container *cont,
2339 struct device *dev)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002340{
Mike Christie7b8631b2006-01-13 18:05:50 -06002341 struct iscsi_cls_session *session;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002342 struct Scsi_Host *shost;
Alex Aizman0896b752005-08-04 19:33:07 -07002343 struct iscsi_internal *priv;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002344
Alex Aizman0896b752005-08-04 19:33:07 -07002345 if (!iscsi_is_session_dev(dev))
Linus Torvalds1da177e2005-04-16 15:20:36 -07002346 return 0;
2347
Mike Christie7b8631b2006-01-13 18:05:50 -06002348 session = iscsi_dev_to_session(dev);
2349 shost = iscsi_session_to_shost(session);
Alex Aizman0896b752005-08-04 19:33:07 -07002350 if (!shost->transportt)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002351 return 0;
2352
Alex Aizman0896b752005-08-04 19:33:07 -07002353 priv = to_iscsi_internal(shost->transportt);
2354 if (priv->session_cont.ac.class != &iscsi_session_class.class)
2355 return 0;
2356
2357 return &priv->session_cont.ac == cont;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002358}
2359
Alex Aizman0896b752005-08-04 19:33:07 -07002360static int iscsi_conn_match(struct attribute_container *cont,
2361 struct device *dev)
2362{
Mike Christie7b8631b2006-01-13 18:05:50 -06002363 struct iscsi_cls_session *session;
2364 struct iscsi_cls_conn *conn;
Alex Aizman0896b752005-08-04 19:33:07 -07002365 struct Scsi_Host *shost;
2366 struct iscsi_internal *priv;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002367
Alex Aizman0896b752005-08-04 19:33:07 -07002368 if (!iscsi_is_conn_dev(dev))
2369 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002370
Mike Christie7b8631b2006-01-13 18:05:50 -06002371 conn = iscsi_dev_to_conn(dev);
2372 session = iscsi_dev_to_session(conn->dev.parent);
2373 shost = iscsi_session_to_shost(session);
2374
Alex Aizman0896b752005-08-04 19:33:07 -07002375 if (!shost->transportt)
2376 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002377
Alex Aizman0896b752005-08-04 19:33:07 -07002378 priv = to_iscsi_internal(shost->transportt);
2379 if (priv->conn_cont.ac.class != &iscsi_connection_class.class)
2380 return 0;
2381
2382 return &priv->conn_cont.ac == cont;
2383}
2384
Mike Christie30a6c652006-04-06 21:13:39 -05002385static int iscsi_host_match(struct attribute_container *cont,
2386 struct device *dev)
2387{
2388 struct Scsi_Host *shost;
2389 struct iscsi_internal *priv;
2390
2391 if (!scsi_is_host_device(dev))
2392 return 0;
2393
2394 shost = dev_to_shost(dev);
2395 if (!shost->transportt ||
2396 shost->transportt->host_attrs.ac.class != &iscsi_host_class.class)
2397 return 0;
2398
2399 priv = to_iscsi_internal(shost->transportt);
2400 return &priv->t.host_attrs.ac == cont;
2401}
2402
Mike Christie7b8631b2006-01-13 18:05:50 -06002403struct scsi_transport_template *
2404iscsi_register_transport(struct iscsi_transport *tt)
Alex Aizman0896b752005-08-04 19:33:07 -07002405{
2406 struct iscsi_internal *priv;
2407 unsigned long flags;
Mike Christief27fb2e2011-07-25 13:48:45 -05002408 int err;
Alex Aizman0896b752005-08-04 19:33:07 -07002409
2410 BUG_ON(!tt);
2411
2412 priv = iscsi_if_transport_lookup(tt);
2413 if (priv)
Mike Christie7b8631b2006-01-13 18:05:50 -06002414 return NULL;
Alex Aizman0896b752005-08-04 19:33:07 -07002415
Jes Sorensen24669f752006-01-16 10:31:18 -05002416 priv = kzalloc(sizeof(*priv), GFP_KERNEL);
Alex Aizman0896b752005-08-04 19:33:07 -07002417 if (!priv)
Mike Christie7b8631b2006-01-13 18:05:50 -06002418 return NULL;
Alex Aizman0896b752005-08-04 19:33:07 -07002419 INIT_LIST_HEAD(&priv->list);
Alex Aizman0896b752005-08-04 19:33:07 -07002420 priv->iscsi_transport = tt;
Mike Christie30a6c652006-04-06 21:13:39 -05002421 priv->t.user_scan = iscsi_user_scan;
Mike Christie06d25af2009-03-05 14:46:02 -06002422 priv->t.create_work_queue = 1;
Alex Aizman0896b752005-08-04 19:33:07 -07002423
Tony Jonesee959b02008-02-22 00:13:36 +01002424 priv->dev.class = &iscsi_transport_class;
Kay Sievers71610f52008-12-03 22:41:36 +01002425 dev_set_name(&priv->dev, "%s", tt->name);
Tony Jonesee959b02008-02-22 00:13:36 +01002426 err = device_register(&priv->dev);
Alex Aizman0896b752005-08-04 19:33:07 -07002427 if (err)
2428 goto free_priv;
2429
Tony Jonesee959b02008-02-22 00:13:36 +01002430 err = sysfs_create_group(&priv->dev.kobj, &iscsi_transport_group);
Alex Aizman0896b752005-08-04 19:33:07 -07002431 if (err)
Tony Jonesee959b02008-02-22 00:13:36 +01002432 goto unregister_dev;
Alex Aizman0896b752005-08-04 19:33:07 -07002433
Mike Christie30a6c652006-04-06 21:13:39 -05002434 /* host parameters */
Mike Christie30a6c652006-04-06 21:13:39 -05002435 priv->t.host_attrs.ac.class = &iscsi_host_class.class;
2436 priv->t.host_attrs.ac.match = iscsi_host_match;
Mike Christief27fb2e2011-07-25 13:48:45 -05002437 priv->t.host_attrs.ac.grp = &iscsi_host_group;
Mike Christie32c6e1b2008-05-21 15:53:58 -05002438 priv->t.host_size = sizeof(struct iscsi_cls_host);
Mike Christie30a6c652006-04-06 21:13:39 -05002439 transport_container_register(&priv->t.host_attrs);
2440
Alex Aizman0896b752005-08-04 19:33:07 -07002441 /* connection parameters */
Alex Aizman0896b752005-08-04 19:33:07 -07002442 priv->conn_cont.ac.class = &iscsi_connection_class.class;
2443 priv->conn_cont.ac.match = iscsi_conn_match;
Mike Christie3128c6c2011-07-25 13:48:42 -05002444 priv->conn_cont.ac.grp = &iscsi_conn_group;
Alex Aizman0896b752005-08-04 19:33:07 -07002445 transport_container_register(&priv->conn_cont);
2446
Alex Aizman0896b752005-08-04 19:33:07 -07002447 /* session parameters */
Alex Aizman0896b752005-08-04 19:33:07 -07002448 priv->session_cont.ac.class = &iscsi_session_class.class;
2449 priv->session_cont.ac.match = iscsi_session_match;
Mike Christie1d063c12011-07-25 13:48:43 -05002450 priv->session_cont.ac.grp = &iscsi_session_group;
Alex Aizman0896b752005-08-04 19:33:07 -07002451 transport_container_register(&priv->session_cont);
2452
Alex Aizman0896b752005-08-04 19:33:07 -07002453 spin_lock_irqsave(&iscsi_transport_lock, flags);
2454 list_add(&priv->list, &iscsi_transports);
2455 spin_unlock_irqrestore(&iscsi_transport_lock, flags);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002456
Alex Aizman0896b752005-08-04 19:33:07 -07002457 printk(KERN_NOTICE "iscsi: registered transport (%s)\n", tt->name);
Mike Christie7b8631b2006-01-13 18:05:50 -06002458 return &priv->t;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002459
Tony Jonesee959b02008-02-22 00:13:36 +01002460unregister_dev:
2461 device_unregister(&priv->dev);
Mike Christied82ff9be2008-05-21 15:54:13 -05002462 return NULL;
Alex Aizman0896b752005-08-04 19:33:07 -07002463free_priv:
2464 kfree(priv);
Mike Christie7b8631b2006-01-13 18:05:50 -06002465 return NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002466}
Alex Aizman0896b752005-08-04 19:33:07 -07002467EXPORT_SYMBOL_GPL(iscsi_register_transport);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002468
Alex Aizman0896b752005-08-04 19:33:07 -07002469int iscsi_unregister_transport(struct iscsi_transport *tt)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002470{
Alex Aizman0896b752005-08-04 19:33:07 -07002471 struct iscsi_internal *priv;
2472 unsigned long flags;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002473
Alex Aizman0896b752005-08-04 19:33:07 -07002474 BUG_ON(!tt);
2475
Arjan van de Ven0b950672006-01-11 13:16:10 +01002476 mutex_lock(&rx_queue_mutex);
Alex Aizman0896b752005-08-04 19:33:07 -07002477
2478 priv = iscsi_if_transport_lookup(tt);
2479 BUG_ON (!priv);
2480
Alex Aizman0896b752005-08-04 19:33:07 -07002481 spin_lock_irqsave(&iscsi_transport_lock, flags);
2482 list_del(&priv->list);
2483 spin_unlock_irqrestore(&iscsi_transport_lock, flags);
2484
2485 transport_container_unregister(&priv->conn_cont);
2486 transport_container_unregister(&priv->session_cont);
Mike Christie30a6c652006-04-06 21:13:39 -05002487 transport_container_unregister(&priv->t.host_attrs);
Alex Aizman0896b752005-08-04 19:33:07 -07002488
Tony Jonesee959b02008-02-22 00:13:36 +01002489 sysfs_remove_group(&priv->dev.kobj, &iscsi_transport_group);
2490 device_unregister(&priv->dev);
Arjan van de Ven0b950672006-01-11 13:16:10 +01002491 mutex_unlock(&rx_queue_mutex);
Alex Aizman0896b752005-08-04 19:33:07 -07002492
2493 return 0;
2494}
2495EXPORT_SYMBOL_GPL(iscsi_unregister_transport);
2496
Linus Torvalds1da177e2005-04-16 15:20:36 -07002497static __init int iscsi_transport_init(void)
2498{
Alex Aizman0896b752005-08-04 19:33:07 -07002499 int err;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002500
Meelis Roos09492602006-12-17 12:10:26 -06002501 printk(KERN_INFO "Loading iSCSI transport class v%s.\n",
Mike Christief4246b32006-07-24 15:47:54 -05002502 ISCSI_TRANSPORT_VERSION);
2503
Mike Christie41be1442007-02-28 17:32:18 -06002504 atomic_set(&iscsi_session_nr, 0);
2505
Alex Aizman0896b752005-08-04 19:33:07 -07002506 err = class_register(&iscsi_transport_class);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002507 if (err)
2508 return err;
Alex Aizman0896b752005-08-04 19:33:07 -07002509
Mike Christied82ff9be2008-05-21 15:54:13 -05002510 err = class_register(&iscsi_endpoint_class);
Alex Aizman0896b752005-08-04 19:33:07 -07002511 if (err)
2512 goto unregister_transport_class;
2513
Mike Christie8d079132011-07-25 13:48:40 -05002514 err = class_register(&iscsi_iface_class);
Mike Christied82ff9be2008-05-21 15:54:13 -05002515 if (err)
2516 goto unregister_endpoint_class;
2517
Mike Christie8d079132011-07-25 13:48:40 -05002518 err = transport_class_register(&iscsi_host_class);
2519 if (err)
2520 goto unregister_iface_class;
2521
Mike Christie30a6c652006-04-06 21:13:39 -05002522 err = transport_class_register(&iscsi_connection_class);
2523 if (err)
2524 goto unregister_host_class;
2525
Alex Aizman0896b752005-08-04 19:33:07 -07002526 err = transport_class_register(&iscsi_session_class);
2527 if (err)
2528 goto unregister_conn_class;
2529
Mike Christied82ff9be2008-05-21 15:54:13 -05002530 nls = netlink_kernel_create(&init_net, NETLINK_ISCSI, 1, iscsi_if_rx,
2531 NULL, THIS_MODULE);
Alex Aizman0896b752005-08-04 19:33:07 -07002532 if (!nls) {
2533 err = -ENOBUFS;
Mike Christie43a145a2006-10-16 18:09:38 -04002534 goto unregister_session_class;
Alex Aizman0896b752005-08-04 19:33:07 -07002535 }
2536
Mike Christied8bf5412007-12-13 12:43:27 -06002537 iscsi_eh_timer_workq = create_singlethread_workqueue("iscsi_eh");
2538 if (!iscsi_eh_timer_workq)
2539 goto release_nls;
2540
Mike Christie43a145a2006-10-16 18:09:38 -04002541 return 0;
Alex Aizman0896b752005-08-04 19:33:07 -07002542
Mike Christied8bf5412007-12-13 12:43:27 -06002543release_nls:
Denis V. Lunevb7c6ba62008-01-28 14:41:19 -08002544 netlink_kernel_release(nls);
Alex Aizman0896b752005-08-04 19:33:07 -07002545unregister_session_class:
2546 transport_class_unregister(&iscsi_session_class);
2547unregister_conn_class:
2548 transport_class_unregister(&iscsi_connection_class);
Mike Christie30a6c652006-04-06 21:13:39 -05002549unregister_host_class:
2550 transport_class_unregister(&iscsi_host_class);
Mike Christie8d079132011-07-25 13:48:40 -05002551unregister_iface_class:
2552 class_unregister(&iscsi_iface_class);
Mike Christied82ff9be2008-05-21 15:54:13 -05002553unregister_endpoint_class:
2554 class_unregister(&iscsi_endpoint_class);
Alex Aizman0896b752005-08-04 19:33:07 -07002555unregister_transport_class:
2556 class_unregister(&iscsi_transport_class);
2557 return err;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002558}
2559
2560static void __exit iscsi_transport_exit(void)
2561{
Mike Christied8bf5412007-12-13 12:43:27 -06002562 destroy_workqueue(iscsi_eh_timer_workq);
Denis V. Lunevb7c6ba62008-01-28 14:41:19 -08002563 netlink_kernel_release(nls);
Alex Aizman0896b752005-08-04 19:33:07 -07002564 transport_class_unregister(&iscsi_connection_class);
2565 transport_class_unregister(&iscsi_session_class);
Mike Christie30a6c652006-04-06 21:13:39 -05002566 transport_class_unregister(&iscsi_host_class);
Mike Christied82ff9be2008-05-21 15:54:13 -05002567 class_unregister(&iscsi_endpoint_class);
Mike Christie8d079132011-07-25 13:48:40 -05002568 class_unregister(&iscsi_iface_class);
Alex Aizman0896b752005-08-04 19:33:07 -07002569 class_unregister(&iscsi_transport_class);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002570}
2571
2572module_init(iscsi_transport_init);
2573module_exit(iscsi_transport_exit);
2574
Alex Aizman0896b752005-08-04 19:33:07 -07002575MODULE_AUTHOR("Mike Christie <michaelc@cs.wisc.edu>, "
2576 "Dmitry Yusupov <dmitry_yus@yahoo.com>, "
2577 "Alex Aizman <itn780@yahoo.com>");
2578MODULE_DESCRIPTION("iSCSI Transport Interface");
Linus Torvalds1da177e2005-04-16 15:20:36 -07002579MODULE_LICENSE("GPL");
Mike Christief4246b32006-07-24 15:47:54 -05002580MODULE_VERSION(ISCSI_TRANSPORT_VERSION);
Stephen Hemminger058548a2010-12-09 09:37:56 -08002581MODULE_ALIAS_NET_PF_PROTO(PF_NETLINK, NETLINK_ISCSI);