scsi_transport_iscsi.c 62.7 KB
Newer Older
1
/*
Linus Torvalds's avatar
Linus Torvalds committed
2 3 4
 * iSCSI transport class definitions
 *
 * Copyright (C) IBM Corporation, 2004
5 6 7
 * Copyright (C) Mike Christie, 2004 - 2005
 * Copyright (C) Dmitry Yusupov, 2004 - 2005
 * Copyright (C) Alex Aizman, 2004 - 2005
Linus Torvalds's avatar
Linus Torvalds committed
8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
 */
#include <linux/module.h>
24
#include <linux/mutex.h>
25
#include <linux/slab.h>
26
#include <net/tcp.h>
Linus Torvalds's avatar
Linus Torvalds committed
27 28 29 30 31
#include <scsi/scsi.h>
#include <scsi/scsi_host.h>
#include <scsi/scsi_device.h>
#include <scsi/scsi_transport.h>
#include <scsi/scsi_transport_iscsi.h>
32
#include <scsi/iscsi_if.h>
33
#include <scsi/scsi_cmnd.h>
Linus Torvalds's avatar
Linus Torvalds committed
34

35
#define ISCSI_SESSION_ATTRS 23
36
#define ISCSI_CONN_ATTRS 13
37
#define ISCSI_HOST_ATTRS 4
38

39
#define ISCSI_TRANSPORT_VERSION "2.0-870"
Linus Torvalds's avatar
Linus Torvalds committed
40

41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72
static int dbg_session;
module_param_named(debug_session, dbg_session, int,
		   S_IRUGO | S_IWUSR);
MODULE_PARM_DESC(debug_session,
		 "Turn on debugging for sessions in scsi_transport_iscsi "
		 "module. Set to 1 to turn on, and zero to turn off. Default "
		 "is off.");

static int dbg_conn;
module_param_named(debug_conn, dbg_conn, int,
		   S_IRUGO | S_IWUSR);
MODULE_PARM_DESC(debug_conn,
		 "Turn on debugging for connections in scsi_transport_iscsi "
		 "module. Set to 1 to turn on, and zero to turn off. Default "
		 "is off.");

