13699d92aSKiran Patil /******************************************************************************* 23699d92aSKiran Patil * Filename: tcm_fc.c 33699d92aSKiran Patil * 43699d92aSKiran Patil * This file contains the configfs implementation for TCM_fc fabric node. 53699d92aSKiran Patil * Based on tcm_loop_configfs.c 63699d92aSKiran Patil * 73699d92aSKiran Patil * Copyright (c) 2010 Cisco Systems, Inc. 83699d92aSKiran Patil * Copyright (c) 2009,2010 Rising Tide, Inc. 93699d92aSKiran Patil * Copyright (c) 2009,2010 Linux-iSCSI.org 103699d92aSKiran Patil * 113699d92aSKiran Patil * Copyright (c) 2009,2010 Nicholas A. Bellinger <nab@linux-iscsi.org> 123699d92aSKiran Patil * 133699d92aSKiran Patil * This program is free software; you can redistribute it and/or modify 143699d92aSKiran Patil * it under the terms of the GNU General Public License as published by 153699d92aSKiran Patil * the Free Software Foundation; either version 2 of the License, or 163699d92aSKiran Patil * (at your option) any later version. 173699d92aSKiran Patil * 183699d92aSKiran Patil * This program is distributed in the hope that it will be useful, 193699d92aSKiran Patil * but WITHOUT ANY WARRANTY; without even the implied warranty of 203699d92aSKiran Patil * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 213699d92aSKiran Patil * GNU General Public License for more details. 223699d92aSKiran Patil ****************************************************************************/ 233699d92aSKiran Patil 243699d92aSKiran Patil #include <linux/module.h> 253699d92aSKiran Patil #include <linux/moduleparam.h> 263699d92aSKiran Patil #include <generated/utsrelease.h> 273699d92aSKiran Patil #include <linux/utsname.h> 283699d92aSKiran Patil #include <linux/init.h> 293699d92aSKiran Patil #include <linux/slab.h> 303699d92aSKiran Patil #include <linux/kthread.h> 313699d92aSKiran Patil #include <linux/types.h> 323699d92aSKiran Patil #include <linux/string.h> 333699d92aSKiran Patil #include <linux/configfs.h> 34635a2b3fSAndy Shevchenko #include <linux/kernel.h> 353699d92aSKiran Patil #include <linux/ctype.h> 363699d92aSKiran Patil #include <asm/unaligned.h> 373699d92aSKiran Patil #include <scsi/libfc.h> 383699d92aSKiran Patil 393699d92aSKiran Patil #include <target/target_core_base.h> 40c4795fb2SChristoph Hellwig #include <target/target_core_fabric.h> 413699d92aSKiran Patil 423699d92aSKiran Patil #include "tcm_fc.h" 433699d92aSKiran Patil 44705665daSAndy Grover static LIST_HEAD(ft_wwn_list); 453699d92aSKiran Patil DEFINE_MUTEX(ft_lport_lock); 463699d92aSKiran Patil 473699d92aSKiran Patil unsigned int ft_debug_logging; 483699d92aSKiran Patil module_param_named(debug_logging, ft_debug_logging, int, S_IRUGO|S_IWUSR); 493699d92aSKiran Patil MODULE_PARM_DESC(debug_logging, "a bit mask of logging levels"); 503699d92aSKiran Patil 513699d92aSKiran Patil /* 523699d92aSKiran Patil * Parse WWN. 533699d92aSKiran Patil * If strict, we require lower-case hex and colon separators to be sure 543699d92aSKiran Patil * the name is the same as what would be generated by ft_format_wwn() 553699d92aSKiran Patil * so the name and wwn are mapped one-to-one. 563699d92aSKiran Patil */ 573699d92aSKiran Patil static ssize_t ft_parse_wwn(const char *name, u64 *wwn, int strict) 583699d92aSKiran Patil { 593699d92aSKiran Patil const char *cp; 603699d92aSKiran Patil char c; 613699d92aSKiran Patil u32 byte = 0; 623699d92aSKiran Patil u32 pos = 0; 633699d92aSKiran Patil u32 err; 64635a2b3fSAndy Shevchenko int val; 653699d92aSKiran Patil 663699d92aSKiran Patil *wwn = 0; 673699d92aSKiran Patil for (cp = name; cp < &name[FT_NAMELEN - 1]; cp++) { 683699d92aSKiran Patil c = *cp; 693699d92aSKiran Patil if (c == '\n' && cp[1] == '\0') 703699d92aSKiran Patil continue; 713699d92aSKiran Patil if (strict && pos++ == 2 && byte++ < 7) { 723699d92aSKiran Patil pos = 0; 733699d92aSKiran Patil if (c == ':') 743699d92aSKiran Patil continue; 753699d92aSKiran Patil err = 1; 763699d92aSKiran Patil goto fail; 773699d92aSKiran Patil } 783699d92aSKiran Patil if (c == '\0') { 793699d92aSKiran Patil err = 2; 803699d92aSKiran Patil if (strict && byte != 8) 813699d92aSKiran Patil goto fail; 823699d92aSKiran Patil return cp - name; 833699d92aSKiran Patil } 843699d92aSKiran Patil err = 3; 85635a2b3fSAndy Shevchenko val = hex_to_bin(c); 86635a2b3fSAndy Shevchenko if (val < 0 || (strict && isupper(c))) 873699d92aSKiran Patil goto fail; 88635a2b3fSAndy Shevchenko *wwn = (*wwn << 4) | val; 893699d92aSKiran Patil } 903699d92aSKiran Patil err = 4; 913699d92aSKiran Patil fail: 926708bb27SAndy Grover pr_debug("err %u len %zu pos %u byte %u\n", 933699d92aSKiran Patil err, cp - name, pos, byte); 943699d92aSKiran Patil return -1; 953699d92aSKiran Patil } 963699d92aSKiran Patil 973699d92aSKiran Patil ssize_t ft_format_wwn(char *buf, size_t len, u64 wwn) 983699d92aSKiran Patil { 993699d92aSKiran Patil u8 b[8]; 1003699d92aSKiran Patil 1013699d92aSKiran Patil put_unaligned_be64(wwn, b); 1023699d92aSKiran Patil return snprintf(buf, len, 1033699d92aSKiran Patil "%2.2x:%2.2x:%2.2x:%2.2x:%2.2x:%2.2x:%2.2x:%2.2x", 1043699d92aSKiran Patil b[0], b[1], b[2], b[3], b[4], b[5], b[6], b[7]); 1053699d92aSKiran Patil } 1063699d92aSKiran Patil 1073699d92aSKiran Patil static ssize_t ft_wwn_show(void *arg, char *buf) 1083699d92aSKiran Patil { 1093699d92aSKiran Patil u64 *wwn = arg; 1103699d92aSKiran Patil ssize_t len; 1113699d92aSKiran Patil 1123699d92aSKiran Patil len = ft_format_wwn(buf, PAGE_SIZE - 2, *wwn); 1133699d92aSKiran Patil buf[len++] = '\n'; 1143699d92aSKiran Patil return len; 1153699d92aSKiran Patil } 1163699d92aSKiran Patil 1173699d92aSKiran Patil static ssize_t ft_wwn_store(void *arg, const char *buf, size_t len) 1183699d92aSKiran Patil { 1193699d92aSKiran Patil ssize_t ret; 1203699d92aSKiran Patil u64 wwn; 1213699d92aSKiran Patil 1223699d92aSKiran Patil ret = ft_parse_wwn(buf, &wwn, 0); 1233699d92aSKiran Patil if (ret > 0) 1243699d92aSKiran Patil *(u64 *)arg = wwn; 1253699d92aSKiran Patil return ret; 1263699d92aSKiran Patil } 1273699d92aSKiran Patil 1283699d92aSKiran Patil /* 1293699d92aSKiran Patil * ACL auth ops. 1303699d92aSKiran Patil */ 1313699d92aSKiran Patil 1322eafd729SChristoph Hellwig static ssize_t ft_nacl_port_name_show(struct config_item *item, char *page) 1333699d92aSKiran Patil { 1342eafd729SChristoph Hellwig struct se_node_acl *se_nacl = acl_to_nacl(item); 1353699d92aSKiran Patil struct ft_node_acl *acl = container_of(se_nacl, 1363699d92aSKiran Patil struct ft_node_acl, se_node_acl); 1373699d92aSKiran Patil 1383699d92aSKiran Patil return ft_wwn_show(&acl->node_auth.port_name, page); 1393699d92aSKiran Patil } 1403699d92aSKiran Patil 1412eafd729SChristoph Hellwig static ssize_t ft_nacl_port_name_store(struct config_item *item, 1422eafd729SChristoph Hellwig const char *page, size_t count) 1433699d92aSKiran Patil { 1442eafd729SChristoph Hellwig struct se_node_acl *se_nacl = acl_to_nacl(item); 1453699d92aSKiran Patil struct ft_node_acl *acl = container_of(se_nacl, 1463699d92aSKiran Patil struct ft_node_acl, se_node_acl); 1473699d92aSKiran Patil 1483699d92aSKiran Patil return ft_wwn_store(&acl->node_auth.port_name, page, count); 1493699d92aSKiran Patil } 1503699d92aSKiran Patil 1512eafd729SChristoph Hellwig static ssize_t ft_nacl_node_name_show(struct config_item *item, 1523699d92aSKiran Patil char *page) 1533699d92aSKiran Patil { 1542eafd729SChristoph Hellwig struct se_node_acl *se_nacl = acl_to_nacl(item); 1553699d92aSKiran Patil struct ft_node_acl *acl = container_of(se_nacl, 1563699d92aSKiran Patil struct ft_node_acl, se_node_acl); 1573699d92aSKiran Patil 1583699d92aSKiran Patil return ft_wwn_show(&acl->node_auth.node_name, page); 1593699d92aSKiran Patil } 1603699d92aSKiran Patil 1612eafd729SChristoph Hellwig static ssize_t ft_nacl_node_name_store(struct config_item *item, 1622eafd729SChristoph Hellwig const char *page, size_t count) 1633699d92aSKiran Patil { 1642eafd729SChristoph Hellwig struct se_node_acl *se_nacl = acl_to_nacl(item); 1653699d92aSKiran Patil struct ft_node_acl *acl = container_of(se_nacl, 1663699d92aSKiran Patil struct ft_node_acl, se_node_acl); 1673699d92aSKiran Patil 1683699d92aSKiran Patil return ft_wwn_store(&acl->node_auth.node_name, page, count); 1693699d92aSKiran Patil } 1703699d92aSKiran Patil 1712eafd729SChristoph Hellwig CONFIGFS_ATTR(ft_nacl_, node_name); 1722eafd729SChristoph Hellwig CONFIGFS_ATTR(ft_nacl_, port_name); 1733699d92aSKiran Patil 174*091b7062SAndy Grover static ssize_t ft_nacl_tag_show(struct config_item *item, 175*091b7062SAndy Grover char *page) 176*091b7062SAndy Grover { 177*091b7062SAndy Grover return snprintf(page, PAGE_SIZE, "%s", acl_to_nacl(item)->acl_tag); 178*091b7062SAndy Grover } 179*091b7062SAndy Grover 180*091b7062SAndy Grover static ssize_t ft_nacl_tag_store(struct config_item *item, 181*091b7062SAndy Grover const char *page, size_t count) 182*091b7062SAndy Grover { 183*091b7062SAndy Grover struct se_node_acl *se_nacl = acl_to_nacl(item); 184*091b7062SAndy Grover int ret; 185*091b7062SAndy Grover 186*091b7062SAndy Grover ret = core_tpg_set_initiator_node_tag(se_nacl->se_tpg, se_nacl, page); 187*091b7062SAndy Grover 188*091b7062SAndy Grover if (ret < 0) 189*091b7062SAndy Grover return ret; 190*091b7062SAndy Grover return count; 191*091b7062SAndy Grover } 192*091b7062SAndy Grover 193*091b7062SAndy Grover CONFIGFS_ATTR(ft_nacl_, tag); 194*091b7062SAndy Grover 1953699d92aSKiran Patil static struct configfs_attribute *ft_nacl_base_attrs[] = { 1962eafd729SChristoph Hellwig &ft_nacl_attr_port_name, 1972eafd729SChristoph Hellwig &ft_nacl_attr_node_name, 198*091b7062SAndy Grover &ft_nacl_attr_tag, 1993699d92aSKiran Patil NULL, 2003699d92aSKiran Patil }; 2013699d92aSKiran Patil 2023699d92aSKiran Patil /* 2033699d92aSKiran Patil * ACL ops. 2043699d92aSKiran Patil */ 2053699d92aSKiran Patil 2063699d92aSKiran Patil /* 2073699d92aSKiran Patil * Add ACL for an initiator. The ACL is named arbitrarily. 2083699d92aSKiran Patil * The port_name and/or node_name are attributes. 2093699d92aSKiran Patil */ 210c7d6a803SChristoph Hellwig static int ft_init_nodeacl(struct se_node_acl *nacl, const char *name) 2113699d92aSKiran Patil { 212c7d6a803SChristoph Hellwig struct ft_node_acl *acl = 213c7d6a803SChristoph Hellwig container_of(nacl, struct ft_node_acl, se_node_acl); 2143699d92aSKiran Patil u64 wwpn; 2153699d92aSKiran Patil 2163699d92aSKiran Patil if (ft_parse_wwn(name, &wwpn, 1) < 0) 217c7d6a803SChristoph Hellwig return -EINVAL; 2183699d92aSKiran Patil 2193699d92aSKiran Patil acl->node_auth.port_name = wwpn; 220c7d6a803SChristoph Hellwig return 0; 2213699d92aSKiran Patil } 2223699d92aSKiran Patil 2233699d92aSKiran Patil struct ft_node_acl *ft_acl_get(struct ft_tpg *tpg, struct fc_rport_priv *rdata) 2243699d92aSKiran Patil { 2253699d92aSKiran Patil struct ft_node_acl *found = NULL; 2263699d92aSKiran Patil struct ft_node_acl *acl; 2273699d92aSKiran Patil struct se_portal_group *se_tpg = &tpg->se_tpg; 2283699d92aSKiran Patil struct se_node_acl *se_acl; 2293699d92aSKiran Patil 230403edd78SNicholas Bellinger mutex_lock(&se_tpg->acl_node_mutex); 2313699d92aSKiran Patil list_for_each_entry(se_acl, &se_tpg->acl_node_list, acl_list) { 2323699d92aSKiran Patil acl = container_of(se_acl, struct ft_node_acl, se_node_acl); 2336708bb27SAndy Grover pr_debug("acl %p port_name %llx\n", 2343699d92aSKiran Patil acl, (unsigned long long)acl->node_auth.port_name); 2353699d92aSKiran Patil if (acl->node_auth.port_name == rdata->ids.port_name || 2363699d92aSKiran Patil acl->node_auth.node_name == rdata->ids.node_name) { 2376708bb27SAndy Grover pr_debug("acl %p port_name %llx matched\n", acl, 2383699d92aSKiran Patil (unsigned long long)rdata->ids.port_name); 2393699d92aSKiran Patil found = acl; 2403699d92aSKiran Patil /* XXX need to hold onto ACL */ 2413699d92aSKiran Patil break; 2423699d92aSKiran Patil } 2433699d92aSKiran Patil } 244403edd78SNicholas Bellinger mutex_unlock(&se_tpg->acl_node_mutex); 2453699d92aSKiran Patil return found; 2463699d92aSKiran Patil } 2473699d92aSKiran Patil 2483699d92aSKiran Patil /* 2493699d92aSKiran Patil * local_port port_group (tpg) ops. 2503699d92aSKiran Patil */ 2513699d92aSKiran Patil static struct se_portal_group *ft_add_tpg( 2523699d92aSKiran Patil struct se_wwn *wwn, 2533699d92aSKiran Patil struct config_group *group, 2543699d92aSKiran Patil const char *name) 2553699d92aSKiran Patil { 256705665daSAndy Grover struct ft_lport_wwn *ft_wwn; 2573699d92aSKiran Patil struct ft_tpg *tpg; 25806383f10SMark Rustad struct workqueue_struct *wq; 2593699d92aSKiran Patil unsigned long index; 2603699d92aSKiran Patil int ret; 2613699d92aSKiran Patil 2626708bb27SAndy Grover pr_debug("tcm_fc: add tpg %s\n", name); 2633699d92aSKiran Patil 2643699d92aSKiran Patil /* 2653699d92aSKiran Patil * Name must be "tpgt_" followed by the index. 2663699d92aSKiran Patil */ 2673699d92aSKiran Patil if (strstr(name, "tpgt_") != name) 2683699d92aSKiran Patil return NULL; 26957103d7fSJingoo Han 27057103d7fSJingoo Han ret = kstrtoul(name + 5, 10, &index); 27157103d7fSJingoo Han if (ret) 27257103d7fSJingoo Han return NULL; 27357103d7fSJingoo Han if (index > UINT_MAX) 2743699d92aSKiran Patil return NULL; 2753699d92aSKiran Patil 276d242c1d7SAndy Grover if ((index != 1)) { 277d242c1d7SAndy Grover pr_err("Error, a single TPG=1 is used for HW port mappings\n"); 278d242c1d7SAndy Grover return ERR_PTR(-ENOSYS); 279d242c1d7SAndy Grover } 280d242c1d7SAndy Grover 281705665daSAndy Grover ft_wwn = container_of(wwn, struct ft_lport_wwn, se_wwn); 2823699d92aSKiran Patil tpg = kzalloc(sizeof(*tpg), GFP_KERNEL); 2833699d92aSKiran Patil if (!tpg) 2843699d92aSKiran Patil return NULL; 2853699d92aSKiran Patil tpg->index = index; 286705665daSAndy Grover tpg->lport_wwn = ft_wwn; 2873699d92aSKiran Patil INIT_LIST_HEAD(&tpg->lun_list); 2883699d92aSKiran Patil 28906383f10SMark Rustad wq = alloc_workqueue("tcm_fc", 0, 1); 29006383f10SMark Rustad if (!wq) { 2913699d92aSKiran Patil kfree(tpg); 2923699d92aSKiran Patil return NULL; 2933699d92aSKiran Patil } 2943699d92aSKiran Patil 295bc0c94b1SNicholas Bellinger ret = core_tpg_register(wwn, &tpg->se_tpg, SCSI_PROTOCOL_FCP); 29606383f10SMark Rustad if (ret < 0) { 29706383f10SMark Rustad destroy_workqueue(wq); 2983699d92aSKiran Patil kfree(tpg); 2993699d92aSKiran Patil return NULL; 3003699d92aSKiran Patil } 30106383f10SMark Rustad tpg->workqueue = wq; 3023699d92aSKiran Patil 3033699d92aSKiran Patil mutex_lock(&ft_lport_lock); 304705665daSAndy Grover ft_wwn->tpg = tpg; 3053699d92aSKiran Patil mutex_unlock(&ft_lport_lock); 3063699d92aSKiran Patil 3073699d92aSKiran Patil return &tpg->se_tpg; 3083699d92aSKiran Patil } 3093699d92aSKiran Patil 3103699d92aSKiran Patil static void ft_del_tpg(struct se_portal_group *se_tpg) 3113699d92aSKiran Patil { 3123699d92aSKiran Patil struct ft_tpg *tpg = container_of(se_tpg, struct ft_tpg, se_tpg); 313705665daSAndy Grover struct ft_lport_wwn *ft_wwn = tpg->lport_wwn; 3143699d92aSKiran Patil 3156708bb27SAndy Grover pr_debug("del tpg %s\n", 3163699d92aSKiran Patil config_item_name(&tpg->se_tpg.tpg_group.cg_item)); 3173699d92aSKiran Patil 31858fc73d1SChristoph Hellwig destroy_workqueue(tpg->workqueue); 3193699d92aSKiran Patil 3203699d92aSKiran Patil /* Wait for sessions to be freed thru RCU, for BUG_ON below */ 3213699d92aSKiran Patil synchronize_rcu(); 3223699d92aSKiran Patil 3233699d92aSKiran Patil mutex_lock(&ft_lport_lock); 324705665daSAndy Grover ft_wwn->tpg = NULL; 3253699d92aSKiran Patil if (tpg->tport) { 3263699d92aSKiran Patil tpg->tport->tpg = NULL; 3273699d92aSKiran Patil tpg->tport = NULL; 3283699d92aSKiran Patil } 3293699d92aSKiran Patil mutex_unlock(&ft_lport_lock); 3303699d92aSKiran Patil 3313699d92aSKiran Patil core_tpg_deregister(se_tpg); 3323699d92aSKiran Patil kfree(tpg); 3333699d92aSKiran Patil } 3343699d92aSKiran Patil 3353699d92aSKiran Patil /* 3363699d92aSKiran Patil * Verify that an lport is configured to use the tcm_fc module, and return 3373699d92aSKiran Patil * the target port group that should be used. 3383699d92aSKiran Patil * 3393699d92aSKiran Patil * The caller holds ft_lport_lock. 3403699d92aSKiran Patil */ 3413699d92aSKiran Patil struct ft_tpg *ft_lport_find_tpg(struct fc_lport *lport) 3423699d92aSKiran Patil { 343705665daSAndy Grover struct ft_lport_wwn *ft_wwn; 3443699d92aSKiran Patil 345705665daSAndy Grover list_for_each_entry(ft_wwn, &ft_wwn_list, ft_wwn_node) { 346705665daSAndy Grover if (ft_wwn->wwpn == lport->wwpn) 347705665daSAndy Grover return ft_wwn->tpg; 3483699d92aSKiran Patil } 3493699d92aSKiran Patil return NULL; 3503699d92aSKiran Patil } 3513699d92aSKiran Patil 3523699d92aSKiran Patil /* 3533699d92aSKiran Patil * target config instance ops. 3543699d92aSKiran Patil */ 3553699d92aSKiran Patil 3563699d92aSKiran Patil /* 3573699d92aSKiran Patil * Add lport to allowed config. 3583699d92aSKiran Patil * The name is the WWPN in lower-case ASCII, colon-separated bytes. 3593699d92aSKiran Patil */ 3600d7cb932SAndy Grover static struct se_wwn *ft_add_wwn( 3613699d92aSKiran Patil struct target_fabric_configfs *tf, 3623699d92aSKiran Patil struct config_group *group, 3633699d92aSKiran Patil const char *name) 3643699d92aSKiran Patil { 365705665daSAndy Grover struct ft_lport_wwn *ft_wwn; 366705665daSAndy Grover struct ft_lport_wwn *old_ft_wwn; 3673699d92aSKiran Patil u64 wwpn; 3683699d92aSKiran Patil 3690d7cb932SAndy Grover pr_debug("add wwn %s\n", name); 3703699d92aSKiran Patil if (ft_parse_wwn(name, &wwpn, 1) < 0) 3713699d92aSKiran Patil return NULL; 372705665daSAndy Grover ft_wwn = kzalloc(sizeof(*ft_wwn), GFP_KERNEL); 373705665daSAndy Grover if (!ft_wwn) 3743699d92aSKiran Patil return NULL; 375705665daSAndy Grover ft_wwn->wwpn = wwpn; 3763699d92aSKiran Patil 3773699d92aSKiran Patil mutex_lock(&ft_lport_lock); 378705665daSAndy Grover list_for_each_entry(old_ft_wwn, &ft_wwn_list, ft_wwn_node) { 379705665daSAndy Grover if (old_ft_wwn->wwpn == wwpn) { 3803699d92aSKiran Patil mutex_unlock(&ft_lport_lock); 381705665daSAndy Grover kfree(ft_wwn); 3823699d92aSKiran Patil return NULL; 3833699d92aSKiran Patil } 3843699d92aSKiran Patil } 385705665daSAndy Grover list_add_tail(&ft_wwn->ft_wwn_node, &ft_wwn_list); 386705665daSAndy Grover ft_format_wwn(ft_wwn->name, sizeof(ft_wwn->name), wwpn); 3873699d92aSKiran Patil mutex_unlock(&ft_lport_lock); 3883699d92aSKiran Patil 389705665daSAndy Grover return &ft_wwn->se_wwn; 3903699d92aSKiran Patil } 3913699d92aSKiran Patil 3920d7cb932SAndy Grover static void ft_del_wwn(struct se_wwn *wwn) 3933699d92aSKiran Patil { 394705665daSAndy Grover struct ft_lport_wwn *ft_wwn = container_of(wwn, 395705665daSAndy Grover struct ft_lport_wwn, se_wwn); 3963699d92aSKiran Patil 3970d7cb932SAndy Grover pr_debug("del wwn %s\n", ft_wwn->name); 3983699d92aSKiran Patil mutex_lock(&ft_lport_lock); 399705665daSAndy Grover list_del(&ft_wwn->ft_wwn_node); 4003699d92aSKiran Patil mutex_unlock(&ft_lport_lock); 4013699d92aSKiran Patil 402705665daSAndy Grover kfree(ft_wwn); 4033699d92aSKiran Patil } 4043699d92aSKiran Patil 4052eafd729SChristoph Hellwig static ssize_t ft_wwn_version_show(struct config_item *item, char *page) 4063699d92aSKiran Patil { 4073699d92aSKiran Patil return sprintf(page, "TCM FC " FT_VERSION " on %s/%s on " 4083699d92aSKiran Patil ""UTS_RELEASE"\n", utsname()->sysname, utsname()->machine); 4093699d92aSKiran Patil } 4103699d92aSKiran Patil 4112eafd729SChristoph Hellwig CONFIGFS_ATTR_RO(ft_wwn_, version); 4123699d92aSKiran Patil 4133699d92aSKiran Patil static struct configfs_attribute *ft_wwn_attrs[] = { 4142eafd729SChristoph Hellwig &ft_wwn_attr_version, 4153699d92aSKiran Patil NULL, 4163699d92aSKiran Patil }; 4173699d92aSKiran Patil 4183868e436SChristoph Hellwig static inline struct ft_tpg *ft_tpg(struct se_portal_group *se_tpg) 4193868e436SChristoph Hellwig { 4203868e436SChristoph Hellwig return container_of(se_tpg, struct ft_tpg, se_tpg); 4213868e436SChristoph Hellwig } 4223868e436SChristoph Hellwig 4233699d92aSKiran Patil static char *ft_get_fabric_name(void) 4243699d92aSKiran Patil { 4253699d92aSKiran Patil return "fc"; 4263699d92aSKiran Patil } 4273699d92aSKiran Patil 4283699d92aSKiran Patil static char *ft_get_fabric_wwn(struct se_portal_group *se_tpg) 4293699d92aSKiran Patil { 4303868e436SChristoph Hellwig return ft_tpg(se_tpg)->lport_wwn->name; 4313699d92aSKiran Patil } 4323699d92aSKiran Patil 4333699d92aSKiran Patil static u16 ft_get_tag(struct se_portal_group *se_tpg) 4343699d92aSKiran Patil { 4353699d92aSKiran Patil /* 4363699d92aSKiran Patil * This tag is used when forming SCSI Name identifier in EVPD=1 0x83 4373699d92aSKiran Patil * to represent the SCSI Target Port. 4383699d92aSKiran Patil */ 4393868e436SChristoph Hellwig return ft_tpg(se_tpg)->index; 4403699d92aSKiran Patil } 4413699d92aSKiran Patil 4423699d92aSKiran Patil static int ft_check_false(struct se_portal_group *se_tpg) 4433699d92aSKiran Patil { 4443699d92aSKiran Patil return 0; 4453699d92aSKiran Patil } 4463699d92aSKiran Patil 4473699d92aSKiran Patil static void ft_set_default_node_attr(struct se_node_acl *se_nacl) 4483699d92aSKiran Patil { 4493699d92aSKiran Patil } 4503699d92aSKiran Patil 4513699d92aSKiran Patil static u32 ft_tpg_get_inst_index(struct se_portal_group *se_tpg) 4523699d92aSKiran Patil { 4533868e436SChristoph Hellwig return ft_tpg(se_tpg)->index; 4543699d92aSKiran Patil } 4553699d92aSKiran Patil 4569ac8928eSChristoph Hellwig static const struct target_core_fabric_ops ft_fabric_ops = { 4579ac8928eSChristoph Hellwig .module = THIS_MODULE, 4589ac8928eSChristoph Hellwig .name = "fc", 459144bc4c2SChristoph Hellwig .node_acl_size = sizeof(struct ft_node_acl), 4603699d92aSKiran Patil .get_fabric_name = ft_get_fabric_name, 4613699d92aSKiran Patil .tpg_get_wwn = ft_get_fabric_wwn, 4623699d92aSKiran Patil .tpg_get_tag = ft_get_tag, 4633699d92aSKiran Patil .tpg_check_demo_mode = ft_check_false, 4643699d92aSKiran Patil .tpg_check_demo_mode_cache = ft_check_false, 4653699d92aSKiran Patil .tpg_check_demo_mode_write_protect = ft_check_false, 4663699d92aSKiran Patil .tpg_check_prod_mode_write_protect = ft_check_false, 4673699d92aSKiran Patil .tpg_get_inst_index = ft_tpg_get_inst_index, 4683699d92aSKiran Patil .check_stop_free = ft_check_stop_free, 46935462975SChristoph Hellwig .release_cmd = ft_release_cmd, 4703699d92aSKiran Patil .shutdown_session = ft_sess_shutdown, 4713699d92aSKiran Patil .close_session = ft_sess_close, 4723699d92aSKiran Patil .sess_get_index = ft_sess_get_index, 4733699d92aSKiran Patil .sess_get_initiator_sid = NULL, 4743699d92aSKiran Patil .write_pending = ft_write_pending, 4753699d92aSKiran Patil .write_pending_status = ft_write_pending_status, 4763699d92aSKiran Patil .set_default_node_attributes = ft_set_default_node_attr, 4773699d92aSKiran Patil .get_cmd_state = ft_get_cmd_state, 4783699d92aSKiran Patil .queue_data_in = ft_queue_data_in, 4793699d92aSKiran Patil .queue_status = ft_queue_status, 4803699d92aSKiran Patil .queue_tm_rsp = ft_queue_tm_resp, 481131e6abcSNicholas Bellinger .aborted_task = ft_aborted_task, 4823699d92aSKiran Patil /* 4833699d92aSKiran Patil * Setup function pointers for generic logic in 4843699d92aSKiran Patil * target_core_fabric_configfs.c 4853699d92aSKiran Patil */ 4860d7cb932SAndy Grover .fabric_make_wwn = &ft_add_wwn, 4870d7cb932SAndy Grover .fabric_drop_wwn = &ft_del_wwn, 4883699d92aSKiran Patil .fabric_make_tpg = &ft_add_tpg, 4893699d92aSKiran Patil .fabric_drop_tpg = &ft_del_tpg, 490c7d6a803SChristoph Hellwig .fabric_init_nodeacl = &ft_init_nodeacl, 4919ac8928eSChristoph Hellwig 4929ac8928eSChristoph Hellwig .tfc_wwn_attrs = ft_wwn_attrs, 4939ac8928eSChristoph Hellwig .tfc_tpg_nacl_base_attrs = ft_nacl_base_attrs, 4943699d92aSKiran Patil }; 4953699d92aSKiran Patil 4963699d92aSKiran Patil static struct notifier_block ft_notifier = { 4973699d92aSKiran Patil .notifier_call = ft_lport_notify 4983699d92aSKiran Patil }; 4993699d92aSKiran Patil 5003699d92aSKiran Patil static int __init ft_init(void) 5013699d92aSKiran Patil { 5029ac8928eSChristoph Hellwig int ret; 5039ac8928eSChristoph Hellwig 5049ac8928eSChristoph Hellwig ret = target_register_template(&ft_fabric_ops); 5059ac8928eSChristoph Hellwig if (ret) 5069ac8928eSChristoph Hellwig goto out; 5079ac8928eSChristoph Hellwig 5089ac8928eSChristoph Hellwig ret = fc_fc4_register_provider(FC_TYPE_FCP, &ft_prov); 5099ac8928eSChristoph Hellwig if (ret) 5109ac8928eSChristoph Hellwig goto out_unregister_template; 5119ac8928eSChristoph Hellwig 5123699d92aSKiran Patil blocking_notifier_chain_register(&fc_lport_notifier_head, &ft_notifier); 5133699d92aSKiran Patil fc_lport_iterate(ft_lport_add, NULL); 5143699d92aSKiran Patil return 0; 5159ac8928eSChristoph Hellwig 5169ac8928eSChristoph Hellwig out_unregister_template: 5179ac8928eSChristoph Hellwig target_unregister_template(&ft_fabric_ops); 5189ac8928eSChristoph Hellwig out: 5199ac8928eSChristoph Hellwig return ret; 5203699d92aSKiran Patil } 5213699d92aSKiran Patil 5223699d92aSKiran Patil static void __exit ft_exit(void) 5233699d92aSKiran Patil { 5243699d92aSKiran Patil blocking_notifier_chain_unregister(&fc_lport_notifier_head, 5253699d92aSKiran Patil &ft_notifier); 5263699d92aSKiran Patil fc_fc4_deregister_provider(FC_TYPE_FCP, &ft_prov); 5273699d92aSKiran Patil fc_lport_iterate(ft_lport_del, NULL); 5289ac8928eSChristoph Hellwig target_unregister_template(&ft_fabric_ops); 5293699d92aSKiran Patil synchronize_rcu(); 5303699d92aSKiran Patil } 5313699d92aSKiran Patil 5323699d92aSKiran Patil MODULE_DESCRIPTION("FC TCM fabric driver " FT_VERSION); 5333699d92aSKiran Patil MODULE_LICENSE("GPL"); 5343699d92aSKiran Patil module_init(ft_init); 5353699d92aSKiran Patil module_exit(ft_exit); 536