xref: /openbmc/linux/drivers/target/tcm_fc/tfc_sess.c (revision 7a3beeae)
1a61127c2SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only
23699d92aSKiran Patil /*
33699d92aSKiran Patil  * Copyright (c) 2010 Cisco Systems, Inc.
43699d92aSKiran Patil  */
53699d92aSKiran Patil 
63699d92aSKiran Patil /* XXX TBD some includes may be extraneous */
73699d92aSKiran Patil 
83699d92aSKiran Patil #include <linux/module.h>
93699d92aSKiran Patil #include <linux/moduleparam.h>
103699d92aSKiran Patil #include <linux/utsname.h>
113699d92aSKiran Patil #include <linux/init.h>
123699d92aSKiran Patil #include <linux/slab.h>
133699d92aSKiran Patil #include <linux/kthread.h>
143699d92aSKiran Patil #include <linux/types.h>
153699d92aSKiran Patil #include <linux/string.h>
163699d92aSKiran Patil #include <linux/configfs.h>
173699d92aSKiran Patil #include <linux/ctype.h>
183699d92aSKiran Patil #include <linux/hash.h>
193699d92aSKiran Patil #include <linux/rcupdate.h>
203699d92aSKiran Patil #include <linux/rculist.h>
213699d92aSKiran Patil #include <linux/kref.h>
223699d92aSKiran Patil #include <asm/unaligned.h>
233699d92aSKiran Patil #include <scsi/libfc.h>
243699d92aSKiran Patil 
253699d92aSKiran Patil #include <target/target_core_base.h>
26c4795fb2SChristoph Hellwig #include <target/target_core_fabric.h>
273699d92aSKiran Patil 
283699d92aSKiran Patil #include "tcm_fc.h"
293699d92aSKiran Patil 
308962a4d2SHannes Reinecke #define TFC_SESS_DBG(lport, fmt, args...) \
318962a4d2SHannes Reinecke 	pr_debug("host%u: rport %6.6x: " fmt,	   \
328962a4d2SHannes Reinecke 		 (lport)->host->host_no,	   \
338962a4d2SHannes Reinecke 		 (lport)->port_id, ##args )
348962a4d2SHannes Reinecke 
353699d92aSKiran Patil static void ft_sess_delete_all(struct ft_tport *);
363699d92aSKiran Patil 
373699d92aSKiran Patil /*
383699d92aSKiran Patil  * Lookup or allocate target local port.
393699d92aSKiran Patil  * Caller holds ft_lport_lock.
403699d92aSKiran Patil  */
ft_tport_get(struct fc_lport * lport)41e3d4440cSAndy Grover static struct ft_tport *ft_tport_get(struct fc_lport *lport)
423699d92aSKiran Patil {
433699d92aSKiran Patil 	struct ft_tpg *tpg;
443699d92aSKiran Patil 	struct ft_tport *tport;
453699d92aSKiran Patil 	int i;
463699d92aSKiran Patil 
47863555beSMark Rustad 	tport = rcu_dereference_protected(lport->prov[FC_TYPE_FCP],
48863555beSMark Rustad 					  lockdep_is_held(&ft_lport_lock));
493699d92aSKiran Patil 	if (tport && tport->tpg)
503699d92aSKiran Patil 		return tport;
513699d92aSKiran Patil 
523699d92aSKiran Patil 	tpg = ft_lport_find_tpg(lport);
533699d92aSKiran Patil 	if (!tpg)
543699d92aSKiran Patil 		return NULL;
553699d92aSKiran Patil 
563699d92aSKiran Patil 	if (tport) {
573699d92aSKiran Patil 		tport->tpg = tpg;
582c42be2dSAndy Grover 		tpg->tport = tport;
593699d92aSKiran Patil 		return tport;
603699d92aSKiran Patil 	}
613699d92aSKiran Patil 
623699d92aSKiran Patil 	tport = kzalloc(sizeof(*tport), GFP_KERNEL);
633699d92aSKiran Patil 	if (!tport)
643699d92aSKiran Patil 		return NULL;
653699d92aSKiran Patil 
663699d92aSKiran Patil 	tport->lport = lport;
673699d92aSKiran Patil 	tport->tpg = tpg;
683699d92aSKiran Patil 	tpg->tport = tport;
693699d92aSKiran Patil 	for (i = 0; i < FT_SESS_HASH_SIZE; i++)
703699d92aSKiran Patil 		INIT_HLIST_HEAD(&tport->hash[i]);
713699d92aSKiran Patil 
723699d92aSKiran Patil 	rcu_assign_pointer(lport->prov[FC_TYPE_FCP], tport);
733699d92aSKiran Patil 	return tport;
743699d92aSKiran Patil }
753699d92aSKiran Patil 
763699d92aSKiran Patil /*
773699d92aSKiran Patil  * Delete a target local port.
783699d92aSKiran Patil  * Caller holds ft_lport_lock.
793699d92aSKiran Patil  */
ft_tport_delete(struct ft_tport * tport)803699d92aSKiran Patil static void ft_tport_delete(struct ft_tport *tport)
813699d92aSKiran Patil {
823699d92aSKiran Patil 	struct fc_lport *lport;
833699d92aSKiran Patil 	struct ft_tpg *tpg;
843699d92aSKiran Patil 
853699d92aSKiran Patil 	ft_sess_delete_all(tport);
863699d92aSKiran Patil 	lport = tport->lport;
87107818e2SHannes Reinecke 	lport->service_params &= ~FCP_SPPF_TARG_FCN;
883699d92aSKiran Patil 	BUG_ON(tport != lport->prov[FC_TYPE_FCP]);
89c04047ecSAndreea-Cristina Bernat 	RCU_INIT_POINTER(lport->prov[FC_TYPE_FCP], NULL);
903699d92aSKiran Patil 
913699d92aSKiran Patil 	tpg = tport->tpg;
923699d92aSKiran Patil 	if (tpg) {
933699d92aSKiran Patil 		tpg->tport = NULL;
943699d92aSKiran Patil 		tport->tpg = NULL;
953699d92aSKiran Patil 	}
96a6c76da8SPaul E. McKenney 	kfree_rcu(tport, rcu);
973699d92aSKiran Patil }
983699d92aSKiran Patil 
993699d92aSKiran Patil /*
1003699d92aSKiran Patil  * Add local port.
1013699d92aSKiran Patil  * Called thru fc_lport_iterate().
1023699d92aSKiran Patil  */
ft_lport_add(struct fc_lport * lport,void * arg)1033699d92aSKiran Patil void ft_lport_add(struct fc_lport *lport, void *arg)
1043699d92aSKiran Patil {
1053699d92aSKiran Patil 	mutex_lock(&ft_lport_lock);
106e3d4440cSAndy Grover 	ft_tport_get(lport);
107107818e2SHannes Reinecke 	lport->service_params |= FCP_SPPF_TARG_FCN;
1083699d92aSKiran Patil 	mutex_unlock(&ft_lport_lock);
1093699d92aSKiran Patil }
1103699d92aSKiran Patil 
1113699d92aSKiran Patil /*
1123699d92aSKiran Patil  * Delete local port.
1133699d92aSKiran Patil  * Called thru fc_lport_iterate().
1143699d92aSKiran Patil  */
ft_lport_del(struct fc_lport * lport,void * arg)1153699d92aSKiran Patil void ft_lport_del(struct fc_lport *lport, void *arg)
1163699d92aSKiran Patil {
1173699d92aSKiran Patil 	struct ft_tport *tport;
1183699d92aSKiran Patil 
1193699d92aSKiran Patil 	mutex_lock(&ft_lport_lock);
1203699d92aSKiran Patil 	tport = lport->prov[FC_TYPE_FCP];
1213699d92aSKiran Patil 	if (tport)
1223699d92aSKiran Patil 		ft_tport_delete(tport);
1233699d92aSKiran Patil 	mutex_unlock(&ft_lport_lock);
1243699d92aSKiran Patil }
1253699d92aSKiran Patil 
1263699d92aSKiran Patil /*
1273699d92aSKiran Patil  * Notification of local port change from libfc.
1283699d92aSKiran Patil  * Create or delete local port and associated tport.
1293699d92aSKiran Patil  */
ft_lport_notify(struct notifier_block * nb,unsigned long event,void * arg)1303699d92aSKiran Patil int ft_lport_notify(struct notifier_block *nb, unsigned long event, void *arg)
1313699d92aSKiran Patil {
1323699d92aSKiran Patil 	struct fc_lport *lport = arg;
1333699d92aSKiran Patil 
1343699d92aSKiran Patil 	switch (event) {
1353699d92aSKiran Patil 	case FC_LPORT_EV_ADD:
1363699d92aSKiran Patil 		ft_lport_add(lport, NULL);
1373699d92aSKiran Patil 		break;
1383699d92aSKiran Patil 	case FC_LPORT_EV_DEL:
1393699d92aSKiran Patil 		ft_lport_del(lport, NULL);
1403699d92aSKiran Patil 		break;
1413699d92aSKiran Patil 	}
1423699d92aSKiran Patil 	return NOTIFY_DONE;
1433699d92aSKiran Patil }
1443699d92aSKiran Patil 
1453699d92aSKiran Patil /*
1463699d92aSKiran Patil  * Hash function for FC_IDs.
1473699d92aSKiran Patil  */
ft_sess_hash(u32 port_id)1483699d92aSKiran Patil static u32 ft_sess_hash(u32 port_id)
1493699d92aSKiran Patil {
1503699d92aSKiran Patil 	return hash_32(port_id, FT_SESS_HASH_BITS);
1513699d92aSKiran Patil }
1523699d92aSKiran Patil 
1533699d92aSKiran Patil /*
1543699d92aSKiran Patil  * Find session in local port.
1553699d92aSKiran Patil  * Sessions and hash lists are RCU-protected.
1563699d92aSKiran Patil  * A reference is taken which must be eventually freed.
1573699d92aSKiran Patil  */
ft_sess_get(struct fc_lport * lport,u32 port_id)1583699d92aSKiran Patil static struct ft_sess *ft_sess_get(struct fc_lport *lport, u32 port_id)
1593699d92aSKiran Patil {
1603699d92aSKiran Patil 	struct ft_tport *tport;
1613699d92aSKiran Patil 	struct hlist_head *head;
1623699d92aSKiran Patil 	struct ft_sess *sess;
1638962a4d2SHannes Reinecke 	char *reason = "no session created";
1643699d92aSKiran Patil 
1653699d92aSKiran Patil 	rcu_read_lock();
1663699d92aSKiran Patil 	tport = rcu_dereference(lport->prov[FC_TYPE_FCP]);
1678962a4d2SHannes Reinecke 	if (!tport) {
1688962a4d2SHannes Reinecke 		reason = "not an FCP port";
1693699d92aSKiran Patil 		goto out;
1708962a4d2SHannes Reinecke 	}
1713699d92aSKiran Patil 
1723699d92aSKiran Patil 	head = &tport->hash[ft_sess_hash(port_id)];
173b67bfe0dSSasha Levin 	hlist_for_each_entry_rcu(sess, head, hash) {
1743699d92aSKiran Patil 		if (sess->port_id == port_id) {
1753699d92aSKiran Patil 			kref_get(&sess->kref);
1763699d92aSKiran Patil 			rcu_read_unlock();
1778962a4d2SHannes Reinecke 			TFC_SESS_DBG(lport, "port_id %x found %p\n",
1788962a4d2SHannes Reinecke 				     port_id, sess);
1793699d92aSKiran Patil 			return sess;
1803699d92aSKiran Patil 		}
1813699d92aSKiran Patil 	}
1823699d92aSKiran Patil out:
1833699d92aSKiran Patil 	rcu_read_unlock();
1848962a4d2SHannes Reinecke 	TFC_SESS_DBG(lport, "port_id %x not found, %s\n",
1858962a4d2SHannes Reinecke 		     port_id, reason);
1863699d92aSKiran Patil 	return NULL;
1873699d92aSKiran Patil }
1883699d92aSKiran Patil 
ft_sess_alloc_cb(struct se_portal_group * se_tpg,struct se_session * se_sess,void * p)1899ed59655SNicholas Bellinger static int ft_sess_alloc_cb(struct se_portal_group *se_tpg,
1909ed59655SNicholas Bellinger 			    struct se_session *se_sess, void *p)
1919ed59655SNicholas Bellinger {
1929ed59655SNicholas Bellinger 	struct ft_sess *sess = p;
1939ed59655SNicholas Bellinger 	struct ft_tport *tport = sess->tport;
1949ed59655SNicholas Bellinger 	struct hlist_head *head = &tport->hash[ft_sess_hash(sess->port_id)];
1959ed59655SNicholas Bellinger 
1968962a4d2SHannes Reinecke 	TFC_SESS_DBG(tport->lport, "port_id %x sess %p\n", sess->port_id, sess);
1979ed59655SNicholas Bellinger 	hlist_add_head_rcu(&sess->hash, head);
1989ed59655SNicholas Bellinger 	tport->sess_count++;
1999ed59655SNicholas Bellinger 
2009ed59655SNicholas Bellinger 	return 0;
2019ed59655SNicholas Bellinger }
2029ed59655SNicholas Bellinger 
2033699d92aSKiran Patil /*
2043699d92aSKiran Patil  * Allocate session and enter it in the hash for the local port.
2053699d92aSKiran Patil  * Caller holds ft_lport_lock.
2063699d92aSKiran Patil  */
ft_sess_create(struct ft_tport * tport,u32 port_id,struct fc_rport_priv * rdata)2073699d92aSKiran Patil static struct ft_sess *ft_sess_create(struct ft_tport *tport, u32 port_id,
2087fbef3d0SNicholas Bellinger 				      struct fc_rport_priv *rdata)
2093699d92aSKiran Patil {
2107fbef3d0SNicholas Bellinger 	struct se_portal_group *se_tpg = &tport->tpg->se_tpg;
2113699d92aSKiran Patil 	struct ft_sess *sess;
2123699d92aSKiran Patil 	struct hlist_head *head;
2137fbef3d0SNicholas Bellinger 	unsigned char initiatorname[TRANSPORT_IQN_LEN];
2147fbef3d0SNicholas Bellinger 
2157fbef3d0SNicholas Bellinger 	ft_format_wwn(&initiatorname[0], TRANSPORT_IQN_LEN, rdata->ids.port_name);
2163699d92aSKiran Patil 
2173699d92aSKiran Patil 	head = &tport->hash[ft_sess_hash(port_id)];
218b67bfe0dSSasha Levin 	hlist_for_each_entry_rcu(sess, head, hash)
2193699d92aSKiran Patil 		if (sess->port_id == port_id)
2203699d92aSKiran Patil 			return sess;
2213699d92aSKiran Patil 
2223699d92aSKiran Patil 	sess = kzalloc(sizeof(*sess), GFP_KERNEL);
2233699d92aSKiran Patil 	if (!sess)
22491b385b4SHannes Reinecke 		return ERR_PTR(-ENOMEM);
2253699d92aSKiran Patil 
2269ed59655SNicholas Bellinger 	kref_init(&sess->kref); /* ref for table entry */
2279ed59655SNicholas Bellinger 	sess->tport = tport;
2289ed59655SNicholas Bellinger 	sess->port_id = port_id;
2299ed59655SNicholas Bellinger 
230fa834287SMike Christie 	sess->se_sess = target_setup_session(se_tpg, TCM_FC_DEFAULT_TAGS,
231e70beee7SNicholas Bellinger 					     sizeof(struct ft_cmd),
2329ed59655SNicholas Bellinger 					     TARGET_PROT_NORMAL, &initiatorname[0],
2339ed59655SNicholas Bellinger 					     sess, ft_sess_alloc_cb);
234552523dcSDan Carpenter 	if (IS_ERR(sess->se_sess)) {
23591b385b4SHannes Reinecke 		int rc = PTR_ERR(sess->se_sess);
2363699d92aSKiran Patil 		kfree(sess);
23791b385b4SHannes Reinecke 		sess = ERR_PTR(rc);
2383699d92aSKiran Patil 	}
2393699d92aSKiran Patil 	return sess;
2403699d92aSKiran Patil }
2413699d92aSKiran Patil 
2423699d92aSKiran Patil /*
2433699d92aSKiran Patil  * Unhash the session.
2443699d92aSKiran Patil  * Caller holds ft_lport_lock.
2453699d92aSKiran Patil  */
ft_sess_unhash(struct ft_sess * sess)2463699d92aSKiran Patil static void ft_sess_unhash(struct ft_sess *sess)
2473699d92aSKiran Patil {
2483699d92aSKiran Patil 	struct ft_tport *tport = sess->tport;
2493699d92aSKiran Patil 
2503699d92aSKiran Patil 	hlist_del_rcu(&sess->hash);
2513699d92aSKiran Patil 	BUG_ON(!tport->sess_count);
2523699d92aSKiran Patil 	tport->sess_count--;
2533699d92aSKiran Patil 	sess->port_id = -1;
2543699d92aSKiran Patil 	sess->params = 0;
2553699d92aSKiran Patil }
2563699d92aSKiran Patil 
2573699d92aSKiran Patil /*
2583699d92aSKiran Patil  * Delete session from hash.
2593699d92aSKiran Patil  * Caller holds ft_lport_lock.
2603699d92aSKiran Patil  */
ft_sess_delete(struct ft_tport * tport,u32 port_id)2613699d92aSKiran Patil static struct ft_sess *ft_sess_delete(struct ft_tport *tport, u32 port_id)
2623699d92aSKiran Patil {
2633699d92aSKiran Patil 	struct hlist_head *head;
2643699d92aSKiran Patil 	struct ft_sess *sess;
2653699d92aSKiran Patil 
2663699d92aSKiran Patil 	head = &tport->hash[ft_sess_hash(port_id)];
267b67bfe0dSSasha Levin 	hlist_for_each_entry_rcu(sess, head, hash) {
2683699d92aSKiran Patil 		if (sess->port_id == port_id) {
2693699d92aSKiran Patil 			ft_sess_unhash(sess);
2703699d92aSKiran Patil 			return sess;
2713699d92aSKiran Patil 		}
2723699d92aSKiran Patil 	}
2733699d92aSKiran Patil 	return NULL;
2743699d92aSKiran Patil }
2753699d92aSKiran Patil 
ft_close_sess(struct ft_sess * sess)276de7ee9a2SBart Van Assche static void ft_close_sess(struct ft_sess *sess)
277de7ee9a2SBart Van Assche {
2786f55b06fSMike Christie 	target_stop_session(sess->se_sess);
279de7ee9a2SBart Van Assche 	target_wait_for_sess_cmds(sess->se_sess);
280de7ee9a2SBart Van Assche 	ft_sess_put(sess);
281de7ee9a2SBart Van Assche }
282de7ee9a2SBart Van Assche 
2833699d92aSKiran Patil /*
2843699d92aSKiran Patil  * Delete all sessions from tport.
2853699d92aSKiran Patil  * Caller holds ft_lport_lock.
2863699d92aSKiran Patil  */
ft_sess_delete_all(struct ft_tport * tport)2873699d92aSKiran Patil static void ft_sess_delete_all(struct ft_tport *tport)
2883699d92aSKiran Patil {
2893699d92aSKiran Patil 	struct hlist_head *head;
2903699d92aSKiran Patil 	struct ft_sess *sess;
2913699d92aSKiran Patil 
2923699d92aSKiran Patil 	for (head = tport->hash;
2933699d92aSKiran Patil 	     head < &tport->hash[FT_SESS_HASH_SIZE]; head++) {
294b67bfe0dSSasha Levin 		hlist_for_each_entry_rcu(sess, head, hash) {
2953699d92aSKiran Patil 			ft_sess_unhash(sess);
296de7ee9a2SBart Van Assche 			ft_close_sess(sess);	/* release from table */
2973699d92aSKiran Patil 		}
2983699d92aSKiran Patil 	}
2993699d92aSKiran Patil }
3003699d92aSKiran Patil 
3013699d92aSKiran Patil /*
3023699d92aSKiran Patil  * TCM ops for sessions.
3033699d92aSKiran Patil  */
3043699d92aSKiran Patil 
3053699d92aSKiran Patil /*
3063699d92aSKiran Patil  * Remove session and send PRLO.
3073699d92aSKiran Patil  * This is called when the ACL is being deleted or queue depth is changing.
3083699d92aSKiran Patil  */
ft_sess_close(struct se_session * se_sess)3093699d92aSKiran Patil void ft_sess_close(struct se_session *se_sess)
3103699d92aSKiran Patil {
3113699d92aSKiran Patil 	struct ft_sess *sess = se_sess->fabric_sess_ptr;
3123699d92aSKiran Patil 	u32 port_id;
3133699d92aSKiran Patil 
3143699d92aSKiran Patil 	mutex_lock(&ft_lport_lock);
3153699d92aSKiran Patil 	port_id = sess->port_id;
3163699d92aSKiran Patil 	if (port_id == -1) {
3177c7cf3b9SDan Carpenter 		mutex_unlock(&ft_lport_lock);
3183699d92aSKiran Patil 		return;
3193699d92aSKiran Patil 	}
3208962a4d2SHannes Reinecke 	TFC_SESS_DBG(sess->tport->lport, "port_id %x close session\n", port_id);
3213699d92aSKiran Patil 	ft_sess_unhash(sess);
3223699d92aSKiran Patil 	mutex_unlock(&ft_lport_lock);
323de7ee9a2SBart Van Assche 	ft_close_sess(sess);
3243699d92aSKiran Patil 	/* XXX Send LOGO or PRLO */
3253699d92aSKiran Patil 	synchronize_rcu();		/* let transport deregister happen */
3263699d92aSKiran Patil }
3273699d92aSKiran Patil 
ft_sess_get_index(struct se_session * se_sess)3283699d92aSKiran Patil u32 ft_sess_get_index(struct se_session *se_sess)
3293699d92aSKiran Patil {
3303699d92aSKiran Patil 	struct ft_sess *sess = se_sess->fabric_sess_ptr;
3313699d92aSKiran Patil 
3323699d92aSKiran Patil 	return sess->port_id;	/* XXX TBD probably not what is needed */
3333699d92aSKiran Patil }
3343699d92aSKiran Patil 
ft_sess_get_port_name(struct se_session * se_sess,unsigned char * buf,u32 len)3353699d92aSKiran Patil u32 ft_sess_get_port_name(struct se_session *se_sess,
3363699d92aSKiran Patil 			  unsigned char *buf, u32 len)
3373699d92aSKiran Patil {
3383699d92aSKiran Patil 	struct ft_sess *sess = se_sess->fabric_sess_ptr;
3393699d92aSKiran Patil 
3403699d92aSKiran Patil 	return ft_format_wwn(buf, len, sess->port_name);
3413699d92aSKiran Patil }
3423699d92aSKiran Patil 
3433699d92aSKiran Patil /*
3443699d92aSKiran Patil  * libfc ops involving sessions.
3453699d92aSKiran Patil  */
3463699d92aSKiran Patil 
ft_prli_locked(struct fc_rport_priv * rdata,u32 spp_len,const struct fc_els_spp * rspp,struct fc_els_spp * spp)3473699d92aSKiran Patil static int ft_prli_locked(struct fc_rport_priv *rdata, u32 spp_len,
3483699d92aSKiran Patil 			  const struct fc_els_spp *rspp, struct fc_els_spp *spp)
3493699d92aSKiran Patil {
3503699d92aSKiran Patil 	struct ft_tport *tport;
3513699d92aSKiran Patil 	struct ft_sess *sess;
3523699d92aSKiran Patil 	u32 fcp_parm;
3533699d92aSKiran Patil 
354e3d4440cSAndy Grover 	tport = ft_tport_get(rdata->local_port);
3553699d92aSKiran Patil 	if (!tport)
356edec8dfeSMark Rustad 		goto not_target;	/* not a target for this local port */
3573699d92aSKiran Patil 
3583699d92aSKiran Patil 	if (!rspp)
3593699d92aSKiran Patil 		goto fill;
3603699d92aSKiran Patil 
3613699d92aSKiran Patil 	if (rspp->spp_flags & (FC_SPP_OPA_VAL | FC_SPP_RPA_VAL))
3623699d92aSKiran Patil 		return FC_SPP_RESP_NO_PA;
3633699d92aSKiran Patil 
3643699d92aSKiran Patil 	/*
3653699d92aSKiran Patil 	 * If both target and initiator bits are off, the SPP is invalid.
3663699d92aSKiran Patil 	 */
3673699d92aSKiran Patil 	fcp_parm = ntohl(rspp->spp_params);
3683699d92aSKiran Patil 	if (!(fcp_parm & (FCP_SPPF_INIT_FCN | FCP_SPPF_TARG_FCN)))
3693699d92aSKiran Patil 		return FC_SPP_RESP_INVL;
3703699d92aSKiran Patil 
3713699d92aSKiran Patil 	/*
3723699d92aSKiran Patil 	 * Create session (image pair) only if requested by
3733699d92aSKiran Patil 	 * EST_IMG_PAIR flag and if the requestor is an initiator.
3743699d92aSKiran Patil 	 */
3753699d92aSKiran Patil 	if (rspp->spp_flags & FC_SPP_EST_IMG_PAIR) {
3763699d92aSKiran Patil 		spp->spp_flags |= FC_SPP_EST_IMG_PAIR;
3773699d92aSKiran Patil 		if (!(fcp_parm & FCP_SPPF_INIT_FCN))
3783699d92aSKiran Patil 			return FC_SPP_RESP_CONF;
3797fbef3d0SNicholas Bellinger 		sess = ft_sess_create(tport, rdata->ids.port_id, rdata);
3808962a4d2SHannes Reinecke 		if (IS_ERR(sess)) {
3818962a4d2SHannes Reinecke 			if (PTR_ERR(sess) == -EACCES) {
3828962a4d2SHannes Reinecke 				spp->spp_flags &= ~FC_SPP_EST_IMG_PAIR;
3838962a4d2SHannes Reinecke 				return FC_SPP_RESP_CONF;
3848962a4d2SHannes Reinecke 			} else
3853699d92aSKiran Patil 				return FC_SPP_RESP_RES;
3868962a4d2SHannes Reinecke 		}
3873699d92aSKiran Patil 		if (!sess->params)
3883699d92aSKiran Patil 			rdata->prli_count++;
3893699d92aSKiran Patil 		sess->params = fcp_parm;
3903699d92aSKiran Patil 		sess->port_name = rdata->ids.port_name;
3913699d92aSKiran Patil 		sess->max_frame = rdata->maxframe_size;
3923699d92aSKiran Patil 
3933699d92aSKiran Patil 		/* XXX TBD - clearing actions.  unit attn, see 4.10 */
3943699d92aSKiran Patil 	}
3953699d92aSKiran Patil 
3963699d92aSKiran Patil 	/*
3973699d92aSKiran Patil 	 * OR in our service parameters with other provider (initiator), if any.
3983699d92aSKiran Patil 	 */
3993699d92aSKiran Patil fill:
4003699d92aSKiran Patil 	fcp_parm = ntohl(spp->spp_params);
401f2eeba21SMark Rustad 	fcp_parm &= ~FCP_SPPF_RETRY;
4023699d92aSKiran Patil 	spp->spp_params = htonl(fcp_parm | FCP_SPPF_TARG_FCN);
4033699d92aSKiran Patil 	return FC_SPP_RESP_ACK;
404edec8dfeSMark Rustad 
405edec8dfeSMark Rustad not_target:
406edec8dfeSMark Rustad 	fcp_parm = ntohl(spp->spp_params);
407edec8dfeSMark Rustad 	fcp_parm &= ~FCP_SPPF_TARG_FCN;
408edec8dfeSMark Rustad 	spp->spp_params = htonl(fcp_parm);
409edec8dfeSMark Rustad 	return 0;
4103699d92aSKiran Patil }
4113699d92aSKiran Patil 
4123699d92aSKiran Patil /**
413*7a3beeaeSBart Van Assche  * ft_prli() - Handle incoming or outgoing PRLI for the FCP target
4143699d92aSKiran Patil  * @rdata: remote port private
4153699d92aSKiran Patil  * @spp_len: service parameter page length
4163699d92aSKiran Patil  * @rspp: received service parameter page (NULL for outgoing PRLI)
4173699d92aSKiran Patil  * @spp: response service parameter page
4183699d92aSKiran Patil  *
4193699d92aSKiran Patil  * Returns spp response code.
4203699d92aSKiran Patil  */
ft_prli(struct fc_rport_priv * rdata,u32 spp_len,const struct fc_els_spp * rspp,struct fc_els_spp * spp)4213699d92aSKiran Patil static int ft_prli(struct fc_rport_priv *rdata, u32 spp_len,
4223699d92aSKiran Patil 		   const struct fc_els_spp *rspp, struct fc_els_spp *spp)
4233699d92aSKiran Patil {
4243699d92aSKiran Patil 	int ret;
4253699d92aSKiran Patil 
4263699d92aSKiran Patil 	mutex_lock(&ft_lport_lock);
4273699d92aSKiran Patil 	ret = ft_prli_locked(rdata, spp_len, rspp, spp);
4283699d92aSKiran Patil 	mutex_unlock(&ft_lport_lock);
4298962a4d2SHannes Reinecke 	TFC_SESS_DBG(rdata->local_port, "port_id %x flags %x ret %x\n",
4303699d92aSKiran Patil 		     rdata->ids.port_id, rspp ? rspp->spp_flags : 0, ret);
4313699d92aSKiran Patil 	return ret;
4323699d92aSKiran Patil }
4333699d92aSKiran Patil 
ft_sess_free(struct kref * kref)4343699d92aSKiran Patil static void ft_sess_free(struct kref *kref)
4353699d92aSKiran Patil {
4363699d92aSKiran Patil 	struct ft_sess *sess = container_of(kref, struct ft_sess, kref);
4373699d92aSKiran Patil 
43860daca10SMike Christie 	target_remove_session(sess->se_sess);
439a6ad57efSWei Yongjun 	kfree_rcu(sess, rcu);
4403699d92aSKiran Patil }
4413699d92aSKiran Patil 
ft_sess_put(struct ft_sess * sess)4423699d92aSKiran Patil void ft_sess_put(struct ft_sess *sess)
4433699d92aSKiran Patil {
4442c935bc5SPeter Zijlstra 	int sess_held = kref_read(&sess->kref);
4453699d92aSKiran Patil 
4463699d92aSKiran Patil 	BUG_ON(!sess_held);
4473699d92aSKiran Patil 	kref_put(&sess->kref, ft_sess_free);
4483699d92aSKiran Patil }
4493699d92aSKiran Patil 
ft_prlo(struct fc_rport_priv * rdata)4503699d92aSKiran Patil static void ft_prlo(struct fc_rport_priv *rdata)
4513699d92aSKiran Patil {
4523699d92aSKiran Patil 	struct ft_sess *sess;
4533699d92aSKiran Patil 	struct ft_tport *tport;
4543699d92aSKiran Patil 
4553699d92aSKiran Patil 	mutex_lock(&ft_lport_lock);
45608a16208SDenis Efremov 	tport = rcu_dereference_protected(rdata->local_port->prov[FC_TYPE_FCP],
45708a16208SDenis Efremov 					  lockdep_is_held(&ft_lport_lock));
45808a16208SDenis Efremov 
4593699d92aSKiran Patil 	if (!tport) {
4603699d92aSKiran Patil 		mutex_unlock(&ft_lport_lock);
4613699d92aSKiran Patil 		return;
4623699d92aSKiran Patil 	}
4633699d92aSKiran Patil 	sess = ft_sess_delete(tport, rdata->ids.port_id);
4643699d92aSKiran Patil 	if (!sess) {
4653699d92aSKiran Patil 		mutex_unlock(&ft_lport_lock);
4663699d92aSKiran Patil 		return;
4673699d92aSKiran Patil 	}
4683699d92aSKiran Patil 	mutex_unlock(&ft_lport_lock);
469de7ee9a2SBart Van Assche 	ft_close_sess(sess);		/* release from table */
4703699d92aSKiran Patil 	rdata->prli_count--;
4713699d92aSKiran Patil 	/* XXX TBD - clearing actions.  unit attn, see 4.10 */
4723699d92aSKiran Patil }
4733699d92aSKiran Patil 
4743699d92aSKiran Patil /*
4753699d92aSKiran Patil  * Handle incoming FCP request.
4763699d92aSKiran Patil  * Caller has verified that the frame is type FCP.
4773699d92aSKiran Patil  */
ft_recv(struct fc_lport * lport,struct fc_frame * fp)4783699d92aSKiran Patil static void ft_recv(struct fc_lport *lport, struct fc_frame *fp)
4793699d92aSKiran Patil {
4803699d92aSKiran Patil 	struct ft_sess *sess;
4813699d92aSKiran Patil 	u32 sid = fc_frame_sid(fp);
4823699d92aSKiran Patil 
4838962a4d2SHannes Reinecke 	TFC_SESS_DBG(lport, "recv sid %x\n", sid);
4843699d92aSKiran Patil 
4853699d92aSKiran Patil 	sess = ft_sess_get(lport, sid);
4863699d92aSKiran Patil 	if (!sess) {
4878962a4d2SHannes Reinecke 		TFC_SESS_DBG(lport, "sid %x sess lookup failed\n", sid);
4883699d92aSKiran Patil 		/* TBD XXX - if FCP_CMND, send PRLO */
4893699d92aSKiran Patil 		fc_frame_free(fp);
4903699d92aSKiran Patil 		return;
4913699d92aSKiran Patil 	}
4923699d92aSKiran Patil 	ft_recv_req(sess, fp);	/* must do ft_sess_put() */
4933699d92aSKiran Patil }
4943699d92aSKiran Patil 
4953699d92aSKiran Patil /*
4963699d92aSKiran Patil  * Provider ops for libfc.
4973699d92aSKiran Patil  */
4983699d92aSKiran Patil struct fc4_prov ft_prov = {
4993699d92aSKiran Patil 	.prli = ft_prli,
5003699d92aSKiran Patil 	.prlo = ft_prlo,
5013699d92aSKiran Patil 	.recv = ft_recv,
5023699d92aSKiran Patil 	.module = THIS_MODULE,
5033699d92aSKiran Patil };
504