#include <linux/timer.h>
#include <linux/if.h>
+#include <linux/percpu.h>
#include <scsi/scsi_transport.h>
#include <scsi/scsi_transport_fc.h>
#include <scsi/fc_frame.h>
-#define LIBFC_DEBUG
-
-#ifdef LIBFC_DEBUG
-/* Log messages */
-#define FC_DBG(fmt, args...) \
- do { \
- printk(KERN_INFO "%s " fmt, __func__, ##args); \
- } while (0)
-#else
-#define FC_DBG(fmt, args...)
-#endif
+#define FC_LIBFC_LOGGING 0x01 /* General logging, not categorized */
+#define FC_LPORT_LOGGING 0x02 /* lport layer logging */
+#define FC_DISC_LOGGING 0x04 /* discovery layer logging */
+#define FC_RPORT_LOGGING 0x08 /* rport layer logging */
+#define FC_FCP_LOGGING 0x10 /* I/O path logging */
+#define FC_EM_LOGGING 0x20 /* Exchange Manager logging */
+#define FC_EXCH_LOGGING 0x40 /* Exchange/Sequence logging */
+#define FC_SCSI_LOGGING 0x80 /* SCSI logging (mostly error handling) */
+
+extern unsigned int fc_debug_logging;
+
+#define FC_CHECK_LOGGING(LEVEL, CMD) \
+do { \
+ if (unlikely(fc_debug_logging & LEVEL)) \
+ do { \
+ CMD; \
+ } while (0); \
+} while (0)
+
+#define FC_LIBFC_DBG(fmt, args...) \
+ FC_CHECK_LOGGING(FC_LIBFC_LOGGING, \
+ printk(KERN_INFO "libfc: " fmt, ##args))
+
+#define FC_LPORT_DBG(lport, fmt, args...) \
+ FC_CHECK_LOGGING(FC_LPORT_LOGGING, \
+ printk(KERN_INFO "host%u: lport %6x: " fmt, \
+ (lport)->host->host_no, \
+ fc_host_port_id((lport)->host), ##args))
+
+#define FC_DISC_DBG(disc, fmt, args...) \
+ FC_CHECK_LOGGING(FC_DISC_LOGGING, \
+ printk(KERN_INFO "host%u: disc: " fmt, \
+ (disc)->lport->host->host_no, \
+ ##args))
+
+#define FC_RPORT_ID_DBG(lport, port_id, fmt, args...) \
+ FC_CHECK_LOGGING(FC_RPORT_LOGGING, \
+ printk(KERN_INFO "host%u: rport %6x: " fmt, \
+ (lport)->host->host_no, \
+ (port_id), ##args))
+
+#define FC_RPORT_DBG(rport, fmt, args...) \
+do { \
+ struct fc_rport_priv *rdata = rport->dd_data; \
+ struct fc_lport *lport = rdata->local_port; \
+ FC_RPORT_ID_DBG(lport, rport->port_id, fmt, ##args); \
+} while (0)
+
+#define FC_FCP_DBG(pkt, fmt, args...) \
+ FC_CHECK_LOGGING(FC_FCP_LOGGING, \
+ printk(KERN_INFO "host%u: fcp: %6x: " fmt, \
+ (pkt)->lp->host->host_no, \
+ pkt->rport->port_id, ##args))
+
+#define FC_EXCH_DBG(exch, fmt, args...) \
+ FC_CHECK_LOGGING(FC_EXCH_LOGGING, \
+ printk(KERN_INFO "host%u: xid %4x: " fmt, \
+ (exch)->lp->host->host_no, \
+ exch->xid, ##args))
+
+#define FC_SCSI_DBG(lport, fmt, args...) \
+ FC_CHECK_LOGGING(FC_SCSI_LOGGING, \
+ printk(KERN_INFO "host%u: scsi: " fmt, \
+ (lport)->host->host_no, ##args))
/*
* libfc error codes
/*
* FC HBA status
*/
-#define FC_PAUSE (1 << 1)
-#define FC_LINK_UP (1 << 0)
-
enum fc_lport_state {
- LPORT_ST_NONE = 0,
+ LPORT_ST_DISABLED = 0,
LPORT_ST_FLOGI,
LPORT_ST_DNS,
LPORT_ST_RPN_ID,
};
enum fc_rport_state {
- RPORT_ST_NONE = 0,
RPORT_ST_INIT, /* initialized */
RPORT_ST_PLOGI, /* waiting for PLOGI completion */
RPORT_ST_PRLI, /* waiting for PRLI completion */
RPORT_ST_RTV, /* waiting for RTV completion */
RPORT_ST_READY, /* ready for use */
RPORT_ST_LOGO, /* port logout sent */
+ RPORT_ST_DELETE, /* port being deleted */
};
enum fc_rport_trans_state {
RPORT_EV_LOGO
};
+/*
+ * Temporary definition to prepare for split off from fc_rport_libfc_priv
+ * of a separately-allocated structure called fc_rport_priv. This will
+ * be the primary object for the discovery and rport state machines.
+ * This definition is just to make this patch series easier to review.
+ */
+#define fc_rport_priv fc_rport_libfc_priv
+
struct fc_rport_operations {
void (*event_callback)(struct fc_lport *, struct fc_rport *,
enum fc_rport_event);
#define RPORT_TO_PRIV(x) \
(struct fc_rport_libfc_priv *)((void *)x + sizeof(struct fc_rport));
-struct fc_rport *fc_rport_rogue_create(struct fc_disc_port *);
-
static inline void fc_rport_set_name(struct fc_rport *rport, u64 wwpn, u64 wwnn)
{
rport->node_name = wwnn;
*/
struct fcp_cmnd cdb_cmd;
size_t xfer_len;
+ u16 xfer_ddp; /* this xfer is ddped */
u32 xfer_contig_end; /* offset of end of contiguous xfer */
u16 max_payload; /* max payload size in bytes */
u8 recov_retry; /* count of recovery retries */
struct fc_seq *recov_seq; /* sequence for REC or SRR */
};
+/*
+ * FC_FCP HELPER FUNCTIONS
+ *****************************/
+static inline bool fc_fcp_is_read(const struct fc_fcp_pkt *fsp)
+{
+ if (fsp && fsp->cmd)
+ return fsp->cmd->sc_data_direction == DMA_FROM_DEVICE;
+ return false;
+}
/*
* Structure and function definitions for managing Fibre Channel Exchanges
*/
struct fc_exch_mgr;
+struct fc_exch_mgr_anchor;
+extern u16 fc_cpu_mask; /* cpu mask for possible cpus */
/*
* Sequence.
*/
struct fc_exch {
struct fc_exch_mgr *em; /* exchange manager */
+ struct fc_exch_pool *pool; /* per cpu exches pool */
u32 state; /* internal driver state */
u16 xid; /* our exchange ID */
struct list_head ex_list; /* free or busy list linkage */
struct libfc_function_template {
- /**
- * Mandatory Fields
- *
- * These handlers must be implemented by the LLD.
- */
-
/*
* Interface to send a FC frame
- */
- int (*frame_send)(struct fc_lport *lp, struct fc_frame *fp);
-
- /**
- * Optional Fields
*
- * The LLD may choose to implement any of the following handlers.
- * If LLD doesn't specify hander and leaves its pointer NULL then
- * the default libfc function will be used for that handler.
- */
-
- /**
- * ELS/CT interfaces
+ * STATUS: REQUIRED
*/
+ int (*frame_send)(struct fc_lport *lp, struct fc_frame *fp);
/*
- * elsct_send - sends ELS/CT frame
+ * Interface to send ELS/CT frames
+ *
+ * STATUS: OPTIONAL
*/
struct fc_seq *(*elsct_send)(struct fc_lport *lport,
struct fc_rport *rport,
struct fc_frame *fp,
void *arg),
void *arg, u32 timer_msec);
- /**
- * Exhance Manager interfaces
- */
/*
* Send the FC frame payload using a new exchange and sequence.
* 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.
+ *
+ * STATUS: OPTIONAL
*/
struct fc_seq *(*exch_seq_send)(struct fc_lport *lp,
struct fc_frame *fp,
void *arg, unsigned int timer_msec);
/*
- * send a frame using existing sequence and exchange.
+ * Sets up the DDP context for a given exchange id on the given
+ * scatterlist if LLD supports DDP for large receive.
+ *
+ * STATUS: OPTIONAL
+ */
+ int (*ddp_setup)(struct fc_lport *lp, u16 xid,
+ struct scatterlist *sgl, unsigned int sgc);
+ /*
+ * Completes the DDP transfer and returns the length of data DDPed
+ * for the given exchange id.
+ *
+ * STATUS: OPTIONAL
+ */
+ int (*ddp_done)(struct fc_lport *lp, u16 xid);
+ /*
+ * Send a frame using an existing sequence and exchange.
+ *
+ * STATUS: OPTIONAL
*/
int (*seq_send)(struct fc_lport *lp, struct fc_seq *sp,
struct fc_frame *fp);
/*
- * Send ELS response using mainly infomation
- * in exchange and sequence in EM layer.
+ * Send an ELS response using infomation from a previous
+ * exchange and sequence.
+ *
+ * STATUS: OPTIONAL
*/
void (*seq_els_rsp_send)(struct fc_seq *sp, enum fc_els_cmd els_cmd,
struct fc_seq_els_data *els_data);
* 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.
+ *
+ * STATUS: OPTIONAL
*/
int (*seq_exch_abort)(const struct fc_seq *req_sp,
unsigned int timer_msec);
/*
* Indicate that an exchange/sequence tuple is complete and the memory
* allocated for the related objects may be freed.
+ *
+ * STATUS: OPTIONAL
*/
void (*exch_done)(struct fc_seq *sp);
/*
- * Assigns a EM and a free XID for an new exchange and then
- * allocates a new exchange and sequence pair.
- * The fp can be used to determine free XID.
- */
- struct fc_exch *(*exch_get)(struct fc_lport *lp, struct fc_frame *fp);
-
- /*
- * Release previously assigned XID by exch_get API.
- * The LLD may implement this if XID is assigned by LLD
- * in exch_get().
- */
- void (*exch_put)(struct fc_lport *lp, struct fc_exch_mgr *mp,
- u16 ex_id);
-
- /*
* Start a new sequence on the same exchange/sequence tuple.
+ *
+ * STATUS: OPTIONAL
*/
struct fc_seq *(*seq_start_next)(struct fc_seq *sp);
* Reset an exchange manager, completing all sequences and exchanges.
* If s_id is non-zero, reset only exchanges originating from that FID.
* If d_id is non-zero, reset only exchanges sending to that FID.
+ *
+ * STATUS: OPTIONAL
*/
- void (*exch_mgr_reset)(struct fc_exch_mgr *,
+ void (*exch_mgr_reset)(struct fc_lport *,
u32 s_id, u32 d_id);
- void (*rport_flush_queue)(void);
- /**
- * Local Port interfaces
+ /*
+ * Flush the rport work queue. Generally used before shutdown.
+ *
+ * STATUS: OPTIONAL
*/
+ void (*rport_flush_queue)(void);
/*
- * Receive a frame to a local port.
+ * Receive a frame for a local port.
+ *
+ * STATUS: OPTIONAL
*/
void (*lport_recv)(struct fc_lport *lp, struct fc_seq *sp,
struct fc_frame *fp);
+ /*
+ * Reset the local port.
+ *
+ * STATUS: OPTIONAL
+ */
int (*lport_reset)(struct fc_lport *);
- /**
- * Remote Port interfaces
+ /*
+ * Create a remote port
*/
+ struct fc_rport *(*rport_create)(struct fc_lport *,
+ struct fc_rport_identifiers *);
/*
* Initiates the RP state machine. It is called from the LP module.
* - PLOGI
* - PRLI
* - RTV
+ *
+ * STATUS: OPTIONAL
*/
int (*rport_login)(struct fc_rport *rport);
/*
* Logoff, and remove the rport from the transport if
* it had been added. This will send a LOGO to the target.
+ *
+ * STATUS: OPTIONAL
*/
int (*rport_logoff)(struct fc_rport *rport);
/*
* Recieve a request from a remote port.
+ *
+ * STATUS: OPTIONAL
*/
void (*rport_recv_req)(struct fc_seq *, struct fc_frame *,
struct fc_rport *);
- struct fc_rport *(*rport_lookup)(const struct fc_lport *, u32);
-
- /**
- * FCP interfaces
+ /*
+ * lookup an rport by it's port ID.
+ *
+ * STATUS: OPTIONAL
*/
+ struct fc_rport *(*rport_lookup)(const struct fc_lport *, u32);
/*
* Send a fcp cmd from fsp pkt.
*
* The resp handler is called when FCP_RSP received.
*
+ * STATUS: OPTIONAL
*/
int (*fcp_cmd_send)(struct fc_lport *lp, struct fc_fcp_pkt *fsp,
void (*resp)(struct fc_seq *, struct fc_frame *fp,
void *arg));
/*
- * Used at least durring linkdown and reset
+ * Cleanup the FCP layer, used durring link down and reset
+ *
+ * STATUS: OPTIONAL
*/
void (*fcp_cleanup)(struct fc_lport *lp);
/*
* Abort all I/O on a local port
+ *
+ * STATUS: OPTIONAL
*/
void (*fcp_abort_io)(struct fc_lport *lp);
- /**
- * Discovery interfaces
+ /*
+ * Receive a request for the discovery layer.
+ *
+ * STATUS: OPTIONAL
*/
-
void (*disc_recv_req)(struct fc_seq *,
struct fc_frame *, struct fc_lport *);
/*
* Start discovery for a local port.
+ *
+ * STATUS: OPTIONAL
*/
void (*disc_start)(void (*disc_callback)(struct fc_lport *,
enum fc_disc_event),
/*
* Stop discovery for a given lport. This will remove
* all discovered rports
+ *
+ * STATUS: OPTIONAL
*/
void (*disc_stop) (struct fc_lport *);
* Stop discovery for a given lport. This will block
* until all discovered rports are deleted from the
* FC transport class
+ *
+ * STATUS: OPTIONAL
*/
void (*disc_stop_final) (struct fc_lport *);
};
enum fc_disc_event);
struct list_head rports;
+ struct list_head rogue_rports;
struct fc_lport *lport;
struct mutex disc_mutex;
struct fc_gpn_ft_resp partial_buf; /* partial name buffer */
/* Associations */
struct Scsi_Host *host;
- struct fc_exch_mgr *emp;
+ struct list_head ema_list;
struct fc_rport *dns_rp;
struct fc_rport *ptp_rp;
void *scsi_priv;
/* Operational Information */
struct libfc_function_template tt;
- u16 link_status;
+ u8 link_up;
+ u8 qfull;
enum fc_lport_state state;
unsigned long boot_time;
struct fc_host_statistics host_stats;
- struct fcoe_dev_stats *dev_stats[NR_CPUS];
+ struct fcoe_dev_stats *dev_stats;
+
u64 wwpn;
u64 wwnn;
u8 retry_count;
unsigned int e_d_tov;
unsigned int r_a_tov;
u8 max_retry_count;
+ u8 max_rport_retry_count;
u16 link_speed;
u16 link_supported_speeds;
u16 lro_xid; /* max xid for fcoe lro */
+ unsigned int lso_max; /* max large send size */
struct fc_ns_fts fcts; /* FC-4 type masks */
struct fc_els_rnid_gen rnid_gen; /* RNID information */
struct delayed_work disc_work;
};
-/**
+/*
* FC_LPORT HELPER FUNCTIONS
*****************************/
-static inline void *lport_priv(const struct fc_lport *lp)
-{
- return (void *)(lp + 1);
-}
-
static inline int fc_lport_test_ready(struct fc_lport *lp)
{
return lp->state == LPORT_ST_READY;
lp->state = state;
}
+static inline int fc_lport_init_stats(struct fc_lport *lp)
+{
+ /* allocate per cpu stats block */
+ lp->dev_stats = alloc_percpu(struct fcoe_dev_stats);
+ if (!lp->dev_stats)
+ return -ENOMEM;
+ return 0;
+}
+
+static inline void fc_lport_free_stats(struct fc_lport *lp)
+{
+ free_percpu(lp->dev_stats);
+}
+
+static inline struct fcoe_dev_stats *fc_lport_get_stats(struct fc_lport *lp)
+{
+ return per_cpu_ptr(lp->dev_stats, smp_processor_id());
+}
+
+static inline void *lport_priv(const struct fc_lport *lp)
+{
+ return (void *)(lp + 1);
+}
/**
+ * libfc_host_alloc() - Allocate a Scsi_Host with room for the fc_lport
+ * @sht: ptr to the scsi host templ
+ * @priv_size: size of private data after fc_lport
+ *
+ * Returns: ptr to Scsi_Host
+ */
+static inline struct Scsi_Host *
+libfc_host_alloc(struct scsi_host_template *sht, int priv_size)
+{
+ return scsi_host_alloc(sht, sizeof(struct fc_lport) + priv_size);
+}
+
+/*
* LOCAL PORT LAYER
*****************************/
int fc_lport_init(struct fc_lport *lp);
void fc_linkdown(struct fc_lport *);
/*
- * Pause and unpause traffic.
- */
-void fc_pause(struct fc_lport *);
-void fc_unpause(struct fc_lport *);
-
-/*
* Configure the local port.
*/
int fc_lport_config(struct fc_lport *);
int fc_set_mfs(struct fc_lport *lp, u32 mfs);
-/**
+/*
* REMOTE PORT LAYER
*****************************/
int fc_rport_init(struct fc_lport *lp);
void fc_rport_terminate_io(struct fc_rport *rp);
-/**
+/*
* DISCOVERY LAYER
*****************************/
int fc_disc_init(struct fc_lport *lp);
-/**
+/*
* SCSI LAYER
*****************************/
/*
*/
void fc_fcp_destroy(struct fc_lport *);
-/**
+/*
+ * Set up direct-data placement for this I/O request
+ */
+void fc_fcp_ddp_setup(struct fc_fcp_pkt *fsp, u16 xid);
+
+/*
* ELS/CT interface
*****************************/
/*
int fc_elsct_init(struct fc_lport *lp);
-/**
+/*
* EXCHANGE MANAGER LAYER
*****************************/
/*
int fc_exch_init(struct fc_lport *lp);
/*
+ * Adds Exchange Manager (EM) mp to lport.
+ *
+ * Adds specified mp to lport using struct fc_exch_mgr_anchor,
+ * the struct fc_exch_mgr_anchor allows same EM sharing by
+ * more than one lport with their specified match function,
+ * the match function is used in allocating exchange from
+ * added mp.
+ */
+struct fc_exch_mgr_anchor *fc_exch_mgr_add(struct fc_lport *lport,
+ struct fc_exch_mgr *mp,
+ bool (*match)(struct fc_frame *));
+
+/*
+ * Deletes Exchange Manager (EM) from lport by removing
+ * its anchor ema from lport.
+ *
+ * If removed anchor ema was the last user of its associated EM
+ * then also destroys associated EM.
+ */
+void fc_exch_mgr_del(struct fc_exch_mgr_anchor *ema);
+
+/*
* Allocates an Exchange Manager (EM).
*
* The EM manages exchanges for their allocation and
* a new exchange.
* The LLD may choose to have multiple EMs,
* e.g. one EM instance per CPU receive thread in LLD.
- * The LLD can use exch_get() of struct libfc_function_template
- * to specify XID for a new exchange within
- * a specified EM instance.
*
- * The em_idx to uniquely identify an EM instance.
+ * Specified match function is used in allocating exchanges
+ * from newly allocated EM.
*/
struct fc_exch_mgr *fc_exch_mgr_alloc(struct fc_lport *lp,
enum fc_class class,
u16 min_xid,
- u16 max_xid);
+ u16 max_xid,
+ bool (*match)(struct fc_frame *));
/*
- * Free an exchange manager.
+ * Free all exchange managers of a lport.
*/
-void fc_exch_mgr_free(struct fc_exch_mgr *mp);
+void fc_exch_mgr_free(struct fc_lport *lport);
/*
* Receive a frame on specified local port and exchange manager.
*/
-void fc_exch_recv(struct fc_lport *lp, struct fc_exch_mgr *mp,
- struct fc_frame *fp);
+void fc_exch_recv(struct fc_lport *lp, struct fc_frame *fp);
/*
* This function is for exch_seq_send function pointer in
void fc_exch_done(struct fc_seq *sp);
/*
- * Assigns a EM and XID for a frame and then allocates
- * a new exchange and sequence pair.
- * The fp can be used to determine free XID.
- */
-struct fc_exch *fc_exch_get(struct fc_lport *lp, struct fc_frame *fp);
-
-/*
* Allocate a new exchange and sequence pair.
- * if ex_id is zero then next free exchange id
- * from specified exchange manger mp will be assigned.
*/
-struct fc_exch *fc_exch_alloc(struct fc_exch_mgr *mp,
- struct fc_frame *fp, u16 ex_id);
+struct fc_exch *fc_exch_alloc(struct fc_lport *lport, struct fc_frame *fp);
/*
* Start a new sequence on the same exchange as the supplied sequence.
*/
struct fc_seq *fc_seq_start_next(struct fc_seq *sp);
+
/*
- * Reset an exchange manager, completing all sequences and exchanges.
- * If s_id is non-zero, reset only exchanges originating from that FID.
- * If d_id is non-zero, reset only exchanges sending to that FID.
+ * Reset all EMs of a lport, releasing its all sequences and
+ * exchanges. If sid is non-zero, then reset only exchanges
+ * we sourced from that FID. If did is non-zero, reset only
+ * exchanges destined to that FID.
*/
-void fc_exch_mgr_reset(struct fc_exch_mgr *, u32 s_id, u32 d_id);
+void fc_exch_mgr_reset(struct fc_lport *, u32 s_id, u32 d_id);
/*
* Functions for fc_functions_template