#define ISCSI_DBG_TRANS_SESSION(_session, dbg_fmt, arg...)		\
	do {								\
		if (dbg_session)					\
			iscsi_cls_session_printk(KERN_INFO, _session,	\
						 "%s: " dbg_fmt,	\
						 __func__, ##arg);	\
	} while (0);

#define ISCSI_DBG_TRANS_CONN(_conn, dbg_fmt, arg...)			\
	do {								\
		if (dbg_conn)						\
			iscsi_cls_conn_printk(KERN_INFO, _conn,		\
					      "%s: " dbg_fmt,		\
					      __func__, ##arg);	\
	} while (0);

Linus Torvalds's avatar
Linus Torvalds committed
73 74
struct iscsi_internal {
	struct scsi_transport_template t;
75 76
	struct iscsi_transport *iscsi_transport;
	struct list_head list;
77
	struct device dev;
78

79
	struct device_attribute *host_attrs[ISCSI_HOST_ATTRS + 1];
80
	struct transport_container conn_cont;
81
	struct device_attribute *conn_attrs[ISCSI_CONN_ATTRS + 1];
82
	struct transport_container session_cont;
83
	struct device_attribute *session_attrs[ISCSI_SESSION_ATTRS + 1];
Linus Torvalds's avatar
Linus Torvalds committed
84 85
};

86
static atomic_t iscsi_session_nr; /* sysfs session id for next new session */
87
static struct workqueue_struct *iscsi_eh_timer_workq;
88

89 90 91
/*
 * list of registered transports and lock that must
 * be held while accessing list. The iscsi_transport_lock must
92
 * be acquired after the rx_queue_mutex.
93 94 95 96 97 98 99
 */
static LIST_HEAD(iscsi_transports);
static DEFINE_SPINLOCK(iscsi_transport_lock);

#define to_iscsi_internal(tmpl) \
	container_of(tmpl, struct iscsi_internal, t)

100 101
#define dev_to_iscsi_internal(_dev) \
	container_of(_dev, struct iscsi_internal, dev)
102

103
static void iscsi_transport_release(struct device *dev)
104
{
105
	struct iscsi_internal *priv = dev_to_iscsi_internal(dev);
106 107
	kfree(priv);
}
Linus Torvalds's avatar
Linus Torvalds committed
108

109 110 111 112 113 114
/*
 * iscsi_transport_class represents the iscsi_transports that are
 * registered.
 */
static struct class iscsi_transport_class = {
	.name = "iscsi_transport",
115
	.dev_release = iscsi_transport_release,
116 117 118
};

static ssize_t
119 120
show_transport_handle(struct device *dev, struct device_attribute *attr,
		      char *buf)
121
{
122
	struct iscsi_internal *priv = dev_to_iscsi_internal(dev);
123
	return sprintf(buf, "%llu\n", (unsigned long long)iscsi_handle(priv->iscsi_transport));
124
}
125
static DEVICE_ATTR(handle, S_IRUGO, show_transport_handle, NULL);
126 127 128

#define show_transport_attr(name, format)				\
static ssize_t								\
129 130
show_transport_##name(struct device *dev, 				\
		      struct device_attribute *attr,char *buf)		\
131
{									\
132
	struct iscsi_internal *priv = dev_to_iscsi_internal(dev);	\
133 134
	return sprintf(buf, format"\n", priv->iscsi_transport->name);	\
}									\
135
static DEVICE_ATTR(name, S_IRUGO, show_transport_##name, NULL);
136 137 138 139

show_transport_attr(caps, "0x%x");

static struct attribute *iscsi_transport_attrs[] = {
140 141
	&dev_attr_handle.attr,
	&dev_attr_caps.attr,
142 143 144 145 146 147 148
	NULL,
};

static struct attribute_group iscsi_transport_group = {
	.attrs = iscsi_transport_attrs,
};

149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173
/*
 * iSCSI endpoint attrs
 */
#define iscsi_dev_to_endpoint(_dev) \
	container_of(_dev, struct iscsi_endpoint, dev)

#define ISCSI_ATTR(_prefix,_name,_mode,_show,_store)	\
struct device_attribute dev_attr_##_prefix##_##_name =	\
        __ATTR(_name,_mode,_show,_store)

static void iscsi_endpoint_release(struct device *dev)
{
	struct iscsi_endpoint *ep = iscsi_dev_to_endpoint(dev);
	kfree(ep);
}

static struct class iscsi_endpoint_class = {
	.name = "iscsi_endpoint",
	.dev_release = iscsi_endpoint_release,
};

static ssize_t
show_ep_handle(struct device *dev, struct device_attribute *attr, char *buf)
{
	struct iscsi_endpoint *ep = iscsi_dev_to_endpoint(dev);
174
	return sprintf(buf, "%llu\n", (unsigned long long) ep->id);
175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191
}
static ISCSI_ATTR(ep, handle, S_IRUGO, show_ep_handle, NULL);

static struct attribute *iscsi_endpoint_attrs[] = {
	&dev_attr_ep_handle.attr,
	NULL,
};

static struct attribute_group iscsi_endpoint_group = {
	.attrs = iscsi_endpoint_attrs,
};

#define ISCSI_MAX_EPID -1

static int iscsi_match_epid(struct device *dev, void *data)
{
	struct iscsi_endpoint *ep = iscsi_dev_to_endpoint(dev);
192
	uint64_t *epid = (uint64_t *) data;
193 194 195 196 197 198 199 200 201

	return *epid == ep->id;
}

struct iscsi_endpoint *
iscsi_create_endpoint(int dd_size)
{
	struct device *dev;
	struct iscsi_endpoint *ep;
202
	uint64_t id;
203 204 205
	int err;

	for (id = 1; id < ISCSI_MAX_EPID; id++) {
206
		dev = class_find_device(&iscsi_endpoint_class, NULL, &id,
207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222
					iscsi_match_epid);
		if (!dev)
			break;
	}
	if (id == ISCSI_MAX_EPID) {
		printk(KERN_ERR "Too many connections. Max supported %u\n",
		       ISCSI_MAX_EPID - 1);
		return NULL;
	}

	ep = kzalloc(sizeof(*ep) + dd_size, GFP_KERNEL);
	if (!ep)
		return NULL;

	ep->id = id;
	ep->dev.class = &iscsi_endpoint_class;
223
	dev_set_name(&ep->dev, "ep-%llu", (unsigned long long) id);
224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254
	err = device_register(&ep->dev);
        if (err)
                goto free_ep;

	err = sysfs_create_group(&ep->dev.kobj, &iscsi_endpoint_group);
	if (err)
		goto unregister_dev;

	if (dd_size)
		ep->dd_data = &ep[1];
	return ep;

unregister_dev:
	device_unregister(&ep->dev);
	return NULL;

free_ep:
	kfree(ep);
	return NULL;
}
EXPORT_SYMBOL_GPL(iscsi_create_endpoint);

void iscsi_destroy_endpoint(struct iscsi_endpoint *ep)
{
	sysfs_remove_group(&ep->dev.kobj, &iscsi_endpoint_group);
	device_unregister(&ep->dev);
}
EXPORT_SYMBOL_GPL(iscsi_destroy_endpoint);

struct iscsi_endpoint *iscsi_lookup_endpoint(u64 handle)
{
255
	struct iscsi_endpoint *ep;
256 257
	struct device *dev;

258
	dev = class_find_device(&iscsi_endpoint_class, NULL, &handle,
259 260 261 262
				iscsi_match_epid);
	if (!dev)
		return NULL;

263 264 265 266 267 268 269
	ep = iscsi_dev_to_endpoint(dev);
	/*
	 * we can drop this now because the interface will prevent
	 * removals and lookups from racing.
	 */
	put_device(dev);
	return ep;
270 271 272
}
EXPORT_SYMBOL_GPL(iscsi_lookup_endpoint);

273
static int iscsi_setup_host(struct transport_container *tc, struct device *dev,
274
			    struct device *cdev)
275 276
{
	struct Scsi_Host *shost = dev_to_shost(dev);
277
	struct iscsi_cls_host *ihost = shost->shost_data;
278 279

	memset(ihost, 0, sizeof(*ihost));
280
	atomic_set(&ihost->nr_scans, 0);
281
	mutex_init(&ihost->mutex);
282 283 284 285 286 287
	return 0;
}

static DECLARE_TRANSPORT_CLASS(iscsi_host_class,
			       "iscsi_host",
			       iscsi_setup_host,
288
			       NULL,
289 290
			       NULL);

291 292
static DECLARE_TRANSPORT_CLASS(iscsi_session_class,
			       "iscsi_session",
Linus Torvalds's avatar
Linus Torvalds committed
293 294 295 296
			       NULL,
			       NULL,
			       NULL);

297 298
static DECLARE_TRANSPORT_CLASS(iscsi_connection_class,
			       "iscsi_connection",
Linus Torvalds's avatar
Linus Torvalds committed
299 300 301
			       NULL,
			       NULL,
			       NULL);
302 303

static struct sock *nls;
304
static DEFINE_MUTEX(rx_queue_mutex);
305

306 307
static LIST_HEAD(sesslist);
static DEFINE_SPINLOCK(sesslock);
308 309
static LIST_HEAD(connlist);
static DEFINE_SPINLOCK(connlock);
310

311 312 313 314 315 316 317 318 319 320
static uint32_t iscsi_conn_get_sid(struct iscsi_cls_conn *conn)
{
	struct iscsi_cls_session *sess = iscsi_dev_to_session(conn->dev.parent);
	return sess->sid;
}

/*
 * Returns the matching session to a given sid
 */
static struct iscsi_cls_session *iscsi_session_lookup(uint32_t sid)
321 322 323 324 325 326
{
	unsigned long flags;
	struct iscsi_cls_session *sess;

	spin_lock_irqsave(&sesslock, flags);
	list_for_each_entry(sess, &sesslist, sess_list) {
327
		if (sess->sid == sid) {
328 329 330 331 332 333 334 335
			spin_unlock_irqrestore(&sesslock, flags);
			return sess;
		}
	}
	spin_unlock_irqrestore(&sesslock, flags);
	return NULL;
}

336 337 338 339
/*
 * Returns the matching connection to a given sid / cid tuple
 */
static struct iscsi_cls_conn *iscsi_conn_lookup(uint32_t sid, uint32_t cid)
340 341 342 343 344 345
{
	unsigned long flags;
	struct iscsi_cls_conn *conn;

	spin_lock_irqsave(&connlock, flags);
	list_for_each_entry(conn, &connlist, conn_list) {
346
		if ((conn->cid == cid) && (iscsi_conn_get_sid(conn) == sid)) {
347 348 349 350 351 352 353 354
			spin_unlock_irqrestore(&connlock, flags);
			return conn;
		}
	}
	spin_unlock_irqrestore(&connlock, flags);
	return NULL;
}

355 356 357 358
/*
 * The following functions can be used by LLDs that allocate
 * their own scsi_hosts or by software iscsi LLDs
 */
359 360 361 362 363 364 365 366 367
static struct {
	int value;
	char *name;
} iscsi_session_state_names[] = {
	{ ISCSI_SESSION_LOGGED_IN,	"LOGGED_IN" },
	{ ISCSI_SESSION_FAILED,		"FAILED" },
	{ ISCSI_SESSION_FREE,		"FREE" },
};

368
static const char *iscsi_session_state_name(int state)
369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392
{
	int i;
	char *name = NULL;

	for (i = 0; i < ARRAY_SIZE(iscsi_session_state_names); i++) {
		if (iscsi_session_state_names[i].value == state) {
			name = iscsi_session_state_names[i].name;
			break;
		}
	}
	return name;
}

int iscsi_session_chkready(struct iscsi_cls_session *session)
{
	unsigned long flags;
	int err;

	spin_lock_irqsave(&session->lock, flags);
	switch (session->state) {
	case ISCSI_SESSION_LOGGED_IN:
		err = 0;
		break;
	case ISCSI_SESSION_FAILED:
393
		err = DID_IMM_RETRY << 16;
394 395
		break;
	case ISCSI_SESSION_FREE:
396
		err = DID_TRANSPORT_FAILFAST << 16;
397 398 399 400 401 402 403 404 405 406
		break;
	default:
		err = DID_NO_CONNECT << 16;
		break;
	}
	spin_unlock_irqrestore(&session->lock, flags);
	return err;
}
EXPORT_SYMBOL_GPL(iscsi_session_chkready);

407 408 409 410
static void iscsi_session_release(struct device *dev)
{
	struct iscsi_cls_session *session = iscsi_dev_to_session(dev);
	struct Scsi_Host *shost;
411

412 413
	shost = iscsi_session_to_shost(session);
	scsi_host_put(shost);
414
	ISCSI_DBG_TRANS_SESSION(session, "Completing session release\n");
415 416
	kfree(session);
}
417

418 419 420 421
static int iscsi_is_session_dev(const struct device *dev)
{
	return dev->release == iscsi_session_release;
}
422

423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440
static int iscsi_iter_session_fn(struct device *dev, void *data)
{
	void (* fn) (struct iscsi_cls_session *) = data;

	if (!iscsi_is_session_dev(dev))
		return 0;
	fn(iscsi_dev_to_session(dev));
	return 0;
}

void iscsi_host_for_each_session(struct Scsi_Host *shost,
				 void (*fn)(struct iscsi_cls_session *))
{
	device_for_each_child(&shost->shost_gendev, fn,
			      iscsi_iter_session_fn);
}
EXPORT_SYMBOL_GPL(iscsi_host_for_each_session);

441 442 443 444 445 446 447 448 449 450
/**
 * iscsi_scan_finished - helper to report when running scans are done
 * @shost: scsi host
 * @time: scan run time
 *
 * This function can be used by drives like qla4xxx to report to the scsi
 * layer when the scans it kicked off at module load time are done.
 */
int iscsi_scan_finished(struct Scsi_Host *shost, unsigned long time)
{
451
	struct iscsi_cls_host *ihost = shost->shost_data;
452 453 454 455 456 457 458 459
	/*
	 * qla4xxx will have kicked off some session unblocks before calling
	 * scsi_scan_host, so just wait for them to complete.
	 */
	return !atomic_read(&ihost->nr_scans);
}
EXPORT_SYMBOL_GPL(iscsi_scan_finished);

460 461 462 463 464 465 466
struct iscsi_scan_data {
	unsigned int channel;
	unsigned int id;
	unsigned int lun;
};

static int iscsi_user_scan_session(struct device *dev, void *data)
467
{
468
	struct iscsi_scan_data *scan_data = data;
469
	struct iscsi_cls_session *session;
470 471 472 473 474 475 476 477 478
	struct Scsi_Host *shost;
	struct iscsi_cls_host *ihost;
	unsigned long flags;
	unsigned int id;

	if (!iscsi_is_session_dev(dev))
		return 0;

	session = iscsi_dev_to_session(dev);
479 480 481

	ISCSI_DBG_TRANS_SESSION(session, "Scanning session\n");

482 483
	shost = iscsi_session_to_shost(session);
	ihost = shost->shost_data;
484 485

	mutex_lock(&ihost->mutex);
486 487 488
	spin_lock_irqsave(&session->lock, flags);
	if (session->state != ISCSI_SESSION_LOGGED_IN) {
		spin_unlock_irqrestore(&session->lock, flags);
489
		goto user_scan_exit;
490
	}
491 492
	id = session->target_id;
	spin_unlock_irqrestore(&session->lock, flags);
493

494 495 496 497 498 499 500 501
	if (id != ISCSI_MAX_TARGET) {
		if ((scan_data->channel == SCAN_WILD_CARD ||
		     scan_data->channel == 0) &&
		    (scan_data->id == SCAN_WILD_CARD ||
		     scan_data->id == id))
			scsi_scan_target(&session->dev, 0, id,
					 scan_data->lun, 1);
	}
502 503

user_scan_exit:
504
	mutex_unlock(&ihost->mutex);
505
	ISCSI_DBG_TRANS_SESSION(session, "Completed session scan\n");
506 507 508
	return 0;
}

509 510 511 512 513 514 515 516 517 518 519 520 521
static int iscsi_user_scan(struct Scsi_Host *shost, uint channel,
			   uint id, uint lun)
{
	struct iscsi_scan_data scan_data;

	scan_data.channel = channel;
	scan_data.id = id;
	scan_data.lun = lun;

	return device_for_each_child(&shost->shost_gendev, &scan_data,
				     iscsi_user_scan_session);
}

522 523 524 525
static void iscsi_scan_session(struct work_struct *work)
{
	struct iscsi_cls_session *session =
			container_of(work, struct iscsi_cls_session, scan_work);
526
	struct Scsi_Host *shost = iscsi_session_to_shost(session);
527
	struct iscsi_cls_host *ihost = shost->shost_data;
528
	struct iscsi_scan_data scan_data;
529

530 531 532
	scan_data.channel = 0;
	scan_data.id = SCAN_WILD_CARD;
	scan_data.lun = SCAN_WILD_CARD;
533

534
	iscsi_user_scan_session(&session->dev, &scan_data);
535
	atomic_dec(&ihost->nr_scans);
536 537
}

538 539
/**
 * iscsi_block_scsi_eh - block scsi eh until session state has transistioned
540
 * @cmd: scsi cmd passed to scsi eh handler
541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568
 *
 * If the session is down this function will wait for the recovery
 * timer to fire or for the session to be logged back in. If the
 * recovery timer fires then FAST_IO_FAIL is returned. The caller
 * should pass this error value to the scsi eh.
 */
int iscsi_block_scsi_eh(struct scsi_cmnd *cmd)
{
	struct iscsi_cls_session *session =
			starget_to_session(scsi_target(cmd->device));
	unsigned long flags;
	int ret = 0;

	spin_lock_irqsave(&session->lock, flags);
	while (session->state != ISCSI_SESSION_LOGGED_IN) {
		if (session->state == ISCSI_SESSION_FREE) {
			ret = FAST_IO_FAIL;
			break;
		}
		spin_unlock_irqrestore(&session->lock, flags);
		msleep(1000);
		spin_lock_irqsave(&session->lock, flags);
	}
	spin_unlock_irqrestore(&session->lock, flags);
	return ret;
}
EXPORT_SYMBOL_GPL(iscsi_block_scsi_eh);

569
static void session_recovery_timedout(struct work_struct *work)
570
{
571 572 573
	struct iscsi_cls_session *session =
		container_of(work, struct iscsi_cls_session,
			     recovery_work.work);
574
	unsigned long flags;
575

576 577 578
	iscsi_cls_session_printk(KERN_INFO, session,
				 "session recovery timed out after %d secs\n",
				 session->recovery_tmo);
579

580 581 582 583 584 585 586 587 588 589 590 591 592
	spin_lock_irqsave(&session->lock, flags);
	switch (session->state) {
	case ISCSI_SESSION_FAILED:
		session->state = ISCSI_SESSION_FREE;
		break;
	case ISCSI_SESSION_LOGGED_IN:
	case ISCSI_SESSION_FREE:
		/* we raced with the unblock's flush */
		spin_unlock_irqrestore(&session->lock, flags);
		return;
	}
	spin_unlock_irqrestore(&session->lock, flags);

593 594 595
	if (session->transport->session_recovery_timedout)
		session->transport->session_recovery_timedout(session);

596
	ISCSI_DBG_TRANS_SESSION(session, "Unblocking SCSI target\n");
597
	scsi_target_unblock(&session->dev);
598
	ISCSI_DBG_TRANS_SESSION(session, "Completed unblocking SCSI target\n");
599 600
}

601
static void __iscsi_unblock_session(struct work_struct *work)
602
{
603 604 605
	struct iscsi_cls_session *session =
			container_of(work, struct iscsi_cls_session,
				     unblock_work);
606
	struct Scsi_Host *shost = iscsi_session_to_shost(session);
607
	struct iscsi_cls_host *ihost = shost->shost_data;
608 609
	unsigned long flags;

610
	ISCSI_DBG_TRANS_SESSION(session, "Unblocking session\n");
611 612 613 614 615
	/*
	 * The recovery and unblock work get run from the same workqueue,
	 * so try to cancel it if it was going to run after this unblock.
	 */
	cancel_delayed_work(&session->recovery_work);
616 617 618
	spin_lock_irqsave(&session->lock, flags);
	session->state = ISCSI_SESSION_LOGGED_IN;
	spin_unlock_irqrestore(&session->lock, flags);
619 620
	/* start IO */
	scsi_target_unblock(&session->dev);
621 622 623 624 625 626
	/*
	 * Only do kernel scanning if the driver is properly hooked into
	 * the async scanning code (drivers like iscsi_tcp do login and
	 * scanning from userspace).
	 */
	if (shost->hostt->scan_finished) {
627
		if (scsi_queue_work(shost, &session->scan_work))
628 629
			atomic_inc(&ihost->nr_scans);
	}
630
	ISCSI_DBG_TRANS_SESSION(session, "Completed unblocking session\n");
631
}
632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647

/**
 * iscsi_unblock_session - set a session as logged in and start IO.
 * @session: iscsi session
 *
 * Mark a session as ready to accept IO.
 */
void iscsi_unblock_session(struct iscsi_cls_session *session)
{
	queue_work(iscsi_eh_timer_workq, &session->unblock_work);
	/*
	 * make sure all the events have completed before tell the driver
	 * it is safe
	 */
	flush_workqueue(iscsi_eh_timer_workq);
}
648 649
EXPORT_SYMBOL_GPL(iscsi_unblock_session);

650
static void __iscsi_block_session(struct work_struct *work)
651
{
652 653 654
	struct iscsi_cls_session *session =
			container_of(work, struct iscsi_cls_session,
				     block_work);
655 656
	unsigned long flags;

657
	ISCSI_DBG_TRANS_SESSION(session, "Blocking session\n");
658 659 660
	spin_lock_irqsave(&session->lock, flags);
	session->state = ISCSI_SESSION_FAILED;
	spin_unlock_irqrestore(&session->lock, flags);
661
	scsi_target_block(&session->dev);
662
	ISCSI_DBG_TRANS_SESSION(session, "Completed SCSI target blocking\n");
663 664 665 666
	if (session->recovery_tmo >= 0)
		queue_delayed_work(iscsi_eh_timer_workq,
				   &session->recovery_work,
				   session->recovery_tmo * HZ);
667
}
668 669 670 671 672

void iscsi_block_session(struct iscsi_cls_session *session)
{
	queue_work(iscsi_eh_timer_workq, &session->block_work);
}
673 674
EXPORT_SYMBOL_GPL(iscsi_block_session);

675 676 677 678 679 680
static void __iscsi_unbind_session(struct work_struct *work)
{
	struct iscsi_cls_session *session =
			container_of(work, struct iscsi_cls_session,
				     unbind_work);
	struct Scsi_Host *shost = iscsi_session_to_shost(session);
681
	struct iscsi_cls_host *ihost = shost->shost_data;
682
	unsigned long flags;
683

684 685
	ISCSI_DBG_TRANS_SESSION(session, "Unbinding session\n");

686 687
	/* Prevent new scans and make sure scanning is not in progress */
	mutex_lock(&ihost->mutex);
688 689 690
	spin_lock_irqsave(&session->lock, flags);
	if (session->target_id == ISCSI_MAX_TARGET) {
		spin_unlock_irqrestore(&session->lock, flags);
691 692 693
		mutex_unlock(&ihost->mutex);
		return;
	}
694 695
	session->target_id = ISCSI_MAX_TARGET;
	spin_unlock_irqrestore(&session->lock, flags);
696 697 698 699
	mutex_unlock(&ihost->mutex);

	scsi_remove_target(&session->dev);
	iscsi_session_event(session, ISCSI_KEVENT_UNBIND_SESSION);
700
	ISCSI_DBG_TRANS_SESSION(session, "Completed target removal\n");
701 702
}

703
struct iscsi_cls_session *
704 705
iscsi_alloc_session(struct Scsi_Host *shost, struct iscsi_transport *transport,
		    int dd_size)
706 707 708
{
	struct iscsi_cls_session *session;

709
	session = kzalloc(sizeof(*session) + dd_size,
710
			  GFP_KERNEL);
711
	if (!session)
712 713
		return NULL;

714
	session->transport = transport;
715
	session->recovery_tmo = 120;
716
	session->state = ISCSI_SESSION_FREE;
717
	INIT_DELAYED_WORK(&session->recovery_work, session_recovery_timedout);
718
	INIT_LIST_HEAD(&session->sess_list);
719 720
	INIT_WORK(&session->unblock_work, __iscsi_unblock_session);
	INIT_WORK(&session->block_work, __iscsi_block_session);
721
	INIT_WORK(&session->unbind_work, __iscsi_unbind_session);
722
	INIT_WORK(&session->scan_work, iscsi_scan_session);
723
	spin_lock_init(&session->lock);
724

725 726
	/* this is released in the dev's release function */
	scsi_host_get(shost);
727 728 729
	session->dev.parent = &shost->shost_gendev;
	session->dev.release = iscsi_session_release;
	device_initialize(&session->dev);
730
	if (dd_size)
731
		session->dd_data = &session[1];
732 733

	ISCSI_DBG_TRANS_SESSION(session, "Completed session allocation\n");
734 735 736 737
	return session;
}
EXPORT_SYMBOL_GPL(iscsi_alloc_session);

738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754
static int iscsi_get_next_target_id(struct device *dev, void *data)
{
	struct iscsi_cls_session *session;
	unsigned long flags;
	int err = 0;

	if (!iscsi_is_session_dev(dev))
		return 0;

	session = iscsi_dev_to_session(dev);
	spin_lock_irqsave(&session->lock, flags);
	if (*((unsigned int *) data) == session->target_id)
		err = -EEXIST;
	spin_unlock_irqrestore(&session->lock, flags);
	return err;
}

755
int iscsi_add_session(struct iscsi_cls_session *session, unsigned int target_id)
756 757
{
	struct Scsi_Host *shost = iscsi_session_to_shost(session);
758
	struct iscsi_cls_host *ihost;
759
	unsigned long flags;
760
	unsigned int id = target_id;
761
	int err;
762

763
	ihost = shost->shost_data;
764
	session->sid = atomic_add_return(1, &iscsi_session_nr);
765 766 767 768 769 770 771 772 773 774 775 776 777 778

	if (id == ISCSI_MAX_TARGET) {
		for (id = 0; id < ISCSI_MAX_TARGET; id++) {
			err = device_for_each_child(&shost->shost_gendev, &id,
						    iscsi_get_next_target_id);
			if (!err)
				break;
		}

		if (id == ISCSI_MAX_TARGET) {
			iscsi_cls_session_printk(KERN_ERR, session,
						 "Too many iscsi targets. Max "
						 "number of targets is %d.\n",
						 ISCSI_MAX_TARGET - 1);
779
			err = -EOVERFLOW;
780 781 782 783
			goto release_host;
		}
	}
	session->target_id = id;
784

785
	dev_set_name(&session->dev, "session%u", session->sid);
786
	err = device_add(&session->dev);
787
	if (err) {
788 789
		iscsi_cls_session_printk(KERN_ERR, session,
					 "could not register session's dev\n");
790
		goto release_host;
791 792 793
	}
	transport_register_device(&session->dev);

794 795 796 797 798
	spin_lock_irqsave(&sesslock, flags);
	list_add(&session->sess_list, &sesslist);
	spin_unlock_irqrestore(&sesslock, flags);

	iscsi_session_event(session, ISCSI_KEVENT_CREATE_SESSION);
799
	ISCSI_DBG_TRANS_SESSION(session, "Completed session adding\n");
800
	return 0;
801

802 803 804
release_host:
	scsi_host_put(shost);
	return err;
805
}
806
EXPORT_SYMBOL_GPL(iscsi_add_session);
807 808

/**
809 810 811
 * iscsi_create_session - create iscsi class session
 * @shost: scsi host
 * @transport: iscsi transport
812
 * @dd_size: private driver data size
813
 * @target_id: which target
814
 *
815
 * This can be called from a LLD or iscsi_transport.
816
 */
817
struct iscsi_cls_session *
818 819
iscsi_create_session(struct Scsi_Host *shost, struct iscsi_transport *transport,
		     int dd_size, unsigned int target_id)
820 821 822
{
	struct iscsi_cls_session *session;

823
	session = iscsi_alloc_session(shost, transport, dd_size);
824 825 826
	if (!session)
		return NULL;

827
	if (iscsi_add_session(session, target_id)) {
828 829 830 831 832 833 834
		iscsi_free_session(session);
		return NULL;
	}
	return session;
}
EXPORT_SYMBOL_GPL(iscsi_create_session);

835 836 837 838 839
static void iscsi_conn_release(struct device *dev)
{
	struct iscsi_cls_conn *conn = iscsi_dev_to_conn(dev);
	struct device *parent = conn->dev.parent;

840
	ISCSI_DBG_TRANS_CONN(conn, "Releasing conn\n");
841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856
	kfree(conn);
	put_device(parent);
}

static int iscsi_is_conn_dev(const struct device *dev)
{
	return dev->release == iscsi_conn_release;
}

static int iscsi_iter_destroy_conn_fn(struct device *dev, void *data)
{
	if (!iscsi_is_conn_dev(dev))
		return 0;
	return iscsi_destroy_conn(iscsi_dev_to_conn(dev));
}

857
void iscsi_remove_session(struct iscsi_cls_session *session)
858
{
859
	struct Scsi_Host *shost = iscsi_session_to_shost(session);
860 861 862
	unsigned long flags;
	int err;

863 864
	ISCSI_DBG_TRANS_SESSION(session, "Removing session\n");

865 866 867
	spin_lock_irqsave(&sesslock, flags);
	list_del(&session->sess_list);
	spin_unlock_irqrestore(&sesslock, flags);
868

869 870 871 872 873
	/* make sure there are no blocks/unblocks queued */
	flush_workqueue(iscsi_eh_timer_workq);
	/* make sure the timedout callout is not running */
	if (!cancel_delayed_work(&session->recovery_work))
		flush_workqueue(iscsi_eh_timer_workq);
874 875 876
	/*
	 * If we are blocked let commands flow again. The lld or iscsi
	 * layer should set up the queuecommand to fail commands.
877 878
	 * We assume that LLD will not be calling block/unblock while
	 * removing the session.
879
	 */
880 881 882
	spin_lock_irqsave(&session->lock, flags);
	session->state = ISCSI_SESSION_FREE;
	spin_unlock_irqrestore(&session->lock, flags);
883

884 885
	scsi_target_unblock(&session->dev);
	/* flush running scans then delete devices */
886
	scsi_flush_work(shost);
887
	__iscsi_unbind_session(&session->unbind_work);
888

889 890 891 892
	/* hw iscsi may not have removed all connections from session */
	err = device_for_each_child(&session->dev, NULL,
				    iscsi_iter_destroy_conn_fn);
	if (err)
893 894 895
		iscsi_cls_session_printk(KERN_ERR, session,
					 "Could not delete all connections "
					 "for session. Error %d.\n", err);
896

897
	transport_unregister_device(&session->dev);
898 899

	ISCSI_DBG_TRANS_SESSION(session, "Completing session removal\n");
900 901 902 903 904 905
	device_del(&session->dev);
}
EXPORT_SYMBOL_GPL(iscsi_remove_session);

void iscsi_free_session(struct iscsi_cls_session *session)
{
906
	ISCSI_DBG_TRANS_SESSION(session, "Freeing session\n");
907
	iscsi_session_event(session, ISCSI_KEVENT_DESTROY_SESSION);
908
	put_device(&session->dev);
909
}
910 911 912 913 914 915 916 917
EXPORT_SYMBOL_GPL(iscsi_free_session);

/**
 * iscsi_destroy_session - destroy iscsi session
 * @session: iscsi_session
 *
 * Can be called by a LLD or iscsi_transport. There must not be
 * any running connections.
918
 */
919 920 921
int iscsi_destroy_session(struct iscsi_cls_session *session)
{
	iscsi_remove_session(session);
922
	ISCSI_DBG_TRANS_SESSION(session, "Completing session destruction\n");
923 924 925
	iscsi_free_session(session);
	return 0;
}
926 927 928 929 930
EXPORT_SYMBOL_GPL(iscsi_destroy_session);

/**
 * iscsi_create_conn - create iscsi class connection
 * @session: iscsi cls session
931
 * @dd_size: private driver data size
932 933 934 935 936
 * @cid: connection id
 *
 * This can be called from a LLD or iscsi_transport. The connection
 * is child of the session so cid must be unique for all connections
 * on the session.
937 938 939 940 941
 *
 * Since we do not support MCS, cid will normally be zero. In some cases
 * for software iscsi we could be trying to preallocate a connection struct
 * in which case there could be two connection structs and cid would be
 * non-zero.
942
 */
943
struct iscsi_cls_conn *
944
iscsi_create_conn(struct iscsi_cls_session *session, int dd_size, uint32_t cid)
945 946 947
{
	struct iscsi_transport *transport = session->transport;
	struct iscsi_cls_conn *conn;
948
	unsigned long flags;
949 950
	int err;

951
	conn = kzalloc(sizeof(*conn) + dd_size, GFP_KERNEL);
952 953
	if (!conn)
		return NULL;
954
	if (dd_size)
955 956
		conn->dd_data = &conn[1];

957
	mutex_init(&conn->ep_mutex);
958 959
	INIT_LIST_HEAD(&conn->conn_list);
	conn->transport = transport;
960
	conn->cid = cid;
961 962 963

	/* this is released in the dev's release function */
	if (!get_device(&session->dev))
964
		goto free_conn;
965

966
	dev_set_name(&conn->dev, "connection%d:%u", session->sid, cid);
967 968 969 970
	conn->dev.parent = &session->dev;
	conn->dev.release = iscsi_conn_release;
	err = device_register(&conn->dev);
	if (err) {
971 972
		iscsi_cls_session_printk(KERN_ERR, session, "could not "
					 "register connection's dev\n");
973 974 975
		goto release_parent_ref;
	}
	transport_register_device(&conn->dev);
976 977 978 979

	spin_lock_irqsave(&connlock, flags);
	list_add(&conn->conn_list, &connlist);
	spin_unlock_irqrestore(&connlock, flags);
980 981

	ISCSI_DBG_TRANS_CONN(conn, "Completed conn creation\n");
982 983 984 985 986 987 988 989 990 991 992 993 994
	return conn;

release_parent_ref:
	put_device(&session->dev);
free_conn:
	kfree(conn);
	return NULL;
}

EXPORT_SYMBOL_GPL(iscsi_create_conn);

/**
 * iscsi_destroy_conn - destroy iscsi class connection
995
 * @conn: iscsi cls session
996
 *
997
 * This can be called from a LLD or iscsi_transport.
998
 */
999 1000
int iscsi_destroy_conn(struct iscsi_cls_conn *conn)
{
1001 1002 1003 1004 1005 1006
	unsigned long flags;

	spin_lock_irqsave(&connlock, flags);
	list_del(&conn->conn_list);
	spin_unlock_irqrestore(&connlock, flags);

1007
	transport_unregister_device(&conn->dev);
1008
	ISCSI_DBG_TRANS_CONN(conn, "Completing conn destruction\n");
1009 1010 1011 1012 1013 1014 1015 1016
	device_unregister(&conn->dev);
	return 0;
}
EXPORT_SYMBOL_GPL(iscsi_destroy_conn);

/*
 * iscsi interface functions
 */
1017 1018 1019 1020 1021 1022 1023 1024 1025 1026 1027 1028 1029 1030 1031 1032
static struct iscsi_internal *
iscsi_if_transport_lookup(struct iscsi_transport *tt)
{
	struct iscsi_internal *priv;
	unsigned long flags;

	spin_lock_irqsave(&iscsi_transport_lock, flags);
	list_for_each_entry(priv, &iscsi_transports, list) {
		if (tt == priv->iscsi_transport) {
			spin_unlock_irqrestore(&iscsi_transport_lock, flags);
			return priv;
		}
	}
	spin_unlock_irqrestore(&iscsi_transport_lock, flags);
	return NULL;
}
Linus Torvalds's avatar
Linus Torvalds committed
1033

1034
static int
1035
iscsi_multicast_skb(struct sk_buff *skb, uint32_t group, gfp_t gfp)
1036
{
1037
	return nlmsg_multicast(nls, skb, 0, group, gfp);
Linus Torvalds's avatar
Linus Torvalds committed
1038 1039
}

1040
int iscsi_recv_pdu(struct iscsi_cls_conn *conn, struct iscsi_hdr *hdr,
1041
		   char *data, uint32_t data_size)
Linus Torvalds's avatar
Linus Torvalds committed
1042
{
1043 1044 1045 1046
	struct nlmsghdr	*nlh;
	struct sk_buff *skb;
	struct iscsi_uevent *ev;
	char *pdu;
1047
	struct iscsi_internal *priv;
1048 1049 1050
	int len = NLMSG_SPACE(sizeof(*ev) + sizeof(struct iscsi_hdr) +
			      data_size);

1051 1052 1053 1054
	priv = iscsi_if_transport_lookup(conn->transport);
	if (!priv)
		return -EINVAL;

1055
	skb = alloc_skb(len, GFP_ATOMIC);
1056
	if (!skb) {
1057
		iscsi_conn_error_event(conn, ISCSI_ERR_CONN_FAILED);
1058 1059
		iscsi_cls_conn_printk(KERN_ERR, conn, "can not deliver "
				      "control PDU: OOM\n");
1060 1061
		return -ENOMEM;
	}
Linus Torvalds's avatar
Linus Torvalds committed
1062

1063
	nlh = __nlmsg_put(skb, 0, 0, 0, (len - sizeof(*nlh)), 0);
1064 1065 1066 1067
	ev = NLMSG_DATA(nlh);
	memset(ev, 0, sizeof(*ev));
	ev->transport_handle = iscsi_handle(conn->transport);
	ev->type = ISCSI_KEVENT_RECV_PDU;
1068 1069
	ev->r.recv_req.cid = conn->cid;
	ev->r.recv_req.sid = iscsi_conn_get_sid(conn);
1070 1071 1072
	pdu = (char*)ev + sizeof(*ev);
	memcpy(pdu, hdr, sizeof(struct iscsi_hdr));
	memcpy(pdu + sizeof(struct iscsi_hdr), data, data_size);
Linus Torvalds's avatar
Linus Torvalds committed
1073

1074
	return iscsi_multicast_skb(skb, ISCSI_NL_GRP_ISCSID, GFP_ATOMIC);
Linus Torvalds's avatar
Linus Torvalds committed
1075
}
1076
EXPORT_SYMBOL_GPL(iscsi_recv_pdu);
Linus Torvalds's avatar
Linus Torvalds committed
1077

1078 1079 1080 1081 1082 1083 1084 1085 1086
int iscsi_offload_mesg(struct Scsi_Host *shost,
		       struct iscsi_transport *transport, uint32_t type,
		       char *data, uint16_t data_size)
{
	struct nlmsghdr	*nlh;
	struct sk_buff *skb;
	struct iscsi_uevent *ev;
	int len = NLMSG_SPACE(sizeof(*ev) + data_size);

1087
	skb = alloc_skb(len, GFP_ATOMIC);
1088 1089 1090 1091 1092 1093 1094 1095 1096 1097 1098 1099 1100 1101 1102 1103 1104 1105 1106 1107 1108
	if (!skb) {
		printk(KERN_ERR "can not deliver iscsi offload message:OOM\n");
		return -ENOMEM;
	}

	nlh = __nlmsg_put(skb, 0, 0, 0, (len - sizeof(*nlh)), 0);
	ev = NLMSG_DATA(nlh);
	memset(ev, 0, sizeof(*ev));
	ev->type = type;
	ev->transport_handle = iscsi_handle(transport);
	switch (type) {
	case ISCSI_KEVENT_PATH_REQ:
		ev->r.req_path.host_no = shost->host_no;
		break;
	case ISCSI_KEVENT_IF_DOWN:
		ev->r.notify_if_down.host_no = shost->host_no;
		break;
	}

	memcpy((char *)ev + sizeof(*ev), data, data_size);

1109
	return iscsi_multicast_skb(skb, ISCSI_NL_GRP_UIP, GFP_ATOMIC);
1110 1111 1112
}
EXPORT_SYMBOL_GPL(iscsi_offload_mesg);

1113
void iscsi_conn_error_event(struct iscsi_cls_conn *conn, enum iscsi_err error)
Linus Torvalds's avatar
Linus Torvalds committed
1114
{
1115 1116 1117
	struct nlmsghdr	*nlh;
	struct sk_buff	*skb;
	struct iscsi_uevent *ev;
1118
	struct iscsi_internal *priv;
1119 1120
	int len = NLMSG_SPACE(sizeof(*ev));

1121 1122 1123 1124
	priv = iscsi_if_transport_lookup(conn->transport);
	if (!priv)
		return;

1125
	skb = alloc_skb(len, GFP_ATOMIC);
1126
	if (!skb) {
1127 1128
		iscsi_cls_conn_printk(KERN_ERR, conn, "gracefully ignored "
				      "conn error (%d)\n", error);
1129 1130 1131
		return;
	}

1132
	nlh = __nlmsg_put(skb, 0, 0, 0, (len - sizeof(*nlh)), 0);
1133 1134 1135 1136
	ev = NLMSG_DATA(nlh);
	ev->transport_handle = iscsi_handle(conn->transport);
	ev->type = ISCSI_KEVENT_CONN_ERROR;
	ev->r.connerror.error = error;
1137 1138
	ev->r.connerror.cid = conn->cid;
	ev->r.connerror.sid = iscsi_conn_get_sid(conn);
Linus Torvalds's avatar
Linus Torvalds committed
1139

1140
	iscsi_multicast_skb(skb, ISCSI_NL_GRP_ISCSID, GFP_ATOMIC);
Linus Torvalds's avatar
Linus Torvalds committed
1141

1142 1143
	iscsi_cls_conn_printk(KERN_INFO, conn, "detected conn error (%d)\n",
			      error);
1144
}
1145
EXPORT_SYMBOL_GPL(iscsi_conn_error_event);
1146 1147

static int
1148 1149
iscsi_if_send_reply(uint32_t group, int seq, int type, int done, int multi,
		    void *payload, int size)
1150 1151 1152 1153 1154 1155 1156
{
	struct sk_buff	*skb;
	struct nlmsghdr	*nlh;
	int len = NLMSG_SPACE(size);
	int flags = multi ? NLM_F_MULTI : 0;
	int t = done ? NLMSG_DONE : type;

1157
	skb = alloc_skb(len, GFP_ATOMIC);
1158 1159 1160 1161
	if (!skb) {
		printk(KERN_ERR "Could not allocate skb to send reply.\n");
		return -ENOMEM;
	}
1162

1163
	nlh = __nlmsg_put(skb, 0, 0, t, (len - sizeof(*nlh)), 0);
1164 1165
	nlh->nlmsg_flags = flags;
	memcpy(NLMSG_DATA(nlh), payload, size);
1166
	return iscsi_multicast_skb(skb, group, GFP_ATOMIC);
Linus Torvalds's avatar
Linus Torvalds committed
1167 1168
}

1169
static int
1170
iscsi_if_get_stats(struct iscsi_transport *transport, struct nlmsghdr *nlh)
1171 1172 1173 1174 1175 1176 1177
{
	struct iscsi_uevent *ev = NLMSG_DATA(nlh);
	struct iscsi_stats *stats;
	struct sk_buff *skbstat;
	struct iscsi_cls_conn *conn;
	struct nlmsghdr	*nlhstat;
	struct iscsi_uevent *evstat;
1178
	struct iscsi_internal *priv;
1179 1180 1181 1182 1183
	int len = NLMSG_SPACE(sizeof(*ev) +
			      sizeof(struct iscsi_stats) +
			      sizeof(struct iscsi_stats_custom) *
			      ISCSI_STATS_CUSTOM_MAX);
	int err = 0;
1184

1185 1186 1187 1188
	priv = iscsi_if_transport_lookup(transport);
	if (!priv)
		return -EINVAL;

1189
	conn = iscsi_conn_lookup(ev->u.get_stats.sid, ev->u.get_stats.cid);
1190 1191
	if (!conn)
		return -EEXIST;
1192

1193 1194
	do {
		int actual_size;
1195

1196
		skbstat = alloc_skb(len, GFP_ATOMIC);
1197
		if (!skbstat) {
1198 1199
			iscsi_cls_conn_printk(KERN_ERR, conn, "can not "
					      "deliver stats: OOM\n");
1200 1201 1202
			return -ENOMEM;
		}

1203
		nlhstat = __nlmsg_put(skbstat, 0, 0, 0,
1204 1205 1206 1207 1208
				      (len - sizeof(*nlhstat)), 0);
		evstat = NLMSG_DATA(nlhstat);
		memset(evstat, 0, sizeof(*evstat));
		evstat->transport_handle = iscsi_handle(conn->transport);
		evstat->type = nlh->nlmsg_type;
1209 1210 1211 1212
		evstat->u.get_stats.cid =
			ev->u.get_stats.cid;
		evstat->u.get_stats.sid =
			ev->u.get_stats.sid;
1213 1214 1215 1216
		stats = (struct iscsi_stats *)
			((char*)evstat + sizeof(*evstat));
		memset(stats, 0, sizeof(*stats));

1217
		transport->get_stats(conn, stats);
1218 1219 1220 1221 1222 1223
		actual_size = NLMSG_SPACE(sizeof(struct iscsi_uevent) +
					  sizeof(struct iscsi_stats) +
					  sizeof(struct iscsi_stats_custom) *
					  stats->custom_length);
		actual_size -= sizeof(*nlhstat);
		actual_size = NLMSG_LENGTH(actual_size);
1224
		skb_trim(skbstat, NLMSG_ALIGN(actual_size));
1225 1226
		nlhstat->nlmsg_len = actual_size;

1227 1228
		err = iscsi_multicast_skb(skbstat, ISCSI_NL_GRP_ISCSID,
					  GFP_ATOMIC);
1229 1230 1231
	} while (err < 0 && err != -ECONNREFUSED);

	return err;
1232 1233
}

1234
/**
1235 1236 1237
 * iscsi_session_event - send session destr. completion event
 * @session: iscsi class session
 * @event: type of event
1238
 */
1239 1240
int iscsi_session_event(struct iscsi_cls_session *session,
			enum iscsi_uevent_e event)
1241 1242 1243 1244 1245 1246 1247 1248
{
	struct iscsi_internal *priv;
	struct Scsi_Host *shost;
	struct iscsi_uevent *ev;
	struct sk_buff  *skb;
	struct nlmsghdr *nlh;
	int rc, len = NLMSG_SPACE(sizeof(*ev));

1249
	priv = iscsi_if_transport_lookup(session->transport);
1250 1251 1252 1253
	if (!priv)
		return -EINVAL;
	shost = iscsi_session_to_shost(session);

1254
	skb = alloc_skb(len, GFP_KERNEL);
1255
	if (!skb) {
1256 1257 1258
		iscsi_cls_session_printk(KERN_ERR, session,
					 "Cannot notify userspace of session "
					 "event %u\n", event);
1259 1260 1261
		return -ENOMEM;
	}

1262
	nlh = __nlmsg_put(skb, 0, 0, 0, (len - sizeof(*nlh)), 0);
1263
	ev = NLMSG_DATA(nlh);
1264
	ev->transport_handle = iscsi_handle(session->transport);
1265

1266 1267 1268 1269 1270 1271 1272 1273 1274 1275 1276 1277 1278 1279 1280
	ev->type = event;
	switch (event) {
	case ISCSI_KEVENT_DESTROY_SESSION:
		ev->r.d_session.host_no = shost->host_no;
		ev->r.d_session.sid = session->sid;
		break;
	case ISCSI_KEVENT_CREATE_SESSION:
		ev->r.c_session_ret.host_no = shost->host_no;
		ev->r.c_session_ret.sid = session->sid;
		break;
	case ISCSI_KEVENT_UNBIND_SESSION:
		ev->r.unbind_session.host_no = shost->host_no;
		ev->r.unbind_session.sid = session->sid;
		break;
	default:
1281 1282
		iscsi_cls_session_printk(KERN_ERR, session, "Invalid event "
					 "%u.\n", event);
1283
		kfree_skb(skb);
1284 1285 1286 1287 1288 1289 1290
		return -EINVAL;
	}

	/*
	 * this will occur if the daemon is not up, so we just warn
	 * the user and when the daemon is restarted it will handle it
	 */
1291
	rc = iscsi_multicast_skb(skb, ISCSI_NL_GRP_ISCSID, GFP_KERNEL);
1292
	if (rc == -ESRCH)
1293 1294 1295 1296
		iscsi_cls_session_printk(KERN_ERR, session,
					 "Cannot notify userspace of session "
					 "event %u. Check iscsi daemon\n",
					 event);
1297 1298 1299

	ISCSI_DBG_TRANS_SESSION(session, "Completed handling event %d rc %d\n",
				event, rc);
1300 1301
	return rc;
}
1302
EXPORT_SYMBOL_GPL(iscsi_session_event);
1303

1304
static int
1305 1306
iscsi_if_create_session(struct iscsi_internal *priv, struct iscsi_endpoint *ep,
			struct iscsi_uevent *ev, uint32_t initial_cmdsn,
1307
			uint16_t cmds_max, uint16_t queue_depth)
1308 1309
{
	struct iscsi_transport *transport = priv->iscsi_transport;
1310
	struct iscsi_cls_session *session;
1311
	struct Scsi_Host *shost;
1312

1313
	session = transport->create_session(ep, cmds_max, queue_depth,
1314
					    initial_cmdsn);
1315
	if (!session)
1316
		return -ENOMEM;
1317

1318 1319
	shost = iscsi_session_to_shost(session);
	ev->r.c_session_ret.host_no = shost->host_no;
1320
	ev->r.c_session_ret.sid = session->sid;
1321 1322
	ISCSI_DBG_TRANS_SESSION(session,
				"Completed creating transport session\n");
1323 1324 1325 1326
	return 0;
}

static int
1327
iscsi_if_create_conn(struct iscsi_transport *transport, struct iscsi_uevent *ev)
1328
{
1329
	struct iscsi_cls_conn *conn;
1330
	struct iscsi_cls_session *session;
1331

1332 1333
	session = iscsi_session_lookup(ev->u.c_conn.sid);
	if (!session) {
1334
		printk(KERN_ERR "iscsi: invalid session %d.\n",
1335
		       ev->u.c_conn.sid);
1336
		return -EINVAL;
1337
	}
1338

1339
	conn = transport->create_conn(session, ev->u.c_conn.cid);
1340
	if (!conn) {
1341 1342
		iscsi_cls_session_printk(KERN_ERR, session,
					 "couldn't create a new connection.");
1343
		return -ENOMEM;
1344
	}
1345

1346 1347
	ev->r.c_conn_ret.sid = session->sid;
	ev->r.c_conn_ret.cid = conn->cid;
1348 1349

	ISCSI_DBG_TRANS_CONN(conn, "Completed creating transport conn\n");
1350
	return 0;
Linus Torvalds's avatar
Linus Torvalds committed
1351 1352
}

1353 1354 1355
static int
iscsi_if_destroy_conn(struct iscsi_transport *transport, struct iscsi_uevent *ev)
{
1356
	struct iscsi_cls_conn *conn;
1357

1358
	conn = iscsi_conn_lookup(ev->u.d_conn.sid, ev->u.d_conn.cid);
1359
	if (!conn)
1360
		return -EINVAL;
1361

1362
	ISCSI_DBG_TRANS_CONN(conn, "Destroying transport conn\n");
1363 1364
	if (transport->destroy_conn)
		transport->destroy_conn(conn);
1365

1366
	return 0;
1367 1368
}

1369 1370 1371 1372 1373 1374
static int
iscsi_set_param(struct iscsi_transport *transport, struct iscsi_uevent *ev)
{
	char *data = (char*)ev + sizeof(*ev);
	struct iscsi_cls_conn *conn;
	struct iscsi_cls_session *session;
1375
	int err = 0, value = 0;
1376 1377 1378 1379 1380 1381 1382

	session = iscsi_session_lookup(ev->u.set_param.sid);
	conn = iscsi_conn_lookup(ev->u.set_param.sid, ev->u.set_param.cid);
	if (!conn || !session)
		return -EINVAL;

	switch (ev->u.set_param.param) {
1383
	case ISCSI_PARAM_SESS_RECOVERY_TMO:
1384
		sscanf(data, "%d", &value);
1385
		session->recovery_tmo = value;
1386
		break;
1387
	default:
1388 1389
		err = transport->set_param(conn, ev->u.set_param.param,
					   data, ev->u.set_param.len);
1390 1391 1392 1393 1394
	}

	return err;
}

1395 1396 1397 1398 1399 1400 1401 1402 1403 1404 1405 1406 1407 1408 1409 1410 1411 1412 1413 1414 1415 1416 1417 1418 1419 1420 1421 1422 1423 1424 1425 1426 1427 1428 1429 1430 1431
static int iscsi_if_ep_connect(struct iscsi_transport *transport,
			       struct iscsi_uevent *ev, int msg_type)
{
	struct iscsi_endpoint *ep;
	struct sockaddr *dst_addr;
	struct Scsi_Host *shost = NULL;
	int non_blocking, err = 0;

	if (!transport->ep_connect)
		return -EINVAL;

	if (msg_type == ISCSI_UEVENT_TRANSPORT_EP_CONNECT_THROUGH_HOST) {
		shost = scsi_host_lookup(ev->u.ep_connect_through_host.host_no);
		if (!shost) {
			printk(KERN_ERR "ep connect failed. Could not find "
			       "host no %u\n",
			       ev->u.ep_connect_through_host.host_no);
			return -ENODEV;
		}
		non_blocking = ev->u.ep_connect_through_host.non_blocking;
	} else
		non_blocking = ev->u.ep_connect.non_blocking;

	dst_addr = (struct sockaddr *)((char*)ev + sizeof(*ev));
	ep = transport->ep_connect(shost, dst_addr, non_blocking);
	if (IS_ERR(ep)) {
		err = PTR_ERR(ep);
		goto release_host;
	}

	ev->r.ep_connect_ret.handle = ep->id;
release_host:
	if (shost)
		scsi_host_put(shost);
	return err;
}

1432 1433 1434 1435 1436 1437 1438 1439 1440 1441 1442 1443 1444 1445 1446 1447 1448 1449 1450 1451 1452 1453 1454
static int iscsi_if_ep_disconnect(struct iscsi_transport *transport,
				  u64 ep_handle)
{
	struct iscsi_cls_conn *conn;
	struct iscsi_endpoint *ep;

	if (!transport->ep_disconnect)
		return -EINVAL;

	ep = iscsi_lookup_endpoint(ep_handle);
	if (!ep)
		return -EINVAL;
	conn = ep->conn;
	if (conn) {
		mutex_lock(&conn->ep_mutex);
		conn->ep = NULL;
		mutex_unlock(&conn->ep_mutex);
	}

	transport->ep_disconnect(ep);
	return 0;
}

1455 1456 1457 1458
static int
iscsi_if_transport_ep(struct iscsi_transport *transport,
		      struct iscsi_uevent *ev, int msg_type)
{
1459
	struct iscsi_endpoint *ep;
1460 1461 1462
	int rc = 0;

	switch (msg_type) {
1463
	case ISCSI_UEVENT_TRANSPORT_EP_CONNECT_THROUGH_HOST:
1464
	case ISCSI_UEVENT_TRANSPORT_EP_CONNECT:
1465
		rc = iscsi_if_ep_connect(transport, ev, msg_type);
1466 1467 1468 1469 1470
		break;
	case ISCSI_UEVENT_TRANSPORT_EP_POLL:
		if (!transport->ep_poll)
			return -EINVAL;

1471 1472 1473 1474 1475
		ep = iscsi_lookup_endpoint(ev->u.ep_poll.ep_handle);
		if (!ep)
			return -EINVAL;

		ev->r.retcode = transport->ep_poll(ep,
1476 1477 1478
						   ev->u.ep_poll.timeout_ms);
		break;
	case ISCSI_UEVENT_TRANSPORT_EP_DISCONNECT:
1479 1480
		rc = iscsi_if_ep_disconnect(transport,
					    ev->u.ep_disconnect.ep_handle);
1481 1482 1483 1484 1485
		break;
	}
	return rc;
}

1486 1487 1488 1489
static int
iscsi_tgt_dscvr(struct iscsi_transport *transport,
		struct iscsi_uevent *ev)
{
1490
	struct Scsi_Host *shost;
1491
	struct sockaddr *dst_addr;
1492
	int err;
1493 1494 1495 1496

	if (!transport->tgt_dscvr)
		return -EINVAL;

1497
	shost = scsi_host_lookup(ev->u.tgt_dscvr.host_no);
1498
	if (!shost) {
1499 1500 1501 1502 1503 1504
		printk(KERN_ERR "target discovery could not find host no %u\n",
		       ev->u.tgt_dscvr.host_no);
		return -ENODEV;
	}


1505
	dst_addr = (struct sockaddr *)((char*)ev + sizeof(*ev));
1506 1507 1508 1509
	err = transport->tgt_dscvr(shost, ev->u.tgt_dscvr.type,
				   ev->u.tgt_dscvr.enable, dst_addr);
	scsi_host_put(shost);
	return err;
1510 1511
}

1512 1513 1514 1515 1516 1517 1518 1519 1520 1521 1522 1523
static int
iscsi_set_host_param(struct iscsi_transport *transport,
		     struct iscsi_uevent *ev)
{
	char *data = (char*)ev + sizeof(*ev);
	struct Scsi_Host *shost;
	int err;

	if (!transport->set_host_param)
		return -ENOSYS;

	shost = scsi_host_lookup(ev->u.set_host_param.host_no);
1524
	if (!shost) {
1525 1526 1527 1528 1529 1530 1531 1532 1533 1534 1535
		printk(KERN_ERR "set_host_param could not find host no %u\n",
		       ev->u.set_host_param.host_no);
		return -ENODEV;
	}

	err = transport->set_host_param(shost, ev->u.set_host_param.param,
					data, ev->u.set_host_param.len);
	scsi_host_put(shost);
	return err;
}

1536
static int
1537 1538 1539 1540 1541 1542 1543 1544 1545 1546 1547 1548 1549 1550 1551 1552 1553 1554 1555 1556 1557 1558 1559
iscsi_set_path(struct iscsi_transport *transport, struct iscsi_uevent *ev)
{
	struct Scsi_Host *shost;
	struct iscsi_path *params;
	int err;

	if (!transport->set_path)
		return -ENOSYS;

	shost = scsi_host_lookup(ev->u.set_path.host_no);
	if (!shost) {
		printk(KERN_ERR "set path could not find host no %u\n",
		       ev->u.set_path.host_no);
		return -ENODEV;
	}

	params = (struct iscsi_path *)((char *)ev + sizeof(*ev));
	err = transport->set_path(shost, params);

	scsi_host_put(shost);
	return err;
}

1560 1561 1562 1563 1564 1565 1566 1567 1568 1569 1570 1571 1572 1573 1574 1575 1576 1577 1578 1579 1580 1581 1582 1583
static int
iscsi_set_iface_params(struct iscsi_transport *transport,
		       struct iscsi_uevent *ev)
{
	char *data = (char *)ev + sizeof(*ev);
	struct Scsi_Host *shost;
	int err;

	if (!transport->set_iface_param)
		return -ENOSYS;

	shost = scsi_host_lookup(ev->u.set_iface_params.host_no);
	if (!shost) {
		printk(KERN_ERR "set_iface_params could not find host no %u\n",
		       ev->u.set_iface_params.host_no);
		return -ENODEV;
	}

	err = transport->set_iface_param(shost, data,
					 ev->u.set_iface_params.count);
	scsi_host_put(shost);
	return err;
}

1584 1585
static int
iscsi_if_recv_msg(struct sk_buff *skb, struct nlmsghdr *nlh, uint32_t *group)
1586 1587 1588 1589 1590
{
	int err = 0;
	struct iscsi_uevent *ev = NLMSG_DATA(nlh);
	struct iscsi_transport *transport = NULL;
	struct iscsi_internal *priv;
1591 1592
	struct iscsi_cls_session *session;
	struct iscsi_cls_conn *conn;
1593
	struct iscsi_endpoint *ep = NULL;
1594

1595 1596 1597 1598 1599
	if (nlh->nlmsg_type == ISCSI_UEVENT_PATH_UPDATE)
		*group = ISCSI_NL_GRP_UIP;
	else
		*group = ISCSI_NL_GRP_ISCSID;

1600 1601 1602 1603 1604
	priv = iscsi_if_transport_lookup(iscsi_ptr(ev->transport_handle));
	if (!priv)
		return -EINVAL;
	transport = priv->iscsi_transport;

1605 1606 1607
	if (!try_module_get(transport->owner))
		return -EINVAL;

1608 1609
	switch (nlh->nlmsg_type) {
	case ISCSI_UEVENT_CREATE_SESSION:
1610
		err = iscsi_if_create_session(priv, ep, ev,
1611 1612 1613 1614 1615
					      ev->u.c_session.initial_cmdsn,
					      ev->u.c_session.cmds_max,
					      ev->u.c_session.queue_depth);
		break;
	case ISCSI_UEVENT_CREATE_BOUND_SESSION:
1616
		ep = iscsi_lookup_endpoint(ev->u.c_bound_session.ep_handle);
1617 1618 1619 1620
		if (!ep) {
			err = -EINVAL;
			break;
		}
1621 1622

		err = iscsi_if_create_session(priv, ep, ev,
1623 1624 1625
					ev->u.c_bound_session.initial_cmdsn,
					ev->u.c_bound_session.cmds_max,
					ev->u.c_bound_session.queue_depth);
1626 1627
		break;
	case ISCSI_UEVENT_DESTROY_SESSION:
1628
		session = iscsi_session_lookup(ev->u.d_session.sid);
1629
		if (session)
1630
			transport->destroy_session(session);
1631 1632 1633 1634 1635 1636
		else
			err = -EINVAL;
		break;
	case ISCSI_UEVENT_UNBIND_SESSION:
		session = iscsi_session_lookup(ev->u.d_session.sid);
		if (session)
1637 1638
			scsi_queue_work(iscsi_session_to_shost(session),
					&session->unbind_work);
1639
		else
1640
			err = -EINVAL;
1641 1642 1643 1644 1645 1646 1647 1648
		break;
	case ISCSI_UEVENT_CREATE_CONN:
		err = iscsi_if_create_conn(transport, ev);
		break;
	case ISCSI_UEVENT_DESTROY_CONN:
		err = iscsi_if_destroy_conn(transport, ev);
		break;
	case ISCSI_UEVENT_BIND_CONN:
1649 1650
		session = iscsi_session_lookup(ev->u.b_conn.sid);
		conn = iscsi_conn_lookup(ev->u.b_conn.sid, ev->u.b_conn.cid);
1651

1652 1653 1654 1655
		if (conn && conn->ep)
			iscsi_if_ep_disconnect(transport, conn->ep->id);

		if (!session || !conn) {
1656
			err = -EINVAL;
1657 1658 1659 1660 1661 1662 1663 1664 1665 1666 1667 1668 1669 1670 1671 1672 1673 1674 1675 1676
			break;
		}

		ev->r.retcode =	transport->bind_conn(session, conn,
						ev->u.b_conn.transport_eph,
						ev->u.b_conn.is_leading);
		if (ev->r.retcode || !transport->ep_connect)
			break;

		ep = iscsi_lookup_endpoint(ev->u.b_conn.transport_eph);
		if (ep) {
			ep->conn = conn;

			mutex_lock(&conn->ep_mutex);
			conn->ep = ep;
			mutex_unlock(&conn->ep_mutex);
		} else
			iscsi_cls_conn_printk(KERN_ERR, conn,
					      "Could not set ep conn "
					      "binding\n");
1677 1678
		break;
	case ISCSI_UEVENT_SET_PARAM:
1679
		err = iscsi_set_param(transport, ev);
1680 1681
		break;
	case ISCSI_UEVENT_START_CONN:
1682
		conn = iscsi_conn_lookup(ev->u.start_conn.sid, ev->u.start_conn.cid);
1683 1684 1685 1686
		if (conn)
			ev->r.retcode = transport->start_conn(conn);
		else
			err = -EINVAL;
1687 1688
		break;
	case ISCSI_UEVENT_STOP_CONN:
1689
		conn = iscsi_conn_lookup(ev->u.stop_conn.sid, ev->u.stop_conn.cid);
1690 1691 1692 1693
		if (conn)
			transport->stop_conn(conn, ev->u.stop_conn.flag);
		else
			err = -EINVAL;
1694 1695
		break;
	case ISCSI_UEVENT_SEND_PDU:
1696
		conn = iscsi_conn_lookup(ev->u.send_pdu.sid, ev->u.send_pdu.cid);
1697 1698 1699 1700 1701 1702 1703
		if (conn)
			ev->r.retcode =	transport->send_pdu(conn,
				(struct iscsi_hdr*)((char*)ev + sizeof(*ev)),
				(char*)ev + sizeof(*ev) + ev->u.send_pdu.hdr_size,
				ev->u.send_pdu.data_size);
		else
			err = -EINVAL;
1704 1705
		break;
	case ISCSI_UEVENT_GET_STATS:
1706
		err = iscsi_if_get_stats(transport, nlh);
1707
		break;
1708 1709 1710
	case ISCSI_UEVENT_TRANSPORT_EP_CONNECT:
	case ISCSI_UEVENT_TRANSPORT_EP_POLL:
	case ISCSI_UEVENT_TRANSPORT_EP_DISCONNECT:
1711
	case ISCSI_UEVENT_TRANSPORT_EP_CONNECT_THROUGH_HOST:
1712 1713
		err = iscsi_if_transport_ep(transport, ev, nlh->nlmsg_type);
		break;
1714 1715 1716
	case ISCSI_UEVENT_TGT_DSCVR:
		err = iscsi_tgt_dscvr(transport, ev);
		break;
1717 1718 1719
	case ISCSI_UEVENT_SET_HOST_PARAM:
		err = iscsi_set_host_param(transport, ev);
		break;
1720 1721 1722
	case ISCSI_UEVENT_PATH_UPDATE:
		err = iscsi_set_path(transport, ev);
		break;
1723 1724 1725
	case ISCSI_UEVENT_SET_IFACE_PARAMS:
		err = iscsi_set_iface_params(transport, ev);
		break;
1726
	default:
1727
		err = -ENOSYS;
1728 1729 1730
		break;
	}

1731
	module_put(transport->owner);
1732 1733 1734
	return err;
}

1735
/*
1736 1737
 * Get message from skb.  Each message is processed by iscsi_if_recv_msg.
 * Malformed skbs with wrong lengths or invalid creds are not processed.
1738
 */
1739
static void
1740
iscsi_if_rx(struct sk_buff *skb)
1741
{
1742
	mutex_lock(&rx_queue_mutex);
1743 1744 1745 1746 1747
	while (skb->len >= NLMSG_SPACE(0)) {
		int err;
		uint32_t rlen;
		struct nlmsghdr	*nlh;
		struct iscsi_uevent *ev;
1748
		uint32_t group;
1749 1750 1751 1752 1753

		nlh = nlmsg_hdr(skb);
		if (nlh->nlmsg_len < sizeof(*nlh) ||
		    skb->len < nlh->nlmsg_len) {
			break;
1754 1755
		}

1756 1757 1758 1759
		ev = NLMSG_DATA(nlh);
		rlen = NLMSG_ALIGN(nlh->nlmsg_len);
		if (rlen > skb->len)
			rlen = skb->len;
1760

1761
		err = iscsi_if_recv_msg(skb, nlh, &group);
1762 1763 1764
		if (err) {
			ev->type = ISCSI_KEVENT_IF_ERROR;
			ev->iferror = err;
1765
		}
1766 1767 1768 1769 1770 1771 1772 1773 1774
		do {
			/*
			 * special case for GET_STATS:
			 * on success - sending reply and stats from
			 * inside of if_recv_msg(),
			 * on error - fall through.
			 */
			if (ev->type == ISCSI_UEVENT_GET_STATS && !err)
				break;
1775
			err = iscsi_if_send_reply(group, nlh->nlmsg_seq,
1776 1777 1778
				nlh->nlmsg_type, 0, 0, ev, sizeof(*ev));
		} while (err < 0 && err != -ECONNREFUSED);
		skb_pull(skb, rlen);
1779
	}
1780
	mutex_unlock(&rx_queue_mutex);
1781
}
Linus Torvalds's avatar
Linus Torvalds committed
1782

1783
#define ISCSI_CLASS_ATTR(_prefix,_name,_mode,_show,_store)		\
1784
struct device_attribute dev_attr_##_prefix##_##_name =	\
1785 1786
	__ATTR(_name,_mode,_show,_store)

Linus Torvalds's avatar
Linus Torvalds committed
1787
/*
1788
 * iSCSI connection attrs
Linus Torvalds's avatar
Linus Torvalds committed
1789
 */
1790
#define iscsi_conn_attr_show(param)					\
1791
static ssize_t								\
1792 1793
show_conn_param_##param(struct device *dev, 				\
			struct device_attribute *attr, char *buf)	\
1794
{									\
1795
	struct iscsi_cls_conn *conn = iscsi_dev_to_conn(dev->parent);	\
1796
	struct iscsi_transport *t = conn->transport;			\
1797
	return t->get_conn_param(conn, param, buf);			\
1798 1799
}

1800 1801 1802
#define iscsi_conn_attr(field, param)					\
	iscsi_conn_attr_show(param)					\
static ISCSI_CLASS_ATTR(conn, field, S_IRUGO, show_conn_param_##param,	\
1803
			NULL);
1804

1805 1806 1807 1808 1809 1810 1811 1812 1813
iscsi_conn_attr(max_recv_dlength, ISCSI_PARAM_MAX_RECV_DLENGTH);
iscsi_conn_attr(max_xmit_dlength, ISCSI_PARAM_MAX_XMIT_DLENGTH);
iscsi_conn_attr(header_digest, ISCSI_PARAM_HDRDGST_EN);
iscsi_conn_attr(data_digest, ISCSI_PARAM_DATADGST_EN);
iscsi_conn_attr(ifmarker, ISCSI_PARAM_IFMARKER_EN);
iscsi_conn_attr(ofmarker, ISCSI_PARAM_OFMARKER_EN);
iscsi_conn_attr(persistent_port, ISCSI_PARAM_PERSISTENT_PORT);
iscsi_conn_attr(exp_statsn, ISCSI_PARAM_EXP_STATSN);
iscsi_conn_attr(persistent_address, ISCSI_PARAM_PERSISTENT_ADDRESS);
1814 1815
iscsi_conn_attr(ping_tmo, ISCSI_PARAM_PING_TMO);
iscsi_conn_attr(recv_tmo, ISCSI_PARAM_RECV_TMO);
Linus Torvalds's avatar
Linus Torvalds committed
1816

1817 1818 1819 1820 1821 1822 1823 1824 1825 1826 1827 1828 1829 1830 1831 1832 1833 1834 1835 1836 1837 1838 1839 1840 1841 1842 1843 1844 1845 1846 1847 1848 1849 1850 1851 1852 1853
#define iscsi_conn_ep_attr_show(param)					\
static ssize_t show_conn_ep_param_##param(struct device *dev,		\
					  struct device_attribute *attr,\
					  char *buf)			\
{									\
	struct iscsi_cls_conn *conn = iscsi_dev_to_conn(dev->parent);	\
	struct iscsi_transport *t = conn->transport;			\
	struct iscsi_endpoint *ep;					\
	ssize_t rc;							\
									\
	/*								\
	 * Need to make sure ep_disconnect does not free the LLD's	\
	 * interconnect resources while we are trying to read them.	\
	 */								\
	mutex_lock(&conn->ep_mutex);					\
	ep = conn->ep;							\
	if (!ep && t->ep_connect) {					\
		mutex_unlock(&conn->ep_mutex);				\
		return -ENOTCONN;					\
	}								\
									\
	if (ep)								\
		rc = t->get_ep_param(ep, param, buf);			\
	else								\
		rc = t->get_conn_param(conn, param, buf);		\
	mutex_unlock(&conn->ep_mutex);					\
	return rc;							\
}

