scsi_transport_iscsi.c 58.3 KB
Newer Older
1
/*
L
Linus Torvalds 已提交
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
L
Linus Torvalds 已提交
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>
L
Linus Torvalds 已提交
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>
L
Linus Torvalds 已提交
33

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

38
#define ISCSI_TRANSPORT_VERSION "2.0-870"
L
Linus Torvalds 已提交
39

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

L
Linus Torvalds 已提交
72 73
struct iscsi_internal {
	struct scsi_transport_template t;
74 75
	struct iscsi_transport *iscsi_transport;
	struct list_head list;
76
	struct device dev;
M
Mike Christie 已提交
77

78
	struct device_attribute *host_attrs[ISCSI_HOST_ATTRS + 1];
79
	struct transport_container conn_cont;
80
	struct device_attribute *conn_attrs[ISCSI_CONN_ATTRS + 1];
81
	struct transport_container session_cont;
82
	struct device_attribute *session_attrs[ISCSI_SESSION_ATTRS + 1];
L
Linus Torvalds 已提交
83 84
};

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

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

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

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

102
static void iscsi_transport_release(struct device *dev)
103
{
104
	struct iscsi_internal *priv = dev_to_iscsi_internal(dev);
105 106
	kfree(priv);
}
L
Linus Torvalds 已提交
107

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

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

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

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

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

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

148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172
/*
 * 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);
173
	return sprintf(buf, "%llu\n", (unsigned long long) ep->id);
174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190
}
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);
191
	uint64_t *epid = (uint64_t *) data;
192 193 194 195 196 197 198 199 200

	return *epid == ep->id;
}

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

	for (id = 1; id < ISCSI_MAX_EPID; id++) {
205
		dev = class_find_device(&iscsi_endpoint_class, NULL, &id,
206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221
					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;
222
	dev_set_name(&ep->dev, "ep-%llu", (unsigned long long) id);
223 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
	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)
{
254
	struct iscsi_endpoint *ep;
255 256
	struct device *dev;

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

262 263 264 265 266 267 268
	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;
269 270 271
}
EXPORT_SYMBOL_GPL(iscsi_lookup_endpoint);

M
Mike Christie 已提交
272
static int iscsi_setup_host(struct transport_container *tc, struct device *dev,
273
			    struct device *cdev)
M
Mike Christie 已提交
274 275
{
	struct Scsi_Host *shost = dev_to_shost(dev);
276
	struct iscsi_cls_host *ihost = shost->shost_data;
M
Mike Christie 已提交
277 278

	memset(ihost, 0, sizeof(*ihost));
279
	atomic_set(&ihost->nr_scans, 0);
280
	mutex_init(&ihost->mutex);
M
Mike Christie 已提交
281 282 283 284 285 286
	return 0;
}

static DECLARE_TRANSPORT_CLASS(iscsi_host_class,
			       "iscsi_host",
			       iscsi_setup_host,
287
			       NULL,
M
Mike Christie 已提交
288 289
			       NULL);

290 291
static DECLARE_TRANSPORT_CLASS(iscsi_session_class,
			       "iscsi_session",
L
Linus Torvalds 已提交
292 293 294 295
			       NULL,
			       NULL,
			       NULL);

296 297
static DECLARE_TRANSPORT_CLASS(iscsi_connection_class,
			       "iscsi_connection",
L
Linus Torvalds 已提交
298 299 300
			       NULL,
			       NULL,
			       NULL);
301 302

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

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

310 311 312 313 314 315 316 317 318 319
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)
320 321 322 323 324 325
{
	unsigned long flags;
	struct iscsi_cls_session *sess;

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

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

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

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

367
static const char *iscsi_session_state_name(int state)
368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391
{
	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:
392
		err = DID_IMM_RETRY << 16;
393 394
		break;
	case ISCSI_SESSION_FREE:
395
		err = DID_TRANSPORT_FAILFAST << 16;
396 397 398 399 400 401 402 403 404 405
		break;
	default:
		err = DID_NO_CONNECT << 16;
		break;
	}
	spin_unlock_irqrestore(&session->lock, flags);
	return err;
}
EXPORT_SYMBOL_GPL(iscsi_session_chkready);

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

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

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

422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439
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);

440 441 442 443 444 445 446 447 448 449
/**
 * 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)
{
450
	struct iscsi_cls_host *ihost = shost->shost_data;
451 452 453 454 455 456 457 458
	/*
	 * 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);

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

static int iscsi_user_scan_session(struct device *dev, void *data)
M
Mike Christie 已提交
466
{
467
	struct iscsi_scan_data *scan_data = data;
M
Mike Christie 已提交
468
	struct iscsi_cls_session *session;
469 470 471 472 473 474 475 476 477
	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);
478 479 480

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

481 482
	shost = iscsi_session_to_shost(session);
	ihost = shost->shost_data;
M
Mike Christie 已提交
483 484

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

493 494 495 496 497 498 499 500
	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);
	}
501 502

user_scan_exit:
503
	mutex_unlock(&ihost->mutex);
504
	ISCSI_DBG_TRANS_SESSION(session, "Completed session scan\n");
M
Mike Christie 已提交
505 506 507
	return 0;
}

508 509 510 511 512 513 514 515 516 517 518 519 520
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);
}

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

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

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

D
David Howells 已提交
537
static void session_recovery_timedout(struct work_struct *work)
M
Mike Christie 已提交
538
{
D
David Howells 已提交
539 540 541
	struct iscsi_cls_session *session =
		container_of(work, struct iscsi_cls_session,
			     recovery_work.work);
542
	unsigned long flags;
M
Mike Christie 已提交
543

544 545 546
	iscsi_cls_session_printk(KERN_INFO, session,
				 "session recovery timed out after %d secs\n",
				 session->recovery_tmo);
M
Mike Christie 已提交
547

548 549 550 551 552 553 554 555 556 557 558 559 560
	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);

M
Mike Christie 已提交
561 562 563
	if (session->transport->session_recovery_timedout)
		session->transport->session_recovery_timedout(session);

564
	ISCSI_DBG_TRANS_SESSION(session, "Unblocking SCSI target\n");
M
Mike Christie 已提交
565
	scsi_target_unblock(&session->dev);
566
	ISCSI_DBG_TRANS_SESSION(session, "Completed unblocking SCSI target\n");
M
Mike Christie 已提交
567 568
}

569
static void __iscsi_unblock_session(struct work_struct *work)
570
{
571 572 573
	struct iscsi_cls_session *session =
			container_of(work, struct iscsi_cls_session,
				     unblock_work);
574
	struct Scsi_Host *shost = iscsi_session_to_shost(session);
575
	struct iscsi_cls_host *ihost = shost->shost_data;
576 577
	unsigned long flags;

578
	ISCSI_DBG_TRANS_SESSION(session, "Unblocking session\n");
579 580 581 582 583
	/*
	 * 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);
584 585 586
	spin_lock_irqsave(&session->lock, flags);
	session->state = ISCSI_SESSION_LOGGED_IN;
	spin_unlock_irqrestore(&session->lock, flags);
587 588
	/* start IO */
	scsi_target_unblock(&session->dev);
