/* * This file is part of the Chelsio FCoE driver for Linux. * * Copyright (c) 2008-2012 Chelsio Communications, Inc. All rights reserved. * * This software is available to you under a choice of one of two * licenses. You may choose to be licensed under the terms of the GNU * General Public License (GPL) Version 2, available from the file * COPYING in the main directory of this source tree, or the * OpenIB.org BSD license below: * * Redistribution and use in source and binary forms, with or * without modification, are permitted provided that the following * conditions are met: * * - Redistributions of source code must retain the above * copyright notice, this list of conditions and the following * disclaimer. * * - Redistributions in binary form must reproduce the above * copyright notice, this list of conditions and the following * disclaimer in the documentation and/or other materials * provided with the distribution. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ #include <linux/device.h> #include <linux/delay.h> #include <linux/ctype.h> #include <linux/kernel.h> #include <linux/slab.h> #include <linux/string.h> #include <linux/compiler.h> #include <linux/export.h> #include <linux/module.h> #include <asm/unaligned.h> #include <asm/page.h> #include <scsi/scsi.h> #include <scsi/scsi_device.h> #include <scsi/scsi_transport_fc.h> #include "csio_hw.h" #include "csio_lnode.h" #include "csio_rnode.h" #include "csio_scsi.h" #include "csio_init.h" int csio_scsi_eqsize = …; int csio_scsi_iqlen = …; int csio_scsi_ioreqs = …; uint32_t csio_max_scan_tmo; uint32_t csio_delta_scan_tmo = …; int csio_lun_qdepth = …; static int csio_ddp_descs = …; static int csio_do_abrt_cls(struct csio_hw *, struct csio_ioreq *, bool); static void csio_scsis_uninit(struct csio_ioreq *, enum csio_scsi_ev); static void csio_scsis_io_active(struct csio_ioreq *, enum csio_scsi_ev); static void csio_scsis_tm_active(struct csio_ioreq *, enum csio_scsi_ev); static void csio_scsis_aborting(struct csio_ioreq *, enum csio_scsi_ev); static void csio_scsis_closing(struct csio_ioreq *, enum csio_scsi_ev); static void csio_scsis_shost_cmpl_await(struct csio_ioreq *, enum csio_scsi_ev); /* * csio_scsi_match_io - Match an ioreq with the given SCSI level data. * @ioreq: The I/O request * @sld: Level information * * Should be called with lock held. * */ static bool csio_scsi_match_io(struct csio_ioreq *ioreq, struct csio_scsi_level_data *sld) { … } /* * csio_scsi_gather_active_ios - Gather active I/Os based on level * @scm: SCSI module * @sld: Level information * @dest: The queue where these I/Os have to be gathered. * * Should be called with lock held. */ static void csio_scsi_gather_active_ios(struct csio_scsim *scm, struct csio_scsi_level_data *sld, struct list_head *dest) { … } static inline bool csio_scsi_itnexus_loss_error(uint16_t error) { … } /* * csio_scsi_fcp_cmnd - Frame the SCSI FCP command paylod. * @req: IO req structure. * @addr: DMA location to place the payload. * * This routine is shared between FCP_WRITE, FCP_READ and FCP_CMD requests. */ static inline void csio_scsi_fcp_cmnd(struct csio_ioreq *req, void *addr) { … } /* * csio_scsi_init_cmd_wr - Initialize the SCSI CMD WR. * @req: IO req structure. * @addr: DMA location to place the payload. * @size: Size of WR (including FW WR + immed data + rsp SG entry * * Wrapper for populating fw_scsi_cmd_wr. */ static inline void csio_scsi_init_cmd_wr(struct csio_ioreq *req, void *addr, uint32_t size) { … } #define CSIO_SCSI_CMD_WR_SZ(_imm) … #define CSIO_SCSI_CMD_WR_SZ_16(_imm) … /* * csio_scsi_cmd - Create a SCSI CMD WR. * @req: IO req structure. * * Gets a WR slot in the ingress queue and initializes it with SCSI CMD WR. * */ static inline void csio_scsi_cmd(struct csio_ioreq *req) { … } /* * csio_scsi_init_ulptx_dsgl - Fill in a ULP_TX_SC_DSGL * @hw: HW module * @req: IO request * @sgl: ULP TX SGL pointer. * */ static inline void csio_scsi_init_ultptx_dsgl(struct csio_hw *hw, struct csio_ioreq *req, struct ulptx_sgl *sgl) { … } /* * csio_scsi_init_read_wr - Initialize the READ SCSI WR. * @req: IO req structure. * @wrp: DMA location to place the payload. * @size: Size of WR (including FW WR + immed data + rsp SG entry + data SGL * * Wrapper for populating fw_scsi_read_wr. */ static inline void csio_scsi_init_read_wr(struct csio_ioreq *req, void *wrp, uint32_t size) { … } /* * csio_scsi_init_write_wr - Initialize the WRITE SCSI WR. * @req: IO req structure. * @wrp: DMA location to place the payload. * @size: Size of WR (including FW WR + immed data + rsp SG entry + data SGL * * Wrapper for populating fw_scsi_write_wr. */ static inline void csio_scsi_init_write_wr(struct csio_ioreq *req, void *wrp, uint32_t size) { … } /* Calculate WR size needed for fw_scsi_read_wr/fw_scsi_write_wr */ #define CSIO_SCSI_DATA_WRSZ(req, oper, sz, imm) … /* * csio_scsi_read - Create a SCSI READ WR. * @req: IO req structure. * * Gets a WR slot in the ingress queue and initializes it with * SCSI READ WR. * */ static inline void csio_scsi_read(struct csio_ioreq *req) { … } /* * csio_scsi_write - Create a SCSI WRITE WR. * @req: IO req structure. * * Gets a WR slot in the ingress queue and initializes it with * SCSI WRITE WR. * */ static inline void csio_scsi_write(struct csio_ioreq *req) { … } /* * csio_setup_ddp - Setup DDP buffers for Read request. * @req: IO req structure. * * Checks SGLs/Data buffers are virtually contiguous required for DDP. * If contiguous,driver posts SGLs in the WR otherwise post internal * buffers for such request for DDP. */ static inline void csio_setup_ddp(struct csio_scsim *scsim, struct csio_ioreq *req) { … } /* * csio_scsi_init_abrt_cls_wr - Initialize an ABORT/CLOSE WR. * @req: IO req structure. * @addr: DMA location to place the payload. * @size: Size of WR * @abort: abort OR close * * Wrapper for populating fw_scsi_cmd_wr. */ static inline void csio_scsi_init_abrt_cls_wr(struct csio_ioreq *req, void *addr, uint32_t size, bool abort) { … } static inline void csio_scsi_abrt_cls(struct csio_ioreq *req, bool abort) { … } /*****************************************************************************/ /* START: SCSI SM */ /*****************************************************************************/ static void csio_scsis_uninit(struct csio_ioreq *req, enum csio_scsi_ev evt) { … } static void csio_scsis_io_active(struct csio_ioreq *req, enum csio_scsi_ev evt) { … } static void csio_scsis_tm_active(struct csio_ioreq *req, enum csio_scsi_ev evt) { … } static void csio_scsis_aborting(struct csio_ioreq *req, enum csio_scsi_ev evt) { … } static void csio_scsis_closing(struct csio_ioreq *req, enum csio_scsi_ev evt) { … } static void csio_scsis_shost_cmpl_await(struct csio_ioreq *req, enum csio_scsi_ev evt) { … } /* * csio_scsi_cmpl_handler - WR completion handler for SCSI. * @hw: HW module. * @wr: The completed WR from the ingress queue. * @len: Length of the WR. * @flb: Freelist buffer array. * @priv: Private object * @scsiwr: Pointer to SCSI WR. * * This is the WR completion handler called per completion from the * ISR. It is called with lock held. It walks past the RSS and CPL message * header where the actual WR is present. * It then gets the status, WR handle (ioreq pointer) and the len of * the WR, based on WR opcode. Only on a non-good status is the entire * WR copied into the WR cache (ioreq->fw_wr). * The ioreq corresponding to the WR is returned to the caller. * NOTE: The SCSI queue doesnt allocate a freelist today, hence * no freelist buffer is expected. */ struct csio_ioreq * csio_scsi_cmpl_handler(struct csio_hw *hw, void *wr, uint32_t len, struct csio_fl_dma_buf *flb, void *priv, uint8_t **scsiwr) { … } /* * csio_scsi_cleanup_io_q - Cleanup the given queue. * @scm: SCSI module. * @q: Queue to be cleaned up. * * Called with lock held. Has to exit with lock held. */ void csio_scsi_cleanup_io_q(struct csio_scsim *scm, struct list_head *q) { … } #define CSIO_SCSI_ABORT_Q_POLL_MS … static void csio_abrt_cls(struct csio_ioreq *ioreq, struct scsi_cmnd *scmnd) { … } /* * csio_scsi_abort_io_q - Abort all I/Os on given queue * @scm: SCSI module. * @q: Queue to abort. * @tmo: Timeout in ms * * Attempt to abort all I/Os on given queue, and wait for a max * of tmo milliseconds for them to complete. Returns success * if all I/Os are aborted. Else returns -ETIMEDOUT. * Should be entered with lock held. Exits with lock held. * NOTE: * Lock has to be held across the loop that aborts I/Os, since dropping the lock * in between can cause the list to be corrupted. As a result, the caller * of this function has to ensure that the number of I/os to be aborted * is finite enough to not cause lock-held-for-too-long issues. */ static int csio_scsi_abort_io_q(struct csio_scsim *scm, struct list_head *q, uint32_t tmo) { … } /* * csio_scsim_cleanup_io - Cleanup all I/Os in SCSI module. * @scm: SCSI module. * @abort: abort required. * Called with lock held, should exit with lock held. * Can sleep when waiting for I/Os to complete. */ int csio_scsim_cleanup_io(struct csio_scsim *scm, bool abort) { … } /* * csio_scsim_cleanup_io_lnode - Cleanup all I/Os of given lnode. * @scm: SCSI module. * @lnode: lnode * * Called with lock held, should exit with lock held. * Can sleep (with dropped lock) when waiting for I/Os to complete. */ int csio_scsim_cleanup_io_lnode(struct csio_scsim *scm, struct csio_lnode *ln) { … } static ssize_t csio_show_hw_state(struct device *dev, struct device_attribute *attr, char *buf) { … } /* Device reset */ static ssize_t csio_device_reset(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { … } /* disable port */ static ssize_t csio_disable_port(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { … } /* Show debug level */ static ssize_t csio_show_dbg_level(struct device *dev, struct device_attribute *attr, char *buf) { … } /* Store debug level */ static ssize_t csio_store_dbg_level(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { … } static DEVICE_ATTR(hw_state, S_IRUGO, csio_show_hw_state, NULL); static DEVICE_ATTR(device_reset, S_IWUSR, NULL, csio_device_reset); static DEVICE_ATTR(disable_port, S_IWUSR, NULL, csio_disable_port); static DEVICE_ATTR(dbg_level, S_IRUGO | S_IWUSR, csio_show_dbg_level, csio_store_dbg_level); static struct attribute *csio_fcoe_lport_attrs[] = …; ATTRIBUTE_GROUPS(…); static ssize_t csio_show_num_reg_rnodes(struct device *dev, struct device_attribute *attr, char *buf) { … } static DEVICE_ATTR(num_reg_rnodes, S_IRUGO, csio_show_num_reg_rnodes, NULL); static struct attribute *csio_fcoe_vport_attrs[] = …; ATTRIBUTE_GROUPS(…); static inline uint32_t csio_scsi_copy_to_sgl(struct csio_hw *hw, struct csio_ioreq *req) { … } /* * csio_scsi_err_handler - SCSI error handler. * @hw: HW module. * @req: IO request. * */ static inline void csio_scsi_err_handler(struct csio_hw *hw, struct csio_ioreq *req) { … } /* * csio_scsi_cbfn - SCSI callback function. * @hw: HW module. * @req: IO request. * */ static void csio_scsi_cbfn(struct csio_hw *hw, struct csio_ioreq *req) { … } /** * csio_queuecommand - Entry point to kickstart an I/O request. * @host: The scsi_host pointer. * @cmnd: The I/O request from ML. * * This routine does the following: * - Checks for HW and Rnode module readiness. * - Gets a free ioreq structure (which is already initialized * to uninit during its allocation). * - Maps SG elements. * - Initializes ioreq members. * - Kicks off the SCSI state machine for this IO. * - Returns busy status on error. */ static int csio_queuecommand(struct Scsi_Host *host, struct scsi_cmnd *cmnd) { … } static int csio_do_abrt_cls(struct csio_hw *hw, struct csio_ioreq *ioreq, bool abort) { … } static int csio_eh_abort_handler(struct scsi_cmnd *cmnd) { … } /* * csio_tm_cbfn - TM callback function. * @hw: HW module. * @req: IO request. * * Cache the result in 'cmnd', since ioreq will be freed soon * after we return from here, and the waiting thread shouldnt trust * the ioreq contents. */ static void csio_tm_cbfn(struct csio_hw *hw, struct csio_ioreq *req) { … } static int csio_eh_lun_reset_handler(struct scsi_cmnd *cmnd) { … } static int csio_slave_alloc(struct scsi_device *sdev) { … } static int csio_slave_configure(struct scsi_device *sdev) { … } static void csio_slave_destroy(struct scsi_device *sdev) { … } static int csio_scan_finished(struct Scsi_Host *shost, unsigned long time) { … } struct scsi_host_template csio_fcoe_shost_template = …; struct scsi_host_template csio_fcoe_shost_vport_template = …; /* * csio_scsi_alloc_ddp_bufs - Allocate buffers for DDP of unaligned SGLs. * @scm: SCSI Module * @hw: HW device. * @buf_size: buffer size * @num_buf : Number of buffers. * * This routine allocates DMA buffers required for SCSI Data xfer, if * each SGL buffer for a SCSI Read request posted by SCSI midlayer are * not virtually contiguous. */ static int csio_scsi_alloc_ddp_bufs(struct csio_scsim *scm, struct csio_hw *hw, int buf_size, int num_buf) { … } /* * csio_scsi_free_ddp_bufs - free DDP buffers of unaligned SGLs. * @scm: SCSI Module * @hw: HW device. * * This routine frees ddp buffers. */ static void csio_scsi_free_ddp_bufs(struct csio_scsim *scm, struct csio_hw *hw) { … } /** * csio_scsim_init - Initialize SCSI Module * @scm: SCSI Module * @hw: HW module * */ int csio_scsim_init(struct csio_scsim *scm, struct csio_hw *hw) { … } /** * csio_scsim_exit: Uninitialize SCSI Module * @scm: SCSI Module * */ void csio_scsim_exit(struct csio_scsim *scm) { … }