#define iscsi_conn_ep_attr(field, param)				\
	iscsi_conn_ep_attr_show(param)					\
static ISCSI_CLASS_ATTR(conn, field, S_IRUGO,				\
			show_conn_ep_param_##param, NULL);

iscsi_conn_ep_attr(address, ISCSI_PARAM_CONN_ADDRESS);
iscsi_conn_ep_attr(port, ISCSI_PARAM_CONN_PORT);

Linus Torvalds's avatar
Linus Torvalds committed
1854
/*
1855
 * iSCSI session attrs
Linus Torvalds's avatar
Linus Torvalds committed
1856
 */
1857
#define iscsi_session_attr_show(param, perm)				\
1858
static ssize_t								\
1859 1860
show_session_param_##param(struct device *dev,				\
			   struct device_attribute *attr, char *buf)	\
1861
{									\
1862 1863
	struct iscsi_cls_session *session = 				\
		iscsi_dev_to_session(dev->parent);			\
1864
	struct iscsi_transport *t = session->transport;			\
1865 1866 1867
									\
	if (perm && !capable(CAP_SYS_ADMIN))				\
		return -EACCES;						\
1868
	return t->get_session_param(session, param, buf);		\
1869 1870
}

1871 1872
#define iscsi_session_attr(field, param, perm)				\
	iscsi_session_attr_show(param, perm)				\
