tfc_conf.c 12.3 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33
/*******************************************************************************
 * Filename:  tcm_fc.c
 *
 * This file contains the configfs implementation for TCM_fc fabric node.
 * Based on tcm_loop_configfs.c
 *
 * Copyright (c) 2010 Cisco Systems, Inc.
 * Copyright (c) 2009,2010 Rising Tide, Inc.
 * Copyright (c) 2009,2010 Linux-iSCSI.org
 *
 * Copyright (c) 2009,2010 Nicholas A. Bellinger <nab@linux-iscsi.org>
 *
 * 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.
 ****************************************************************************/

#include <linux/module.h>
#include <linux/moduleparam.h>
#include <generated/utsrelease.h>
#include <linux/utsname.h>
#include <linux/init.h>
#include <linux/slab.h>
#include <linux/kthread.h>
#include <linux/types.h>
#include <linux/string.h>
#include <linux/configfs.h>
34
#include <linux/kernel.h>
35 36 37 38 39
#include <linux/ctype.h>
#include <asm/unaligned.h>
#include <scsi/libfc.h>

#include <target/target_core_base.h>
40
#include <target/target_core_fabric.h>
41 42 43

#include "tcm_fc.h"

44
static LIST_HEAD(ft_wwn_list);
45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63
DEFINE_MUTEX(ft_lport_lock);

unsigned int ft_debug_logging;
module_param_named(debug_logging, ft_debug_logging, int, S_IRUGO|S_IWUSR);
MODULE_PARM_DESC(debug_logging, "a bit mask of logging levels");

/*
 * Parse WWN.
 * If strict, we require lower-case hex and colon separators to be sure
 * the name is the same as what would be generated by ft_format_wwn()
 * so the name and wwn are mapped one-to-one.
 */
static ssize_t ft_parse_wwn(const char *name, u64 *wwn, int strict)
{
	const char *cp;
	char c;
	u32 byte = 0;
	u32 pos = 0;
	u32 err;
64
	int val;
65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84

	*wwn = 0;
	for (cp = name; cp < &name[FT_NAMELEN - 1]; cp++) {
		c = *cp;
		if (c == '\n' && cp[1] == '\0')
			continue;
		if (strict && pos++ == 2 && byte++ < 7) {
			pos = 0;
			if (c == ':')
				continue;
			err = 1;
			goto fail;
		}
		if (c == '\0') {
			err = 2;
			if (strict && byte != 8)
				goto fail;
			return cp - name;
		}
		err = 3;
85 86
		val = hex_to_bin(c);
		if (val < 0 || (strict && isupper(c)))
87
			goto fail;
88
		*wwn = (*wwn << 4) | val;
89 90 91
	}
	err = 4;
fail:
92
	pr_debug("err %u len %zu pos %u byte %u\n",
93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131
		    err, cp - name, pos, byte);
	return -1;
}

ssize_t ft_format_wwn(char *buf, size_t len, u64 wwn)
{
	u8 b[8];

	put_unaligned_be64(wwn, b);
	return snprintf(buf, len,
		 "%2.2x:%2.2x:%2.2x:%2.2x:%2.2x:%2.2x:%2.2x:%2.2x",
		 b[0], b[1], b[2], b[3], b[4], b[5], b[6], b[7]);
}

static ssize_t ft_wwn_show(void *arg, char *buf)
{
	u64 *wwn = arg;
	ssize_t len;

	len = ft_format_wwn(buf, PAGE_SIZE - 2, *wwn);
	buf[len++] = '\n';
	return len;
}

static ssize_t ft_wwn_store(void *arg, const char *buf, size_t len)
{
	ssize_t ret;
	u64 wwn;

	ret = ft_parse_wwn(buf, &wwn, 0);
	if (ret > 0)
		*(u64 *)arg = wwn;
	return ret;
}

/*
 * ACL auth ops.
 */

132
static ssize_t ft_nacl_port_name_show(struct config_item *item, char *page)
133
{
134
	struct se_node_acl *se_nacl = acl_to_nacl(item);
135 136 137 138 139 140
	struct ft_node_acl *acl = container_of(se_nacl,
			struct ft_node_acl, se_node_acl);

	return ft_wwn_show(&acl->node_auth.port_name, page);
}

141 142
static ssize_t ft_nacl_port_name_store(struct config_item *item,
		const char *page, size_t count)
143
{
144
	struct se_node_acl *se_nacl = acl_to_nacl(item);
145 146 147 148 149 150
	struct ft_node_acl *acl = container_of(se_nacl,
			struct ft_node_acl, se_node_acl);

	return ft_wwn_store(&acl->node_auth.port_name, page, count);
}

