Quellcodebibliothek Statistik Leitseite products/Sources/formale Sprachen/C/Linux/drivers/scsi/be2iscsi/   (Open Source Betriebssystem Version 6.17.9©)  Datei vom 24.10.2025 mit Größe 163 kB image not shown  

Quelle  be_main.c   Sprache: C

 
/*
 * This file is part of the Emulex Linux Device Driver for Enterprise iSCSI
 * Host Bus Adapters. Refer to the README file included with this package
 * for driver version and adapter compatibility.
 *
 * Copyright (c) 2018 Broadcom. All Rights Reserved.
 * The term “Broadcom” refers to Broadcom Inc. and/or its subsidiaries.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of version 2 of the GNU General Public License as published
 * by the Free Software Foundation.
 *
 * This program is distributed in the hope that it will be useful. ALL EXPRESS
 * OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, INCLUDING ANY
 * IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE,
 * OR NON-INFRINGEMENT, ARE DISCLAIMED, EXCEPT TO THE EXTENT THAT SUCH
 * DISCLAIMERS ARE HELD TO BE LEGALLY INVALID.
 * See the GNU General Public License for more details, a copy of which
 * can be found in the file COPYING included with this package.
 *
 * Contact Information:
 * linux-drivers@broadcom.com
 *
 */


#include <linux/reboot.h>
#include <linux/delay.h>
#include <linux/slab.h>
#include <linux/interrupt.h>
#include <linux/blkdev.h>
#include <linux/pci.h>
#include <linux/string.h>
#include <linux/kernel.h>
#include <linux/semaphore.h>
#include <linux/iscsi_boot_sysfs.h>
#include <linux/module.h>
#include <linux/bsg-lib.h>
#include <linux/irq_poll.h>

#include <scsi/libiscsi.h>
#include <scsi/scsi_bsg_iscsi.h>
#include <scsi/scsi_netlink.h>
#include <scsi/scsi_transport_iscsi.h>
#include <scsi/scsi_transport.h>
#include <scsi/scsi_cmnd.h>
#include <scsi/scsi_device.h>
#include <scsi/scsi_host.h>
#include <scsi/scsi.h>
#include "be_main.h"
#include "be_iscsi.h"
#include "be_mgmt.h"
#include "be_cmds.h"

static unsigned int be_iopoll_budget = 10;
static unsigned int be_max_phys_size = 64;
static unsigned int enable_msix = 1;

MODULE_DESCRIPTION(DRV_DESC " " BUILD_STR);
MODULE_VERSION(BUILD_STR);
MODULE_AUTHOR("Emulex Corporation");
MODULE_LICENSE("GPL");
module_param(be_iopoll_budget, int, 0);
module_param(enable_msix, int, 0);
module_param(be_max_phys_size, uint, S_IRUGO);
MODULE_PARM_DESC(be_max_phys_size,
  "Maximum Size (In Kilobytes) of physically contiguous "
  "memory that can be allocated. Range is 16 - 128");