589 590 591 592 593 594
	/*
	 * 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) {
595
		if (scsi_queue_work(shost, &session->scan_work))
596 597
			atomic_inc(&ihost->nr_scans);
	}
598
	ISCSI_DBG_TRANS_SESSION(session, "Completed unblocking session\n");
599
}
600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615

/**
 * 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);
}
M
Mike Christie 已提交
616 617
EXPORT_SYMBOL_GPL(iscsi_unblock_session);

618
static void __iscsi_block_session(struct work_struct *work)
M
Mike Christie 已提交
619
{
620 621 622
	struct iscsi_cls_session *session =
			container_of(work, struct iscsi_cls_session,
				     block_work);
623 624
	unsigned long flags;

625
	ISCSI_DBG_TRANS_SESSION(session, "Blocking session\n");
626 627 628
	spin_lock_irqsave(&session->lock, flags);
	session->state = ISCSI_SESSION_FAILED;
	spin_unlock_irqrestore(&session->lock, flags);
M
Mike Christie 已提交
629
	scsi_target_block(&session->dev);
630
	ISCSI_DBG_TRANS_SESSION(session, "Completed SCSI target blocking\n");
631 632 633 634
	if (session->recovery_tmo >= 0)
		queue_delayed_work(iscsi_eh_timer_workq,
				   &session->recovery_work,
				   session->recovery_tmo * HZ);
M
Mike Christie 已提交
635
}
636 637 638 639 640

void iscsi_block_session(struct iscsi_cls_session *session)
{
	queue_work(iscsi_eh_timer_workq, &session->block_work);
}
M
Mike Christie 已提交
641 642
EXPORT_SYMBOL_GPL(iscsi_block_session);

M
Mike Christie 已提交
643 644 645 646 647 648
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);
649
	struct iscsi_cls_host *ihost = shost->shost_data;
650
	unsigned long flags;
M
Mike Christie 已提交
651

652 653
	ISCSI_DBG_TRANS_SESSION(session, "Unbinding session\n");

M
Mike Christie 已提交
654 655
	/* Prevent new scans and make sure scanning is not in progress */
	mutex_lock(&ihost->mutex);
656 657 658
	spin_lock_irqsave(&session->lock, flags);
	if (session->target_id == ISCSI_MAX_TARGET) {
		spin_unlock_irqrestore(&session->lock, flags);
M
Mike Christie 已提交
659 660 661
		mutex_unlock(&ihost->mutex);
		return;
	}
662 663
	session->target_id = ISCSI_MAX_TARGET;
	spin_unlock_irqrestore(&session->lock, flags);
M
Mike Christie 已提交
664 665 666 667
	mutex_unlock(&ihost->mutex);

	scsi_remove_target(&session->dev);
	iscsi_session_event(session, ISCSI_KEVENT_UNBIND_SESSION);
668
	ISCSI_DBG_TRANS_SESSION(session, "Completed target removal\n");
M
Mike Christie 已提交
669 670
}

671
struct iscsi_cls_session *
672 673
iscsi_alloc_session(struct Scsi_Host *shost, struct iscsi_transport *transport,
		    int dd_size)
674 675 676
{
	struct iscsi_cls_session *session;

677
	session = kzalloc(sizeof(*session) + dd_size,
678
			  GFP_KERNEL);
679
	if (!session)
680 681
		return NULL;

682
	session->transport = transport;
M
Mike Christie 已提交
683
	session->recovery_tmo = 120;
684
	session->state = ISCSI_SESSION_FREE;
D
David Howells 已提交
685
	INIT_DELAYED_WORK(&session->recovery_work, session_recovery_timedout);
M
Mike Christie 已提交
686
	INIT_LIST_HEAD(&session->sess_list);
687 688
	INIT_WORK(&session->unblock_work, __iscsi_unblock_session);
	INIT_WORK(&session->block_work, __iscsi_block_session);
M
Mike Christie 已提交
689
	INIT_WORK(&session->unbind_work, __iscsi_unbind_session);
690
	INIT_WORK(&session->scan_work, iscsi_scan_session);
691
	spin_lock_init(&session->lock);
692

693 694
	/* this is released in the dev's release function */
	scsi_host_get(shost);
695 696 697
	session->dev.parent = &shost->shost_gendev;
	session->dev.release = iscsi_session_release;
	device_initialize(&session->dev);
698
	if (dd_size)
699
		session->dd_data = &session[1];
700 701

	ISCSI_DBG_TRANS_SESSION(session, "Completed session allocation\n");
702 703 704 705
	return session;
}
EXPORT_SYMBOL_GPL(iscsi_alloc_session);

706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722
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;
}

723
int iscsi_add_session(struct iscsi_cls_session *session, unsigned int target_id)
724 725
{
	struct Scsi_Host *shost = iscsi_session_to_shost(session);
726
	struct iscsi_cls_host *ihost;
M
Mike Christie 已提交
727
	unsigned long flags;
728
	unsigned int id = target_id;
729
	int err;
730

M
Mike Christie 已提交
731
	ihost = shost->shost_data;
732
	session->sid = atomic_add_return(1, &iscsi_session_nr);
733 734 735 736 737 738 739 740 741 742 743 744 745 746

	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);
747
			err = -EOVERFLOW;
748 749 750 751
			goto release_host;
		}
	}
	session->target_id = id;
M
Mike Christie 已提交
752

753
	dev_set_name(&session->dev, "session%u", session->sid);
754
	err = device_add(&session->dev);
755
	if (err) {
756 757
		iscsi_cls_session_printk(KERN_ERR, session,
					 "could not register session's dev\n");
758
		goto release_host;
759 760 761
	}
	transport_register_device(&session->dev);

M
Mike Christie 已提交
762 763 764 765 766
	spin_lock_irqsave(&sesslock, flags);
	list_add(&session->sess_list, &sesslist);
	spin_unlock_irqrestore(&sesslock, flags);

	iscsi_session_event(session, ISCSI_KEVENT_CREATE_SESSION);
767
	ISCSI_DBG_TRANS_SESSION(session, "Completed session adding\n");
768
	return 0;
M
Mike Christie 已提交
769

770 771 772
release_host:
	scsi_host_put(shost);
	return err;
773
}
774
EXPORT_SYMBOL_GPL(iscsi_add_session);
775 776

/**
777 778 779
 * iscsi_create_session - create iscsi class session
 * @shost: scsi host
 * @transport: iscsi transport
780
 * @dd_size: private driver data size
781
 * @target_id: which target
782
 *
783
 * This can be called from a LLD or iscsi_transport.
784
 */
785
struct iscsi_cls_session *
786 787
iscsi_create_session(struct Scsi_Host *shost, struct iscsi_transport *transport,
		     int dd_size, unsigned int target_id)
788 789 790
{
	struct iscsi_cls_session *session;

791
	session = iscsi_alloc_session(shost, transport, dd_size);
792 793 794
	if (!session)
		return NULL;

795
	if (iscsi_add_session(session, target_id)) {
796 797 798 799 800 801 802
		iscsi_free_session(session);
		return NULL;
	}
	return session;
}
EXPORT_SYMBOL_GPL(iscsi_create_session);

M
Mike Christie 已提交
803 804 805 806 807
static void iscsi_conn_release(struct device *dev)
{
	struct iscsi_cls_conn *conn = iscsi_dev_to_conn(dev);
	struct device *parent = conn->dev.parent;

808
	ISCSI_DBG_TRANS_CONN(conn, "Releasing conn\n");
M
Mike Christie 已提交
809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824
	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));
}

