// SPDX-License-Identifier: GPL-2.0-only /* * Copyright(c) 2007 - 2008 Intel Corporation. All rights reserved. * * Maintained at www.Open-FCoE.org */ /* * RPORT GENERAL INFO * * This file contains all processing regarding fc_rports. It contains the * rport state machine and does all rport interaction with the transport class. * There should be no other places in libfc that interact directly with the * transport class in regards to adding and deleting rports. * * fc_rport's represent N_Port's within the fabric. */ /* * RPORT LOCKING * * The rport should never hold the rport mutex and then attempt to acquire * either the lport or disc mutexes. The rport's mutex is considered lesser * than both the lport's mutex and the disc mutex. Refer to fc_lport.c for * more comments on the hierarchy. * * The locking strategy is similar to the lport's strategy. The lock protects * the rport's states and is held and released by the entry points to the rport * block. All _enter_* functions correspond to rport states and expect the rport * mutex to be locked before calling them. This means that rports only handle * one request or response at a time, since they're not critical for the I/O * path this potential over-use of the mutex is acceptable. */ /* * RPORT REFERENCE COUNTING * * A rport reference should be taken when: * - an rport is allocated * - a workqueue item is scheduled * - an ELS request is send * The reference should be dropped when: * - the workqueue function has finished * - the ELS response is handled * - an rport is removed */ #include <linux/kernel.h> #include <linux/spinlock.h> #include <linux/interrupt.h> #include <linux/slab.h> #include <linux/rcupdate.h> #include <linux/timer.h> #include <linux/workqueue.h> #include <linux/export.h> #include <linux/rculist.h> #include <linux/unaligned.h> #include <scsi/libfc.h> #include "fc_encode.h" #include "fc_libfc.h" static struct workqueue_struct *rport_event_queue; static void fc_rport_enter_flogi(struct fc_rport_priv *); static void fc_rport_enter_plogi(struct fc_rport_priv *); static void fc_rport_enter_prli(struct fc_rport_priv *); static void fc_rport_enter_rtv(struct fc_rport_priv *); static void fc_rport_enter_ready(struct fc_rport_priv *); static void fc_rport_enter_logo(struct fc_rport_priv *); static void fc_rport_enter_adisc(struct fc_rport_priv *); static void fc_rport_recv_plogi_req(struct fc_lport *, struct fc_frame *); static void fc_rport_recv_prli_req(struct fc_rport_priv *, struct fc_frame *); static void fc_rport_recv_prlo_req(struct fc_rport_priv *, struct fc_frame *); static void fc_rport_recv_logo_req(struct fc_lport *, struct fc_frame *); static void fc_rport_timeout(struct work_struct *); static void fc_rport_error(struct fc_rport_priv *, int); static void fc_rport_error_retry(struct fc_rport_priv *, int); static void fc_rport_work(struct work_struct *); static const char *fc_rport_state_names[] = …; /** * fc_rport_lookup() - Lookup a remote port by port_id * @lport: The local port to lookup the remote port on * @port_id: The remote port ID to look up * * The reference count of the fc_rport_priv structure is * increased by one. */ struct fc_rport_priv *fc_rport_lookup(const struct fc_lport *lport, u32 port_id) { … } EXPORT_SYMBOL(…); /** * fc_rport_create() - Create a new remote port * @lport: The local port this remote port will be associated with * @port_id: The identifiers for the new remote port * * The remote port will start in the INIT state. */ struct fc_rport_priv *fc_rport_create(struct fc_lport *lport, u32 port_id) { … } EXPORT_SYMBOL(…); /** * fc_rport_destroy() - Free a remote port after last reference is released * @kref: The remote port's kref */ void fc_rport_destroy(struct kref *kref) { … } EXPORT_SYMBOL(…); /** * fc_rport_state() - Return a string identifying the remote port's state * @rdata: The remote port */ static const char *fc_rport_state(struct fc_rport_priv *rdata) { … } /** * fc_set_rport_loss_tmo() - Set the remote port loss timeout * @rport: The remote port that gets a new timeout value * @timeout: The new timeout value (in seconds) */ void fc_set_rport_loss_tmo(struct fc_rport *rport, u32 timeout) { … } EXPORT_SYMBOL(…); /** * fc_plogi_get_maxframe() - Get the maximum payload from the common service * parameters in a FLOGI frame * @flp: The FLOGI or PLOGI payload * @maxval: The maximum frame size upper limit; this may be less than what * is in the service parameters */ static unsigned int fc_plogi_get_maxframe(struct fc_els_flogi *flp, unsigned int maxval) { … } /** * fc_rport_state_enter() - Change the state of a remote port * @rdata: The remote port whose state should change * @new: The new state */ static void fc_rport_state_enter(struct fc_rport_priv *rdata, enum fc_rport_state new) { … } /** * fc_rport_work() - Handler for remote port events in the rport_event_queue * @work: Handle to the remote port being dequeued * * Reference counting: drops kref on return */ static void fc_rport_work(struct work_struct *work) { … } /** * fc_rport_login() - Start the remote port login state machine * @rdata: The remote port to be logged in to * * Initiates the RP state machine. It is called from the LP module. * This function will issue the following commands to the N_Port * identified by the FC ID provided. * * - PLOGI * - PRLI * - RTV * * Locking Note: Called without the rport lock held. This * function will hold the rport lock, call an _enter_* * function and then unlock the rport. * * This indicates the intent to be logged into the remote port. * If it appears we are already logged in, ADISC is used to verify * the setup. */ int fc_rport_login(struct fc_rport_priv *rdata) { … } EXPORT_SYMBOL(…); /** * fc_rport_enter_delete() - Schedule a remote port to be deleted * @rdata: The remote port to be deleted * @event: The event to report as the reason for deletion * * Allow state change into DELETE only once. * * Call queue_work only if there's no event already pending. * Set the new event so that the old pending event will not occur. * Since we have the mutex, even if fc_rport_work() is already started, * it'll see the new event. * * Reference counting: does not modify kref */ static void fc_rport_enter_delete(struct fc_rport_priv *rdata, enum fc_rport_event event) { … } /** * fc_rport_logoff() - Logoff and remove a remote port * @rdata: The remote port to be logged off of * * Locking Note: Called without the rport lock held. This * function will hold the rport lock, call an _enter_* * function and then unlock the rport. */ int fc_rport_logoff(struct fc_rport_priv *rdata) { … } EXPORT_SYMBOL(…); /** * fc_rport_enter_ready() - Transition to the RPORT_ST_READY state * @rdata: The remote port that is ready * * Reference counting: schedules workqueue, does not modify kref */ static void fc_rport_enter_ready(struct fc_rport_priv *rdata) { … } /** * fc_rport_timeout() - Handler for the retry_work timer * @work: Handle to the remote port that has timed out * * Locking Note: Called without the rport lock held. This * function will hold the rport lock, call an _enter_* * function and then unlock the rport. * * Reference counting: Drops kref on return. */ static void fc_rport_timeout(struct work_struct *work) { … } /** * fc_rport_error() - Error handler, called once retries have been exhausted * @rdata: The remote port the error is happened on * @err: The error code * * Reference counting: does not modify kref */ static void fc_rport_error(struct fc_rport_priv *rdata, int err) { … } /** * fc_rport_error_retry() - Handler for remote port state retries * @rdata: The remote port whose state is to be retried * @err: The error code * * If the error was an exchange timeout retry immediately, * otherwise wait for E_D_TOV. * * Reference counting: increments kref when scheduling retry_work */ static void fc_rport_error_retry(struct fc_rport_priv *rdata, int err) { … } /** * fc_rport_login_complete() - Handle parameters and completion of p-mp login. * @rdata: The remote port which we logged into or which logged into us. * @fp: The FLOGI or PLOGI request or response frame * * Returns non-zero error if a problem is detected with the frame. * Does not free the frame. * * This is only used in point-to-multipoint mode for FIP currently. */ static int fc_rport_login_complete(struct fc_rport_priv *rdata, struct fc_frame *fp) { … } /** * fc_rport_flogi_resp() - Handle response to FLOGI request for p-mp mode * @sp: The sequence that the FLOGI was on * @fp: The FLOGI response frame * @rp_arg: The remote port that received the FLOGI response */ static void fc_rport_flogi_resp(struct fc_seq *sp, struct fc_frame *fp, void *rp_arg) { … } /** * fc_rport_enter_flogi() - Send a FLOGI request to the remote port for p-mp * @rdata: The remote port to send a FLOGI to * * Reference counting: increments kref when sending ELS */ static void fc_rport_enter_flogi(struct fc_rport_priv *rdata) { … } /** * fc_rport_recv_flogi_req() - Handle Fabric Login (FLOGI) request in p-mp mode * @lport: The local port that received the PLOGI request * @rx_fp: The PLOGI request frame * * Reference counting: drops kref on return */ static void fc_rport_recv_flogi_req(struct fc_lport *lport, struct fc_frame *rx_fp) { … } /** * fc_rport_plogi_resp() - Handler for ELS PLOGI responses * @sp: The sequence the PLOGI is on * @fp: The PLOGI response frame * @rdata_arg: The remote port that sent the PLOGI response * * Locking Note: This function will be called without the rport lock * held, but it will lock, call an _enter_* function or fc_rport_error * and then unlock the rport. */ static void fc_rport_plogi_resp(struct fc_seq *sp, struct fc_frame *fp, void *rdata_arg) { … } static bool fc_rport_compatible_roles(struct fc_lport *lport, struct fc_rport_priv *rdata) { … } /** * fc_rport_enter_plogi() - Send Port Login (PLOGI) request * @rdata: The remote port to send a PLOGI to * * Reference counting: increments kref when sending ELS */ static void fc_rport_enter_plogi(struct fc_rport_priv *rdata) { … } /** * fc_rport_prli_resp() - Process Login (PRLI) response handler * @sp: The sequence the PRLI response was on * @fp: The PRLI response frame * @rdata_arg: The remote port that sent the PRLI response * * Locking Note: This function will be called without the rport lock * held, but it will lock, call an _enter_* function or fc_rport_error * and then unlock the rport. */ static void fc_rport_prli_resp(struct fc_seq *sp, struct fc_frame *fp, void *rdata_arg) { … } /** * fc_rport_enter_prli() - Send Process Login (PRLI) request * @rdata: The remote port to send the PRLI request to * * Reference counting: increments kref when sending ELS */ static void fc_rport_enter_prli(struct fc_rport_priv *rdata) { … } /** * fc_rport_rtv_resp() - Handler for Request Timeout Value (RTV) responses * @sp: The sequence the RTV was on * @fp: The RTV response frame * @rdata_arg: The remote port that sent the RTV response * * Many targets don't seem to support this. * * Locking Note: This function will be called without the rport lock * held, but it will lock, call an _enter_* function or fc_rport_error * and then unlock the rport. */ static void fc_rport_rtv_resp(struct fc_seq *sp, struct fc_frame *fp, void *rdata_arg) { … } /** * fc_rport_enter_rtv() - Send Request Timeout Value (RTV) request * @rdata: The remote port to send the RTV request to * * Reference counting: increments kref when sending ELS */ static void fc_rport_enter_rtv(struct fc_rport_priv *rdata) { … } /** * fc_rport_recv_rtv_req() - Handler for Read Timeout Value (RTV) requests * @rdata: The remote port that sent the RTV request * @in_fp: The RTV request frame */ static void fc_rport_recv_rtv_req(struct fc_rport_priv *rdata, struct fc_frame *in_fp) { … } /** * fc_rport_logo_resp() - Handler for logout (LOGO) responses * @sp: The sequence the LOGO was on * @fp: The LOGO response frame * @rdata_arg: The remote port */ static void fc_rport_logo_resp(struct fc_seq *sp, struct fc_frame *fp, void *rdata_arg) { … } /** * fc_rport_enter_logo() - Send a logout (LOGO) request * @rdata: The remote port to send the LOGO request to * * Reference counting: increments kref when sending ELS */ static void fc_rport_enter_logo(struct fc_rport_priv *rdata) { … } /** * fc_rport_adisc_resp() - Handler for Address Discovery (ADISC) responses * @sp: The sequence the ADISC response was on * @fp: The ADISC response frame * @rdata_arg: The remote port that sent the ADISC response * * Locking Note: This function will be called without the rport lock * held, but it will lock, call an _enter_* function or fc_rport_error * and then unlock the rport. */ static void fc_rport_adisc_resp(struct fc_seq *sp, struct fc_frame *fp, void *rdata_arg) { … } /** * fc_rport_enter_adisc() - Send Address Discover (ADISC) request * @rdata: The remote port to send the ADISC request to * * Reference counting: increments kref when sending ELS */ static void fc_rport_enter_adisc(struct fc_rport_priv *rdata) { … } /** * fc_rport_recv_adisc_req() - Handler for Address Discovery (ADISC) requests * @rdata: The remote port that sent the ADISC request * @in_fp: The ADISC request frame */ static void fc_rport_recv_adisc_req(struct fc_rport_priv *rdata, struct fc_frame *in_fp) { … } /** * fc_rport_recv_rls_req() - Handle received Read Link Status request * @rdata: The remote port that sent the RLS request * @rx_fp: The PRLI request frame */ static void fc_rport_recv_rls_req(struct fc_rport_priv *rdata, struct fc_frame *rx_fp) { … } /** * fc_rport_recv_els_req() - Handler for validated ELS requests * @lport: The local port that received the ELS request * @fp: The ELS request frame * * Handle incoming ELS requests that require port login. * The ELS opcode has already been validated by the caller. * * Reference counting: does not modify kref */ static void fc_rport_recv_els_req(struct fc_lport *lport, struct fc_frame *fp) { … } /** * fc_rport_recv_req() - Handler for requests * @lport: The local port that received the request * @fp: The request frame * * Reference counting: does not modify kref */ void fc_rport_recv_req(struct fc_lport *lport, struct fc_frame *fp) { … } EXPORT_SYMBOL(…); /** * fc_rport_recv_plogi_req() - Handler for Port Login (PLOGI) requests * @lport: The local port that received the PLOGI request * @rx_fp: The PLOGI request frame * * Reference counting: increments kref on return */ static void fc_rport_recv_plogi_req(struct fc_lport *lport, struct fc_frame *rx_fp) { … } /** * fc_rport_recv_prli_req() - Handler for process login (PRLI) requests * @rdata: The remote port that sent the PRLI request * @rx_fp: The PRLI request frame */ static void fc_rport_recv_prli_req(struct fc_rport_priv *rdata, struct fc_frame *rx_fp) { … } /** * fc_rport_recv_prlo_req() - Handler for process logout (PRLO) requests * @rdata: The remote port that sent the PRLO request * @rx_fp: The PRLO request frame */ static void fc_rport_recv_prlo_req(struct fc_rport_priv *rdata, struct fc_frame *rx_fp) { … } /** * fc_rport_recv_logo_req() - Handler for logout (LOGO) requests * @lport: The local port that received the LOGO request * @fp: The LOGO request frame * * Reference counting: drops kref on return */ static void fc_rport_recv_logo_req(struct fc_lport *lport, struct fc_frame *fp) { … } /** * fc_rport_flush_queue() - Flush the rport_event_queue */ void fc_rport_flush_queue(void) { … } EXPORT_SYMBOL(…); /** * fc_rport_fcp_prli() - Handle incoming PRLI for the FCP initiator. * @rdata: remote port private * @spp_len: service parameter page length * @rspp: received service parameter page * @spp: response service parameter page * * Returns the value for the response code to be placed in spp_flags; * Returns 0 if not an initiator. */ static int fc_rport_fcp_prli(struct fc_rport_priv *rdata, u32 spp_len, const struct fc_els_spp *rspp, struct fc_els_spp *spp) { … } /* * FC-4 provider ops for FCP initiator. */ struct fc4_prov fc_rport_fcp_init = …; /** * fc_rport_t0_prli() - Handle incoming PRLI parameters for type 0 * @rdata: remote port private * @spp_len: service parameter page length * @rspp: received service parameter page * @spp: response service parameter page */ static int fc_rport_t0_prli(struct fc_rport_priv *rdata, u32 spp_len, const struct fc_els_spp *rspp, struct fc_els_spp *spp) { … } /* * FC-4 provider ops for type 0 service parameters. * * This handles the special case of type 0 which is always successful * but doesn't do anything otherwise. */ struct fc4_prov fc_rport_t0_prov = …; /** * fc_setup_rport() - Initialize the rport_event_queue */ int fc_setup_rport(void) { … } /** * fc_destroy_rport() - Destroy the rport_event_queue */ void fc_destroy_rport(void) { … } /** * fc_rport_terminate_io() - Stop all outstanding I/O on a remote port * @rport: The remote port whose I/O should be terminated */ void fc_rport_terminate_io(struct fc_rport *rport) { … } EXPORT_SYMBOL(…);