1873
static ISCSI_CLASS_ATTR(sess, field, S_IRUGO, show_session_param_##param, \
1874 1875
			NULL);

1876 1877 1878 1879 1880 1881 1882 1883 1884 1885 1886 1887 1888 1889
iscsi_session_attr(targetname, ISCSI_PARAM_TARGET_NAME, 0);
iscsi_session_attr(initial_r2t, ISCSI_PARAM_INITIAL_R2T_EN, 0);
iscsi_session_attr(max_outstanding_r2t, ISCSI_PARAM_MAX_R2T, 0);
iscsi_session_attr(immediate_data, ISCSI_PARAM_IMM_DATA_EN, 0);
iscsi_session_attr(first_burst_len, ISCSI_PARAM_FIRST_BURST, 0);
iscsi_session_attr(max_burst_len, ISCSI_PARAM_MAX_BURST, 0);
iscsi_session_attr(data_pdu_in_order, ISCSI_PARAM_PDU_INORDER_EN, 0);
iscsi_session_attr(data_seq_in_order, ISCSI_PARAM_DATASEQ_INORDER_EN, 0);
iscsi_session_attr(erl, ISCSI_PARAM_ERL, 0);
iscsi_session_attr(tpgt, ISCSI_PARAM_TPGT, 0);
iscsi_session_attr(username, ISCSI_PARAM_USERNAME, 1);
iscsi_session_attr(username_in, ISCSI_PARAM_USERNAME_IN, 1);
iscsi_session_attr(password, ISCSI_PARAM_PASSWORD, 1);
iscsi_session_attr(password_in, ISCSI_PARAM_PASSWORD_IN, 1);
1890 1891 1892
iscsi_session_attr(fast_abort, ISCSI_PARAM_FAST_ABORT, 0);
iscsi_session_attr(abort_tmo, ISCSI_PARAM_ABORT_TMO, 0);
iscsi_session_attr(lu_reset_tmo, ISCSI_PARAM_LU_RESET_TMO, 0);
1893
iscsi_session_attr(tgt_reset_tmo, ISCSI_PARAM_TGT_RESET_TMO, 0);
1894
iscsi_session_attr(ifacename, ISCSI_PARAM_IFACE_NAME, 0);
1895 1896
iscsi_session_attr(initiatorname, ISCSI_PARAM_INITIATOR_NAME, 0);
iscsi_session_attr(targetalias, ISCSI_PARAM_TARGET_ALIAS, 0);
Linus Torvalds's avatar
Linus Torvalds committed
1897

