cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

tfc_sess.c (11604B)


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