linux/drivers/scsi/libfc/fc_lport.c

// 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).
 */

#include <linux/timer.h>
#include <linux/delay.h>
#include <linux/module.h>
#include <linux/slab.h>
#include <asm/unaligned.h>

#include <scsi/fc/fc_gs.h>

#include <scsi/libfc.h>
#include <linux/scatterlist.h>

#include "fc_encode.h"
#include "fc_libfc.h"

/* Fabric IDs to use for point-to-point mode, chosen on whims. */
#define FC_LOCAL_PTP_FID_LO
#define FC_LOCAL_PTP_FID_HI

#define DNS_DELAY
#define MAX_CT_PAYLOAD
#define DISCOVERED_PORTS
#define NUMBER_OF_PORTS

static void fc_lport_error(struct fc_lport *, struct fc_frame *);

static void fc_lport_enter_reset(struct fc_lport *);
static void fc_lport_enter_flogi(struct fc_lport *);
static void fc_lport_enter_dns(struct fc_lport *);
static void fc_lport_enter_ns(struct fc_lport *, enum fc_lport_state);
static void fc_lport_enter_scr(struct fc_lport *);
static void fc_lport_enter_ready(struct fc_lport *);
static void fc_lport_enter_logo(struct fc_lport *);
static void fc_lport_enter_fdmi(struct fc_lport *lport);
static void fc_lport_enter_ms(struct fc_lport *, enum fc_lport_state);

static const char *fc_lport_state_names[] =;

/**
 * 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 {};

/**
 * fc_frame_drop() - Dummy frame handler
 * @lport: The local port the frame was received on
 * @fp:	   The received frame
 */
static int fc_frame_drop(struct fc_lport *lport, struct fc_frame *fp)
{}

/**
 * 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.
 */
static void fc_lport_rport_callback(struct fc_lport *lport,
				    struct fc_rport_priv *rdata,
				    enum fc_rport_event event)
{}

/**
 * fc_lport_state() - Return a string which represents the lport's state
 * @lport: The lport whose state is to converted to a string
 */
static const char *fc_lport_state(struct fc_lport *lport)
{}

/**
 * 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
 */
static void fc_lport_ptp_setup(struct fc_lport *lport,
			       u32 remote_fid, u64 remote_wwpn,
			       u64 remote_wwnn)
{}

/**
 * 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)
{}
EXPORT_SYMBOL();

/**
 * 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)
{}
EXPORT_SYMBOL();

/**
 * fc_get_host_stats() - Return the Scsi_Host's statistics
 * @shost: The SCSI host whose statistics are to be returned
 */
struct fc_host_statistics *fc_get_host_stats(struct Scsi_Host *shost)
{}
EXPORT_SYMBOL();

/**
 * 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
 */
static void fc_lport_flogi_fill(struct fc_lport *lport,
				struct fc_els_flogi *flogi,
				unsigned int op)
{}

/**
 * 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
 */
static void fc_lport_add_fc4_type(struct fc_lport *lport, enum fc_fh_type type)
{}

/**
 * fc_lport_recv_rlir_req() - Handle received Registered Link Incident Report.
 * @lport: Fibre Channel local port receiving the RLIR
 * @fp:	   The RLIR request frame
 */
static void fc_lport_recv_rlir_req(struct fc_lport *lport, struct fc_frame *fp)
{}

/**
 * fc_lport_recv_echo_req() - Handle received ECHO request
 * @lport: The local port receiving the ECHO
 * @in_fp: ECHO request frame
 */
static void fc_lport_recv_echo_req(struct fc_lport *lport,
				   struct fc_frame *in_fp)
{}

/**
 * fc_lport_recv_rnid_req() - Handle received Request Node ID data request
 * @lport: The local port receiving the RNID
 * @in_fp: The RNID request frame
 */
static void fc_lport_recv_rnid_req(struct fc_lport *lport,
				   struct fc_frame *in_fp)
{}

/**
 * fc_lport_recv_logo_req() - Handle received fabric LOGO request
 * @lport: The local port receiving the LOGO
 * @fp:	   The LOGO request frame
 */
static void fc_lport_recv_logo_req(struct fc_lport *lport, struct fc_frame *fp)
{}

/**
 * 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)
{}
EXPORT_SYMBOL();

/**
 * __fc_linkup() - Handler for transport linkup events
 * @lport: The lport whose link is up
 */
void __fc_linkup(struct fc_lport *lport)
{}

/**
 * fc_linkup() - Handler for transport linkup events
 * @lport: The local port whose link is up
 */
void fc_linkup(struct fc_lport *lport)
{}
EXPORT_SYMBOL();

/**
 * __fc_linkdown() - Handler for transport linkdown events
 * @lport: The lport whose link is down
 */
void __fc_linkdown(struct fc_lport *lport)
{}

/**
 * fc_linkdown() - Handler for transport linkdown events
 * @lport: The local port whose link is down
 */
void fc_linkdown(struct fc_lport *lport)
{}
EXPORT_SYMBOL();

/**
 * 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)
{}
EXPORT_SYMBOL();

/**
 * 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)
{}
EXPORT_SYMBOL();

/**
 * 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)
{}
EXPORT_SYMBOL();

/**
 * fc_lport_disc_callback() - Callback for discovery events
 * @lport: The local port receiving the event
 * @event: The discovery event
 */