825
void iscsi_remove_session(struct iscsi_cls_session *session)
826
{
M
Mike Christie 已提交
827
	struct Scsi_Host *shost = iscsi_session_to_shost(session);
M
Mike Christie 已提交
828 829 830
	unsigned long flags;
	int err;

831 832
	ISCSI_DBG_TRANS_SESSION(session, "Removing session\n");

M
Mike Christie 已提交
833 834 835
	spin_lock_irqsave(&sesslock, flags);
	list_del(&session->sess_list);
	spin_unlock_irqrestore(&sesslock, flags);
M
Mike Christie 已提交
836

837 838 839 840 841
	/* 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);
M
Mike Christie 已提交
842 843 844
	/*
	 * If we are blocked let commands flow again. The lld or iscsi
	 * layer should set up the queuecommand to fail commands.
845 846
	 * We assume that LLD will not be calling block/unblock while
	 * removing the session.
M
Mike Christie 已提交
847
	 */
848 849 850
	spin_lock_irqsave(&session->lock, flags);
	session->state = ISCSI_SESSION_FREE;
	spin_unlock_irqrestore(&session->lock, flags);
851

852 853
	scsi_target_unblock(&session->dev);
	/* flush running scans then delete devices */
854
	scsi_flush_work(shost);
855
	__iscsi_unbind_session(&session->unbind_work);
M
Mike Christie 已提交
856

M
Mike Christie 已提交
857 858 859 860
	/* 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)
861 862 863
		iscsi_cls_session_printk(KERN_ERR, session,
					 "Could not delete all connections "
					 "for session. Error %d.\n", err);
864

865
	transport_unregister_device(&session->dev);
866 867

	ISCSI_DBG_TRANS_SESSION(session, "Completing session removal\n");
868 869 870 871 872 873
	device_del(&session->dev);
}
EXPORT_SYMBOL_GPL(iscsi_remove_session);

void iscsi_free_session(struct iscsi_cls_session *session)
{
874
	ISCSI_DBG_TRANS_SESSION(session, "Freeing session\n");
M
Mike Christie 已提交
875
	iscsi_session_event(session, ISCSI_KEVENT_DESTROY_SESSION);
876
	put_device(&session->dev);
877
}
878 879 880 881 882 883 884 885
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.
886
 */
887 888 889
int iscsi_destroy_session(struct iscsi_cls_session *session)
{
	iscsi_remove_session(session);
890
	ISCSI_DBG_TRANS_SESSION(session, "Completing session destruction\n");
891 892 893
	iscsi_free_session(session);
	return 0;
}
894 895 896 897 898
EXPORT_SYMBOL_GPL(iscsi_destroy_session);

/**
 * iscsi_create_conn - create iscsi class connection
 * @session: iscsi cls session
899
 * @dd_size: private driver data size
900 901 902 903 904
 * @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.
905 906 907 908 909
 *
 * 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.
910
 */
911
struct iscsi_cls_conn *
912
iscsi_create_conn(struct iscsi_cls_session *session, int dd_size, uint32_t cid)
913 914 915
{
	struct iscsi_transport *transport = session->transport;
	struct iscsi_cls_conn *conn;
M
Mike Christie 已提交
916
	unsigned long flags;
917 918
	int err;

919
	conn = kzalloc(sizeof(*conn) + dd_size, GFP_KERNEL);
920 921
	if (!conn)
		return NULL;
922
	if (dd_size)
923 924 925 926
		conn->dd_data = &conn[1];

	INIT_LIST_HEAD(&conn->conn_list);
	conn->transport = transport;
927
	conn->cid = cid;
928 929 930

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

933
	dev_set_name(&conn->dev, "connection%d:%u", session->sid, cid);
934 935 936 937
	conn->dev.parent = &session->dev;
	conn->dev.release = iscsi_conn_release;
	err = device_register(&conn->dev);
	if (err) {
938 939
		iscsi_cls_session_printk(KERN_ERR, session, "could not "
					 "register connection's dev\n");
940 941 942
		goto release_parent_ref;
	}
	transport_register_device(&conn->dev);
M
Mike Christie 已提交
943 944 945 946 947

	spin_lock_irqsave(&connlock, flags);
	list_add(&conn->conn_list, &connlist);
	conn->active = 1;
	spin_unlock_irqrestore(&connlock, flags);
948 949

	ISCSI_DBG_TRANS_CONN(conn, "Completed conn creation\n");
950 951 952 953 954 955 956 957 958 959 960 961 962
	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
963
 * @conn: iscsi cls session
964
 *
M
Mike Christie 已提交
965
 * This can be called from a LLD or iscsi_transport.
966
 */
967 968
int iscsi_destroy_conn(struct iscsi_cls_conn *conn)
{
M
Mike Christie 已提交
969 970 971 972 973 974 975
	unsigned long flags;

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

976
	transport_unregister_device(&conn->dev);
977
	ISCSI_DBG_TRANS_CONN(conn, "Completing conn destruction\n");
978 979 980 981 982 983 984 985
	device_unregister(&conn->dev);
	return 0;
}
EXPORT_SYMBOL_GPL(iscsi_destroy_conn);

/*
 * iscsi interface functions
 */
986 987 988 989 990 991 992 993 994 995 996 997 998 999 1000 1001
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;
}
L
Linus Torvalds 已提交
1002

1003
static int
1004
iscsi_multicast_skb(struct sk_buff *skb, uint32_t group, gfp_t gfp)
1005
{
1006
	return nlmsg_multicast(nls, skb, 0, group, gfp);
L
Linus Torvalds 已提交
1007 1008
}

1009
int iscsi_recv_pdu(struct iscsi_cls_conn *conn, struct iscsi_hdr *hdr,
1010
		   char *data, uint32_t data_size)
L
Linus Torvalds 已提交
1011
{
1012 1013 1014 1015
	struct nlmsghdr	*nlh;
	struct sk_buff *skb;
	struct iscsi_uevent *ev;
	char *pdu;
1016
	struct iscsi_internal *priv;
1017 1018 1019
	int len = NLMSG_SPACE(sizeof(*ev) + sizeof(struct iscsi_hdr) +
			      data_size);

1020 1021 1022 1023
	priv = iscsi_if_transport_lookup(conn->transport);
	if (!priv)
		return -EINVAL;

1024
	skb = alloc_skb(len, GFP_ATOMIC);
1025
	if (!skb) {
1026
		iscsi_conn_error_event(conn, ISCSI_ERR_CONN_FAILED);
1027 1028
		iscsi_cls_conn_printk(KERN_ERR, conn, "can not deliver "
				      "control PDU: OOM\n");
1029 1030
		return -ENOMEM;
	}
L
Linus Torvalds 已提交
1031

1032
	nlh = __nlmsg_put(skb, 0, 0, 0, (len - sizeof(*nlh)), 0);
1033 1034 1035 1036
	ev = NLMSG_DATA(nlh);
	memset(ev, 0, sizeof(*ev));
	ev->transport_handle = iscsi_handle(conn->transport);
	ev->type = ISCSI_KEVENT_RECV_PDU;
1037 1038
	ev->r.recv_req.cid = conn->cid;
	ev->r.recv_req.sid = iscsi_conn_get_sid(conn);
1039 1040 1041
	pdu = (char*)ev + sizeof(*ev);
	memcpy(pdu, hdr, sizeof(struct iscsi_hdr));
	memcpy(pdu + sizeof(struct iscsi_hdr), data, data_size);
L
Linus Torvalds 已提交
1042

1043
	return iscsi_multicast_skb(skb, ISCSI_NL_GRP_ISCSID, GFP_ATOMIC);
L
Linus Torvalds 已提交
1044
}
1045
EXPORT_SYMBOL_GPL(iscsi_recv_pdu);
L
Linus Torvalds 已提交
1046

1047 1048 1049 1050 1051 1052 1053 1054 1055
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);

1056
	skb = alloc_skb(len, GFP_ATOMIC);
1057 1058 1059 1060 1061 1062 1063 1064 1065 1066 1067 1068 1069 1070 1071 1072 1073 1074 1075 1076 1077
	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);

1078
	return iscsi_multicast_skb(skb, ISCSI_NL_GRP_UIP, GFP_ATOMIC);
1079 1080 1081
}
EXPORT_SYMBOL_GPL(iscsi_offload_mesg);