151 152
static ssize_t ft_nacl_node_name_show(struct config_item *item,
		char *page)
153
{
154
	struct se_node_acl *se_nacl = acl_to_nacl(item);
155 156 157 158 159 160
	struct ft_node_acl *acl = container_of(se_nacl,
			struct ft_node_acl, se_node_acl);

	return ft_wwn_show(&acl->node_auth.node_name, page);
}

161 162
static ssize_t ft_nacl_node_name_store(struct config_item *item,
		const char *page, size_t count)
163
{
164
	struct se_node_acl *se_nacl = acl_to_nacl(item);
165 166 167 168 169 170
	struct ft_node_acl *acl = container_of(se_nacl,
			struct ft_node_acl, se_node_acl);

	return ft_wwn_store(&acl->node_auth.node_name, page, count);
}

171 172
CONFIGFS_ATTR(ft_nacl_, node_name);
CONFIGFS_ATTR(ft_nacl_, port_name);
173 174

static struct configfs_attribute *ft_nacl_base_attrs[] = {
175 176
	&ft_nacl_attr_port_name,
	&ft_nacl_attr_node_name,
177 178 179 180 181 182 183 184 185 186 187
	NULL,
};

/*
 * ACL ops.
 */

/*
 * Add ACL for an initiator.  The ACL is named arbitrarily.
 * The port_name and/or node_name are attributes.
 */
188
static int ft_init_nodeacl(struct se_node_acl *nacl, const char *name)
189
{
190 191
	struct ft_node_acl *acl =
		container_of(nacl, struct ft_node_acl, se_node_acl);
192 193 194
	u64 wwpn;

	if (ft_parse_wwn(name, &wwpn, 1) < 0)
195
		return -EINVAL;
196 197

	acl->node_auth.port_name = wwpn;
198
	return 0;
199 200 201 202 203 204 205 206 207
}

struct ft_node_acl *ft_acl_get(struct ft_tpg *tpg, struct fc_rport_priv *rdata)
{
	struct ft_node_acl *found = NULL;
	struct ft_node_acl *acl;
	struct se_portal_group *se_tpg = &tpg->se_tpg;
	struct se_node_acl *se_acl;

208
	mutex_lock(&se_tpg->acl_node_mutex);
209 210
	list_for_each_entry(se_acl, &se_tpg->acl_node_list, acl_list) {
		acl = container_of(se_acl, struct ft_node_acl, se_node_acl);
211
		pr_debug("acl %p port_name %llx\n",
212 213 214
			acl, (unsigned long long)acl->node_auth.port_name);
		if (acl->node_auth.port_name == rdata->ids.port_name ||
		    acl->node_auth.node_name == rdata->ids.node_name) {
215
			pr_debug("acl %p port_name %llx matched\n", acl,
216 217 218 219 220 221
				    (unsigned long long)rdata->ids.port_name);
			found = acl;
			/* XXX need to hold onto ACL */
			break;
		}
	}
222
	mutex_unlock(&se_tpg->acl_node_mutex);
223 224 225 226 227 228 229 230 231 232 233
	return found;
}

/*
 * local_port port_group (tpg) ops.
 */
static struct se_portal_group *ft_add_tpg(
	struct se_wwn *wwn,
	struct config_group *group,
	const char *name)
{
234
	struct ft_lport_wwn *ft_wwn;
235
	struct ft_tpg *tpg;
236
	struct workqueue_struct *wq;
237 238 239
	unsigned long index;
	int ret;

240
	pr_debug("tcm_fc: add tpg %s\n", name);
241 242 243 244 245 246

	/*
	 * Name must be "tpgt_" followed by the index.
	 */
	if (strstr(name, "tpgt_") != name)
		return NULL;
247 248 249 250 251

	ret = kstrtoul(name + 5, 10, &index);
	if (ret)
		return NULL;
	if (index > UINT_MAX)
252 253
		return NULL;

254 255 256 257 258
	if ((index != 1)) {
		pr_err("Error, a single TPG=1 is used for HW port mappings\n");
		return ERR_PTR(-ENOSYS);
	}

259
	ft_wwn = container_of(wwn, struct ft_lport_wwn, se_wwn);
260 261 262 263
	tpg = kzalloc(sizeof(*tpg), GFP_KERNEL);
	if (!tpg)
		return NULL;
	tpg->index = index;
264
	tpg->lport_wwn = ft_wwn;
265 266
	INIT_LIST_HEAD(&tpg->lun_list);

267 268
	wq = alloc_workqueue("tcm_fc", 0, 1);
	if (!wq) {
269 270 271 272
		kfree(tpg);
		return NULL;
	}

273
	ret = core_tpg_register(wwn, &tpg->se_tpg, SCSI_PROTOCOL_FCP);
274 275
	if (ret < 0) {
		destroy_workqueue(wq);
276 277 278
		kfree(tpg);
		return NULL;
	}
279
	tpg->workqueue = wq;
280 281

	mutex_lock(&ft_lport_lock);
282
	ft_wwn->tpg = tpg;
283 284 285 286 287 288 289 290
	mutex_unlock(&ft_lport_lock);

	return &tpg->se_tpg;
}