#define beiscsi_disp_param(_name)\
static ssize_t \
beiscsi_##_name##_disp(struct device *dev,\
   struct device_attribute *attrib, char *buf) \
{ \
 struct Scsi_Host *shost = class_to_shost(dev);\
 struct beiscsi_hba *phba = iscsi_host_priv(shost); \
 return snprintf(buf, PAGE_SIZE, "%d\n",\
   phba->attr_##_name);\
}

#define beiscsi_change_param(_name, _minval, _maxval, _defaval)\
static int \
beiscsi_##_name##_change(struct beiscsi_hba *phba, uint32_t val)\
{\
 if (val >= _minval && val <= _maxval) {\
  beiscsi_log(phba, KERN_ERR, BEISCSI_LOG_INIT,\
       "BA_%d : beiscsi_"#_name" updated "\
       "from 0x%x ==> 0x%x\n",\
       phba->attr_##_name, val); \
  phba->attr_##_name = val;\
  return 0;\
 } \
 beiscsi_log(phba, KERN_ERR, BEISCSI_LOG_INIT, \
      "BA_%d beiscsi_"#_name" attribute "\
      "cannot be updated to 0x%x, "\
      "range allowed is ["#_minval" - "#_maxval"]\n", val);\
  return -EINVAL;\
}

#define beiscsi_store_param(_name)  \
static ssize_t \
beiscsi_##_name##_store(struct device *dev,\
    struct device_attribute *attr, const char *buf,\
    size_t count) \
{ \
 struct Scsi_Host  *shost = class_to_shost(dev);\
 struct beiscsi_hba *phba = iscsi_host_priv(shost);\
 uint32_t param_val = 0;\
 if (!isdigit(buf[0]))\
  return -EINVAL;\
 if (sscanf(buf, "%i", ¶m_val) != 1)\
  return -EINVAL;\
 if (beiscsi_##_name##_change(phba, param_val) == 0) \
  return strlen(buf);\
 else \
  return -EINVAL;\
}

#define beiscsi_init_param(_name, _minval, _maxval, _defval) \
static int \
beiscsi_##_name##_init(struct beiscsi_hba *phba, uint32_t val) \
{ \
 if (val >= _minval && val <= _maxval) {\
  phba->attr_##_name = val;\
  return 0;\
 } \
 beiscsi_log(phba, KERN_ERR, BEISCSI_LOG_INIT,\
      "BA_%d beiscsi_"#_name" attribute " \
      "cannot be updated to 0x%x, "\
      "range allowed is ["#_minval" - "#_maxval"]\n", val);\
 phba->attr_##_name = _defval;\
 return -EINVAL;\
}

#define BEISCSI_RW_ATTR(_name, _minval, _maxval, _defval, _descp) \
static uint beiscsi_##_name = _defval;\
module_param(beiscsi_##_name, uint, S_IRUGO);\
MODULE_PARM_DESC(beiscsi_##_name, _descp);\
beiscsi_disp_param(_name)\
beiscsi_change_param(_name, _minval, _maxval, _defval)\
beiscsi_store_param(_name)\
beiscsi_init_param(_name, _minval, _maxval, _defval)\
static DEVICE_ATTR(beiscsi_##_name, S_IRUGO | S_IWUSR,\
       beiscsi_##_name##_disp, beiscsi_##_name##_store)

/*
 * When new log level added update MAX allowed value for log_enable
 */

BEISCSI_RW_ATTR(log_enable, 0x00,
  0xFF, 0x00, "Enable logging Bit Mask\n"
  "\t\t\t\tInitialization Events : 0x01\n"
  "\t\t\t\tMailbox Events : 0x02\n"
  "\t\t\t\tMiscellaneous Events : 0x04\n"
  "\t\t\t\tError Handling : 0x08\n"
  "\t\t\t\tIO Path Events : 0x10\n"
  "\t\t\t\tConfiguration Path : 0x20\n"
  "\t\t\t\tiSCSI Protocol : 0x40\n");

static DEVICE_ATTR(beiscsi_drvr_ver, S_IRUGO, beiscsi_drvr_ver_disp, NULL);
static DEVICE_ATTR(beiscsi_adapter_family, S_IRUGO, beiscsi_adap_family_disp, NULL);
static DEVICE_ATTR(beiscsi_fw_ver, S_IRUGO, beiscsi_fw_ver_disp, NULL);
static DEVICE_ATTR(beiscsi_phys_port, S_IRUGO, beiscsi_phys_port_disp, NULL);
static DEVICE_ATTR(beiscsi_active_session_count, S_IRUGO,
     beiscsi_active_session_disp, NULL);
static DEVICE_ATTR(beiscsi_free_session_count, S_IRUGO,
     beiscsi_free_session_disp, NULL);

static struct attribute *beiscsi_attrs[] = {
 &dev_attr_beiscsi_log_enable.attr,
 &dev_attr_beiscsi_drvr_ver.attr,
 &dev_attr_beiscsi_adapter_family.attr,
 &dev_attr_beiscsi_fw_ver.attr,
 &dev_attr_beiscsi_active_session_count.attr,
 &dev_attr_beiscsi_free_session_count.attr,
 &dev_attr_beiscsi_phys_port.attr,
 NULL,
};

ATTRIBUTE_GROUPS(beiscsi);

static char const *cqe_desc[] = {
 "RESERVED_DESC",
 "SOL_CMD_COMPLETE",
 "SOL_CMD_KILLED_DATA_DIGEST_ERR",
 "CXN_KILLED_PDU_SIZE_EXCEEDS_DSL",
 "CXN_KILLED_BURST_LEN_MISMATCH",
 "CXN_KILLED_AHS_RCVD",
 "CXN_KILLED_HDR_DIGEST_ERR",
 "CXN_KILLED_UNKNOWN_HDR",
 "CXN_KILLED_STALE_ITT_TTT_RCVD",
 "CXN_KILLED_INVALID_ITT_TTT_RCVD",
 "CXN_KILLED_RST_RCVD",
 "CXN_KILLED_TIMED_OUT",
 "CXN_KILLED_RST_SENT",
 "CXN_KILLED_FIN_RCVD",
 "CXN_KILLED_BAD_UNSOL_PDU_RCVD",
 "CXN_KILLED_BAD_WRB_INDEX_ERROR",
 "CXN_KILLED_OVER_RUN_RESIDUAL",
 "CXN_KILLED_UNDER_RUN_RESIDUAL",
 "CMD_KILLED_INVALID_STATSN_RCVD",
 "CMD_KILLED_INVALID_R2T_RCVD",
 "CMD_CXN_KILLED_LUN_INVALID",
 "CMD_CXN_KILLED_ICD_INVALID",
 "CMD_CXN_KILLED_ITT_INVALID",
 "CMD_CXN_KILLED_SEQ_OUTOFORDER",
 "CMD_CXN_KILLED_INVALID_DATASN_RCVD",
 "CXN_INVALIDATE_NOTIFY",
 "CXN_INVALIDATE_INDEX_NOTIFY",
 "CMD_INVALIDATED_NOTIFY",
 "UNSOL_HDR_NOTIFY",
 "UNSOL_DATA_NOTIFY",
 "UNSOL_DATA_DIGEST_ERROR_NOTIFY",
 "DRIVERMSG_NOTIFY",
 "CXN_KILLED_CMND_DATA_NOT_ON_SAME_CONN",
 "SOL_CMD_KILLED_DIF_ERR",
 "CXN_KILLED_SYN_RCVD",
 "CXN_KILLED_IMM_DATA_RCVD"
};

static int beiscsi_eh_abort(struct scsi_cmnd *sc)
{
 struct iscsi_task *abrt_task = iscsi_cmd(sc)->task;
 struct iscsi_cls_session *cls_session;
 struct beiscsi_io_task *abrt_io_task;
 struct beiscsi_conn *beiscsi_conn;
 struct iscsi_session *session;
 struct invldt_cmd_tbl inv_tbl;
 struct beiscsi_hba *phba;
 struct iscsi_conn *conn;
 int rc;

 cls_session = starget_to_session(scsi_target(sc->device));
 session = cls_session->dd_data;

completion_check:
 /* check if we raced, task just got cleaned up under us */
 spin_lock_bh(&session->back_lock);
 if (!abrt_task || !abrt_task->sc) {
  spin_unlock_bh(&session->back_lock);
  return SUCCESS;
 }
 /* get a task ref till FW processes the req for the ICD used */
 if (!iscsi_get_task(abrt_task)) {
  spin_unlock(&session->back_lock);
  /* We are just about to call iscsi_free_task so wait for it. */
  udelay(5);
  goto completion_check;
 }

 abrt_io_task = abrt_task->dd_data;
 conn = abrt_task->conn;
 beiscsi_conn = conn->dd_data;
 phba = beiscsi_conn->phba;
 /* mark WRB invalid which have been not processed by FW yet */
 if (is_chip_be2_be3r(phba)) {
  AMAP_SET_BITS(struct amap_iscsi_wrb, invld,
         abrt_io_task->pwrb_handle->pwrb, 1);
 } else {
  AMAP_SET_BITS(struct amap_iscsi_wrb_v2, invld,
         abrt_io_task->pwrb_handle->pwrb, 1);
 }
 inv_tbl.cid = beiscsi_conn->beiscsi_conn_cid;
 inv_tbl.icd = abrt_io_task->psgl_handle->sgl_index;
 spin_unlock_bh(&session->back_lock);

 rc = beiscsi_mgmt_invalidate_icds(phba, &inv_tbl, 1);
 iscsi_put_task(abrt_task);
 if (rc) {
  beiscsi_log(phba, KERN_WARNING, BEISCSI_LOG_EH,
       "BM_%d : sc %p invalidation failed %d\n",
       sc, rc);
  return FAILED;
 }

 return iscsi_eh_abort(sc);
}

static int beiscsi_eh_device_reset(struct scsi_cmnd *sc)
{
 struct beiscsi_invldt_cmd_tbl {
  struct invldt_cmd_tbl tbl[BE_INVLDT_CMD_TBL_SZ];
  struct iscsi_task *task[BE_INVLDT_CMD_TBL_SZ];
 } *inv_tbl;
 struct iscsi_cls_session *cls_session;
 struct beiscsi_conn *beiscsi_conn;
 struct beiscsi_io_task *io_task;
 struct iscsi_session *session;
 struct beiscsi_hba *phba;
 struct iscsi_conn *conn;
 struct iscsi_task *task;
 unsigned int i, nents;
 int rc, more = 0;

 cls_session = starget_to_session(scsi_target(sc->device));
 session = cls_session->dd_data;

 spin_lock_bh(&session->frwd_lock);
 if (!session->leadconn || session->state != ISCSI_STATE_LOGGED_IN) {
  spin_unlock_bh(&session->frwd_lock);
  return FAILED;
 }

 conn = session->leadconn;
 beiscsi_conn = conn->dd_data;
 phba = beiscsi_conn->phba;

 inv_tbl = kzalloc(sizeof(*inv_tbl), GFP_ATOMIC);
 if (!inv_tbl) {
  spin_unlock_bh(&session->frwd_lock);
  beiscsi_log(phba, KERN_ERR, BEISCSI_LOG_EH,
       "BM_%d : invldt_cmd_tbl alloc failed\n");
  return FAILED;
 }
 nents = 0;
 /* take back_lock to prevent task from getting cleaned up under us */
 spin_lock(&session->back_lock);
 for (i = 0; i < conn->session->cmds_max; i++) {
  task = conn->session->cmds[i];
  if (!task->sc)
   continue;

  if (sc->device->lun != task->sc->device->lun)
   continue;
  /**
 * Can't fit in more cmds? Normally this won't happen b'coz
 * BEISCSI_CMD_PER_LUN is same as BE_INVLDT_CMD_TBL_SZ.
 */

  if (nents == BE_INVLDT_CMD_TBL_SZ) {
   more = 1;
   break;
  }

  /* get a task ref till FW processes the req for the ICD used */
  if (!iscsi_get_task(task)) {
   /*
 * The task has completed in the driver and is
 * completing in libiscsi. Just ignore it here. When we
 * call iscsi_eh_device_reset, it will wait for us.
 */

   continue;
  }

  io_task = task->dd_data;
  /* mark WRB invalid which have been not processed by FW yet */
  if (is_chip_be2_be3r(phba)) {
   AMAP_SET_BITS(struct amap_iscsi_wrb, invld,
          io_task->pwrb_handle->pwrb, 1);
  } else {
   AMAP_SET_BITS(struct amap_iscsi_wrb_v2, invld,
          io_task->pwrb_handle->pwrb, 1);
  }

  inv_tbl->tbl[nents].cid = beiscsi_conn->beiscsi_conn_cid;
  inv_tbl->tbl[nents].icd = io_task->psgl_handle->sgl_index;
  inv_tbl->task[nents] = task;
  nents++;
 }
 spin_unlock(&session->back_lock);
 spin_unlock_bh(&session->frwd_lock);

 rc = SUCCESS;
 if (!nents)
  goto end_reset;

 if (more) {
  beiscsi_log(phba, KERN_ERR, BEISCSI_LOG_EH,
       "BM_%d : number of cmds exceeds size of invalidation table\n");
  rc = FAILED;
  goto end_reset;
 }

 if (beiscsi_mgmt_invalidate_icds(phba, &inv_tbl->tbl[0], nents)) {
  beiscsi_log(phba, KERN_WARNING, BEISCSI_LOG_EH,
       "BM_%d : cid %u scmds invalidation failed\n",
       beiscsi_conn->beiscsi_conn_cid);
  rc = FAILED;
 }

end_reset:
 for (i = 0; i < nents; i++)
  iscsi_put_task(inv_tbl->task[i]);
 kfree(inv_tbl);

 if (rc == SUCCESS)
  rc = iscsi_eh_device_reset(sc);
 return rc;
}

/*------------------- PCI Driver operations and data ----------------- */
static const struct pci_device_id beiscsi_pci_id_table[] = {
 { PCI_DEVICE(BE_VENDOR_ID, BE_DEVICE_ID1) },
 { PCI_DEVICE(BE_VENDOR_ID, BE_DEVICE_ID2) },
 { PCI_DEVICE(BE_VENDOR_ID, OC_DEVICE_ID1) },
 { PCI_DEVICE(BE_VENDOR_ID, OC_DEVICE_ID2) },
 { PCI_DEVICE(BE_VENDOR_ID, OC_DEVICE_ID3) },
 { PCI_DEVICE(ELX_VENDOR_ID, OC_SKH_ID1) },
 { 0 }
};
MODULE_DEVICE_TABLE(pci, beiscsi_pci_id_table);


static const struct scsi_host_template beiscsi_sht = {
 .module = THIS_MODULE,
 .name = "Emulex 10Gbe open-iscsi Initiator Driver",
 .proc_name = DRV_NAME,
 .queuecommand = iscsi_queuecommand,
 .change_queue_depth = scsi_change_queue_depth,
 .target_alloc = iscsi_target_alloc,
 .eh_timed_out = iscsi_eh_cmd_timed_out,
 .eh_abort_handler = beiscsi_eh_abort,
 .eh_device_reset_handler = beiscsi_eh_device_reset,
 .eh_target_reset_handler = iscsi_eh_session_reset,
 .shost_groups = beiscsi_groups,
 .sg_tablesize = BEISCSI_SGLIST_ELEMENTS,
 .can_queue = BE2_IO_DEPTH,
 .this_id = -1,
 .max_sectors = BEISCSI_MAX_SECTORS,
 .max_segment_size = 65536,
 .cmd_per_lun = BEISCSI_CMD_PER_LUN,
 .vendor_id = SCSI_NL_VID_TYPE_PCI | BE_VENDOR_ID,
 .track_queue_depth = 1,
 .cmd_size = sizeof(struct iscsi_cmd),
};

static struct scsi_transport_template *beiscsi_scsi_transport;

static struct beiscsi_hba *beiscsi_hba_alloc(struct pci_dev *pcidev)
{
 struct beiscsi_hba *phba;
 struct Scsi_Host *shost;

 shost = iscsi_host_alloc(&beiscsi_sht, sizeof(*phba), 0);
 if (!shost) {
  dev_err(&pcidev->dev,
   "beiscsi_hba_alloc - iscsi_host_alloc failed\n");
  return NULL;
 }
 shost->max_id = BE2_MAX_SESSIONS - 1;
 shost->max_channel = 0;
 shost->max_cmd_len = BEISCSI_MAX_CMD_LEN;
 shost->max_lun = BEISCSI_NUM_MAX_LUN;
 shost->transportt = beiscsi_scsi_transport;
 phba = iscsi_host_priv(shost);
 memset(phba, 0, sizeof(*phba));
 phba->shost = shost;
 phba->pcidev = pci_dev_get(pcidev);
 pci_set_drvdata(pcidev, phba);
 phba->interface_handle = 0xFFFFFFFF;

 return phba;
}

static void beiscsi_unmap_pci_function(struct beiscsi_hba *phba)
{
 if (phba->csr_va) {
  iounmap(phba->csr_va);
  phba->csr_va = NULL;
 }
 if (phba->db_va) {
  iounmap(phba->db_va);
  phba->db_va = NULL;
 }
 if (phba->pci_va) {
  iounmap(phba->pci_va);
  phba->pci_va = NULL;
 }
}

static int beiscsi_map_pci_bars(struct beiscsi_hba *phba,
    struct pci_dev *pcidev)
{
 u8 __iomem *addr;
 int pcicfg_reg;

 addr = ioremap(pci_resource_start(pcidev, 2),
          pci_resource_len(pcidev, 2));
 if (addr == NULL)
  return -ENOMEM;
 phba->ctrl.csr = addr;
 phba->csr_va = addr;

 addr = ioremap(pci_resource_start(pcidev, 4), 128 * 1024);
 if (addr == NULL)
  goto pci_map_err;
 phba->ctrl.db = addr;
 phba->db_va = addr;

 if (phba->generation == BE_GEN2)
  pcicfg_reg = 1;
 else
  pcicfg_reg = 0;

 addr = ioremap(pci_resource_start(pcidev, pcicfg_reg),
          pci_resource_len(pcidev, pcicfg_reg));

 if (addr == NULL)
  goto pci_map_err;
 phba->ctrl.pcicfg = addr;
 phba->pci_va = addr;
 return 0;

pci_map_err:
 beiscsi_unmap_pci_function(phba);
 return -ENOMEM;
}

static int beiscsi_enable_pci(struct pci_dev *pcidev)
{
 int ret;

 ret = pci_enable_device(pcidev);
 if (ret) {
  dev_err(&pcidev->dev,
   "beiscsi_enable_pci - enable device failed\n");
  return ret;
 }

 ret = pci_request_regions(pcidev, DRV_NAME);
 if (ret) {
  dev_err(&pcidev->dev,
    "beiscsi_enable_pci - request region failed\n");
  goto pci_dev_disable;
 }

 pci_set_master(pcidev);
 ret = dma_set_mask_and_coherent(&pcidev->dev, DMA_BIT_MASK(64));
 if (ret) {
  ret = dma_set_mask_and_coherent(&pcidev->dev, DMA_BIT_MASK(32));
  if (ret) {
   dev_err(&pcidev->dev, "Could not set PCI DMA Mask\n");
   goto pci_region_release;
  }
 }
 return 0;

pci_region_release:
 pci_release_regions(pcidev);
pci_dev_disable:
 pci_disable_device(pcidev);

 return ret;
}

static int be_ctrl_init(struct beiscsi_hba *phba, struct pci_dev *pdev)
{
 struct be_ctrl_info *ctrl = &phba->ctrl;
 struct be_dma_mem *mbox_mem_alloc = &ctrl->mbox_mem_alloced;
 struct be_dma_mem *mbox_mem_align = &ctrl->mbox_mem;
 int status = 0;

 ctrl->pdev = pdev;
 status = beiscsi_map_pci_bars(phba, pdev);
 if (status)
  return status;
 mbox_mem_alloc->size = sizeof(struct be_mcc_mailbox) + 16;
 mbox_mem_alloc->va = dma_alloc_coherent(&pdev->dev,
   mbox_mem_alloc->size, &mbox_mem_alloc->dma, GFP_KERNEL);
 if (!mbox_mem_alloc->va) {
  beiscsi_unmap_pci_function(phba);
  return -ENOMEM;
 }

 mbox_mem_align->size = sizeof(struct be_mcc_mailbox);
 mbox_mem_align->va = PTR_ALIGN(mbox_mem_alloc->va, 16);
 mbox_mem_align->dma = PTR_ALIGN(mbox_mem_alloc->dma, 16);
 memset(mbox_mem_align->va, 0, sizeof(struct be_mcc_mailbox));
 mutex_init(&ctrl->mbox_lock);
 spin_lock_init(&phba->ctrl.mcc_lock);

 return status;
}

/**
 * beiscsi_get_params()- Set the config paramters
 * @phba: ptr  device priv structure
 **/

static void beiscsi_get_params(struct beiscsi_hba *phba)
{
 uint32_t total_cid_count = 0;
 uint32_t total_icd_count = 0;
 uint8_t ulp_num = 0;

 total_cid_count = BEISCSI_GET_CID_COUNT(phba, BEISCSI_ULP0) +
     BEISCSI_GET_CID_COUNT(phba, BEISCSI_ULP1);

 for (ulp_num = 0; ulp_num < BEISCSI_ULP_COUNT; ulp_num++) {
  uint32_t align_mask = 0;
  uint32_t icd_post_per_page = 0;
  uint32_t icd_count_unavailable = 0;
  uint32_t icd_start = 0, icd_count = 0;
  uint32_t icd_start_align = 0, icd_count_align = 0;

  if (test_bit(ulp_num, &phba->fw_config.ulp_supported)) {
   icd_start = phba->fw_config.iscsi_icd_start[ulp_num];
   icd_count = phba->fw_config.iscsi_icd_count[ulp_num];

   /* Get ICD count that can be posted on each page */
   icd_post_per_page = (PAGE_SIZE / (BE2_SGE *
          sizeof(struct iscsi_sge)));
   align_mask = (icd_post_per_page - 1);

   /* Check if icd_start is aligned ICD per page posting */
   if (icd_start % icd_post_per_page) {
    icd_start_align = ((icd_start +
          icd_post_per_page) &
          ~(align_mask));
    phba->fw_config.
     iscsi_icd_start[ulp_num] =
     icd_start_align;
   }

   icd_count_align = (icd_count & ~align_mask);

   /* ICD discarded in the process of alignment */
   if (icd_start_align)
    icd_count_unavailable = ((icd_start_align -
         icd_start) +
        (icd_count -
         icd_count_align));

   /* Updated ICD count available */
   phba->fw_config.iscsi_icd_count[ulp_num] = (icd_count -
     icd_count_unavailable);

   beiscsi_log(phba, KERN_INFO, BEISCSI_LOG_INIT,
     "BM_%d : Aligned ICD values\n"
     "\t ICD Start : %d\n"
     "\t ICD Count : %d\n"
     "\t ICD Discarded : %d\n",
     phba->fw_config.
     iscsi_icd_start[ulp_num],
     phba->fw_config.
     iscsi_icd_count[ulp_num],
     icd_count_unavailable);
   break;
  }
 }

 total_icd_count = phba->fw_config.iscsi_icd_count[ulp_num];
 phba->params.ios_per_ctrl = (total_icd_count -
        (total_cid_count +
         BE2_TMFS + BE2_NOPOUT_REQ));
 phba->params.cxns_per_ctrl = total_cid_count;
 phba->params.icds_per_ctrl = total_icd_count;
 phba->params.num_sge_per_io = BE2_SGE;
 phba->params.defpdu_hdr_sz = BE2_DEFPDU_HDR_SZ;
 phba->params.defpdu_data_sz = BE2_DEFPDU_DATA_SZ;
 phba->params.num_eq_entries = 1024;
 phba->params.num_cq_entries = 1024;
 phba->params.wrbs_per_cxn = 256;
}

static void hwi_ring_eq_db(struct beiscsi_hba *phba,
      unsigned int id, unsigned int clr_interrupt,
      unsigned int num_processed,
      unsigned char rearm, unsigned char event)
{
 u32 val = 0;

 if (rearm)
  val |= 1 << DB_EQ_REARM_SHIFT;
 if (clr_interrupt)
  val |= 1 << DB_EQ_CLR_SHIFT;
 if (event)
  val |= 1 << DB_EQ_EVNT_SHIFT;

 val |= num_processed << DB_EQ_NUM_POPPED_SHIFT;
 /* Setting lower order EQ_ID Bits */
 val |= (id & DB_EQ_RING_ID_LOW_MASK);

 /* Setting Higher order EQ_ID Bits */
 val |= (((id >> DB_EQ_HIGH_FEILD_SHIFT) &
    DB_EQ_RING_ID_HIGH_MASK)
    << DB_EQ_HIGH_SET_SHIFT);

 iowrite32(val, phba->db_va + DB_EQ_OFFSET);
}

/**
 * be_isr_mcc - The isr routine of the driver.
 * @irq: Not used
 * @dev_id: Pointer to host adapter structure
 */

static irqreturn_t be_isr_mcc(int irq, void *dev_id)
{
 struct beiscsi_hba *phba;
 struct be_eq_entry *eqe;
 struct be_queue_info *eq;
 struct be_queue_info *mcc;
 unsigned int mcc_events;
 struct be_eq_obj *pbe_eq;

 pbe_eq = dev_id;
 eq = &pbe_eq->q;
 phba =  pbe_eq->phba;
 mcc = &phba->ctrl.mcc_obj.cq;
 eqe = queue_tail_node(eq);

 mcc_events = 0;
 while (eqe->dw[offsetof(struct amap_eq_entry, valid) / 32]
    & EQE_VALID_MASK) {
  if (((eqe->dw[offsetof(struct amap_eq_entry,
       resource_id) / 32] &
       EQE_RESID_MASK) >> 16) == mcc->id) {
   mcc_events++;
  }
  AMAP_SET_BITS(struct amap_eq_entry, valid, eqe, 0);
  queue_tail_inc(eq);
  eqe = queue_tail_node(eq);
 }

 if (mcc_events) {
  queue_work(phba->wq, &pbe_eq->mcc_work);
  hwi_ring_eq_db(phba, eq->id, 1, mcc_events, 1, 1);
 }
 return IRQ_HANDLED;
}

/**
 * be_isr_msix - The isr routine of the driver.
 * @irq: Not used
 * @dev_id: Pointer to host adapter structure
 */

static irqreturn_t be_isr_msix(int irq, void *dev_id)
{
 struct beiscsi_hba *phba;
 struct be_queue_info *eq;
 struct be_eq_obj *pbe_eq;

 pbe_eq = dev_id;
 eq = &pbe_eq->q;

 phba = pbe_eq->phba;
 /* disable interrupt till iopoll completes */
 hwi_ring_eq_db(phba, eq->id, 1, 0, 0, 1);
 irq_poll_sched(&pbe_eq->iopoll);

 return IRQ_HANDLED;
}

/**
 * be_isr - The isr routine of the driver.
 * @irq: Not used
 * @dev_id: Pointer to host adapter structure
 */

static irqreturn_t be_isr(int irq, void *dev_id)
{
 struct beiscsi_hba *phba;
 struct hwi_controller *phwi_ctrlr;
 struct hwi_context_memory *phwi_context;
 struct be_eq_entry *eqe;
 struct be_queue_info *eq;
 struct be_queue_info *mcc;
 unsigned int mcc_events, io_events;
 struct be_ctrl_info *ctrl;
 struct be_eq_obj *pbe_eq;
 int isr, rearm;

 phba = dev_id;
 ctrl = &phba->ctrl;
 isr = ioread32(ctrl->csr + CEV_ISR0_OFFSET +
         (PCI_FUNC(ctrl->pdev->devfn) * CEV_ISR_SIZE));
 if (!isr)
  return IRQ_NONE;

 phwi_ctrlr = phba->phwi_ctrlr;
 phwi_context = phwi_ctrlr->phwi_ctxt;
 pbe_eq = &phwi_context->be_eq[0];

 eq = &phwi_context->be_eq[0].q;
 mcc = &phba->ctrl.mcc_obj.cq;
 eqe = queue_tail_node(eq);

 io_events = 0;
 mcc_events = 0;
 while (eqe->dw[offsetof(struct amap_eq_entry, valid) / 32]
    & EQE_VALID_MASK) {
  if (((eqe->dw[offsetof(struct amap_eq_entry,
        resource_id) / 32] & EQE_RESID_MASK) >> 16) == mcc->id)
   mcc_events++;
  else
   io_events++;
  AMAP_SET_BITS(struct amap_eq_entry, valid, eqe, 0);
  queue_tail_inc(eq);
  eqe = queue_tail_node(eq);
 }
 if (!io_events && !mcc_events)
  return IRQ_NONE;

 /* no need to rearm if interrupt is only for IOs */
 rearm = 0;
 if (mcc_events) {
  queue_work(phba->wq, &pbe_eq->mcc_work);
  /* rearm for MCCQ */
  rearm = 1;
 }
 if (io_events)
  irq_poll_sched(&pbe_eq->iopoll);
 hwi_ring_eq_db(phba, eq->id, 0, (io_events + mcc_events), rearm, 1);
 return IRQ_HANDLED;
}

static void beiscsi_free_irqs(struct beiscsi_hba *phba)
{
 struct hwi_context_memory *phwi_context;
 int i;

 if (!phba->pcidev->msix_enabled) {
  if (phba->pcidev->irq)
   free_irq(phba->pcidev->irq, phba);
  return;
 }

 phwi_context = phba->phwi_ctrlr->phwi_ctxt;
 for (i = 0; i <= phba->num_cpus; i++) {
  free_irq(pci_irq_vector(phba->pcidev, i),
    &phwi_context->be_eq[i]);
  kfree(phba->msi_name[i]);
 }
}

static int beiscsi_init_irqs(struct beiscsi_hba *phba)
{
 struct pci_dev *pcidev = phba->pcidev;
 struct hwi_controller *phwi_ctrlr;
 struct hwi_context_memory *phwi_context;
 int ret, i, j;

 phwi_ctrlr = phba->phwi_ctrlr;
 phwi_context = phwi_ctrlr->phwi_ctxt;

 if (pcidev->msix_enabled) {
  for (i = 0; i < phba->num_cpus; i++) {
   phba->msi_name[i] = kasprintf(GFP_KERNEL,
            "beiscsi_%02x_%02x",
            phba->shost->host_no, i);
   if (!phba->msi_name[i]) {
    ret = -ENOMEM;
    goto free_msix_irqs;
   }

   ret = request_irq(pci_irq_vector(pcidev, i),
       be_isr_msix, 0, phba->msi_name[i],
       &phwi_context->be_eq[i]);
   if (ret) {
    beiscsi_log(phba, KERN_ERR, BEISCSI_LOG_INIT,
         "BM_%d : %s-Failed to register msix for i = %d\n",
         __func__, i);
    kfree(phba->msi_name[i]);
    goto free_msix_irqs;
   }
  }
  phba->msi_name[i] = kasprintf(GFP_KERNEL, "beiscsi_mcc_%02x",
           phba->shost->host_no);
  if (!phba->msi_name[i]) {
   ret = -ENOMEM;
   goto free_msix_irqs;
  }
  ret = request_irq(pci_irq_vector(pcidev, i), be_isr_mcc, 0,
      phba->msi_name[i], &phwi_context->be_eq[i]);
  if (ret) {
   beiscsi_log(phba, KERN_ERR, BEISCSI_LOG_INIT,
        "BM_%d : %s-Failed to register beiscsi_msix_mcc\n",
        __func__);
   kfree(phba->msi_name[i]);
   goto free_msix_irqs;
  }

 } else {
  ret = request_irq(pcidev->irq, be_isr, IRQF_SHARED,
      "beiscsi", phba);
  if (ret) {
   beiscsi_log(phba, KERN_ERR, BEISCSI_LOG_INIT,
        "BM_%d : %s-Failed to register irq\n",
        __func__);
   return ret;
  }
 }
 return 0;
free_msix_irqs:
 for (j = i - 1; j >= 0; j--) {
  free_irq(pci_irq_vector(pcidev, i), &phwi_context->be_eq[j]);
  kfree(phba->msi_name[j]);
 }
 return ret;
}

void hwi_ring_cq_db(struct beiscsi_hba *phba,
      unsigned int id, unsigned int num_processed,
      unsigned char rearm)
{
 u32 val = 0;

 if (rearm)
  val |= 1 << DB_CQ_REARM_SHIFT;

 val |= num_processed << DB_CQ_NUM_POPPED_SHIFT;

 /* Setting lower order CQ_ID Bits */
 val |= (id & DB_CQ_RING_ID_LOW_MASK);

 /* Setting Higher order CQ_ID Bits */
 val |= (((id >> DB_CQ_HIGH_FEILD_SHIFT) &
    DB_CQ_RING_ID_HIGH_MASK)
    << DB_CQ_HIGH_SET_SHIFT);

 iowrite32(val, phba->db_va + DB_CQ_OFFSET);
}

static struct sgl_handle *alloc_io_sgl_handle(struct beiscsi_hba *phba)
{
 struct sgl_handle *psgl_handle;
 unsigned long flags;

 spin_lock_irqsave(&phba->io_sgl_lock, flags);
 if (phba->io_sgl_hndl_avbl) {
  beiscsi_log(phba, KERN_INFO, BEISCSI_LOG_IO,
       "BM_%d : In alloc_io_sgl_handle,"
       " io_sgl_alloc_index=%d\n",
       phba->io_sgl_alloc_index);

  psgl_handle = phba->io_sgl_hndl_base[phba->
      io_sgl_alloc_index];
  phba->io_sgl_hndl_base[phba->io_sgl_alloc_index] = NULL;
  phba->io_sgl_hndl_avbl--;
  if (phba->io_sgl_alloc_index == (phba->params.
       ios_per_ctrl - 1))
   phba->io_sgl_alloc_index = 0;
  else
   phba->io_sgl_alloc_index++;
 } else
  psgl_handle = NULL;
 spin_unlock_irqrestore(&phba->io_sgl_lock, flags);
 return psgl_handle;
}

static void
free_io_sgl_handle(struct beiscsi_hba *phba, struct sgl_handle *psgl_handle)
{
 unsigned long flags;

 spin_lock_irqsave(&phba->io_sgl_lock, flags);
 beiscsi_log(phba, KERN_INFO, BEISCSI_LOG_IO,
      "BM_%d : In free_,io_sgl_free_index=%d\n",
      phba->io_sgl_free_index);

 if (phba->io_sgl_hndl_base[phba->io_sgl_free_index]) {
  /*
 * this can happen if clean_task is called on a task that
 * failed in xmit_task or alloc_pdu.
 */

  beiscsi_log(phba, KERN_INFO, BEISCSI_LOG_IO,
       "BM_%d : Double Free in IO SGL io_sgl_free_index=%d, value there=%p\n",
       phba->io_sgl_free_index,
       phba->io_sgl_hndl_base[phba->io_sgl_free_index]);
  spin_unlock_irqrestore(&phba->io_sgl_lock, flags);
  return;
 }
 phba->io_sgl_hndl_base[phba->io_sgl_free_index] = psgl_handle;
 phba->io_sgl_hndl_avbl++;
 if (phba->io_sgl_free_index == (phba->params.ios_per_ctrl - 1))
  phba->io_sgl_free_index = 0;
 else
  phba->io_sgl_free_index++;
 spin_unlock_irqrestore(&phba->io_sgl_lock, flags);
}

static inline struct wrb_handle *
beiscsi_get_wrb_handle(struct hwi_wrb_context *pwrb_context,
         unsigned int wrbs_per_cxn)
{
 struct wrb_handle *pwrb_handle;
 unsigned long flags;

 spin_lock_irqsave(&pwrb_context->wrb_lock, flags);
 if (!pwrb_context->wrb_handles_available) {
  spin_unlock_irqrestore(&pwrb_context->wrb_lock, flags);
  return NULL;
 }
 pwrb_handle = pwrb_context->pwrb_handle_base[pwrb_context->alloc_index];
 pwrb_context->wrb_handles_available--;
 if (pwrb_context->alloc_index == (wrbs_per_cxn - 1))
  pwrb_context->alloc_index = 0;
 else
  pwrb_context->alloc_index++;
 spin_unlock_irqrestore(&pwrb_context->wrb_lock, flags);

 if (pwrb_handle)
  memset(pwrb_handle->pwrb, 0, sizeof(*pwrb_handle->pwrb));

 return pwrb_handle;
}

/**
 * alloc_wrb_handle - To allocate a wrb handle
 * @phba: The hba pointer
 * @cid: The cid to use for allocation
 * @pcontext: ptr to ptr to wrb context
 *
 * This happens under session_lock until submission to chip
 */

struct wrb_handle *alloc_wrb_handle(struct beiscsi_hba *phba, unsigned int cid,
        struct hwi_wrb_context **pcontext)
{
 struct hwi_wrb_context *pwrb_context;
 struct hwi_controller *phwi_ctrlr;
 uint16_t cri_index = BE_GET_CRI_FROM_CID(cid);

 phwi_ctrlr = phba->phwi_ctrlr;
 pwrb_context = &phwi_ctrlr->wrb_context[cri_index];
 /* return the context address */
 *pcontext = pwrb_context;
 return beiscsi_get_wrb_handle(pwrb_context, phba->params.wrbs_per_cxn);
}

static inline void
beiscsi_put_wrb_handle(struct hwi_wrb_context *pwrb_context,
         struct wrb_handle *pwrb_handle,
         unsigned int wrbs_per_cxn)
{
 unsigned long flags;

 spin_lock_irqsave(&pwrb_context->wrb_lock, flags);
 pwrb_context->pwrb_handle_base[pwrb_context->free_index] = pwrb_handle;
 pwrb_context->wrb_handles_available++;
 if (pwrb_context->free_index == (wrbs_per_cxn - 1))
  pwrb_context->free_index = 0;
 else
  pwrb_context->free_index++;
 pwrb_handle->pio_handle = NULL;
 spin_unlock_irqrestore(&pwrb_context->wrb_lock, flags);
}

/**
 * free_wrb_handle - To free the wrb handle back to pool
 * @phba: The hba pointer
 * @pwrb_context: The context to free from
 * @pwrb_handle: The wrb_handle to free
 *
 * This happens under session_lock until submission to chip
 */

static void
free_wrb_handle(struct beiscsi_hba *phba, struct hwi_wrb_context *pwrb_context,
  struct wrb_handle *pwrb_handle)
{
 beiscsi_put_wrb_handle(pwrb_context,
          pwrb_handle,
          phba->params.wrbs_per_cxn);
 beiscsi_log(phba, KERN_INFO,
      BEISCSI_LOG_IO | BEISCSI_LOG_CONFIG,
      "BM_%d : FREE WRB: pwrb_handle=%p free_index=0x%x "
      "wrb_handles_available=%d\n",
      pwrb_handle, pwrb_context->free_index,
      pwrb_context->wrb_handles_available);
}

static struct sgl_handle *alloc_mgmt_sgl_handle(struct beiscsi_hba *phba)
{
 struct sgl_handle *psgl_handle;
 unsigned long flags;

 spin_lock_irqsave(&phba->mgmt_sgl_lock, flags);
 if (phba->eh_sgl_hndl_avbl) {
  psgl_handle = phba->eh_sgl_hndl_base[phba->eh_sgl_alloc_index];
  phba->eh_sgl_hndl_base[phba->eh_sgl_alloc_index] = NULL;
  beiscsi_log(phba, KERN_INFO, BEISCSI_LOG_CONFIG,
       "BM_%d : mgmt_sgl_alloc_index=%d=0x%x\n",
       phba->eh_sgl_alloc_index,
       phba->eh_sgl_alloc_index);

  phba->eh_sgl_hndl_avbl--;
  if (phba->eh_sgl_alloc_index ==
      (phba->params.icds_per_ctrl - phba->params.ios_per_ctrl -
       1))
   phba->eh_sgl_alloc_index = 0;
  else
   phba->eh_sgl_alloc_index++;
 } else
  psgl_handle = NULL;
 spin_unlock_irqrestore(&phba->mgmt_sgl_lock, flags);
 return psgl_handle;
}

void
free_mgmt_sgl_handle(struct beiscsi_hba *phba, struct sgl_handle *psgl_handle)
{
 unsigned long flags;

 spin_lock_irqsave(&phba->mgmt_sgl_lock, flags);
 beiscsi_log(phba, KERN_INFO, BEISCSI_LOG_CONFIG,
      "BM_%d : In free_mgmt_sgl_handle,"
      "eh_sgl_free_index=%d\n",
      phba->eh_sgl_free_index);

 if (phba->eh_sgl_hndl_base[phba->eh_sgl_free_index]) {
  /*
 * this can happen if clean_task is called on a task that
 * failed in xmit_task or alloc_pdu.
 */

  beiscsi_log(phba, KERN_WARNING, BEISCSI_LOG_CONFIG,
       "BM_%d : Double Free in eh SGL ,"
       "eh_sgl_free_index=%d\n",
       phba->eh_sgl_free_index);
  spin_unlock_irqrestore(&phba->mgmt_sgl_lock, flags);
  return;
 }
 phba->eh_sgl_hndl_base[phba->eh_sgl_free_index] = psgl_handle;
 phba->eh_sgl_hndl_avbl++;
 if (phba->eh_sgl_free_index ==
     (phba->params.icds_per_ctrl - phba->params.ios_per_ctrl - 1))
  phba->eh_sgl_free_index = 0;
 else
  phba->eh_sgl_free_index++;
 spin_unlock_irqrestore(&phba->mgmt_sgl_lock, flags);
}

static void
be_complete_io(struct beiscsi_conn *beiscsi_conn,
  struct iscsi_task *task,
  struct common_sol_cqe *csol_cqe)
{
 struct beiscsi_io_task *io_task = task->dd_data;
 struct be_status_bhs *sts_bhs =
    (struct be_status_bhs *)io_task->cmd_bhs;
 struct iscsi_conn *conn = beiscsi_conn->conn;
 unsigned char *sense;
 u32 resid = 0, exp_cmdsn, max_cmdsn;
 u8 rsp, status, flags;

 exp_cmdsn = csol_cqe->exp_cmdsn;
 max_cmdsn = (csol_cqe->exp_cmdsn +
       csol_cqe->cmd_wnd - 1);
 rsp = csol_cqe->i_resp;
 status = csol_cqe->i_sts;
 flags = csol_cqe->i_flags;
 resid = csol_cqe->res_cnt;

 if (!task->sc) {
  if (io_task->scsi_cmnd) {
   scsi_dma_unmap(io_task->scsi_cmnd);
   io_task->scsi_cmnd = NULL;
  }

  return;
 }
 task->sc->result = (DID_OK << 16) | status;
 if (rsp != ISCSI_STATUS_CMD_COMPLETED) {
  task->sc->result = DID_ERROR << 16;
  goto unmap;
 }

 /* bidi not initially supported */
 if (flags & (ISCSI_FLAG_CMD_UNDERFLOW | ISCSI_FLAG_CMD_OVERFLOW)) {
  if (!status && (flags & ISCSI_FLAG_CMD_OVERFLOW))
   task->sc->result = DID_ERROR << 16;

  if (flags & ISCSI_FLAG_CMD_UNDERFLOW) {
   scsi_set_resid(task->sc, resid);
   if (!status && (scsi_bufflen(task->sc) - resid <
       task->sc->underflow))
    task->sc->result = DID_ERROR << 16;
  }
 }

 if (status == SAM_STAT_CHECK_CONDITION) {
  u16 sense_len;
  unsigned short *slen = (unsigned short *)sts_bhs->sense_info;

  sense = sts_bhs->sense_info + sizeof(unsigned short);
  sense_len = be16_to_cpu(*slen);
  memcpy(task->sc->sense_buffer, sense,
         min_t(u16, sense_len, SCSI_SENSE_BUFFERSIZE));
 }

 if (io_task->cmd_bhs->iscsi_hdr.flags & ISCSI_FLAG_CMD_READ)
  conn->rxdata_octets += resid;
unmap:
 if (io_task->scsi_cmnd) {
  scsi_dma_unmap(io_task->scsi_cmnd);
  io_task->scsi_cmnd = NULL;
 }
 iscsi_complete_scsi_task(task, exp_cmdsn, max_cmdsn);
}

static void
be_complete_logout(struct beiscsi_conn *beiscsi_conn,
      struct iscsi_task *task,
      struct common_sol_cqe *csol_cqe)
{
 struct iscsi_logout_rsp *hdr;
 struct beiscsi_io_task *io_task = task->dd_data;
 struct iscsi_conn *conn = beiscsi_conn->conn;

 hdr = (struct iscsi_logout_rsp *)task->hdr;
 hdr->opcode = ISCSI_OP_LOGOUT_RSP;
 hdr->t2wait = 5;
 hdr->t2retain = 0;
 hdr->flags = csol_cqe->i_flags;
 hdr->response = csol_cqe->i_resp;
 hdr->exp_cmdsn = cpu_to_be32(csol_cqe->exp_cmdsn);
 hdr->max_cmdsn = cpu_to_be32(csol_cqe->exp_cmdsn +
         csol_cqe->cmd_wnd - 1);

 hdr->dlength[0] = 0;
 hdr->dlength[1] = 0;
 hdr->dlength[2] = 0;
 hdr->hlength = 0;
 hdr->itt = io_task->libiscsi_itt;
 __iscsi_complete_pdu(conn, (struct iscsi_hdr *)hdr, NULL, 0);
}

static void
be_complete_tmf(struct beiscsi_conn *beiscsi_conn,
   struct iscsi_task *task,
   struct common_sol_cqe *csol_cqe)
{
 struct iscsi_tm_rsp *hdr;
 struct iscsi_conn *conn = beiscsi_conn->conn;
 struct beiscsi_io_task *io_task = task->dd_data;

 hdr = (struct iscsi_tm_rsp *)task->hdr;
 hdr->opcode = ISCSI_OP_SCSI_TMFUNC_RSP;
 hdr->flags = csol_cqe->i_flags;
 hdr->response = csol_cqe->i_resp;
 hdr->exp_cmdsn = cpu_to_be32(csol_cqe->exp_cmdsn);
 hdr->max_cmdsn = cpu_to_be32(csol_cqe->exp_cmdsn +
         csol_cqe->cmd_wnd - 1);

 hdr->itt = io_task->libiscsi_itt;
 __iscsi_complete_pdu(conn, (struct iscsi_hdr *)hdr, NULL, 0);
}

static void
hwi_complete_drvr_msgs(struct beiscsi_conn *beiscsi_conn,
         struct beiscsi_hba *phba, struct sol_cqe *psol)
{
 struct hwi_wrb_context *pwrb_context;
 uint16_t wrb_index, cid, cri_index;
 struct hwi_controller *phwi_ctrlr;
 struct wrb_handle *pwrb_handle;
 struct iscsi_session *session;
 struct iscsi_task *task;

 phwi_ctrlr = phba->phwi_ctrlr;
 if (is_chip_be2_be3r(phba)) {
  wrb_index = AMAP_GET_BITS(struct amap_it_dmsg_cqe,
       wrb_idx, psol);
  cid = AMAP_GET_BITS(struct amap_it_dmsg_cqe,
        cid, psol);
 } else {
  wrb_index = AMAP_GET_BITS(struct amap_it_dmsg_cqe_v2,
       wrb_idx, psol);
  cid = AMAP_GET_BITS(struct amap_it_dmsg_cqe_v2,
        cid, psol);
 }

 cri_index = BE_GET_CRI_FROM_CID(cid);
 pwrb_context = &phwi_ctrlr->wrb_context[cri_index];
 pwrb_handle = pwrb_context->pwrb_handle_basestd[wrb_index];
 session = beiscsi_conn->conn->session;
 spin_lock_bh(&session->back_lock);
 task = pwrb_handle->pio_handle;
 if (task)
  __iscsi_put_task(task);
 spin_unlock_bh(&session->back_lock);
}

static void
be_complete_nopin_resp(struct beiscsi_conn *beiscsi_conn,
   struct iscsi_task *task,
   struct common_sol_cqe *csol_cqe)
{
 struct iscsi_nopin *hdr;
 struct iscsi_conn *conn = beiscsi_conn->conn;
 struct beiscsi_io_task *io_task = task->dd_data;

 hdr = (struct iscsi_nopin *)task->hdr;
 hdr->flags = csol_cqe->i_flags;
 hdr->exp_cmdsn = cpu_to_be32(csol_cqe->exp_cmdsn);
 hdr->max_cmdsn = cpu_to_be32(csol_cqe->exp_cmdsn +
         csol_cqe->cmd_wnd - 1);

 hdr->opcode = ISCSI_OP_NOOP_IN;
 hdr->itt = io_task->libiscsi_itt;
 __iscsi_complete_pdu(conn, (struct iscsi_hdr *)hdr, NULL, 0);
}

static void adapter_get_sol_cqe(struct beiscsi_hba *phba,
  struct sol_cqe *psol,
  struct common_sol_cqe *csol_cqe)
{
 if (is_chip_be2_be3r(phba)) {
  csol_cqe->exp_cmdsn = AMAP_GET_BITS(struct amap_sol_cqe,
          i_exp_cmd_sn, psol);
  csol_cqe->res_cnt = AMAP_GET_BITS(struct amap_sol_cqe,
        i_res_cnt, psol);
  csol_cqe->cmd_wnd = AMAP_GET_BITS(struct amap_sol_cqe,
        i_cmd_wnd, psol);
  csol_cqe->wrb_index = AMAP_GET_BITS(struct amap_sol_cqe,
          wrb_index, psol);
  csol_cqe->cid = AMAP_GET_BITS(struct amap_sol_cqe,
           cid, psol);
  csol_cqe->hw_sts = AMAP_GET_BITS(struct amap_sol_cqe,
       hw_sts, psol);
  csol_cqe->i_resp = AMAP_GET_BITS(struct amap_sol_cqe,
       i_resp, psol);
  csol_cqe->i_sts = AMAP_GET_BITS(struct amap_sol_cqe,
      i_sts, psol);
  csol_cqe->i_flags = AMAP_GET_BITS(struct amap_sol_cqe,
        i_flags, psol);
 } else {
  csol_cqe->exp_cmdsn = AMAP_GET_BITS(struct amap_sol_cqe_v2,
          i_exp_cmd_sn, psol);
  csol_cqe->res_cnt = AMAP_GET_BITS(struct amap_sol_cqe_v2,
        i_res_cnt, psol);
  csol_cqe->wrb_index = AMAP_GET_BITS(struct amap_sol_cqe_v2,
          wrb_index, psol);
  csol_cqe->cid = AMAP_GET_BITS(struct amap_sol_cqe_v2,
           cid, psol);
  csol_cqe->hw_sts = AMAP_GET_BITS(struct amap_sol_cqe_v2,
       hw_sts, psol);
  csol_cqe->cmd_wnd = AMAP_GET_BITS(struct amap_sol_cqe_v2,
        i_cmd_wnd, psol);
  if (AMAP_GET_BITS(struct amap_sol_cqe_v2,
      cmd_cmpl, psol))
   csol_cqe->i_sts = AMAP_GET_BITS(struct amap_sol_cqe_v2,
       i_sts, psol);
  else
   csol_cqe->i_resp = AMAP_GET_BITS(struct amap_sol_cqe_v2,
        i_sts, psol);
  if (AMAP_GET_BITS(struct amap_sol_cqe_v2,
      u, psol))
   csol_cqe->i_flags = ISCSI_FLAG_CMD_UNDERFLOW;

  if (AMAP_GET_BITS(struct amap_sol_cqe_v2,
      o, psol))
   csol_cqe->i_flags |= ISCSI_FLAG_CMD_OVERFLOW;
 }
}


static void hwi_complete_cmd(struct beiscsi_conn *beiscsi_conn,
        struct beiscsi_hba *phba, struct sol_cqe *psol)
{
 struct iscsi_conn *conn = beiscsi_conn->conn;
 struct iscsi_session *session = conn->session;
 struct common_sol_cqe csol_cqe = {0};
 struct hwi_wrb_context *pwrb_context;
 struct hwi_controller *phwi_ctrlr;
 struct wrb_handle *pwrb_handle;
 struct iscsi_task *task;
 uint16_t cri_index = 0;
 uint8_t type;

 phwi_ctrlr = phba->phwi_ctrlr;

 /* Copy the elements to a common structure */
 adapter_get_sol_cqe(phba, psol, &csol_cqe);

 cri_index = BE_GET_CRI_FROM_CID(csol_cqe.cid);
 pwrb_context = &phwi_ctrlr->wrb_context[cri_index];

 pwrb_handle = pwrb_context->pwrb_handle_basestd[
        csol_cqe.wrb_index];

 spin_lock_bh(&session->back_lock);
 task = pwrb_handle->pio_handle;
 if (!task) {
  spin_unlock_bh(&session->back_lock);
  return;
 }
 type = ((struct beiscsi_io_task *)task->dd_data)->wrb_type;

 switch (type) {
 case HWH_TYPE_IO:
 case HWH_TYPE_IO_RD:
  if ((task->hdr->opcode & ISCSI_OPCODE_MASK) ==
       ISCSI_OP_NOOP_OUT)
   be_complete_nopin_resp(beiscsi_conn, task, &csol_cqe);
  else
   be_complete_io(beiscsi_conn, task, &csol_cqe);
  break;

 case HWH_TYPE_LOGOUT:
  if ((task->hdr->opcode & ISCSI_OPCODE_MASK) == ISCSI_OP_LOGOUT)
   be_complete_logout(beiscsi_conn, task, &csol_cqe);
  else
   be_complete_tmf(beiscsi_conn, task, &csol_cqe);
  break;

 case HWH_TYPE_LOGIN:
  beiscsi_log(phba, KERN_ERR,
       BEISCSI_LOG_CONFIG | BEISCSI_LOG_IO,
       "BM_%d :\t\t No HWH_TYPE_LOGIN Expected in"
       " %s- Solicited path\n", __func__);
  break;

 case HWH_TYPE_NOP:
  be_complete_nopin_resp(beiscsi_conn, task, &csol_cqe);
  break;

 default:
  beiscsi_log(phba, KERN_WARNING,
       BEISCSI_LOG_CONFIG | BEISCSI_LOG_IO,
       "BM_%d : In %s, unknown type = %d "
       "wrb_index 0x%x CID 0x%x\n", __func__, type,
       csol_cqe.wrb_index,
       csol_cqe.cid);
  break;
 }

 spin_unlock_bh(&session->back_lock);
}

/*
 * ASYNC PDUs include
 * a. Unsolicited NOP-In (target initiated NOP-In)
 * b. ASYNC Messages
 * c. Reject PDU
 * d. Login response
 * These headers arrive unprocessed by the EP firmware.
 * iSCSI layer processes them.
 */

static unsigned int
beiscsi_complete_pdu(struct beiscsi_conn *beiscsi_conn,
  struct pdu_base *phdr, void *pdata, unsigned int dlen)
{
 struct beiscsi_hba *phba = beiscsi_conn->phba;
 struct iscsi_conn *conn = beiscsi_conn->conn;
 struct beiscsi_io_task *io_task;
 struct iscsi_hdr *login_hdr;
 struct iscsi_task *task;
 u8 code;

 code = AMAP_GET_BITS(struct amap_pdu_base, opcode, phdr);
 switch (code) {
 case ISCSI_OP_NOOP_IN:
  pdata = NULL;
  dlen = 0;
  break;
 case ISCSI_OP_ASYNC_EVENT:
  break;
 case ISCSI_OP_REJECT:
  WARN_ON(!pdata);
  WARN_ON(!(dlen == 48));
  beiscsi_log(phba, KERN_ERR,
       BEISCSI_LOG_CONFIG | BEISCSI_LOG_IO,
       "BM_%d : In ISCSI_OP_REJECT\n");
  break;
 case ISCSI_OP_LOGIN_RSP:
 case ISCSI_OP_TEXT_RSP:
  task = conn->login_task;
  io_task = task->dd_data;
  login_hdr = (struct iscsi_hdr *)phdr;
  login_hdr->itt = io_task->libiscsi_itt;
  break;
 default:
  beiscsi_log(phba, KERN_WARNING,
       BEISCSI_LOG_IO | BEISCSI_LOG_CONFIG,
       "BM_%d : unrecognized async PDU opcode 0x%x\n",
       code);
  return 1;
 }
 __iscsi_complete_pdu(conn, (struct iscsi_hdr *)phdr, pdata, dlen);
 return 0;
}

static inline void
beiscsi_hdl_put_handle(struct hd_async_context *pasync_ctx,
    struct hd_async_handle *pasync_handle)
{
 pasync_handle->is_final = 0;
 pasync_handle->buffer_len = 0;
 pasync_handle->in_use = 0;
 list_del_init(&pasync_handle->link);
}

static void
beiscsi_hdl_purge_handles(struct beiscsi_hba *phba,
     struct hd_async_context *pasync_ctx,
     u16 cri)
{
 struct hd_async_handle *pasync_handle, *tmp_handle;
 struct list_head *plist;

 plist  = &pasync_ctx->async_entry[cri].wq.list;
 list_for_each_entry_safe(pasync_handle, tmp_handle, plist, link)
  beiscsi_hdl_put_handle(pasync_ctx, pasync_handle);

 INIT_LIST_HEAD(&pasync_ctx->async_entry[cri].wq.list);
 pasync_ctx->async_entry[cri].wq.hdr_len = 0;
 pasync_ctx->async_entry[cri].wq.bytes_received = 0;
 pasync_ctx->async_entry[cri].wq.bytes_needed = 0;
}

static struct hd_async_handle *
beiscsi_hdl_get_handle(struct beiscsi_conn *beiscsi_conn,
         struct hd_async_context *pasync_ctx,
         struct i_t_dpdu_cqe *pdpdu_cqe,
         u8 *header)
{
 struct beiscsi_hba *phba = beiscsi_conn->phba;
 struct hd_async_handle *pasync_handle;
 struct be_bus_address phys_addr;
 u16 cid, code, ci, cri;
 u8 final, error = 0;
 u32 dpl;

 cid = beiscsi_conn->beiscsi_conn_cid;
 cri = BE_GET_ASYNC_CRI_FROM_CID(cid);
 /**
 * This function is invoked to get the right async_handle structure
 * from a given DEF PDU CQ entry.
 *
 * - index in CQ entry gives the vertical index
 * - address in CQ entry is the offset where the DMA last ended
 * - final - no more notifications for this PDU
 */

 if (is_chip_be2_be3r(phba)) {
  dpl = AMAP_GET_BITS(struct amap_i_t_dpdu_cqe,
        dpl, pdpdu_cqe);
  ci = AMAP_GET_BITS(struct amap_i_t_dpdu_cqe,
          index, pdpdu_cqe);
  final = AMAP_GET_BITS(struct amap_i_t_dpdu_cqe,
          final, pdpdu_cqe);
 } else {
  dpl = AMAP_GET_BITS(struct amap_i_t_dpdu_cqe_v2,
        dpl, pdpdu_cqe);
  ci = AMAP_GET_BITS(struct amap_i_t_dpdu_cqe_v2,
          index, pdpdu_cqe);
  final = AMAP_GET_BITS(struct amap_i_t_dpdu_cqe_v2,
          final, pdpdu_cqe);
 }

 /**
 * DB addr Hi/Lo is same for BE and SKH.
 * Subtract the dataplacementlength to get to the base.
 */

 phys_addr.u.a32.address_lo = AMAP_GET_BITS(struct amap_i_t_dpdu_cqe,
         db_addr_lo, pdpdu_cqe);
 phys_addr.u.a32.address_lo -= dpl;
 phys_addr.u.a32.address_hi = AMAP_GET_BITS(struct amap_i_t_dpdu_cqe,
         db_addr_hi, pdpdu_cqe);

 code = AMAP_GET_BITS(struct amap_i_t_dpdu_cqe, code, pdpdu_cqe);
 switch (code) {
 case UNSOL_HDR_NOTIFY:
  pasync_handle = pasync_ctx->async_entry[ci].header;
  *header = 1;
  break;
 case UNSOL_DATA_DIGEST_ERROR_NOTIFY:
  error = 1;
  fallthrough;
 case UNSOL_DATA_NOTIFY:
  pasync_handle = pasync_ctx->async_entry[ci].data;
  break;
 /* called only for above codes */
 default:
  return NULL;
 }

 if (pasync_handle->pa.u.a64.address != phys_addr.u.a64.address ||
     pasync_handle->index != ci) {
  /* driver bug - if ci does not match async handle index */
  error = 1;
  beiscsi_log(phba, KERN_ERR, BEISCSI_LOG_ISCSI,
       "BM_%d : cid %u async PDU handle mismatch - addr in %cQE %llx at %u:addr in CQE %llx ci %u\n",
       cid, pasync_handle->is_header ? 'H' : 'D',
       pasync_handle->pa.u.a64.address,
       pasync_handle->index,
       phys_addr.u.a64.address, ci);
  /* FW has stale address - attempt continuing by dropping */
 }

 /**
 * DEF PDU header and data buffers with errors should be simply
 * dropped as there are no consumers for it.
 */

 if (error) {
  beiscsi_hdl_put_handle(pasync_ctx, pasync_handle);
  return NULL;
 }

 if (pasync_handle->in_use || !list_empty(&pasync_handle->link)) {
  beiscsi_log(phba, KERN_ERR, BEISCSI_LOG_ISCSI,
       "BM_%d : cid %d async PDU handle in use - code %d ci %d addr %llx\n",
       cid, code, ci, phys_addr.u.a64.address);
  beiscsi_hdl_purge_handles(phba, pasync_ctx, cri);
 }

 list_del_init(&pasync_handle->link);
 /**
 * Each CID is associated with unique CRI.
 * ASYNC_CRI_FROM_CID mapping and CRI_FROM_CID are totaly different.
 **/

 pasync_handle->cri = cri;
 pasync_handle->is_final = final;
 pasync_handle->buffer_len = dpl;
 pasync_handle->in_use = 1;

 return pasync_handle;
}

static unsigned int
beiscsi_hdl_fwd_pdu(struct beiscsi_conn *beiscsi_conn,
      struct hd_async_context *pasync_ctx,
      u16 cri)
{
 struct iscsi_session *session = beiscsi_conn->conn->session;
 struct hd_async_handle *pasync_handle, *plast_handle;
 struct beiscsi_hba *phba = beiscsi_conn->phba;
 void *phdr = NULL, *pdata = NULL;
 u32 dlen = 0, status = 0;
 struct list_head *plist;

 plist = &pasync_ctx->async_entry[cri].wq.list;
 plast_handle = NULL;
 list_for_each_entry(pasync_handle, plist, link) {
  plast_handle = pasync_handle;
  /* get the header, the first entry */
  if (!phdr) {
   phdr = pasync_handle->pbuffer;
   continue;
  }
  /* use first buffer to collect all the data */
  if (!pdata) {
   pdata = pasync_handle->pbuffer;
   dlen = pasync_handle->buffer_len;
   continue;
  }
  if (!pasync_handle->buffer_len ||
      (dlen + pasync_handle->buffer_len) >
      pasync_ctx->async_data.buffer_size)
   break;
  memcpy(pdata + dlen, pasync_handle->pbuffer,
         pasync_handle->buffer_len);
  dlen += pasync_handle->buffer_len;
 }

 if (!plast_handle->is_final) {
  /* last handle should have final PDU notification from FW */
  beiscsi_log(phba, KERN_ERR, BEISCSI_LOG_ISCSI,
       "BM_%d : cid %u %p fwd async PDU opcode %x with last handle missing - HL%u:DN%u:DR%u\n",
       beiscsi_conn->beiscsi_conn_cid, plast_handle,
       AMAP_GET_BITS(struct amap_pdu_base, opcode, phdr),
       pasync_ctx->async_entry[cri].wq.hdr_len,
       pasync_ctx->async_entry[cri].wq.bytes_needed,
       pasync_ctx->async_entry[cri].wq.bytes_received);
 }
 spin_lock_bh(&session->back_lock);
 status = beiscsi_complete_pdu(beiscsi_conn, phdr, pdata, dlen);
 spin_unlock_bh(&session->back_lock);
 beiscsi_hdl_purge_handles(phba, pasync_ctx, cri);
 return status;
}

static unsigned int
beiscsi_hdl_gather_pdu(struct beiscsi_conn *beiscsi_conn,
         struct hd_async_context *pasync_ctx,
         struct hd_async_handle *pasync_handle)
{
 unsigned int bytes_needed = 0, status = 0;
 u16 cri = pasync_handle->cri;
 struct cri_wait_queue *wq;
 struct beiscsi_hba *phba;
 struct pdu_base *ppdu;
 char *err = "";

 phba = beiscsi_conn->phba;
 wq = &pasync_ctx->async_entry[cri].wq;
 if (pasync_handle->is_header) {
  /* check if PDU hdr is rcv'd when old hdr not completed */
  if (wq->hdr_len) {
   err = "incomplete";
   goto drop_pdu;
  }
  ppdu = pasync_handle->pbuffer;
  bytes_needed = AMAP_GET_BITS(struct amap_pdu_base,
          data_len_hi, ppdu);
  bytes_needed <<= 16;
  bytes_needed |= be16_to_cpu(AMAP_GET_BITS(struct amap_pdu_base,
         data_len_lo, ppdu));
  wq->hdr_len = pasync_handle->buffer_len;
  wq->bytes_received = 0;
  wq->bytes_needed = bytes_needed;
  list_add_tail(&pasync_handle->link, &wq->list);
  if (!bytes_needed)
   status = beiscsi_hdl_fwd_pdu(beiscsi_conn,
           pasync_ctx, cri);
 } else {
  /* check if data received has header and is needed */
  if (!wq->hdr_len || !wq->bytes_needed) {
   err = "header less";
   goto drop_pdu;
  }
  wq->bytes_received += pasync_handle->buffer_len;
  /* Something got overwritten? Better catch it here. */
  if (wq->bytes_received > wq->bytes_needed) {
   err = "overflow";
   goto drop_pdu;
  }
  list_add_tail(&pasync_handle->link, &wq->list);
  if (wq->bytes_received == wq->bytes_needed)
   status = beiscsi_hdl_fwd_pdu(beiscsi_conn,
           pasync_ctx, cri);
 }
 return status;

drop_pdu:
 beiscsi_log(phba, KERN_ERR, BEISCSI_LOG_ISCSI,
      "BM_%d : cid %u async PDU %s - def-%c:HL%u:DN%u:DR%u\n",
      beiscsi_conn->beiscsi_conn_cid, err,
      pasync_handle->is_header ? 'H' : 'D',
      wq->hdr_len, wq->bytes_needed,
      pasync_handle->buffer_len);
 /* discard this handle */
 beiscsi_hdl_put_handle(pasync_ctx, pasync_handle);
 /* free all the other handles in cri_wait_queue */
 beiscsi_hdl_purge_handles(phba, pasync_ctx, cri);
 /* try continuing */
 return status;
}

static void
beiscsi_hdq_post_handles(struct beiscsi_hba *phba,
    u8 header, u8 ulp_num, u16 nbuf)
{
 struct hd_async_handle *pasync_handle;
 struct hd_async_context *pasync_ctx;
 struct hwi_controller *phwi_ctrlr;
 struct phys_addr *pasync_sge;
 u32 ring_id, doorbell = 0;
 u32 doorbell_offset;
 u16 prod, pi;

 phwi_ctrlr = phba->phwi_ctrlr;
 pasync_ctx = HWI_GET_ASYNC_PDU_CTX(phwi_ctrlr, ulp_num);
 if (header) {
  pasync_sge = pasync_ctx->async_header.ring_base;
  pi = pasync_ctx->async_header.pi;
  ring_id = phwi_ctrlr->default_pdu_hdr[ulp_num].id;
  doorbell_offset = phwi_ctrlr->default_pdu_hdr[ulp_num].
     doorbell_offset;
 } else {
  pasync_sge = pasync_ctx->async_data.ring_base;
  pi = pasync_ctx->async_data.pi;
  ring_id = phwi_ctrlr->default_pdu_data[ulp_num].id;
  doorbell_offset = phwi_ctrlr->default_pdu_data[ulp_num].
     doorbell_offset;
 }

 for (prod = 0; prod < nbuf; prod++) {
  if (header)
   pasync_handle = pasync_ctx->async_entry[pi].header;
  else
   pasync_handle = pasync_ctx->async_entry[pi].data;
  WARN_ON(pasync_handle->is_header != header);
  WARN_ON(pasync_handle->index != pi);
  /* setup the ring only once */
  if (nbuf == pasync_ctx->num_entries) {
   /* note hi is lo */
   pasync_sge[pi].hi = pasync_handle->pa.u.a32.address_lo;
   pasync_sge[pi].lo = pasync_handle->pa.u.a32.address_hi;
  }
  if (++pi == pasync_ctx->num_entries)
   pi = 0;
 }

 if (header)
  pasync_ctx->async_header.pi = pi;
 else
  pasync_ctx->async_data.pi = pi;

 doorbell |= ring_id & DB_DEF_PDU_RING_ID_MASK;
 doorbell |= 1 << DB_DEF_PDU_REARM_SHIFT;
 doorbell |= 0 << DB_DEF_PDU_EVENT_SHIFT;
 doorbell |= (prod & DB_DEF_PDU_CQPROC_MASK) << DB_DEF_PDU_CQPROC_SHIFT;
 iowrite32(doorbell, phba->db_va + doorbell_offset);
}

static void
beiscsi_hdq_process_compl(struct beiscsi_conn *beiscsi_conn,
     struct i_t_dpdu_cqe *pdpdu_cqe)
{
 struct beiscsi_hba *phba = beiscsi_conn->phba;
 struct hd_async_handle *pasync_handle = NULL;
 struct hd_async_context *pasync_ctx;
 struct hwi_controller *phwi_ctrlr;
 u8 ulp_num, consumed, header = 0;
 u16 cid_cri;

 phwi_ctrlr = phba->phwi_ctrlr;
 cid_cri = BE_GET_CRI_FROM_CID(beiscsi_conn->beiscsi_conn_cid);
 ulp_num = BEISCSI_GET_ULP_FROM_CRI(phwi_ctrlr, cid_cri);
 pasync_ctx = HWI_GET_ASYNC_PDU_CTX(phwi_ctrlr, ulp_num);
 pasync_handle = beiscsi_hdl_get_handle(beiscsi_conn, pasync_ctx,
            pdpdu_cqe, &header);
 if (is_chip_be2_be3r(phba))
  consumed = AMAP_GET_BITS(struct amap_i_t_dpdu_cqe,
      num_cons, pdpdu_cqe);
 else
  consumed = AMAP_GET_BITS(struct amap_i_t_dpdu_cqe_v2,
      num_cons, pdpdu_cqe);
 if (pasync_handle)
  beiscsi_hdl_gather_pdu(beiscsi_conn, pasync_ctx, pasync_handle);
 /* num_cons indicates number of 8 RQEs consumed */
 if (consumed)
  beiscsi_hdq_post_handles(phba, header, ulp_num, 8 * consumed);
}

void beiscsi_process_mcc_cq(struct beiscsi_hba *phba)
{
 struct be_queue_info *mcc_cq;
 struct  be_mcc_compl *mcc_compl;
 unsigned int num_processed = 0;

 mcc_cq = &phba->ctrl.mcc_obj.cq;
 mcc_compl = queue_tail_node(mcc_cq);
 mcc_compl->flags = le32_to_cpu(mcc_compl->flags);
 while (mcc_compl->flags & CQE_FLAGS_VALID_MASK) {
  if (beiscsi_hba_in_error(phba))
   return;

  if (num_processed >= 32) {
   hwi_ring_cq_db(phba, mcc_cq->id,
     num_processed, 0);
   num_processed = 0;
  }
  if (mcc_compl->flags & CQE_FLAGS_ASYNC_MASK) {
   beiscsi_process_async_event(phba, mcc_compl);
  } else if (mcc_compl->flags & CQE_FLAGS_COMPLETED_MASK) {
   beiscsi_process_mcc_compl(&phba->ctrl, mcc_compl);
  }

  mcc_compl->flags = 0;
  queue_tail_inc(mcc_cq);
  mcc_compl = queue_tail_node(mcc_cq);
  mcc_compl->flags = le32_to_cpu(mcc_compl->flags);
  num_processed++;
 }

 if (num_processed > 0)
  hwi_ring_cq_db(phba, mcc_cq->id, num_processed, 1);
}

static void beiscsi_mcc_work(struct work_struct *work)
{
 struct be_eq_obj *pbe_eq;
 struct beiscsi_hba *phba;

 pbe_eq = container_of(work, struct be_eq_obj, mcc_work);
 phba = pbe_eq->phba;
 beiscsi_process_mcc_cq(phba);
 /* rearm EQ for further interrupts */
 if (!beiscsi_hba_in_error(phba))
  hwi_ring_eq_db(phba, pbe_eq->q.id, 0, 0, 1, 1);
}

/**
 * beiscsi_process_cq()- Process the Completion Queue
 * @pbe_eq: Event Q on which the Completion has come
 * @budget: Max number of events to processed
 *
 * return
 *     Number of Completion Entries processed.
 **/

unsigned int beiscsi_process_cq(struct be_eq_obj *pbe_eq, int budget)
{
 struct be_queue_info *cq;
 struct sol_cqe *sol;
 unsigned int total = 0;
 unsigned int num_processed = 0;
 unsigned short code = 0, cid = 0;
 uint16_t cri_index = 0;
 struct beiscsi_conn *beiscsi_conn;
 struct beiscsi_endpoint *beiscsi_ep;
 struct iscsi_endpoint *ep;
 struct beiscsi_hba *phba;

 cq = pbe_eq->cq;
 sol = queue_tail_node(cq);
 phba = pbe_eq->phba;

 while (sol->dw[offsetof(struct amap_sol_cqe, valid) / 32] &
        CQE_VALID_MASK) {
  if (beiscsi_hba_in_error(phba))
   return 0;

  be_dws_le_to_cpu(sol, sizeof(struct sol_cqe));

  code = (sol->dw[offsetof(struct amap_sol_cqe, code) / 32] &
    CQE_CODE_MASK);

   /* Get the CID */
  if (is_chip_be2_be3r(phba)) {
   cid = AMAP_GET_BITS(struct amap_sol_cqe, cid, sol);
  } else {
   if ((code == DRIVERMSG_NOTIFY) ||
       (code == UNSOL_HDR_NOTIFY) ||
       (code == UNSOL_DATA_NOTIFY))
    cid = AMAP_GET_BITS(
          struct amap_i_t_dpdu_cqe_v2,
          cid, sol);
   else
    cid = AMAP_GET_BITS(struct amap_sol_cqe_v2,
          cid, sol);
  }

  cri_index = BE_GET_CRI_FROM_CID(cid);
  ep = phba->ep_array[cri_index];

  if (ep == NULL) {
   /* connection has already been freed
 * just move on to next one
 */

   beiscsi_log(phba, KERN_WARNING,
        BEISCSI_LOG_INIT,
        "BM_%d : proc cqe of disconn ep: cid %d\n",
        cid);
   goto proc_next_cqe;
  }

  beiscsi_ep = ep->dd_data;
  beiscsi_conn = beiscsi_ep->conn;

  /* replenish cq */
  if (num_processed == 32) {
   hwi_ring_cq_db(phba, cq->id, 32, 0);
   num_processed = 0;
  }
  total++;

  switch (code) {
  case SOL_CMD_COMPLETE:
   hwi_complete_cmd(beiscsi_conn, phba, sol);
   break;
  case DRIVERMSG_NOTIFY:
   beiscsi_log(phba, KERN_INFO,
        BEISCSI_LOG_IO | BEISCSI_LOG_CONFIG,
        "BM_%d : Received %s[%d] on CID : %d\n",
        cqe_desc[code], code, cid);

   hwi_complete_drvr_msgs(beiscsi_conn, phba, sol);
   break;
  case UNSOL_HDR_NOTIFY:
   beiscsi_log(phba, KERN_INFO,
        BEISCSI_LOG_IO | BEISCSI_LOG_CONFIG,
        "BM_%d : Received %s[%d] on CID : %d\n",
        cqe_desc[code], code, cid);

   spin_lock_bh(&phba->async_pdu_lock);
   beiscsi_hdq_process_compl(beiscsi_conn,
        (struct i_t_dpdu_cqe *)sol);
   spin_unlock_bh(&phba->async_pdu_lock);
   break;
  case UNSOL_DATA_NOTIFY:
   beiscsi_log(phba, KERN_INFO,
        BEISCSI_LOG_CONFIG | BEISCSI_LOG_IO,
        "BM_%d : Received %s[%d] on CID : %d\n",
        cqe_desc[code], code, cid);

   spin_lock_bh(&phba->async_pdu_lock);
   beiscsi_hdq_process_compl(beiscsi_conn,
        (struct i_t_dpdu_cqe *)sol);
   spin_unlock_bh(&phba->async_pdu_lock);
   break;
  case CXN_INVALIDATE_INDEX_NOTIFY:
  case CMD_INVALIDATED_NOTIFY:
  case CXN_INVALIDATE_NOTIFY:
   beiscsi_log(phba, KERN_ERR,
        BEISCSI_LOG_IO | BEISCSI_LOG_CONFIG,
        "BM_%d : Ignoring %s[%d] on CID : %d\n",
        cqe_desc[code], code, cid);
   break;
  case CXN_KILLED_HDR_DIGEST_ERR:
  case SOL_CMD_KILLED_DATA_DIGEST_ERR:
   beiscsi_log(phba, KERN_ERR,
        BEISCSI_LOG_CONFIG | BEISCSI_LOG_IO,
        "BM_%d : Cmd Notification %s[%d] on CID : %d\n",
        cqe_desc[code], code,  cid);
   break;
  case CMD_KILLED_INVALID_STATSN_RCVD:
  case CMD_KILLED_INVALID_R2T_RCVD:
  case CMD_CXN_KILLED_LUN_INVALID:
  case CMD_CXN_KILLED_ICD_INVALID:
  case CMD_CXN_KILLED_ITT_INVALID:
  case CMD_CXN_KILLED_SEQ_OUTOFORDER:
  case CMD_CXN_KILLED_INVALID_DATASN_RCVD:
   beiscsi_log(phba, KERN_ERR,
        BEISCSI_LOG_CONFIG | BEISCSI_LOG_IO,
        "BM_%d : Cmd Notification %s[%d] on CID : %d\n",
        cqe_desc[code], code,  cid);
   break;
  case UNSOL_DATA_DIGEST_ERROR_NOTIFY:
   beiscsi_log(phba, KERN_ERR,
        BEISCSI_LOG_IO | BEISCSI_LOG_CONFIG,
        "BM_%d : Dropping %s[%d] on DPDU ring on CID : %d\n",
        cqe_desc[code], code, cid);
   spin_lock_bh(&phba->async_pdu_lock);
   /* driver consumes the entry and drops the contents */
   beiscsi_hdq_process_compl(beiscsi_conn,
        (struct i_t_dpdu_cqe *)sol);
   spin_unlock_bh(&phba->async_pdu_lock);
   break;
  case CXN_KILLED_PDU_SIZE_EXCEEDS_DSL:
  case CXN_KILLED_BURST_LEN_MISMATCH:
  case CXN_KILLED_AHS_RCVD:
  case CXN_KILLED_UNKNOWN_HDR:
  case CXN_KILLED_STALE_ITT_TTT_RCVD:
  case CXN_KILLED_INVALID_ITT_TTT_RCVD:
  case CXN_KILLED_TIMED_OUT:
  case CXN_KILLED_FIN_RCVD:
  case CXN_KILLED_RST_SENT:
  case CXN_KILLED_RST_RCVD:
  case CXN_KILLED_BAD_UNSOL_PDU_RCVD:
  case CXN_KILLED_BAD_WRB_INDEX_ERROR:
  case CXN_KILLED_OVER_RUN_RESIDUAL:
  case CXN_KILLED_UNDER_RUN_RESIDUAL:
  case CXN_KILLED_CMND_DATA_NOT_ON_SAME_CONN:
   beiscsi_log(phba, KERN_ERR,
        BEISCSI_LOG_IO | BEISCSI_LOG_CONFIG,
        "BM_%d : Event %s[%d] received on CID : %d\n",
        cqe_desc[code], code, cid);
   if (beiscsi_conn)
    iscsi_conn_failure(beiscsi_conn->conn,
         ISCSI_ERR_CONN_FAILED);
   break;
  default:
   beiscsi_log(phba, KERN_ERR,
        BEISCSI_LOG_IO | BEISCSI_LOG_CONFIG,
        "BM_%d : Invalid CQE Event Received Code : %d CID 0x%x...\n",
        code, cid);
   break;
  }

proc_next_cqe:
  AMAP_SET_BITS(struct amap_sol_cqe, valid, sol, 0);
  queue_tail_inc(cq);
  sol = queue_tail_node(cq);
  num_processed++;
  if (total == budget)
   break;
 }

 hwi_ring_cq_db(phba, cq->id, num_processed, 1);
 return total;
}

static int be_iopoll(struct irq_poll *iop, int budget)
{
 unsigned int ret, io_events;
 struct beiscsi_hba *phba;
 struct be_eq_obj *pbe_eq;
 struct be_eq_entry *eqe = NULL;
 struct be_queue_info *eq;

 pbe_eq = container_of(iop, struct be_eq_obj, iopoll);
 phba = pbe_eq->phba;
 if (beiscsi_hba_in_error(phba)) {
  irq_poll_complete(iop);
  return 0;
 }

 io_events = 0;
 eq = &pbe_eq->q;
 eqe = queue_tail_node(eq);
 while (eqe->dw[offsetof(struct amap_eq_entry, valid) / 32] &
   EQE_VALID_MASK) {
  AMAP_SET_BITS(struct amap_eq_entry, valid, eqe, 0);
  queue_tail_inc(eq);
  eqe = queue_tail_node(eq);
  io_events++;
 }
 hwi_ring_eq_db(phba, eq->id, 1, io_events, 0, 1);

 ret = beiscsi_process_cq(pbe_eq, budget);
 pbe_eq->cq_count += ret;
 if (ret < budget) {
  irq_poll_complete(iop);
  beiscsi_log(phba, KERN_INFO,
       BEISCSI_LOG_CONFIG | BEISCSI_LOG_IO,
       "BM_%d : rearm pbe_eq->q.id =%d ret %d\n",
       pbe_eq->q.id, ret);
  if (!beiscsi_hba_in_error(phba))
   hwi_ring_eq_db(phba, pbe_eq->q.id, 0, 0, 1, 1);
 }
 return ret;
}

static void
hwi_write_sgl_v2(struct iscsi_wrb *pwrb, struct scatterlist *sg,
    unsigned int num_sg, struct beiscsi_io_task *io_task)
{
 struct iscsi_sge *psgl;
 unsigned int sg_len, index;
 unsigned int sge_len = 0;
 unsigned long long addr;
 struct scatterlist *l_sg;
 unsigned int offset;

 AMAP_SET_BITS(struct amap_iscsi_wrb_v2, iscsi_bhs_addr_lo, pwrb,
        io_task->bhs_pa.u.a32.address_lo);
 AMAP_SET_BITS(struct amap_iscsi_wrb_v2, iscsi_bhs_addr_hi, pwrb,
        io_task->bhs_pa.u.a32.address_hi);

 l_sg = sg;
 for (index = 0; (index < num_sg) && (index < 2); index++,
   sg = sg_next(sg)) {
  if (index == 0) {
   sg_len = sg_dma_len(sg);
   addr = (u64) sg_dma_address(sg);
   AMAP_SET_BITS(struct amap_iscsi_wrb_v2,
          sge0_addr_lo, pwrb,
          lower_32_bits(addr));
   AMAP_SET_BITS(struct amap_iscsi_wrb_v2,
          sge0_addr_hi, pwrb,
          upper_32_bits(addr));
   AMAP_SET_BITS(struct amap_iscsi_wrb_v2,
          sge0_len, pwrb,
          sg_len);
   sge_len = sg_len;
  } else {
   AMAP_SET_BITS(struct amap_iscsi_wrb_v2, sge1_r2t_offset,
          pwrb, sge_len);
   sg_len = sg_dma_len(sg);
   addr = (u64) sg_dma_address(sg);
   AMAP_SET_BITS(struct amap_iscsi_wrb_v2,
          sge1_addr_lo, pwrb,
          lower_32_bits(addr));
   AMAP_SET_BITS(struct amap_iscsi_wrb_v2,
          sge1_addr_hi, pwrb,
          upper_32_bits(addr));
   AMAP_SET_BITS(struct amap_iscsi_wrb_v2,
          sge1_len, pwrb,
          sg_len);
  }
 }
 psgl = (struct iscsi_sge *)io_task->psgl_handle->pfrag;
 memset(psgl, 0, sizeof(*psgl) * BE2_SGE);

 AMAP_SET_BITS(struct amap_iscsi_sge, len, psgl, io_task->bhs_len - 2);

 AMAP_SET_BITS(struct amap_iscsi_sge, addr_hi, psgl,
        io_task->bhs_pa.u.a32.address_hi);
 AMAP_SET_BITS(struct amap_iscsi_sge, addr_lo, psgl,
        io_task->bhs_pa.u.a32.address_lo);

 if (num_sg == 1) {
  AMAP_SET_BITS(struct amap_iscsi_wrb_v2, sge0_last, pwrb,
         1);
  AMAP_SET_BITS(struct amap_iscsi_wrb_v2, sge1_last, pwrb,
         0);
 } else if (num_sg == 2) {
  AMAP_SET_BITS(struct amap_iscsi_wrb_v2, sge0_last, pwrb,
         0);
  AMAP_SET_BITS(struct amap_iscsi_wrb_v2, sge1_last, pwrb,
         1);
 } else {
  AMAP_SET_BITS(struct amap_iscsi_wrb_v2, sge0_last, pwrb,
         0);
  AMAP_SET_BITS(struct amap_iscsi_wrb_v2, sge1_last, pwrb,
         0);
 }

 sg = l_sg;
 psgl++;
 psgl++;
 offset = 0;
 for (index = 0; index < num_sg; index++, sg = sg_next(sg), psgl++) {
  sg_len = sg_dma_len(sg);
  addr = (u64) sg_dma_address(sg);
  AMAP_SET_BITS(struct amap_iscsi_sge, addr_lo, psgl,
         lower_32_bits(addr));
  AMAP_SET_BITS(struct amap_iscsi_sge, addr_hi, psgl,
         upper_32_bits(addr));
  AMAP_SET_BITS(struct amap_iscsi_sge, len, psgl, sg_len);
  AMAP_SET_BITS(struct amap_iscsi_sge, sge_offset, psgl, offset);
  AMAP_SET_BITS(struct amap_iscsi_sge, last_sge, psgl, 0);
  offset += sg_len;
 }
 psgl--;
 AMAP_SET_BITS(struct amap_iscsi_sge, last_sge, psgl, 1);
}

static void
hwi_write_sgl(struct iscsi_wrb *pwrb, struct scatterlist *sg,
       unsigned int num_sg, struct beiscsi_io_task *io_task)
{
 struct iscsi_sge *psgl;
 unsigned int sg_len, index;
 unsigned int sge_len = 0;
 unsigned long long addr;
 struct scatterlist *l_sg;
 unsigned int offset;

 AMAP_SET_BITS(struct amap_iscsi_wrb, iscsi_bhs_addr_lo, pwrb,
          io_task->bhs_pa.u.a32.address_lo);
 AMAP_SET_BITS(struct amap_iscsi_wrb, iscsi_bhs_addr_hi, pwrb,
          io_task->bhs_pa.u.a32.address_hi);

 l_sg = sg;
 for (index = 0; (index < num_sg) && (index < 2); index++,
        sg = sg_next(sg)) {
  if (index == 0) {
   sg_len = sg_dma_len(sg);
   addr = (u64) sg_dma_address(sg);
   AMAP_SET_BITS(struct amap_iscsi_wrb, sge0_addr_lo, pwrb,
      ((u32)(addr & 0xFFFFFFFF)));
   AMAP_SET_BITS(struct amap_iscsi_wrb, sge0_addr_hi, pwrb,
       ((u32)(addr >> 32)));
   AMAP_SET_BITS(struct amap_iscsi_wrb, sge0_len, pwrb,
       sg_len);
   sge_len = sg_len;
  } else {
   AMAP_SET_BITS(struct amap_iscsi_wrb, sge1_r2t_offset,
       pwrb, sge_len);
   sg_len = sg_dma_len(sg);
   addr = (u64) sg_dma_address(sg);
   AMAP_SET_BITS(struct amap_iscsi_wrb, sge1_addr_lo, pwrb,
      ((u32)(addr & 0xFFFFFFFF)));
   AMAP_SET_BITS(struct amap_iscsi_wrb, sge1_addr_hi, pwrb,
       ((u32)(addr >> 32)));
   AMAP_SET_BITS(struct amap_iscsi_wrb, sge1_len, pwrb,
       sg_len);
  }
 }
 psgl = (struct iscsi_sge *)io_task->psgl_handle->pfrag;
 memset(psgl, 0, sizeof(*psgl) * BE2_SGE);

 AMAP_SET_BITS(struct amap_iscsi_sge, len, psgl, io_task->bhs_len - 2);

 AMAP_SET_BITS(struct amap_iscsi_sge, addr_hi, psgl,
   io_task->bhs_pa.u.a32.address_hi);
 AMAP_SET_BITS(struct amap_iscsi_sge, addr_lo, psgl,
   io_task->bhs_pa.u.a32.address_lo);

 if (num_sg == 1) {
  AMAP_SET_BITS(struct amap_iscsi_wrb, sge0_last, pwrb,
        1);
  AMAP_SET_BITS(struct amap_iscsi_wrb, sge1_last, pwrb,
--> --------------------

--> maximum size reached

--> --------------------

Messung V0.5
C=98 H=94 G=95

¤ Dauer der Verarbeitung: 0.22 Sekunden  ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

Beweissystem der NASA

Beweissystem Isabelle

NIST Cobol Testsuite

Cephes Mathematical Library

Wiener Entwicklungsmethode

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.