1082
void iscsi_conn_error_event(struct iscsi_cls_conn *conn, enum iscsi_err error)
L
Linus Torvalds 已提交
1083
{
1084 1085 1086
	struct nlmsghdr	*nlh;
	struct sk_buff	*skb;
	struct iscsi_uevent *ev;
1087
	struct iscsi_internal *priv;
1088 1089
	int len = NLMSG_SPACE(sizeof(*ev));

1090 1091 1092 1093
	priv = iscsi_if_transport_lookup(conn->transport);
	if (!priv)
		return;

1094
	skb = alloc_skb(len, GFP_ATOMIC);
1095
	if (!skb) {
1096 1097
		iscsi_cls_conn_printk(KERN_ERR, conn, "gracefully ignored "
				      "conn error (%d)\n", error);
1098 1099 1100
		return;
	}

1101
	nlh = __nlmsg_put(skb, 0, 0, 0, (len - sizeof(*nlh)), 0);
1102 1103 1104 1105
	ev = NLMSG_DATA(nlh);
	ev->transport_handle = iscsi_handle(conn->transport);
	ev->type = ISCSI_KEVENT_CONN_ERROR;
	ev->r.connerror.error = error;
1106 1107
	ev->r.connerror.cid = conn->cid;
	ev->r.connerror.sid = iscsi_conn_get_sid(conn);
L
Linus Torvalds 已提交
1108

1109
	iscsi_multicast_skb(skb, ISCSI_NL_GRP_ISCSID, GFP_ATOMIC);
L
Linus Torvalds 已提交
1110

1111 1112
	iscsi_cls_conn_printk(KERN_INFO, conn, "detected conn error (%d)\n",
			      error);
1113
}
1114
EXPORT_SYMBOL_GPL(iscsi_conn_error_event);
1115 1116

static int
1117 1118
iscsi_if_send_reply(uint32_t group, int seq, int type, int done, int multi,
		    void *payload, int size)
1119 1120 1121 1122 1123 1124 1125
{
	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;

1126
	skb = alloc_skb(len, GFP_ATOMIC);
1127 1128 1129 1130
	if (!skb) {
		printk(KERN_ERR "Could not allocate skb to send reply.\n");
		return -ENOMEM;
	}
1131

1132
	nlh = __nlmsg_put(skb, 0, 0, t, (len - sizeof(*nlh)), 0);
1133 1134
	nlh->nlmsg_flags = flags;
	memcpy(NLMSG_DATA(nlh), payload, size);
1135
	return iscsi_multicast_skb(skb, group, GFP_ATOMIC);
L
Linus Torvalds 已提交
1136 1137
}

1138
static int
1139
iscsi_if_get_stats(struct iscsi_transport *transport, struct nlmsghdr *nlh)
1140 1141 1142 1143 1144 1145 1146
{
	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;
1147
	struct iscsi_internal *priv;
1148 1149 1150 1151 1152
	int len = NLMSG_SPACE(sizeof(*ev) +
			      sizeof(struct iscsi_stats) +
			      sizeof(struct iscsi_stats_custom) *
			      ISCSI_STATS_CUSTOM_MAX);
	int err = 0;
1153

1154 1155 1156 1157
	priv = iscsi_if_transport_lookup(transport);
	if (!priv)
		return -EINVAL;

1158
	conn = iscsi_conn_lookup(ev->u.get_stats.sid, ev->u.get_stats.cid);
1159 1160
	if (!conn)
		return -EEXIST;
1161

1162 1163
	do {
		int actual_size;
1164

1165
		skbstat = alloc_skb(len, GFP_ATOMIC);
1166
		if (!skbstat) {
1167 1168
			iscsi_cls_conn_printk(KERN_ERR, conn, "can not "
					      "deliver stats: OOM\n");
1169 1170 1171
			return -ENOMEM;
		}

1172
		nlhstat = __nlmsg_put(skbstat, 0, 0, 0,
1173 1174 1175 1176 1177
				      (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;
1178 1179 1180 1181
		evstat->u.get_stats.cid =
			ev->u.get_stats.cid;
		evstat->u.get_stats.sid =
			ev->u.get_stats.sid;
1182 1183 1184 1185
		stats = (struct iscsi_stats *)
			((char*)evstat + sizeof(*evstat));
		memset(stats, 0, sizeof(*stats));

1186
		transport->get_stats(conn, stats);
1187 1188 1189 1190 1191 1192
		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);
1193
		skb_trim(skbstat, NLMSG_ALIGN(actual_size));
1194 1195
		nlhstat->nlmsg_len = actual_size;

1196 1197
		err = iscsi_multicast_skb(skbstat, ISCSI_NL_GRP_ISCSID,
					  GFP_ATOMIC);
1198 1199 1200
	} while (err < 0 && err != -ECONNREFUSED);

	return err;
1201 1202
}

1203
/**
M
Mike Christie 已提交
1204 1205 1206
 * iscsi_session_event - send session destr. completion event
 * @session: iscsi class session
 * @event: type of event
1207
 */
M
Mike Christie 已提交
1208 1209
int iscsi_session_event(struct iscsi_cls_session *session,
			enum iscsi_uevent_e event)
1210 1211 1212 1213 1214 1215 1216 1217
{
	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));

M
Mike Christie 已提交
1218
	priv = iscsi_if_transport_lookup(session->transport);
1219 1220 1221 1222
	if (!priv)
		return -EINVAL;
	shost = iscsi_session_to_shost(session);

1223
	skb = alloc_skb(len, GFP_KERNEL);
1224
	if (!skb) {
1225 1226 1227
		iscsi_cls_session_printk(KERN_ERR, session,
					 "Cannot notify userspace of session "
					 "event %u\n", event);
1228 1229 1230
		return -ENOMEM;
	}

1231
	nlh = __nlmsg_put(skb, 0, 0, 0, (len - sizeof(*nlh)), 0);
1232
	ev = NLMSG_DATA(nlh);
M
Mike Christie 已提交
1233
	ev->transport_handle = iscsi_handle(session->transport);
1234

M
Mike Christie 已提交
1235 1236 1237 1238 1239 1240 1241 1242 1243 1244 1245 1246 1247 1248 1249
	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:
1250 1251
		iscsi_cls_session_printk(KERN_ERR, session, "Invalid event "
					 "%u.\n", event);
M
Mike Christie 已提交
1252
		kfree_skb(skb);
1253 1254 1255 1256 1257 1258 1259
		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
	 */
1260
	rc = iscsi_multicast_skb(skb, ISCSI_NL_GRP_ISCSID, GFP_KERNEL);
1261
	if (rc == -ESRCH)
1262 1263 1264 1265
		iscsi_cls_session_printk(KERN_ERR, session,
					 "Cannot notify userspace of session "
					 "event %u. Check iscsi daemon\n",
					 event);
1266 1267 1268

	ISCSI_DBG_TRANS_SESSION(session, "Completed handling event %d rc %d\n",
				event, rc);
1269 1270
	return rc;
}
M
Mike Christie 已提交
1271
EXPORT_SYMBOL_GPL(iscsi_session_event);
1272

1273
static int
1274 1275
iscsi_if_create_session(struct iscsi_internal *priv, struct iscsi_endpoint *ep,
			struct iscsi_uevent *ev, uint32_t initial_cmdsn,
1276
			uint16_t cmds_max, uint16_t queue_depth)
1277 1278
{
	struct iscsi_transport *transport = priv->iscsi_transport;
1279
	struct iscsi_cls_session *session;
1280
	struct Scsi_Host *shost;
1281

1282
	session = transport->create_session(ep, cmds_max, queue_depth,
1283
					    initial_cmdsn);
1284
	if (!session)
1285
		return -ENOMEM;
1286

1287 1288
	shost = iscsi_session_to_shost(session);
	ev->r.c_session_ret.host_no = shost->host_no;
1289
	ev->r.c_session_ret.sid = session->sid;
1290 1291
	ISCSI_DBG_TRANS_SESSION(session,
				"Completed creating transport session\n");
1292 1293 1294 1295
	return 0;
}