static void ft_del_tpg(struct se_portal_group *se_tpg)
{
	struct ft_tpg *tpg = container_of(se_tpg, struct ft_tpg, se_tpg);
291
	struct ft_lport_wwn *ft_wwn = tpg->lport_wwn;
292

293
	pr_debug("del tpg %s\n",
294 295
		    config_item_name(&tpg->se_tpg.tpg_group.cg_item));

296
	destroy_workqueue(tpg->workqueue);
297 298 299 300 301

	/* Wait for sessions to be freed thru RCU, for BUG_ON below */
	synchronize_rcu();

	mutex_lock(&ft_lport_lock);
302
	ft_wwn->tpg = NULL;
303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320
	if (tpg->tport) {
		tpg->tport->tpg = NULL;
		tpg->tport = NULL;
	}
	mutex_unlock(&ft_lport_lock);

	core_tpg_deregister(se_tpg);
	kfree(tpg);
}

/*
 * Verify that an lport is configured to use the tcm_fc module, and return
 * the target port group that should be used.
 *
 * The caller holds ft_lport_lock.
 */
struct ft_tpg *ft_lport_find_tpg(struct fc_lport *lport)
{
321
	struct ft_lport_wwn *ft_wwn;
322

323 324 325
	list_for_each_entry(ft_wwn, &ft_wwn_list, ft_wwn_node) {
		if (ft_wwn->wwpn == lport->wwpn)
			return ft_wwn->tpg;
326 327 328 329 330 331 332 333 334 335 336 337
	}
	return NULL;
}

/*
 * target config instance ops.
 */

/*
 * Add lport to allowed config.
 * The name is the WWPN in lower-case ASCII, colon-separated bytes.
 */
338
static struct se_wwn *ft_add_wwn(
339 340 341 342
	struct target_fabric_configfs *tf,
	struct config_group *group,
	const char *name)
{
343 344
	struct ft_lport_wwn *ft_wwn;
	struct ft_lport_wwn *old_ft_wwn;
345 346
	u64 wwpn;

347
	pr_debug("add wwn %s\n", name);
348 349
	if (ft_parse_wwn(name, &wwpn, 1) < 0)
		return NULL;
350 351
	ft_wwn = kzalloc(sizeof(*ft_wwn), GFP_KERNEL);
	if (!ft_wwn)
352
		return NULL;
353
	ft_wwn->wwpn = wwpn;
354 355

	mutex_lock(&ft_lport_lock);
356 357
	list_for_each_entry(old_ft_wwn, &ft_wwn_list, ft_wwn_node) {
		if (old_ft_wwn->wwpn == wwpn) {
358
			mutex_unlock(&ft_lport_lock);
359
			kfree(ft_wwn);
360 361 362
			return NULL;
		}
	}
363 364
	list_add_tail(&ft_wwn->ft_wwn_node, &ft_wwn_list);
	ft_format_wwn(ft_wwn->name, sizeof(ft_wwn->name), wwpn);
365 366
	mutex_unlock(&ft_lport_lock);

367
	return &ft_wwn->se_wwn;
368 369
}

370
static void ft_del_wwn(struct se_wwn *wwn)
371
{
372 373
	struct ft_lport_wwn *ft_wwn = container_of(wwn,
				struct ft_lport_wwn, se_wwn);
374

375
	pr_debug("del wwn %s\n", ft_wwn->name);
376
	mutex_lock(&ft_lport_lock);
377
	list_del(&ft_wwn->ft_wwn_node);
378 379
	mutex_unlock(&ft_lport_lock);

380
	kfree(ft_wwn);
381 382
}

383
static ssize_t ft_wwn_version_show(struct config_item *item, char *page)
384 385 386 387 388
{
	return sprintf(page, "TCM FC " FT_VERSION " on %s/%s on "
		""UTS_RELEASE"\n",  utsname()->sysname, utsname()->machine);
}

389
CONFIGFS_ATTR_RO(ft_wwn_, version);
390 391

static struct configfs_attribute *ft_wwn_attrs[] = {
392
	&ft_wwn_attr_version,
393 394 395
	NULL,
};

396 397 398 399 400
static inline struct ft_tpg *ft_tpg(struct se_portal_group *se_tpg)
{
	return container_of(se_tpg, struct ft_tpg, se_tpg);
}

