brintos

brintos / linux-shallow public Read only

0
0
Text · 11.3 KiB · d6afaba Raw
504 lines · c
1// SPDX-License-Identifier: GPL-2.0-only2/*3 * Copyright (c) 2010 Cisco Systems, Inc.4 */5 6/* XXX TBD some includes may be extraneous */7 8#include <linux/module.h>9#include <linux/moduleparam.h>10#include <linux/utsname.h>11#include <linux/init.h>12#include <linux/slab.h>13#include <linux/kthread.h>14#include <linux/types.h>15#include <linux/string.h>16#include <linux/configfs.h>17#include <linux/ctype.h>18#include <linux/hash.h>19#include <linux/rcupdate.h>20#include <linux/rculist.h>21#include <linux/kref.h>22#include <linux/unaligned.h>23#include <scsi/libfc.h>24 25#include <target/target_core_base.h>26#include <target/target_core_fabric.h>27 28#include "tcm_fc.h"29 30#define TFC_SESS_DBG(lport, fmt, args...) \31	pr_debug("host%u: rport %6.6x: " fmt,	   \32		 (lport)->host->host_no,	   \33		 (lport)->port_id, ##args )34 35static void ft_sess_delete_all(struct ft_tport *);36 37/*38 * Lookup or allocate target local port.39 * Caller holds ft_lport_lock.40 */41static struct ft_tport *ft_tport_get(struct fc_lport *lport)42{43	struct ft_tpg *tpg;44	struct ft_tport *tport;45	int i;46 47	tport = rcu_dereference_protected(lport->prov[FC_TYPE_FCP],48					  lockdep_is_held(&ft_lport_lock));49	if (tport && tport->tpg)50		return tport;51 52	tpg = ft_lport_find_tpg(lport);53	if (!tpg)54		return NULL;55 56	if (tport) {57		tport->tpg = tpg;58		tpg->tport = tport;59		return tport;60	}61 62	tport = kzalloc(sizeof(*tport), GFP_KERNEL);63	if (!tport)64		return NULL;65 66	tport->lport = lport;67	tport->tpg = tpg;68	tpg->tport = tport;69	for (i = 0; i < FT_SESS_HASH_SIZE; i++)70		INIT_HLIST_HEAD(&tport->hash[i]);71 72	rcu_assign_pointer(lport->prov[FC_TYPE_FCP], tport);73	return tport;74}75 76/*77 * Delete a target local port.78 * Caller holds ft_lport_lock.79 */80static void ft_tport_delete(struct ft_tport *tport)81{82	struct fc_lport *lport;83	struct ft_tpg *tpg;84 85	ft_sess_delete_all(tport);86	lport = tport->lport;87	lport->service_params &= ~FCP_SPPF_TARG_FCN;88	BUG_ON(tport != lport->prov[FC_TYPE_FCP]);89	RCU_INIT_POINTER(lport->prov[FC_TYPE_FCP], NULL);90 91	tpg = tport->tpg;92	if (tpg) {93		tpg->tport = NULL;94		tport->tpg = NULL;95	}96	kfree_rcu(tport, rcu);97}98 99/*100 * Add local port.101 * Called thru fc_lport_iterate().102 */103void ft_lport_add(struct fc_lport *lport, void *arg)104{105	mutex_lock(&ft_lport_lock);106	ft_tport_get(lport);107	lport->service_params |= FCP_SPPF_TARG_FCN;108	mutex_unlock(&ft_lport_lock);109}110 111/*112 * Delete local port.113 * Called thru fc_lport_iterate().114 */115void ft_lport_del(struct fc_lport *lport, void *arg)116{117	struct ft_tport *tport;118 119	mutex_lock(&ft_lport_lock);120	tport = lport->prov[FC_TYPE_FCP];121	if (tport)122		ft_tport_delete(tport);123	mutex_unlock(&ft_lport_lock);124}125 126/*127 * Notification of local port change from libfc.128 * Create or delete local port and associated tport.129 */130int ft_lport_notify(struct notifier_block *nb, unsigned long event, void *arg)131{132	struct fc_lport *lport = arg;133 134	switch (event) {135	case FC_LPORT_EV_ADD:136		ft_lport_add(lport, NULL);137		break;138	case FC_LPORT_EV_DEL:139		ft_lport_del(lport, NULL);140		break;141	}142	return NOTIFY_DONE;143}144 145/*146 * Hash function for FC_IDs.147 */148static u32 ft_sess_hash(u32 port_id)149{150	return hash_32(port_id, FT_SESS_HASH_BITS);151}152 153/*154 * Find session in local port.155 * Sessions and hash lists are RCU-protected.156 * A reference is taken which must be eventually freed.157 */158static struct ft_sess *ft_sess_get(struct fc_lport *lport, u32 port_id)159{160	struct ft_tport *tport;161	struct hlist_head *head;162	struct ft_sess *sess;163	char *reason = "no session created";164 165	rcu_read_lock();166	tport = rcu_dereference(lport->prov[FC_TYPE_FCP]);167	if (!tport) {168		reason = "not an FCP port";169		goto out;170	}171 172	head = &tport->hash[ft_sess_hash(port_id)];173	hlist_for_each_entry_rcu(sess, head, hash) {174		if (sess->port_id == port_id) {175			kref_get(&sess->kref);176			rcu_read_unlock();177			TFC_SESS_DBG(lport, "port_id %x found %p\n",178				     port_id, sess);179			return sess;180		}181	}182out:183	rcu_read_unlock();184	TFC_SESS_DBG(lport, "port_id %x not found, %s\n",185		     port_id, reason);186	return NULL;187}188 189static int ft_sess_alloc_cb(struct se_portal_group *se_tpg,190			    struct se_session *se_sess, void *p)191{192	struct ft_sess *sess = p;193	struct ft_tport *tport = sess->tport;194	struct hlist_head *head = &tport->hash[ft_sess_hash(sess->port_id)];195 196	TFC_SESS_DBG(tport->lport, "port_id %x sess %p\n", sess->port_id, sess);197	hlist_add_head_rcu(&sess->hash, head);198	tport->sess_count++;199 200	return 0;201}202 203/*204 * Allocate session and enter it in the hash for the local port.205 * Caller holds ft_lport_lock.206 */207static struct ft_sess *ft_sess_create(struct ft_tport *tport, u32 port_id,208				      struct fc_rport_priv *rdata)209{210	struct se_portal_group *se_tpg = &tport->tpg->se_tpg;211	struct ft_sess *sess;212	struct hlist_head *head;213	unsigned char initiatorname[TRANSPORT_IQN_LEN];214 215	ft_format_wwn(&initiatorname[0], TRANSPORT_IQN_LEN, rdata->ids.port_name);216 217	head = &tport->hash[ft_sess_hash(port_id)];218	hlist_for_each_entry_rcu(sess, head, hash)219		if (sess->port_id == port_id)220			return sess;221 222	sess = kzalloc(sizeof(*sess), GFP_KERNEL);223	if (!sess)224		return ERR_PTR(-ENOMEM);225 226	kref_init(&sess->kref); /* ref for table entry */227	sess->tport = tport;228	sess->port_id = port_id;229 230	sess->se_sess = target_setup_session(se_tpg, TCM_FC_DEFAULT_TAGS,231					     sizeof(struct ft_cmd),232					     TARGET_PROT_NORMAL, &initiatorname[0],233					     sess, ft_sess_alloc_cb);234	if (IS_ERR(sess->se_sess)) {235		int rc = PTR_ERR(sess->se_sess);236		kfree(sess);237		sess = ERR_PTR(rc);238	}239	return sess;240}241 242/*243 * Unhash the session.244 * Caller holds ft_lport_lock.245 */246static void ft_sess_unhash(struct ft_sess *sess)247{248	struct ft_tport *tport = sess->tport;249 250	hlist_del_rcu(&sess->hash);251	BUG_ON(!tport->sess_count);252	tport->sess_count--;253	sess->port_id = -1;254	sess->params = 0;255}256 257/*258 * Delete session from hash.259 * Caller holds ft_lport_lock.260 */261static struct ft_sess *ft_sess_delete(struct ft_tport *tport, u32 port_id)262{263	struct hlist_head *head;264	struct ft_sess *sess;265 266	head = &tport->hash[ft_sess_hash(port_id)];267	hlist_for_each_entry_rcu(sess, head, hash) {268		if (sess->port_id == port_id) {269			ft_sess_unhash(sess);270			return sess;271		}272	}273	return NULL;274}275 276static void ft_close_sess(struct ft_sess *sess)277{278	target_stop_session(sess->se_sess);279	target_wait_for_sess_cmds(sess->se_sess);280	ft_sess_put(sess);281}282 283/*284 * Delete all sessions from tport.285 * Caller holds ft_lport_lock.286 */287static void ft_sess_delete_all(struct ft_tport *tport)288{289	struct hlist_head *head;290	struct ft_sess *sess;291 292	for (head = tport->hash;293	     head < &tport->hash[FT_SESS_HASH_SIZE]; head++) {294		hlist_for_each_entry_rcu(sess, head, hash) {295			ft_sess_unhash(sess);296			ft_close_sess(sess);	/* release from table */297		}298	}299}300 301/*302 * TCM ops for sessions.303 */304 305/*306 * Remove session and send PRLO.307 * This is called when the ACL is being deleted or queue depth is changing.308 */309void ft_sess_close(struct se_session *se_sess)310{311	struct ft_sess *sess = se_sess->fabric_sess_ptr;312	u32 port_id;313 314	mutex_lock(&ft_lport_lock);315	port_id = sess->port_id;316	if (port_id == -1) {317		mutex_unlock(&ft_lport_lock);318		return;319	}320	TFC_SESS_DBG(sess->tport->lport, "port_id %x close session\n", port_id);321	ft_sess_unhash(sess);322	mutex_unlock(&ft_lport_lock);323	ft_close_sess(sess);324	/* XXX Send LOGO or PRLO */325	synchronize_rcu();		/* let transport deregister happen */326}327 328u32 ft_sess_get_index(struct se_session *se_sess)329{330	struct ft_sess *sess = se_sess->fabric_sess_ptr;331 332	return sess->port_id;	/* XXX TBD probably not what is needed */333}334 335u32 ft_sess_get_port_name(struct se_session *se_sess,336			  unsigned char *buf, u32 len)337{338	struct ft_sess *sess = se_sess->fabric_sess_ptr;339 340	return ft_format_wwn(buf, len, sess->port_name);341}342 343/*344 * libfc ops involving sessions.345 */346 347static int ft_prli_locked(struct fc_rport_priv *rdata, u32 spp_len,348			  const struct fc_els_spp *rspp, struct fc_els_spp *spp)349{350	struct ft_tport *tport;351	struct ft_sess *sess;352	u32 fcp_parm;353 354	tport = ft_tport_get(rdata->local_port);355	if (!tport)356		goto not_target;	/* not a target for this local port */357 358	if (!rspp)359		goto fill;360 361	if (rspp->spp_flags & (FC_SPP_OPA_VAL | FC_SPP_RPA_VAL))362		return FC_SPP_RESP_NO_PA;363 364	/*365	 * If both target and initiator bits are off, the SPP is invalid.366	 */367	fcp_parm = ntohl(rspp->spp_params);368	if (!(fcp_parm & (FCP_SPPF_INIT_FCN | FCP_SPPF_TARG_FCN)))369		return FC_SPP_RESP_INVL;370 371	/*372	 * Create session (image pair) only if requested by373	 * EST_IMG_PAIR flag and if the requestor is an initiator.374	 */375	if (rspp->spp_flags & FC_SPP_EST_IMG_PAIR) {376		spp->spp_flags |= FC_SPP_EST_IMG_PAIR;377		if (!(fcp_parm & FCP_SPPF_INIT_FCN))378			return FC_SPP_RESP_CONF;379		sess = ft_sess_create(tport, rdata->ids.port_id, rdata);380		if (IS_ERR(sess)) {381			if (PTR_ERR(sess) == -EACCES) {382				spp->spp_flags &= ~FC_SPP_EST_IMG_PAIR;383				return FC_SPP_RESP_CONF;384			} else385				return FC_SPP_RESP_RES;386		}387		if (!sess->params)388			rdata->prli_count++;389		sess->params = fcp_parm;390		sess->port_name = rdata->ids.port_name;391		sess->max_frame = rdata->maxframe_size;392 393		/* XXX TBD - clearing actions.  unit attn, see 4.10 */394	}395 396	/*397	 * OR in our service parameters with other provider (initiator), if any.398	 */399fill:400	fcp_parm = ntohl(spp->spp_params);401	fcp_parm &= ~FCP_SPPF_RETRY;402	spp->spp_params = htonl(fcp_parm | FCP_SPPF_TARG_FCN);403	return FC_SPP_RESP_ACK;404 405not_target:406	fcp_parm = ntohl(spp->spp_params);407	fcp_parm &= ~FCP_SPPF_TARG_FCN;408	spp->spp_params = htonl(fcp_parm);409	return 0;410}411 412/**413 * ft_prli() - Handle incoming or outgoing PRLI for the FCP target414 * @rdata: remote port private415 * @spp_len: service parameter page length416 * @rspp: received service parameter page (NULL for outgoing PRLI)417 * @spp: response service parameter page418 *419 * Returns spp response code.420 */421static int ft_prli(struct fc_rport_priv *rdata, u32 spp_len,422		   const struct fc_els_spp *rspp, struct fc_els_spp *spp)423{424	int ret;425 426	mutex_lock(&ft_lport_lock);427	ret = ft_prli_locked(rdata, spp_len, rspp, spp);428	mutex_unlock(&ft_lport_lock);429	TFC_SESS_DBG(rdata->local_port, "port_id %x flags %x ret %x\n",430		     rdata->ids.port_id, rspp ? rspp->spp_flags : 0, ret);431	return ret;432}433 434static void ft_sess_free(struct kref *kref)435{436	struct ft_sess *sess = container_of(kref, struct ft_sess, kref);437 438	target_remove_session(sess->se_sess);439	kfree_rcu(sess, rcu);440}441 442void ft_sess_put(struct ft_sess *sess)443{444	int sess_held = kref_read(&sess->kref);445 446	BUG_ON(!sess_held);447	kref_put(&sess->kref, ft_sess_free);448}449 450static void ft_prlo(struct fc_rport_priv *rdata)451{452	struct ft_sess *sess;453	struct ft_tport *tport;454 455	mutex_lock(&ft_lport_lock);456	tport = rcu_dereference_protected(rdata->local_port->prov[FC_TYPE_FCP],457					  lockdep_is_held(&ft_lport_lock));458 459	if (!tport) {460		mutex_unlock(&ft_lport_lock);461		return;462	}463	sess = ft_sess_delete(tport, rdata->ids.port_id);464	if (!sess) {465		mutex_unlock(&ft_lport_lock);466		return;467	}468	mutex_unlock(&ft_lport_lock);469	ft_close_sess(sess);		/* release from table */470	rdata->prli_count--;471	/* XXX TBD - clearing actions.  unit attn, see 4.10 */472}473 474/*475 * Handle incoming FCP request.476 * Caller has verified that the frame is type FCP.477 */478static void ft_recv(struct fc_lport *lport, struct fc_frame *fp)479{480	struct ft_sess *sess;481	u32 sid = fc_frame_sid(fp);482 483	TFC_SESS_DBG(lport, "recv sid %x\n", sid);484 485	sess = ft_sess_get(lport, sid);486	if (!sess) {487		TFC_SESS_DBG(lport, "sid %x sess lookup failed\n", sid);488		/* TBD XXX - if FCP_CMND, send PRLO */489		fc_frame_free(fp);490		return;491	}492	ft_recv_req(sess, fp);	/* must do ft_sess_put() */493}494 495/*496 * Provider ops for libfc.497 */498struct fc4_prov ft_prov = {499	.prli = ft_prli,500	.prlo = ft_prlo,501	.recv = ft_recv,502	.module = THIS_MODULE,503};504