// 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(…);