1898
static ssize_t
1899 1900
show_priv_session_state(struct device *dev, struct device_attribute *attr,
			char *buf)
1901
{
1902
	struct iscsi_cls_session *session = iscsi_dev_to_session(dev->parent);
1903 1904 1905 1906 1907
	return sprintf(buf, "%s\n", iscsi_session_state_name(session->state));
}
static ISCSI_CLASS_ATTR(priv_sess, state, S_IRUGO, show_priv_session_state,
			NULL);

1908 1909
#define iscsi_priv_session_attr_show(field, format)			\
static ssize_t								\
1910 1911
show_priv_session_##field(struct device *dev, 				\
			  struct device_attribute *attr, char *buf)	\
1912
{									\
1913 1914
	struct iscsi_cls_session *session = 				\
			iscsi_dev_to_session(dev->parent);		\
1915 1916
	if (session->field == -1)					\
		return sprintf(buf, "off\n");				\
1917 1918 1919
	return sprintf(buf, format"\n", session->field);		\
}

1920 1921 1922 1923 1924 1925 1926 1927 1928 1929 1930 1931 1932 1933 1934 1935 1936 1937 1938 1939 1940 1941 1942 1943 1944
#define iscsi_priv_session_attr_store(field)				\
static ssize_t								\
store_priv_session_##field(struct device *dev,				\
			   struct device_attribute *attr,		\
			   const char *buf, size_t count)		\
{									\
	int val;							\
	char *cp;							\
	struct iscsi_cls_session *session =				\
		iscsi_dev_to_session(dev->parent);			\
	if ((session->state == ISCSI_SESSION_FREE) ||			\
	    (session->state == ISCSI_SESSION_FAILED))			\
		return -EBUSY;						\
	if (strncmp(buf, "off", 3) == 0)				\
		session->field = -1;					\
	else {								\
		val = simple_strtoul(buf, &cp, 0);			\
		if (*cp != '\0' && *cp != '\n')				\
			return -EINVAL;					\
		session->field = val;					\
	}								\
	return count;							\
}

