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