// SPDX-License-Identifier: GPL-2.0-only /* * Copyright(c) 2007 Intel Corporation. All rights reserved. * * Maintained at www.Open-FCoE.org
*/
/* * PORT LOCKING NOTES * * These comments only apply to the 'port code' which consists of the lport, * disc and rport blocks. * * MOTIVATION * * The lport, disc and rport blocks all have mutexes that are used to protect * those objects. The main motivation for these locks is to prevent from * having an lport reset just before we send a frame. In that scenario the * lport's FID would get set to zero and then we'd send a frame with an * invalid SID. We also need to ensure that states don't change unexpectedly * while processing another state. * * HIERARCHY * * The following hierarchy defines the locking rules. A greater lock * may be held before acquiring a lesser lock, but a lesser lock should never * be held while attempting to acquire a greater lock. Here is the hierarchy- * * lport > disc, lport > rport, disc > rport * * CALLBACKS * * The callbacks cause complications with this scheme. There is a callback * from the rport (to either lport or disc) and a callback from disc * (to the lport). * * As rports exit the rport state machine a callback is made to the owner of * the rport to notify success or failure. Since the callback is likely to * cause the lport or disc to grab its lock we cannot hold the rport lock * while making the callback. To ensure that the rport is not free'd while * processing the callback the rport callbacks are serialized through a * single-threaded workqueue. An rport would never be free'd while in a * callback handler because no other rport work in this queue can be executed * at the same time. * * When discovery succeeds or fails a callback is made to the lport as * notification. Currently, successful discovery causes the lport to take no * action. A failure will cause the lport to reset. There is likely a circular * locking problem with this implementation.
*/
/* * LPORT LOCKING * * The critical sections protected by the lport's mutex are quite broad and * may be improved upon in the future. The lport code and its locking doesn't * influence the I/O path, so excessive locking doesn't penalize I/O * performance. * * The strategy is to lock whenever processing a request or response. Note * that every _enter_* function corresponds to a state change. They generally * change the lports state and then send a request out on the wire. We lock * before calling any of these functions to protect that state change. This * means that the entry points into the lport block manage the locks while * the state machine can transition between states (i.e. _enter_* functions) * while always staying protected. * * When handling responses we also hold the lport mutex broadly. When the * lport receives the response frame it locks the mutex and then calls the * appropriate handler for the particuar response. Generally a response will * trigger a state change and so the lock must already be held. * * Retries also have to consider the locking. The retries occur from a work * context and the work function will lock the lport and then retry the state * (i.e. _enter_* function).
*/
/** * struct fc_bsg_info - FC Passthrough managemet structure * @job: The passthrough job * @lport: The local port to pass through a command * @rsp_code: The expected response code * @sg: job->reply_payload.sg_list * @nents: job->reply_payload.sg_cnt * @offset: The offset into the response data
*/ struct fc_bsg_info { struct bsg_job *job; struct fc_lport *lport;
u16 rsp_code; struct scatterlist *sg;
u32 nents;
size_t offset;
};
/** * fc_frame_drop() - Dummy frame handler * @lport: The local port the frame was received on * @fp: The received frame
*/ staticint fc_frame_drop(struct fc_lport *lport, struct fc_frame *fp)
{
fc_frame_free(fp); return 0;
}
/** * fc_lport_rport_callback() - Event handler for rport events * @lport: The lport which is receiving the event * @rdata: private remote port data * @event: The event that occurred * * Locking Note: The rport lock should not be held when calling * this function.
*/ staticvoid fc_lport_rport_callback(struct fc_lport *lport, struct fc_rport_priv *rdata, enum fc_rport_event event)
{
FC_LPORT_DBG(lport, "Received a %d event for port (%6.6x)\n", event,
rdata->ids.port_id);
mutex_lock(&lport->lp_mutex); switch (event) { case RPORT_EV_READY: if (lport->state == LPORT_ST_DNS) {
lport->dns_rdata = rdata;
fc_lport_enter_ns(lport, LPORT_ST_RNN_ID);
} elseif (lport->state == LPORT_ST_FDMI) {
lport->ms_rdata = rdata;
fc_lport_enter_ms(lport, LPORT_ST_DHBA);
} else {
FC_LPORT_DBG(lport, "Received an READY event " "on port (%6.6x) for the directory " "server, but the lport is not " "in the DNS or FDMI state, it's in the " "%d state", rdata->ids.port_id,
lport->state);
fc_rport_logoff(rdata);
} break; case RPORT_EV_LOGO: case RPORT_EV_FAILED: case RPORT_EV_STOP: if (rdata->ids.port_id == FC_FID_DIR_SERV)
lport->dns_rdata = NULL; elseif (rdata->ids.port_id == FC_FID_MGMT_SERV)
lport->ms_rdata = NULL; break; case RPORT_EV_NONE: break;
}
mutex_unlock(&lport->lp_mutex);
}
/** * fc_lport_state() - Return a string which represents the lport's state * @lport: The lport whose state is to converted to a string
*/ staticconstchar *fc_lport_state(struct fc_lport *lport)
{ constchar *cp;
/** * fc_lport_ptp_setup() - Create an rport for point-to-point mode * @lport: The lport to attach the ptp rport to * @remote_fid: The FID of the ptp rport * @remote_wwpn: The WWPN of the ptp rport * @remote_wwnn: The WWNN of the ptp rport
*/ staticvoid fc_lport_ptp_setup(struct fc_lport *lport,
u32 remote_fid, u64 remote_wwpn,
u64 remote_wwnn)
{
lockdep_assert_held(&lport->lp_mutex);
/** * fc_get_host_port_state() - Return the port state of the given Scsi_Host * @shost: The SCSI host whose port state is to be determined
*/ void fc_get_host_port_state(struct Scsi_Host *shost)
{ struct fc_lport *lport = shost_priv(shost);
/** * fc_get_host_speed() - Return the speed of the given Scsi_Host * @shost: The SCSI host whose port speed is to be determined
*/ void fc_get_host_speed(struct Scsi_Host *shost)
{ struct fc_lport *lport = shost_priv(shost);
/** * fc_lport_flogi_fill() - Fill in FLOGI command for request * @lport: The local port the FLOGI is for * @flogi: The FLOGI command * @op: The opcode
*/ staticvoid fc_lport_flogi_fill(struct fc_lport *lport, struct fc_els_flogi *flogi, unsignedint op)
{ struct fc_els_csp *sp; struct fc_els_cssp *cp;
/** * fc_lport_add_fc4_type() - Add a supported FC-4 type to a local port * @lport: The local port to add a new FC-4 type to * @type: The new FC-4 type
*/ staticvoid fc_lport_add_fc4_type(struct fc_lport *lport, enum fc_fh_type type)
{
__be32 *mp;
/** * fc_lport_recv_logo_req() - Handle received fabric LOGO request * @lport: The local port receiving the LOGO * @fp: The LOGO request frame
*/ staticvoid fc_lport_recv_logo_req(struct fc_lport *lport, struct fc_frame *fp)
{
lockdep_assert_held(&lport->lp_mutex);
/** * fc_fabric_login() - Start the lport state machine * @lport: The local port that should log into the fabric * * Locking Note: This function should not be called * with the lport lock held.
*/ int fc_fabric_login(struct fc_lport *lport)
{ int rc = -1;
/** * __fc_linkup() - Handler for transport linkup events * @lport: The lport whose link is up
*/ void __fc_linkup(struct fc_lport *lport)
{
lockdep_assert_held(&lport->lp_mutex);
if (!lport->link_up) {
lport->link_up = 1;
if (lport->state == LPORT_ST_RESET)
fc_lport_enter_flogi(lport);
}
}
/** * fc_linkup() - Handler for transport linkup events * @lport: The local port whose link is up
*/ void fc_linkup(struct fc_lport *lport)
{
printk(KERN_INFO "host%d: libfc: Link up on port (%6.6x)\n",
lport->host->host_no, lport->port_id);
/** * __fc_linkdown() - Handler for transport linkdown events * @lport: The lport whose link is down
*/ void __fc_linkdown(struct fc_lport *lport)
{
lockdep_assert_held(&lport->lp_mutex);
if (lport->link_up) {
lport->link_up = 0;
fc_lport_enter_reset(lport);
lport->tt.fcp_cleanup(lport);
}
}
/** * fc_linkdown() - Handler for transport linkdown events * @lport: The local port whose link is down
*/ void fc_linkdown(struct fc_lport *lport)
{
printk(KERN_INFO "host%d: libfc: Link down on port (%6.6x)\n",
lport->host->host_no, lport->port_id);
/** * fc_fabric_logoff() - Logout of the fabric * @lport: The local port to logoff the fabric * * Return value: * 0 for success, -1 for failure
*/ int fc_fabric_logoff(struct fc_lport *lport)
{
lport->tt.disc_stop_final(lport);
mutex_lock(&lport->lp_mutex); if (lport->dns_rdata)
fc_rport_logoff(lport->dns_rdata);
mutex_unlock(&lport->lp_mutex);
fc_rport_flush_queue();
mutex_lock(&lport->lp_mutex);
fc_lport_enter_logo(lport);
mutex_unlock(&lport->lp_mutex);
cancel_delayed_work_sync(&lport->retry_work); return 0;
}
EXPORT_SYMBOL(fc_fabric_logoff);
/** * fc_lport_destroy() - Unregister a fc_lport * @lport: The local port to unregister * * Note: * exit routine for fc_lport instance * clean-up all the allocated memory * and free up other system resources. *
*/ int fc_lport_destroy(struct fc_lport *lport)
{
mutex_lock(&lport->lp_mutex);
lport->state = LPORT_ST_DISABLED;
lport->link_up = 0;
lport->tt.frame_send = fc_frame_drop;
mutex_unlock(&lport->lp_mutex);
/** * fc_set_mfs() - Set the maximum frame size for a local port * @lport: The local port to set the MFS for * @mfs: The new MFS
*/ int fc_set_mfs(struct fc_lport *lport, u32 mfs)
{ unsignedint old_mfs; int rc = -EINVAL;
if (!rc && mfs < old_mfs)
fc_lport_enter_reset(lport);
mutex_unlock(&lport->lp_mutex);
return rc;
}
EXPORT_SYMBOL(fc_set_mfs);
/** * fc_lport_disc_callback() - Callback for discovery events * @lport: The local port receiving the event * @event: The discovery event
*/ staticvoid fc_lport_disc_callback(struct fc_lport *lport, enum fc_disc_event event)
{ switch (event) { case DISC_EV_SUCCESS:
FC_LPORT_DBG(lport, "Discovery succeeded\n"); break; case DISC_EV_FAILED:
printk(KERN_ERR "host%d: libfc: " "Discovery failed for port (%6.6x)\n",
lport->host->host_no, lport->port_id);
mutex_lock(&lport->lp_mutex);
fc_lport_enter_reset(lport);
mutex_unlock(&lport->lp_mutex); break; case DISC_EV_NONE:
WARN_ON(1); break;
}
}
/** * fc_lport_enter_ready() - Enter the ready state and start discovery * @lport: The local port that is ready
*/ staticvoid fc_lport_enter_ready(struct fc_lport *lport)
{
lockdep_assert_held(&lport->lp_mutex);
FC_LPORT_DBG(lport, "Entered READY from state %s\n",
fc_lport_state(lport));
fc_lport_state_enter(lport, LPORT_ST_READY); if (lport->vport)
fc_vport_set_state(lport->vport, FC_VPORT_ACTIVE);
fc_vports_linkchange(lport);
if (!lport->ptp_rdata)
lport->tt.disc_start(fc_lport_disc_callback, lport);
}
/** * fc_lport_set_port_id() - set the local port Port ID * @lport: The local port which will have its Port ID set. * @port_id: The new port ID. * @fp: The frame containing the incoming request, or NULL.
*/ staticvoid fc_lport_set_port_id(struct fc_lport *lport, u32 port_id, struct fc_frame *fp)
{
lockdep_assert_held(&lport->lp_mutex);
if (port_id)
printk(KERN_INFO "host%d: Assigned Port ID %6.6x\n",
lport->host->host_no, port_id);
lport->port_id = port_id;
/* Update the fc_host */
fc_host_port_id(lport->host) = port_id;
if (lport->tt.lport_set_port_id)
lport->tt.lport_set_port_id(lport, port_id, fp);
}
/** * fc_lport_set_local_id() - set the local port Port ID for point-to-multipoint * @lport: The local port which will have its Port ID set. * @port_id: The new port ID. * * Called by the lower-level driver when transport sets the local port_id. * This is used in VN_port to VN_port mode for FCoE, and causes FLOGI and * discovery to be skipped.
*/ void fc_lport_set_local_id(struct fc_lport *lport, u32 port_id)
{
mutex_lock(&lport->lp_mutex);
fc_lport_set_port_id(lport, port_id, NULL);
switch (lport->state) { case LPORT_ST_RESET: case LPORT_ST_FLOGI: if (port_id)
fc_lport_enter_ready(lport); break; default: break;
}
mutex_unlock(&lport->lp_mutex);
}
EXPORT_SYMBOL(fc_lport_set_local_id);
/** * fc_lport_recv_flogi_req() - Receive a FLOGI request * @lport: The local port that received the request * @rx_fp: The FLOGI frame * * A received FLOGI request indicates a point-to-point connection. * Accept it with the common service parameters indicating our N port. * Set up to do a PLOGI if we have the higher-number WWPN.
*/ staticvoid fc_lport_recv_flogi_req(struct fc_lport *lport, struct fc_frame *rx_fp)
{ struct fc_frame *fp; struct fc_frame_header *fh; struct fc_els_flogi *flp; struct fc_els_flogi *new_flp;
u64 remote_wwpn;
u32 remote_fid;
u32 local_fid;
lockdep_assert_held(&lport->lp_mutex);
FC_LPORT_DBG(lport, "Received FLOGI request while in state %s\n",
fc_lport_state(lport));
remote_fid = fc_frame_sid(rx_fp);
flp = fc_frame_payload_get(rx_fp, sizeof(*flp)); if (!flp) goto out;
remote_wwpn = get_unaligned_be64(&flp->fl_wwpn); if (remote_wwpn == lport->wwpn) {
printk(KERN_WARNING "host%d: libfc: Received FLOGI from port " "with same WWPN %16.16llx\n",
lport->host->host_no, remote_wwpn); goto out;
}
FC_LPORT_DBG(lport, "FLOGI from port WWPN %16.16llx\n", remote_wwpn);
/* * XXX what is the right thing to do for FIDs? * The originator might expect our S_ID to be 0xfffffe. * But if so, both of us could end up with the same FID.
*/
local_fid = FC_LOCAL_PTP_FID_LO; if (remote_wwpn < lport->wwpn) {
local_fid = FC_LOCAL_PTP_FID_HI; if (!remote_fid || remote_fid == local_fid)
remote_fid = FC_LOCAL_PTP_FID_LO;
} elseif (!remote_fid) {
remote_fid = FC_LOCAL_PTP_FID_HI;
}
/* * Send the response. If this fails, the originator should * repeat the sequence.
*/
fc_fill_reply_hdr(fp, rx_fp, FC_RCTL_ELS_REP, 0);
fh = fc_frame_header_get(fp);
hton24(fh->fh_s_id, local_fid);
hton24(fh->fh_d_id, remote_fid);
lport->tt.frame_send(lport, fp);
/** * fc_lport_recv_els_req() - The generic lport ELS request handler * @lport: The local port that received the request * @fp: The request frame * * This function will see if the lport handles the request or * if an rport should handle the request. * * Locking Note: This function should not be called with the lport * lock held because it will grab the lock.
*/ staticvoid fc_lport_recv_els_req(struct fc_lport *lport, struct fc_frame *fp)
{
mutex_lock(&lport->lp_mutex);
/* * Handle special ELS cases like FLOGI, LOGO, and * RSCN here. These don't require a session. * Even if we had a session, it might not be ready.
*/ if (!lport->link_up)
fc_frame_free(fp); else { /* * Check opcode.
*/ switch (fc_frame_payload_op(fp)) { case ELS_FLOGI: if (!lport->point_to_multipoint)
fc_lport_recv_flogi_req(lport, fp); else
fc_rport_recv_req(lport, fp); break; case ELS_LOGO: if (fc_frame_sid(fp) == FC_FID_FLOGI)
fc_lport_recv_logo_req(lport, fp); else
fc_rport_recv_req(lport, fp); break; case ELS_RSCN:
lport->tt.disc_recv_req(lport, fp); break; case ELS_ECHO:
fc_lport_recv_echo_req(lport, fp); break; case ELS_RLIR:
fc_lport_recv_rlir_req(lport, fp); break; case ELS_RNID:
fc_lport_recv_rnid_req(lport, fp); break; default:
fc_rport_recv_req(lport, fp); break;
}
}
mutex_unlock(&lport->lp_mutex);
}
/** * fc_lport_recv() - The generic lport request handler * @lport: The lport that received the request * @fp: The frame the request is in * * Locking Note: This function should not be called with the lport * lock held because it may grab the lock.
*/ void fc_lport_recv(struct fc_lport *lport, struct fc_frame *fp)
{ struct fc_frame_header *fh = fc_frame_header_get(fp); struct fc_seq *sp = fr_seq(fp); struct fc4_prov *prov;
/* * Use RCU read lock and module_lock to be sure module doesn't * deregister and get unloaded while we're calling it. * try_module_get() is inlined and accepts a NULL parameter. * Only ELSes and FCP target ops should come through here. * The locking is unfortunate, and a better scheme is being sought.
*/
rcu_read_lock(); if (fh->fh_type >= FC_FC4_PROV_SIZE) goto drop;
prov = rcu_dereference(fc_passive_prov[fh->fh_type]); if (!prov || !try_module_get(prov->module)) goto drop;
rcu_read_unlock();
prov->recv(lport, fp);
module_put(prov->module); return;
drop:
rcu_read_unlock();
FC_LPORT_DBG(lport, "dropping unexpected frame type %x\n", fh->fh_type);
fc_frame_free(fp); if (sp)
fc_exch_done(sp);
}
EXPORT_SYMBOL(fc_lport_recv);
/** * fc_lport_reset() - Reset a local port * @lport: The local port which should be reset * * Locking Note: This functions should not be called with the * lport lock held.
*/ int fc_lport_reset(struct fc_lport *lport)
{
cancel_delayed_work_sync(&lport->retry_work);
mutex_lock(&lport->lp_mutex);
fc_lport_enter_reset(lport);
mutex_unlock(&lport->lp_mutex); return 0;
}
EXPORT_SYMBOL(fc_lport_reset);
/** * fc_lport_reset_locked() - Reset the local port w/ the lport lock held * @lport: The local port to be reset
*/ staticvoid fc_lport_reset_locked(struct fc_lport *lport)
{
lockdep_assert_held(&lport->lp_mutex);
if (lport->dns_rdata) {
fc_rport_logoff(lport->dns_rdata);
lport->dns_rdata = NULL;
}
if (lport->ptp_rdata) {
fc_rport_logoff(lport->ptp_rdata);
kref_put(&lport->ptp_rdata->kref, fc_rport_destroy);
lport->ptp_rdata = NULL;
}
if (lport->port_id && (!lport->point_to_multipoint || !lport->link_up))
fc_lport_set_port_id(lport, 0, NULL);
}
/** * fc_lport_enter_reset() - Reset the local port * @lport: The local port to be reset
*/ staticvoid fc_lport_enter_reset(struct fc_lport *lport)
{
lockdep_assert_held(&lport->lp_mutex);
FC_LPORT_DBG(lport, "Entered RESET state from %s state\n",
fc_lport_state(lport));
if (lport->state == LPORT_ST_DISABLED || lport->state == LPORT_ST_LOGO) return;
if (lport->vport) { if (lport->link_up)
fc_vport_set_state(lport->vport, FC_VPORT_INITIALIZING); else
fc_vport_set_state(lport->vport, FC_VPORT_LINKDOWN);
}
fc_lport_state_enter(lport, LPORT_ST_RESET);
fc_host_post_event(lport->host, fc_get_event_number(),
FCH_EVT_LIPRESET, 0);
fc_vports_linkchange(lport);
fc_lport_reset_locked(lport); if (lport->link_up)
fc_lport_enter_flogi(lport);
}
/** * fc_lport_enter_disabled() - Disable the local port * @lport: The local port to be reset
*/ staticvoid fc_lport_enter_disabled(struct fc_lport *lport)
{
lockdep_assert_held(&lport->lp_mutex);
FC_LPORT_DBG(lport, "Entered disabled state from %s state\n",
fc_lport_state(lport));
/** * fc_lport_error() - Handler for any errors * @lport: The local port that the error was on * @fp: The error code encoded in a frame pointer * * If the error was caused by a resource allocation failure * then wait for half a second and retry, otherwise retry * after the e_d_tov time.
*/ staticvoid fc_lport_error(struct fc_lport *lport, struct fc_frame *fp)
{ unsignedlong delay = 0;
FC_LPORT_DBG(lport, "Error %ld in state %s, retries %d\n",
IS_ERR(fp) ? -PTR_ERR(fp) : 0, fc_lport_state(lport),
lport->retry_count);
if (PTR_ERR(fp) == -FC_EX_CLOSED) return;
/* * Memory allocation failure, or the exchange timed out * or we received LS_RJT. * Retry after delay
*/ if (lport->retry_count < lport->max_retry_count) {
lport->retry_count++; if (!fp)
delay = msecs_to_jiffies(500); else
delay = msecs_to_jiffies(lport->e_d_tov);
/** * fc_lport_ns_resp() - Handle response to a name server * registration exchange * @sp: current sequence in exchange * @fp: response frame * @lp_arg: Fibre Channel host port instance * * Locking Note: This function will be called without the lport lock * held, but it will lock, call an _enter_* function or fc_lport_error() * and then unlock the lport.
*/ staticvoid fc_lport_ns_resp(struct fc_seq *sp, struct fc_frame *fp, void *lp_arg)
{ struct fc_lport *lport = lp_arg; struct fc_frame_header *fh; struct fc_ct_hdr *ct;
FC_LPORT_DBG(lport, "Received a ns %s\n", fc_els_resp_type(fp));
if (fp == ERR_PTR(-FC_EX_CLOSED)) return;
mutex_lock(&lport->lp_mutex);
if (lport->state < LPORT_ST_RNN_ID || lport->state > LPORT_ST_RFF_ID) {
FC_LPORT_DBG(lport, "Received a name server response, " "but in state %s\n", fc_lport_state(lport)); if (IS_ERR(fp)) goto err; goto out;
}
if (IS_ERR(fp)) {
fc_lport_error(lport, fp); goto err;
}
if (fh && ct && fh->fh_type == FC_TYPE_CT &&
ct->ct_fs_type == FC_FST_DIR &&
ct->ct_fs_subtype == FC_NS_SUBTYPE &&
ntohs(ct->ct_cmd) == FC_FS_ACC) switch (lport->state) { case LPORT_ST_RNN_ID:
fc_lport_enter_ns(lport, LPORT_ST_RSNN_NN); break; case LPORT_ST_RSNN_NN:
fc_lport_enter_ns(lport, LPORT_ST_RSPN_ID); break; case LPORT_ST_RSPN_ID:
fc_lport_enter_ns(lport, LPORT_ST_RFT_ID); break; case LPORT_ST_RFT_ID:
fc_lport_enter_ns(lport, LPORT_ST_RFF_ID); break; case LPORT_ST_RFF_ID: if (lport->fdmi_enabled)
fc_lport_enter_fdmi(lport); else
fc_lport_enter_scr(lport); break; default: /* should have already been caught by state checks */ break;
} else
fc_lport_error(lport, fp);
out:
fc_frame_free(fp);
err:
mutex_unlock(&lport->lp_mutex);
}
/** * fc_lport_ms_resp() - Handle response to a management server * exchange * @sp: current sequence in exchange * @fp: response frame * @lp_arg: Fibre Channel host port instance * * Locking Note: This function will be called without the lport lock * held, but it will lock, call an _enter_* function or fc_lport_error() * and then unlock the lport.
*/ staticvoid fc_lport_ms_resp(struct fc_seq *sp, struct fc_frame *fp, void *lp_arg)
{ struct fc_lport *lport = lp_arg; struct fc_frame_header *fh; struct fc_ct_hdr *ct; struct fc_host_attrs *fc_host = shost_to_fc_host(lport->host);
FC_LPORT_DBG(lport, "Received a ms %s\n", fc_els_resp_type(fp));
if (fp == ERR_PTR(-FC_EX_CLOSED)) return;
mutex_lock(&lport->lp_mutex);
if (lport->state < LPORT_ST_RHBA || lport->state > LPORT_ST_DPRT) {
FC_LPORT_DBG(lport, "Received a management server response, " "but in state %s\n", fc_lport_state(lport)); if (IS_ERR(fp)) goto err; goto out;
}
if (IS_ERR(fp)) {
fc_lport_error(lport, fp); goto err;
}
if (fh && ct && fh->fh_type == FC_TYPE_CT &&
ct->ct_fs_type == FC_FST_MGMT &&
ct->ct_fs_subtype == FC_FDMI_SUBTYPE) {
FC_LPORT_DBG(lport, "Received a management server response, " "reason=%d explain=%d\n",
ct->ct_reason,
ct->ct_explan);
switch (lport->state) { case LPORT_ST_RHBA: if ((ntohs(ct->ct_cmd) == FC_FS_RJT) && fc_host->fdmi_version == FDMI_V2) {
FC_LPORT_DBG(lport, "Error for FDMI-V2, fall back to FDMI-V1\n");
fc_host->fdmi_version = FDMI_V1;
fc_lport_enter_ms(lport, LPORT_ST_RHBA);
} elseif (ntohs(ct->ct_cmd) == FC_FS_ACC)
fc_lport_enter_ms(lport, LPORT_ST_RPA); else/* Error Skip RPA */
fc_lport_enter_scr(lport); break; case LPORT_ST_RPA:
fc_lport_enter_scr(lport); break; case LPORT_ST_DPRT:
fc_lport_enter_ms(lport, LPORT_ST_RHBA); break; case LPORT_ST_DHBA:
fc_lport_enter_ms(lport, LPORT_ST_DPRT); break; default: /* should have already been caught by state checks */ break;
}
} else { /* Invalid Frame? */
fc_lport_error(lport, fp);
}
out:
fc_frame_free(fp);
err:
mutex_unlock(&lport->lp_mutex);
}
/** * fc_lport_scr_resp() - Handle response to State Change Register (SCR) request * @sp: current sequence in SCR exchange * @fp: response frame * @lp_arg: Fibre Channel lport port instance that sent the registration request * * Locking Note: This function will be called without the lport lock * held, but it will lock, call an _enter_* function or fc_lport_error * and then unlock the lport.
*/ staticvoid fc_lport_scr_resp(struct fc_seq *sp, struct fc_frame *fp, void *lp_arg)
{ struct fc_lport *lport = lp_arg;
u8 op;
FC_LPORT_DBG(lport, "Received a SCR %s\n", fc_els_resp_type(fp));
if (fp == ERR_PTR(-FC_EX_CLOSED)) return;
mutex_lock(&lport->lp_mutex);
if (lport->state != LPORT_ST_SCR) {
FC_LPORT_DBG(lport, "Received a SCR response, but in state " "%s\n", fc_lport_state(lport)); if (IS_ERR(fp)) goto err; goto out;
}
if (IS_ERR(fp)) {
fc_lport_error(lport, fp); goto err;
}
op = fc_frame_payload_op(fp); if (op == ELS_LS_ACC)
fc_lport_enter_ready(lport); else
fc_lport_error(lport, fp);
/** * fc_lport_enter_scr() - Send a SCR (State Change Register) request * @lport: The local port to register for state changes
*/ staticvoid fc_lport_enter_scr(struct fc_lport *lport)
{ struct fc_frame *fp;
lockdep_assert_held(&lport->lp_mutex);
FC_LPORT_DBG(lport, "Entered SCR state from %s state\n",
fc_lport_state(lport));
/** * fc_lport_enter_ns() - register some object with the name server * @lport: Fibre Channel local port to register * @state: Local port state
*/ staticvoid fc_lport_enter_ns(struct fc_lport *lport, enum fc_lport_state state)
{ struct fc_frame *fp; enum fc_ns_req cmd; int size = sizeof(struct fc_ct_hdr);
size_t len;
lockdep_assert_held(&lport->lp_mutex);
FC_LPORT_DBG(lport, "Entered %s state from %s state\n",
fc_lport_state_names[state],
fc_lport_state(lport));
fc_lport_state_enter(lport, state);
switch (state) { case LPORT_ST_RNN_ID:
cmd = FC_NS_RNN_ID;
size += sizeof(struct fc_ns_rn_id); break; case LPORT_ST_RSNN_NN:
len = strnlen(fc_host_symbolic_name(lport->host), 255); /* if there is no symbolic name, skip to RFT_ID */ if (!len) return fc_lport_enter_ns(lport, LPORT_ST_RFT_ID);
cmd = FC_NS_RSNN_NN;
size += sizeof(struct fc_ns_rsnn) + len; break; case LPORT_ST_RSPN_ID:
len = strnlen(fc_host_symbolic_name(lport->host), 255); /* if there is no symbolic name, skip to RFT_ID */ if (!len) return fc_lport_enter_ns(lport, LPORT_ST_RFT_ID);
cmd = FC_NS_RSPN_ID;
size += sizeof(struct fc_ns_rspn) + len; break; case LPORT_ST_RFT_ID:
cmd = FC_NS_RFT_ID;
size += sizeof(struct fc_ns_rft); break; case LPORT_ST_RFF_ID:
cmd = FC_NS_RFF_ID;
size += sizeof(struct fc_ns_rff_id); break; default:
fc_lport_error(lport, NULL); return;
}
/** * fc_lport_enter_dns() - Create a fc_rport for the name server * @lport: The local port requesting a remote port for the name server
*/ staticvoid fc_lport_enter_dns(struct fc_lport *lport)
{ struct fc_rport_priv *rdata;
lockdep_assert_held(&lport->lp_mutex);
FC_LPORT_DBG(lport, "Entered DNS state from %s state\n",
fc_lport_state(lport));
fc_lport_state_enter(lport, LPORT_ST_DNS);
mutex_lock(&lport->disc.disc_mutex);
rdata = fc_rport_create(lport, FC_FID_DIR_SERV);
mutex_unlock(&lport->disc.disc_mutex); if (!rdata) goto err;
/** * fc_lport_enter_ms() - management server commands * @lport: Fibre Channel local port to register * @state: Local port state
*/ staticvoid fc_lport_enter_ms(struct fc_lport *lport, enum fc_lport_state state)
{ struct fc_frame *fp; enum fc_fdmi_req cmd; int size = sizeof(struct fc_ct_hdr);
size_t len; int numattrs; struct fc_host_attrs *fc_host = shost_to_fc_host(lport->host);
lockdep_assert_held(&lport->lp_mutex);
FC_LPORT_DBG(lport, "Entered %s state from %s state\n",
fc_lport_state_names[state],
fc_lport_state(lport));
fc_lport_state_enter(lport, state);
switch (state) { case LPORT_ST_RHBA:
cmd = FC_FDMI_RHBA; /* Number of HBA Attributes */
numattrs = 11;
len = sizeof(struct fc_fdmi_rhba);
len -= sizeof(struct fc_fdmi_attr_entry);
len += FC_FDMI_HBA_ATTR_NODENAME_LEN;
len += FC_FDMI_HBA_ATTR_MANUFACTURER_LEN;
len += FC_FDMI_HBA_ATTR_SERIALNUMBER_LEN;
len += FC_FDMI_HBA_ATTR_MODEL_LEN;
len += FC_FDMI_HBA_ATTR_MODELDESCR_LEN;
len += FC_FDMI_HBA_ATTR_HARDWAREVERSION_LEN;
len += FC_FDMI_HBA_ATTR_DRIVERVERSION_LEN;
len += FC_FDMI_HBA_ATTR_OPTIONROMVERSION_LEN;
len += FC_FDMI_HBA_ATTR_FIRMWAREVERSION_LEN;
len += FC_FDMI_HBA_ATTR_OSNAMEVERSION_LEN;
len += FC_FDMI_HBA_ATTR_MAXCTPAYLOAD_LEN;
if (fc_host->fdmi_version == FDMI_V2) {
numattrs += 7;
len += FC_FDMI_HBA_ATTR_NODESYMBLNAME_LEN;
len += FC_FDMI_HBA_ATTR_VENDORSPECIFICINFO_LEN;
len += FC_FDMI_HBA_ATTR_NUMBEROFPORTS_LEN;
len += FC_FDMI_HBA_ATTR_FABRICNAME_LEN;
len += FC_FDMI_HBA_ATTR_BIOSVERSION_LEN;
len += FC_FDMI_HBA_ATTR_BIOSSTATE_LEN;
len += FC_FDMI_HBA_ATTR_VENDORIDENTIFIER_LEN;
}
len += (numattrs * FC_FDMI_ATTR_ENTRY_HEADER_LEN);
size += len; break; case LPORT_ST_RPA:
cmd = FC_FDMI_RPA; /* Number of Port Attributes */
numattrs = 6;
len = sizeof(struct fc_fdmi_rpa);
len -= sizeof(struct fc_fdmi_attr_entry);
len += FC_FDMI_PORT_ATTR_FC4TYPES_LEN;
len += FC_FDMI_PORT_ATTR_SUPPORTEDSPEED_LEN;
len += FC_FDMI_PORT_ATTR_CURRENTPORTSPEED_LEN;
len += FC_FDMI_PORT_ATTR_MAXFRAMESIZE_LEN;
len += FC_FDMI_PORT_ATTR_OSDEVICENAME_LEN;
len += FC_FDMI_PORT_ATTR_HOSTNAME_LEN;
if (fc_host->fdmi_version == FDMI_V2) {
numattrs += 10;
len += FC_FDMI_PORT_ATTR_NODENAME_LEN;
len += FC_FDMI_PORT_ATTR_PORTNAME_LEN;
len += FC_FDMI_PORT_ATTR_SYMBOLICNAME_LEN;
len += FC_FDMI_PORT_ATTR_PORTTYPE_LEN;
len += FC_FDMI_PORT_ATTR_SUPPORTEDCLASSSRVC_LEN;
len += FC_FDMI_PORT_ATTR_FABRICNAME_LEN;
len += FC_FDMI_PORT_ATTR_CURRENTFC4TYPE_LEN;
len += FC_FDMI_PORT_ATTR_PORTSTATE_LEN;
len += FC_FDMI_PORT_ATTR_DISCOVEREDPORTS_LEN;
len += FC_FDMI_PORT_ATTR_PORTID_LEN;
}
len += (numattrs * FC_FDMI_ATTR_ENTRY_HEADER_LEN);
size += len; break; case LPORT_ST_DPRT:
cmd = FC_FDMI_DPRT;
len = sizeof(struct fc_fdmi_dprt);
size += len; break; case LPORT_ST_DHBA:
cmd = FC_FDMI_DHBA;
len = sizeof(struct fc_fdmi_dhba);
size += len; break; default:
fc_lport_error(lport, NULL); return;
}
FC_LPORT_DBG(lport, "Cmd=0x%x Len %d size %d\n",
cmd, (int)len, size);
fp = fc_frame_alloc(lport, size); if (!fp) {
fc_lport_error(lport, fp); return;
}
/** * fc_lport_enter_fdmi() - Create a fc_rport for the management server * @lport: The local port requesting a remote port for the management server
*/ staticvoid fc_lport_enter_fdmi(struct fc_lport *lport)
{ struct fc_rport_priv *rdata;
lockdep_assert_held(&lport->lp_mutex);
FC_LPORT_DBG(lport, "Entered FDMI state from %s state\n",
fc_lport_state(lport));
fc_lport_state_enter(lport, LPORT_ST_FDMI);
mutex_lock(&lport->disc.disc_mutex);
rdata = fc_rport_create(lport, FC_FID_MGMT_SERV);
mutex_unlock(&lport->disc.disc_mutex); if (!rdata) goto err;
/** * fc_lport_timeout() - Handler for the retry_work timer * @work: The work struct of the local port
*/ staticvoid fc_lport_timeout(struct work_struct *work)
{ struct fc_lport *lport =
container_of(work, struct fc_lport,
retry_work.work); struct fc_host_attrs *fc_host = shost_to_fc_host(lport->host);
mutex_lock(&lport->lp_mutex);
switch (lport->state) { case LPORT_ST_DISABLED: break; case LPORT_ST_READY: break; case LPORT_ST_RESET: break; case LPORT_ST_FLOGI:
fc_lport_enter_flogi(lport); break; case LPORT_ST_DNS:
fc_lport_enter_dns(lport); break; case LPORT_ST_RNN_ID: case LPORT_ST_RSNN_NN: case LPORT_ST_RSPN_ID: case LPORT_ST_RFT_ID: case LPORT_ST_RFF_ID:
fc_lport_enter_ns(lport, lport->state); break; case LPORT_ST_FDMI:
fc_lport_enter_fdmi(lport); break; case LPORT_ST_RHBA: if (fc_host->fdmi_version == FDMI_V2) {
FC_LPORT_DBG(lport, "timeout for FDMI-V2 RHBA,fall back to FDMI-V1\n");
fc_host->fdmi_version = FDMI_V1;
fc_lport_enter_ms(lport, LPORT_ST_RHBA); break;
}
fallthrough; case LPORT_ST_RPA: case LPORT_ST_DHBA: case LPORT_ST_DPRT:
FC_LPORT_DBG(lport, "Skipping lport state %s to SCR\n",
fc_lport_state(lport));
fallthrough; case LPORT_ST_SCR:
fc_lport_enter_scr(lport); break; case LPORT_ST_LOGO:
fc_lport_enter_logo(lport); break;
}
mutex_unlock(&lport->lp_mutex);
}
/** * fc_lport_logo_resp() - Handle response to LOGO request * @sp: The sequence that the LOGO was on * @fp: The LOGO frame * @lp_arg: The lport port that received the LOGO request * * Locking Note: This function will be called without the lport lock * held, but it will lock, call an _enter_* function or fc_lport_error() * and then unlock the lport.
*/ void fc_lport_logo_resp(struct fc_seq *sp, struct fc_frame *fp, void *lp_arg)
{ struct fc_lport *lport = lp_arg;
u8 op;
FC_LPORT_DBG(lport, "Received a LOGO %s\n", fc_els_resp_type(fp));
if (fp == ERR_PTR(-FC_EX_CLOSED)) return;
mutex_lock(&lport->lp_mutex);
if (lport->state != LPORT_ST_LOGO) {
FC_LPORT_DBG(lport, "Received a LOGO response, but in state " "%s\n", fc_lport_state(lport)); if (IS_ERR(fp)) goto err; goto out;
}
if (IS_ERR(fp)) {
fc_lport_error(lport, fp); goto err;
}
op = fc_frame_payload_op(fp); if (op == ELS_LS_ACC)
fc_lport_enter_disabled(lport); else
fc_lport_error(lport, fp);
/** * fc_lport_enter_logo() - Logout of the fabric * @lport: The local port to be logged out
*/ staticvoid fc_lport_enter_logo(struct fc_lport *lport)
{ struct fc_frame *fp; struct fc_els_logo *logo;
lockdep_assert_held(&lport->lp_mutex);
FC_LPORT_DBG(lport, "Entered LOGO state from %s state\n",
fc_lport_state(lport));
/** * fc_lport_flogi_resp() - Handle response to FLOGI request * @sp: The sequence that the FLOGI was on * @fp: The FLOGI response frame * @lp_arg: The lport port that received the FLOGI response * * Locking Note: This function will be called without the lport lock * held, but it will lock, call an _enter_* function or fc_lport_error() * and then unlock the lport.
*/ void fc_lport_flogi_resp(struct fc_seq *sp, struct fc_frame *fp, void *lp_arg)
{ struct fc_lport *lport = lp_arg; struct fc_frame_header *fh; struct fc_els_flogi *flp;
u32 did;
u16 csp_flags; unsignedint r_a_tov; unsignedint e_d_tov;
u16 mfs;
FC_LPORT_DBG(lport, "Received a FLOGI %s\n", fc_els_resp_type(fp));
if (fp == ERR_PTR(-FC_EX_CLOSED)) return;
mutex_lock(&lport->lp_mutex);
if (lport->state != LPORT_ST_FLOGI) {
FC_LPORT_DBG(lport, "Received a FLOGI response, but in state " "%s\n", fc_lport_state(lport)); if (IS_ERR(fp)) goto err; goto out;
}
if (IS_ERR(fp)) {
fc_lport_error(lport, fp); goto err;
}
fh = fc_frame_header_get(fp);
did = fc_frame_did(fp); if (fh->fh_r_ctl != FC_RCTL_ELS_REP || did == 0 ||
fc_frame_payload_op(fp) != ELS_LS_ACC) {
FC_LPORT_DBG(lport, "FLOGI not accepted or bad response\n");
fc_lport_error(lport, fp); goto out;
}
flp = fc_frame_payload_get(fp, sizeof(*flp)); if (!flp) {
FC_LPORT_DBG(lport, "FLOGI bad response\n");
fc_lport_error(lport, fp); goto out;
}
/** * fc_lport_enter_flogi() - Send a FLOGI request to the fabric manager * @lport: Fibre Channel local port to be logged in to the fabric
*/ staticvoid fc_lport_enter_flogi(struct fc_lport *lport)
{ struct fc_frame *fp;
lockdep_assert_held(&lport->lp_mutex);
FC_LPORT_DBG(lport, "Entered FLOGI state from %s state\n",
fc_lport_state(lport));
fc_lport_state_enter(lport, LPORT_ST_FLOGI);
if (lport->point_to_multipoint) { if (lport->port_id)
fc_lport_enter_ready(lport); return;
}
fp = fc_frame_alloc(lport, sizeof(struct fc_els_flogi)); if (!fp) return fc_lport_error(lport, fp);
/** * fc_lport_config() - Configure a fc_lport * @lport: The local port to be configured
*/ int fc_lport_config(struct fc_lport *lport)
{
INIT_DELAYED_WORK(&lport->retry_work, fc_lport_timeout);
mutex_init(&lport->lp_mutex);
/** * fc_lport_init() - Initialize the lport layer for a local port * @lport: The local port to initialize the exchange layer for
*/ int fc_lport_init(struct fc_lport *lport)
{ struct fc_host_attrs *fc_host;
fc_host = shost_to_fc_host(lport->host);
/* Set FDMI version to FDMI-2 specification*/
fc_host->fdmi_version = FDMI_V2;
/** * fc_lport_bsg_resp() - The common response handler for FC Passthrough requests * @sp: The sequence for the FC Passthrough response * @fp: The response frame * @info_arg: The BSG info that the response is for
*/ staticvoid fc_lport_bsg_resp(struct fc_seq *sp, struct fc_frame *fp, void *info_arg)
{ struct fc_bsg_info *info = info_arg; struct bsg_job *job = info->job; struct fc_bsg_reply *bsg_reply = job->reply; struct fc_lport *lport = info->lport; struct fc_frame_header *fh;
size_t len; void *buf;
if (fr_sof(fp) == FC_SOF_I3 && !ntohs(fh->fh_seq_cnt)) { /* Get the response code from the first frame payload */ unsignedshort cmd = (info->rsp_code == FC_FS_ACC) ?
ntohs(((struct fc_ct_hdr *)buf)->ct_cmd) :
(unsignedshort)fc_frame_payload_op(fp);
/* Save the reply status of the job */
bsg_reply->reply_data.ctels_reply.status =
(cmd == info->rsp_code) ?
FC_CTELS_STATUS_OK : FC_CTELS_STATUS_REJECT;
}
/** * fc_lport_els_request() - Send ELS passthrough request * @job: The BSG Passthrough job * @lport: The local port sending the request * @did: The destination port id * @tov: The timeout period (in ms)
*/ staticint fc_lport_els_request(struct bsg_job *job, struct fc_lport *lport,
u32 did, u32 tov)
{ struct fc_bsg_info *info; struct fc_frame *fp; struct fc_frame_header *fh; char *pp; int len;
lockdep_assert_held(&lport->lp_mutex);
fp = fc_frame_alloc(lport, job->request_payload.payload_len); if (!fp) return -ENOMEM;
len = job->request_payload.payload_len;
pp = fc_frame_payload_get(fp, len);
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.