#define iscsi_priv_session_rw_attr(field, format)			\
1945
	iscsi_priv_session_attr_show(field, format)			\
1946
	iscsi_priv_session_attr_store(field)				\
1947
static ISCSI_CLASS_ATTR(priv_sess, field, S_IRUGO | S_IWUSR,		\
1948 1949 1950
			show_priv_session_##field,			\
			store_priv_session_##field)
iscsi_priv_session_rw_attr(recovery_tmo, "%d");
1951

1952 1953 1954 1955 1956
/*
 * iSCSI host attrs
 */
#define iscsi_host_attr_show(param)					\
static ssize_t								\
1957 1958
show_host_param_##param(struct device *dev, 				\
			struct device_attribute *attr, char *buf)	\
1959
{									\
1960
	struct Scsi_Host *shost = transport_class_to_shost(dev);	\
1961 1962 1963 1964 1965 1966 1967 1968 1969
	struct iscsi_internal *priv = to_iscsi_internal(shost->transportt); \
	return priv->iscsi_transport->get_host_param(shost, param, buf); \
}

#define iscsi_host_attr(field, param)					\
	iscsi_host_attr_show(param)					\
static ISCSI_CLASS_ATTR(host, field, S_IRUGO, show_host_param_##param,	\
			NULL);

1970
iscsi_host_attr(netdev, ISCSI_HOST_PARAM_NETDEV_NAME);
1971
iscsi_host_attr(hwaddress, ISCSI_HOST_PARAM_HWADDRESS);
1972
iscsi_host_attr(ipaddress, ISCSI_HOST_PARAM_IPADDRESS);
1973
iscsi_host_attr(initiatorname, ISCSI_HOST_PARAM_INITIATOR_NAME);
1974

