162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 262306a36Sopenharmony_ci/******************************************************************************* 362306a36Sopenharmony_ci * Filename: tcm_fc.c 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * This file contains the configfs implementation for TCM_fc fabric node. 662306a36Sopenharmony_ci * Based on tcm_loop_configfs.c 762306a36Sopenharmony_ci * 862306a36Sopenharmony_ci * Copyright (c) 2010 Cisco Systems, Inc. 962306a36Sopenharmony_ci * Copyright (c) 2009,2010 Rising Tide, Inc. 1062306a36Sopenharmony_ci * Copyright (c) 2009,2010 Linux-iSCSI.org 1162306a36Sopenharmony_ci * 1262306a36Sopenharmony_ci * Copyright (c) 2009,2010 Nicholas A. Bellinger <nab@linux-iscsi.org> 1362306a36Sopenharmony_ci * 1462306a36Sopenharmony_ci ****************************************************************************/ 1562306a36Sopenharmony_ci 1662306a36Sopenharmony_ci#include <linux/module.h> 1762306a36Sopenharmony_ci#include <linux/moduleparam.h> 1862306a36Sopenharmony_ci#include <generated/utsrelease.h> 1962306a36Sopenharmony_ci#include <linux/utsname.h> 2062306a36Sopenharmony_ci#include <linux/init.h> 2162306a36Sopenharmony_ci#include <linux/slab.h> 2262306a36Sopenharmony_ci#include <linux/kthread.h> 2362306a36Sopenharmony_ci#include <linux/types.h> 2462306a36Sopenharmony_ci#include <linux/string.h> 2562306a36Sopenharmony_ci#include <linux/configfs.h> 2662306a36Sopenharmony_ci#include <linux/kernel.h> 2762306a36Sopenharmony_ci#include <linux/ctype.h> 2862306a36Sopenharmony_ci#include <asm/unaligned.h> 2962306a36Sopenharmony_ci#include <scsi/libfc.h> 3062306a36Sopenharmony_ci 3162306a36Sopenharmony_ci#include <target/target_core_base.h> 3262306a36Sopenharmony_ci#include <target/target_core_fabric.h> 3362306a36Sopenharmony_ci 3462306a36Sopenharmony_ci#include "tcm_fc.h" 3562306a36Sopenharmony_ci 3662306a36Sopenharmony_cistatic LIST_HEAD(ft_wwn_list); 3762306a36Sopenharmony_ciDEFINE_MUTEX(ft_lport_lock); 3862306a36Sopenharmony_ci 3962306a36Sopenharmony_ciunsigned int ft_debug_logging; 4062306a36Sopenharmony_cimodule_param_named(debug_logging, ft_debug_logging, int, S_IRUGO|S_IWUSR); 4162306a36Sopenharmony_ciMODULE_PARM_DESC(debug_logging, "a bit mask of logging levels"); 4262306a36Sopenharmony_ci 4362306a36Sopenharmony_ci/* 4462306a36Sopenharmony_ci * Parse WWN. 4562306a36Sopenharmony_ci * If strict, we require lower-case hex and colon separators to be sure 4662306a36Sopenharmony_ci * the name is the same as what would be generated by ft_format_wwn() 4762306a36Sopenharmony_ci * so the name and wwn are mapped one-to-one. 4862306a36Sopenharmony_ci */ 4962306a36Sopenharmony_cistatic ssize_t ft_parse_wwn(const char *name, u64 *wwn, int strict) 5062306a36Sopenharmony_ci{ 5162306a36Sopenharmony_ci const char *cp; 5262306a36Sopenharmony_ci char c; 5362306a36Sopenharmony_ci u32 byte = 0; 5462306a36Sopenharmony_ci u32 pos = 0; 5562306a36Sopenharmony_ci u32 err; 5662306a36Sopenharmony_ci int val; 5762306a36Sopenharmony_ci 5862306a36Sopenharmony_ci *wwn = 0; 5962306a36Sopenharmony_ci for (cp = name; cp < &name[FT_NAMELEN - 1]; cp++) { 6062306a36Sopenharmony_ci c = *cp; 6162306a36Sopenharmony_ci if (c == '\n' && cp[1] == '\0') 6262306a36Sopenharmony_ci continue; 6362306a36Sopenharmony_ci if (strict && pos++ == 2 && byte++ < 7) { 6462306a36Sopenharmony_ci pos = 0; 6562306a36Sopenharmony_ci if (c == ':') 6662306a36Sopenharmony_ci continue; 6762306a36Sopenharmony_ci err = 1; 6862306a36Sopenharmony_ci goto fail; 6962306a36Sopenharmony_ci } 7062306a36Sopenharmony_ci if (c == '\0') { 7162306a36Sopenharmony_ci err = 2; 7262306a36Sopenharmony_ci if (strict && byte != 8) 7362306a36Sopenharmony_ci goto fail; 7462306a36Sopenharmony_ci return cp - name; 7562306a36Sopenharmony_ci } 7662306a36Sopenharmony_ci err = 3; 7762306a36Sopenharmony_ci val = hex_to_bin(c); 7862306a36Sopenharmony_ci if (val < 0 || (strict && isupper(c))) 7962306a36Sopenharmony_ci goto fail; 8062306a36Sopenharmony_ci *wwn = (*wwn << 4) | val; 8162306a36Sopenharmony_ci } 8262306a36Sopenharmony_ci err = 4; 8362306a36Sopenharmony_cifail: 8462306a36Sopenharmony_ci pr_debug("err %u len %zu pos %u byte %u\n", 8562306a36Sopenharmony_ci err, cp - name, pos, byte); 8662306a36Sopenharmony_ci return -1; 8762306a36Sopenharmony_ci} 8862306a36Sopenharmony_ci 8962306a36Sopenharmony_cissize_t ft_format_wwn(char *buf, size_t len, u64 wwn) 9062306a36Sopenharmony_ci{ 9162306a36Sopenharmony_ci u8 b[8]; 9262306a36Sopenharmony_ci 9362306a36Sopenharmony_ci put_unaligned_be64(wwn, b); 9462306a36Sopenharmony_ci return snprintf(buf, len, 9562306a36Sopenharmony_ci "%2.2x:%2.2x:%2.2x:%2.2x:%2.2x:%2.2x:%2.2x:%2.2x", 9662306a36Sopenharmony_ci b[0], b[1], b[2], b[3], b[4], b[5], b[6], b[7]); 9762306a36Sopenharmony_ci} 9862306a36Sopenharmony_ci 9962306a36Sopenharmony_cistatic ssize_t ft_wwn_show(void *arg, char *buf) 10062306a36Sopenharmony_ci{ 10162306a36Sopenharmony_ci u64 *wwn = arg; 10262306a36Sopenharmony_ci ssize_t len; 10362306a36Sopenharmony_ci 10462306a36Sopenharmony_ci len = ft_format_wwn(buf, PAGE_SIZE - 2, *wwn); 10562306a36Sopenharmony_ci buf[len++] = '\n'; 10662306a36Sopenharmony_ci return len; 10762306a36Sopenharmony_ci} 10862306a36Sopenharmony_ci 10962306a36Sopenharmony_cistatic ssize_t ft_wwn_store(void *arg, const char *buf, size_t len) 11062306a36Sopenharmony_ci{ 11162306a36Sopenharmony_ci ssize_t ret; 11262306a36Sopenharmony_ci u64 wwn; 11362306a36Sopenharmony_ci 11462306a36Sopenharmony_ci ret = ft_parse_wwn(buf, &wwn, 0); 11562306a36Sopenharmony_ci if (ret > 0) 11662306a36Sopenharmony_ci *(u64 *)arg = wwn; 11762306a36Sopenharmony_ci return ret; 11862306a36Sopenharmony_ci} 11962306a36Sopenharmony_ci 12062306a36Sopenharmony_ci/* 12162306a36Sopenharmony_ci * ACL auth ops. 12262306a36Sopenharmony_ci */ 12362306a36Sopenharmony_ci 12462306a36Sopenharmony_cistatic ssize_t ft_nacl_port_name_show(struct config_item *item, char *page) 12562306a36Sopenharmony_ci{ 12662306a36Sopenharmony_ci struct se_node_acl *se_nacl = acl_to_nacl(item); 12762306a36Sopenharmony_ci struct ft_node_acl *acl = container_of(se_nacl, 12862306a36Sopenharmony_ci struct ft_node_acl, se_node_acl); 12962306a36Sopenharmony_ci 13062306a36Sopenharmony_ci return ft_wwn_show(&acl->node_auth.port_name, page); 13162306a36Sopenharmony_ci} 13262306a36Sopenharmony_ci 13362306a36Sopenharmony_cistatic ssize_t ft_nacl_port_name_store(struct config_item *item, 13462306a36Sopenharmony_ci const char *page, size_t count) 13562306a36Sopenharmony_ci{ 13662306a36Sopenharmony_ci struct se_node_acl *se_nacl = acl_to_nacl(item); 13762306a36Sopenharmony_ci struct ft_node_acl *acl = container_of(se_nacl, 13862306a36Sopenharmony_ci struct ft_node_acl, se_node_acl); 13962306a36Sopenharmony_ci 14062306a36Sopenharmony_ci return ft_wwn_store(&acl->node_auth.port_name, page, count); 14162306a36Sopenharmony_ci} 14262306a36Sopenharmony_ci 14362306a36Sopenharmony_cistatic ssize_t ft_nacl_node_name_show(struct config_item *item, 14462306a36Sopenharmony_ci char *page) 14562306a36Sopenharmony_ci{ 14662306a36Sopenharmony_ci struct se_node_acl *se_nacl = acl_to_nacl(item); 14762306a36Sopenharmony_ci struct ft_node_acl *acl = container_of(se_nacl, 14862306a36Sopenharmony_ci struct ft_node_acl, se_node_acl); 14962306a36Sopenharmony_ci 15062306a36Sopenharmony_ci return ft_wwn_show(&acl->node_auth.node_name, page); 15162306a36Sopenharmony_ci} 15262306a36Sopenharmony_ci 15362306a36Sopenharmony_cistatic ssize_t ft_nacl_node_name_store(struct config_item *item, 15462306a36Sopenharmony_ci const char *page, size_t count) 15562306a36Sopenharmony_ci{ 15662306a36Sopenharmony_ci struct se_node_acl *se_nacl = acl_to_nacl(item); 15762306a36Sopenharmony_ci struct ft_node_acl *acl = container_of(se_nacl, 15862306a36Sopenharmony_ci struct ft_node_acl, se_node_acl); 15962306a36Sopenharmony_ci 16062306a36Sopenharmony_ci return ft_wwn_store(&acl->node_auth.node_name, page, count); 16162306a36Sopenharmony_ci} 16262306a36Sopenharmony_ci 16362306a36Sopenharmony_ciCONFIGFS_ATTR(ft_nacl_, node_name); 16462306a36Sopenharmony_ciCONFIGFS_ATTR(ft_nacl_, port_name); 16562306a36Sopenharmony_ci 16662306a36Sopenharmony_cistatic ssize_t ft_nacl_tag_show(struct config_item *item, 16762306a36Sopenharmony_ci char *page) 16862306a36Sopenharmony_ci{ 16962306a36Sopenharmony_ci return snprintf(page, PAGE_SIZE, "%s", acl_to_nacl(item)->acl_tag); 17062306a36Sopenharmony_ci} 17162306a36Sopenharmony_ci 17262306a36Sopenharmony_cistatic ssize_t ft_nacl_tag_store(struct config_item *item, 17362306a36Sopenharmony_ci const char *page, size_t count) 17462306a36Sopenharmony_ci{ 17562306a36Sopenharmony_ci struct se_node_acl *se_nacl = acl_to_nacl(item); 17662306a36Sopenharmony_ci int ret; 17762306a36Sopenharmony_ci 17862306a36Sopenharmony_ci ret = core_tpg_set_initiator_node_tag(se_nacl->se_tpg, se_nacl, page); 17962306a36Sopenharmony_ci 18062306a36Sopenharmony_ci if (ret < 0) 18162306a36Sopenharmony_ci return ret; 18262306a36Sopenharmony_ci return count; 18362306a36Sopenharmony_ci} 18462306a36Sopenharmony_ci 18562306a36Sopenharmony_ciCONFIGFS_ATTR(ft_nacl_, tag); 18662306a36Sopenharmony_ci 18762306a36Sopenharmony_cistatic struct configfs_attribute *ft_nacl_base_attrs[] = { 18862306a36Sopenharmony_ci &ft_nacl_attr_port_name, 18962306a36Sopenharmony_ci &ft_nacl_attr_node_name, 19062306a36Sopenharmony_ci &ft_nacl_attr_tag, 19162306a36Sopenharmony_ci NULL, 19262306a36Sopenharmony_ci}; 19362306a36Sopenharmony_ci 19462306a36Sopenharmony_ci/* 19562306a36Sopenharmony_ci * ACL ops. 19662306a36Sopenharmony_ci */ 19762306a36Sopenharmony_ci 19862306a36Sopenharmony_ci/* 19962306a36Sopenharmony_ci * Add ACL for an initiator. The ACL is named arbitrarily. 20062306a36Sopenharmony_ci * The port_name and/or node_name are attributes. 20162306a36Sopenharmony_ci */ 20262306a36Sopenharmony_cistatic int ft_init_nodeacl(struct se_node_acl *nacl, const char *name) 20362306a36Sopenharmony_ci{ 20462306a36Sopenharmony_ci struct ft_node_acl *acl = 20562306a36Sopenharmony_ci container_of(nacl, struct ft_node_acl, se_node_acl); 20662306a36Sopenharmony_ci u64 wwpn; 20762306a36Sopenharmony_ci 20862306a36Sopenharmony_ci if (ft_parse_wwn(name, &wwpn, 1) < 0) 20962306a36Sopenharmony_ci return -EINVAL; 21062306a36Sopenharmony_ci 21162306a36Sopenharmony_ci acl->node_auth.port_name = wwpn; 21262306a36Sopenharmony_ci return 0; 21362306a36Sopenharmony_ci} 21462306a36Sopenharmony_ci 21562306a36Sopenharmony_ci/* 21662306a36Sopenharmony_ci * local_port port_group (tpg) ops. 21762306a36Sopenharmony_ci */ 21862306a36Sopenharmony_cistatic struct se_portal_group *ft_add_tpg(struct se_wwn *wwn, const char *name) 21962306a36Sopenharmony_ci{ 22062306a36Sopenharmony_ci struct ft_lport_wwn *ft_wwn; 22162306a36Sopenharmony_ci struct ft_tpg *tpg; 22262306a36Sopenharmony_ci struct workqueue_struct *wq; 22362306a36Sopenharmony_ci unsigned long index; 22462306a36Sopenharmony_ci int ret; 22562306a36Sopenharmony_ci 22662306a36Sopenharmony_ci pr_debug("tcm_fc: add tpg %s\n", name); 22762306a36Sopenharmony_ci 22862306a36Sopenharmony_ci /* 22962306a36Sopenharmony_ci * Name must be "tpgt_" followed by the index. 23062306a36Sopenharmony_ci */ 23162306a36Sopenharmony_ci if (strstr(name, "tpgt_") != name) 23262306a36Sopenharmony_ci return NULL; 23362306a36Sopenharmony_ci 23462306a36Sopenharmony_ci ret = kstrtoul(name + 5, 10, &index); 23562306a36Sopenharmony_ci if (ret) 23662306a36Sopenharmony_ci return NULL; 23762306a36Sopenharmony_ci if (index > UINT_MAX) 23862306a36Sopenharmony_ci return NULL; 23962306a36Sopenharmony_ci 24062306a36Sopenharmony_ci if ((index != 1)) { 24162306a36Sopenharmony_ci pr_err("Error, a single TPG=1 is used for HW port mappings\n"); 24262306a36Sopenharmony_ci return ERR_PTR(-ENOSYS); 24362306a36Sopenharmony_ci } 24462306a36Sopenharmony_ci 24562306a36Sopenharmony_ci ft_wwn = container_of(wwn, struct ft_lport_wwn, se_wwn); 24662306a36Sopenharmony_ci tpg = kzalloc(sizeof(*tpg), GFP_KERNEL); 24762306a36Sopenharmony_ci if (!tpg) 24862306a36Sopenharmony_ci return NULL; 24962306a36Sopenharmony_ci tpg->index = index; 25062306a36Sopenharmony_ci tpg->lport_wwn = ft_wwn; 25162306a36Sopenharmony_ci INIT_LIST_HEAD(&tpg->lun_list); 25262306a36Sopenharmony_ci 25362306a36Sopenharmony_ci wq = alloc_workqueue("tcm_fc", 0, 1); 25462306a36Sopenharmony_ci if (!wq) { 25562306a36Sopenharmony_ci kfree(tpg); 25662306a36Sopenharmony_ci return NULL; 25762306a36Sopenharmony_ci } 25862306a36Sopenharmony_ci 25962306a36Sopenharmony_ci ret = core_tpg_register(wwn, &tpg->se_tpg, SCSI_PROTOCOL_FCP); 26062306a36Sopenharmony_ci if (ret < 0) { 26162306a36Sopenharmony_ci destroy_workqueue(wq); 26262306a36Sopenharmony_ci kfree(tpg); 26362306a36Sopenharmony_ci return NULL; 26462306a36Sopenharmony_ci } 26562306a36Sopenharmony_ci tpg->workqueue = wq; 26662306a36Sopenharmony_ci 26762306a36Sopenharmony_ci mutex_lock(&ft_lport_lock); 26862306a36Sopenharmony_ci ft_wwn->tpg = tpg; 26962306a36Sopenharmony_ci mutex_unlock(&ft_lport_lock); 27062306a36Sopenharmony_ci 27162306a36Sopenharmony_ci return &tpg->se_tpg; 27262306a36Sopenharmony_ci} 27362306a36Sopenharmony_ci 27462306a36Sopenharmony_cistatic void ft_del_tpg(struct se_portal_group *se_tpg) 27562306a36Sopenharmony_ci{ 27662306a36Sopenharmony_ci struct ft_tpg *tpg = container_of(se_tpg, struct ft_tpg, se_tpg); 27762306a36Sopenharmony_ci struct ft_lport_wwn *ft_wwn = tpg->lport_wwn; 27862306a36Sopenharmony_ci 27962306a36Sopenharmony_ci pr_debug("del tpg %s\n", 28062306a36Sopenharmony_ci config_item_name(&tpg->se_tpg.tpg_group.cg_item)); 28162306a36Sopenharmony_ci 28262306a36Sopenharmony_ci destroy_workqueue(tpg->workqueue); 28362306a36Sopenharmony_ci 28462306a36Sopenharmony_ci /* Wait for sessions to be freed thru RCU, for BUG_ON below */ 28562306a36Sopenharmony_ci synchronize_rcu(); 28662306a36Sopenharmony_ci 28762306a36Sopenharmony_ci mutex_lock(&ft_lport_lock); 28862306a36Sopenharmony_ci ft_wwn->tpg = NULL; 28962306a36Sopenharmony_ci if (tpg->tport) { 29062306a36Sopenharmony_ci tpg->tport->tpg = NULL; 29162306a36Sopenharmony_ci tpg->tport = NULL; 29262306a36Sopenharmony_ci } 29362306a36Sopenharmony_ci mutex_unlock(&ft_lport_lock); 29462306a36Sopenharmony_ci 29562306a36Sopenharmony_ci core_tpg_deregister(se_tpg); 29662306a36Sopenharmony_ci kfree(tpg); 29762306a36Sopenharmony_ci} 29862306a36Sopenharmony_ci 29962306a36Sopenharmony_ci/* 30062306a36Sopenharmony_ci * Verify that an lport is configured to use the tcm_fc module, and return 30162306a36Sopenharmony_ci * the target port group that should be used. 30262306a36Sopenharmony_ci * 30362306a36Sopenharmony_ci * The caller holds ft_lport_lock. 30462306a36Sopenharmony_ci */ 30562306a36Sopenharmony_cistruct ft_tpg *ft_lport_find_tpg(struct fc_lport *lport) 30662306a36Sopenharmony_ci{ 30762306a36Sopenharmony_ci struct ft_lport_wwn *ft_wwn; 30862306a36Sopenharmony_ci 30962306a36Sopenharmony_ci list_for_each_entry(ft_wwn, &ft_wwn_list, ft_wwn_node) { 31062306a36Sopenharmony_ci if (ft_wwn->wwpn == lport->wwpn) 31162306a36Sopenharmony_ci return ft_wwn->tpg; 31262306a36Sopenharmony_ci } 31362306a36Sopenharmony_ci return NULL; 31462306a36Sopenharmony_ci} 31562306a36Sopenharmony_ci 31662306a36Sopenharmony_ci/* 31762306a36Sopenharmony_ci * target config instance ops. 31862306a36Sopenharmony_ci */ 31962306a36Sopenharmony_ci 32062306a36Sopenharmony_ci/* 32162306a36Sopenharmony_ci * Add lport to allowed config. 32262306a36Sopenharmony_ci * The name is the WWPN in lower-case ASCII, colon-separated bytes. 32362306a36Sopenharmony_ci */ 32462306a36Sopenharmony_cistatic struct se_wwn *ft_add_wwn( 32562306a36Sopenharmony_ci struct target_fabric_configfs *tf, 32662306a36Sopenharmony_ci struct config_group *group, 32762306a36Sopenharmony_ci const char *name) 32862306a36Sopenharmony_ci{ 32962306a36Sopenharmony_ci struct ft_lport_wwn *ft_wwn; 33062306a36Sopenharmony_ci struct ft_lport_wwn *old_ft_wwn; 33162306a36Sopenharmony_ci u64 wwpn; 33262306a36Sopenharmony_ci 33362306a36Sopenharmony_ci pr_debug("add wwn %s\n", name); 33462306a36Sopenharmony_ci if (ft_parse_wwn(name, &wwpn, 1) < 0) 33562306a36Sopenharmony_ci return NULL; 33662306a36Sopenharmony_ci ft_wwn = kzalloc(sizeof(*ft_wwn), GFP_KERNEL); 33762306a36Sopenharmony_ci if (!ft_wwn) 33862306a36Sopenharmony_ci return NULL; 33962306a36Sopenharmony_ci ft_wwn->wwpn = wwpn; 34062306a36Sopenharmony_ci 34162306a36Sopenharmony_ci mutex_lock(&ft_lport_lock); 34262306a36Sopenharmony_ci list_for_each_entry(old_ft_wwn, &ft_wwn_list, ft_wwn_node) { 34362306a36Sopenharmony_ci if (old_ft_wwn->wwpn == wwpn) { 34462306a36Sopenharmony_ci mutex_unlock(&ft_lport_lock); 34562306a36Sopenharmony_ci kfree(ft_wwn); 34662306a36Sopenharmony_ci return NULL; 34762306a36Sopenharmony_ci } 34862306a36Sopenharmony_ci } 34962306a36Sopenharmony_ci list_add_tail(&ft_wwn->ft_wwn_node, &ft_wwn_list); 35062306a36Sopenharmony_ci ft_format_wwn(ft_wwn->name, sizeof(ft_wwn->name), wwpn); 35162306a36Sopenharmony_ci mutex_unlock(&ft_lport_lock); 35262306a36Sopenharmony_ci 35362306a36Sopenharmony_ci return &ft_wwn->se_wwn; 35462306a36Sopenharmony_ci} 35562306a36Sopenharmony_ci 35662306a36Sopenharmony_cistatic void ft_del_wwn(struct se_wwn *wwn) 35762306a36Sopenharmony_ci{ 35862306a36Sopenharmony_ci struct ft_lport_wwn *ft_wwn = container_of(wwn, 35962306a36Sopenharmony_ci struct ft_lport_wwn, se_wwn); 36062306a36Sopenharmony_ci 36162306a36Sopenharmony_ci pr_debug("del wwn %s\n", ft_wwn->name); 36262306a36Sopenharmony_ci mutex_lock(&ft_lport_lock); 36362306a36Sopenharmony_ci list_del(&ft_wwn->ft_wwn_node); 36462306a36Sopenharmony_ci mutex_unlock(&ft_lport_lock); 36562306a36Sopenharmony_ci 36662306a36Sopenharmony_ci kfree(ft_wwn); 36762306a36Sopenharmony_ci} 36862306a36Sopenharmony_ci 36962306a36Sopenharmony_cistatic ssize_t ft_wwn_version_show(struct config_item *item, char *page) 37062306a36Sopenharmony_ci{ 37162306a36Sopenharmony_ci return sprintf(page, "TCM FC " FT_VERSION " on %s/%s on " 37262306a36Sopenharmony_ci ""UTS_RELEASE"\n", utsname()->sysname, utsname()->machine); 37362306a36Sopenharmony_ci} 37462306a36Sopenharmony_ci 37562306a36Sopenharmony_ciCONFIGFS_ATTR_RO(ft_wwn_, version); 37662306a36Sopenharmony_ci 37762306a36Sopenharmony_cistatic struct configfs_attribute *ft_wwn_attrs[] = { 37862306a36Sopenharmony_ci &ft_wwn_attr_version, 37962306a36Sopenharmony_ci NULL, 38062306a36Sopenharmony_ci}; 38162306a36Sopenharmony_ci 38262306a36Sopenharmony_cistatic inline struct ft_tpg *ft_tpg(struct se_portal_group *se_tpg) 38362306a36Sopenharmony_ci{ 38462306a36Sopenharmony_ci return container_of(se_tpg, struct ft_tpg, se_tpg); 38562306a36Sopenharmony_ci} 38662306a36Sopenharmony_ci 38762306a36Sopenharmony_cistatic char *ft_get_fabric_wwn(struct se_portal_group *se_tpg) 38862306a36Sopenharmony_ci{ 38962306a36Sopenharmony_ci return ft_tpg(se_tpg)->lport_wwn->name; 39062306a36Sopenharmony_ci} 39162306a36Sopenharmony_ci 39262306a36Sopenharmony_cistatic u16 ft_get_tag(struct se_portal_group *se_tpg) 39362306a36Sopenharmony_ci{ 39462306a36Sopenharmony_ci /* 39562306a36Sopenharmony_ci * This tag is used when forming SCSI Name identifier in EVPD=1 0x83 39662306a36Sopenharmony_ci * to represent the SCSI Target Port. 39762306a36Sopenharmony_ci */ 39862306a36Sopenharmony_ci return ft_tpg(se_tpg)->index; 39962306a36Sopenharmony_ci} 40062306a36Sopenharmony_ci 40162306a36Sopenharmony_cistatic u32 ft_tpg_get_inst_index(struct se_portal_group *se_tpg) 40262306a36Sopenharmony_ci{ 40362306a36Sopenharmony_ci return ft_tpg(se_tpg)->index; 40462306a36Sopenharmony_ci} 40562306a36Sopenharmony_ci 40662306a36Sopenharmony_cistatic const struct target_core_fabric_ops ft_fabric_ops = { 40762306a36Sopenharmony_ci .module = THIS_MODULE, 40862306a36Sopenharmony_ci .fabric_name = "fc", 40962306a36Sopenharmony_ci .node_acl_size = sizeof(struct ft_node_acl), 41062306a36Sopenharmony_ci .tpg_get_wwn = ft_get_fabric_wwn, 41162306a36Sopenharmony_ci .tpg_get_tag = ft_get_tag, 41262306a36Sopenharmony_ci .tpg_get_inst_index = ft_tpg_get_inst_index, 41362306a36Sopenharmony_ci .check_stop_free = ft_check_stop_free, 41462306a36Sopenharmony_ci .release_cmd = ft_release_cmd, 41562306a36Sopenharmony_ci .close_session = ft_sess_close, 41662306a36Sopenharmony_ci .sess_get_index = ft_sess_get_index, 41762306a36Sopenharmony_ci .sess_get_initiator_sid = NULL, 41862306a36Sopenharmony_ci .write_pending = ft_write_pending, 41962306a36Sopenharmony_ci .queue_data_in = ft_queue_data_in, 42062306a36Sopenharmony_ci .queue_status = ft_queue_status, 42162306a36Sopenharmony_ci .queue_tm_rsp = ft_queue_tm_resp, 42262306a36Sopenharmony_ci .aborted_task = ft_aborted_task, 42362306a36Sopenharmony_ci /* 42462306a36Sopenharmony_ci * Setup function pointers for generic logic in 42562306a36Sopenharmony_ci * target_core_fabric_configfs.c 42662306a36Sopenharmony_ci */ 42762306a36Sopenharmony_ci .fabric_make_wwn = &ft_add_wwn, 42862306a36Sopenharmony_ci .fabric_drop_wwn = &ft_del_wwn, 42962306a36Sopenharmony_ci .fabric_make_tpg = &ft_add_tpg, 43062306a36Sopenharmony_ci .fabric_drop_tpg = &ft_del_tpg, 43162306a36Sopenharmony_ci .fabric_init_nodeacl = &ft_init_nodeacl, 43262306a36Sopenharmony_ci 43362306a36Sopenharmony_ci .tfc_wwn_attrs = ft_wwn_attrs, 43462306a36Sopenharmony_ci .tfc_tpg_nacl_base_attrs = ft_nacl_base_attrs, 43562306a36Sopenharmony_ci}; 43662306a36Sopenharmony_ci 43762306a36Sopenharmony_cistatic struct notifier_block ft_notifier = { 43862306a36Sopenharmony_ci .notifier_call = ft_lport_notify 43962306a36Sopenharmony_ci}; 44062306a36Sopenharmony_ci 44162306a36Sopenharmony_cistatic int __init ft_init(void) 44262306a36Sopenharmony_ci{ 44362306a36Sopenharmony_ci int ret; 44462306a36Sopenharmony_ci 44562306a36Sopenharmony_ci ret = target_register_template(&ft_fabric_ops); 44662306a36Sopenharmony_ci if (ret) 44762306a36Sopenharmony_ci goto out; 44862306a36Sopenharmony_ci 44962306a36Sopenharmony_ci ret = fc_fc4_register_provider(FC_TYPE_FCP, &ft_prov); 45062306a36Sopenharmony_ci if (ret) 45162306a36Sopenharmony_ci goto out_unregister_template; 45262306a36Sopenharmony_ci 45362306a36Sopenharmony_ci blocking_notifier_chain_register(&fc_lport_notifier_head, &ft_notifier); 45462306a36Sopenharmony_ci fc_lport_iterate(ft_lport_add, NULL); 45562306a36Sopenharmony_ci return 0; 45662306a36Sopenharmony_ci 45762306a36Sopenharmony_ciout_unregister_template: 45862306a36Sopenharmony_ci target_unregister_template(&ft_fabric_ops); 45962306a36Sopenharmony_ciout: 46062306a36Sopenharmony_ci return ret; 46162306a36Sopenharmony_ci} 46262306a36Sopenharmony_ci 46362306a36Sopenharmony_cistatic void __exit ft_exit(void) 46462306a36Sopenharmony_ci{ 46562306a36Sopenharmony_ci blocking_notifier_chain_unregister(&fc_lport_notifier_head, 46662306a36Sopenharmony_ci &ft_notifier); 46762306a36Sopenharmony_ci fc_fc4_deregister_provider(FC_TYPE_FCP, &ft_prov); 46862306a36Sopenharmony_ci fc_lport_iterate(ft_lport_del, NULL); 46962306a36Sopenharmony_ci target_unregister_template(&ft_fabric_ops); 47062306a36Sopenharmony_ci synchronize_rcu(); 47162306a36Sopenharmony_ci} 47262306a36Sopenharmony_ci 47362306a36Sopenharmony_ciMODULE_DESCRIPTION("FC TCM fabric driver " FT_VERSION); 47462306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 47562306a36Sopenharmony_cimodule_init(ft_init); 47662306a36Sopenharmony_cimodule_exit(ft_exit); 477