static int
1296
iscsi_if_create_conn(struct iscsi_transport *transport, struct iscsi_uevent *ev)
1297
{
1298
	struct iscsi_cls_conn *conn;
1299
	struct iscsi_cls_session *session;
1300

1301 1302
	session = iscsi_session_lookup(ev->u.c_conn.sid);
	if (!session) {
1303
		printk(KERN_ERR "iscsi: invalid session %d.\n",
1304
		       ev->u.c_conn.sid);
1305
		return -EINVAL;
1306
	}
1307

1308
	conn = transport->create_conn(session, ev->u.c_conn.cid);
1309
	if (!conn) {
1310 1311
		iscsi_cls_session_printk(KERN_ERR, session,
					 "couldn't create a new connection.");
1312
		return -ENOMEM;
1313
	}
1314

1315 1316
	ev->r.c_conn_ret.sid = session->sid;
	ev->r.c_conn_ret.cid = conn->cid;
1317 1318

	ISCSI_DBG_TRANS_CONN(conn, "Completed creating transport conn\n");
1319
	return 0;
L
Linus Torvalds 已提交
1320 1321
}

1322 1323 1324
static int
iscsi_if_destroy_conn(struct iscsi_transport *transport, struct iscsi_uevent *ev)
{
1325
	struct iscsi_cls_conn *conn;
1326

1327
	conn = iscsi_conn_lookup(ev->u.d_conn.sid, ev->u.d_conn.cid);
1328
	if (!conn)
1329
		return -EINVAL;
1330

1331
	ISCSI_DBG_TRANS_CONN(conn, "Destroying transport conn\n");
1332 1333
	if (transport->destroy_conn)
		transport->destroy_conn(conn);
1334

1335
	return 0;
1336 1337
}

1338 1339 1340 1341 1342 1343
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;
1344
	int err = 0, value = 0;
1345 1346 1347 1348 1349 1350 1351

	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) {
M
Mike Christie 已提交
1352
	case ISCSI_PARAM_SESS_RECOVERY_TMO:
1353
		sscanf(data, "%d", &value);
1354
		session->recovery_tmo = value;
M
Mike Christie 已提交
1355
		break;
1356
	default:
1357 1358
		err = transport->set_param(conn, ev->u.set_param.param,
					   data, ev->u.set_param.len);
1359 1360 1361 1362 1363
	}

	return err;
}

1364 1365 1366 1367 1368 1369 1370 1371 1372 1373 1374 1375 1376 1377 1378 1379 1380 1381 1382 1383 1384 1385 1386 1387 1388 1389 1390 1391 1392 1393 1394 1395 1396 1397 1398 1399 1400
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;
}

1401 1402 1403 1404
static int
iscsi_if_transport_ep(struct iscsi_transport *transport,
		      struct iscsi_uevent *ev, int msg_type)
{
1405
	struct iscsi_endpoint *ep;
1406 1407 1408
	int rc = 0;

	switch (msg_type) {
1409
	case ISCSI_UEVENT_TRANSPORT_EP_CONNECT_THROUGH_HOST:
1410
	case ISCSI_UEVENT_TRANSPORT_EP_CONNECT:
1411
		rc = iscsi_if_ep_connect(transport, ev, msg_type);
1412 1413 1414 1415 1416
		break;
	case ISCSI_UEVENT_TRANSPORT_EP_POLL:
		if (!transport->ep_poll)
			return -EINVAL;

1417 1418 1419 1420 1421
		ep = iscsi_lookup_endpoint(ev->u.ep_poll.ep_handle);
		if (!ep)
			return -EINVAL;

		ev->r.retcode = transport->ep_poll(ep,
1422 1423 1424 1425 1426 1427
						   ev->u.ep_poll.timeout_ms);
		break;
	case ISCSI_UEVENT_TRANSPORT_EP_DISCONNECT:
		if (!transport->ep_disconnect)
			return -EINVAL;

1428 1429 1430 1431 1432
		ep = iscsi_lookup_endpoint(ev->u.ep_disconnect.ep_handle);
		if (!ep)
			return -EINVAL;

		transport->ep_disconnect(ep);
1433 1434 1435 1436 1437
		break;
	}
	return rc;
}

1438 1439 1440 1441
static int
iscsi_tgt_dscvr(struct iscsi_transport *transport,
		struct iscsi_uevent *ev)
{
1442
	struct Scsi_Host *shost;
1443
	struct sockaddr *dst_addr;
1444
	int err;
1445 1446 1447 1448

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

1449
	shost = scsi_host_lookup(ev->u.tgt_dscvr.host_no);
1450
	if (!shost) {
1451 1452 1453 1454 1455 1456
		printk(KERN_ERR "target discovery could not find host no %u\n",
		       ev->u.tgt_dscvr.host_no);
		return -ENODEV;
	}


1457
	dst_addr = (struct sockaddr *)((char*)ev + sizeof(*ev));
1458 1459 1460 1461
	err = transport->tgt_dscvr(shost, ev->u.tgt_dscvr.type,
				   ev->u.tgt_dscvr.enable, dst_addr);
	scsi_host_put(shost);
	return err;
1462 1463
}

1464 1465 1466 1467 1468 1469 1470 1471 1472 1473 1474 1475
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);
1476
	if (!shost) {
1477 1478 1479 1480 1481 1482 1483 1484 1485 1486 1487
		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;
}

1488
static int
1489 1490 1491 1492 1493 1494 1495 1496 1497 1498 1499 1500 1501 1502 1503 1504 1505 1506 1507 1508 1509 1510 1511 1512 1513
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;
}

