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