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