xref: /linux/drivers/target/tcm_fc/tfc_conf.c (revision 0ea5c948cb64bab5bc7a5516774eb8536f05aa0d)
1c942fddfSThomas Gleixner // SPDX-License-Identifier: GPL-2.0-or-later
23699d92aSKiran Patil /*******************************************************************************
33699d92aSKiran Patil  * Filename:  tcm_fc.c
43699d92aSKiran Patil  *
53699d92aSKiran Patil  * This file contains the configfs implementation for TCM_fc fabric node.
63699d92aSKiran Patil  * Based on tcm_loop_configfs.c
73699d92aSKiran Patil  *
83699d92aSKiran Patil  * Copyright (c) 2010 Cisco Systems, Inc.
93699d92aSKiran Patil  * Copyright (c) 2009,2010 Rising Tide, Inc.
103699d92aSKiran Patil  * Copyright (c) 2009,2010 Linux-iSCSI.org
113699d92aSKiran Patil  *
123699d92aSKiran Patil  * Copyright (c) 2009,2010 Nicholas A. Bellinger <nab@linux-iscsi.org>
133699d92aSKiran Patil  *
143699d92aSKiran Patil  ****************************************************************************/
153699d92aSKiran Patil 
163699d92aSKiran Patil #include <linux/module.h>
173699d92aSKiran Patil #include <linux/moduleparam.h>
183699d92aSKiran Patil #include <generated/utsrelease.h>
193699d92aSKiran Patil #include <linux/utsname.h>
203699d92aSKiran Patil #include <linux/init.h>
213699d92aSKiran Patil #include <linux/slab.h>
223699d92aSKiran Patil #include <linux/kthread.h>
233699d92aSKiran Patil #include <linux/types.h>
243699d92aSKiran Patil #include <linux/string.h>
253699d92aSKiran Patil #include <linux/configfs.h>
26635a2b3fSAndy Shevchenko #include <linux/kernel.h>
273699d92aSKiran Patil #include <linux/ctype.h>
283699d92aSKiran Patil #include <asm/unaligned.h>
293699d92aSKiran Patil #include <scsi/libfc.h>
303699d92aSKiran Patil 
313699d92aSKiran Patil #include <target/target_core_base.h>
32c4795fb2SChristoph Hellwig #include <target/target_core_fabric.h>
333699d92aSKiran Patil 
343699d92aSKiran Patil #include "tcm_fc.h"
353699d92aSKiran Patil 
36705665daSAndy Grover static LIST_HEAD(ft_wwn_list);
373699d92aSKiran Patil DEFINE_MUTEX(ft_lport_lock);
383699d92aSKiran Patil 
393699d92aSKiran Patil unsigned int ft_debug_logging;
403699d92aSKiran Patil module_param_named(debug_logging, ft_debug_logging, int, S_IRUGO|S_IWUSR);
413699d92aSKiran Patil MODULE_PARM_DESC(debug_logging, "a bit mask of logging levels");
423699d92aSKiran Patil 
433699d92aSKiran Patil /*
443699d92aSKiran Patil  * Parse WWN.
453699d92aSKiran Patil  * If strict, we require lower-case hex and colon separators to be sure
463699d92aSKiran Patil  * the name is the same as what would be generated by ft_format_wwn()
473699d92aSKiran Patil  * so the name and wwn are mapped one-to-one.
483699d92aSKiran Patil  */
ft_parse_wwn(const char * name,u64 * wwn,int strict)493699d92aSKiran Patil static ssize_t ft_parse_wwn(const char *name, u64 *wwn, int strict)
503699d92aSKiran Patil {
513699d92aSKiran Patil 	const char *cp;
523699d92aSKiran Patil 	char c;
533699d92aSKiran Patil 	u32 byte = 0;
543699d92aSKiran Patil 	u32 pos = 0;
553699d92aSKiran Patil 	u32 err;
56635a2b3fSAndy Shevchenko 	int val;
573699d92aSKiran Patil 
583699d92aSKiran Patil 	*wwn = 0;
593699d92aSKiran Patil 	for (cp = name; cp < &name[FT_NAMELEN - 1]; cp++) {
603699d92aSKiran Patil 		c = *cp;
613699d92aSKiran Patil 		if (c == '\n' && cp[1] == '\0')
623699d92aSKiran Patil 			continue;
633699d92aSKiran Patil 		if (strict && pos++ == 2 && byte++ < 7) {
643699d92aSKiran Patil 			pos = 0;
653699d92aSKiran Patil 			if (c == ':')
663699d92aSKiran Patil 				continue;
673699d92aSKiran Patil 			err = 1;
683699d92aSKiran Patil 			goto fail;
693699d92aSKiran Patil 		}
703699d92aSKiran Patil 		if (c == '\0') {
713699d92aSKiran Patil 			err = 2;
723699d92aSKiran Patil 			if (strict && byte != 8)
733699d92aSKiran Patil 				goto fail;
743699d92aSKiran Patil 			return cp - name;
753699d92aSKiran Patil 		}
763699d92aSKiran Patil 		err = 3;
77635a2b3fSAndy Shevchenko 		val = hex_to_bin(c);
78635a2b3fSAndy Shevchenko 		if (val < 0 || (strict && isupper(c)))
793699d92aSKiran Patil 			goto fail;
80635a2b3fSAndy Shevchenko 		*wwn = (*wwn << 4) | val;
813699d92aSKiran Patil 	}
823699d92aSKiran Patil 	err = 4;
833699d92aSKiran Patil fail:
846708bb27SAndy Grover 	pr_debug("err %u len %zu pos %u byte %u\n",
853699d92aSKiran Patil 		    err, cp - name, pos, byte);
863699d92aSKiran Patil 	return -1;
873699d92aSKiran Patil }
883699d92aSKiran Patil 
ft_format_wwn(char * buf,size_t len,u64 wwn)893699d92aSKiran Patil ssize_t ft_format_wwn(char *buf, size_t len, u64 wwn)
903699d92aSKiran Patil {
913699d92aSKiran Patil 	u8 b[8];
923699d92aSKiran Patil 
933699d92aSKiran Patil 	put_unaligned_be64(wwn, b);
943699d92aSKiran Patil 	return snprintf(buf, len,
953699d92aSKiran Patil 		 "%2.2x:%2.2x:%2.2x:%2.2x:%2.2x:%2.2x:%2.2x:%2.2x",
963699d92aSKiran Patil 		 b[0], b[1], b[2], b[3], b[4], b[5], b[6], b[7]);
973699d92aSKiran Patil }
983699d92aSKiran Patil 
ft_wwn_show(void * arg,char * buf)993699d92aSKiran Patil static ssize_t ft_wwn_show(void *arg, char *buf)
1003699d92aSKiran Patil {
1013699d92aSKiran Patil 	u64 *wwn = arg;
1023699d92aSKiran Patil 	ssize_t len;
1033699d92aSKiran Patil 
1043699d92aSKiran Patil 	len = ft_format_wwn(buf, PAGE_SIZE - 2, *wwn);
1053699d92aSKiran Patil 	buf[len++] = '\n';
1063699d92aSKiran Patil 	return len;
1073699d92aSKiran Patil }
1083699d92aSKiran Patil 
ft_wwn_store(void * arg,const char * buf,size_t len)1093699d92aSKiran Patil static ssize_t ft_wwn_store(void *arg, const char *buf, size_t len)
1103699d92aSKiran Patil {
1113699d92aSKiran Patil 	ssize_t ret;
1123699d92aSKiran Patil 	u64 wwn;
1133699d92aSKiran Patil 
1143699d92aSKiran Patil 	ret = ft_parse_wwn(buf, &wwn, 0);
1153699d92aSKiran Patil 	if (ret > 0)
1163699d92aSKiran Patil 		*(u64 *)arg = wwn;
1173699d92aSKiran Patil 	return ret;
1183699d92aSKiran Patil }
1193699d92aSKiran Patil 
1203699d92aSKiran Patil /*
1213699d92aSKiran Patil  * ACL auth ops.
1223699d92aSKiran Patil  */
1233699d92aSKiran Patil 
ft_nacl_port_name_show(struct config_item * item,char * page)1242eafd729SChristoph Hellwig static ssize_t ft_nacl_port_name_show(struct config_item *item, char *page)
1253699d92aSKiran Patil {
1262eafd729SChristoph Hellwig 	struct se_node_acl *se_nacl = acl_to_nacl(item);
1273699d92aSKiran Patil 	struct ft_node_acl *acl = container_of(se_nacl,
1283699d92aSKiran Patil 			struct ft_node_acl, se_node_acl);
1293699d92aSKiran Patil 
1303699d92aSKiran Patil 	return ft_wwn_show(&acl->node_auth.port_name, page);
1313699d92aSKiran Patil }
1323699d92aSKiran Patil 
ft_nacl_port_name_store(struct config_item * item,const char * page,size_t count)1332eafd729SChristoph Hellwig static ssize_t ft_nacl_port_name_store(struct config_item *item,
1342eafd729SChristoph Hellwig 		const char *page, size_t count)
1353699d92aSKiran Patil {
1362eafd729SChristoph Hellwig 	struct se_node_acl *se_nacl = acl_to_nacl(item);
1373699d92aSKiran Patil 	struct ft_node_acl *acl = container_of(se_nacl,
1383699d92aSKiran Patil 			struct ft_node_acl, se_node_acl);
1393699d92aSKiran Patil 
1403699d92aSKiran Patil 	return ft_wwn_store(&acl->node_auth.port_name, page, count);
1413699d92aSKiran Patil }
1423699d92aSKiran Patil 
ft_nacl_node_name_show(struct config_item * item,char * page)1432eafd729SChristoph Hellwig static ssize_t ft_nacl_node_name_show(struct config_item *item,
1443699d92aSKiran Patil 		char *page)
1453699d92aSKiran Patil {
1462eafd729SChristoph Hellwig 	struct se_node_acl *se_nacl = acl_to_nacl(item);
1473699d92aSKiran Patil 	struct ft_node_acl *acl = container_of(se_nacl,
1483699d92aSKiran Patil 			struct ft_node_acl, se_node_acl);
1493699d92aSKiran Patil 
1503699d92aSKiran Patil 	return ft_wwn_show(&acl->node_auth.node_name, page);
1513699d92aSKiran Patil }
1523699d92aSKiran Patil 
ft_nacl_node_name_store(struct config_item * item,const char * page,size_t count)1532eafd729SChristoph Hellwig static ssize_t ft_nacl_node_name_store(struct config_item *item,
1542eafd729SChristoph Hellwig 		const char *page, size_t count)
1553699d92aSKiran Patil {
1562eafd729SChristoph Hellwig 	struct se_node_acl *se_nacl = acl_to_nacl(item);
1573699d92aSKiran Patil 	struct ft_node_acl *acl = container_of(se_nacl,
1583699d92aSKiran Patil 			struct ft_node_acl, se_node_acl);
1593699d92aSKiran Patil 
1603699d92aSKiran Patil 	return ft_wwn_store(&acl->node_auth.node_name, page, count);
1613699d92aSKiran Patil }
1623699d92aSKiran Patil 
1632eafd729SChristoph Hellwig CONFIGFS_ATTR(ft_nacl_, node_name);
1642eafd729SChristoph Hellwig CONFIGFS_ATTR(ft_nacl_, port_name);
1653699d92aSKiran Patil 
ft_nacl_tag_show(struct config_item * item,char * page)166091b7062SAndy Grover static ssize_t ft_nacl_tag_show(struct config_item *item,
167091b7062SAndy Grover 		char *page)
168091b7062SAndy Grover {
169091b7062SAndy Grover 	return snprintf(page, PAGE_SIZE, "%s", acl_to_nacl(item)->acl_tag);
170091b7062SAndy Grover }
171091b7062SAndy Grover 
ft_nacl_tag_store(struct config_item * item,const char * page,size_t count)172091b7062SAndy Grover static ssize_t ft_nacl_tag_store(struct config_item *item,
173091b7062SAndy Grover 		const char *page, size_t count)
174091b7062SAndy Grover {
175091b7062SAndy Grover 	struct se_node_acl *se_nacl = acl_to_nacl(item);
176091b7062SAndy Grover 	int ret;
177091b7062SAndy Grover 
178091b7062SAndy Grover 	ret = core_tpg_set_initiator_node_tag(se_nacl->se_tpg, se_nacl, page);
179091b7062SAndy Grover 
180091b7062SAndy Grover 	if (ret < 0)
181091b7062SAndy Grover 		return ret;
182091b7062SAndy Grover 	return count;
183091b7062SAndy Grover }
184091b7062SAndy Grover 
185091b7062SAndy Grover CONFIGFS_ATTR(ft_nacl_, tag);
186091b7062SAndy Grover 
1873699d92aSKiran Patil static struct configfs_attribute *ft_nacl_base_attrs[] = {
1882eafd729SChristoph Hellwig 	&ft_nacl_attr_port_name,
1892eafd729SChristoph Hellwig 	&ft_nacl_attr_node_name,
190091b7062SAndy Grover 	&ft_nacl_attr_tag,
1913699d92aSKiran Patil 	NULL,
1923699d92aSKiran Patil };
1933699d92aSKiran Patil 
1943699d92aSKiran Patil /*
1953699d92aSKiran Patil  * ACL ops.
1963699d92aSKiran Patil  */
1973699d92aSKiran Patil 
1983699d92aSKiran Patil /*
1993699d92aSKiran Patil  * Add ACL for an initiator.  The ACL is named arbitrarily.
2003699d92aSKiran Patil  * The port_name and/or node_name are attributes.
2013699d92aSKiran Patil  */
ft_init_nodeacl(struct se_node_acl * nacl,const char * name)202c7d6a803SChristoph Hellwig static int ft_init_nodeacl(struct se_node_acl *nacl, const char *name)
2033699d92aSKiran Patil {
204c7d6a803SChristoph Hellwig 	struct ft_node_acl *acl =
205c7d6a803SChristoph Hellwig 		container_of(nacl, struct ft_node_acl, se_node_acl);
2063699d92aSKiran Patil 	u64 wwpn;
2073699d92aSKiran Patil 
2083699d92aSKiran Patil 	if (ft_parse_wwn(name, &wwpn, 1) < 0)
209c7d6a803SChristoph Hellwig 		return -EINVAL;
2103699d92aSKiran Patil 
2113699d92aSKiran Patil 	acl->node_auth.port_name = wwpn;
212c7d6a803SChristoph Hellwig 	return 0;
2133699d92aSKiran Patil }
2143699d92aSKiran Patil 
2153699d92aSKiran Patil /*
2163699d92aSKiran Patil  * local_port port_group (tpg) ops.
2173699d92aSKiran Patil  */
ft_add_tpg(struct se_wwn * wwn,const char * name)218aa090eabSBart Van Assche static struct se_portal_group *ft_add_tpg(struct se_wwn *wwn, const char *name)
2193699d92aSKiran Patil {
220705665daSAndy Grover 	struct ft_lport_wwn *ft_wwn;
2213699d92aSKiran Patil 	struct ft_tpg *tpg;
22206383f10SMark Rustad 	struct workqueue_struct *wq;
2233699d92aSKiran Patil 	unsigned long index;
2243699d92aSKiran Patil 	int ret;
2253699d92aSKiran Patil 
2266708bb27SAndy Grover 	pr_debug("tcm_fc: add tpg %s\n", name);
2273699d92aSKiran Patil 
2283699d92aSKiran Patil 	/*
2293699d92aSKiran Patil 	 * Name must be "tpgt_" followed by the index.
2303699d92aSKiran Patil 	 */
2313699d92aSKiran Patil 	if (strstr(name, "tpgt_") != name)
2323699d92aSKiran Patil 		return NULL;
23357103d7fSJingoo Han 
23457103d7fSJingoo Han 	ret = kstrtoul(name + 5, 10, &index);
23557103d7fSJingoo Han 	if (ret)
23657103d7fSJingoo Han 		return NULL;
23757103d7fSJingoo Han 	if (index > UINT_MAX)
2383699d92aSKiran Patil 		return NULL;
2393699d92aSKiran Patil 
240d242c1d7SAndy Grover 	if ((index != 1)) {
241d242c1d7SAndy Grover 		pr_err("Error, a single TPG=1 is used for HW port mappings\n");
242d242c1d7SAndy Grover 		return ERR_PTR(-ENOSYS);
243d242c1d7SAndy Grover 	}
244d242c1d7SAndy Grover 
245705665daSAndy Grover 	ft_wwn = container_of(wwn, struct ft_lport_wwn, se_wwn);
2463699d92aSKiran Patil 	tpg = kzalloc(sizeof(*tpg), GFP_KERNEL);
2473699d92aSKiran Patil 	if (!tpg)
2483699d92aSKiran Patil 		return NULL;
2493699d92aSKiran Patil 	tpg->index = index;
250705665daSAndy Grover 	tpg->lport_wwn = ft_wwn;
2513699d92aSKiran Patil 	INIT_LIST_HEAD(&tpg->lun_list);
2523699d92aSKiran Patil 
25306383f10SMark Rustad 	wq = alloc_workqueue("tcm_fc", 0, 1);
25406383f10SMark Rustad 	if (!wq) {
2553699d92aSKiran Patil 		kfree(tpg);
2563699d92aSKiran Patil 		return NULL;
2573699d92aSKiran Patil 	}
2583699d92aSKiran Patil 
259bc0c94b1SNicholas Bellinger 	ret = core_tpg_register(wwn, &tpg->se_tpg, SCSI_PROTOCOL_FCP);
26006383f10SMark Rustad 	if (ret < 0) {
26106383f10SMark Rustad 		destroy_workqueue(wq);
2623699d92aSKiran Patil 		kfree(tpg);
2633699d92aSKiran Patil 		return NULL;
2643699d92aSKiran Patil 	}
26506383f10SMark Rustad 	tpg->workqueue = wq;
2663699d92aSKiran Patil 
2673699d92aSKiran Patil 	mutex_lock(&ft_lport_lock);
268705665daSAndy Grover 	ft_wwn->tpg = tpg;
2693699d92aSKiran Patil 	mutex_unlock(&ft_lport_lock);
2703699d92aSKiran Patil 
2713699d92aSKiran Patil 	return &tpg->se_tpg;
2723699d92aSKiran Patil }
2733699d92aSKiran Patil 
ft_del_tpg(struct se_portal_group * se_tpg)2743699d92aSKiran Patil static void ft_del_tpg(struct se_portal_group *se_tpg)
2753699d92aSKiran Patil {
2763699d92aSKiran Patil 	struct ft_tpg *tpg = container_of(se_tpg, struct ft_tpg, se_tpg);
277705665daSAndy Grover 	struct ft_lport_wwn *ft_wwn = tpg->lport_wwn;
2783699d92aSKiran Patil 
2796708bb27SAndy Grover 	pr_debug("del tpg %s\n",
2803699d92aSKiran Patil 		    config_item_name(&tpg->se_tpg.tpg_group.cg_item));
2813699d92aSKiran Patil 
28258fc73d1SChristoph Hellwig 	destroy_workqueue(tpg->workqueue);
2833699d92aSKiran Patil 
2843699d92aSKiran Patil 	/* Wait for sessions to be freed thru RCU, for BUG_ON below */
2853699d92aSKiran Patil 	synchronize_rcu();
2863699d92aSKiran Patil 
2873699d92aSKiran Patil 	mutex_lock(&ft_lport_lock);
288705665daSAndy Grover 	ft_wwn->tpg = NULL;
2893699d92aSKiran Patil 	if (tpg->tport) {
2903699d92aSKiran Patil 		tpg->tport->tpg = NULL;
2913699d92aSKiran Patil 		tpg->tport = NULL;
2923699d92aSKiran Patil 	}
2933699d92aSKiran Patil 	mutex_unlock(&ft_lport_lock);
2943699d92aSKiran Patil 
2953699d92aSKiran Patil 	core_tpg_deregister(se_tpg);
2963699d92aSKiran Patil 	kfree(tpg);
2973699d92aSKiran Patil }
2983699d92aSKiran Patil 
2993699d92aSKiran Patil /*
3003699d92aSKiran Patil  * Verify that an lport is configured to use the tcm_fc module, and return
3013699d92aSKiran Patil  * the target port group that should be used.
3023699d92aSKiran Patil  *
3033699d92aSKiran Patil  * The caller holds ft_lport_lock.
3043699d92aSKiran Patil  */
ft_lport_find_tpg(struct fc_lport * lport)3053699d92aSKiran Patil struct ft_tpg *ft_lport_find_tpg(struct fc_lport *lport)
3063699d92aSKiran Patil {
307705665daSAndy Grover 	struct ft_lport_wwn *ft_wwn;
3083699d92aSKiran Patil 
309705665daSAndy Grover 	list_for_each_entry(ft_wwn, &ft_wwn_list, ft_wwn_node) {
310705665daSAndy Grover 		if (ft_wwn->wwpn == lport->wwpn)
311705665daSAndy Grover 			return ft_wwn->tpg;
3123699d92aSKiran Patil 	}
3133699d92aSKiran Patil 	return NULL;
3143699d92aSKiran Patil }
3153699d92aSKiran Patil 
3163699d92aSKiran Patil /*
3173699d92aSKiran Patil  * target config instance ops.
3183699d92aSKiran Patil  */
3193699d92aSKiran Patil 
3203699d92aSKiran Patil /*
3213699d92aSKiran Patil  * Add lport to allowed config.
3223699d92aSKiran Patil  * The name is the WWPN in lower-case ASCII, colon-separated bytes.
3233699d92aSKiran Patil  */
ft_add_wwn(struct target_fabric_configfs * tf,struct config_group * group,const char * name)3240d7cb932SAndy Grover static struct se_wwn *ft_add_wwn(
3253699d92aSKiran Patil 	struct target_fabric_configfs *tf,
3263699d92aSKiran Patil 	struct config_group *group,
3273699d92aSKiran Patil 	const char *name)
3283699d92aSKiran Patil {
329705665daSAndy Grover 	struct ft_lport_wwn *ft_wwn;
330705665daSAndy Grover 	struct ft_lport_wwn *old_ft_wwn;
3313699d92aSKiran Patil 	u64 wwpn;
3323699d92aSKiran Patil 
3330d7cb932SAndy Grover 	pr_debug("add wwn %s\n", name);
3343699d92aSKiran Patil 	if (ft_parse_wwn(name, &wwpn, 1) < 0)
3353699d92aSKiran Patil 		return NULL;
336705665daSAndy Grover 	ft_wwn = kzalloc(sizeof(*ft_wwn), GFP_KERNEL);
337705665daSAndy Grover 	if (!ft_wwn)
3383699d92aSKiran Patil 		return NULL;
339705665daSAndy Grover 	ft_wwn->wwpn = wwpn;
3403699d92aSKiran Patil 
3413699d92aSKiran Patil 	mutex_lock(&ft_lport_lock);
342705665daSAndy Grover 	list_for_each_entry(old_ft_wwn, &ft_wwn_list, ft_wwn_node) {
343705665daSAndy Grover 		if (old_ft_wwn->wwpn == wwpn) {
3443699d92aSKiran Patil 			mutex_unlock(&ft_lport_lock);
345705665daSAndy Grover 			kfree(ft_wwn);
3463699d92aSKiran Patil 			return NULL;
3473699d92aSKiran Patil 		}
3483699d92aSKiran Patil 	}
349705665daSAndy Grover 	list_add_tail(&ft_wwn->ft_wwn_node, &ft_wwn_list);
350705665daSAndy Grover 	ft_format_wwn(ft_wwn->name, sizeof(ft_wwn->name), wwpn);
3513699d92aSKiran Patil 	mutex_unlock(&ft_lport_lock);
3523699d92aSKiran Patil 
353705665daSAndy Grover 	return &ft_wwn->se_wwn;
3543699d92aSKiran Patil }
3553699d92aSKiran Patil 
ft_del_wwn(struct se_wwn * wwn)3560d7cb932SAndy Grover static void ft_del_wwn(struct se_wwn *wwn)
3573699d92aSKiran Patil {
358705665daSAndy Grover 	struct ft_lport_wwn *ft_wwn = container_of(wwn,
359705665daSAndy Grover 				struct ft_lport_wwn, se_wwn);
3603699d92aSKiran Patil 
3610d7cb932SAndy Grover 	pr_debug("del wwn %s\n", ft_wwn->name);
3623699d92aSKiran Patil 	mutex_lock(&ft_lport_lock);
363705665daSAndy Grover 	list_del(&ft_wwn->ft_wwn_node);
3643699d92aSKiran Patil 	mutex_unlock(&ft_lport_lock);
3653699d92aSKiran Patil 
366705665daSAndy Grover 	kfree(ft_wwn);
3673699d92aSKiran Patil }
3683699d92aSKiran Patil 
ft_wwn_version_show(struct config_item * item,char * page)3692eafd729SChristoph Hellwig static ssize_t ft_wwn_version_show(struct config_item *item, char *page)
3703699d92aSKiran Patil {
3713699d92aSKiran Patil 	return sprintf(page, "TCM FC " FT_VERSION " on %s/%s on "
3723699d92aSKiran Patil 		""UTS_RELEASE"\n",  utsname()->sysname, utsname()->machine);
3733699d92aSKiran Patil }
3743699d92aSKiran Patil 
3752eafd729SChristoph Hellwig CONFIGFS_ATTR_RO(ft_wwn_, version);
3763699d92aSKiran Patil 
3773699d92aSKiran Patil static struct configfs_attribute *ft_wwn_attrs[] = {
3782eafd729SChristoph Hellwig 	&ft_wwn_attr_version,
3793699d92aSKiran Patil 	NULL,
3803699d92aSKiran Patil };
3813699d92aSKiran Patil 
ft_tpg(struct se_portal_group * se_tpg)3823868e436SChristoph Hellwig static inline struct ft_tpg *ft_tpg(struct se_portal_group *se_tpg)
3833868e436SChristoph Hellwig {
3843868e436SChristoph Hellwig 	return container_of(se_tpg, struct ft_tpg, se_tpg);
3853868e436SChristoph Hellwig }
3863868e436SChristoph Hellwig 
ft_get_fabric_wwn(struct se_portal_group * se_tpg)3873699d92aSKiran Patil static char *ft_get_fabric_wwn(struct se_portal_group *se_tpg)
3883699d92aSKiran Patil {
3893868e436SChristoph Hellwig 	return ft_tpg(se_tpg)->lport_wwn->name;
3903699d92aSKiran Patil }
3913699d92aSKiran Patil 
ft_get_tag(struct se_portal_group * se_tpg)3923699d92aSKiran Patil static u16 ft_get_tag(struct se_portal_group *se_tpg)
3933699d92aSKiran Patil {
3943699d92aSKiran Patil 	/*
3953699d92aSKiran Patil 	 * This tag is used when forming SCSI Name identifier in EVPD=1 0x83
3963699d92aSKiran Patil 	 * to represent the SCSI Target Port.
3973699d92aSKiran Patil 	 */
3983868e436SChristoph Hellwig 	return ft_tpg(se_tpg)->index;
3993699d92aSKiran Patil }
4003699d92aSKiran Patil 
ft_tpg_get_inst_index(struct se_portal_group * se_tpg)4013699d92aSKiran Patil static u32 ft_tpg_get_inst_index(struct se_portal_group *se_tpg)
4023699d92aSKiran Patil {
4033868e436SChristoph Hellwig 	return ft_tpg(se_tpg)->index;
4043699d92aSKiran Patil }
4053699d92aSKiran Patil 
4069ac8928eSChristoph Hellwig static const struct target_core_fabric_ops ft_fabric_ops = {
4079ac8928eSChristoph Hellwig 	.module =			THIS_MODULE,
40830c7ca93SDavid Disseldorp 	.fabric_name =			"fc",
409144bc4c2SChristoph Hellwig 	.node_acl_size =		sizeof(struct ft_node_acl),
4103699d92aSKiran Patil 	.tpg_get_wwn =			ft_get_fabric_wwn,
4113699d92aSKiran Patil 	.tpg_get_tag =			ft_get_tag,
4123699d92aSKiran Patil 	.tpg_get_inst_index =		ft_tpg_get_inst_index,
4133699d92aSKiran Patil 	.check_stop_free =		ft_check_stop_free,
41435462975SChristoph Hellwig 	.release_cmd =			ft_release_cmd,
4153699d92aSKiran Patil 	.close_session =		ft_sess_close,
4163699d92aSKiran Patil 	.sess_get_index =		ft_sess_get_index,
4173699d92aSKiran Patil 	.sess_get_initiator_sid =	NULL,
4183699d92aSKiran Patil 	.write_pending =		ft_write_pending,
4193699d92aSKiran Patil 	.queue_data_in =		ft_queue_data_in,
4203699d92aSKiran Patil 	.queue_status =			ft_queue_status,
4213699d92aSKiran Patil 	.queue_tm_rsp =			ft_queue_tm_resp,
422131e6abcSNicholas Bellinger 	.aborted_task =			ft_aborted_task,
4233699d92aSKiran Patil 	/*
4243699d92aSKiran Patil 	 * Setup function pointers for generic logic in
4253699d92aSKiran Patil 	 * target_core_fabric_configfs.c
4263699d92aSKiran Patil 	 */
4270d7cb932SAndy Grover 	.fabric_make_wwn =		&ft_add_wwn,
4280d7cb932SAndy Grover 	.fabric_drop_wwn =		&ft_del_wwn,
4293699d92aSKiran Patil 	.fabric_make_tpg =		&ft_add_tpg,
4303699d92aSKiran Patil 	.fabric_drop_tpg =		&ft_del_tpg,
431c7d6a803SChristoph Hellwig 	.fabric_init_nodeacl =		&ft_init_nodeacl,
4329ac8928eSChristoph Hellwig 
4339ac8928eSChristoph Hellwig 	.tfc_wwn_attrs			= ft_wwn_attrs,
4349ac8928eSChristoph Hellwig 	.tfc_tpg_nacl_base_attrs	= ft_nacl_base_attrs,
435*194605d4SMike Christie 
436*194605d4SMike Christie 	.default_submit_type		= TARGET_DIRECT_SUBMIT,
437*194605d4SMike Christie 	.direct_submit_supp		= 1,
4383699d92aSKiran Patil };
4393699d92aSKiran Patil 
4403699d92aSKiran Patil static struct notifier_block ft_notifier = {
4413699d92aSKiran Patil 	.notifier_call = ft_lport_notify
4423699d92aSKiran Patil };
4433699d92aSKiran Patil 
ft_init(void)4443699d92aSKiran Patil static int __init ft_init(void)
4453699d92aSKiran Patil {
4469ac8928eSChristoph Hellwig 	int ret;
4479ac8928eSChristoph Hellwig 
4489ac8928eSChristoph Hellwig 	ret = target_register_template(&ft_fabric_ops);
4499ac8928eSChristoph Hellwig 	if (ret)
4509ac8928eSChristoph Hellwig 		goto out;
4519ac8928eSChristoph Hellwig 
4529ac8928eSChristoph Hellwig 	ret = fc_fc4_register_provider(FC_TYPE_FCP, &ft_prov);
4539ac8928eSChristoph Hellwig 	if (ret)
4549ac8928eSChristoph Hellwig 		goto out_unregister_template;
4559ac8928eSChristoph Hellwig 
4563699d92aSKiran Patil 	blocking_notifier_chain_register(&fc_lport_notifier_head, &ft_notifier);
4573699d92aSKiran Patil 	fc_lport_iterate(ft_lport_add, NULL);
4583699d92aSKiran Patil 	return 0;
4599ac8928eSChristoph Hellwig 
4609ac8928eSChristoph Hellwig out_unregister_template:
4619ac8928eSChristoph Hellwig 	target_unregister_template(&ft_fabric_ops);
4629ac8928eSChristoph Hellwig out:
4639ac8928eSChristoph Hellwig 	return ret;
4643699d92aSKiran Patil }
4653699d92aSKiran Patil 
ft_exit(void)4663699d92aSKiran Patil static void __exit ft_exit(void)
4673699d92aSKiran Patil {
4683699d92aSKiran Patil 	blocking_notifier_chain_unregister(&fc_lport_notifier_head,
4693699d92aSKiran Patil 					   &ft_notifier);
4703699d92aSKiran Patil 	fc_fc4_deregister_provider(FC_TYPE_FCP, &ft_prov);
4713699d92aSKiran Patil 	fc_lport_iterate(ft_lport_del, NULL);
4729ac8928eSChristoph Hellwig 	target_unregister_template(&ft_fabric_ops);
4733699d92aSKiran Patil 	synchronize_rcu();
4743699d92aSKiran Patil }
4753699d92aSKiran Patil 
4763699d92aSKiran Patil MODULE_DESCRIPTION("FC TCM fabric driver " FT_VERSION);
4773699d92aSKiran Patil MODULE_LICENSE("GPL");
4783699d92aSKiran Patil module_init(ft_init);
4793699d92aSKiran Patil module_exit(ft_exit);
480