162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * Copyright (c) 2010 Cisco Systems, Inc.
462306a36Sopenharmony_ci */
562306a36Sopenharmony_ci
662306a36Sopenharmony_ci/* XXX TBD some includes may be extraneous */
762306a36Sopenharmony_ci
862306a36Sopenharmony_ci#include <linux/module.h>
962306a36Sopenharmony_ci#include <linux/moduleparam.h>
1062306a36Sopenharmony_ci#include <linux/utsname.h>
1162306a36Sopenharmony_ci#include <linux/init.h>
1262306a36Sopenharmony_ci#include <linux/slab.h>
1362306a36Sopenharmony_ci#include <linux/kthread.h>
1462306a36Sopenharmony_ci#include <linux/types.h>
1562306a36Sopenharmony_ci#include <linux/string.h>
1662306a36Sopenharmony_ci#include <linux/configfs.h>
1762306a36Sopenharmony_ci#include <linux/ctype.h>
1862306a36Sopenharmony_ci#include <linux/hash.h>
1962306a36Sopenharmony_ci#include <linux/rcupdate.h>
2062306a36Sopenharmony_ci#include <linux/rculist.h>
2162306a36Sopenharmony_ci#include <linux/kref.h>
2262306a36Sopenharmony_ci#include <asm/unaligned.h>
2362306a36Sopenharmony_ci#include <scsi/libfc.h>
2462306a36Sopenharmony_ci
2562306a36Sopenharmony_ci#include <target/target_core_base.h>
2662306a36Sopenharmony_ci#include <target/target_core_fabric.h>
2762306a36Sopenharmony_ci
2862306a36Sopenharmony_ci#include "tcm_fc.h"
2962306a36Sopenharmony_ci
3062306a36Sopenharmony_ci#define TFC_SESS_DBG(lport, fmt, args...) \
3162306a36Sopenharmony_ci	pr_debug("host%u: rport %6.6x: " fmt,	   \
3262306a36Sopenharmony_ci		 (lport)->host->host_no,	   \
3362306a36Sopenharmony_ci		 (lport)->port_id, ##args )
3462306a36Sopenharmony_ci
3562306a36Sopenharmony_cistatic void ft_sess_delete_all(struct ft_tport *);
3662306a36Sopenharmony_ci
3762306a36Sopenharmony_ci/*
3862306a36Sopenharmony_ci * Lookup or allocate target local port.
3962306a36Sopenharmony_ci * Caller holds ft_lport_lock.
4062306a36Sopenharmony_ci */
4162306a36Sopenharmony_cistatic struct ft_tport *ft_tport_get(struct fc_lport *lport)
4262306a36Sopenharmony_ci{
4362306a36Sopenharmony_ci	struct ft_tpg *tpg;
4462306a36Sopenharmony_ci	struct ft_tport *tport;
4562306a36Sopenharmony_ci	int i;
4662306a36Sopenharmony_ci
4762306a36Sopenharmony_ci	tport = rcu_dereference_protected(lport->prov[FC_TYPE_FCP],
4862306a36Sopenharmony_ci					  lockdep_is_held(&ft_lport_lock));
4962306a36Sopenharmony_ci	if (tport && tport->tpg)
5062306a36Sopenharmony_ci		return tport;
5162306a36Sopenharmony_ci
5262306a36Sopenharmony_ci	tpg = ft_lport_find_tpg(lport);
5362306a36Sopenharmony_ci	if (!tpg)
5462306a36Sopenharmony_ci		return NULL;
5562306a36Sopenharmony_ci
5662306a36Sopenharmony_ci	if (tport) {
5762306a36Sopenharmony_ci		tport->tpg = tpg;
5862306a36Sopenharmony_ci		tpg->tport = tport;
5962306a36Sopenharmony_ci		return tport;
6062306a36Sopenharmony_ci	}
6162306a36Sopenharmony_ci
6262306a36Sopenharmony_ci	tport = kzalloc(sizeof(*tport), GFP_KERNEL);
6362306a36Sopenharmony_ci	if (!tport)
6462306a36Sopenharmony_ci		return NULL;
6562306a36Sopenharmony_ci
6662306a36Sopenharmony_ci	tport->lport = lport;
6762306a36Sopenharmony_ci	tport->tpg = tpg;
6862306a36Sopenharmony_ci	tpg->tport = tport;
6962306a36Sopenharmony_ci	for (i = 0; i < FT_SESS_HASH_SIZE; i++)
7062306a36Sopenharmony_ci		INIT_HLIST_HEAD(&tport->hash[i]);
7162306a36Sopenharmony_ci
7262306a36Sopenharmony_ci	rcu_assign_pointer(lport->prov[FC_TYPE_FCP], tport);
7362306a36Sopenharmony_ci	return tport;
7462306a36Sopenharmony_ci}
7562306a36Sopenharmony_ci
7662306a36Sopenharmony_ci/*
7762306a36Sopenharmony_ci * Delete a target local port.
7862306a36Sopenharmony_ci * Caller holds ft_lport_lock.
7962306a36Sopenharmony_ci */
8062306a36Sopenharmony_cistatic void ft_tport_delete(struct ft_tport *tport)
8162306a36Sopenharmony_ci{
8262306a36Sopenharmony_ci	struct fc_lport *lport;
8362306a36Sopenharmony_ci	struct ft_tpg *tpg;
8462306a36Sopenharmony_ci
8562306a36Sopenharmony_ci	ft_sess_delete_all(tport);
8662306a36Sopenharmony_ci	lport = tport->lport;
8762306a36Sopenharmony_ci	lport->service_params &= ~FCP_SPPF_TARG_FCN;
8862306a36Sopenharmony_ci	BUG_ON(tport != lport->prov[FC_TYPE_FCP]);
8962306a36Sopenharmony_ci	RCU_INIT_POINTER(lport->prov[FC_TYPE_FCP], NULL);
9062306a36Sopenharmony_ci
9162306a36Sopenharmony_ci	tpg = tport->tpg;
9262306a36Sopenharmony_ci	if (tpg) {
9362306a36Sopenharmony_ci		tpg->tport = NULL;
9462306a36Sopenharmony_ci		tport->tpg = NULL;
9562306a36Sopenharmony_ci	}
9662306a36Sopenharmony_ci	kfree_rcu(tport, rcu);
9762306a36Sopenharmony_ci}
9862306a36Sopenharmony_ci
9962306a36Sopenharmony_ci/*
10062306a36Sopenharmony_ci * Add local port.
10162306a36Sopenharmony_ci * Called thru fc_lport_iterate().
10262306a36Sopenharmony_ci */
10362306a36Sopenharmony_civoid ft_lport_add(struct fc_lport *lport, void *arg)
10462306a36Sopenharmony_ci{
10562306a36Sopenharmony_ci	mutex_lock(&ft_lport_lock);
10662306a36Sopenharmony_ci	ft_tport_get(lport);
10762306a36Sopenharmony_ci	lport->service_params |= FCP_SPPF_TARG_FCN;
10862306a36Sopenharmony_ci	mutex_unlock(&ft_lport_lock);
10962306a36Sopenharmony_ci}
11062306a36Sopenharmony_ci
11162306a36Sopenharmony_ci/*
11262306a36Sopenharmony_ci * Delete local port.
11362306a36Sopenharmony_ci * Called thru fc_lport_iterate().
11462306a36Sopenharmony_ci */
11562306a36Sopenharmony_civoid ft_lport_del(struct fc_lport *lport, void *arg)
11662306a36Sopenharmony_ci{
11762306a36Sopenharmony_ci	struct ft_tport *tport;
11862306a36Sopenharmony_ci
11962306a36Sopenharmony_ci	mutex_lock(&ft_lport_lock);
12062306a36Sopenharmony_ci	tport = lport->prov[FC_TYPE_FCP];
12162306a36Sopenharmony_ci	if (tport)
12262306a36Sopenharmony_ci		ft_tport_delete(tport);
12362306a36Sopenharmony_ci	mutex_unlock(&ft_lport_lock);
12462306a36Sopenharmony_ci}
12562306a36Sopenharmony_ci
12662306a36Sopenharmony_ci/*
12762306a36Sopenharmony_ci * Notification of local port change from libfc.
12862306a36Sopenharmony_ci * Create or delete local port and associated tport.
12962306a36Sopenharmony_ci */
13062306a36Sopenharmony_ciint ft_lport_notify(struct notifier_block *nb, unsigned long event, void *arg)
13162306a36Sopenharmony_ci{
13262306a36Sopenharmony_ci	struct fc_lport *lport = arg;
13362306a36Sopenharmony_ci
13462306a36Sopenharmony_ci	switch (event) {
13562306a36Sopenharmony_ci	case FC_LPORT_EV_ADD:
13662306a36Sopenharmony_ci		ft_lport_add(lport, NULL);
13762306a36Sopenharmony_ci		break;
13862306a36Sopenharmony_ci	case FC_LPORT_EV_DEL:
13962306a36Sopenharmony_ci		ft_lport_del(lport, NULL);
14062306a36Sopenharmony_ci		break;
14162306a36Sopenharmony_ci	}
14262306a36Sopenharmony_ci	return NOTIFY_DONE;
14362306a36Sopenharmony_ci}
14462306a36Sopenharmony_ci
14562306a36Sopenharmony_ci/*
14662306a36Sopenharmony_ci * Hash function for FC_IDs.
14762306a36Sopenharmony_ci */
14862306a36Sopenharmony_cistatic u32 ft_sess_hash(u32 port_id)
14962306a36Sopenharmony_ci{
15062306a36Sopenharmony_ci	return hash_32(port_id, FT_SESS_HASH_BITS);
15162306a36Sopenharmony_ci}
15262306a36Sopenharmony_ci
15362306a36Sopenharmony_ci/*
15462306a36Sopenharmony_ci * Find session in local port.
15562306a36Sopenharmony_ci * Sessions and hash lists are RCU-protected.
15662306a36Sopenharmony_ci * A reference is taken which must be eventually freed.
15762306a36Sopenharmony_ci */
15862306a36Sopenharmony_cistatic struct ft_sess *ft_sess_get(struct fc_lport *lport, u32 port_id)
15962306a36Sopenharmony_ci{
16062306a36Sopenharmony_ci	struct ft_tport *tport;
16162306a36Sopenharmony_ci	struct hlist_head *head;
16262306a36Sopenharmony_ci	struct ft_sess *sess;
16362306a36Sopenharmony_ci	char *reason = "no session created";
16462306a36Sopenharmony_ci
16562306a36Sopenharmony_ci	rcu_read_lock();
16662306a36Sopenharmony_ci	tport = rcu_dereference(lport->prov[FC_TYPE_FCP]);
16762306a36Sopenharmony_ci	if (!tport) {
16862306a36Sopenharmony_ci		reason = "not an FCP port";
16962306a36Sopenharmony_ci		goto out;
17062306a36Sopenharmony_ci	}
17162306a36Sopenharmony_ci
17262306a36Sopenharmony_ci	head = &tport->hash[ft_sess_hash(port_id)];
17362306a36Sopenharmony_ci	hlist_for_each_entry_rcu(sess, head, hash) {
17462306a36Sopenharmony_ci		if (sess->port_id == port_id) {
17562306a36Sopenharmony_ci			kref_get(&sess->kref);
17662306a36Sopenharmony_ci			rcu_read_unlock();
17762306a36Sopenharmony_ci			TFC_SESS_DBG(lport, "port_id %x found %p\n",
17862306a36Sopenharmony_ci				     port_id, sess);
17962306a36Sopenharmony_ci			return sess;
18062306a36Sopenharmony_ci		}
18162306a36Sopenharmony_ci	}
18262306a36Sopenharmony_ciout:
18362306a36Sopenharmony_ci	rcu_read_unlock();
18462306a36Sopenharmony_ci	TFC_SESS_DBG(lport, "port_id %x not found, %s\n",
18562306a36Sopenharmony_ci		     port_id, reason);
18662306a36Sopenharmony_ci	return NULL;
18762306a36Sopenharmony_ci}
18862306a36Sopenharmony_ci
18962306a36Sopenharmony_cistatic int ft_sess_alloc_cb(struct se_portal_group *se_tpg,
19062306a36Sopenharmony_ci			    struct se_session *se_sess, void *p)
19162306a36Sopenharmony_ci{
19262306a36Sopenharmony_ci	struct ft_sess *sess = p;
19362306a36Sopenharmony_ci	struct ft_tport *tport = sess->tport;
19462306a36Sopenharmony_ci	struct hlist_head *head = &tport->hash[ft_sess_hash(sess->port_id)];
19562306a36Sopenharmony_ci
19662306a36Sopenharmony_ci	TFC_SESS_DBG(tport->lport, "port_id %x sess %p\n", sess->port_id, sess);
19762306a36Sopenharmony_ci	hlist_add_head_rcu(&sess->hash, head);
19862306a36Sopenharmony_ci	tport->sess_count++;
19962306a36Sopenharmony_ci
20062306a36Sopenharmony_ci	return 0;
20162306a36Sopenharmony_ci}
20262306a36Sopenharmony_ci
20362306a36Sopenharmony_ci/*
20462306a36Sopenharmony_ci * Allocate session and enter it in the hash for the local port.
20562306a36Sopenharmony_ci * Caller holds ft_lport_lock.
20662306a36Sopenharmony_ci */
20762306a36Sopenharmony_cistatic struct ft_sess *ft_sess_create(struct ft_tport *tport, u32 port_id,
20862306a36Sopenharmony_ci				      struct fc_rport_priv *rdata)
20962306a36Sopenharmony_ci{
21062306a36Sopenharmony_ci	struct se_portal_group *se_tpg = &tport->tpg->se_tpg;
21162306a36Sopenharmony_ci	struct ft_sess *sess;
21262306a36Sopenharmony_ci	struct hlist_head *head;
21362306a36Sopenharmony_ci	unsigned char initiatorname[TRANSPORT_IQN_LEN];
21462306a36Sopenharmony_ci
21562306a36Sopenharmony_ci	ft_format_wwn(&initiatorname[0], TRANSPORT_IQN_LEN, rdata->ids.port_name);
21662306a36Sopenharmony_ci
21762306a36Sopenharmony_ci	head = &tport->hash[ft_sess_hash(port_id)];
21862306a36Sopenharmony_ci	hlist_for_each_entry_rcu(sess, head, hash)
21962306a36Sopenharmony_ci		if (sess->port_id == port_id)
22062306a36Sopenharmony_ci			return sess;
22162306a36Sopenharmony_ci
22262306a36Sopenharmony_ci	sess = kzalloc(sizeof(*sess), GFP_KERNEL);
22362306a36Sopenharmony_ci	if (!sess)
22462306a36Sopenharmony_ci		return ERR_PTR(-ENOMEM);
22562306a36Sopenharmony_ci
22662306a36Sopenharmony_ci	kref_init(&sess->kref); /* ref for table entry */
22762306a36Sopenharmony_ci	sess->tport = tport;
22862306a36Sopenharmony_ci	sess->port_id = port_id;
22962306a36Sopenharmony_ci
23062306a36Sopenharmony_ci	sess->se_sess = target_setup_session(se_tpg, TCM_FC_DEFAULT_TAGS,
23162306a36Sopenharmony_ci					     sizeof(struct ft_cmd),
23262306a36Sopenharmony_ci					     TARGET_PROT_NORMAL, &initiatorname[0],
23362306a36Sopenharmony_ci					     sess, ft_sess_alloc_cb);
23462306a36Sopenharmony_ci	if (IS_ERR(sess->se_sess)) {
23562306a36Sopenharmony_ci		int rc = PTR_ERR(sess->se_sess);
23662306a36Sopenharmony_ci		kfree(sess);
23762306a36Sopenharmony_ci		sess = ERR_PTR(rc);
23862306a36Sopenharmony_ci	}
23962306a36Sopenharmony_ci	return sess;
24062306a36Sopenharmony_ci}
24162306a36Sopenharmony_ci
24262306a36Sopenharmony_ci/*
24362306a36Sopenharmony_ci * Unhash the session.
24462306a36Sopenharmony_ci * Caller holds ft_lport_lock.
24562306a36Sopenharmony_ci */
24662306a36Sopenharmony_cistatic void ft_sess_unhash(struct ft_sess *sess)
24762306a36Sopenharmony_ci{
24862306a36Sopenharmony_ci	struct ft_tport *tport = sess->tport;
24962306a36Sopenharmony_ci
25062306a36Sopenharmony_ci	hlist_del_rcu(&sess->hash);
25162306a36Sopenharmony_ci	BUG_ON(!tport->sess_count);
25262306a36Sopenharmony_ci	tport->sess_count--;
25362306a36Sopenharmony_ci	sess->port_id = -1;
25462306a36Sopenharmony_ci	sess->params = 0;
25562306a36Sopenharmony_ci}
25662306a36Sopenharmony_ci
25762306a36Sopenharmony_ci/*
25862306a36Sopenharmony_ci * Delete session from hash.
25962306a36Sopenharmony_ci * Caller holds ft_lport_lock.
26062306a36Sopenharmony_ci */
26162306a36Sopenharmony_cistatic struct ft_sess *ft_sess_delete(struct ft_tport *tport, u32 port_id)
26262306a36Sopenharmony_ci{
26362306a36Sopenharmony_ci	struct hlist_head *head;
26462306a36Sopenharmony_ci	struct ft_sess *sess;
26562306a36Sopenharmony_ci
26662306a36Sopenharmony_ci	head = &tport->hash[ft_sess_hash(port_id)];
26762306a36Sopenharmony_ci	hlist_for_each_entry_rcu(sess, head, hash) {
26862306a36Sopenharmony_ci		if (sess->port_id == port_id) {
26962306a36Sopenharmony_ci			ft_sess_unhash(sess);
27062306a36Sopenharmony_ci			return sess;
27162306a36Sopenharmony_ci		}
27262306a36Sopenharmony_ci	}
27362306a36Sopenharmony_ci	return NULL;
27462306a36Sopenharmony_ci}
27562306a36Sopenharmony_ci
27662306a36Sopenharmony_cistatic void ft_close_sess(struct ft_sess *sess)
27762306a36Sopenharmony_ci{
27862306a36Sopenharmony_ci	target_stop_session(sess->se_sess);
27962306a36Sopenharmony_ci	target_wait_for_sess_cmds(sess->se_sess);
28062306a36Sopenharmony_ci	ft_sess_put(sess);
28162306a36Sopenharmony_ci}
28262306a36Sopenharmony_ci
28362306a36Sopenharmony_ci/*
28462306a36Sopenharmony_ci * Delete all sessions from tport.
28562306a36Sopenharmony_ci * Caller holds ft_lport_lock.
28662306a36Sopenharmony_ci */
28762306a36Sopenharmony_cistatic void ft_sess_delete_all(struct ft_tport *tport)
28862306a36Sopenharmony_ci{
28962306a36Sopenharmony_ci	struct hlist_head *head;
29062306a36Sopenharmony_ci	struct ft_sess *sess;
29162306a36Sopenharmony_ci
29262306a36Sopenharmony_ci	for (head = tport->hash;
29362306a36Sopenharmony_ci	     head < &tport->hash[FT_SESS_HASH_SIZE]; head++) {
29462306a36Sopenharmony_ci		hlist_for_each_entry_rcu(sess, head, hash) {
29562306a36Sopenharmony_ci			ft_sess_unhash(sess);
29662306a36Sopenharmony_ci			ft_close_sess(sess);	/* release from table */
29762306a36Sopenharmony_ci		}
29862306a36Sopenharmony_ci	}
29962306a36Sopenharmony_ci}
30062306a36Sopenharmony_ci
30162306a36Sopenharmony_ci/*
30262306a36Sopenharmony_ci * TCM ops for sessions.
30362306a36Sopenharmony_ci */
30462306a36Sopenharmony_ci
30562306a36Sopenharmony_ci/*
30662306a36Sopenharmony_ci * Remove session and send PRLO.
30762306a36Sopenharmony_ci * This is called when the ACL is being deleted or queue depth is changing.
30862306a36Sopenharmony_ci */
30962306a36Sopenharmony_civoid ft_sess_close(struct se_session *se_sess)
31062306a36Sopenharmony_ci{
31162306a36Sopenharmony_ci	struct ft_sess *sess = se_sess->fabric_sess_ptr;
31262306a36Sopenharmony_ci	u32 port_id;
31362306a36Sopenharmony_ci
31462306a36Sopenharmony_ci	mutex_lock(&ft_lport_lock);
31562306a36Sopenharmony_ci	port_id = sess->port_id;
31662306a36Sopenharmony_ci	if (port_id == -1) {
31762306a36Sopenharmony_ci		mutex_unlock(&ft_lport_lock);
31862306a36Sopenharmony_ci		return;
31962306a36Sopenharmony_ci	}
32062306a36Sopenharmony_ci	TFC_SESS_DBG(sess->tport->lport, "port_id %x close session\n", port_id);
32162306a36Sopenharmony_ci	ft_sess_unhash(sess);
32262306a36Sopenharmony_ci	mutex_unlock(&ft_lport_lock);
32362306a36Sopenharmony_ci	ft_close_sess(sess);
32462306a36Sopenharmony_ci	/* XXX Send LOGO or PRLO */
32562306a36Sopenharmony_ci	synchronize_rcu();		/* let transport deregister happen */
32662306a36Sopenharmony_ci}
32762306a36Sopenharmony_ci
32862306a36Sopenharmony_ciu32 ft_sess_get_index(struct se_session *se_sess)
32962306a36Sopenharmony_ci{
33062306a36Sopenharmony_ci	struct ft_sess *sess = se_sess->fabric_sess_ptr;
33162306a36Sopenharmony_ci
33262306a36Sopenharmony_ci	return sess->port_id;	/* XXX TBD probably not what is needed */
33362306a36Sopenharmony_ci}
33462306a36Sopenharmony_ci
33562306a36Sopenharmony_ciu32 ft_sess_get_port_name(struct se_session *se_sess,
33662306a36Sopenharmony_ci			  unsigned char *buf, u32 len)
33762306a36Sopenharmony_ci{
33862306a36Sopenharmony_ci	struct ft_sess *sess = se_sess->fabric_sess_ptr;
33962306a36Sopenharmony_ci
34062306a36Sopenharmony_ci	return ft_format_wwn(buf, len, sess->port_name);
34162306a36Sopenharmony_ci}
34262306a36Sopenharmony_ci
34362306a36Sopenharmony_ci/*
34462306a36Sopenharmony_ci * libfc ops involving sessions.
34562306a36Sopenharmony_ci */
34662306a36Sopenharmony_ci
34762306a36Sopenharmony_cistatic int ft_prli_locked(struct fc_rport_priv *rdata, u32 spp_len,
34862306a36Sopenharmony_ci			  const struct fc_els_spp *rspp, struct fc_els_spp *spp)
34962306a36Sopenharmony_ci{
35062306a36Sopenharmony_ci	struct ft_tport *tport;
35162306a36Sopenharmony_ci	struct ft_sess *sess;
35262306a36Sopenharmony_ci	u32 fcp_parm;
35362306a36Sopenharmony_ci
35462306a36Sopenharmony_ci	tport = ft_tport_get(rdata->local_port);
35562306a36Sopenharmony_ci	if (!tport)
35662306a36Sopenharmony_ci		goto not_target;	/* not a target for this local port */
35762306a36Sopenharmony_ci
35862306a36Sopenharmony_ci	if (!rspp)
35962306a36Sopenharmony_ci		goto fill;
36062306a36Sopenharmony_ci
36162306a36Sopenharmony_ci	if (rspp->spp_flags & (FC_SPP_OPA_VAL | FC_SPP_RPA_VAL))
36262306a36Sopenharmony_ci		return FC_SPP_RESP_NO_PA;
36362306a36Sopenharmony_ci
36462306a36Sopenharmony_ci	/*
36562306a36Sopenharmony_ci	 * If both target and initiator bits are off, the SPP is invalid.
36662306a36Sopenharmony_ci	 */
36762306a36Sopenharmony_ci	fcp_parm = ntohl(rspp->spp_params);
36862306a36Sopenharmony_ci	if (!(fcp_parm & (FCP_SPPF_INIT_FCN | FCP_SPPF_TARG_FCN)))
36962306a36Sopenharmony_ci		return FC_SPP_RESP_INVL;
37062306a36Sopenharmony_ci
37162306a36Sopenharmony_ci	/*
37262306a36Sopenharmony_ci	 * Create session (image pair) only if requested by
37362306a36Sopenharmony_ci	 * EST_IMG_PAIR flag and if the requestor is an initiator.
37462306a36Sopenharmony_ci	 */
37562306a36Sopenharmony_ci	if (rspp->spp_flags & FC_SPP_EST_IMG_PAIR) {
37662306a36Sopenharmony_ci		spp->spp_flags |= FC_SPP_EST_IMG_PAIR;
37762306a36Sopenharmony_ci		if (!(fcp_parm & FCP_SPPF_INIT_FCN))
37862306a36Sopenharmony_ci			return FC_SPP_RESP_CONF;
37962306a36Sopenharmony_ci		sess = ft_sess_create(tport, rdata->ids.port_id, rdata);
38062306a36Sopenharmony_ci		if (IS_ERR(sess)) {
38162306a36Sopenharmony_ci			if (PTR_ERR(sess) == -EACCES) {
38262306a36Sopenharmony_ci				spp->spp_flags &= ~FC_SPP_EST_IMG_PAIR;
38362306a36Sopenharmony_ci				return FC_SPP_RESP_CONF;
38462306a36Sopenharmony_ci			} else
38562306a36Sopenharmony_ci				return FC_SPP_RESP_RES;
38662306a36Sopenharmony_ci		}
38762306a36Sopenharmony_ci		if (!sess->params)
38862306a36Sopenharmony_ci			rdata->prli_count++;
38962306a36Sopenharmony_ci		sess->params = fcp_parm;
39062306a36Sopenharmony_ci		sess->port_name = rdata->ids.port_name;
39162306a36Sopenharmony_ci		sess->max_frame = rdata->maxframe_size;
39262306a36Sopenharmony_ci
39362306a36Sopenharmony_ci		/* XXX TBD - clearing actions.  unit attn, see 4.10 */
39462306a36Sopenharmony_ci	}
39562306a36Sopenharmony_ci
39662306a36Sopenharmony_ci	/*
39762306a36Sopenharmony_ci	 * OR in our service parameters with other provider (initiator), if any.
39862306a36Sopenharmony_ci	 */
39962306a36Sopenharmony_cifill:
40062306a36Sopenharmony_ci	fcp_parm = ntohl(spp->spp_params);
40162306a36Sopenharmony_ci	fcp_parm &= ~FCP_SPPF_RETRY;
40262306a36Sopenharmony_ci	spp->spp_params = htonl(fcp_parm | FCP_SPPF_TARG_FCN);
40362306a36Sopenharmony_ci	return FC_SPP_RESP_ACK;
40462306a36Sopenharmony_ci
40562306a36Sopenharmony_cinot_target:
40662306a36Sopenharmony_ci	fcp_parm = ntohl(spp->spp_params);
40762306a36Sopenharmony_ci	fcp_parm &= ~FCP_SPPF_TARG_FCN;
40862306a36Sopenharmony_ci	spp->spp_params = htonl(fcp_parm);
40962306a36Sopenharmony_ci	return 0;
41062306a36Sopenharmony_ci}
41162306a36Sopenharmony_ci
41262306a36Sopenharmony_ci/**
41362306a36Sopenharmony_ci * ft_prli() - Handle incoming or outgoing PRLI for the FCP target
41462306a36Sopenharmony_ci * @rdata: remote port private
41562306a36Sopenharmony_ci * @spp_len: service parameter page length
41662306a36Sopenharmony_ci * @rspp: received service parameter page (NULL for outgoing PRLI)
41762306a36Sopenharmony_ci * @spp: response service parameter page
41862306a36Sopenharmony_ci *
41962306a36Sopenharmony_ci * Returns spp response code.
42062306a36Sopenharmony_ci */
42162306a36Sopenharmony_cistatic int ft_prli(struct fc_rport_priv *rdata, u32 spp_len,
42262306a36Sopenharmony_ci		   const struct fc_els_spp *rspp, struct fc_els_spp *spp)
42362306a36Sopenharmony_ci{
42462306a36Sopenharmony_ci	int ret;
42562306a36Sopenharmony_ci
42662306a36Sopenharmony_ci	mutex_lock(&ft_lport_lock);
42762306a36Sopenharmony_ci	ret = ft_prli_locked(rdata, spp_len, rspp, spp);
42862306a36Sopenharmony_ci	mutex_unlock(&ft_lport_lock);
42962306a36Sopenharmony_ci	TFC_SESS_DBG(rdata->local_port, "port_id %x flags %x ret %x\n",
43062306a36Sopenharmony_ci		     rdata->ids.port_id, rspp ? rspp->spp_flags : 0, ret);
43162306a36Sopenharmony_ci	return ret;
43262306a36Sopenharmony_ci}
43362306a36Sopenharmony_ci
43462306a36Sopenharmony_cistatic void ft_sess_free(struct kref *kref)
43562306a36Sopenharmony_ci{
43662306a36Sopenharmony_ci	struct ft_sess *sess = container_of(kref, struct ft_sess, kref);
43762306a36Sopenharmony_ci
43862306a36Sopenharmony_ci	target_remove_session(sess->se_sess);
43962306a36Sopenharmony_ci	kfree_rcu(sess, rcu);
44062306a36Sopenharmony_ci}
44162306a36Sopenharmony_ci
44262306a36Sopenharmony_civoid ft_sess_put(struct ft_sess *sess)
44362306a36Sopenharmony_ci{
44462306a36Sopenharmony_ci	int sess_held = kref_read(&sess->kref);
44562306a36Sopenharmony_ci
44662306a36Sopenharmony_ci	BUG_ON(!sess_held);
44762306a36Sopenharmony_ci	kref_put(&sess->kref, ft_sess_free);
44862306a36Sopenharmony_ci}
44962306a36Sopenharmony_ci
45062306a36Sopenharmony_cistatic void ft_prlo(struct fc_rport_priv *rdata)
45162306a36Sopenharmony_ci{
45262306a36Sopenharmony_ci	struct ft_sess *sess;
45362306a36Sopenharmony_ci	struct ft_tport *tport;
45462306a36Sopenharmony_ci
45562306a36Sopenharmony_ci	mutex_lock(&ft_lport_lock);
45662306a36Sopenharmony_ci	tport = rcu_dereference_protected(rdata->local_port->prov[FC_TYPE_FCP],
45762306a36Sopenharmony_ci					  lockdep_is_held(&ft_lport_lock));
45862306a36Sopenharmony_ci
45962306a36Sopenharmony_ci	if (!tport) {
46062306a36Sopenharmony_ci		mutex_unlock(&ft_lport_lock);
46162306a36Sopenharmony_ci		return;
46262306a36Sopenharmony_ci	}
46362306a36Sopenharmony_ci	sess = ft_sess_delete(tport, rdata->ids.port_id);
46462306a36Sopenharmony_ci	if (!sess) {
46562306a36Sopenharmony_ci		mutex_unlock(&ft_lport_lock);
46662306a36Sopenharmony_ci		return;
46762306a36Sopenharmony_ci	}
46862306a36Sopenharmony_ci	mutex_unlock(&ft_lport_lock);
46962306a36Sopenharmony_ci	ft_close_sess(sess);		/* release from table */
47062306a36Sopenharmony_ci	rdata->prli_count--;
47162306a36Sopenharmony_ci	/* XXX TBD - clearing actions.  unit attn, see 4.10 */
47262306a36Sopenharmony_ci}
47362306a36Sopenharmony_ci
47462306a36Sopenharmony_ci/*
47562306a36Sopenharmony_ci * Handle incoming FCP request.
47662306a36Sopenharmony_ci * Caller has verified that the frame is type FCP.
47762306a36Sopenharmony_ci */
47862306a36Sopenharmony_cistatic void ft_recv(struct fc_lport *lport, struct fc_frame *fp)
47962306a36Sopenharmony_ci{
48062306a36Sopenharmony_ci	struct ft_sess *sess;
48162306a36Sopenharmony_ci	u32 sid = fc_frame_sid(fp);
48262306a36Sopenharmony_ci
48362306a36Sopenharmony_ci	TFC_SESS_DBG(lport, "recv sid %x\n", sid);
48462306a36Sopenharmony_ci
48562306a36Sopenharmony_ci	sess = ft_sess_get(lport, sid);
48662306a36Sopenharmony_ci	if (!sess) {
48762306a36Sopenharmony_ci		TFC_SESS_DBG(lport, "sid %x sess lookup failed\n", sid);
48862306a36Sopenharmony_ci		/* TBD XXX - if FCP_CMND, send PRLO */
48962306a36Sopenharmony_ci		fc_frame_free(fp);
49062306a36Sopenharmony_ci		return;
49162306a36Sopenharmony_ci	}
49262306a36Sopenharmony_ci	ft_recv_req(sess, fp);	/* must do ft_sess_put() */
49362306a36Sopenharmony_ci}
49462306a36Sopenharmony_ci
49562306a36Sopenharmony_ci/*
49662306a36Sopenharmony_ci * Provider ops for libfc.
49762306a36Sopenharmony_ci */
49862306a36Sopenharmony_cistruct fc4_prov ft_prov = {
49962306a36Sopenharmony_ci	.prli = ft_prli,
50062306a36Sopenharmony_ci	.prlo = ft_prlo,
50162306a36Sopenharmony_ci	.recv = ft_recv,
50262306a36Sopenharmony_ci	.module = THIS_MODULE,
50362306a36Sopenharmony_ci};
504