static int
iscsi_if_recv_msg(struct sk_buff *skb, struct nlmsghdr *nlh, uint32_t *group)
1514 1515 1516 1517 1518
{
	int err = 0;
	struct iscsi_uevent *ev = NLMSG_DATA(nlh);
	struct iscsi_transport *transport = NULL;
	struct iscsi_internal *priv;
1519 1520
	struct iscsi_cls_session *session;
	struct iscsi_cls_conn *conn;
1521
	struct iscsi_endpoint *ep = NULL;
1522

1523 1524 1525 1526 1527
	if (nlh->nlmsg_type == ISCSI_UEVENT_PATH_UPDATE)
		*group = ISCSI_NL_GRP_UIP;
	else
		*group = ISCSI_NL_GRP_ISCSID;

1528 1529 1530 1531 1532
	priv = iscsi_if_transport_lookup(iscsi_ptr(ev->transport_handle));
	if (!priv)
		return -EINVAL;
	transport = priv->iscsi_transport;

1533 1534 1535
	if (!try_module_get(transport->owner))
		return -EINVAL;

1536 1537
	switch (nlh->nlmsg_type) {
	case ISCSI_UEVENT_CREATE_SESSION:
1538
		err = iscsi_if_create_session(priv, ep, ev,
1539 1540 1541 1542 1543
					      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:
1544
		ep = iscsi_lookup_endpoint(ev->u.c_bound_session.ep_handle);
1545 1546 1547 1548
		if (!ep) {
			err = -EINVAL;
			break;
		}
1549 1550

		err = iscsi_if_create_session(priv, ep, ev,
1551 1552 1553
					ev->u.c_bound_session.initial_cmdsn,
					ev->u.c_bound_session.cmds_max,
					ev->u.c_bound_session.queue_depth);
1554 1555
		break;
	case ISCSI_UEVENT_DESTROY_SESSION:
1556
		session = iscsi_session_lookup(ev->u.d_session.sid);
M
Mike Christie 已提交
1557
		if (session)
1558
			transport->destroy_session(session);
M
Mike Christie 已提交
1559 1560 1561 1562 1563 1564
		else
			err = -EINVAL;
		break;
	case ISCSI_UEVENT_UNBIND_SESSION:
		session = iscsi_session_lookup(ev->u.d_session.sid);
		if (session)
1565 1566
			scsi_queue_work(iscsi_session_to_shost(session),
					&session->unbind_work);
M
Mike Christie 已提交
1567
		else
1568
			err = -EINVAL;
1569 1570 1571 1572 1573 1574 1575 1576
		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:
1577 1578
		session = iscsi_session_lookup(ev->u.b_conn.sid);
		conn = iscsi_conn_lookup(ev->u.b_conn.sid, ev->u.b_conn.cid);
1579 1580 1581

		if (session && conn)
			ev->r.retcode =	transport->bind_conn(session, conn,
1582
					ev->u.b_conn.transport_eph,
1583 1584 1585
					ev->u.b_conn.is_leading);
		else
			err = -EINVAL;
1586 1587
		break;
	case ISCSI_UEVENT_SET_PARAM:
1588
		err = iscsi_set_param(transport, ev);
1589 1590
		break;
	case ISCSI_UEVENT_START_CONN:
1591
		conn = iscsi_conn_lookup(ev->u.start_conn.sid, ev->u.start_conn.cid);
1592 1593 1594 1595
		if (conn)
			ev->r.retcode = transport->start_conn(conn);
		else
			err = -EINVAL;
1596 1597
		break;
	case ISCSI_UEVENT_STOP_CONN:
1598
		conn = iscsi_conn_lookup(ev->u.stop_conn.sid, ev->u.stop_conn.cid);
1599 1600 1601 1602
		if (conn)
			transport->stop_conn(conn, ev->u.stop_conn.flag);
		else
			err = -EINVAL;
1603 1604
		break;
	case ISCSI_UEVENT_SEND_PDU:
1605
		conn = iscsi_conn_lookup(ev->u.send_pdu.sid, ev->u.send_pdu.cid);
1606 1607 1608 1609 1610 1611 1612
		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;
1613 1614
		break;
	case ISCSI_UEVENT_GET_STATS:
1615
		err = iscsi_if_get_stats(transport, nlh);
1616
		break;
1617 1618 1619
	case ISCSI_UEVENT_TRANSPORT_EP_CONNECT:
	case ISCSI_UEVENT_TRANSPORT_EP_POLL:
	case ISCSI_UEVENT_TRANSPORT_EP_DISCONNECT:
1620
	case ISCSI_UEVENT_TRANSPORT_EP_CONNECT_THROUGH_HOST:
1621 1622
		err = iscsi_if_transport_ep(transport, ev, nlh->nlmsg_type);
		break;
1623 1624 1625
	case ISCSI_UEVENT_TGT_DSCVR:
		err = iscsi_tgt_dscvr(transport, ev);
		break;
1626 1627 1628
	case ISCSI_UEVENT_SET_HOST_PARAM:
		err = iscsi_set_host_param(transport, ev);
		break;
1629 1630 1631
	case ISCSI_UEVENT_PATH_UPDATE:
		err = iscsi_set_path(transport, ev);
		break;
1632
	default:
1633
		err = -ENOSYS;
1634 1635 1636
		break;
	}

1637
	module_put(transport->owner);
1638 1639 1640
	return err;
}

1641
/*
1642 1643
 * Get message from skb.  Each message is processed by iscsi_if_recv_msg.
 * Malformed skbs with wrong lengths or invalid creds are not processed.
1644
 */
1645
static void
1646
iscsi_if_rx(struct sk_buff *skb)
1647
{
1648
	mutex_lock(&rx_queue_mutex);
1649 1650 1651 1652 1653
	while (skb->len >= NLMSG_SPACE(0)) {
		int err;
		uint32_t rlen;
		struct nlmsghdr	*nlh;
		struct iscsi_uevent *ev;
1654
		uint32_t group;
1655 1656 1657 1658 1659

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

1662 1663 1664 1665
		ev = NLMSG_DATA(nlh);
		rlen = NLMSG_ALIGN(nlh->nlmsg_len);
		if (rlen > skb->len)
			rlen = skb->len;
1666

1667
		err = iscsi_if_recv_msg(skb, nlh, &group);
1668 1669 1670
		if (err) {
			ev->type = ISCSI_KEVENT_IF_ERROR;
			ev->iferror = err;
1671
		}
1672 1673 1674 1675 1676 1677 1678 1679 1680
		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;
1681
			err = iscsi_if_send_reply(group, nlh->nlmsg_seq,
1682 1683 1684
				nlh->nlmsg_type, 0, 0, ev, sizeof(*ev));
		} while (err < 0 && err != -ECONNREFUSED);
		skb_pull(skb, rlen);
1685
	}
1686
	mutex_unlock(&rx_queue_mutex);
1687
}
L
Linus Torvalds 已提交
1688

1689
#define ISCSI_CLASS_ATTR(_prefix,_name,_mode,_show,_store)		\
1690
struct device_attribute dev_attr_##_prefix##_##_name =	\
1691 1692
	__ATTR(_name,_mode,_show,_store)

L
Linus Torvalds 已提交
1693
/*
1694
 * iSCSI connection attrs
L
Linus Torvalds 已提交
1695
 */
1696
#define iscsi_conn_attr_show(param)					\
1697
static ssize_t								\
1698 1699
show_conn_param_##param(struct device *dev, 				\
			struct device_attribute *attr, char *buf)	\
1700
{									\
1701
	struct iscsi_cls_conn *conn = iscsi_dev_to_conn(dev->parent);	\
1702
	struct iscsi_transport *t = conn->transport;			\
1703
	return t->get_conn_param(conn, param, buf);			\
1704 1705
}

1706 1707 1708
#define iscsi_conn_attr(field, param)					\
	iscsi_conn_attr_show(param)					\
static ISCSI_CLASS_ATTR(conn, field, S_IRUGO, show_conn_param_##param,	\
1709
			NULL);
1710

1711 1712 1713 1714 1715 1716 1717 1718 1719 1720 1721
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(port, ISCSI_PARAM_CONN_PORT);
iscsi_conn_attr(exp_statsn, ISCSI_PARAM_EXP_STATSN);
iscsi_conn_attr(persistent_address, ISCSI_PARAM_PERSISTENT_ADDRESS);
iscsi_conn_attr(address, ISCSI_PARAM_CONN_ADDRESS);
1722 1723
iscsi_conn_attr(ping_tmo, ISCSI_PARAM_PING_TMO);
iscsi_conn_attr(recv_tmo, ISCSI_PARAM_RECV_TMO);
L
Linus Torvalds 已提交
1724 1725

/*
1726
 * iSCSI session attrs
L
Linus Torvalds 已提交
1727
 */
1728
#define iscsi_session_attr_show(param, perm)				\
1729
static ssize_t								\
1730 1731
show_session_param_##param(struct device *dev,				\
			   struct device_attribute *attr, char *buf)	\
1732
{									\
1733 1734
	struct iscsi_cls_session *session = 				\
		iscsi_dev_to_session(dev->parent);			\
1735
	struct iscsi_transport *t = session->transport;			\
1736 1737 1738
									\
	if (perm && !capable(CAP_SYS_ADMIN))				\
		return -EACCES;						\
1739
	return t->get_session_param(session, param, buf);		\
1740 1741
}

