brintos

brintos / linux-shallow public Read only

0
0
Text · 10.9 KiB · 639fc35 Raw
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