On 8/24/2012 1:18 AM, Nicholas A. Bellinger wrote: > On Fri, 2012-08-24 at 03:57 +0530, Naresh Kumar Inna wrote: >> This patch contains code to implement the interrupt handling and the fast >> path I/O functionality. The interrupt handling includes allocation of >> MSIX vectors, registering and implemeting the interrupt service routines. >> The fast path I/O functionality includes posting the I/O request to firmware >> via Work Requests, tracking/completing them, and handling task management >> requests. SCSI midlayer host template implementation is also covered by >> this patch. >> >> Signed-off-by: Naresh Kumar Inna <naresh@xxxxxxxxxxx> >> --- > > Hi Naresh, > > My review comments are inline below.. Hi Nicholas, Thanks for taking the time to review the driver. Please find my replies inline. Regards, Naresh. > >> drivers/scsi/csiostor/csio_isr.c | 631 ++++++++++ >> drivers/scsi/csiostor/csio_scsi.c | 2498 +++++++++++++++++++++++++++++++++++++ >> 2 files changed, 3129 insertions(+), 0 deletions(-) >> create mode 100644 drivers/scsi/csiostor/csio_isr.c >> create mode 100644 drivers/scsi/csiostor/csio_scsi.c >> >> diff --git a/drivers/scsi/csiostor/csio_isr.c b/drivers/scsi/csiostor/csio_isr.c >> new file mode 100644 >> index 0000000..96633e9 >> --- /dev/null >> +++ b/drivers/scsi/csiostor/csio_isr.c > > <SNIP> > >> +#define csio_extra_msix_desc(_desc, _len, _str, _arg1, _arg2, _arg3) \ >> +do { \ >> + memset((_desc), 0, (_len) + 1); \ >> + snprintf((_desc), (_len), (_str), (_arg1), (_arg2), (_arg3)); \ >> +} while (0) >> + > > This type of macro usage is not necessary for just two users below. > Please inline code like this. > OK, I will get rid of this macro. >> +static void >> +csio_add_msix_desc(struct csio_hw *hw) >> +{ >> + int i; >> + struct csio_msix_entries *entryp = &hw->msix_entries[0]; >> + int k = CSIO_EXTRA_VECS; >> + int len = sizeof(entryp->desc) - 1; >> + int cnt = hw->num_sqsets + k; >> + >> + /* Non-data vector */ >> + csio_extra_msix_desc(entryp->desc, len, "csio-%02x:%02x:%x-nondata", >> + CSIO_PCI_BUS(hw), CSIO_PCI_DEV(hw), >> + CSIO_PCI_FUNC(hw)); >> + entryp++; >> + csio_extra_msix_desc(entryp->desc, len, "csio-%02x:%02x:%x-fwevt", >> + CSIO_PCI_BUS(hw), CSIO_PCI_DEV(hw), >> + CSIO_PCI_FUNC(hw)); >> + entryp++; >> + >> + /* Name SCSI vecs */ >> + for (i = k; i < cnt; i++, entryp++) { >> + memset(entryp->desc, 0, len + 1); >> + snprintf(entryp->desc, len, "csio-%02x:%02x:%x-scsi%d", >> + CSIO_PCI_BUS(hw), CSIO_PCI_DEV(hw), >> + CSIO_PCI_FUNC(hw), i - CSIO_EXTRA_VECS); >> + } >> +} >> + > > >> diff --git a/drivers/scsi/csiostor/csio_scsi.c b/drivers/scsi/csiostor/csio_scsi.c >> new file mode 100644 >> index 0000000..0f87b00 >> --- /dev/null >> +++ b/drivers/scsi/csiostor/csio_scsi.c > >> + >> +/* >> + * 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) >> +{ >> + struct scsi_cmnd *scmnd = csio_scsi_cmnd(ioreq); >> + >> + switch (sld->level) { >> + case CSIO_LEV_LUN: >> + if (scmnd == NULL) >> + return CSIO_FALSE; >> + >> + return ((ioreq->lnode == sld->lnode) && >> + (ioreq->rnode == sld->rnode) && >> + ((uint64_t)scmnd->device->lun == sld->oslun)); >> + >> + case CSIO_LEV_RNODE: >> + return ((ioreq->lnode == sld->lnode) && >> + (ioreq->rnode == sld->rnode)); >> + case CSIO_LEV_LNODE: >> + return (ioreq->lnode == sld->lnode); >> + case CSIO_LEV_ALL: >> + return CSIO_TRUE; >> + default: >> + return CSIO_FALSE; >> + } >> +} >> + > > Why can't CSIO_[TRUE,FALSE] just use normal Boolean defines..? > Is there a TRUE/FALSE define in a standard header file? I see a lot of kernel code defining their own TRUE/FALSE. >> +/* >> + * 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) >> +{ >> + struct csio_fcp_cmnd *fcp_cmnd = (struct csio_fcp_cmnd *)addr; >> + struct scsi_cmnd *scmnd = csio_scsi_cmnd(req); >> + >> + /* Check for Task Management */ >> + if (likely(scmnd->SCp.Message == 0)) { >> + int_to_scsilun(scmnd->device->lun, >> + (struct scsi_lun *)fcp_cmnd->lun); >> + fcp_cmnd->tm_flags = 0; >> + fcp_cmnd->cmdref = 0; >> + fcp_cmnd->pri_ta = 0; >> + >> + memcpy(fcp_cmnd->cdb, scmnd->cmnd, 16); >> + csio_scsi_tag(scmnd, &fcp_cmnd->pri_ta, >> + FCP_PTA_HEADQ, FCP_PTA_ORDERED, FCP_PTA_SIMPLE); >> + fcp_cmnd->dl = cpu_to_be32(scsi_bufflen(scmnd)); >> + >> + if (req->nsge) >> + if (req->datadir == CSIO_IOREQF_DMA_WRITE) > > The same goes for CSIO_IOREQF_DMA_*... > > Why can't this just be DMA_* defs from include/linux/dma-direction.h..? OK, I will get rid of them and use the standard defines. > >> + fcp_cmnd->flags = FCP_CFL_WRDATA; >> + else >> + fcp_cmnd->flags = FCP_CFL_RDDATA; >> + else >> + fcp_cmnd->flags = 0; >> + } else { >> + memset(fcp_cmnd, 0, sizeof(*fcp_cmnd)); >> + int_to_scsilun(scmnd->device->lun, >> + (struct scsi_lun *)fcp_cmnd->lun); >> + fcp_cmnd->tm_flags = (uint8_t)scmnd->SCp.Message; >> + } >> +} >> + > > > >> + >> +#define CSIO_SCSI_CMD_WR_SZ(_imm) \ >> + (sizeof(struct fw_scsi_cmd_wr) + /* WR size */ \ >> + ALIGN((_imm), 16)) /* Immed data */ >> + >> +#define CSIO_SCSI_CMD_WR_SZ_16(_imm) \ >> + (ALIGN(CSIO_SCSI_CMD_WR_SZ((_imm)), 16)) >> + >> +/* >> + * 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) >> +{ >> + struct csio_wr_pair wrp; >> + struct csio_hw *hw = req->lnode->hwp; >> + struct csio_scsim *scsim = csio_hw_to_scsim(hw); >> + uint32_t size = CSIO_SCSI_CMD_WR_SZ_16(scsim->proto_cmd_len); >> + >> + req->drv_status = csio_wr_get(hw, req->eq_idx, size, &wrp); >> + if (unlikely(req->drv_status != CSIO_SUCCESS)) >> + return; >> + >> + if (wrp.size1 >= size) { >> + /* Initialize WR in one shot */ >> + csio_scsi_init_cmd_wr(req, wrp.addr1, size); >> + } else { >> + uint8_t tmpwr[512]; > > Mmmm, putting this large of a buffer on the local stack is probably not > a good idea. > > This should become an allocation.. If it's a hot path then you'll > probably want to set this up before-hand. > The else switch above is entered only when we near the end of the DMA ring. This is not a frequent occurrence. If it is a strict no-no to have so many on-stack bytes, I have to think of a way re-work it to use pre-allocated memory. Please let me know. >> + /* >> + * Make a temporary copy of the WR and write back >> + * the copy into the WR pair. >> + */ >> + csio_scsi_init_cmd_wr(req, (void *)tmpwr, size); >> + memcpy(wrp.addr1, tmpwr, wrp.size1); >> + memcpy(wrp.addr2, tmpwr + wrp.size1, size - wrp.size1); >> + } >> +} >> + >> +/* >> + * The following is fast path code. Therefore it is inlined with multi-line >> + * macros using name substitution, thus avoiding if-else switches for >> + * operation (read/write), as well as serving the purpose of code re-use. >> + */ >> +/* >> + * csio_scsi_init_ulptx_dsgl - Fill in a ULP_TX_SC_DSGL >> + * @hw: HW module >> + * @req: IO request >> + * @sgl: ULP TX SGL pointer. >> + * >> + */ >> +#define csio_scsi_init_ultptx_dsgl(hw, req, sgl) \ >> +do { \ >> + struct ulptx_sge_pair *_sge_pair = NULL; \ >> + struct scatterlist *_sgel; \ >> + uint32_t _i = 0; \ >> + uint32_t _xfer_len; \ >> + struct list_head *_tmp; \ >> + struct csio_dma_buf *_dma_buf; \ >> + struct scsi_cmnd *scmnd = csio_scsi_cmnd((req)); \ >> + \ >> + (sgl)->cmd_nsge = htonl(ULPTX_CMD(ULP_TX_SC_DSGL) | ULPTX_MORE | \ >> + ULPTX_NSGE((req)->nsge)); \ >> + /* Now add the data SGLs */ \ >> + if (likely(!(req)->dcopy)) { \ >> + scsi_for_each_sg(scmnd, _sgel, (req)->nsge, _i) { \ >> + if (_i == 0) { \ >> + (sgl)->addr0 = cpu_to_be64( \ >> + sg_dma_address(_sgel)); \ >> + (sgl)->len0 = cpu_to_be32( \ >> + sg_dma_len(_sgel)); \ >> + _sge_pair = \ >> + (struct ulptx_sge_pair *)((sgl) + 1); \ >> + continue; \ >> + } \ >> + if ((_i - 1) & 0x1) { \ >> + _sge_pair->addr[1] = cpu_to_be64( \ >> + sg_dma_address(_sgel)); \ >> + _sge_pair->len[1] = cpu_to_be32( \ >> + sg_dma_len(_sgel)); \ >> + _sge_pair++; \ >> + } else { \ >> + _sge_pair->addr[0] = cpu_to_be64( \ >> + sg_dma_address(_sgel)); \ >> + _sge_pair->len[0] = cpu_to_be32( \ >> + sg_dma_len(_sgel)); \ >> + } \ >> + } \ >> + } else { \ >> + /* Program sg elements with driver's DDP buffer */ \ >> + _xfer_len = scsi_bufflen(scmnd); \ >> + list_for_each(_tmp, &(req)->gen_list) { \ >> + _dma_buf = (struct csio_dma_buf *)_tmp; \ >> + if (_i == 0) { \ >> + (sgl)->addr0 = cpu_to_be64(_dma_buf->paddr); \ >> + (sgl)->len0 = cpu_to_be32( \ >> + min(_xfer_len, _dma_buf->len)); \ >> + _sge_pair = \ >> + (struct ulptx_sge_pair *)((sgl) + 1); \ >> + } \ >> + else if ((_i - 1) & 0x1) { \ >> + _sge_pair->addr[1] = cpu_to_be64( \ >> + _dma_buf->paddr); \ >> + _sge_pair->len[1] = cpu_to_be32( \ >> + min(_xfer_len, _dma_buf->len)); \ >> + _sge_pair++; \ >> + } else { \ >> + _sge_pair->addr[0] = cpu_to_be64( \ >> + _dma_buf->paddr); \ >> + _sge_pair->len[0] = cpu_to_be32( \ >> + min(_xfer_len, _dma_buf->len)); \ >> + } \ >> + _xfer_len -= min(_xfer_len, _dma_buf->len); \ >> + _i++; \ >> + } \ >> + } \ >> +} while (0) >> + > > I don't see any reason why this can't just be a static function..? Why > is the macro usage necessary here..? OK, I will make this static-inline. > >> +/* >> + * csio_scsi_init_data_wr - Initialize the READ/WRITE SCSI WR. >> + * @req: IO req structure. >> + * @oper: read/write >> + * @wrp: DMA location to place the payload. >> + * @size: Size of WR (including FW WR + immed data + rsp SG entry + data SGL >> + * @wrop: _READ_/_WRITE_ >> + * >> + * Wrapper for populating fw_scsi_read_wr/fw_scsi_write_wr. >> + */ >> +#define csio_scsi_init_data_wr(req, oper, wrp, size, wrop) \ >> +do { \ >> + struct csio_hw *_hw = (req)->lnode->hwp; \ >> + struct csio_rnode *_rn = (req)->rnode; \ >> + struct fw_scsi_##oper##_wr *__wr = (struct fw_scsi_##oper##_wr *)(wrp);\ >> + struct ulptx_sgl *_sgl; \ >> + struct csio_dma_buf *_dma_buf; \ >> + uint8_t _imm = csio_hw_to_scsim(_hw)->proto_cmd_len; \ >> + struct scsi_cmnd *scmnd = csio_scsi_cmnd((req)); \ >> + \ >> + __wr->op_immdlen = cpu_to_be32(FW_WR_OP(FW_SCSI##wrop##WR) | \ >> + FW_SCSI##wrop##WR_IMMDLEN(_imm)); \ >> + __wr->flowid_len16 = cpu_to_be32(FW_WR_FLOWID(_rn->flowid) | \ >> + FW_WR_LEN16( \ >> + CSIO_ROUNDUP((size), 16))); \ >> + __wr->cookie = (uintptr_t) (req); \ >> + __wr->iqid = (uint16_t)cpu_to_be16(csio_q_physiqid(_hw, \ >> + (req)->iq_idx));\ >> + __wr->tmo_val = (uint8_t)((req)->tmo); \ >> + __wr->use_xfer_cnt = 1; \ >> + __wr->xfer_cnt = cpu_to_be32(scsi_bufflen(scmnd)); \ >> + __wr->ini_xfer_cnt = cpu_to_be32(scsi_bufflen(scmnd)); \ >> + /* Get RSP DMA buffer */ \ >> + _dma_buf = &(req)->dma_buf; \ >> + \ >> + /* Prepare RSP SGL */ \ >> + __wr->rsp_dmalen = cpu_to_be32(_dma_buf->len); \ >> + __wr->rsp_dmaaddr = cpu_to_be64(_dma_buf->paddr); \ >> + \ >> + __wr->r4 = 0; \ >> + \ >> + __wr->u.fcoe.ctl_pri = 0; \ >> + __wr->u.fcoe.cp_en_class = 0; \ >> + __wr->u.fcoe.r3_lo[0] = 0; \ >> + __wr->u.fcoe.r3_lo[1] = 0; \ >> + csio_scsi_fcp_cmnd((req), (void *)((uintptr_t)(wrp) + \ >> + sizeof(struct fw_scsi_##oper##_wr))); \ >> + \ >> + /* Move WR pointer past command and immediate data */ \ >> + _sgl = (struct ulptx_sgl *) ((uintptr_t)(wrp) + \ >> + sizeof(struct fw_scsi_##oper##_wr) + \ >> + ALIGN(_imm, 16)); \ >> + \ >> + /* Fill in the DSGL */ \ >> + csio_scsi_init_ultptx_dsgl(_hw, (req), _sgl); \ >> + \ >> +} while (0) >> + > > This one has four uses of CPP keys. Just turn those into macros, and > leave the rest of the code in a static function. > So what you are suggesting is to have all the lines of the macro csio_scsi_init_data_wr() added into a static function, but for the ones with the 4 keys. csio_scsi_init_data_wr() will then invoke this new function. Is that correct? >> +/* Calculate WR size needed for fw_scsi_read_wr/fw_scsi_write_wr */ >> +#define csio_scsi_data_wrsz(req, oper, sz, imm) \ >> +do { \ >> + (sz) = sizeof(struct fw_scsi_##oper##_wr) + /* WR size */ \ >> + ALIGN((imm), 16) + /* Immed data */ \ >> + sizeof(struct ulptx_sgl); /* ulptx_sgl */ \ >> + \ >> + if (unlikely((req)->nsge > 1)) \ >> + (sz) += (sizeof(struct ulptx_sge_pair) * \ >> + (ALIGN(((req)->nsge - 1), 2) / 2)); \ >> + /* Data SGE */ \ >> +} while (0) >> + >> +/* >> + * csio_scsi_data - Create a SCSI WRITE/READ WR. >> + * @req: IO req structure. >> + * @oper: read/write >> + * @wrop: _READ_/_WRITE_ (string subsitutions to use with the FW bit field >> + * macros). >> + * >> + * Gets a WR slot in the ingress queue and initializes it with >> + * SCSI CMD READ/WRITE WR. >> + * >> + */ >> +#define csio_scsi_data(req, oper, wrop) \ >> +do { \ >> + struct csio_wr_pair _wrp; \ >> + uint32_t _size; \ >> + struct csio_hw *_hw = (req)->lnode->hwp; \ >> + struct csio_scsim *_scsim = csio_hw_to_scsim(_hw); \ >> + \ >> + csio_scsi_data_wrsz((req), oper, _size, _scsim->proto_cmd_len); \ >> + _size = ALIGN(_size, 16); \ >> + \ >> + (req)->drv_status = csio_wr_get(_hw, (req)->eq_idx, _size, &_wrp); \ >> + if (likely((req)->drv_status == CSIO_SUCCESS)) { \ >> + if (likely(_wrp.size1 >= _size)) { \ >> + /* Initialize WR in one shot */ \ >> + csio_scsi_init_data_wr((req), oper, _wrp.addr1, \ >> + _size, wrop); \ >> + } else { \ >> + uint8_t tmpwr[512]; \ >> + /* \ >> + * Make a temporary copy of the WR and write back \ >> + * the copy into the WR pair. \ >> + */ \ >> + csio_scsi_init_data_wr((req), oper, (void *)tmpwr, \ >> + _size, wrop); \ >> + memcpy(_wrp.addr1, tmpwr, _wrp.size1); \ >> + memcpy(_wrp.addr2, tmpwr + _wrp.size1, \ >> + _size - _wrp.size1); \ >> + } \ >> + } \ >> +} while (0) >> + > > Ditto on this one, along with the tmpwr[512] stack usage.. > >> +static inline void >> +csio_scsi_abrt_cls(struct csio_ioreq *req, bool abort) >> +{ >> + struct csio_wr_pair wrp; >> + struct csio_hw *hw = req->lnode->hwp; >> + uint32_t size = ALIGN(sizeof(struct fw_scsi_abrt_cls_wr), 16); >> + >> + req->drv_status = csio_wr_get(hw, req->eq_idx, size, &wrp); >> + if (req->drv_status != CSIO_SUCCESS) >> + return; >> + >> + if (wrp.size1 >= size) { >> + /* Initialize WR in one shot */ >> + csio_scsi_init_abrt_cls_wr(req, wrp.addr1, size, abort); >> + } else { >> + uint8_t tmpwr[512]; > > Ditto here on local scope stack usage.. > >> + /* >> + * Make a temporary copy of the WR and write back >> + * the copy into the WR pair. >> + */ >> + csio_scsi_init_abrt_cls_wr(req, (void *)tmpwr, size, abort); >> + memcpy(wrp.addr1, tmpwr, wrp.size1); >> + memcpy(wrp.addr2, tmpwr + wrp.size1, size - wrp.size1); >> + } >> +} >> + >> +/*****************************************************************************/ >> +/* START: SCSI SM */ >> +/*****************************************************************************/ >> +static void >> +csio_scsis_uninit(struct csio_ioreq *req, enum csio_scsi_ev evt) >> +{ >> + struct csio_hw *hw = req->lnode->hwp; >> + struct csio_scsim *scsim = csio_hw_to_scsim(hw); >> + >> + switch (evt) { >> + >> + case CSIO_SCSIE_START_IO: > > Extra space between start of first switch case OK, I will remove it. Is there any tool that catches such deviations? checkpath.pl didnt report it. > >> + >> + /* There is data */ > > Point-less comment I will remove it. > >> + if (req->nsge) { >> + if (req->datadir == CSIO_IOREQF_DMA_WRITE) { >> + req->dcopy = 0; >> + csio_scsi_data(req, write, _WRITE_); >> + } else >> + csio_setup_ddp(scsim, req); >> + } else { >> + csio_scsi_cmd(req); >> + } >> + >> + if (likely(req->drv_status == CSIO_SUCCESS)) { >> + /* change state and enqueue on active_q */ >> + csio_set_state(&req->sm, csio_scsis_io_active); >> + list_add_tail(&req->sm.sm_list, &scsim->active_q); >> + csio_wr_issue(hw, req->eq_idx, CSIO_FALSE); >> + csio_inc_stats(scsim, n_active); >> + >> + return; >> + } >> + break; >> + >> + case CSIO_SCSIE_START_TM: >> + csio_scsi_cmd(req); >> + if (req->drv_status == CSIO_SUCCESS) { >> + /* >> + * NOTE: We collect the affected I/Os prior to issuing >> + * LUN reset, and not after it. This is to prevent >> + * aborting I/Os that get issued after the LUN reset, >> + * but prior to LUN reset completion (in the event that >> + * the host stack has not blocked I/Os to a LUN that is >> + * being reset. >> + */ >> + csio_set_state(&req->sm, csio_scsis_tm_active); >> + list_add_tail(&req->sm.sm_list, &scsim->active_q); >> + csio_wr_issue(hw, req->eq_idx, CSIO_FALSE); >> + csio_inc_stats(scsim, n_tm_active); >> + } >> + return; >> + >> + case CSIO_SCSIE_ABORT: >> + case CSIO_SCSIE_CLOSE: >> + /* >> + * NOTE: >> + * We could get here due to : >> + * - a window in the cleanup path of the SCSI module >> + * (csio_scsi_abort_io()). Please see NOTE in this function. >> + * - a window in the time we tried to issue an abort/close >> + * of a request to FW, and the FW completed the request >> + * itself. >> + * Print a message for now, and return INVAL either way. >> + */ >> + req->drv_status = CSIO_INVAL; >> + csio_warn(hw, "Trying to abort/close completed IO:%p!\n", req); >> + break; >> + >> + default: >> + csio_dbg(hw, "Unhandled event:%d sent to req:%p\n", evt, req); >> + CSIO_DB_ASSERT(0); >> + } >> +} >> + >> +static void >> +csio_scsis_io_active(struct csio_ioreq *req, enum csio_scsi_ev evt) >> +{ >> + struct csio_hw *hw = req->lnode->hwp; >> + struct csio_scsim *scm = csio_hw_to_scsim(hw); >> + struct csio_rnode *rn; >> + >> + switch (evt) { >> + >> + case CSIO_SCSIE_COMPLETED: > > Ditto > >> + csio_dec_stats(scm, n_active); >> + list_del_init(&req->sm.sm_list); >> + csio_set_state(&req->sm, csio_scsis_uninit); >> + /* >> + * In MSIX mode, with multiple queues, the SCSI compeltions >> + * could reach us sooner than the FW events sent to indicate >> + * I-T nexus loss (link down, remote device logo etc). We >> + * dont want to be returning such I/Os to the upper layer >> + * immediately, since we wouldnt have reported the I-T nexus >> + * loss itself. This forces us to serialize such completions >> + * with the reporting of the I-T nexus loss. Therefore, we >> + * internally queue up such up such completions in the rnode. >> + * The reporting of I-T nexus loss to the upper layer is then >> + * followed by the returning of I/Os in this internal queue. >> + * Having another state alongwith another queue helps us take >> + * actions for events such as ABORT received while we are >> + * in this rnode queue. >> + */ >> + if (unlikely(req->wr_status != FW_SUCCESS)) { >> + rn = req->rnode; >> + /* >> + * FW says remote device is lost, but rnode >> + * doesnt reflect it. >> + */ >> + if (csio_scsi_itnexus_loss_error(req->wr_status) && >> + csio_is_rnode_ready(rn)) { >> + csio_set_state(&req->sm, >> + csio_scsis_shost_cmpl_await); >> + list_add_tail(&req->sm.sm_list, >> + &rn->host_cmpl_q); >> + } >> + } >> + >> + break; >> + >> + case CSIO_SCSIE_ABORT: >> + csio_scsi_abrt_cls(req, SCSI_ABORT); >> + if (req->drv_status == CSIO_SUCCESS) { >> + csio_wr_issue(hw, req->eq_idx, CSIO_FALSE); >> + csio_set_state(&req->sm, csio_scsis_aborting); >> + } >> + break; >> + >> + case CSIO_SCSIE_CLOSE: >> + csio_scsi_abrt_cls(req, SCSI_CLOSE); >> + if (req->drv_status == CSIO_SUCCESS) { >> + csio_wr_issue(hw, req->eq_idx, CSIO_FALSE); >> + csio_set_state(&req->sm, csio_scsis_closing); >> + } >> + break; >> + >> + case CSIO_SCSIE_DRVCLEANUP: >> + req->wr_status = FW_HOSTERROR; >> + csio_dec_stats(scm, n_active); >> + csio_set_state(&req->sm, csio_scsis_uninit); >> + break; >> + >> + default: >> + csio_dbg(hw, "Unhandled event:%d sent to req:%p\n", evt, req); >> + CSIO_DB_ASSERT(0); >> + } >> +} >> + >> +static void >> +csio_scsis_tm_active(struct csio_ioreq *req, enum csio_scsi_ev evt) >> +{ >> + struct csio_hw *hw = req->lnode->hwp; >> + struct csio_scsim *scm = csio_hw_to_scsim(hw); >> + >> + switch (evt) { >> + >> + case CSIO_SCSIE_COMPLETED: > > Ditto > >> + csio_dec_stats(scm, n_tm_active); >> + list_del_init(&req->sm.sm_list); >> + csio_set_state(&req->sm, csio_scsis_uninit); >> + >> + break; >> + >> + case CSIO_SCSIE_ABORT: >> + csio_scsi_abrt_cls(req, SCSI_ABORT); >> + if (req->drv_status == CSIO_SUCCESS) { >> + csio_wr_issue(hw, req->eq_idx, CSIO_FALSE); >> + csio_set_state(&req->sm, csio_scsis_aborting); >> + } >> + break; >> + >> + >> + case CSIO_SCSIE_CLOSE: >> + csio_scsi_abrt_cls(req, SCSI_CLOSE); >> + if (req->drv_status == CSIO_SUCCESS) { >> + csio_wr_issue(hw, req->eq_idx, CSIO_FALSE); >> + csio_set_state(&req->sm, csio_scsis_closing); >> + } >> + break; >> + >> + case CSIO_SCSIE_DRVCLEANUP: >> + req->wr_status = FW_HOSTERROR; >> + csio_dec_stats(scm, n_tm_active); >> + csio_set_state(&req->sm, csio_scsis_uninit); >> + break; >> + >> + default: >> + csio_dbg(hw, "Unhandled event:%d sent to req:%p\n", evt, req); >> + CSIO_DB_ASSERT(0); >> + } >> +} >> + >> +static void >> +csio_scsis_aborting(struct csio_ioreq *req, enum csio_scsi_ev evt) >> +{ >> + struct csio_hw *hw = req->lnode->hwp; >> + struct csio_scsim *scm = csio_hw_to_scsim(hw); >> + >> + switch (evt) { >> + >> + case CSIO_SCSIE_COMPLETED: > > Ditto > >> + csio_dbg(hw, >> + "ioreq %p recvd cmpltd (wr_status:%d) " >> + "in aborting st\n", req, req->wr_status); >> + /* >> + * Use CSIO_CANCELLED to explicitly tell the ABORTED event that >> + * the original I/O was returned to driver by FW. >> + * We dont really care if the I/O was returned with success by >> + * FW (because the ABORT and completion of the I/O crossed each >> + * other), or any other return value. Once we are in aborting >> + * state, the success or failure of the I/O is unimportant to >> + * us. >> + */ >> + req->drv_status = CSIO_CANCELLED; >> + break; >> + >> + case CSIO_SCSIE_ABORT: >> + csio_inc_stats(scm, n_abrt_dups); >> + break; >> + >> + case CSIO_SCSIE_ABORTED: >> + >> + csio_dbg(hw, "abort of %p return status:0x%x drv_status:%x\n", >> + req, req->wr_status, req->drv_status); >> + /* >> + * Check if original I/O WR completed before the Abort >> + * completion. >> + */ >> + if (req->drv_status != CSIO_CANCELLED) { >> + csio_fatal(hw, >> + "Abort completed before original I/O," >> + " req:%p\n", req); >> + CSIO_DB_ASSERT(0); >> + } >> + >> + /* >> + * There are the following possible scenarios: >> + * 1. The abort completed successfully, FW returned FW_SUCCESS. >> + * 2. The completion of an I/O and the receipt of >> + * abort for that I/O by the FW crossed each other. >> + * The FW returned FW_EINVAL. The original I/O would have >> + * returned with FW_SUCCESS or any other SCSI error. >> + * 3. The FW couldnt sent the abort out on the wire, as there >> + * was an I-T nexus loss (link down, remote device logged >> + * out etc). FW sent back an appropriate IT nexus loss status >> + * for the abort. >> + * 4. FW sent an abort, but abort timed out (remote device >> + * didnt respond). FW replied back with >> + * FW_SCSI_ABORT_TIMEDOUT. >> + * 5. FW couldnt genuinely abort the request for some reason, >> + * and sent us an error. >> + * >> + * The first 3 scenarios are treated as succesful abort >> + * operations by the host, while the last 2 are failed attempts >> + * to abort. Manipulate the return value of the request >> + * appropriately, so that host can convey these results >> + * back to the upper layer. >> + */ >> + if ((req->wr_status == FW_SUCCESS) || >> + (req->wr_status == FW_EINVAL) || >> + csio_scsi_itnexus_loss_error(req->wr_status)) >> + req->wr_status = FW_SCSI_ABORT_REQUESTED; >> + >> + csio_dec_stats(scm, n_active); >> + list_del_init(&req->sm.sm_list); >> + csio_set_state(&req->sm, csio_scsis_uninit); >> + break; >> + >> + case CSIO_SCSIE_DRVCLEANUP: >> + req->wr_status = FW_HOSTERROR; >> + csio_dec_stats(scm, n_active); >> + csio_set_state(&req->sm, csio_scsis_uninit); >> + break; >> + >> + case CSIO_SCSIE_CLOSE: >> + /* >> + * We can receive this event from the module >> + * cleanup paths, if the FW forgot to reply to the ABORT WR >> + * and left this ioreq in this state. For now, just ignore >> + * the event. The CLOSE event is sent to this state, as >> + * the LINK may have already gone down. >> + */ >> + break; >> + >> + default: >> + csio_dbg(hw, "Unhandled event:%d sent to req:%p\n", evt, req); >> + CSIO_DB_ASSERT(0); >> + } >> +} >> + >> +static void >> +csio_scsis_closing(struct csio_ioreq *req, enum csio_scsi_ev evt) >> +{ >> + struct csio_hw *hw = req->lnode->hwp; >> + struct csio_scsim *scm = csio_hw_to_scsim(hw); >> + >> + switch (evt) { >> + >> + case CSIO_SCSIE_COMPLETED: > > Ditto > >> + csio_dbg(hw, >> + "ioreq %p recvd cmpltd (wr_status:%d) " >> + "in closing st\n", req, req->wr_status); >> + /* >> + * Use CSIO_CANCELLED to explicitly tell the CLOSED event that >> + * the original I/O was returned to driver by FW. >> + * We dont really care if the I/O was returned with success by >> + * FW (because the CLOSE and completion of the I/O crossed each >> + * other), or any other return value. Once we are in aborting >> + * state, the success or failure of the I/O is unimportant to >> + * us. >> + */ >> + req->drv_status = CSIO_CANCELLED; >> + break; >> + >> + case CSIO_SCSIE_CLOSED: >> + /* >> + * Check if original I/O WR completed before the Close >> + * completion. >> + */ >> + if (req->drv_status != CSIO_CANCELLED) { >> + csio_fatal(hw, >> + "Close completed before original I/O," >> + " req:%p\n", req); >> + CSIO_DB_ASSERT(0); >> + } >> + >> + /* >> + * Either close succeeded, or we issued close to FW at the >> + * same time FW compelted it to us. Either way, the I/O >> + * is closed. >> + */ >> + CSIO_DB_ASSERT((req->wr_status == FW_SUCCESS) || >> + (req->wr_status == FW_EINVAL)); >> + req->wr_status = FW_SCSI_CLOSE_REQUESTED; >> + >> + csio_dec_stats(scm, n_active); >> + list_del_init(&req->sm.sm_list); >> + csio_set_state(&req->sm, csio_scsis_uninit); >> + break; >> + >> + case CSIO_SCSIE_CLOSE: >> + break; >> + >> + case CSIO_SCSIE_DRVCLEANUP: >> + req->wr_status = FW_HOSTERROR; >> + csio_dec_stats(scm, n_active); >> + csio_set_state(&req->sm, csio_scsis_uninit); >> + break; >> + >> + default: >> + csio_dbg(hw, "Unhandled event:%d sent to req:%p\n", evt, req); >> + CSIO_DB_ASSERT(0); >> + } >> +} >> + >> +static void >> +csio_scsis_shost_cmpl_await(struct csio_ioreq *req, enum csio_scsi_ev evt) >> +{ >> + switch (evt) { >> + case CSIO_SCSIE_ABORT: >> + case CSIO_SCSIE_CLOSE: >> + /* >> + * Just succeed the abort request, and hope that >> + * the remote device unregister path will cleanup >> + * this I/O to the upper layer within a sane >> + * amount of time. >> + */ >> + /* >> + * A close can come in during a LINK DOWN. The FW would have >> + * returned us the I/O back, but not the remote device lost >> + * FW event. In this interval, if the I/O times out at the upper >> + * layer, a close can come in. Take the same action as abort: >> + * return success, and hope that the remote device unregister >> + * path will cleanup this I/O. If the FW still doesnt send >> + * the msg, the close times out, and the upper layer resorts >> + * to the next level of error recovery. >> + */ >> + req->drv_status = CSIO_SUCCESS; >> + break; >> + case CSIO_SCSIE_DRVCLEANUP: >> + csio_set_state(&req->sm, csio_scsis_uninit); >> + break; >> + default: >> + csio_dbg(req->lnode->hwp, "Unhandled event:%d sent to req:%p\n", >> + evt, req); >> + CSIO_DB_ASSERT(0); >> + } >> +} >> + > > >> + >> +/** >> + * csio_queuecommand_lck - Entry point to kickstart an I/O request. >> + * @cmnd: The I/O request from ML. >> + * @done: The ML callback routine. >> + * >> + * 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_lck(struct scsi_cmnd *cmnd, void (*done)(struct scsi_cmnd *)) >> +{ >> + struct csio_lnode *ln = shost_priv(cmnd->device->host); >> + struct csio_hw *hw = csio_lnode_to_hw(ln); >> + struct csio_scsim *scsim = csio_hw_to_scsim(hw); >> + struct csio_rnode *rn = (struct csio_rnode *)(cmnd->device->hostdata); >> + struct csio_ioreq *ioreq = NULL; >> + unsigned long flags; >> + int nsge = 0; >> + int rv = SCSI_MLQUEUE_HOST_BUSY, nr; >> + csio_retval_t retval; >> + int cpu; >> + struct csio_scsi_qset *sqset; >> + struct fc_rport *rport = starget_to_rport(scsi_target(cmnd->device)); >> + >> + if (!blk_rq_cpu_valid(cmnd->request)) >> + cpu = smp_processor_id(); >> + else >> + cpu = cmnd->request->cpu; >> + >> + sqset = &hw->sqset[ln->portid][cpu]; >> + >> + nr = fc_remote_port_chkready(rport); >> + if (nr) { >> + cmnd->result = nr; >> + csio_inc_stats(scsim, n_rn_nr_error); >> + goto err_done; >> + } >> + >> + if (unlikely(!csio_is_hw_ready(hw))) { >> + cmnd->result = (DID_REQUEUE << 16); >> + csio_inc_stats(scsim, n_hw_nr_error); >> + goto err_done; >> + } >> + >> + /* Get req->nsge, if there are SG elements to be mapped */ >> + nsge = scsi_dma_map(cmnd); >> + if (unlikely(nsge < 0)) { >> + csio_inc_stats(scsim, n_dmamap_error); >> + goto err; >> + } >> + >> + /* Do we support so many mappings? */ >> + if (unlikely(nsge > scsim->max_sge)) { >> + csio_warn(hw, >> + "More SGEs than can be supported." >> + " SGEs: %d, Max SGEs: %d\n", nsge, scsim->max_sge); >> + csio_inc_stats(scsim, n_unsupp_sge_error); >> + goto err_dma_unmap; >> + } >> + >> + /* Get a free ioreq structure - SM is already set to uninit */ >> + ioreq = csio_get_scsi_ioreq_lock(hw, scsim); >> + if (!ioreq) { >> + csio_err(hw, "Out of I/O request elements. Active #:%d\n", >> + scsim->stats.n_active); >> + csio_inc_stats(scsim, n_no_req_error); >> + goto err_dma_unmap; >> + } >> + >> + ioreq->nsge = nsge; >> + ioreq->lnode = ln; >> + ioreq->rnode = rn; >> + ioreq->iq_idx = sqset->iq_idx; >> + ioreq->eq_idx = sqset->eq_idx; >> + ioreq->wr_status = 0; >> + ioreq->drv_status = CSIO_SUCCESS; >> + csio_scsi_cmnd(ioreq) = (void *)cmnd; >> + ioreq->tmo = 0; >> + >> + switch (cmnd->sc_data_direction) { >> + case DMA_BIDIRECTIONAL: >> + ioreq->datadir = CSIO_IOREQF_DMA_BIDI; >> + csio_inc_stats(ln, n_control_requests); >> + break; >> + case DMA_TO_DEVICE: >> + ioreq->datadir = CSIO_IOREQF_DMA_WRITE; >> + csio_inc_stats(ln, n_output_requests); >> + ln->stats.n_output_bytes += scsi_bufflen(cmnd); >> + break; >> + case DMA_FROM_DEVICE: >> + ioreq->datadir = CSIO_IOREQF_DMA_READ; >> + csio_inc_stats(ln, n_input_requests); >> + ln->stats.n_input_bytes += scsi_bufflen(cmnd); >> + break; >> + case DMA_NONE: >> + ioreq->datadir = CSIO_IOREQF_DMA_NONE; >> + csio_inc_stats(ln, n_control_requests); >> + break; >> + default: >> + CSIO_DB_ASSERT(0); >> + break; >> + } >> + >> + /* Set cbfn */ >> + ioreq->io_cbfn = csio_scsi_cbfn; >> + >> + /* Needed during abort */ >> + cmnd->host_scribble = (unsigned char *)ioreq; >> + cmnd->scsi_done = done; >> + cmnd->SCp.Message = 0; >> + >> + /* Kick off SCSI IO SM on the ioreq */ >> + spin_lock_irqsave(&hw->lock, flags); >> + retval = csio_scsi_start_io(ioreq); >> + spin_unlock_irqrestore(&hw->lock, flags); >> + >> + if (retval != CSIO_SUCCESS) { >> + csio_err(hw, "ioreq: %p couldnt be started, status:%d\n", >> + ioreq, retval); >> + csio_inc_stats(scsim, n_busy_error); >> + goto err_put_req; >> + } >> + >> + return 0; >> + >> +err_put_req: >> + csio_put_scsi_ioreq_lock(hw, scsim, ioreq); >> +err_dma_unmap: >> + if (nsge > 0) >> + scsi_dma_unmap(cmnd); >> +err: >> + return rv; >> + >> +err_done: >> + done(cmnd); >> + return 0; >> +} >> + >> +static DEF_SCSI_QCMD(csio_queuecommand); >> + > > This means that your running with the host_lock held.. I'm not sure if > that is really what you want to do as it really end's up killing > multi-lun small packet performance.. > > How about dropping DEF_SCSI_QCMD usage here, and figure out what > actually needs to be protected by the SCSI host_lock within > csio_queuecommand_lck()..? It is on my TODO list for the next version of the driver, after the initial submission. Per the current design, we shouldnt need the host_lock to be held, but I would like to test this change thoroughly before I submit it. >> +static csio_retval_t >> +csio_do_abrt_cls(struct csio_hw *hw, struct csio_ioreq *ioreq, bool abort) >> +{ >> + csio_retval_t rv; >> + int cpu = smp_processor_id(); >> + struct csio_lnode *ln = ioreq->lnode; >> + struct csio_scsi_qset *sqset = &hw->sqset[ln->portid][cpu]; >> + >> + ioreq->tmo = CSIO_SCSI_ABRT_TMO_MS; >> + /* >> + * Use current processor queue for posting the abort/close, but retain >> + * the ingress queue ID of the original I/O being aborted/closed - we >> + * need the abort/close completion to be received on the same queue >> + * as the original I/O. >> + */ >> + ioreq->eq_idx = sqset->eq_idx; >> + >> + if (abort == SCSI_ABORT) >> + rv = csio_scsi_abort(ioreq); >> + else /* close */ > > Point-less comment. I will remove it. > >> + rv = csio_scsi_close(ioreq); >> + >> + return rv; >> +} >> + >> +static int >> +csio_eh_abort_handler(struct scsi_cmnd *cmnd) >> +{ >> + struct csio_ioreq *ioreq; >> + struct csio_lnode *ln = shost_priv(cmnd->device->host); >> + struct csio_hw *hw = csio_lnode_to_hw(ln); >> + struct csio_scsim *scsim = csio_hw_to_scsim(hw); >> + int ready = 0, ret; >> + unsigned long tmo = 0; >> + csio_retval_t rv; >> + struct csio_rnode *rn = (struct csio_rnode *)(cmnd->device->hostdata); >> + >> + ret = fc_block_scsi_eh(cmnd); >> + if (ret) >> + return ret; >> + >> + ioreq = (struct csio_ioreq *)cmnd->host_scribble; >> + if (!ioreq) >> + return SUCCESS; >> + >> + if (!rn) >> + return FAILED; >> + >> + csio_dbg(hw, >> + "Request to abort ioreq:%p cmd:%p cdb:%08llx" >> + " ssni:0x%x lun:%d iq:0x%x\n", >> + ioreq, cmnd, *((uint64_t *)cmnd->cmnd), rn->flowid, >> + cmnd->device->lun, csio_q_physiqid(hw, ioreq->iq_idx)); >> + >> + if (((struct scsi_cmnd *)csio_scsi_cmnd(ioreq)) != cmnd) { >> + csio_inc_stats(scsim, n_abrt_race_comp); >> + return SUCCESS; >> + } >> + >> + ready = csio_is_lnode_ready(ln); >> + tmo = CSIO_SCSI_ABRT_TMO_MS; >> + >> + spin_lock_irq(&hw->lock); >> + rv = csio_do_abrt_cls(hw, ioreq, (ready ? SCSI_ABORT : SCSI_CLOSE)); >> + spin_unlock_irq(&hw->lock); >> + >> + if (rv != CSIO_SUCCESS) { >> + if (rv == CSIO_INVAL) { >> + /* Return success, if abort/close request issued on >> + * already completed IO >> + */ >> + return SUCCESS; >> + } >> + if (ready) >> + csio_inc_stats(scsim, n_abrt_busy_error); >> + else >> + csio_inc_stats(scsim, n_cls_busy_error); >> + >> + goto inval_scmnd; >> + } >> + >> + /* Wait for completion */ >> + init_completion(&ioreq->cmplobj); >> + wait_for_completion_timeout(&ioreq->cmplobj, msecs_to_jiffies(tmo)); >> + >> + /* FW didnt respond to abort within our timeout */ >> + if (((struct scsi_cmnd *)csio_scsi_cmnd(ioreq)) == cmnd) { >> + >> + csio_err(hw, "Abort timed out -- req: %p\n", ioreq); >> + csio_inc_stats(scsim, n_abrt_timedout); >> + >> +inval_scmnd: >> + if (ioreq->nsge > 0) >> + scsi_dma_unmap(cmnd); >> + >> + spin_lock_irq(&hw->lock); >> + csio_scsi_cmnd(ioreq) = NULL; >> + spin_unlock_irq(&hw->lock); >> + >> + cmnd->result = (DID_ERROR << 16); >> + cmnd->scsi_done(cmnd); >> + >> + return FAILED; >> + } >> + >> + /* FW successfully aborted the request */ >> + if (host_byte(cmnd->result) == DID_REQUEUE) { >> + csio_info(hw, >> + "Aborted SCSI command to (%d:%d) serial#:0x%lx\n", >> + cmnd->device->id, cmnd->device->lun, >> + cmnd->serial_number); >> + return SUCCESS; >> + } else { >> + csio_info(hw, >> + "Failed to abort SCSI command, (%d:%d) serial#:0x%lx\n", >> + cmnd->device->id, cmnd->device->lun, >> + cmnd->serial_number); >> + return FAILED; >> + } >> +} >> + > >> +struct scsi_host_template csio_fcoe_shost_template = { >> + .module = THIS_MODULE, >> + .name = CSIO_DRV_DESC, >> + .proc_name = KBUILD_MODNAME, >> + .queuecommand = csio_queuecommand, >> + .eh_abort_handler = csio_eh_abort_handler, >> + .eh_device_reset_handler = csio_eh_lun_reset_handler, >> + .slave_alloc = csio_slave_alloc, >> + .slave_configure = csio_slave_configure, >> + .slave_destroy = csio_slave_destroy, >> + .scan_finished = csio_scan_finished, >> + .this_id = -1, >> + .sg_tablesize = CSIO_SCSI_MAX_SGE, >> + .cmd_per_lun = CSIO_MAX_CMD_PER_LUN, >> + .use_clustering = ENABLE_CLUSTERING, >> + .shost_attrs = csio_fcoe_lport_attrs, >> + .max_sectors = CSIO_MAX_SECTOR_SIZE, >> +}; >> + >> +struct scsi_host_template csio_fcoe_shost_vport_template = { >> + .module = THIS_MODULE, >> + .name = CSIO_DRV_DESC, >> + .proc_name = KBUILD_MODNAME, >> + .queuecommand = csio_queuecommand, >> + .eh_abort_handler = csio_eh_abort_handler, >> + .eh_device_reset_handler = csio_eh_lun_reset_handler, >> + .slave_alloc = csio_slave_alloc, >> + .slave_configure = csio_slave_configure, >> + .slave_destroy = csio_slave_destroy, >> + .scan_finished = csio_scan_finished, >> + .this_id = -1, >> + .sg_tablesize = CSIO_SCSI_MAX_SGE, >> + .cmd_per_lun = CSIO_MAX_CMD_PER_LUN, >> + .use_clustering = ENABLE_CLUSTERING, >> + .shost_attrs = csio_fcoe_vport_attrs, >> + .max_sectors = CSIO_MAX_SECTOR_SIZE, >> +}; >> + >> +/* >> + * 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 csio_retval_t >> +csio_scsi_alloc_ddp_bufs(struct csio_scsim *scm, struct csio_hw *hw, >> + int buf_size, int num_buf) >> +{ >> + int n = 0; >> + struct list_head *tmp; >> + struct csio_dma_buf *ddp_desc = NULL; >> + uint32_t unit_size = 0; >> + >> + if (!num_buf) >> + return CSIO_SUCCESS; > > Just return 0..? Since there is a comment about driver-internal return values in your review of patch 6, I would like to take up this discussion in that thread. > >> + >> + if (!buf_size) >> + return CSIO_INVAL; > > Just return -EINVAL..? > >> + >> + INIT_LIST_HEAD(&scm->ddp_freelist); >> + >> + /* Align buf size to page size */ >> + buf_size = (buf_size + PAGE_SIZE - 1) & PAGE_MASK; >> + /* Initialize dma descriptors */ >> + for (n = 0; n < num_buf; n++) { >> + /* Set unit size to request size */ >> + unit_size = buf_size; >> + ddp_desc = kzalloc(sizeof(struct csio_dma_buf), GFP_KERNEL); >> + if (!ddp_desc) { >> + csio_err(hw, >> + "Failed to allocate ddp descriptors," >> + " Num allocated = %d.\n", >> + scm->stats.n_free_ddp); >> + goto no_mem; >> + } >> + >> + /* Allocate Dma buffers for DDP */ >> + ddp_desc->vaddr = pci_alloc_consistent(hw->pdev, unit_size, >> + &ddp_desc->paddr); >> + if (!ddp_desc->vaddr) { >> + csio_err(hw, >> + "SCSI response DMA buffer (ddp) allocation" >> + " failed!\n"); >> + kfree(ddp_desc); >> + goto no_mem; >> + } >> + >> + ddp_desc->len = unit_size; >> + >> + /* Added it to scsi ddp freelist */ >> + list_add_tail(&ddp_desc->list, &scm->ddp_freelist); >> + csio_inc_stats(scm, n_free_ddp); >> + } >> + >> + return CSIO_SUCCESS; > > Just return 0..? > >> +no_mem: >> + /* release dma descs back to freelist and free dma memory */ >> + list_for_each(tmp, &scm->ddp_freelist) { >> + ddp_desc = (struct csio_dma_buf *) tmp; >> + tmp = csio_list_prev(tmp); >> + pci_free_consistent(hw->pdev, ddp_desc->len, ddp_desc->vaddr, >> + ddp_desc->paddr); >> + list_del_init(&ddp_desc->list); >> + kfree(ddp_desc); >> + } >> + scm->stats.n_free_ddp = 0; >> + >> + return CSIO_NOMEM; > > This should be just -ENOMEM..? > >> +} >> + >> +/* >> + * csio_scsi_free_ddp_bufs - free DDP buffers of unaligned SGLs. >> + * @scm: SCSI Module >> + * @hw: HW device. >> + * >> + * This routine frees ddp buffers. >> + */ >> +static csio_retval_t >> +csio_scsi_free_ddp_bufs(struct csio_scsim *scm, struct csio_hw *hw) >> +{ >> + struct list_head *tmp; >> + struct csio_dma_buf *ddp_desc; >> + >> + /* release dma descs back to freelist and free dma memory */ >> + list_for_each(tmp, &scm->ddp_freelist) { >> + ddp_desc = (struct csio_dma_buf *) tmp; >> + tmp = csio_list_prev(tmp); >> + pci_free_consistent(hw->pdev, ddp_desc->len, ddp_desc->vaddr, >> + ddp_desc->paddr); >> + list_del_init(&ddp_desc->list); >> + kfree(ddp_desc); >> + } >> + scm->stats.n_free_ddp = 0; >> + >> + return CSIO_NOMEM; >> +} > > Ditto.. Just -ENOMEM..? > > -- To unsubscribe from this list: send the line "unsubscribe linux-scsi" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html