1742 1743
#define iscsi_session_attr(field, param, perm)				\
	iscsi_session_attr_show(param, perm)				\
1744
static ISCSI_CLASS_ATTR(sess, field, S_IRUGO, show_session_param_##param, \
1745 1746
			NULL);

1747 1748 1749 1750 1751 1752 1753 1754 1755 1756 1757 1758 1759 1760
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);
1761 1762 1763
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);
1764
iscsi_session_attr(tgt_reset_tmo, ISCSI_PARAM_TGT_RESET_TMO, 0);
1765
iscsi_session_attr(ifacename, ISCSI_PARAM_IFACE_NAME, 0);
1766 1767
iscsi_session_attr(initiatorname, ISCSI_PARAM_INITIATOR_NAME, 0);
iscsi_session_attr(targetalias, ISCSI_PARAM_TARGET_ALIAS, 0);
L
Linus Torvalds 已提交
1768

1769
static ssize_t
1770 1771
show_priv_session_state(struct device *dev, struct device_attribute *attr,
			char *buf)
1772
{
1773
	struct iscsi_cls_session *session = iscsi_dev_to_session(dev->parent);
1774 1775 1776 1777 1778
	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);

1779 1780
#define iscsi_priv_session_attr_show(field, format)			\
static ssize_t								\
1781 1782
show_priv_session_##field(struct device *dev, 				\
			  struct device_attribute *attr, char *buf)	\
1783
{									\
1784 1785
	struct iscsi_cls_session *session = 				\
			iscsi_dev_to_session(dev->parent);		\
1786 1787 1788 1789 1790 1791 1792
	return sprintf(buf, format"\n", session->field);		\
}

#define iscsi_priv_session_attr(field, format)				\
	iscsi_priv_session_attr_show(field, format)			\
static ISCSI_CLASS_ATTR(priv_sess, field, S_IRUGO, show_priv_session_##field, \
			NULL)
M
Mike Christie 已提交
1793
iscsi_priv_session_attr(recovery_tmo, "%d");
1794

1795 1796 1797 1798 1799
/*
 * iSCSI host attrs
 */
#define iscsi_host_attr_show(param)					\
static ssize_t								\
1800 1801
show_host_param_##param(struct device *dev, 				\
			struct device_attribute *attr, char *buf)	\
1802
{									\
1803
	struct Scsi_Host *shost = transport_class_to_shost(dev);	\
1804 1805 1806 1807 1808 1809 1810 1811 1812
	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);

1813
iscsi_host_attr(netdev, ISCSI_HOST_PARAM_NETDEV_NAME);
1814
iscsi_host_attr(hwaddress, ISCSI_HOST_PARAM_HWADDRESS);
1815
iscsi_host_attr(ipaddress, ISCSI_HOST_PARAM_IPADDRESS);
1816
iscsi_host_attr(initiatorname, ISCSI_HOST_PARAM_INITIATOR_NAME);
1817

1818 1819
#define SETUP_PRIV_SESSION_RD_ATTR(field)				\
do {									\
1820
	priv->session_attrs[count] = &dev_attr_priv_sess_##field; \
1821 1822 1823
	count++;							\
} while (0)

1824

1825 1826 1827
#define SETUP_SESSION_RD_ATTR(field, param_flag)			\
do {									\
	if (tt->param_mask & param_flag) {				\
1828
		priv->session_attrs[count] = &dev_attr_sess_##field; \
L
Linus Torvalds 已提交
1829
		count++;						\
1830 1831 1832 1833 1834 1835
	}								\
} while (0)

#define SETUP_CONN_RD_ATTR(field, param_flag)				\
do {									\
	if (tt->param_mask & param_flag) {				\
1836
		priv->conn_attrs[count] = &dev_attr_conn_##field; \
1837 1838 1839
		count++;						\
	}								\
} while (0)
L
Linus Torvalds 已提交
1840

1841 1842 1843
#define SETUP_HOST_RD_ATTR(field, param_flag)				\
do {									\
	if (tt->host_param_mask & param_flag) {				\
1844
		priv->host_attrs[count] = &dev_attr_host_##field; \
1845 1846 1847 1848
		count++;						\
	}								\
} while (0)

1849 1850
static int iscsi_session_match(struct attribute_container *cont,
			   struct device *dev)
L
Linus Torvalds 已提交
1851
{
1852
	struct iscsi_cls_session *session;
L
Linus Torvalds 已提交
1853
	struct Scsi_Host *shost;
1854 1855 1856 1857
	struct iscsi_internal *priv;

	if (!iscsi_is_session_dev(dev))
		return 0;
L
Linus Torvalds 已提交
1858

1859 1860
	session = iscsi_dev_to_session(dev);
	shost = iscsi_session_to_shost(session);
1861
	if (!shost->transportt)
L
Linus Torvalds 已提交
1862 1863
		return 0;

1864 1865
	priv = to_iscsi_internal(shost->transportt);
	if (priv->session_cont.ac.class != &iscsi_session_class.class)
L
Linus Torvalds 已提交
1866 1867
		return 0;

1868
	return &priv->session_cont.ac == cont;
L
Linus Torvalds 已提交
1869 1870
}

1871 1872 1873
static int iscsi_conn_match(struct attribute_container *cont,
			   struct device *dev)
{
1874 1875
	struct iscsi_cls_session *session;
	struct iscsi_cls_conn *conn;
L
Linus Torvalds 已提交
1876
	struct Scsi_Host *shost;
1877
	struct iscsi_internal *priv;
L
Linus Torvalds 已提交
1878

1879
	if (!iscsi_is_conn_dev(dev))
L
Linus Torvalds 已提交
1880 1881
		return 0;

1882 1883 1884 1885
	conn = iscsi_dev_to_conn(dev);
	session = iscsi_dev_to_session(conn->dev.parent);
	shost = iscsi_session_to_shost(session);

1886
	if (!shost->transportt)
L
Linus Torvalds 已提交
1887 1888
		return 0;

1889 1890 1891
	priv = to_iscsi_internal(shost->transportt);
	if (priv->conn_cont.ac.class != &iscsi_connection_class.class)
		return 0;
L
Linus Torvalds 已提交
1892

1893 1894 1895
	return &priv->conn_cont.ac == cont;
}

M
Mike Christie 已提交
1896 1897 1898 1899 1900 1901 1902 1903 1904 1905 1906 1907 1908 1909 1910 1911 1912 1913
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;
}

