/* * Delete local port. * Called thru fc_lport_iterate().
*/ void ft_lport_del(struct fc_lport *lport, void *arg)
{ struct ft_tport *tport;
mutex_lock(&ft_lport_lock);
tport = lport->prov[FC_TYPE_FCP]; if (tport)
ft_tport_delete(tport);
mutex_unlock(&ft_lport_lock);
}
/* * Notification of local port change from libfc. * Create or delete local port and associated tport.
*/ int ft_lport_notify(struct notifier_block *nb, unsignedlong event, void *arg)
{ struct fc_lport *lport = arg;
switch (event) { case FC_LPORT_EV_ADD:
ft_lport_add(lport, NULL); break; case FC_LPORT_EV_DEL:
ft_lport_del(lport, NULL); break;
} return NOTIFY_DONE;
}
/* * Hash function for FC_IDs.
*/ static u32 ft_sess_hash(u32 port_id)
{ return hash_32(port_id, FT_SESS_HASH_BITS);
}
/* * Find session in local port. * Sessions and hash lists are RCU-protected. * A reference is taken which must be eventually freed.
*/ staticstruct ft_sess *ft_sess_get(struct fc_lport *lport, u32 port_id)
{ struct ft_tport *tport; struct hlist_head *head; struct ft_sess *sess; char *reason = "no session created";
rcu_read_lock();
tport = rcu_dereference(lport->prov[FC_TYPE_FCP]); if (!tport) {
reason = "not an FCP port"; goto out;
}
head = &tport->hash[ft_sess_hash(port_id)];
hlist_for_each_entry_rcu(sess, head, hash) { if (sess->port_id == port_id) {
kref_get(&sess->kref);
rcu_read_unlock();
TFC_SESS_DBG(lport, "port_id %x found %p\n",
port_id, sess); return sess;
}
}
out:
rcu_read_unlock();
TFC_SESS_DBG(lport, "port_id %x not found, %s\n",
port_id, reason); return NULL;
}
for (head = tport->hash;
head < &tport->hash[FT_SESS_HASH_SIZE]; head++) {
hlist_for_each_entry_rcu(sess, head, hash) {
ft_sess_unhash(sess);
ft_close_sess(sess); /* release from table */
}
}
}
/* * TCM ops for sessions.
*/
/* * Remove session and send PRLO. * This is called when the ACL is being deleted or queue depth is changing.
*/ void ft_sess_close(struct se_session *se_sess)
{ struct ft_sess *sess = se_sess->fabric_sess_ptr;
u32 port_id;
mutex_lock(&ft_lport_lock);
port_id = sess->port_id; if (port_id == -1) {
mutex_unlock(&ft_lport_lock); return;
}
TFC_SESS_DBG(sess->tport->lport, "port_id %x close session\n", port_id);
ft_sess_unhash(sess);
mutex_unlock(&ft_lport_lock);
ft_close_sess(sess); /* XXX Send LOGO or PRLO */
synchronize_rcu(); /* let transport deregister happen */
}
tport = ft_tport_get(rdata->local_port); if (!tport) goto not_target; /* not a target for this local port */
if (!rspp) goto fill;
if (rspp->spp_flags & (FC_SPP_OPA_VAL | FC_SPP_RPA_VAL)) return FC_SPP_RESP_NO_PA;
/* * If both target and initiator bits are off, the SPP is invalid.
*/
fcp_parm = ntohl(rspp->spp_params); if (!(fcp_parm & (FCP_SPPF_INIT_FCN | FCP_SPPF_TARG_FCN))) return FC_SPP_RESP_INVL;
/* * Create session (image pair) only if requested by * EST_IMG_PAIR flag and if the requestor is an initiator.
*/ if (rspp->spp_flags & FC_SPP_EST_IMG_PAIR) {
spp->spp_flags |= FC_SPP_EST_IMG_PAIR; if (!(fcp_parm & FCP_SPPF_INIT_FCN)) return FC_SPP_RESP_CONF;
sess = ft_sess_create(tport, rdata->ids.port_id, rdata); if (IS_ERR(sess)) { if (PTR_ERR(sess) == -EACCES) {
spp->spp_flags &= ~FC_SPP_EST_IMG_PAIR; return FC_SPP_RESP_CONF;
} else return FC_SPP_RESP_RES;
} if (!sess->params)
rdata->prli_count++;
sess->params = fcp_parm;
sess->port_name = rdata->ids.port_name;
sess->max_frame = rdata->maxframe_size;
/* XXX TBD - clearing actions. unit attn, see 4.10 */
}
/* * OR in our service parameters with other provider (initiator), if any.
*/
fill:
fcp_parm = ntohl(spp->spp_params);
fcp_parm &= ~FCP_SPPF_RETRY;
spp->spp_params = htonl(fcp_parm | FCP_SPPF_TARG_FCN); return FC_SPP_RESP_ACK;
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.