1975 1976
#define SETUP_PRIV_SESSION_RD_ATTR(field)				\
do {									\
1977
	priv->session_attrs[count] = &dev_attr_priv_sess_##field; \
1978 1979 1980
	count++;							\
} while (0)

1981 1982 1983 1984 1985
#define SETUP_PRIV_SESSION_RW_ATTR(field)				\
do {									\
	priv->session_attrs[count] = &dev_attr_priv_sess_##field;	\
	count++;							\
} while (0)
1986

1987 1988 1989
#define SETUP_SESSION_RD_ATTR(field, param_flag)			\
do {									\
	if (tt->param_mask & param_flag) {				\
1990
		priv->session_attrs[count] = &dev_attr_sess_##field; \
Linus Torvalds's avatar
Linus Torvalds committed
1991
		count++;						\
1992 1993 1994 1995 1996 1997
	}								\
} while (0)

#define SETUP_CONN_RD_ATTR(field, param_flag)				\
do {									\
	if (tt->param_mask & param_flag) {				\
1998
		priv->conn_attrs[count] = &dev_attr_conn_##field; \
1999 2000 2001
		count++;						\
	}								\
} while (0)
Linus Torvalds's avatar
Linus Torvalds committed
2002

2003 2004 2005
#define SETUP_HOST_RD_ATTR(field, param_flag)				\
do {									\
	if (tt->host_param_mask & param_flag) {				\
2006
		priv->host_attrs[count] = &dev_attr_host_##field; \
2007 2008 2009 2010
		count++;						\
	}								\
} while (0)

2011 2012
static int iscsi_session_match(struct attribute_container *cont,
			   struct device *dev)
Linus Torvalds's avatar
Linus Torvalds committed
2013
{
2014
	struct iscsi_cls_session *session;
Linus Torvalds's avatar
Linus Torvalds committed
2015
	struct Scsi_Host *shost;
2016 2017 2018 2019
	struct iscsi_internal *priv;

	if (!iscsi_is_session_dev(dev))
		return 0;
Linus Torvalds's avatar
Linus Torvalds committed
2020

2021 2022
	session = iscsi_dev_to_session(dev);
	shost = iscsi_session_to_shost(session);
2023
	if (!shost->transportt)
Linus Torvalds's avatar
Linus Torvalds committed
2024 2025
		return 0;

2026 2027
	priv = to_iscsi_internal(shost->transportt);
	if (priv->session_cont.ac.class != &iscsi_session_class.class)
Linus Torvalds's avatar
Linus Torvalds committed
2028 2029
		return 0;

2030
	return &priv->session_cont.ac == cont;
Linus Torvalds's avatar
Linus Torvalds committed
2031 2032
}

2033 2034 2035
static int iscsi_conn_match(struct attribute_container *cont,
			   struct device *dev)
{
2036 2037
	struct iscsi_cls_session *session;
	struct iscsi_cls_conn *conn;
Linus Torvalds's avatar
Linus Torvalds committed
2038
	struct Scsi_Host *shost;
2039
	struct iscsi_internal *priv;
Linus Torvalds's avatar
Linus Torvalds committed
2040

2041
	if (!iscsi_is_conn_dev(dev))
Linus Torvalds's avatar
Linus Torvalds committed
2042 2043
		return 0;

2044 2045 2046 2047
	conn = iscsi_dev_to_conn(dev);
	session = iscsi_dev_to_session(conn->dev.parent);
	shost = iscsi_session_to_shost(session);

2048
	if (!shost->transportt)
Linus Torvalds's avatar
Linus Torvalds committed
2049 2050
		return 0;

2051 2052 2053
	priv = to_iscsi_internal(shost->transportt);
	if (priv->conn_cont.ac.class != &iscsi_connection_class.class)
		return 0;
Linus Torvalds's avatar
Linus Torvalds committed
2054

2055 2056 2057
	return &priv->conn_cont.ac == cont;
}

2058 2059 2060 2061 2062 2063 2064 2065 2066 2067 2068 2069 2070 2071 2072 2073 2074 2075
static int iscsi_host_match(struct attribute_container *cont,
			    struct device *dev)
{
	struct Scsi_Host *shost;
	struct iscsi_internal *priv;

	if (!scsi_is_host_device(dev))
		return 0;

	shost = dev_to_shost(dev);
	if (!shost->transportt  ||
	    shost->transportt->host_attrs.ac.class != &iscsi_host_class.class)
		return 0;

        priv = to_iscsi_internal(shost->transportt);
        return &priv->t.host_attrs.ac == cont;
}