1914 1915
struct scsi_transport_template *
iscsi_register_transport(struct iscsi_transport *tt)
1916 1917 1918 1919 1920 1921 1922 1923 1924
{
	struct iscsi_internal *priv;
	unsigned long flags;
	int count = 0, err;

	BUG_ON(!tt);

	priv = iscsi_if_transport_lookup(tt);
	if (priv)
1925
		return NULL;
1926

J
Jes Sorensen 已提交
1927
	priv = kzalloc(sizeof(*priv), GFP_KERNEL);
1928
	if (!priv)
1929
		return NULL;
1930 1931
	INIT_LIST_HEAD(&priv->list);
	priv->iscsi_transport = tt;
M
Mike Christie 已提交
1932
	priv->t.user_scan = iscsi_user_scan;
1933
	priv->t.create_work_queue = 1;
1934

1935
	priv->dev.class = &iscsi_transport_class;
1936
	dev_set_name(&priv->dev, "%s", tt->name);
1937
	err = device_register(&priv->dev);
1938 1939 1940
	if (err)
		goto free_priv;

1941
	err = sysfs_create_group(&priv->dev.kobj, &iscsi_transport_group);
1942
	if (err)
1943
		goto unregister_dev;
1944

M
Mike Christie 已提交
1945 1946 1947 1948
	/* 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;
1949
	priv->t.host_size = sizeof(struct iscsi_cls_host);
M
Mike Christie 已提交
1950 1951
	transport_container_register(&priv->t.host_attrs);

1952
	SETUP_HOST_RD_ATTR(netdev, ISCSI_HOST_NETDEV_NAME);
1953
	SETUP_HOST_RD_ATTR(ipaddress, ISCSI_HOST_IPADDRESS);
1954
	SETUP_HOST_RD_ATTR(hwaddress, ISCSI_HOST_HWADDRESS);
1955
	SETUP_HOST_RD_ATTR(initiatorname, ISCSI_HOST_INITIATOR_NAME);
1956 1957 1958 1959
	BUG_ON(count > ISCSI_HOST_ATTRS);
	priv->host_attrs[count] = NULL;
	count = 0;

1960 1961 1962 1963 1964
	/* 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);
L
Linus Torvalds 已提交
1965

1966 1967 1968 1969 1970 1971 1972 1973
	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);
1974
	SETUP_CONN_RD_ATTR(exp_statsn, ISCSI_EXP_STATSN);
1975 1976
	SETUP_CONN_RD_ATTR(persistent_address, ISCSI_PERSISTENT_ADDRESS);
	SETUP_CONN_RD_ATTR(persistent_port, ISCSI_PERSISTENT_PORT);
1977 1978
	SETUP_CONN_RD_ATTR(ping_tmo, ISCSI_PING_TMO);
	SETUP_CONN_RD_ATTR(recv_tmo, ISCSI_RECV_TMO);
1979 1980 1981

	BUG_ON(count > ISCSI_CONN_ATTRS);
	priv->conn_attrs[count] = NULL;
L
Linus Torvalds 已提交
1982 1983
	count = 0;

1984 1985 1986 1987 1988 1989
	/* 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);

1990 1991 1992 1993 1994 1995 1996 1997
	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);
1998 1999
	SETUP_SESSION_RD_ATTR(targetname, ISCSI_TARGET_NAME);
	SETUP_SESSION_RD_ATTR(tpgt, ISCSI_TPGT);
2000 2001 2002 2003
	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);
2004
	SETUP_SESSION_RD_ATTR(fast_abort, ISCSI_FAST_ABORT);
2005 2006
	SETUP_SESSION_RD_ATTR(abort_tmo, ISCSI_ABORT_TMO);
	SETUP_SESSION_RD_ATTR(lu_reset_tmo,ISCSI_LU_RESET_TMO);
2007
	SETUP_SESSION_RD_ATTR(tgt_reset_tmo,ISCSI_TGT_RESET_TMO);
2008 2009
	SETUP_SESSION_RD_ATTR(ifacename, ISCSI_IFACE_NAME);
	SETUP_SESSION_RD_ATTR(initiatorname, ISCSI_INITIATOR_NAME);
2010
	SETUP_SESSION_RD_ATTR(targetalias, ISCSI_TARGET_ALIAS);
M
Mike Christie 已提交
2011
	SETUP_PRIV_SESSION_RD_ATTR(recovery_tmo);
2012
	SETUP_PRIV_SESSION_RD_ATTR(state);
2013

2014 2015 2016 2017 2018 2019 2020 2021
	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);
2022
	return &priv->t;
L
Linus Torvalds 已提交
2023

2024 2025
unregister_dev:
	device_unregister(&priv->dev);
2026
	return NULL;
2027 2028
free_priv:
	kfree(priv);
2029
	return NULL;
L
Linus Torvalds 已提交
2030
}
2031 2032 2033 2034 2035 2036 2037 2038 2039
EXPORT_SYMBOL_GPL(iscsi_register_transport);

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

	BUG_ON(!tt);

2040
	mutex_lock(&rx_queue_mutex);
2041 2042 2043 2044 2045 2046 2047 2048 2049 2050

	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);
M
Mike Christie 已提交
2051
	transport_container_unregister(&priv->t.host_attrs);
2052

2053 2054
	sysfs_remove_group(&priv->dev.kobj, &iscsi_transport_group);
	device_unregister(&priv->dev);
2055
	mutex_unlock(&rx_queue_mutex);
L
Linus Torvalds 已提交
2056

2057 2058 2059
	return 0;
}
EXPORT_SYMBOL_GPL(iscsi_unregister_transport);
L
Linus Torvalds 已提交
2060 2061 2062

static __init int iscsi_transport_init(void)
{
2063
	int err;
L
Linus Torvalds 已提交
2064

M
Meelis Roos 已提交
2065
	printk(KERN_INFO "Loading iSCSI transport class v%s.\n",
2066 2067
		ISCSI_TRANSPORT_VERSION);

2068 2069
	atomic_set(&iscsi_session_nr, 0);

2070
	err = class_register(&iscsi_transport_class);
L
Linus Torvalds 已提交
2071 2072
	if (err)
		return err;
2073

2074
	err = class_register(&iscsi_endpoint_class);
2075 2076 2077
	if (err)
		goto unregister_transport_class;

2078 2079 2080 2081
	err = transport_class_register(&iscsi_host_class);
	if (err)
		goto unregister_endpoint_class;

M
Mike Christie 已提交
2082 2083 2084 2085
	err = transport_class_register(&iscsi_connection_class);
	if (err)
		goto unregister_host_class;

2086 2087 2088 2089
	err = transport_class_register(&iscsi_session_class);
	if (err)
		goto unregister_conn_class;

2090 2091
	nls = netlink_kernel_create(&init_net, NETLINK_ISCSI, 1, iscsi_if_rx,
				    NULL, THIS_MODULE);
2092 2093
	if (!nls) {
		err = -ENOBUFS;
2094
		goto unregister_session_class;
2095 2096
	}

2097 2098 2099 2100
	iscsi_eh_timer_workq = create_singlethread_workqueue("iscsi_eh");
	if (!iscsi_eh_timer_workq)
		goto release_nls;

2101
	return 0;
2102

2103
release_nls:
2104
	netlink_kernel_release(nls);
2105 2106 2107 2108
unregister_session_class:
	transport_class_unregister(&iscsi_session_class);
unregister_conn_class:
	transport_class_unregister(&iscsi_connection_class);
M
Mike Christie 已提交
2109 2110
unregister_host_class:
	transport_class_unregister(&iscsi_host_class);
2111 2112
unregister_endpoint_class:
	class_unregister(&iscsi_endpoint_class);
2113 2114 2115
unregister_transport_class:
	class_unregister(&iscsi_transport_class);
	return err;
L
Linus Torvalds 已提交
2116 2117 2118 2119
}

static void __exit iscsi_transport_exit(void)
{
2120
	destroy_workqueue(iscsi_eh_timer_workq);
2121
	netlink_kernel_release(nls);
2122 2123
	transport_class_unregister(&iscsi_connection_class);
	transport_class_unregister(&iscsi_session_class);
M
Mike Christie 已提交
2124
	transport_class_unregister(&iscsi_host_class);
2125
	class_unregister(&iscsi_endpoint_class);
2126
	class_unregister(&iscsi_transport_class);
L
Linus Torvalds 已提交
2127 2128 2129 2130 2131
}

module_init(iscsi_transport_init);
module_exit(iscsi_transport_exit);

2132 2133 2134 2135
MODULE_AUTHOR("Mike Christie <michaelc@cs.wisc.edu>, "
	      "Dmitry Yusupov <dmitry_yus@yahoo.com>, "
	      "Alex Aizman <itn780@yahoo.com>");
MODULE_DESCRIPTION("iSCSI Transport Interface");
L
Linus Torvalds 已提交
2136
MODULE_LICENSE("GPL");
2137
MODULE_VERSION(ISCSI_TRANSPORT_VERSION);