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/scsi.h> 383699d92aSKiran Patil #include <scsi/scsi_host.h> 393699d92aSKiran Patil #include <scsi/scsi_device.h> 403699d92aSKiran Patil #include <scsi/scsi_cmnd.h> 413699d92aSKiran Patil #include <scsi/libfc.h> 423699d92aSKiran Patil 433699d92aSKiran Patil #include <target/target_core_base.h> 44c4795fb2SChristoph Hellwig #include <target/target_core_fabric.h> 453699d92aSKiran Patil #include <target/target_core_fabric_configfs.h> 463699d92aSKiran Patil #include <target/target_core_configfs.h> 473699d92aSKiran Patil #include <target/configfs_macros.h> 483699d92aSKiran Patil 493699d92aSKiran Patil #include "tcm_fc.h" 503699d92aSKiran Patil 513699d92aSKiran Patil struct target_fabric_configfs *ft_configfs; 523699d92aSKiran Patil 53b295e769SAndy Grover static LIST_HEAD(ft_lport_list); 543699d92aSKiran Patil DEFINE_MUTEX(ft_lport_lock); 553699d92aSKiran Patil 563699d92aSKiran Patil unsigned int ft_debug_logging; 573699d92aSKiran Patil module_param_named(debug_logging, ft_debug_logging, int, S_IRUGO|S_IWUSR); 583699d92aSKiran Patil MODULE_PARM_DESC(debug_logging, "a bit mask of logging levels"); 593699d92aSKiran Patil 603699d92aSKiran Patil /* 613699d92aSKiran Patil * Parse WWN. 623699d92aSKiran Patil * If strict, we require lower-case hex and colon separators to be sure 633699d92aSKiran Patil * the name is the same as what would be generated by ft_format_wwn() 643699d92aSKiran Patil * so the name and wwn are mapped one-to-one. 653699d92aSKiran Patil */ 663699d92aSKiran Patil static ssize_t ft_parse_wwn(const char *name, u64 *wwn, int strict) 673699d92aSKiran Patil { 683699d92aSKiran Patil const char *cp; 693699d92aSKiran Patil char c; 703699d92aSKiran Patil u32 byte = 0; 713699d92aSKiran Patil u32 pos = 0; 723699d92aSKiran Patil u32 err; 73635a2b3fSAndy Shevchenko int val; 743699d92aSKiran Patil 753699d92aSKiran Patil *wwn = 0; 763699d92aSKiran Patil for (cp = name; cp < &name[FT_NAMELEN - 1]; cp++) { 773699d92aSKiran Patil c = *cp; 783699d92aSKiran Patil if (c == '\n' && cp[1] == '\0') 793699d92aSKiran Patil continue; 803699d92aSKiran Patil if (strict && pos++ == 2 && byte++ < 7) { 813699d92aSKiran Patil pos = 0; 823699d92aSKiran Patil if (c == ':') 833699d92aSKiran Patil continue; 843699d92aSKiran Patil err = 1; 853699d92aSKiran Patil goto fail; 863699d92aSKiran Patil } 873699d92aSKiran Patil if (c == '\0') { 883699d92aSKiran Patil err = 2; 893699d92aSKiran Patil if (strict && byte != 8) 903699d92aSKiran Patil goto fail; 913699d92aSKiran Patil return cp - name; 923699d92aSKiran Patil } 933699d92aSKiran Patil err = 3; 94635a2b3fSAndy Shevchenko val = hex_to_bin(c); 95635a2b3fSAndy Shevchenko if (val < 0 || (strict && isupper(c))) 963699d92aSKiran Patil goto fail; 97635a2b3fSAndy Shevchenko *wwn = (*wwn << 4) | val; 983699d92aSKiran Patil } 993699d92aSKiran Patil err = 4; 1003699d92aSKiran Patil fail: 1016708bb27SAndy Grover pr_debug("err %u len %zu pos %u byte %u\n", 1023699d92aSKiran Patil err, cp - name, pos, byte); 1033699d92aSKiran Patil return -1; 1043699d92aSKiran Patil } 1053699d92aSKiran Patil 1063699d92aSKiran Patil ssize_t ft_format_wwn(char *buf, size_t len, u64 wwn) 1073699d92aSKiran Patil { 1083699d92aSKiran Patil u8 b[8]; 1093699d92aSKiran Patil 1103699d92aSKiran Patil put_unaligned_be64(wwn, b); 1113699d92aSKiran Patil return snprintf(buf, len, 1123699d92aSKiran Patil "%2.2x:%2.2x:%2.2x:%2.2x:%2.2x:%2.2x:%2.2x:%2.2x", 1133699d92aSKiran Patil b[0], b[1], b[2], b[3], b[4], b[5], b[6], b[7]); 1143699d92aSKiran Patil } 1153699d92aSKiran Patil 1163699d92aSKiran Patil static ssize_t ft_wwn_show(void *arg, char *buf) 1173699d92aSKiran Patil { 1183699d92aSKiran Patil u64 *wwn = arg; 1193699d92aSKiran Patil ssize_t len; 1203699d92aSKiran Patil 1213699d92aSKiran Patil len = ft_format_wwn(buf, PAGE_SIZE - 2, *wwn); 1223699d92aSKiran Patil buf[len++] = '\n'; 1233699d92aSKiran Patil return len; 1243699d92aSKiran Patil } 1253699d92aSKiran Patil 1263699d92aSKiran Patil static ssize_t ft_wwn_store(void *arg, const char *buf, size_t len) 1273699d92aSKiran Patil { 1283699d92aSKiran Patil ssize_t ret; 1293699d92aSKiran Patil u64 wwn; 1303699d92aSKiran Patil 1313699d92aSKiran Patil ret = ft_parse_wwn(buf, &wwn, 0); 1323699d92aSKiran Patil if (ret > 0) 1333699d92aSKiran Patil *(u64 *)arg = wwn; 1343699d92aSKiran Patil return ret; 1353699d92aSKiran Patil } 1363699d92aSKiran Patil 1373699d92aSKiran Patil /* 1383699d92aSKiran Patil * ACL auth ops. 1393699d92aSKiran Patil */ 1403699d92aSKiran Patil 1413699d92aSKiran Patil static ssize_t ft_nacl_show_port_name( 1423699d92aSKiran Patil struct se_node_acl *se_nacl, 1433699d92aSKiran Patil char *page) 1443699d92aSKiran Patil { 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_show(&acl->node_auth.port_name, page); 1493699d92aSKiran Patil } 1503699d92aSKiran Patil 1513699d92aSKiran Patil static ssize_t ft_nacl_store_port_name( 1523699d92aSKiran Patil struct se_node_acl *se_nacl, 1533699d92aSKiran Patil const char *page, 1543699d92aSKiran Patil size_t count) 1553699d92aSKiran Patil { 1563699d92aSKiran Patil struct ft_node_acl *acl = container_of(se_nacl, 1573699d92aSKiran Patil struct ft_node_acl, se_node_acl); 1583699d92aSKiran Patil 1593699d92aSKiran Patil return ft_wwn_store(&acl->node_auth.port_name, page, count); 1603699d92aSKiran Patil } 1613699d92aSKiran Patil 1623699d92aSKiran Patil TF_NACL_BASE_ATTR(ft, port_name, S_IRUGO | S_IWUSR); 1633699d92aSKiran Patil 1643699d92aSKiran Patil static ssize_t ft_nacl_show_node_name( 1653699d92aSKiran Patil struct se_node_acl *se_nacl, 1663699d92aSKiran Patil char *page) 1673699d92aSKiran Patil { 1683699d92aSKiran Patil struct ft_node_acl *acl = container_of(se_nacl, 1693699d92aSKiran Patil struct ft_node_acl, se_node_acl); 1703699d92aSKiran Patil 1713699d92aSKiran Patil return ft_wwn_show(&acl->node_auth.node_name, page); 1723699d92aSKiran Patil } 1733699d92aSKiran Patil 1743699d92aSKiran Patil static ssize_t ft_nacl_store_node_name( 1753699d92aSKiran Patil struct se_node_acl *se_nacl, 1763699d92aSKiran Patil const char *page, 1773699d92aSKiran Patil size_t count) 1783699d92aSKiran Patil { 1793699d92aSKiran Patil struct ft_node_acl *acl = container_of(se_nacl, 1803699d92aSKiran Patil struct ft_node_acl, se_node_acl); 1813699d92aSKiran Patil 1823699d92aSKiran Patil return ft_wwn_store(&acl->node_auth.node_name, page, count); 1833699d92aSKiran Patil } 1843699d92aSKiran Patil 1853699d92aSKiran Patil TF_NACL_BASE_ATTR(ft, node_name, S_IRUGO | S_IWUSR); 1863699d92aSKiran Patil 1873699d92aSKiran Patil static struct configfs_attribute *ft_nacl_base_attrs[] = { 1883699d92aSKiran Patil &ft_nacl_port_name.attr, 1893699d92aSKiran Patil &ft_nacl_node_name.attr, 1903699d92aSKiran Patil NULL, 1913699d92aSKiran Patil }; 1923699d92aSKiran Patil 1933699d92aSKiran Patil /* 1943699d92aSKiran Patil * ACL ops. 1953699d92aSKiran Patil */ 1963699d92aSKiran Patil 1973699d92aSKiran Patil /* 1983699d92aSKiran Patil * Add ACL for an initiator. The ACL is named arbitrarily. 1993699d92aSKiran Patil * The port_name and/or node_name are attributes. 2003699d92aSKiran Patil */ 2013699d92aSKiran Patil static struct se_node_acl *ft_add_acl( 2023699d92aSKiran Patil struct se_portal_group *se_tpg, 2033699d92aSKiran Patil struct config_group *group, 2043699d92aSKiran Patil const char *name) 2053699d92aSKiran Patil { 2063699d92aSKiran Patil struct ft_node_acl *acl; 2073699d92aSKiran Patil struct ft_tpg *tpg; 2083699d92aSKiran Patil u64 wwpn; 2093699d92aSKiran Patil u32 q_depth; 2103699d92aSKiran Patil 2116708bb27SAndy Grover pr_debug("add acl %s\n", name); 2123699d92aSKiran Patil tpg = container_of(se_tpg, struct ft_tpg, se_tpg); 2133699d92aSKiran Patil 2143699d92aSKiran Patil if (ft_parse_wwn(name, &wwpn, 1) < 0) 2153699d92aSKiran Patil return ERR_PTR(-EINVAL); 2163699d92aSKiran Patil 2173699d92aSKiran Patil acl = kzalloc(sizeof(struct ft_node_acl), GFP_KERNEL); 2182be18149SNicholas Bellinger if (!acl) 2193699d92aSKiran Patil return ERR_PTR(-ENOMEM); 2203699d92aSKiran Patil acl->node_auth.port_name = wwpn; 2213699d92aSKiran Patil 2223699d92aSKiran Patil q_depth = 32; /* XXX bogus default - get from tpg? */ 2233699d92aSKiran Patil return core_tpg_add_initiator_node_acl(&tpg->se_tpg, 2243699d92aSKiran Patil &acl->se_node_acl, name, q_depth); 2253699d92aSKiran Patil } 2263699d92aSKiran Patil 2273699d92aSKiran Patil static void ft_del_acl(struct se_node_acl *se_acl) 2283699d92aSKiran Patil { 2293699d92aSKiran Patil struct se_portal_group *se_tpg = se_acl->se_tpg; 2303699d92aSKiran Patil struct ft_tpg *tpg; 2313699d92aSKiran Patil struct ft_node_acl *acl = container_of(se_acl, 2323699d92aSKiran Patil struct ft_node_acl, se_node_acl); 2333699d92aSKiran Patil 2346708bb27SAndy Grover pr_debug("del acl %s\n", 2353699d92aSKiran Patil config_item_name(&se_acl->acl_group.cg_item)); 2363699d92aSKiran Patil 2373699d92aSKiran Patil tpg = container_of(se_tpg, struct ft_tpg, se_tpg); 2386708bb27SAndy Grover pr_debug("del acl %p se_acl %p tpg %p se_tpg %p\n", 2393699d92aSKiran Patil acl, se_acl, tpg, &tpg->se_tpg); 2403699d92aSKiran Patil 2413699d92aSKiran Patil core_tpg_del_initiator_node_acl(&tpg->se_tpg, se_acl, 1); 2423699d92aSKiran Patil kfree(acl); 2433699d92aSKiran Patil } 2443699d92aSKiran Patil 2453699d92aSKiran Patil struct ft_node_acl *ft_acl_get(struct ft_tpg *tpg, struct fc_rport_priv *rdata) 2463699d92aSKiran Patil { 2473699d92aSKiran Patil struct ft_node_acl *found = NULL; 2483699d92aSKiran Patil struct ft_node_acl *acl; 2493699d92aSKiran Patil struct se_portal_group *se_tpg = &tpg->se_tpg; 2503699d92aSKiran Patil struct se_node_acl *se_acl; 2513699d92aSKiran Patil 25228638887SRoland Dreier spin_lock_irq(&se_tpg->acl_node_lock); 2533699d92aSKiran Patil list_for_each_entry(se_acl, &se_tpg->acl_node_list, acl_list) { 2543699d92aSKiran Patil acl = container_of(se_acl, struct ft_node_acl, se_node_acl); 2556708bb27SAndy Grover pr_debug("acl %p port_name %llx\n", 2563699d92aSKiran Patil acl, (unsigned long long)acl->node_auth.port_name); 2573699d92aSKiran Patil if (acl->node_auth.port_name == rdata->ids.port_name || 2583699d92aSKiran Patil acl->node_auth.node_name == rdata->ids.node_name) { 2596708bb27SAndy Grover pr_debug("acl %p port_name %llx matched\n", acl, 2603699d92aSKiran Patil (unsigned long long)rdata->ids.port_name); 2613699d92aSKiran Patil found = acl; 2623699d92aSKiran Patil /* XXX need to hold onto ACL */ 2633699d92aSKiran Patil break; 2643699d92aSKiran Patil } 2653699d92aSKiran Patil } 26628638887SRoland Dreier spin_unlock_irq(&se_tpg->acl_node_lock); 2673699d92aSKiran Patil return found; 2683699d92aSKiran Patil } 2693699d92aSKiran Patil 270594c42e9SRashika Kheria static struct se_node_acl *ft_tpg_alloc_fabric_acl(struct se_portal_group *se_tpg) 2713699d92aSKiran Patil { 2723699d92aSKiran Patil struct ft_node_acl *acl; 2733699d92aSKiran Patil 2743699d92aSKiran Patil acl = kzalloc(sizeof(*acl), GFP_KERNEL); 2752be18149SNicholas Bellinger if (!acl) { 2766708bb27SAndy Grover pr_err("Unable to allocate struct ft_node_acl\n"); 2773699d92aSKiran Patil return NULL; 2783699d92aSKiran Patil } 2796708bb27SAndy Grover pr_debug("acl %p\n", acl); 2803699d92aSKiran Patil return &acl->se_node_acl; 2813699d92aSKiran Patil } 2823699d92aSKiran Patil 2833699d92aSKiran Patil static void ft_tpg_release_fabric_acl(struct se_portal_group *se_tpg, 2843699d92aSKiran Patil struct se_node_acl *se_acl) 2853699d92aSKiran Patil { 2863699d92aSKiran Patil struct ft_node_acl *acl = container_of(se_acl, 2873699d92aSKiran Patil struct ft_node_acl, se_node_acl); 2883699d92aSKiran Patil 2896708bb27SAndy Grover pr_debug("acl %p\n", acl); 2903699d92aSKiran Patil kfree(acl); 2913699d92aSKiran Patil } 2923699d92aSKiran Patil 2933699d92aSKiran Patil /* 2943699d92aSKiran Patil * local_port port_group (tpg) ops. 2953699d92aSKiran Patil */ 2963699d92aSKiran Patil static struct se_portal_group *ft_add_tpg( 2973699d92aSKiran Patil struct se_wwn *wwn, 2983699d92aSKiran Patil struct config_group *group, 2993699d92aSKiran Patil const char *name) 3003699d92aSKiran Patil { 3013699d92aSKiran Patil struct ft_lport_acl *lacl; 3023699d92aSKiran Patil struct ft_tpg *tpg; 30306383f10SMark Rustad struct workqueue_struct *wq; 3043699d92aSKiran Patil unsigned long index; 3053699d92aSKiran Patil int ret; 3063699d92aSKiran Patil 3076708bb27SAndy Grover pr_debug("tcm_fc: add tpg %s\n", name); 3083699d92aSKiran Patil 3093699d92aSKiran Patil /* 3103699d92aSKiran Patil * Name must be "tpgt_" followed by the index. 3113699d92aSKiran Patil */ 3123699d92aSKiran Patil if (strstr(name, "tpgt_") != name) 3133699d92aSKiran Patil return NULL; 31457103d7fSJingoo Han 31557103d7fSJingoo Han ret = kstrtoul(name + 5, 10, &index); 31657103d7fSJingoo Han if (ret) 31757103d7fSJingoo Han return NULL; 31857103d7fSJingoo Han if (index > UINT_MAX) 3193699d92aSKiran Patil return NULL; 3203699d92aSKiran Patil 321*d242c1d7SAndy Grover if ((index != 1)) { 322*d242c1d7SAndy Grover pr_err("Error, a single TPG=1 is used for HW port mappings\n"); 323*d242c1d7SAndy Grover return ERR_PTR(-ENOSYS); 324*d242c1d7SAndy Grover } 325*d242c1d7SAndy Grover 3263699d92aSKiran Patil lacl = container_of(wwn, struct ft_lport_acl, fc_lport_wwn); 3273699d92aSKiran Patil tpg = kzalloc(sizeof(*tpg), GFP_KERNEL); 3283699d92aSKiran Patil if (!tpg) 3293699d92aSKiran Patil return NULL; 3303699d92aSKiran Patil tpg->index = index; 3313699d92aSKiran Patil tpg->lport_acl = lacl; 3323699d92aSKiran Patil INIT_LIST_HEAD(&tpg->lun_list); 3333699d92aSKiran Patil 33406383f10SMark Rustad wq = alloc_workqueue("tcm_fc", 0, 1); 33506383f10SMark Rustad if (!wq) { 3363699d92aSKiran Patil kfree(tpg); 3373699d92aSKiran Patil return NULL; 3383699d92aSKiran Patil } 3393699d92aSKiran Patil 34006383f10SMark Rustad ret = core_tpg_register(&ft_configfs->tf_ops, wwn, &tpg->se_tpg, 34106383f10SMark Rustad tpg, TRANSPORT_TPG_TYPE_NORMAL); 34206383f10SMark Rustad if (ret < 0) { 34306383f10SMark Rustad destroy_workqueue(wq); 3443699d92aSKiran Patil kfree(tpg); 3453699d92aSKiran Patil return NULL; 3463699d92aSKiran Patil } 34706383f10SMark Rustad tpg->workqueue = wq; 3483699d92aSKiran Patil 3493699d92aSKiran Patil mutex_lock(&ft_lport_lock); 350*d242c1d7SAndy Grover lacl->tpg = tpg; 3513699d92aSKiran Patil mutex_unlock(&ft_lport_lock); 3523699d92aSKiran Patil 3533699d92aSKiran Patil return &tpg->se_tpg; 3543699d92aSKiran Patil } 3553699d92aSKiran Patil 3563699d92aSKiran Patil static void ft_del_tpg(struct se_portal_group *se_tpg) 3573699d92aSKiran Patil { 3583699d92aSKiran Patil struct ft_tpg *tpg = container_of(se_tpg, struct ft_tpg, se_tpg); 359*d242c1d7SAndy Grover struct ft_lport_acl *lacl = tpg->lport_acl; 3603699d92aSKiran Patil 3616708bb27SAndy Grover pr_debug("del tpg %s\n", 3623699d92aSKiran Patil config_item_name(&tpg->se_tpg.tpg_group.cg_item)); 3633699d92aSKiran Patil 36458fc73d1SChristoph Hellwig destroy_workqueue(tpg->workqueue); 3653699d92aSKiran Patil 3663699d92aSKiran Patil /* Wait for sessions to be freed thru RCU, for BUG_ON below */ 3673699d92aSKiran Patil synchronize_rcu(); 3683699d92aSKiran Patil 3693699d92aSKiran Patil mutex_lock(&ft_lport_lock); 370*d242c1d7SAndy Grover lacl->tpg = NULL; 371*d242c1d7SAndy Grover 3723699d92aSKiran Patil if (tpg->tport) { 3733699d92aSKiran Patil tpg->tport->tpg = NULL; 3743699d92aSKiran Patil tpg->tport = NULL; 3753699d92aSKiran Patil } 3763699d92aSKiran Patil mutex_unlock(&ft_lport_lock); 3773699d92aSKiran Patil 3783699d92aSKiran Patil core_tpg_deregister(se_tpg); 3793699d92aSKiran Patil kfree(tpg); 3803699d92aSKiran Patil } 3813699d92aSKiran Patil 3823699d92aSKiran Patil /* 3833699d92aSKiran Patil * Verify that an lport is configured to use the tcm_fc module, and return 3843699d92aSKiran Patil * the target port group that should be used. 3853699d92aSKiran Patil * 3863699d92aSKiran Patil * The caller holds ft_lport_lock. 3873699d92aSKiran Patil */ 3883699d92aSKiran Patil struct ft_tpg *ft_lport_find_tpg(struct fc_lport *lport) 3893699d92aSKiran Patil { 3903699d92aSKiran Patil struct ft_lport_acl *lacl; 3913699d92aSKiran Patil 3923699d92aSKiran Patil list_for_each_entry(lacl, &ft_lport_list, list) { 393*d242c1d7SAndy Grover if (lacl->wwpn == lport->wwpn) 394*d242c1d7SAndy Grover return lacl->tpg; 3953699d92aSKiran Patil } 3963699d92aSKiran Patil return NULL; 3973699d92aSKiran Patil } 3983699d92aSKiran Patil 3993699d92aSKiran Patil /* 4003699d92aSKiran Patil * target config instance ops. 4013699d92aSKiran Patil */ 4023699d92aSKiran Patil 4033699d92aSKiran Patil /* 4043699d92aSKiran Patil * Add lport to allowed config. 4053699d92aSKiran Patil * The name is the WWPN in lower-case ASCII, colon-separated bytes. 4063699d92aSKiran Patil */ 4073699d92aSKiran Patil static struct se_wwn *ft_add_lport( 4083699d92aSKiran Patil struct target_fabric_configfs *tf, 4093699d92aSKiran Patil struct config_group *group, 4103699d92aSKiran Patil const char *name) 4113699d92aSKiran Patil { 4123699d92aSKiran Patil struct ft_lport_acl *lacl; 4133699d92aSKiran Patil struct ft_lport_acl *old_lacl; 4143699d92aSKiran Patil u64 wwpn; 4153699d92aSKiran Patil 4166708bb27SAndy Grover pr_debug("add lport %s\n", name); 4173699d92aSKiran Patil if (ft_parse_wwn(name, &wwpn, 1) < 0) 4183699d92aSKiran Patil return NULL; 4193699d92aSKiran Patil lacl = kzalloc(sizeof(*lacl), GFP_KERNEL); 4203699d92aSKiran Patil if (!lacl) 4213699d92aSKiran Patil return NULL; 4223699d92aSKiran Patil lacl->wwpn = wwpn; 4233699d92aSKiran Patil 4243699d92aSKiran Patil mutex_lock(&ft_lport_lock); 4253699d92aSKiran Patil list_for_each_entry(old_lacl, &ft_lport_list, list) { 4263699d92aSKiran Patil if (old_lacl->wwpn == wwpn) { 4273699d92aSKiran Patil mutex_unlock(&ft_lport_lock); 4283699d92aSKiran Patil kfree(lacl); 4293699d92aSKiran Patil return NULL; 4303699d92aSKiran Patil } 4313699d92aSKiran Patil } 4323699d92aSKiran Patil list_add_tail(&lacl->list, &ft_lport_list); 4333699d92aSKiran Patil ft_format_wwn(lacl->name, sizeof(lacl->name), wwpn); 4343699d92aSKiran Patil mutex_unlock(&ft_lport_lock); 4353699d92aSKiran Patil 4363699d92aSKiran Patil return &lacl->fc_lport_wwn; 4373699d92aSKiran Patil } 4383699d92aSKiran Patil 4393699d92aSKiran Patil static void ft_del_lport(struct se_wwn *wwn) 4403699d92aSKiran Patil { 4413699d92aSKiran Patil struct ft_lport_acl *lacl = container_of(wwn, 4423699d92aSKiran Patil struct ft_lport_acl, fc_lport_wwn); 4433699d92aSKiran Patil 4446297b07cSNicholas Bellinger pr_debug("del lport %s\n", lacl->name); 4453699d92aSKiran Patil mutex_lock(&ft_lport_lock); 4463699d92aSKiran Patil list_del(&lacl->list); 4473699d92aSKiran Patil mutex_unlock(&ft_lport_lock); 4483699d92aSKiran Patil 4493699d92aSKiran Patil kfree(lacl); 4503699d92aSKiran Patil } 4513699d92aSKiran Patil 4523699d92aSKiran Patil static ssize_t ft_wwn_show_attr_version( 4533699d92aSKiran Patil struct target_fabric_configfs *tf, 4543699d92aSKiran Patil char *page) 4553699d92aSKiran Patil { 4563699d92aSKiran Patil return sprintf(page, "TCM FC " FT_VERSION " on %s/%s on " 4573699d92aSKiran Patil ""UTS_RELEASE"\n", utsname()->sysname, utsname()->machine); 4583699d92aSKiran Patil } 4593699d92aSKiran Patil 4603699d92aSKiran Patil TF_WWN_ATTR_RO(ft, version); 4613699d92aSKiran Patil 4623699d92aSKiran Patil static struct configfs_attribute *ft_wwn_attrs[] = { 4633699d92aSKiran Patil &ft_wwn_version.attr, 4643699d92aSKiran Patil NULL, 4653699d92aSKiran Patil }; 4663699d92aSKiran Patil 4673699d92aSKiran Patil static char *ft_get_fabric_name(void) 4683699d92aSKiran Patil { 4693699d92aSKiran Patil return "fc"; 4703699d92aSKiran Patil } 4713699d92aSKiran Patil 4723699d92aSKiran Patil static char *ft_get_fabric_wwn(struct se_portal_group *se_tpg) 4733699d92aSKiran Patil { 4743699d92aSKiran Patil struct ft_tpg *tpg = se_tpg->se_tpg_fabric_ptr; 4753699d92aSKiran Patil 4763699d92aSKiran Patil return tpg->lport_acl->name; 4773699d92aSKiran Patil } 4783699d92aSKiran Patil 4793699d92aSKiran Patil static u16 ft_get_tag(struct se_portal_group *se_tpg) 4803699d92aSKiran Patil { 4813699d92aSKiran Patil struct ft_tpg *tpg = se_tpg->se_tpg_fabric_ptr; 4823699d92aSKiran Patil 4833699d92aSKiran Patil /* 4843699d92aSKiran Patil * This tag is used when forming SCSI Name identifier in EVPD=1 0x83 4853699d92aSKiran Patil * to represent the SCSI Target Port. 4863699d92aSKiran Patil */ 4873699d92aSKiran Patil return tpg->index; 4883699d92aSKiran Patil } 4893699d92aSKiran Patil 4903699d92aSKiran Patil static u32 ft_get_default_depth(struct se_portal_group *se_tpg) 4913699d92aSKiran Patil { 4923699d92aSKiran Patil return 1; 4933699d92aSKiran Patil } 4943699d92aSKiran Patil 4953699d92aSKiran Patil static int ft_check_false(struct se_portal_group *se_tpg) 4963699d92aSKiran Patil { 4973699d92aSKiran Patil return 0; 4983699d92aSKiran Patil } 4993699d92aSKiran Patil 5003699d92aSKiran Patil static void ft_set_default_node_attr(struct se_node_acl *se_nacl) 5013699d92aSKiran Patil { 5023699d92aSKiran Patil } 5033699d92aSKiran Patil 5043699d92aSKiran Patil static u32 ft_tpg_get_inst_index(struct se_portal_group *se_tpg) 5053699d92aSKiran Patil { 5063699d92aSKiran Patil struct ft_tpg *tpg = se_tpg->se_tpg_fabric_ptr; 5073699d92aSKiran Patil 5083699d92aSKiran Patil return tpg->index; 5093699d92aSKiran Patil } 5103699d92aSKiran Patil 5113699d92aSKiran Patil static struct target_core_fabric_ops ft_fabric_ops = { 5123699d92aSKiran Patil .get_fabric_name = ft_get_fabric_name, 5133699d92aSKiran Patil .get_fabric_proto_ident = fc_get_fabric_proto_ident, 5143699d92aSKiran Patil .tpg_get_wwn = ft_get_fabric_wwn, 5153699d92aSKiran Patil .tpg_get_tag = ft_get_tag, 5163699d92aSKiran Patil .tpg_get_default_depth = ft_get_default_depth, 5173699d92aSKiran Patil .tpg_get_pr_transport_id = fc_get_pr_transport_id, 5183699d92aSKiran Patil .tpg_get_pr_transport_id_len = fc_get_pr_transport_id_len, 5193699d92aSKiran Patil .tpg_parse_pr_out_transport_id = fc_parse_pr_out_transport_id, 5203699d92aSKiran Patil .tpg_check_demo_mode = ft_check_false, 5213699d92aSKiran Patil .tpg_check_demo_mode_cache = ft_check_false, 5223699d92aSKiran Patil .tpg_check_demo_mode_write_protect = ft_check_false, 5233699d92aSKiran Patil .tpg_check_prod_mode_write_protect = ft_check_false, 5243699d92aSKiran Patil .tpg_alloc_fabric_acl = ft_tpg_alloc_fabric_acl, 5253699d92aSKiran Patil .tpg_release_fabric_acl = ft_tpg_release_fabric_acl, 5263699d92aSKiran Patil .tpg_get_inst_index = ft_tpg_get_inst_index, 5273699d92aSKiran Patil .check_stop_free = ft_check_stop_free, 52835462975SChristoph Hellwig .release_cmd = ft_release_cmd, 5293699d92aSKiran Patil .shutdown_session = ft_sess_shutdown, 5303699d92aSKiran Patil .close_session = ft_sess_close, 5313699d92aSKiran Patil .sess_get_index = ft_sess_get_index, 5323699d92aSKiran Patil .sess_get_initiator_sid = NULL, 5333699d92aSKiran Patil .write_pending = ft_write_pending, 5343699d92aSKiran Patil .write_pending_status = ft_write_pending_status, 5353699d92aSKiran Patil .set_default_node_attributes = ft_set_default_node_attr, 5363699d92aSKiran Patil .get_task_tag = ft_get_task_tag, 5373699d92aSKiran Patil .get_cmd_state = ft_get_cmd_state, 5383699d92aSKiran Patil .queue_data_in = ft_queue_data_in, 5393699d92aSKiran Patil .queue_status = ft_queue_status, 5403699d92aSKiran Patil .queue_tm_rsp = ft_queue_tm_resp, 541131e6abcSNicholas Bellinger .aborted_task = ft_aborted_task, 5423699d92aSKiran Patil /* 5433699d92aSKiran Patil * Setup function pointers for generic logic in 5443699d92aSKiran Patil * target_core_fabric_configfs.c 5453699d92aSKiran Patil */ 5463699d92aSKiran Patil .fabric_make_wwn = &ft_add_lport, 5473699d92aSKiran Patil .fabric_drop_wwn = &ft_del_lport, 5483699d92aSKiran Patil .fabric_make_tpg = &ft_add_tpg, 5493699d92aSKiran Patil .fabric_drop_tpg = &ft_del_tpg, 5503699d92aSKiran Patil .fabric_post_link = NULL, 5513699d92aSKiran Patil .fabric_pre_unlink = NULL, 5523699d92aSKiran Patil .fabric_make_np = NULL, 5533699d92aSKiran Patil .fabric_drop_np = NULL, 5543699d92aSKiran Patil .fabric_make_nodeacl = &ft_add_acl, 5553699d92aSKiran Patil .fabric_drop_nodeacl = &ft_del_acl, 5563699d92aSKiran Patil }; 5573699d92aSKiran Patil 558594c42e9SRashika Kheria static int ft_register_configfs(void) 5593699d92aSKiran Patil { 5603699d92aSKiran Patil struct target_fabric_configfs *fabric; 5613699d92aSKiran Patil int ret; 5623699d92aSKiran Patil 5633699d92aSKiran Patil /* 5643699d92aSKiran Patil * Register the top level struct config_item_type with TCM core 5653699d92aSKiran Patil */ 5663699d92aSKiran Patil fabric = target_fabric_configfs_init(THIS_MODULE, "fc"); 567e3d6f909SAndy Grover if (IS_ERR(fabric)) { 5686708bb27SAndy Grover pr_err("%s: target_fabric_configfs_init() failed!\n", 5693699d92aSKiran Patil __func__); 570e3d6f909SAndy Grover return PTR_ERR(fabric); 5713699d92aSKiran Patil } 5723699d92aSKiran Patil fabric->tf_ops = ft_fabric_ops; 5733699d92aSKiran Patil 5743699d92aSKiran Patil /* 5753699d92aSKiran Patil * Setup default attribute lists for various fabric->tf_cit_tmpl 5763699d92aSKiran Patil */ 577d80e224dSAndy Grover fabric->tf_cit_tmpl.tfc_wwn_cit.ct_attrs = ft_wwn_attrs; 578d80e224dSAndy Grover fabric->tf_cit_tmpl.tfc_tpg_base_cit.ct_attrs = NULL; 579d80e224dSAndy Grover fabric->tf_cit_tmpl.tfc_tpg_attrib_cit.ct_attrs = NULL; 580d80e224dSAndy Grover fabric->tf_cit_tmpl.tfc_tpg_param_cit.ct_attrs = NULL; 581d80e224dSAndy Grover fabric->tf_cit_tmpl.tfc_tpg_np_base_cit.ct_attrs = NULL; 582d80e224dSAndy Grover fabric->tf_cit_tmpl.tfc_tpg_nacl_base_cit.ct_attrs = 5833699d92aSKiran Patil ft_nacl_base_attrs; 584d80e224dSAndy Grover fabric->tf_cit_tmpl.tfc_tpg_nacl_attrib_cit.ct_attrs = NULL; 585d80e224dSAndy Grover fabric->tf_cit_tmpl.tfc_tpg_nacl_auth_cit.ct_attrs = NULL; 586d80e224dSAndy Grover fabric->tf_cit_tmpl.tfc_tpg_nacl_param_cit.ct_attrs = NULL; 5873699d92aSKiran Patil /* 5883699d92aSKiran Patil * register the fabric for use within TCM 5893699d92aSKiran Patil */ 5903699d92aSKiran Patil ret = target_fabric_configfs_register(fabric); 5913699d92aSKiran Patil if (ret < 0) { 5926708bb27SAndy Grover pr_debug("target_fabric_configfs_register() for" 5933699d92aSKiran Patil " FC Target failed!\n"); 5943699d92aSKiran Patil target_fabric_configfs_free(fabric); 5953699d92aSKiran Patil return -1; 5963699d92aSKiran Patil } 5973699d92aSKiran Patil 5983699d92aSKiran Patil /* 5993699d92aSKiran Patil * Setup our local pointer to *fabric. 6003699d92aSKiran Patil */ 6013699d92aSKiran Patil ft_configfs = fabric; 6023699d92aSKiran Patil return 0; 6033699d92aSKiran Patil } 6043699d92aSKiran Patil 605594c42e9SRashika Kheria static void ft_deregister_configfs(void) 6063699d92aSKiran Patil { 6073699d92aSKiran Patil if (!ft_configfs) 6083699d92aSKiran Patil return; 6093699d92aSKiran Patil target_fabric_configfs_deregister(ft_configfs); 6103699d92aSKiran Patil ft_configfs = NULL; 6113699d92aSKiran Patil } 6123699d92aSKiran Patil 6133699d92aSKiran Patil static struct notifier_block ft_notifier = { 6143699d92aSKiran Patil .notifier_call = ft_lport_notify 6153699d92aSKiran Patil }; 6163699d92aSKiran Patil 6173699d92aSKiran Patil static int __init ft_init(void) 6183699d92aSKiran Patil { 6193699d92aSKiran Patil if (ft_register_configfs()) 6203699d92aSKiran Patil return -1; 6213699d92aSKiran Patil if (fc_fc4_register_provider(FC_TYPE_FCP, &ft_prov)) { 6223699d92aSKiran Patil ft_deregister_configfs(); 6233699d92aSKiran Patil return -1; 6243699d92aSKiran Patil } 6253699d92aSKiran Patil blocking_notifier_chain_register(&fc_lport_notifier_head, &ft_notifier); 6263699d92aSKiran Patil fc_lport_iterate(ft_lport_add, NULL); 6273699d92aSKiran Patil return 0; 6283699d92aSKiran Patil } 6293699d92aSKiran Patil 6303699d92aSKiran Patil static void __exit ft_exit(void) 6313699d92aSKiran Patil { 6323699d92aSKiran Patil blocking_notifier_chain_unregister(&fc_lport_notifier_head, 6333699d92aSKiran Patil &ft_notifier); 6343699d92aSKiran Patil fc_fc4_deregister_provider(FC_TYPE_FCP, &ft_prov); 6353699d92aSKiran Patil fc_lport_iterate(ft_lport_del, NULL); 6363699d92aSKiran Patil ft_deregister_configfs(); 6373699d92aSKiran Patil synchronize_rcu(); 6383699d92aSKiran Patil } 6393699d92aSKiran Patil 6403699d92aSKiran Patil MODULE_DESCRIPTION("FC TCM fabric driver " FT_VERSION); 6413699d92aSKiran Patil MODULE_LICENSE("GPL"); 6423699d92aSKiran Patil module_init(ft_init); 6433699d92aSKiran Patil module_exit(ft_exit); 644