static void fc_lport_disc_callback(struct fc_lport *lport,
				   enum fc_disc_event event)
{}

/**
 * fc_lport_enter_ready() - Enter the ready state and start discovery
 * @lport: The local port that is ready
 */
static void fc_lport_enter_ready(struct fc_lport *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.
 */
static void fc_lport_set_port_id(struct fc_lport *lport, u32 port_id,
				 struct fc_frame *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)
{}
EXPORT_SYMBOL();

/**
 * 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.
 */
static void fc_lport_recv_flogi_req(struct fc_lport *lport,
				    struct fc_frame *rx_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.
 */
static void fc_lport_recv_els_req(struct fc_lport *lport,
				  struct fc_frame *fp)
{}

static int fc_lport_els_prli(struct fc_rport_priv *rdata, u32 spp_len,
			     const struct fc_els_spp *spp_in,
			     struct fc_els_spp *spp_out)
{}

struct fc4_prov fc_lport_els_prov =;

/**
 * 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)
{}
EXPORT_SYMBOL();

/**
 * 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)
{}
EXPORT_SYMBOL();

/**
 * fc_lport_reset_locked() - Reset the local port w/ the lport lock held
 * @lport: The local port to be reset
 */
static void fc_lport_reset_locked(struct fc_lport *lport)
{}

/**
 * fc_lport_enter_reset() - Reset the local port
 * @lport: The local port to be reset
 */
static void fc_lport_enter_reset(struct fc_lport *lport)
{}

/**
 * fc_lport_enter_disabled() - Disable the local port
 * @lport: The local port to be reset
 */
static void fc_lport_enter_disabled(struct fc_lport *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.
 */
static void fc_lport_error(struct fc_lport *lport, struct fc_frame *fp)
{}

/**
 * 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.
 */
static void fc_lport_ns_resp(struct fc_seq *sp, struct fc_frame *fp,
			     void *lp_arg)
{}

/**
 * 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.
 */
static void fc_lport_ms_resp(struct fc_seq *sp, struct fc_frame *fp,
			     void *lp_arg)
{}

/**
 * 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.
 */
static void fc_lport_scr_resp(struct fc_seq *sp, struct fc_frame *fp,
			      void *lp_arg)
{}

/**
 * fc_lport_enter_scr() - Send a SCR (State Change Register) request
 * @lport: The local port to register for state changes
 */
static void fc_lport_enter_scr(struct fc_lport *lport)
{}

/**
 * fc_lport_enter_ns() - register some object with the name server
 * @lport: Fibre Channel local port to register
 * @state: Local port state
 */
static void fc_lport_enter_ns(struct fc_lport *lport, enum fc_lport_state state)
{}

static struct fc_rport_operations fc_lport_rport_ops =;

/**
 * fc_lport_enter_dns() - Create a fc_rport for the name server
 * @lport: The local port requesting a remote port for the name server
 */
static void fc_lport_enter_dns(struct fc_lport *lport)
{}

/**
 * fc_lport_enter_ms() - management server commands
 * @lport: Fibre Channel local port to register
 * @state: Local port state
 */
static void fc_lport_enter_ms(struct fc_lport *lport, enum fc_lport_state state)
{}

/**
 * fc_lport_enter_fdmi() - Create a fc_rport for the management server
 * @lport: The local port requesting a remote port for the management server
 */
static void fc_lport_enter_fdmi(struct fc_lport *lport)
{}

/**
 * fc_lport_timeout() - Handler for the retry_work timer
 * @work: The work struct of the local port
 */
static void fc_lport_timeout(struct work_struct *work)
{}

/**
 * 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)
{}
EXPORT_SYMBOL();

/**
 * fc_lport_enter_logo() - Logout of the fabric
 * @lport: The local port to be logged out
 */
static void fc_lport_enter_logo(struct fc_lport *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)
{}
EXPORT_SYMBOL();

/**
 * fc_lport_enter_flogi() - Send a FLOGI request to the fabric manager
 * @lport: Fibre Channel local port to be logged in to the fabric
 */
static void fc_lport_enter_flogi(struct fc_lport *lport)
{}

/**
 * fc_lport_config() - Configure a fc_lport
 * @lport: The local port to be configured
 */
int fc_lport_config(struct fc_lport *lport)
{}
EXPORT_SYMBOL();

/**
 * 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)
{}
EXPORT_SYMBOL();

/**
 * 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
 */
static void fc_lport_bsg_resp(struct fc_seq *sp, struct fc_frame *fp,
			      void *info_arg)
{}

/**
 * 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)
 */
static int fc_lport_els_request(struct bsg_job *job,
				struct fc_lport *lport,
				u32 did, u32 tov)
{}

/**
 * fc_lport_ct_request() - Send CT Passthrough request
 * @job:   The BSG Passthrough job
 * @lport: The local port sending the request
 * @did:   The destination FC-ID
 * @tov:   The timeout period to wait for the response
 */
static int fc_lport_ct_request(struct bsg_job *job,
			       struct fc_lport *lport, u32 did, u32 tov)
{}

/**
 * fc_lport_bsg_request() - The common entry point for sending
 *			    FC Passthrough requests
 * @job: The BSG passthrough job
 */
int fc_lport_bsg_request(struct bsg_job *job)
{}
EXPORT_SYMBOL();