480 lines · c
1// SPDX-License-Identifier: GPL-2.0-or-later2/*******************************************************************************3 * Filename: tcm_fc.c4 *5 * This file contains the configfs implementation for TCM_fc fabric node.6 * Based on tcm_loop_configfs.c7 *8 * Copyright (c) 2010 Cisco Systems, Inc.9 * Copyright (c) 2009,2010 Rising Tide, Inc.10 * Copyright (c) 2009,2010 Linux-iSCSI.org11 *12 * Copyright (c) 2009,2010 Nicholas A. Bellinger <nab@linux-iscsi.org>13 *14 ****************************************************************************/15 16#include <linux/module.h>17#include <linux/moduleparam.h>18#include <generated/utsrelease.h>19#include <linux/utsname.h>20#include <linux/init.h>21#include <linux/slab.h>22#include <linux/kthread.h>23#include <linux/types.h>24#include <linux/string.h>25#include <linux/configfs.h>26#include <linux/kernel.h>27#include <linux/ctype.h>28#include <linux/unaligned.h>29#include <scsi/libfc.h>30 31#include <target/target_core_base.h>32#include <target/target_core_fabric.h>33 34#include "tcm_fc.h"35 36static LIST_HEAD(ft_wwn_list);37DEFINE_MUTEX(ft_lport_lock);38 39unsigned int ft_debug_logging;40module_param_named(debug_logging, ft_debug_logging, int, S_IRUGO|S_IWUSR);41MODULE_PARM_DESC(debug_logging, "a bit mask of logging levels");42 43/*44 * Parse WWN.45 * If strict, we require lower-case hex and colon separators to be sure46 * the name is the same as what would be generated by ft_format_wwn()47 * so the name and wwn are mapped one-to-one.48 */49static ssize_t ft_parse_wwn(const char *name, u64 *wwn, int strict)50{51 const char *cp;52 char c;53 u32 byte = 0;54 u32 pos = 0;55 u32 err;56 int val;57 58 *wwn = 0;59 for (cp = name; cp < &name[FT_NAMELEN - 1]; cp++) {60 c = *cp;61 if (c == '\n' && cp[1] == '\0')62 continue;63 if (strict && pos++ == 2 && byte++ < 7) {64 pos = 0;65 if (c == ':')66 continue;67 err = 1;68 goto fail;69 }70 if (c == '\0') {71 err = 2;72 if (strict && byte != 8)73 goto fail;74 return cp - name;75 }76 err = 3;77 val = hex_to_bin(c);78 if (val < 0 || (strict && isupper(c)))79 goto fail;80 *wwn = (*wwn << 4) | val;81 }82 err = 4;83fail:84 pr_debug("err %u len %zu pos %u byte %u\n",85 err, cp - name, pos, byte);86 return -1;87}88 89ssize_t ft_format_wwn(char *buf, size_t len, u64 wwn)90{91 u8 b[8];92 93 put_unaligned_be64(wwn, b);94 return snprintf(buf, len,95 "%2.2x:%2.2x:%2.2x:%2.2x:%2.2x:%2.2x:%2.2x:%2.2x",96 b[0], b[1], b[2], b[3], b[4], b[5], b[6], b[7]);97}98 99static ssize_t ft_wwn_show(void *arg, char *buf)100{101 u64 *wwn = arg;102 ssize_t len;103 104 len = ft_format_wwn(buf, PAGE_SIZE - 2, *wwn);105 buf[len++] = '\n';106 return len;107}108 109static ssize_t ft_wwn_store(void *arg, const char *buf, size_t len)110{111 ssize_t ret;112 u64 wwn;113 114 ret = ft_parse_wwn(buf, &wwn, 0);115 if (ret > 0)116 *(u64 *)arg = wwn;117 return ret;118}119 120/*121 * ACL auth ops.122 */123 124static ssize_t ft_nacl_port_name_show(struct config_item *item, char *page)125{126 struct se_node_acl *se_nacl = acl_to_nacl(item);127 struct ft_node_acl *acl = container_of(se_nacl,128 struct ft_node_acl, se_node_acl);129 130 return ft_wwn_show(&acl->node_auth.port_name, page);131}132 133static ssize_t ft_nacl_port_name_store(struct config_item *item,134 const char *page, size_t count)135{136 struct se_node_acl *se_nacl = acl_to_nacl(item);137 struct ft_node_acl *acl = container_of(se_nacl,138 struct ft_node_acl, se_node_acl);139 140 return ft_wwn_store(&acl->node_auth.port_name, page, count);141}142 143static ssize_t ft_nacl_node_name_show(struct config_item *item,144 char *page)145{146 struct se_node_acl *se_nacl = acl_to_nacl(item);147 struct ft_node_acl *acl = container_of(se_nacl,148 struct ft_node_acl, se_node_acl);149 150 return ft_wwn_show(&acl->node_auth.node_name, page);151}152 153static ssize_t ft_nacl_node_name_store(struct config_item *item,154 const char *page, size_t count)155{156 struct se_node_acl *se_nacl = acl_to_nacl(item);157 struct ft_node_acl *acl = container_of(se_nacl,158 struct ft_node_acl, se_node_acl);159 160 return ft_wwn_store(&acl->node_auth.node_name, page, count);161}162 163CONFIGFS_ATTR(ft_nacl_, node_name);164CONFIGFS_ATTR(ft_nacl_, port_name);165 166static ssize_t ft_nacl_tag_show(struct config_item *item,167 char *page)168{169 return snprintf(page, PAGE_SIZE, "%s", acl_to_nacl(item)->acl_tag);170}171 172static ssize_t ft_nacl_tag_store(struct config_item *item,173 const char *page, size_t count)174{175 struct se_node_acl *se_nacl = acl_to_nacl(item);176 int ret;177 178 ret = core_tpg_set_initiator_node_tag(se_nacl->se_tpg, se_nacl, page);179 180 if (ret < 0)181 return ret;182 return count;183}184 185CONFIGFS_ATTR(ft_nacl_, tag);186 187static struct configfs_attribute *ft_nacl_base_attrs[] = {188 &ft_nacl_attr_port_name,189 &ft_nacl_attr_node_name,190 &ft_nacl_attr_tag,191 NULL,192};193 194/*195 * ACL ops.196 */197 198/*199 * Add ACL for an initiator. The ACL is named arbitrarily.200 * The port_name and/or node_name are attributes.201 */202static int ft_init_nodeacl(struct se_node_acl *nacl, const char *name)203{204 struct ft_node_acl *acl =205 container_of(nacl, struct ft_node_acl, se_node_acl);206 u64 wwpn;207 208 if (ft_parse_wwn(name, &wwpn, 1) < 0)209 return -EINVAL;210 211 acl->node_auth.port_name = wwpn;212 return 0;213}214 215/*216 * local_port port_group (tpg) ops.217 */218static struct se_portal_group *ft_add_tpg(struct se_wwn *wwn, const char *name)219{220 struct ft_lport_wwn *ft_wwn;221 struct ft_tpg *tpg;222 struct workqueue_struct *wq;223 unsigned long index;224 int ret;225 226 pr_debug("tcm_fc: add tpg %s\n", name);227 228 /*229 * Name must be "tpgt_" followed by the index.230 */231 if (strstr(name, "tpgt_") != name)232 return NULL;233 234 ret = kstrtoul(name + 5, 10, &index);235 if (ret)236 return NULL;237 if (index > UINT_MAX)238 return NULL;239 240 if ((index != 1)) {241 pr_err("Error, a single TPG=1 is used for HW port mappings\n");242 return ERR_PTR(-ENOSYS);243 }244 245 ft_wwn = container_of(wwn, struct ft_lport_wwn, se_wwn);246 tpg = kzalloc(sizeof(*tpg), GFP_KERNEL);247 if (!tpg)248 return NULL;249 tpg->index = index;250 tpg->lport_wwn = ft_wwn;251 INIT_LIST_HEAD(&tpg->lun_list);252 253 wq = alloc_workqueue("tcm_fc", 0, 1);254 if (!wq) {255 kfree(tpg);256 return NULL;257 }258 259 ret = core_tpg_register(wwn, &tpg->se_tpg, SCSI_PROTOCOL_FCP);260 if (ret < 0) {261 destroy_workqueue(wq);262 kfree(tpg);263 return NULL;264 }265 tpg->workqueue = wq;266 267 mutex_lock(&ft_lport_lock);268 ft_wwn->tpg = tpg;269 mutex_unlock(&ft_lport_lock);270 271 return &tpg->se_tpg;272}273 274static void ft_del_tpg(struct se_portal_group *se_tpg)275{276 struct ft_tpg *tpg = container_of(se_tpg, struct ft_tpg, se_tpg);277 struct ft_lport_wwn *ft_wwn = tpg->lport_wwn;278 279 pr_debug("del tpg %s\n",280 config_item_name(&tpg->se_tpg.tpg_group.cg_item));281 282 destroy_workqueue(tpg->workqueue);283 284 /* Wait for sessions to be freed thru RCU, for BUG_ON below */285 synchronize_rcu();286 287 mutex_lock(&ft_lport_lock);288 ft_wwn->tpg = NULL;289 if (tpg->tport) {290 tpg->tport->tpg = NULL;291 tpg->tport = NULL;292 }293 mutex_unlock(&ft_lport_lock);294 295 core_tpg_deregister(se_tpg);296 kfree(tpg);297}298 299/*300 * Verify that an lport is configured to use the tcm_fc module, and return301 * the target port group that should be used.302 *303 * The caller holds ft_lport_lock.304 */305struct ft_tpg *ft_lport_find_tpg(struct fc_lport *lport)306{307 struct ft_lport_wwn *ft_wwn;308 309 list_for_each_entry(ft_wwn, &ft_wwn_list, ft_wwn_node) {310 if (ft_wwn->wwpn == lport->wwpn)311 return ft_wwn->tpg;312 }313 return NULL;314}315 316/*317 * target config instance ops.318 */319 320/*321 * Add lport to allowed config.322 * The name is the WWPN in lower-case ASCII, colon-separated bytes.323 */324static struct se_wwn *ft_add_wwn(325 struct target_fabric_configfs *tf,326 struct config_group *group,327 const char *name)328{329 struct ft_lport_wwn *ft_wwn;330 struct ft_lport_wwn *old_ft_wwn;331 u64 wwpn;332 333 pr_debug("add wwn %s\n", name);334 if (ft_parse_wwn(name, &wwpn, 1) < 0)335 return NULL;336 ft_wwn = kzalloc(sizeof(*ft_wwn), GFP_KERNEL);337 if (!ft_wwn)338 return NULL;339 ft_wwn->wwpn = wwpn;340 341 mutex_lock(&ft_lport_lock);342 list_for_each_entry(old_ft_wwn, &ft_wwn_list, ft_wwn_node) {343 if (old_ft_wwn->wwpn == wwpn) {344 mutex_unlock(&ft_lport_lock);345 kfree(ft_wwn);346 return NULL;347 }348 }349 list_add_tail(&ft_wwn->ft_wwn_node, &ft_wwn_list);350 ft_format_wwn(ft_wwn->name, sizeof(ft_wwn->name), wwpn);351 mutex_unlock(&ft_lport_lock);352 353 return &ft_wwn->se_wwn;354}355 356static void ft_del_wwn(struct se_wwn *wwn)357{358 struct ft_lport_wwn *ft_wwn = container_of(wwn,359 struct ft_lport_wwn, se_wwn);360 361 pr_debug("del wwn %s\n", ft_wwn->name);362 mutex_lock(&ft_lport_lock);363 list_del(&ft_wwn->ft_wwn_node);364 mutex_unlock(&ft_lport_lock);365 366 kfree(ft_wwn);367}368 369static ssize_t ft_wwn_version_show(struct config_item *item, char *page)370{371 return sprintf(page, "TCM FC " FT_VERSION " on %s/%s on "372 ""UTS_RELEASE"\n", utsname()->sysname, utsname()->machine);373}374 375CONFIGFS_ATTR_RO(ft_wwn_, version);376 377static struct configfs_attribute *ft_wwn_attrs[] = {378 &ft_wwn_attr_version,379 NULL,380};381 382static inline struct ft_tpg *ft_tpg(struct se_portal_group *se_tpg)383{384 return container_of(se_tpg, struct ft_tpg, se_tpg);385}386 387static char *ft_get_fabric_wwn(struct se_portal_group *se_tpg)388{389 return ft_tpg(se_tpg)->lport_wwn->name;390}391 392static u16 ft_get_tag(struct se_portal_group *se_tpg)393{394 /*395 * This tag is used when forming SCSI Name identifier in EVPD=1 0x83396 * to represent the SCSI Target Port.397 */398 return ft_tpg(se_tpg)->index;399}400 401static u32 ft_tpg_get_inst_index(struct se_portal_group *se_tpg)402{403 return ft_tpg(se_tpg)->index;404}405 406static const struct target_core_fabric_ops ft_fabric_ops = {407 .module = THIS_MODULE,408 .fabric_name = "fc",409 .node_acl_size = sizeof(struct ft_node_acl),410 .tpg_get_wwn = ft_get_fabric_wwn,411 .tpg_get_tag = ft_get_tag,412 .tpg_get_inst_index = ft_tpg_get_inst_index,413 .check_stop_free = ft_check_stop_free,414 .release_cmd = ft_release_cmd,415 .close_session = ft_sess_close,416 .sess_get_index = ft_sess_get_index,417 .sess_get_initiator_sid = NULL,418 .write_pending = ft_write_pending,419 .queue_data_in = ft_queue_data_in,420 .queue_status = ft_queue_status,421 .queue_tm_rsp = ft_queue_tm_resp,422 .aborted_task = ft_aborted_task,423 /*424 * Setup function pointers for generic logic in425 * target_core_fabric_configfs.c426 */427 .fabric_make_wwn = &ft_add_wwn,428 .fabric_drop_wwn = &ft_del_wwn,429 .fabric_make_tpg = &ft_add_tpg,430 .fabric_drop_tpg = &ft_del_tpg,431 .fabric_init_nodeacl = &ft_init_nodeacl,432 433 .tfc_wwn_attrs = ft_wwn_attrs,434 .tfc_tpg_nacl_base_attrs = ft_nacl_base_attrs,435 436 .default_submit_type = TARGET_DIRECT_SUBMIT,437 .direct_submit_supp = 1,438};439 440static struct notifier_block ft_notifier = {441 .notifier_call = ft_lport_notify442};443 444static int __init ft_init(void)445{446 int ret;447 448 ret = target_register_template(&ft_fabric_ops);449 if (ret)450 goto out;451 452 ret = fc_fc4_register_provider(FC_TYPE_FCP, &ft_prov);453 if (ret)454 goto out_unregister_template;455 456 blocking_notifier_chain_register(&fc_lport_notifier_head, &ft_notifier);457 fc_lport_iterate(ft_lport_add, NULL);458 return 0;459 460out_unregister_template:461 target_unregister_template(&ft_fabric_ops);462out:463 return ret;464}465 466static void __exit ft_exit(void)467{468 blocking_notifier_chain_unregister(&fc_lport_notifier_head,469 &ft_notifier);470 fc_fc4_deregister_provider(FC_TYPE_FCP, &ft_prov);471 fc_lport_iterate(ft_lport_del, NULL);472 target_unregister_template(&ft_fabric_ops);473 synchronize_rcu();474}475 476MODULE_DESCRIPTION("FC TCM fabric driver " FT_VERSION);477MODULE_LICENSE("GPL");478module_init(ft_init);479module_exit(ft_exit);480