401 402 403 404 405 406 407
static char *ft_get_fabric_name(void)
{
	return "fc";
}

static char *ft_get_fabric_wwn(struct se_portal_group *se_tpg)
{
408
	return ft_tpg(se_tpg)->lport_wwn->name;
409 410 411 412 413 414 415 416
}

static u16 ft_get_tag(struct se_portal_group *se_tpg)
{
	/*
	 * This tag is used when forming SCSI Name identifier in EVPD=1 0x83
	 * to represent the SCSI Target Port.
	 */
417
	return ft_tpg(se_tpg)->index;
418 419 420 421 422 423 424 425 426 427 428 429 430
}

static int ft_check_false(struct se_portal_group *se_tpg)
{
	return 0;
}

static void ft_set_default_node_attr(struct se_node_acl *se_nacl)
{
}

static u32 ft_tpg_get_inst_index(struct se_portal_group *se_tpg)
{
431
	return ft_tpg(se_tpg)->index;
432 433
}

434 435 436
static const struct target_core_fabric_ops ft_fabric_ops = {
	.module =			THIS_MODULE,
	.name =				"fc",
437
	.node_acl_size =		sizeof(struct ft_node_acl),
438 439 440 441 442 443 444 445 446
	.get_fabric_name =		ft_get_fabric_name,
	.tpg_get_wwn =			ft_get_fabric_wwn,
	.tpg_get_tag =			ft_get_tag,
	.tpg_check_demo_mode =		ft_check_false,
	.tpg_check_demo_mode_cache =	ft_check_false,
	.tpg_check_demo_mode_write_protect = ft_check_false,
	.tpg_check_prod_mode_write_protect = ft_check_false,
	.tpg_get_inst_index =		ft_tpg_get_inst_index,
	.check_stop_free =		ft_check_stop_free,
447
	.release_cmd =			ft_release_cmd,
448 449 450 451 452 453 454 455 456 457 458
	.shutdown_session =		ft_sess_shutdown,
	.close_session =		ft_sess_close,
	.sess_get_index =		ft_sess_get_index,
	.sess_get_initiator_sid =	NULL,
	.write_pending =		ft_write_pending,
	.write_pending_status =		ft_write_pending_status,
	.set_default_node_attributes =	ft_set_default_node_attr,
	.get_cmd_state =		ft_get_cmd_state,
	.queue_data_in =		ft_queue_data_in,
	.queue_status =			ft_queue_status,
	.queue_tm_rsp =			ft_queue_tm_resp,
459
	.aborted_task =			ft_aborted_task,
460 461 462 463
	/*
	 * Setup function pointers for generic logic in
	 * target_core_fabric_configfs.c
	 */
464 465
	.fabric_make_wwn =		&ft_add_wwn,
	.fabric_drop_wwn =		&ft_del_wwn,
466 467
	.fabric_make_tpg =		&ft_add_tpg,
	.fabric_drop_tpg =		&ft_del_tpg,
468
	.fabric_init_nodeacl =		&ft_init_nodeacl,
469

470 471 472
	.tfc_wwn_attrs			= ft_wwn_attrs,
	.tfc_tpg_nacl_base_attrs	= ft_nacl_base_attrs,
};
473 474 475 476 477 478 479

static struct notifier_block ft_notifier = {
	.notifier_call = ft_lport_notify
};

static int __init ft_init(void)
{
480 481 482 483 484 485 486 487 488 489
	int ret;

	ret = target_register_template(&ft_fabric_ops);
	if (ret)
		goto out;

	ret = fc_fc4_register_provider(FC_TYPE_FCP, &ft_prov);
	if (ret)
		goto out_unregister_template;

490 491 492
	blocking_notifier_chain_register(&fc_lport_notifier_head, &ft_notifier);
	fc_lport_iterate(ft_lport_add, NULL);
	return 0;
493 494 495 496 497

out_unregister_template:
	target_unregister_template(&ft_fabric_ops);
out:
	return ret;
498 499 500 501 502 503 504 505
}

static void __exit ft_exit(void)
{
	blocking_notifier_chain_unregister(&fc_lport_notifier_head,
					   &ft_notifier);
	fc_fc4_deregister_provider(FC_TYPE_FCP, &ft_prov);
	fc_lport_iterate(ft_lport_del, NULL);
506
	target_unregister_template(&ft_fabric_ops);
507 508 509 510 511 512 513
	synchronize_rcu();
}

MODULE_DESCRIPTION("FC TCM fabric driver " FT_VERSION);
MODULE_LICENSE("GPL");
module_init(ft_init);
module_exit(ft_exit);