2076 2077
struct scsi_transport_template *
iscsi_register_transport(struct iscsi_transport *tt)
2078 2079 2080 2081 2082 2083 2084 2085 2086
{
	struct iscsi_internal *priv;
	unsigned long flags;
	int count = 0, err;

	BUG_ON(!tt);

	priv = iscsi_if_transport_lookup(tt);
	if (priv)
2087
		return NULL;
2088

2089
	priv = kzalloc(sizeof(*priv), GFP_KERNEL);
2090
	if (!priv)
2091
		return NULL;
2092 2093
	INIT_LIST_HEAD(&priv->list);
	priv->iscsi_transport = tt;
2094
	priv->t.user_scan = iscsi_user_scan;
2095
	priv->t.create_work_queue = 1;
2096

2097
	priv->dev.class = &iscsi_transport_class;
2098
	dev_set_name(&priv->dev, "%s", tt->name);
2099
	err = device_register(&priv->dev);
2100 2101 2102
	if (err)
		goto free_priv;

2103
	err = sysfs_create_group(&priv->dev.kobj, &iscsi_transport_group);
2104
	if (err)
2105
		goto unregister_dev;
2106

2107 2108 2109 2110
	/* host parameters */
	priv->t.host_attrs.ac.attrs = &priv->host_attrs[0];
	priv->t.host_attrs.ac.class = &iscsi_host_class.class;
	priv->t.host_attrs.ac.match = iscsi_host_match;
2111
	priv->t.host_size = sizeof(struct iscsi_cls_host);
2112 2113
	transport_container_register(&priv->t.host_attrs);

2114
	SETUP_HOST_RD_ATTR(netdev, ISCSI_HOST_NETDEV_NAME);
2115
	SETUP_HOST_RD_ATTR(ipaddress, ISCSI_HOST_IPADDRESS);
2116
	SETUP_HOST_RD_ATTR(hwaddress, ISCSI_HOST_HWADDRESS);
2117
	SETUP_HOST_RD_ATTR(initiatorname, ISCSI_HOST_INITIATOR_NAME);
2118 2119 2120 2121
	BUG_ON(count > ISCSI_HOST_ATTRS);
	priv->host_attrs[count] = NULL;
	count = 0;

2122 2123 2124 2125 2126
	/* connection parameters */
	priv->conn_cont.ac.attrs = &priv->conn_attrs[0];
	priv->conn_cont.ac.class = &iscsi_connection_class.class;
	priv->conn_cont.ac.match = iscsi_conn_match;
	transport_container_register(&priv->conn_cont);
Linus Torvalds's avatar
Linus Torvalds committed
2127

2128 2129 2130 2131 2132 2133 2134 2135
	SETUP_CONN_RD_ATTR(max_recv_dlength, ISCSI_MAX_RECV_DLENGTH);
	SETUP_CONN_RD_ATTR(max_xmit_dlength, ISCSI_MAX_XMIT_DLENGTH);
	SETUP_CONN_RD_ATTR(header_digest, ISCSI_HDRDGST_EN);
	SETUP_CONN_RD_ATTR(data_digest, ISCSI_DATADGST_EN);
	SETUP_CONN_RD_ATTR(ifmarker, ISCSI_IFMARKER_EN);
	SETUP_CONN_RD_ATTR(ofmarker, ISCSI_OFMARKER_EN);
	SETUP_CONN_RD_ATTR(address, ISCSI_CONN_ADDRESS);
	SETUP_CONN_RD_ATTR(port, ISCSI_CONN_PORT);
2136
	SETUP_CONN_RD_ATTR(exp_statsn, ISCSI_EXP_STATSN);
2137 2138
	SETUP_CONN_RD_ATTR(persistent_address, ISCSI_PERSISTENT_ADDRESS);
	SETUP_CONN_RD_ATTR(persistent_port, ISCSI_PERSISTENT_PORT);
2139 2140
	SETUP_CONN_RD_ATTR(ping_tmo, ISCSI_PING_TMO);
	SETUP_CONN_RD_ATTR(recv_tmo, ISCSI_RECV_TMO);
2141 2142 2143

	BUG_ON(count > ISCSI_CONN_ATTRS);
	priv->conn_attrs[count] = NULL;
Linus Torvalds's avatar
Linus Torvalds committed
2144 2145
	count = 0;

2146 2147 2148 2149 2150 2151
	/* session parameters */
	priv->session_cont.ac.attrs = &priv->session_attrs[0];
	priv->session_cont.ac.class = &iscsi_session_class.class;
	priv->session_cont.ac.match = iscsi_session_match;
	transport_container_register(&priv->session_cont);

2152 2153 2154 2155 2156 2157 2158 2159
	SETUP_SESSION_RD_ATTR(initial_r2t, ISCSI_INITIAL_R2T_EN);
	SETUP_SESSION_RD_ATTR(max_outstanding_r2t, ISCSI_MAX_R2T);
	SETUP_SESSION_RD_ATTR(immediate_data, ISCSI_IMM_DATA_EN);
	SETUP_SESSION_RD_ATTR(first_burst_len, ISCSI_FIRST_BURST);
	SETUP_SESSION_RD_ATTR(max_burst_len, ISCSI_MAX_BURST);
	SETUP_SESSION_RD_ATTR(data_pdu_in_order, ISCSI_PDU_INORDER_EN);
	SETUP_SESSION_RD_ATTR(data_seq_in_order, ISCSI_DATASEQ_INORDER_EN);
	SETUP_SESSION_RD_ATTR(erl, ISCSI_ERL);
2160 2161
	SETUP_SESSION_RD_ATTR(targetname, ISCSI_TARGET_NAME);
	SETUP_SESSION_RD_ATTR(tpgt, ISCSI_TPGT);
2162 2163 2164 2165
	SETUP_SESSION_RD_ATTR(password, ISCSI_USERNAME);
	SETUP_SESSION_RD_ATTR(password_in, ISCSI_USERNAME_IN);
	SETUP_SESSION_RD_ATTR(username, ISCSI_PASSWORD);
	SETUP_SESSION_RD_ATTR(username_in, ISCSI_PASSWORD_IN);
2166
	SETUP_SESSION_RD_ATTR(fast_abort, ISCSI_FAST_ABORT);
2167 2168
	SETUP_SESSION_RD_ATTR(abort_tmo, ISCSI_ABORT_TMO);
	SETUP_SESSION_RD_ATTR(lu_reset_tmo,ISCSI_LU_RESET_TMO);
2169
	SETUP_SESSION_RD_ATTR(tgt_reset_tmo,ISCSI_TGT_RESET_TMO);
2170 2171
	SETUP_SESSION_RD_ATTR(ifacename, ISCSI_IFACE_NAME);
	SETUP_SESSION_RD_ATTR(initiatorname, ISCSI_INITIATOR_NAME);
2172
	SETUP_SESSION_RD_ATTR(targetalias, ISCSI_TARGET_ALIAS);
2173
	SETUP_PRIV_SESSION_RW_ATTR(recovery_tmo);
2174
	SETUP_PRIV_SESSION_RD_ATTR(state);
2175

2176 2177 2178 2179 2180 2181 2182 2183
	BUG_ON(count > ISCSI_SESSION_ATTRS);
	priv->session_attrs[count] = NULL;

	spin_lock_irqsave(&iscsi_transport_lock, flags);
	list_add(&priv->list, &iscsi_transports);
	spin_unlock_irqrestore(&iscsi_transport_lock, flags);

	printk(KERN_NOTICE "iscsi: registered transport (%s)\n", tt->name);
2184
	return &priv->t;
Linus Torvalds's avatar
Linus Torvalds committed
2185

2186 2187
unregister_dev:
	device_unregister(&priv->dev);
2188
	return NULL;
2189 2190
free_priv:
	kfree(priv);
2191
	return NULL;
Linus Torvalds's avatar
Linus Torvalds committed
2192
}
2193 2194 2195 2196 2197 2198 2199 2200 2201
EXPORT_SYMBOL_GPL(iscsi_register_transport);

int iscsi_unregister_transport(struct iscsi_transport *tt)
{
	struct iscsi_internal *priv;
	unsigned long flags;

	BUG_ON(!tt);

2202
	mutex_lock(&rx_queue_mutex);
2203 2204 2205 2206 2207 2208 2209 2210 2211 2212

	priv = iscsi_if_transport_lookup(tt);
	BUG_ON (!priv);

	spin_lock_irqsave(&iscsi_transport_lock, flags);
	list_del(&priv->list);
	spin_unlock_irqrestore(&iscsi_transport_lock, flags);

	transport_container_unregister(&priv->conn_cont);
	transport_container_unregister(&priv->session_cont);
2213
	transport_container_unregister(&priv->t.host_attrs);
2214

2215 2216
	sysfs_remove_group(&priv->dev.kobj, &iscsi_transport_group);
	device_unregister(&priv->dev);
2217
	mutex_unlock(&rx_queue_mutex);
Linus Torvalds's avatar
Linus Torvalds committed
2218

2219 2220 2221
	return 0;
}
EXPORT_SYMBOL_GPL(iscsi_unregister_transport);
Linus Torvalds's avatar
Linus Torvalds committed
2222 2223 2224

static __init int iscsi_transport_init(void)
{
2225
	int err;
Linus Torvalds's avatar
Linus Torvalds committed
2226

2227
	printk(KERN_INFO "Loading iSCSI transport class v%s.\n",
2228 2229
		ISCSI_TRANSPORT_VERSION);

2230 2231
	atomic_set(&iscsi_session_nr, 0);

2232
	err = class_register(&iscsi_transport_class);
Linus Torvalds's avatar
Linus Torvalds committed
2233 2234
	if (err)
		return err;
2235

2236
	err = class_register(&iscsi_endpoint_class);
2237 2238 2239
	if (err)
		goto unregister_transport_class;

2240 2241 2242 2243
	err = transport_class_register(&iscsi_host_class);
	if (err)
		goto unregister_endpoint_class;

2244 2245 2246 2247
	err = transport_class_register(&iscsi_connection_class);
	if (err)
		goto unregister_host_class;

2248 2249 2250 2251
	err = transport_class_register(&iscsi_session_class);
	if (err)
		goto unregister_conn_class;

2252 2253
	nls = netlink_kernel_create(&init_net, NETLINK_ISCSI, 1, iscsi_if_rx,
				    NULL, THIS_MODULE);
2254 2255
	if (!nls) {
		err = -ENOBUFS;
2256
		goto unregister_session_class;
2257 2258
	}

2259 2260 2261 2262
	iscsi_eh_timer_workq = create_singlethread_workqueue("iscsi_eh");
	if (!iscsi_eh_timer_workq)
		goto release_nls;

2263
	return 0;
2264

2265
release_nls:
2266
	netlink_kernel_release(nls);
2267 2268 2269 2270
unregister_session_class:
	transport_class_unregister(&iscsi_session_class);
unregister_conn_class:
	transport_class_unregister(&iscsi_connection_class);
2271 2272
unregister_host_class:
	transport_class_unregister(&iscsi_host_class);
2273 2274
unregister_endpoint_class:
	class_unregister(&iscsi_endpoint_class);
2275 2276 2277
unregister_transport_class:
	class_unregister(&iscsi_transport_class);
	return err;
Linus Torvalds's avatar
Linus Torvalds committed
2278 2279 2280 2281
}

static void __exit iscsi_transport_exit(void)
{
2282
	destroy_workqueue(iscsi_eh_timer_workq);
2283
	netlink_kernel_release(nls);
2284 2285
	transport_class_unregister(&iscsi_connection_class);
	transport_class_unregister(&iscsi_session_class);
2286
	transport_class_unregister(&iscsi_host_class);
2287
	class_unregister(&iscsi_endpoint_class);
2288
	class_unregister(&iscsi_transport_class);
Linus Torvalds's avatar
Linus Torvalds committed
2289 2290 2291 2292 2293
}

module_init(iscsi_transport_init);
module_exit(iscsi_transport_exit);

2294 2295 2296 2297
MODULE_AUTHOR("Mike Christie <michaelc@cs.wisc.edu>, "
	      "Dmitry Yusupov <dmitry_yus@yahoo.com>, "
	      "Alex Aizman <itn780@yahoo.com>");
MODULE_DESCRIPTION("iSCSI Transport Interface");
Linus Torvalds's avatar
Linus Torvalds committed
2298
MODULE_LICENSE("GPL");
2299
MODULE_VERSION(ISCSI_TRANSPORT_VERSION);
2300
MODULE_ALIAS_NET_PF_PROTO(PF_NETLINK, NETLINK_ISCSI);