// SPDX-License-Identifier: GPL-2.0-only /* * Copyright(c) 2007 Intel Corporation. All rights reserved. * Copyright(c) 2008 Red Hat, Inc. All rights reserved. * Copyright(c) 2008 Mike Christie * * Maintained at www.Open-FCoE.org */ /* * Fibre Channel exchange and sequence handling. */ #include <linux/timer.h> #include <linux/slab.h> #include <linux/err.h> #include <linux/export.h> #include <linux/log2.h> #include <scsi/fc/fc_fc2.h> #include <scsi/libfc.h> #include "fc_libfc.h" u16 fc_cpu_mask; /* cpu mask for possible cpus */ EXPORT_SYMBOL(…); static u16 fc_cpu_order; /* 2's power to represent total possible cpus */ static struct kmem_cache *fc_em_cachep; /* cache for exchanges */ static struct workqueue_struct *fc_exch_workqueue; /* * Structure and function definitions for managing Fibre Channel Exchanges * and Sequences. * * The three primary structures used here are fc_exch_mgr, fc_exch, and fc_seq. * * fc_exch_mgr holds the exchange state for an N port * * fc_exch holds state for one exchange and links to its active sequence. * * fc_seq holds the state for an individual sequence. */ /** * struct fc_exch_pool - Per cpu exchange pool * @next_index: Next possible free exchange index * @total_exches: Total allocated exchanges * @lock: Exch pool lock * @ex_list: List of exchanges * @left: Cache of free slot in exch array * @right: Cache of free slot in exch array * * This structure manages per cpu exchanges in array of exchange pointers. * This array is allocated followed by struct fc_exch_pool memory for * assigned range of exchanges to per cpu pool. */ struct fc_exch_pool { … } ____cacheline_aligned_in_smp; /** * struct fc_exch_mgr - The Exchange Manager (EM). * @class: Default class for new sequences * @kref: Reference counter * @min_xid: Minimum exchange ID * @max_xid: Maximum exchange ID * @ep_pool: Reserved exchange pointers * @pool_max_index: Max exch array index in exch pool * @pool: Per cpu exch pool * @lport: Local exchange port * @stats: Statistics structure * * This structure is the center for creating exchanges and sequences. * It manages the allocation of exchange IDs. */ struct fc_exch_mgr { … }; /** * struct fc_exch_mgr_anchor - primary structure for list of EMs * @ema_list: Exchange Manager Anchor list * @mp: Exchange Manager associated with this anchor * @match: Routine to determine if this anchor's EM should be used * * When walking the list of anchors the match routine will be called * for each anchor to determine if that EM should be used. The last * anchor in the list will always match to handle any exchanges not * handled by other EMs. The non-default EMs would be added to the * anchor list by HW that provides offloads. */ struct fc_exch_mgr_anchor { … }; static void fc_exch_rrq(struct fc_exch *); static void fc_seq_ls_acc(struct fc_frame *); static void fc_seq_ls_rjt(struct fc_frame *, enum fc_els_rjt_reason, enum fc_els_rjt_explan); static void fc_exch_els_rec(struct fc_frame *); static void fc_exch_els_rrq(struct fc_frame *); /* * Internal implementation notes. * * The exchange manager is one by default in libfc but LLD may choose * to have one per CPU. The sequence manager is one per exchange manager * and currently never separated. * * Section 9.8 in FC-FS-2 specifies: "The SEQ_ID is a one-byte field * assigned by the Sequence Initiator that shall be unique for a specific * D_ID and S_ID pair while the Sequence is open." Note that it isn't * qualified by exchange ID, which one might think it would be. * In practice this limits the number of open sequences and exchanges to 256 * per session. For most targets we could treat this limit as per exchange. * * The exchange and its sequence are freed when the last sequence is received. * It's possible for the remote port to leave an exchange open without * sending any sequences. * * Notes on reference counts: * * Exchanges are reference counted and exchange gets freed when the reference * count becomes zero. * * Timeouts: * Sequences are timed out for E_D_TOV and R_A_TOV. * * Sequence event handling: * * The following events may occur on initiator sequences: * * Send. * For now, the whole thing is sent. * Receive ACK * This applies only to class F. * The sequence is marked complete. * ULP completion. * The upper layer calls fc_exch_done() when done * with exchange and sequence tuple. * RX-inferred completion. * When we receive the next sequence on the same exchange, we can * retire the previous sequence ID. (XXX not implemented). * Timeout. * R_A_TOV frees the sequence ID. If we're waiting for ACK, * E_D_TOV causes abort and calls upper layer response handler * with FC_EX_TIMEOUT error. * Receive RJT * XXX defer. * Send ABTS * On timeout. * * The following events may occur on recipient sequences: * * Receive * Allocate sequence for first frame received. * Hold during receive handler. * Release when final frame received. * Keep status of last N of these for the ELS RES command. XXX TBD. * Receive ABTS * Deallocate sequence * Send RJT * Deallocate * * For now, we neglect conditions where only part of a sequence was * received or transmitted, or where out-of-order receipt is detected. */ /* * Locking notes: * * The EM code run in a per-CPU worker thread. * * To protect against concurrency between a worker thread code and timers, * sequence allocation and deallocation must be locked. * - exchange refcnt can be done atomicly without locks. * - sequence allocation must be locked by exch lock. * - If the EM pool lock and ex_lock must be taken at the same time, then the * EM pool lock must be taken before the ex_lock. */ /* * opcode names for debugging. */ static char *fc_exch_rctl_names[] = …; /** * fc_exch_name_lookup() - Lookup name by opcode * @op: Opcode to be looked up * @table: Opcode/name table * @max_index: Index not to be exceeded * * This routine is used to determine a human-readable string identifying * a R_CTL opcode. */ static inline const char *fc_exch_name_lookup(unsigned int op, char **table, unsigned int max_index) { … } /** * fc_exch_rctl_name() - Wrapper routine for fc_exch_name_lookup() * @op: The opcode to be looked up */ static const char *fc_exch_rctl_name(unsigned int op) { … } /** * fc_exch_hold() - Increment an exchange's reference count * @ep: Echange to be held */ static inline void fc_exch_hold(struct fc_exch *ep) { … } /** * fc_exch_setup_hdr() - Initialize a FC header by initializing some fields * and determine SOF and EOF. * @ep: The exchange to that will use the header * @fp: The frame whose header is to be modified * @f_ctl: F_CTL bits that will be used for the frame header * * The fields initialized by this routine are: fh_ox_id, fh_rx_id, * fh_seq_id, fh_seq_cnt and the SOF and EOF. */ static void fc_exch_setup_hdr(struct fc_exch *ep, struct fc_frame *fp, u32 f_ctl) { … } /** * fc_exch_release() - Decrement an exchange's reference count * @ep: Exchange to be released * * If the reference count reaches zero and the exchange is complete, * it is freed. */ static void fc_exch_release(struct fc_exch *ep) { … } /** * fc_exch_timer_cancel() - cancel exch timer * @ep: The exchange whose timer to be canceled */ static inline void fc_exch_timer_cancel(struct fc_exch *ep) { … } /** * fc_exch_timer_set_locked() - Start a timer for an exchange w/ the * the exchange lock held * @ep: The exchange whose timer will start * @timer_msec: The timeout period * * Used for upper level protocols to time out the exchange. * The timer is cancelled when it fires or when the exchange completes. */ static inline void fc_exch_timer_set_locked(struct fc_exch *ep, unsigned int timer_msec) { … } /** * fc_exch_timer_set() - Lock the exchange and set the timer * @ep: The exchange whose timer will start * @timer_msec: The timeout period */ static void fc_exch_timer_set(struct fc_exch *ep, unsigned int timer_msec) { … } /** * fc_exch_done_locked() - Complete an exchange with the exchange lock held * @ep: The exchange that is complete * * Note: May sleep if invoked from outside a response handler. */ static int fc_exch_done_locked(struct fc_exch *ep) { … } static struct fc_exch fc_quarantine_exch; /** * fc_exch_ptr_get() - Return an exchange from an exchange pool * @pool: Exchange Pool to get an exchange from * @index: Index of the exchange within the pool * * Use the index to get an exchange from within an exchange pool. exches * will point to an array of exchange pointers. The index will select * the exchange within the array. */ static inline struct fc_exch *fc_exch_ptr_get(struct fc_exch_pool *pool, u16 index) { … } /** * fc_exch_ptr_set() - Assign an exchange to a slot in an exchange pool * @pool: The pool to assign the exchange to * @index: The index in the pool where the exchange will be assigned * @ep: The exchange to assign to the pool */ static inline void fc_exch_ptr_set(struct fc_exch_pool *pool, u16 index, struct fc_exch *ep) { … } /** * fc_exch_delete() - Delete an exchange * @ep: The exchange to be deleted */ static void fc_exch_delete(struct fc_exch *ep) { … } static int fc_seq_send_locked(struct fc_lport *lport, struct fc_seq *sp, struct fc_frame *fp) { … } /** * fc_seq_send() - Send a frame using existing sequence/exchange pair * @lport: The local port that the exchange will be sent on * @sp: The sequence to be sent * @fp: The frame to be sent on the exchange * * Note: The frame will be freed either by a direct call to fc_frame_free(fp) * or indirectly by calling libfc_function_template.frame_send(). */ int fc_seq_send(struct fc_lport *lport, struct fc_seq *sp, struct fc_frame *fp) { … } EXPORT_SYMBOL(…); /** * fc_seq_alloc() - Allocate a sequence for a given exchange * @ep: The exchange to allocate a new sequence for * @seq_id: The sequence ID to be used * * We don't support multiple originated sequences on the same exchange. * By implication, any previously originated sequence on this exchange * is complete, and we reallocate the same sequence. */ static struct fc_seq *fc_seq_alloc(struct fc_exch *ep, u8 seq_id) { … } /** * fc_seq_start_next_locked() - Allocate a new sequence on the same * exchange as the supplied sequence * @sp: The sequence/exchange to get a new sequence for */ static struct fc_seq *fc_seq_start_next_locked(struct fc_seq *sp) { … } /** * fc_seq_start_next() - Lock the exchange and get a new sequence * for a given sequence/exchange pair * @sp: The sequence/exchange to get a new exchange for */ struct fc_seq *fc_seq_start_next(struct fc_seq *sp) { … } EXPORT_SYMBOL(…); /* * Set the response handler for the exchange associated with a sequence. * * Note: May sleep if invoked from outside a response handler. */ void fc_seq_set_resp(struct fc_seq *sp, void (*resp)(struct fc_seq *, struct fc_frame *, void *), void *arg) { … } EXPORT_SYMBOL(…); /** * fc_exch_abort_locked() - Abort an exchange * @ep: The exchange to be aborted * @timer_msec: The period of time to wait before aborting * * Abort an exchange and sequence. Generally called because of a * exchange timeout or an abort from the upper layer. * * A timer_msec can be specified for abort timeout, if non-zero * timer_msec value is specified then exchange resp handler * will be called with timeout error if no response to abort. * * Locking notes: Called with exch lock held * * Return value: 0 on success else error code */ static int fc_exch_abort_locked(struct fc_exch *ep, unsigned int timer_msec) { … } /** * fc_seq_exch_abort() - Abort an exchange and sequence * @req_sp: The sequence to be aborted * @timer_msec: The period of time to wait before aborting * * Generally called because of a timeout or an abort from the upper layer. * * Return value: 0 on success else error code */ int fc_seq_exch_abort(const struct fc_seq *req_sp, unsigned int timer_msec) { … } /** * fc_invoke_resp() - invoke ep->resp() * @ep: The exchange to be operated on * @fp: The frame pointer to pass through to ->resp() * @sp: The sequence pointer to pass through to ->resp() * * Notes: * It is assumed that after initialization finished (this means the * first unlock of ex_lock after fc_exch_alloc()) ep->resp and ep->arg are * modified only via fc_seq_set_resp(). This guarantees that none of these * two variables changes if ep->resp_active > 0. * * If an fc_seq_set_resp() call is busy modifying ep->resp and ep->arg when * this function is invoked, the first spin_lock_bh() call in this function * will wait until fc_seq_set_resp() has finished modifying these variables. * * Since fc_exch_done() invokes fc_seq_set_resp() it is guaranteed that that * ep->resp() won't be invoked after fc_exch_done() has returned. * * The response handler itself may invoke fc_exch_done(), which will clear the * ep->resp pointer. * * Return value: * Returns true if and only if ep->resp has been invoked. */ static bool fc_invoke_resp(struct fc_exch *ep, struct fc_seq *sp, struct fc_frame *fp) { … } /** * fc_exch_timeout() - Handle exchange timer expiration * @work: The work_struct identifying the exchange that timed out */ static void fc_exch_timeout(struct work_struct *work) { … } /** * fc_exch_em_alloc() - Allocate an exchange from a specified EM. * @lport: The local port that the exchange is for * @mp: The exchange manager that will allocate the exchange * * Returns pointer to allocated fc_exch with exch lock held. */ static struct fc_exch *fc_exch_em_alloc(struct fc_lport *lport, struct fc_exch_mgr *mp) { … } /** * fc_exch_alloc() - Allocate an exchange from an EM on a * local port's list of EMs. * @lport: The local port that will own the exchange * @fp: The FC frame that the exchange will be for * * This function walks the list of exchange manager(EM) * anchors to select an EM for a new exchange allocation. The * EM is selected when a NULL match function pointer is encountered * or when a call to a match function returns true. */ static struct fc_exch *fc_exch_alloc(struct fc_lport *lport, struct fc_frame *fp) { … } /** * fc_exch_find() - Lookup and hold an exchange * @mp: The exchange manager to lookup the exchange from * @xid: The XID of the exchange to look up */ static struct fc_exch *fc_exch_find(struct fc_exch_mgr *mp, u16 xid) { … } /** * fc_exch_done() - Indicate that an exchange/sequence tuple is complete and * the memory allocated for the related objects may be freed. * @sp: The sequence that has completed * * Note: May sleep if invoked from outside a response handler. */ void fc_exch_done(struct fc_seq *sp) { … } EXPORT_SYMBOL(…); /** * fc_exch_resp() - Allocate a new exchange for a response frame * @lport: The local port that the exchange was for * @mp: The exchange manager to allocate the exchange from * @fp: The response frame * * Sets the responder ID in the frame header. */ static struct fc_exch *fc_exch_resp(struct fc_lport *lport, struct fc_exch_mgr *mp, struct fc_frame *fp) { … } /** * fc_seq_lookup_recip() - Find a sequence where the other end * originated the sequence * @lport: The local port that the frame was sent to * @mp: The Exchange Manager to lookup the exchange from * @fp: The frame associated with the sequence we're looking for * * If fc_pf_rjt_reason is FC_RJT_NONE then this function will have a hold * on the ep that should be released by the caller. */ static enum fc_pf_rjt_reason fc_seq_lookup_recip(struct fc_lport *lport, struct fc_exch_mgr *mp, struct fc_frame *fp) { … } /** * fc_seq_lookup_orig() - Find a sequence where this end * originated the sequence * @mp: The Exchange Manager to lookup the exchange from * @fp: The frame associated with the sequence we're looking for * * Does not hold the sequence for the caller. */ static struct fc_seq *fc_seq_lookup_orig(struct fc_exch_mgr *mp, struct fc_frame *fp) { … } /** * fc_exch_set_addr() - Set the source and destination IDs for an exchange * @ep: The exchange to set the addresses for * @orig_id: The originator's ID * @resp_id: The responder's ID * * Note this must be done before the first sequence of the exchange is sent. */ static void fc_exch_set_addr(struct fc_exch *ep, u32 orig_id, u32 resp_id) { … } /** * fc_seq_els_rsp_send() - Send an ELS response using information from * the existing sequence/exchange. * @fp: The received frame * @els_cmd: The ELS command to be sent * @els_data: The ELS data to be sent * * The received frame is not freed. */ void fc_seq_els_rsp_send(struct fc_frame *fp, enum fc_els_cmd els_cmd, struct fc_seq_els_data *els_data) { … } EXPORT_SYMBOL_GPL(…); /** * fc_seq_send_last() - Send a sequence that is the last in the exchange * @sp: The sequence that is to be sent * @fp: The frame that will be sent on the sequence * @rctl: The R_CTL information to be sent * @fh_type: The frame header type */ static void fc_seq_send_last(struct fc_seq *sp, struct fc_frame *fp, enum fc_rctl rctl, enum fc_fh_type fh_type) { … } /** * fc_seq_send_ack() - Send an acknowledgement that we've received a frame * @sp: The sequence to send the ACK on * @rx_fp: The received frame that is being acknoledged * * Send ACK_1 (or equiv.) indicating we received something. */ static void fc_seq_send_ack(struct fc_seq *sp, const struct fc_frame *rx_fp) { … } /** * fc_exch_send_ba_rjt() - Send BLS Reject * @rx_fp: The frame being rejected * @reason: The reason the frame is being rejected * @explan: The explanation for the rejection * * This is for rejecting BA_ABTS only. */ static void fc_exch_send_ba_rjt(struct fc_frame *rx_fp, enum fc_ba_rjt_reason reason, enum fc_ba_rjt_explan explan) { … } /** * fc_exch_recv_abts() - Handle an incoming ABTS * @ep: The exchange the abort was on * @rx_fp: The ABTS frame * * This would be for target mode usually, but could be due to lost * FCP transfer ready, confirm or RRQ. We always handle this as an * exchange abort, ignoring the parameter. */ static void fc_exch_recv_abts(struct fc_exch *ep, struct fc_frame *rx_fp) { … } /** * fc_seq_assign() - Assign exchange and sequence for incoming request * @lport: The local port that received the request * @fp: The request frame * * On success, the sequence pointer will be returned and also in fr_seq(@fp). * A reference will be held on the exchange/sequence for the caller, which * must call fc_seq_release(). */ struct fc_seq *fc_seq_assign(struct fc_lport *lport, struct fc_frame *fp) { … } EXPORT_SYMBOL(…); /** * fc_seq_release() - Release the hold * @sp: The sequence. */ void fc_seq_release(struct fc_seq *sp) { … } EXPORT_SYMBOL(…); /** * fc_exch_recv_req() - Handler for an incoming request * @lport: The local port that received the request * @mp: The EM that the exchange is on * @fp: The request frame * * This is used when the other end is originating the exchange * and the sequence. */ static void fc_exch_recv_req(struct fc_lport *lport, struct fc_exch_mgr *mp, struct fc_frame *fp) { … } /** * fc_exch_recv_seq_resp() - Handler for an incoming response where the other * end is the originator of the sequence that is a * response to our initial exchange * @mp: The EM that the exchange is on * @fp: The response frame */ static void fc_exch_recv_seq_resp(struct fc_exch_mgr *mp, struct fc_frame *fp) { … } /** * fc_exch_recv_resp() - Handler for a sequence where other end is * responding to our sequence * @mp: The EM that the exchange is on * @fp: The response frame */ static void fc_exch_recv_resp(struct fc_exch_mgr *mp, struct fc_frame *fp) { … } /** * fc_exch_abts_resp() - Handler for a response to an ABT * @ep: The exchange that the frame is on * @fp: The response frame * * This response would be to an ABTS cancelling an exchange or sequence. * The response can be either BA_ACC or BA_RJT */ static void fc_exch_abts_resp(struct fc_exch *ep, struct fc_frame *fp) { … } /** * fc_exch_recv_bls() - Handler for a BLS sequence * @mp: The EM that the exchange is on * @fp: The request frame * * The BLS frame is always a sequence initiated by the remote side. * We may be either the originator or recipient of the exchange. */ static void fc_exch_recv_bls(struct fc_exch_mgr *mp, struct fc_frame *fp) { … } /** * fc_seq_ls_acc() - Accept sequence with LS_ACC * @rx_fp: The received frame, not freed here. * * If this fails due to allocation or transmit congestion, assume the * originator will repeat the sequence. */ static void fc_seq_ls_acc(struct fc_frame *rx_fp) { … } /** * fc_seq_ls_rjt() - Reject a sequence with ELS LS_RJT * @rx_fp: The received frame, not freed here. * @reason: The reason the sequence is being rejected * @explan: The explanation for the rejection * * If this fails due to allocation or transmit congestion, assume the * originator will repeat the sequence. */ static void fc_seq_ls_rjt(struct fc_frame *rx_fp, enum fc_els_rjt_reason reason, enum fc_els_rjt_explan explan) { … } /** * fc_exch_reset() - Reset an exchange * @ep: The exchange to be reset * * Note: May sleep if invoked from outside a response handler. */ static void fc_exch_reset(struct fc_exch *ep) { … } /** * fc_exch_pool_reset() - Reset a per cpu exchange pool * @lport: The local port that the exchange pool is on * @pool: The exchange pool to be reset * @sid: The source ID * @did: The destination ID * * Resets a per cpu exches pool, releasing all of its sequences * and exchanges. If sid is non-zero then reset only exchanges * we sourced from the local port's FID. If did is non-zero then * only reset exchanges destined for the local port's FID. */ static void fc_exch_pool_reset(struct fc_lport *lport, struct fc_exch_pool *pool, u32 sid, u32 did) { … } /** * fc_exch_mgr_reset() - Reset all EMs of a local port * @lport: The local port whose EMs are to be reset * @sid: The source ID * @did: The destination ID * * Reset all EMs associated with a given local port. Release all * sequences and exchanges. If sid is non-zero then reset only the * exchanges sent from the local port's FID. If did is non-zero then * reset only exchanges destined for the local port's FID. */ void fc_exch_mgr_reset(struct fc_lport *lport, u32 sid, u32 did) { … } EXPORT_SYMBOL(…); /** * fc_exch_lookup() - find an exchange * @lport: The local port * @xid: The exchange ID * * Returns exchange pointer with hold for caller, or NULL if not found. */ static struct fc_exch *fc_exch_lookup(struct fc_lport *lport, u32 xid) { … } /** * fc_exch_els_rec() - Handler for ELS REC (Read Exchange Concise) requests * @rfp: The REC frame, not freed here. * * Note that the requesting port may be different than the S_ID in the request. */ static void fc_exch_els_rec(struct fc_frame *rfp) { … } /** * fc_exch_rrq_resp() - Handler for RRQ responses * @sp: The sequence that the RRQ is on * @fp: The RRQ frame * @arg: The exchange that the RRQ is on * * TODO: fix error handler. */ static void fc_exch_rrq_resp(struct fc_seq *sp, struct fc_frame *fp, void *arg) { … } /** * fc_exch_seq_send() - Send a frame using a new exchange and sequence * @lport: The local port to send the frame on * @fp: The frame to be sent * @resp: The response handler for this request * @destructor: The destructor for the exchange * @arg: The argument to be passed to the response handler * @timer_msec: The timeout period for the exchange * * The exchange response handler is set in this routine to resp() * function pointer. It can be called in two scenarios: if a timeout * occurs or if a response frame is received for the exchange. The * fc_frame pointer in response handler will also indicate timeout * as error using IS_ERR related macros. * * The exchange destructor handler is also set in this routine. * The destructor handler is invoked by EM layer when exchange * is about to free, this can be used by caller to free its * resources along with exchange free. * * The arg is passed back to resp and destructor handler. * * The timeout value (in msec) for an exchange is set if non zero * timer_msec argument is specified. The timer is canceled when * it fires or when the exchange is done. The exchange timeout handler * is registered by EM layer. * * The frame pointer with some of the header's fields must be * filled before calling this routine, those fields are: * * - routing control * - FC port did * - FC port sid * - FC header type * - frame control * - parameter or relative offset */ struct fc_seq *fc_exch_seq_send(struct fc_lport *lport, struct fc_frame *fp, void (*resp)(struct fc_seq *, struct fc_frame *fp, void *arg), void (*destructor)(struct fc_seq *, void *), void *arg, u32 timer_msec) { … } EXPORT_SYMBOL(…); /** * fc_exch_rrq() - Send an ELS RRQ (Reinstate Recovery Qualifier) command * @ep: The exchange to send the RRQ on * * This tells the remote port to stop blocking the use of * the exchange and the seq_cnt range. */ static void fc_exch_rrq(struct fc_exch *ep) { … } /** * fc_exch_els_rrq() - Handler for ELS RRQ (Reset Recovery Qualifier) requests * @fp: The RRQ frame, not freed here. */ static void fc_exch_els_rrq(struct fc_frame *fp) { … } /** * fc_exch_update_stats() - update exches stats to lport * @lport: The local port to update exchange manager stats */ void fc_exch_update_stats(struct fc_lport *lport) { … } EXPORT_SYMBOL(…); /** * fc_exch_mgr_add() - Add an exchange manager to a local port's list of EMs * @lport: The local port to add the exchange manager to * @mp: The exchange manager to be added to the local port * @match: The match routine that indicates when this EM should be used */ struct fc_exch_mgr_anchor *fc_exch_mgr_add(struct fc_lport *lport, struct fc_exch_mgr *mp, bool (*match)(struct fc_frame *)) { … } EXPORT_SYMBOL(…); /** * fc_exch_mgr_destroy() - Destroy an exchange manager * @kref: The reference to the EM to be destroyed */ static void fc_exch_mgr_destroy(struct kref *kref) { … } /** * fc_exch_mgr_del() - Delete an EM from a local port's list * @ema: The exchange manager anchor identifying the EM to be deleted */ void fc_exch_mgr_del(struct fc_exch_mgr_anchor *ema) { … } EXPORT_SYMBOL(…); /** * fc_exch_mgr_list_clone() - Share all exchange manager objects * @src: Source lport to clone exchange managers from * @dst: New lport that takes references to all the exchange managers */ int fc_exch_mgr_list_clone(struct fc_lport *src, struct fc_lport *dst) { … } EXPORT_SYMBOL(…); /** * fc_exch_mgr_alloc() - Allocate an exchange manager * @lport: The local port that the new EM will be associated with * @class: The default FC class for new exchanges * @min_xid: The minimum XID for exchanges from the new EM * @max_xid: The maximum XID for exchanges from the new EM * @match: The match routine for the new EM */ struct fc_exch_mgr *fc_exch_mgr_alloc(struct fc_lport *lport, enum fc_class class, u16 min_xid, u16 max_xid, bool (*match)(struct fc_frame *)) { … } EXPORT_SYMBOL(…); /** * fc_exch_mgr_free() - Free all exchange managers on a local port * @lport: The local port whose EMs are to be freed */ void fc_exch_mgr_free(struct fc_lport *lport) { … } EXPORT_SYMBOL(…); /** * fc_find_ema() - Lookup and return appropriate Exchange Manager Anchor depending * upon 'xid'. * @f_ctl: f_ctl * @lport: The local port the frame was received on * @fh: The received frame header */ static struct fc_exch_mgr_anchor *fc_find_ema(u32 f_ctl, struct fc_lport *lport, struct fc_frame_header *fh) { … } /** * fc_exch_recv() - Handler for received frames * @lport: The local port the frame was received on * @fp: The received frame */ void fc_exch_recv(struct fc_lport *lport, struct fc_frame *fp) { … } EXPORT_SYMBOL(…); /** * fc_exch_init() - Initialize the exchange layer for a local port * @lport: The local port to initialize the exchange layer for */ int fc_exch_init(struct fc_lport *lport) { … } EXPORT_SYMBOL(…); /** * fc_setup_exch_mgr() - Setup an exchange manager */ int fc_setup_exch_mgr(void) { … } /** * fc_destroy_exch_mgr() - Destroy an exchange manager */ void fc_destroy_exch_mgr(void) { … }