acf3368ffb
The module.h header was implicitly present everywhere, so files with no explicit include of the module infrastructure would build anyway. We are now removing the implicit include, and so we need to call out the module.h file that we need explicitly. Signed-off-by: Paul Gortmaker <paul.gortmaker@windriver.com>
4484 lines
127 KiB
C
4484 lines
127 KiB
C
/**
|
|
* Copyright (C) 2005 - 2011 Emulex
|
|
* All rights reserved.
|
|
*
|
|
* This program is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU General Public License version 2
|
|
* as published by the Free Software Foundation. The full GNU General
|
|
* Public License is included in this distribution in the file called COPYING.
|
|
*
|
|
* Written by: Jayamohan Kallickal (jayamohan.kallickal@emulex.com)
|
|
*
|
|
* Contact Information:
|
|
* linux-drivers@emulex.com
|
|
*
|
|
* Emulex
|
|
* 3333 Susan Street
|
|
* Costa Mesa, CA 92626
|
|
*/
|
|
|
|
#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 <scsi/libiscsi.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"
|
|
|
|
static unsigned int be_iopoll_budget = 10;
|
|
static unsigned int be_max_phys_size = 64;
|
|
static unsigned int enable_msix = 1;
|
|
static unsigned int gcrashmode = 0;
|
|
static unsigned int num_hba = 0;
|
|
|
|
MODULE_DEVICE_TABLE(pci, beiscsi_pci_id_table);
|
|
MODULE_DESCRIPTION(DRV_DESC " " BUILD_STR);
|
|
MODULE_AUTHOR("ServerEngines 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");
|
|
|
|
static int beiscsi_slave_configure(struct scsi_device *sdev)
|
|
{
|
|
blk_queue_max_segment_size(sdev->request_queue, 65536);
|
|
return 0;
|
|
}
|
|
|
|
static int beiscsi_eh_abort(struct scsi_cmnd *sc)
|
|
{
|
|
struct iscsi_cls_session *cls_session;
|
|
struct iscsi_task *aborted_task = (struct iscsi_task *)sc->SCp.ptr;
|
|
struct beiscsi_io_task *aborted_io_task;
|
|
struct iscsi_conn *conn;
|
|
struct beiscsi_conn *beiscsi_conn;
|
|
struct beiscsi_hba *phba;
|
|
struct iscsi_session *session;
|
|
struct invalidate_command_table *inv_tbl;
|
|
struct be_dma_mem nonemb_cmd;
|
|
unsigned int cid, tag, num_invalidate;
|
|
|
|
cls_session = starget_to_session(scsi_target(sc->device));
|
|
session = cls_session->dd_data;
|
|
|
|
spin_lock_bh(&session->lock);
|
|
if (!aborted_task || !aborted_task->sc) {
|
|
/* we raced */
|
|
spin_unlock_bh(&session->lock);
|
|
return SUCCESS;
|
|
}
|
|
|
|
aborted_io_task = aborted_task->dd_data;
|
|
if (!aborted_io_task->scsi_cmnd) {
|
|
/* raced or invalid command */
|
|
spin_unlock_bh(&session->lock);
|
|
return SUCCESS;
|
|
}
|
|
spin_unlock_bh(&session->lock);
|
|
conn = aborted_task->conn;
|
|
beiscsi_conn = conn->dd_data;
|
|
phba = beiscsi_conn->phba;
|
|
|
|
/* invalidate iocb */
|
|
cid = beiscsi_conn->beiscsi_conn_cid;
|
|
inv_tbl = phba->inv_tbl;
|
|
memset(inv_tbl, 0x0, sizeof(*inv_tbl));
|
|
inv_tbl->cid = cid;
|
|
inv_tbl->icd = aborted_io_task->psgl_handle->sgl_index;
|
|
num_invalidate = 1;
|
|
nonemb_cmd.va = pci_alloc_consistent(phba->ctrl.pdev,
|
|
sizeof(struct invalidate_commands_params_in),
|
|
&nonemb_cmd.dma);
|
|
if (nonemb_cmd.va == NULL) {
|
|
SE_DEBUG(DBG_LVL_1,
|
|
"Failed to allocate memory for"
|
|
"mgmt_invalidate_icds\n");
|
|
return FAILED;
|
|
}
|
|
nonemb_cmd.size = sizeof(struct invalidate_commands_params_in);
|
|
|
|
tag = mgmt_invalidate_icds(phba, inv_tbl, num_invalidate,
|
|
cid, &nonemb_cmd);
|
|
if (!tag) {
|
|
shost_printk(KERN_WARNING, phba->shost,
|
|
"mgmt_invalidate_icds could not be"
|
|
" submitted\n");
|
|
pci_free_consistent(phba->ctrl.pdev, nonemb_cmd.size,
|
|
nonemb_cmd.va, nonemb_cmd.dma);
|
|
|
|
return FAILED;
|
|
} else {
|
|
wait_event_interruptible(phba->ctrl.mcc_wait[tag],
|
|
phba->ctrl.mcc_numtag[tag]);
|
|
free_mcc_tag(&phba->ctrl, tag);
|
|
}
|
|
pci_free_consistent(phba->ctrl.pdev, nonemb_cmd.size,
|
|
nonemb_cmd.va, nonemb_cmd.dma);
|
|
return iscsi_eh_abort(sc);
|
|
}
|
|
|
|
static int beiscsi_eh_device_reset(struct scsi_cmnd *sc)
|
|
{
|
|
struct iscsi_task *abrt_task;
|
|
struct beiscsi_io_task *abrt_io_task;
|
|
struct iscsi_conn *conn;
|
|
struct beiscsi_conn *beiscsi_conn;
|
|
struct beiscsi_hba *phba;
|
|
struct iscsi_session *session;
|
|
struct iscsi_cls_session *cls_session;
|
|
struct invalidate_command_table *inv_tbl;
|
|
struct be_dma_mem nonemb_cmd;
|
|
unsigned int cid, tag, i, num_invalidate;
|
|
int rc = FAILED;
|
|
|
|
/* invalidate iocbs */
|
|
cls_session = starget_to_session(scsi_target(sc->device));
|
|
session = cls_session->dd_data;
|
|
spin_lock_bh(&session->lock);
|
|
if (!session->leadconn || session->state != ISCSI_STATE_LOGGED_IN)
|
|
goto unlock;
|
|
|
|
conn = session->leadconn;
|
|
beiscsi_conn = conn->dd_data;
|
|
phba = beiscsi_conn->phba;
|
|
cid = beiscsi_conn->beiscsi_conn_cid;
|
|
inv_tbl = phba->inv_tbl;
|
|
memset(inv_tbl, 0x0, sizeof(*inv_tbl) * BE2_CMDS_PER_CXN);
|
|
num_invalidate = 0;
|
|
for (i = 0; i < conn->session->cmds_max; i++) {
|
|
abrt_task = conn->session->cmds[i];
|
|
abrt_io_task = abrt_task->dd_data;
|
|
if (!abrt_task->sc || abrt_task->state == ISCSI_TASK_FREE)
|
|
continue;
|
|
|
|
if (abrt_task->sc->device->lun != abrt_task->sc->device->lun)
|
|
continue;
|
|
|
|
inv_tbl->cid = cid;
|
|
inv_tbl->icd = abrt_io_task->psgl_handle->sgl_index;
|
|
num_invalidate++;
|
|
inv_tbl++;
|
|
}
|
|
spin_unlock_bh(&session->lock);
|
|
inv_tbl = phba->inv_tbl;
|
|
|
|
nonemb_cmd.va = pci_alloc_consistent(phba->ctrl.pdev,
|
|
sizeof(struct invalidate_commands_params_in),
|
|
&nonemb_cmd.dma);
|
|
if (nonemb_cmd.va == NULL) {
|
|
SE_DEBUG(DBG_LVL_1,
|
|
"Failed to allocate memory for"
|
|
"mgmt_invalidate_icds\n");
|
|
return FAILED;
|
|
}
|
|
nonemb_cmd.size = sizeof(struct invalidate_commands_params_in);
|
|
memset(nonemb_cmd.va, 0, nonemb_cmd.size);
|
|
tag = mgmt_invalidate_icds(phba, inv_tbl, num_invalidate,
|
|
cid, &nonemb_cmd);
|
|
if (!tag) {
|
|
shost_printk(KERN_WARNING, phba->shost,
|
|
"mgmt_invalidate_icds could not be"
|
|
" submitted\n");
|
|
pci_free_consistent(phba->ctrl.pdev, nonemb_cmd.size,
|
|
nonemb_cmd.va, nonemb_cmd.dma);
|
|
return FAILED;
|
|
} else {
|
|
wait_event_interruptible(phba->ctrl.mcc_wait[tag],
|
|
phba->ctrl.mcc_numtag[tag]);
|
|
free_mcc_tag(&phba->ctrl, tag);
|
|
}
|
|
pci_free_consistent(phba->ctrl.pdev, nonemb_cmd.size,
|
|
nonemb_cmd.va, nonemb_cmd.dma);
|
|
return iscsi_eh_device_reset(sc);
|
|
unlock:
|
|
spin_unlock_bh(&session->lock);
|
|
return rc;
|
|
}
|
|
|
|
static ssize_t beiscsi_show_boot_tgt_info(void *data, int type, char *buf)
|
|
{
|
|
struct beiscsi_hba *phba = data;
|
|
struct mgmt_session_info *boot_sess = &phba->boot_sess;
|
|
struct mgmt_conn_info *boot_conn = &boot_sess->conn_list[0];
|
|
char *str = buf;
|
|
int rc;
|
|
|
|
switch (type) {
|
|
case ISCSI_BOOT_TGT_NAME:
|
|
rc = sprintf(buf, "%.*s\n",
|
|
(int)strlen(boot_sess->target_name),
|
|
(char *)&boot_sess->target_name);
|
|
break;
|
|
case ISCSI_BOOT_TGT_IP_ADDR:
|
|
if (boot_conn->dest_ipaddr.ip_type == 0x1)
|
|
rc = sprintf(buf, "%pI4\n",
|
|
(char *)&boot_conn->dest_ipaddr.ip_address);
|
|
else
|
|
rc = sprintf(str, "%pI6\n",
|
|
(char *)&boot_conn->dest_ipaddr.ip_address);
|
|
break;
|
|
case ISCSI_BOOT_TGT_PORT:
|
|
rc = sprintf(str, "%d\n", boot_conn->dest_port);
|
|
break;
|
|
|
|
case ISCSI_BOOT_TGT_CHAP_NAME:
|
|
rc = sprintf(str, "%.*s\n",
|
|
boot_conn->negotiated_login_options.auth_data.chap.
|
|
target_chap_name_length,
|
|
(char *)&boot_conn->negotiated_login_options.
|
|
auth_data.chap.target_chap_name);
|
|
break;
|
|
case ISCSI_BOOT_TGT_CHAP_SECRET:
|
|
rc = sprintf(str, "%.*s\n",
|
|
boot_conn->negotiated_login_options.auth_data.chap.
|
|
target_secret_length,
|
|
(char *)&boot_conn->negotiated_login_options.
|
|
auth_data.chap.target_secret);
|
|
break;
|
|
case ISCSI_BOOT_TGT_REV_CHAP_NAME:
|
|
rc = sprintf(str, "%.*s\n",
|
|
boot_conn->negotiated_login_options.auth_data.chap.
|
|
intr_chap_name_length,
|
|
(char *)&boot_conn->negotiated_login_options.
|
|
auth_data.chap.intr_chap_name);
|
|
break;
|
|
case ISCSI_BOOT_TGT_REV_CHAP_SECRET:
|
|
rc = sprintf(str, "%.*s\n",
|
|
boot_conn->negotiated_login_options.auth_data.chap.
|
|
intr_secret_length,
|
|
(char *)&boot_conn->negotiated_login_options.
|
|
auth_data.chap.intr_secret);
|
|
break;
|
|
case ISCSI_BOOT_TGT_FLAGS:
|
|
rc = sprintf(str, "2\n");
|
|
break;
|
|
case ISCSI_BOOT_TGT_NIC_ASSOC:
|
|
rc = sprintf(str, "0\n");
|
|
break;
|
|
default:
|
|
rc = -ENOSYS;
|
|
break;
|
|
}
|
|
return rc;
|
|
}
|
|
|
|
static ssize_t beiscsi_show_boot_ini_info(void *data, int type, char *buf)
|
|
{
|
|
struct beiscsi_hba *phba = data;
|
|
char *str = buf;
|
|
int rc;
|
|
|
|
switch (type) {
|
|
case ISCSI_BOOT_INI_INITIATOR_NAME:
|
|
rc = sprintf(str, "%s\n", phba->boot_sess.initiator_iscsiname);
|
|
break;
|
|
default:
|
|
rc = -ENOSYS;
|
|
break;
|
|
}
|
|
return rc;
|
|
}
|
|
|
|
static ssize_t beiscsi_show_boot_eth_info(void *data, int type, char *buf)
|
|
{
|
|
struct beiscsi_hba *phba = data;
|
|
char *str = buf;
|
|
int rc;
|
|
|
|
switch (type) {
|
|
case ISCSI_BOOT_ETH_FLAGS:
|
|
rc = sprintf(str, "2\n");
|
|
break;
|
|
case ISCSI_BOOT_ETH_INDEX:
|
|
rc = sprintf(str, "0\n");
|
|
break;
|
|
case ISCSI_BOOT_ETH_MAC:
|
|
rc = beiscsi_get_macaddr(buf, phba);
|
|
if (rc < 0) {
|
|
SE_DEBUG(DBG_LVL_1, "beiscsi_get_macaddr Failed\n");
|
|
return rc;
|
|
}
|
|
break;
|
|
default:
|
|
rc = -ENOSYS;
|
|
break;
|
|
}
|
|
return rc;
|
|
}
|
|
|
|
|
|
static mode_t beiscsi_tgt_get_attr_visibility(void *data, int type)
|
|
{
|
|
int rc;
|
|
|
|
switch (type) {
|
|
case ISCSI_BOOT_TGT_NAME:
|
|
case ISCSI_BOOT_TGT_IP_ADDR:
|
|
case ISCSI_BOOT_TGT_PORT:
|
|
case ISCSI_BOOT_TGT_CHAP_NAME:
|
|
case ISCSI_BOOT_TGT_CHAP_SECRET:
|
|
case ISCSI_BOOT_TGT_REV_CHAP_NAME:
|
|
case ISCSI_BOOT_TGT_REV_CHAP_SECRET:
|
|
case ISCSI_BOOT_TGT_NIC_ASSOC:
|
|
case ISCSI_BOOT_TGT_FLAGS:
|
|
rc = S_IRUGO;
|
|
break;
|
|
default:
|
|
rc = 0;
|
|
break;
|
|
}
|
|
return rc;
|
|
}
|
|
|
|
static mode_t beiscsi_ini_get_attr_visibility(void *data, int type)
|
|
{
|
|
int rc;
|
|
|
|
switch (type) {
|
|
case ISCSI_BOOT_INI_INITIATOR_NAME:
|
|
rc = S_IRUGO;
|
|
break;
|
|
default:
|
|
rc = 0;
|
|
break;
|
|
}
|
|
return rc;
|
|
}
|
|
|
|
|
|
static mode_t beiscsi_eth_get_attr_visibility(void *data, int type)
|
|
{
|
|
int rc;
|
|
|
|
switch (type) {
|
|
case ISCSI_BOOT_ETH_FLAGS:
|
|
case ISCSI_BOOT_ETH_MAC:
|
|
case ISCSI_BOOT_ETH_INDEX:
|
|
rc = S_IRUGO;
|
|
break;
|
|
default:
|
|
rc = 0;
|
|
break;
|
|
}
|
|
return rc;
|
|
}
|
|
|
|
/*------------------- PCI Driver operations and data ----------------- */
|
|
static DEFINE_PCI_DEVICE_TABLE(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) },
|
|
{ 0 }
|
|
};
|
|
MODULE_DEVICE_TABLE(pci, beiscsi_pci_id_table);
|
|
|
|
static struct scsi_host_template beiscsi_sht = {
|
|
.module = THIS_MODULE,
|
|
.name = "ServerEngines 10Gbe open-iscsi Initiator Driver",
|
|
.proc_name = DRV_NAME,
|
|
.queuecommand = iscsi_queuecommand,
|
|
.change_queue_depth = iscsi_change_queue_depth,
|
|
.slave_configure = beiscsi_slave_configure,
|
|
.target_alloc = iscsi_target_alloc,
|
|
.eh_abort_handler = beiscsi_eh_abort,
|
|
.eh_device_reset_handler = beiscsi_eh_device_reset,
|
|
.eh_target_reset_handler = iscsi_eh_session_reset,
|
|
.sg_tablesize = BEISCSI_SGLIST_ELEMENTS,
|
|
.can_queue = BE2_IO_DEPTH,
|
|
.this_id = -1,
|
|
.max_sectors = BEISCSI_MAX_SECTORS,
|
|
.cmd_per_lun = BEISCSI_CMD_PER_LUN,
|
|
.use_clustering = ENABLE_CLUSTERING,
|
|
};
|
|
|
|
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->dma_boundary = pcidev->dma_mask;
|
|
shost->max_id = BE2_MAX_SESSIONS;
|
|
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);
|
|
|
|
if (iscsi_host_add(shost, &phba->pcidev->dev))
|
|
goto free_devices;
|
|
|
|
return phba;
|
|
|
|
free_devices:
|
|
pci_dev_put(phba->pcidev);
|
|
iscsi_host_free(phba->shost);
|
|
return NULL;
|
|
}
|
|
|
|
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_nocache(pci_resource_start(pcidev, 2),
|
|
pci_resource_len(pcidev, 2));
|
|
if (addr == NULL)
|
|
return -ENOMEM;
|
|
phba->ctrl.csr = addr;
|
|
phba->csr_va = addr;
|
|
phba->csr_pa.u.a64.address = pci_resource_start(pcidev, 2);
|
|
|
|
addr = ioremap_nocache(pci_resource_start(pcidev, 4), 128 * 1024);
|
|
if (addr == NULL)
|
|
goto pci_map_err;
|
|
phba->ctrl.db = addr;
|
|
phba->db_va = addr;
|
|
phba->db_pa.u.a64.address = pci_resource_start(pcidev, 4);
|
|
|
|
if (phba->generation == BE_GEN2)
|
|
pcicfg_reg = 1;
|
|
else
|
|
pcicfg_reg = 0;
|
|
|
|
addr = ioremap_nocache(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;
|
|
phba->pci_pa.u.a64.address = pci_resource_start(pcidev, pcicfg_reg);
|
|
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. Returning -ENODEV\n");
|
|
return ret;
|
|
}
|
|
|
|
pci_set_master(pcidev);
|
|
if (pci_set_consistent_dma_mask(pcidev, DMA_BIT_MASK(64))) {
|
|
ret = pci_set_consistent_dma_mask(pcidev, DMA_BIT_MASK(32));
|
|
if (ret) {
|
|
dev_err(&pcidev->dev, "Could not set PCI DMA Mask\n");
|
|
pci_disable_device(pcidev);
|
|
return ret;
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
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 = pci_alloc_consistent(pdev,
|
|
mbox_mem_alloc->size,
|
|
&mbox_mem_alloc->dma);
|
|
if (!mbox_mem_alloc->va) {
|
|
beiscsi_unmap_pci_function(phba);
|
|
status = -ENOMEM;
|
|
return status;
|
|
}
|
|
|
|
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));
|
|
spin_lock_init(&ctrl->mbox_lock);
|
|
spin_lock_init(&phba->ctrl.mcc_lock);
|
|
spin_lock_init(&phba->ctrl.mcc_cq_lock);
|
|
|
|
return status;
|
|
}
|
|
|
|
static void beiscsi_get_params(struct beiscsi_hba *phba)
|
|
{
|
|
phba->params.ios_per_ctrl = (phba->fw_config.iscsi_icd_count
|
|
- (phba->fw_config.iscsi_cid_count
|
|
+ BE2_TMFS
|
|
+ BE2_NOPOUT_REQ));
|
|
phba->params.cxns_per_ctrl = phba->fw_config.iscsi_cid_count;
|
|
phba->params.asyncpdus_per_ctrl = phba->fw_config.iscsi_cid_count * 2;
|
|
phba->params.icds_per_ctrl = phba->fw_config.iscsi_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.eq_timer = 64;
|
|
phba->params.num_eq_entries =
|
|
(((BE2_CMDS_PER_CXN * 2 + phba->fw_config.iscsi_cid_count * 2
|
|
+ BE2_TMFS) / 512) + 1) * 512;
|
|
phba->params.num_eq_entries = (phba->params.num_eq_entries < 1024)
|
|
? 1024 : phba->params.num_eq_entries;
|
|
SE_DEBUG(DBG_LVL_8, "phba->params.num_eq_entries=%d\n",
|
|
phba->params.num_eq_entries);
|
|
phba->params.num_cq_entries =
|
|
(((BE2_CMDS_PER_CXN * 2 + phba->fw_config.iscsi_cid_count * 2
|
|
+ BE2_TMFS) / 512) + 1) * 512;
|
|
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;
|
|
val |= id & DB_EQ_RING_ID_MASK;
|
|
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;
|
|
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 = NULL;
|
|
struct be_queue_info *eq;
|
|
struct be_queue_info *mcc;
|
|
unsigned int num_eq_processed;
|
|
struct be_eq_obj *pbe_eq;
|
|
unsigned long flags;
|
|
|
|
pbe_eq = dev_id;
|
|
eq = &pbe_eq->q;
|
|
phba = pbe_eq->phba;
|
|
mcc = &phba->ctrl.mcc_obj.cq;
|
|
eqe = queue_tail_node(eq);
|
|
if (!eqe)
|
|
SE_DEBUG(DBG_LVL_1, "eqe is NULL\n");
|
|
|
|
num_eq_processed = 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) {
|
|
spin_lock_irqsave(&phba->isr_lock, flags);
|
|
phba->todo_mcc_cq = 1;
|
|
spin_unlock_irqrestore(&phba->isr_lock, flags);
|
|
}
|
|
AMAP_SET_BITS(struct amap_eq_entry, valid, eqe, 0);
|
|
queue_tail_inc(eq);
|
|
eqe = queue_tail_node(eq);
|
|
num_eq_processed++;
|
|
}
|
|
if (phba->todo_mcc_cq)
|
|
queue_work(phba->wq, &phba->work_cqs);
|
|
if (num_eq_processed)
|
|
hwi_ring_eq_db(phba, eq->id, 1, num_eq_processed, 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_eq_entry *eqe = NULL;
|
|
struct be_queue_info *eq;
|
|
struct be_queue_info *cq;
|
|
unsigned int num_eq_processed;
|
|
struct be_eq_obj *pbe_eq;
|
|
unsigned long flags;
|
|
|
|
pbe_eq = dev_id;
|
|
eq = &pbe_eq->q;
|
|
cq = pbe_eq->cq;
|
|
eqe = queue_tail_node(eq);
|
|
if (!eqe)
|
|
SE_DEBUG(DBG_LVL_1, "eqe is NULL\n");
|
|
|
|
phba = pbe_eq->phba;
|
|
num_eq_processed = 0;
|
|
if (blk_iopoll_enabled) {
|
|
while (eqe->dw[offsetof(struct amap_eq_entry, valid) / 32]
|
|
& EQE_VALID_MASK) {
|
|
if (!blk_iopoll_sched_prep(&pbe_eq->iopoll))
|
|
blk_iopoll_sched(&pbe_eq->iopoll);
|
|
|
|
AMAP_SET_BITS(struct amap_eq_entry, valid, eqe, 0);
|
|
queue_tail_inc(eq);
|
|
eqe = queue_tail_node(eq);
|
|
num_eq_processed++;
|
|
}
|
|
if (num_eq_processed)
|
|
hwi_ring_eq_db(phba, eq->id, 1, num_eq_processed, 0, 1);
|
|
|
|
return IRQ_HANDLED;
|
|
} else {
|
|
while (eqe->dw[offsetof(struct amap_eq_entry, valid) / 32]
|
|
& EQE_VALID_MASK) {
|
|
spin_lock_irqsave(&phba->isr_lock, flags);
|
|
phba->todo_cq = 1;
|
|
spin_unlock_irqrestore(&phba->isr_lock, flags);
|
|
AMAP_SET_BITS(struct amap_eq_entry, valid, eqe, 0);
|
|
queue_tail_inc(eq);
|
|
eqe = queue_tail_node(eq);
|
|
num_eq_processed++;
|
|
}
|
|
if (phba->todo_cq)
|
|
queue_work(phba->wq, &phba->work_cqs);
|
|
|
|
if (num_eq_processed)
|
|
hwi_ring_eq_db(phba, eq->id, 1, num_eq_processed, 1, 1);
|
|
|
|
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 = NULL;
|
|
struct be_queue_info *eq;
|
|
struct be_queue_info *cq;
|
|
struct be_queue_info *mcc;
|
|
unsigned long flags, index;
|
|
unsigned int num_mcceq_processed, num_ioeq_processed;
|
|
struct be_ctrl_info *ctrl;
|
|
struct be_eq_obj *pbe_eq;
|
|
int isr;
|
|
|
|
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;
|
|
index = 0;
|
|
eqe = queue_tail_node(eq);
|
|
if (!eqe)
|
|
SE_DEBUG(DBG_LVL_1, "eqe is NULL\n");
|
|
|
|
num_ioeq_processed = 0;
|
|
num_mcceq_processed = 0;
|
|
if (blk_iopoll_enabled) {
|
|
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) {
|
|
spin_lock_irqsave(&phba->isr_lock, flags);
|
|
phba->todo_mcc_cq = 1;
|
|
spin_unlock_irqrestore(&phba->isr_lock, flags);
|
|
num_mcceq_processed++;
|
|
} else {
|
|
if (!blk_iopoll_sched_prep(&pbe_eq->iopoll))
|
|
blk_iopoll_sched(&pbe_eq->iopoll);
|
|
num_ioeq_processed++;
|
|
}
|
|
AMAP_SET_BITS(struct amap_eq_entry, valid, eqe, 0);
|
|
queue_tail_inc(eq);
|
|
eqe = queue_tail_node(eq);
|
|
}
|
|
if (num_ioeq_processed || num_mcceq_processed) {
|
|
if (phba->todo_mcc_cq)
|
|
queue_work(phba->wq, &phba->work_cqs);
|
|
|
|
if ((num_mcceq_processed) && (!num_ioeq_processed))
|
|
hwi_ring_eq_db(phba, eq->id, 0,
|
|
(num_ioeq_processed +
|
|
num_mcceq_processed) , 1, 1);
|
|
else
|
|
hwi_ring_eq_db(phba, eq->id, 0,
|
|
(num_ioeq_processed +
|
|
num_mcceq_processed), 0, 1);
|
|
|
|
return IRQ_HANDLED;
|
|
} else
|
|
return IRQ_NONE;
|
|
} else {
|
|
cq = &phwi_context->be_cq[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) != cq->id) {
|
|
spin_lock_irqsave(&phba->isr_lock, flags);
|
|
phba->todo_mcc_cq = 1;
|
|
spin_unlock_irqrestore(&phba->isr_lock, flags);
|
|
} else {
|
|
spin_lock_irqsave(&phba->isr_lock, flags);
|
|
phba->todo_cq = 1;
|
|
spin_unlock_irqrestore(&phba->isr_lock, flags);
|
|
}
|
|
AMAP_SET_BITS(struct amap_eq_entry, valid, eqe, 0);
|
|
queue_tail_inc(eq);
|
|
eqe = queue_tail_node(eq);
|
|
num_ioeq_processed++;
|
|
}
|
|
if (phba->todo_cq || phba->todo_mcc_cq)
|
|
queue_work(phba->wq, &phba->work_cqs);
|
|
|
|
if (num_ioeq_processed) {
|
|
hwi_ring_eq_db(phba, eq->id, 0,
|
|
num_ioeq_processed, 1, 1);
|
|
return IRQ_HANDLED;
|
|
} else
|
|
return IRQ_NONE;
|
|
}
|
|
}
|
|
|
|
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, msix_vec, i, j;
|
|
|
|
phwi_ctrlr = phba->phwi_ctrlr;
|
|
phwi_context = phwi_ctrlr->phwi_ctxt;
|
|
|
|
if (phba->msix_enabled) {
|
|
for (i = 0; i < phba->num_cpus; i++) {
|
|
phba->msi_name[i] = kzalloc(BEISCSI_MSI_NAME,
|
|
GFP_KERNEL);
|
|
if (!phba->msi_name[i]) {
|
|
ret = -ENOMEM;
|
|
goto free_msix_irqs;
|
|
}
|
|
|
|
sprintf(phba->msi_name[i], "beiscsi_%02x_%02x",
|
|
phba->shost->host_no, i);
|
|
msix_vec = phba->msix_entries[i].vector;
|
|
ret = request_irq(msix_vec, be_isr_msix, 0,
|
|
phba->msi_name[i],
|
|
&phwi_context->be_eq[i]);
|
|
if (ret) {
|
|
shost_printk(KERN_ERR, phba->shost,
|
|
"beiscsi_init_irqs-Failed to"
|
|
"register msix for i = %d\n", i);
|
|
kfree(phba->msi_name[i]);
|
|
goto free_msix_irqs;
|
|
}
|
|
}
|
|
phba->msi_name[i] = kzalloc(BEISCSI_MSI_NAME, GFP_KERNEL);
|
|
if (!phba->msi_name[i]) {
|
|
ret = -ENOMEM;
|
|
goto free_msix_irqs;
|
|
}
|
|
sprintf(phba->msi_name[i], "beiscsi_mcc_%02x",
|
|
phba->shost->host_no);
|
|
msix_vec = phba->msix_entries[i].vector;
|
|
ret = request_irq(msix_vec, be_isr_mcc, 0, phba->msi_name[i],
|
|
&phwi_context->be_eq[i]);
|
|
if (ret) {
|
|
shost_printk(KERN_ERR, phba->shost, "beiscsi_init_irqs-"
|
|
"Failed to register beiscsi_msix_mcc\n");
|
|
kfree(phba->msi_name[i]);
|
|
goto free_msix_irqs;
|
|
}
|
|
|
|
} else {
|
|
ret = request_irq(pcidev->irq, be_isr, IRQF_SHARED,
|
|
"beiscsi", phba);
|
|
if (ret) {
|
|
shost_printk(KERN_ERR, phba->shost, "beiscsi_init_irqs-"
|
|
"Failed to register irq\\n");
|
|
return ret;
|
|
}
|
|
}
|
|
return 0;
|
|
free_msix_irqs:
|
|
for (j = i - 1; j >= 0; j--) {
|
|
kfree(phba->msi_name[j]);
|
|
msix_vec = phba->msix_entries[j].vector;
|
|
free_irq(msix_vec, &phwi_context->be_eq[j]);
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
static void hwi_ring_cq_db(struct beiscsi_hba *phba,
|
|
unsigned int id, unsigned int num_processed,
|
|
unsigned char rearm, unsigned char event)
|
|
{
|
|
u32 val = 0;
|
|
val |= id & DB_CQ_RING_ID_MASK;
|
|
if (rearm)
|
|
val |= 1 << DB_CQ_REARM_SHIFT;
|
|
val |= num_processed << DB_CQ_NUM_POPPED_SHIFT;
|
|
iowrite32(val, phba->db_va + DB_CQ_OFFSET);
|
|
}
|
|
|
|
static unsigned int
|
|
beiscsi_process_async_pdu(struct beiscsi_conn *beiscsi_conn,
|
|
struct beiscsi_hba *phba,
|
|
unsigned short cid,
|
|
struct pdu_base *ppdu,
|
|
unsigned long pdu_len,
|
|
void *pbuffer, unsigned long buf_len)
|
|
{
|
|
struct iscsi_conn *conn = beiscsi_conn->conn;
|
|
struct iscsi_session *session = conn->session;
|
|
struct iscsi_task *task;
|
|
struct beiscsi_io_task *io_task;
|
|
struct iscsi_hdr *login_hdr;
|
|
|
|
switch (ppdu->dw[offsetof(struct amap_pdu_base, opcode) / 32] &
|
|
PDUBASE_OPCODE_MASK) {
|
|
case ISCSI_OP_NOOP_IN:
|
|
pbuffer = NULL;
|
|
buf_len = 0;
|
|
break;
|
|
case ISCSI_OP_ASYNC_EVENT:
|
|
break;
|
|
case ISCSI_OP_REJECT:
|
|
WARN_ON(!pbuffer);
|
|
WARN_ON(!(buf_len == 48));
|
|
SE_DEBUG(DBG_LVL_1, "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 *)ppdu;
|
|
login_hdr->itt = io_task->libiscsi_itt;
|
|
break;
|
|
default:
|
|
shost_printk(KERN_WARNING, phba->shost,
|
|
"Unrecognized opcode 0x%x in async msg\n",
|
|
(ppdu->
|
|
dw[offsetof(struct amap_pdu_base, opcode) / 32]
|
|
& PDUBASE_OPCODE_MASK));
|
|
return 1;
|
|
}
|
|
|
|
spin_lock_bh(&session->lock);
|
|
__iscsi_complete_pdu(conn, (struct iscsi_hdr *)ppdu, pbuffer, buf_len);
|
|
spin_unlock_bh(&session->lock);
|
|
return 0;
|
|
}
|
|
|
|
static struct sgl_handle *alloc_io_sgl_handle(struct beiscsi_hba *phba)
|
|
{
|
|
struct sgl_handle *psgl_handle;
|
|
|
|
if (phba->io_sgl_hndl_avbl) {
|
|
SE_DEBUG(DBG_LVL_8,
|
|
"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;
|
|
return psgl_handle;
|
|
}
|
|
|
|
static void
|
|
free_io_sgl_handle(struct beiscsi_hba *phba, struct sgl_handle *psgl_handle)
|
|
{
|
|
SE_DEBUG(DBG_LVL_8, "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.
|
|
*/
|
|
SE_DEBUG(DBG_LVL_8,
|
|
"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]);
|
|
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++;
|
|
}
|
|
|
|
/**
|
|
* alloc_wrb_handle - To allocate a wrb handle
|
|
* @phba: The hba pointer
|
|
* @cid: The cid to use for allocation
|
|
*
|
|
* 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 *pwrb_context;
|
|
struct hwi_controller *phwi_ctrlr;
|
|
struct wrb_handle *pwrb_handle, *pwrb_handle_tmp;
|
|
|
|
phwi_ctrlr = phba->phwi_ctrlr;
|
|
pwrb_context = &phwi_ctrlr->wrb_context[cid];
|
|
if (pwrb_context->wrb_handles_available >= 2) {
|
|
pwrb_handle = pwrb_context->pwrb_handle_base[
|
|
pwrb_context->alloc_index];
|
|
pwrb_context->wrb_handles_available--;
|
|
if (pwrb_context->alloc_index ==
|
|
(phba->params.wrbs_per_cxn - 1))
|
|
pwrb_context->alloc_index = 0;
|
|
else
|
|
pwrb_context->alloc_index++;
|
|
pwrb_handle_tmp = pwrb_context->pwrb_handle_base[
|
|
pwrb_context->alloc_index];
|
|
pwrb_handle->nxt_wrb_index = pwrb_handle_tmp->wrb_index;
|
|
} else
|
|
pwrb_handle = NULL;
|
|
return pwrb_handle;
|
|
}
|
|
|
|
/**
|
|
* 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)
|
|
{
|
|
pwrb_context->pwrb_handle_base[pwrb_context->free_index] = pwrb_handle;
|
|
pwrb_context->wrb_handles_available++;
|
|
if (pwrb_context->free_index == (phba->params.wrbs_per_cxn - 1))
|
|
pwrb_context->free_index = 0;
|
|
else
|
|
pwrb_context->free_index++;
|
|
|
|
SE_DEBUG(DBG_LVL_8,
|
|
"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;
|
|
|
|
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;
|
|
SE_DEBUG(DBG_LVL_8, "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;
|
|
return psgl_handle;
|
|
}
|
|
|
|
void
|
|
free_mgmt_sgl_handle(struct beiscsi_hba *phba, struct sgl_handle *psgl_handle)
|
|
{
|
|
|
|
SE_DEBUG(DBG_LVL_8, "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.
|
|
*/
|
|
SE_DEBUG(DBG_LVL_8,
|
|
"Double Free in eh SGL ,eh_sgl_free_index=%d\n",
|
|
phba->eh_sgl_free_index);
|
|
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++;
|
|
}
|
|
|
|
static void
|
|
be_complete_io(struct beiscsi_conn *beiscsi_conn,
|
|
struct iscsi_task *task, struct sol_cqe *psol)
|
|
{
|
|
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 int sense_len;
|
|
unsigned char *sense;
|
|
u32 resid = 0, exp_cmdsn, max_cmdsn;
|
|
u8 rsp, status, flags;
|
|
|
|
exp_cmdsn = (psol->
|
|
dw[offsetof(struct amap_sol_cqe, i_exp_cmd_sn) / 32]
|
|
& SOL_EXP_CMD_SN_MASK);
|
|
max_cmdsn = ((psol->
|
|
dw[offsetof(struct amap_sol_cqe, i_exp_cmd_sn) / 32]
|
|
& SOL_EXP_CMD_SN_MASK) +
|
|
((psol->dw[offsetof(struct amap_sol_cqe, i_cmd_wnd)
|
|
/ 32] & SOL_CMD_WND_MASK) >> 24) - 1);
|
|
rsp = ((psol->dw[offsetof(struct amap_sol_cqe, i_resp) / 32]
|
|
& SOL_RESP_MASK) >> 16);
|
|
status = ((psol->dw[offsetof(struct amap_sol_cqe, i_sts) / 32]
|
|
& SOL_STS_MASK) >> 8);
|
|
flags = ((psol->dw[offsetof(struct amap_sol_cqe, i_flags) / 32]
|
|
& SOL_FLAGS_MASK) >> 24) | 0x80;
|
|
if (!task->sc) {
|
|
if (io_task->scsi_cmnd)
|
|
scsi_dma_unmap(io_task->scsi_cmnd);
|
|
|
|
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)) {
|
|
resid = (psol->dw[offsetof(struct amap_sol_cqe, i_res_cnt) /
|
|
32] & SOL_RES_CNT_MASK);
|
|
|
|
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) {
|
|
unsigned short *slen = (unsigned short *)sts_bhs->sense_info;
|
|
sense = sts_bhs->sense_info + sizeof(unsigned short);
|
|
sense_len = cpu_to_be16(*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) {
|
|
if (psol->dw[offsetof(struct amap_sol_cqe, i_res_cnt) / 32]
|
|
& SOL_RES_CNT_MASK)
|
|
conn->rxdata_octets += (psol->
|
|
dw[offsetof(struct amap_sol_cqe, i_res_cnt) / 32]
|
|
& SOL_RES_CNT_MASK);
|
|
}
|
|
unmap:
|
|
scsi_dma_unmap(io_task->scsi_cmnd);
|
|
iscsi_complete_scsi_task(task, exp_cmdsn, max_cmdsn);
|
|
}
|
|
|
|
static void
|
|
be_complete_logout(struct beiscsi_conn *beiscsi_conn,
|
|
struct iscsi_task *task, struct sol_cqe *psol)
|
|
{
|
|
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 = ((psol->dw[offsetof(struct amap_sol_cqe, i_flags) / 32]
|
|
& SOL_FLAGS_MASK) >> 24) | 0x80;
|
|
hdr->response = (psol->dw[offsetof(struct amap_sol_cqe, i_resp) /
|
|
32] & SOL_RESP_MASK);
|
|
hdr->exp_cmdsn = cpu_to_be32(psol->
|
|
dw[offsetof(struct amap_sol_cqe, i_exp_cmd_sn) / 32]
|
|
& SOL_EXP_CMD_SN_MASK);
|
|
hdr->max_cmdsn = be32_to_cpu((psol->
|
|
dw[offsetof(struct amap_sol_cqe, i_exp_cmd_sn) / 32]
|
|
& SOL_EXP_CMD_SN_MASK) +
|
|
((psol->dw[offsetof(struct amap_sol_cqe, i_cmd_wnd)
|
|
/ 32] & SOL_CMD_WND_MASK) >> 24) - 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 sol_cqe *psol)
|
|
{
|
|
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 = ((psol->dw[offsetof(struct amap_sol_cqe, i_flags) / 32]
|
|
& SOL_FLAGS_MASK) >> 24) | 0x80;
|
|
hdr->response = (psol->dw[offsetof(struct amap_sol_cqe, i_resp) /
|
|
32] & SOL_RESP_MASK);
|
|
hdr->exp_cmdsn = cpu_to_be32(psol->dw[offsetof(struct amap_sol_cqe,
|
|
i_exp_cmd_sn) / 32] & SOL_EXP_CMD_SN_MASK);
|
|
hdr->max_cmdsn = be32_to_cpu((psol->dw[offsetof(struct amap_sol_cqe,
|
|
i_exp_cmd_sn) / 32] & SOL_EXP_CMD_SN_MASK) +
|
|
((psol->dw[offsetof(struct amap_sol_cqe, i_cmd_wnd)
|
|
/ 32] & SOL_CMD_WND_MASK) >> 24) - 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;
|
|
struct wrb_handle *pwrb_handle = NULL;
|
|
struct hwi_controller *phwi_ctrlr;
|
|
struct iscsi_task *task;
|
|
struct beiscsi_io_task *io_task;
|
|
struct iscsi_conn *conn = beiscsi_conn->conn;
|
|
struct iscsi_session *session = conn->session;
|
|
|
|
phwi_ctrlr = phba->phwi_ctrlr;
|
|
pwrb_context = &phwi_ctrlr->wrb_context[((psol->
|
|
dw[offsetof(struct amap_sol_cqe, cid) / 32] &
|
|
SOL_CID_MASK) >> 6) -
|
|
phba->fw_config.iscsi_cid_start];
|
|
pwrb_handle = pwrb_context->pwrb_handle_basestd[((psol->
|
|
dw[offsetof(struct amap_sol_cqe, wrb_index) /
|
|
32] & SOL_WRB_INDEX_MASK) >> 16)];
|
|
task = pwrb_handle->pio_handle;
|
|
|
|
io_task = task->dd_data;
|
|
spin_lock(&phba->mgmt_sgl_lock);
|
|
free_mgmt_sgl_handle(phba, io_task->psgl_handle);
|
|
spin_unlock(&phba->mgmt_sgl_lock);
|
|
spin_lock_bh(&session->lock);
|
|
free_wrb_handle(phba, pwrb_context, pwrb_handle);
|
|
spin_unlock_bh(&session->lock);
|
|
}
|
|
|
|
static void
|
|
be_complete_nopin_resp(struct beiscsi_conn *beiscsi_conn,
|
|
struct iscsi_task *task, struct sol_cqe *psol)
|
|
{
|
|
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 = ((psol->dw[offsetof(struct amap_sol_cqe, i_flags) / 32]
|
|
& SOL_FLAGS_MASK) >> 24) | 0x80;
|
|
hdr->exp_cmdsn = cpu_to_be32(psol->dw[offsetof(struct amap_sol_cqe,
|
|
i_exp_cmd_sn) / 32] & SOL_EXP_CMD_SN_MASK);
|
|
hdr->max_cmdsn = be32_to_cpu((psol->dw[offsetof(struct amap_sol_cqe,
|
|
i_exp_cmd_sn) / 32] & SOL_EXP_CMD_SN_MASK) +
|
|
((psol->dw[offsetof(struct amap_sol_cqe, i_cmd_wnd)
|
|
/ 32] & SOL_CMD_WND_MASK) >> 24) - 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 hwi_complete_cmd(struct beiscsi_conn *beiscsi_conn,
|
|
struct beiscsi_hba *phba, struct sol_cqe *psol)
|
|
{
|
|
struct hwi_wrb_context *pwrb_context;
|
|
struct wrb_handle *pwrb_handle;
|
|
struct iscsi_wrb *pwrb = NULL;
|
|
struct hwi_controller *phwi_ctrlr;
|
|
struct iscsi_task *task;
|
|
unsigned int type;
|
|
struct iscsi_conn *conn = beiscsi_conn->conn;
|
|
struct iscsi_session *session = conn->session;
|
|
|
|
phwi_ctrlr = phba->phwi_ctrlr;
|
|
pwrb_context = &phwi_ctrlr->wrb_context[((psol->dw[offsetof
|
|
(struct amap_sol_cqe, cid) / 32]
|
|
& SOL_CID_MASK) >> 6) -
|
|
phba->fw_config.iscsi_cid_start];
|
|
pwrb_handle = pwrb_context->pwrb_handle_basestd[((psol->
|
|
dw[offsetof(struct amap_sol_cqe, wrb_index) /
|
|
32] & SOL_WRB_INDEX_MASK) >> 16)];
|
|
task = pwrb_handle->pio_handle;
|
|
pwrb = pwrb_handle->pwrb;
|
|
type = (pwrb->dw[offsetof(struct amap_iscsi_wrb, type) / 32] &
|
|
WRB_TYPE_MASK) >> 28;
|
|
|
|
spin_lock_bh(&session->lock);
|
|
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, psol);
|
|
else
|
|
be_complete_io(beiscsi_conn, task, psol);
|
|
break;
|
|
|
|
case HWH_TYPE_LOGOUT:
|
|
if ((task->hdr->opcode & ISCSI_OPCODE_MASK) == ISCSI_OP_LOGOUT)
|
|
be_complete_logout(beiscsi_conn, task, psol);
|
|
else
|
|
be_complete_tmf(beiscsi_conn, task, psol);
|
|
|
|
break;
|
|
|
|
case HWH_TYPE_LOGIN:
|
|
SE_DEBUG(DBG_LVL_1,
|
|
"\t\t No HWH_TYPE_LOGIN Expected in hwi_complete_cmd"
|
|
"- Solicited path\n");
|
|
break;
|
|
|
|
case HWH_TYPE_NOP:
|
|
be_complete_nopin_resp(beiscsi_conn, task, psol);
|
|
break;
|
|
|
|
default:
|
|
shost_printk(KERN_WARNING, phba->shost,
|
|
"In hwi_complete_cmd, unknown type = %d"
|
|
"wrb_index 0x%x CID 0x%x\n", type,
|
|
((psol->dw[offsetof(struct amap_iscsi_wrb,
|
|
type) / 32] & SOL_WRB_INDEX_MASK) >> 16),
|
|
((psol->dw[offsetof(struct amap_sol_cqe,
|
|
cid) / 32] & SOL_CID_MASK) >> 6));
|
|
break;
|
|
}
|
|
|
|
spin_unlock_bh(&session->lock);
|
|
}
|
|
|
|
static struct list_head *hwi_get_async_busy_list(struct hwi_async_pdu_context
|
|
*pasync_ctx, unsigned int is_header,
|
|
unsigned int host_write_ptr)
|
|
{
|
|
if (is_header)
|
|
return &pasync_ctx->async_entry[host_write_ptr].
|
|
header_busy_list;
|
|
else
|
|
return &pasync_ctx->async_entry[host_write_ptr].data_busy_list;
|
|
}
|
|
|
|
static struct async_pdu_handle *
|
|
hwi_get_async_handle(struct beiscsi_hba *phba,
|
|
struct beiscsi_conn *beiscsi_conn,
|
|
struct hwi_async_pdu_context *pasync_ctx,
|
|
struct i_t_dpdu_cqe *pdpdu_cqe, unsigned int *pcq_index)
|
|
{
|
|
struct be_bus_address phys_addr;
|
|
struct list_head *pbusy_list;
|
|
struct async_pdu_handle *pasync_handle = NULL;
|
|
int buffer_len = 0;
|
|
unsigned char buffer_index = -1;
|
|
unsigned char is_header = 0;
|
|
|
|
phys_addr.u.a32.address_lo =
|
|
pdpdu_cqe->dw[offsetof(struct amap_i_t_dpdu_cqe, db_addr_lo) / 32] -
|
|
((pdpdu_cqe->dw[offsetof(struct amap_i_t_dpdu_cqe, dpl) / 32]
|
|
& PDUCQE_DPL_MASK) >> 16);
|
|
phys_addr.u.a32.address_hi =
|
|
pdpdu_cqe->dw[offsetof(struct amap_i_t_dpdu_cqe, db_addr_hi) / 32];
|
|
|
|
phys_addr.u.a64.address =
|
|
*((unsigned long long *)(&phys_addr.u.a64.address));
|
|
|
|
switch (pdpdu_cqe->dw[offsetof(struct amap_i_t_dpdu_cqe, code) / 32]
|
|
& PDUCQE_CODE_MASK) {
|
|
case UNSOL_HDR_NOTIFY:
|
|
is_header = 1;
|
|
|
|
pbusy_list = hwi_get_async_busy_list(pasync_ctx, 1,
|
|
(pdpdu_cqe->dw[offsetof(struct amap_i_t_dpdu_cqe,
|
|
index) / 32] & PDUCQE_INDEX_MASK));
|
|
|
|
buffer_len = (unsigned int)(phys_addr.u.a64.address -
|
|
pasync_ctx->async_header.pa_base.u.a64.address);
|
|
|
|
buffer_index = buffer_len /
|
|
pasync_ctx->async_header.buffer_size;
|
|
|
|
break;
|
|
case UNSOL_DATA_NOTIFY:
|
|
pbusy_list = hwi_get_async_busy_list(pasync_ctx, 0, (pdpdu_cqe->
|
|
dw[offsetof(struct amap_i_t_dpdu_cqe,
|
|
index) / 32] & PDUCQE_INDEX_MASK));
|
|
buffer_len = (unsigned long)(phys_addr.u.a64.address -
|
|
pasync_ctx->async_data.pa_base.u.
|
|
a64.address);
|
|
buffer_index = buffer_len / pasync_ctx->async_data.buffer_size;
|
|
break;
|
|
default:
|
|
pbusy_list = NULL;
|
|
shost_printk(KERN_WARNING, phba->shost,
|
|
"Unexpected code=%d\n",
|
|
pdpdu_cqe->dw[offsetof(struct amap_i_t_dpdu_cqe,
|
|
code) / 32] & PDUCQE_CODE_MASK);
|
|
return NULL;
|
|
}
|
|
|
|
WARN_ON(!(buffer_index <= pasync_ctx->async_data.num_entries));
|
|
WARN_ON(list_empty(pbusy_list));
|
|
list_for_each_entry(pasync_handle, pbusy_list, link) {
|
|
WARN_ON(pasync_handle->consumed);
|
|
if (pasync_handle->index == buffer_index)
|
|
break;
|
|
}
|
|
|
|
WARN_ON(!pasync_handle);
|
|
|
|
pasync_handle->cri = (unsigned short)beiscsi_conn->beiscsi_conn_cid -
|
|
phba->fw_config.iscsi_cid_start;
|
|
pasync_handle->is_header = is_header;
|
|
pasync_handle->buffer_len = ((pdpdu_cqe->
|
|
dw[offsetof(struct amap_i_t_dpdu_cqe, dpl) / 32]
|
|
& PDUCQE_DPL_MASK) >> 16);
|
|
|
|
*pcq_index = (pdpdu_cqe->dw[offsetof(struct amap_i_t_dpdu_cqe,
|
|
index) / 32] & PDUCQE_INDEX_MASK);
|
|
return pasync_handle;
|
|
}
|
|
|
|
static unsigned int
|
|
hwi_update_async_writables(struct hwi_async_pdu_context *pasync_ctx,
|
|
unsigned int is_header, unsigned int cq_index)
|
|
{
|
|
struct list_head *pbusy_list;
|
|
struct async_pdu_handle *pasync_handle;
|
|
unsigned int num_entries, writables = 0;
|
|
unsigned int *pep_read_ptr, *pwritables;
|
|
|
|
|
|
if (is_header) {
|
|
pep_read_ptr = &pasync_ctx->async_header.ep_read_ptr;
|
|
pwritables = &pasync_ctx->async_header.writables;
|
|
num_entries = pasync_ctx->async_header.num_entries;
|
|
} else {
|
|
pep_read_ptr = &pasync_ctx->async_data.ep_read_ptr;
|
|
pwritables = &pasync_ctx->async_data.writables;
|
|
num_entries = pasync_ctx->async_data.num_entries;
|
|
}
|
|
|
|
while ((*pep_read_ptr) != cq_index) {
|
|
(*pep_read_ptr)++;
|
|
*pep_read_ptr = (*pep_read_ptr) % num_entries;
|
|
|
|
pbusy_list = hwi_get_async_busy_list(pasync_ctx, is_header,
|
|
*pep_read_ptr);
|
|
if (writables == 0)
|
|
WARN_ON(list_empty(pbusy_list));
|
|
|
|
if (!list_empty(pbusy_list)) {
|
|
pasync_handle = list_entry(pbusy_list->next,
|
|
struct async_pdu_handle,
|
|
link);
|
|
WARN_ON(!pasync_handle);
|
|
pasync_handle->consumed = 1;
|
|
}
|
|
|
|
writables++;
|
|
}
|
|
|
|
if (!writables) {
|
|
SE_DEBUG(DBG_LVL_1,
|
|
"Duplicate notification received - index 0x%x!!\n",
|
|
cq_index);
|
|
WARN_ON(1);
|
|
}
|
|
|
|
*pwritables = *pwritables + writables;
|
|
return 0;
|
|
}
|
|
|
|
static unsigned int hwi_free_async_msg(struct beiscsi_hba *phba,
|
|
unsigned int cri)
|
|
{
|
|
struct hwi_controller *phwi_ctrlr;
|
|
struct hwi_async_pdu_context *pasync_ctx;
|
|
struct async_pdu_handle *pasync_handle, *tmp_handle;
|
|
struct list_head *plist;
|
|
unsigned int i = 0;
|
|
|
|
phwi_ctrlr = phba->phwi_ctrlr;
|
|
pasync_ctx = HWI_GET_ASYNC_PDU_CTX(phwi_ctrlr);
|
|
|
|
plist = &pasync_ctx->async_entry[cri].wait_queue.list;
|
|
|
|
list_for_each_entry_safe(pasync_handle, tmp_handle, plist, link) {
|
|
list_del(&pasync_handle->link);
|
|
|
|
if (i == 0) {
|
|
list_add_tail(&pasync_handle->link,
|
|
&pasync_ctx->async_header.free_list);
|
|
pasync_ctx->async_header.free_entries++;
|
|
i++;
|
|
} else {
|
|
list_add_tail(&pasync_handle->link,
|
|
&pasync_ctx->async_data.free_list);
|
|
pasync_ctx->async_data.free_entries++;
|
|
i++;
|
|
}
|
|
}
|
|
|
|
INIT_LIST_HEAD(&pasync_ctx->async_entry[cri].wait_queue.list);
|
|
pasync_ctx->async_entry[cri].wait_queue.hdr_received = 0;
|
|
pasync_ctx->async_entry[cri].wait_queue.bytes_received = 0;
|
|
return 0;
|
|
}
|
|
|
|
static struct phys_addr *
|
|
hwi_get_ring_address(struct hwi_async_pdu_context *pasync_ctx,
|
|
unsigned int is_header, unsigned int host_write_ptr)
|
|
{
|
|
struct phys_addr *pasync_sge = NULL;
|
|
|
|
if (is_header)
|
|
pasync_sge = pasync_ctx->async_header.ring_base;
|
|
else
|
|
pasync_sge = pasync_ctx->async_data.ring_base;
|
|
|
|
return pasync_sge + host_write_ptr;
|
|
}
|
|
|
|
static void hwi_post_async_buffers(struct beiscsi_hba *phba,
|
|
unsigned int is_header)
|
|
{
|
|
struct hwi_controller *phwi_ctrlr;
|
|
struct hwi_async_pdu_context *pasync_ctx;
|
|
struct async_pdu_handle *pasync_handle;
|
|
struct list_head *pfree_link, *pbusy_list;
|
|
struct phys_addr *pasync_sge;
|
|
unsigned int ring_id, num_entries;
|
|
unsigned int host_write_num;
|
|
unsigned int writables;
|
|
unsigned int i = 0;
|
|
u32 doorbell = 0;
|
|
|
|
phwi_ctrlr = phba->phwi_ctrlr;
|
|
pasync_ctx = HWI_GET_ASYNC_PDU_CTX(phwi_ctrlr);
|
|
|
|
if (is_header) {
|
|
num_entries = pasync_ctx->async_header.num_entries;
|
|
writables = min(pasync_ctx->async_header.writables,
|
|
pasync_ctx->async_header.free_entries);
|
|
pfree_link = pasync_ctx->async_header.free_list.next;
|
|
host_write_num = pasync_ctx->async_header.host_write_ptr;
|
|
ring_id = phwi_ctrlr->default_pdu_hdr.id;
|
|
} else {
|
|
num_entries = pasync_ctx->async_data.num_entries;
|
|
writables = min(pasync_ctx->async_data.writables,
|
|
pasync_ctx->async_data.free_entries);
|
|
pfree_link = pasync_ctx->async_data.free_list.next;
|
|
host_write_num = pasync_ctx->async_data.host_write_ptr;
|
|
ring_id = phwi_ctrlr->default_pdu_data.id;
|
|
}
|
|
|
|
writables = (writables / 8) * 8;
|
|
if (writables) {
|
|
for (i = 0; i < writables; i++) {
|
|
pbusy_list =
|
|
hwi_get_async_busy_list(pasync_ctx, is_header,
|
|
host_write_num);
|
|
pasync_handle =
|
|
list_entry(pfree_link, struct async_pdu_handle,
|
|
link);
|
|
WARN_ON(!pasync_handle);
|
|
pasync_handle->consumed = 0;
|
|
|
|
pfree_link = pfree_link->next;
|
|
|
|
pasync_sge = hwi_get_ring_address(pasync_ctx,
|
|
is_header, host_write_num);
|
|
|
|
pasync_sge->hi = pasync_handle->pa.u.a32.address_lo;
|
|
pasync_sge->lo = pasync_handle->pa.u.a32.address_hi;
|
|
|
|
list_move(&pasync_handle->link, pbusy_list);
|
|
|
|
host_write_num++;
|
|
host_write_num = host_write_num % num_entries;
|
|
}
|
|
|
|
if (is_header) {
|
|
pasync_ctx->async_header.host_write_ptr =
|
|
host_write_num;
|
|
pasync_ctx->async_header.free_entries -= writables;
|
|
pasync_ctx->async_header.writables -= writables;
|
|
pasync_ctx->async_header.busy_entries += writables;
|
|
} else {
|
|
pasync_ctx->async_data.host_write_ptr = host_write_num;
|
|
pasync_ctx->async_data.free_entries -= writables;
|
|
pasync_ctx->async_data.writables -= writables;
|
|
pasync_ctx->async_data.busy_entries += writables;
|
|
}
|
|
|
|
doorbell |= ring_id & DB_DEF_PDU_RING_ID_MASK;
|
|
doorbell |= 1 << DB_DEF_PDU_REARM_SHIFT;
|
|
doorbell |= 0 << DB_DEF_PDU_EVENT_SHIFT;
|
|
doorbell |= (writables & DB_DEF_PDU_CQPROC_MASK)
|
|
<< DB_DEF_PDU_CQPROC_SHIFT;
|
|
|
|
iowrite32(doorbell, phba->db_va + DB_RXULP0_OFFSET);
|
|
}
|
|
}
|
|
|
|
static void hwi_flush_default_pdu_buffer(struct beiscsi_hba *phba,
|
|
struct beiscsi_conn *beiscsi_conn,
|
|
struct i_t_dpdu_cqe *pdpdu_cqe)
|
|
{
|
|
struct hwi_controller *phwi_ctrlr;
|
|
struct hwi_async_pdu_context *pasync_ctx;
|
|
struct async_pdu_handle *pasync_handle = NULL;
|
|
unsigned int cq_index = -1;
|
|
|
|
phwi_ctrlr = phba->phwi_ctrlr;
|
|
pasync_ctx = HWI_GET_ASYNC_PDU_CTX(phwi_ctrlr);
|
|
|
|
pasync_handle = hwi_get_async_handle(phba, beiscsi_conn, pasync_ctx,
|
|
pdpdu_cqe, &cq_index);
|
|
BUG_ON(pasync_handle->is_header != 0);
|
|
if (pasync_handle->consumed == 0)
|
|
hwi_update_async_writables(pasync_ctx, pasync_handle->is_header,
|
|
cq_index);
|
|
|
|
hwi_free_async_msg(phba, pasync_handle->cri);
|
|
hwi_post_async_buffers(phba, pasync_handle->is_header);
|
|
}
|
|
|
|
static unsigned int
|
|
hwi_fwd_async_msg(struct beiscsi_conn *beiscsi_conn,
|
|
struct beiscsi_hba *phba,
|
|
struct hwi_async_pdu_context *pasync_ctx, unsigned short cri)
|
|
{
|
|
struct list_head *plist;
|
|
struct async_pdu_handle *pasync_handle;
|
|
void *phdr = NULL;
|
|
unsigned int hdr_len = 0, buf_len = 0;
|
|
unsigned int status, index = 0, offset = 0;
|
|
void *pfirst_buffer = NULL;
|
|
unsigned int num_buf = 0;
|
|
|
|
plist = &pasync_ctx->async_entry[cri].wait_queue.list;
|
|
|
|
list_for_each_entry(pasync_handle, plist, link) {
|
|
if (index == 0) {
|
|
phdr = pasync_handle->pbuffer;
|
|
hdr_len = pasync_handle->buffer_len;
|
|
} else {
|
|
buf_len = pasync_handle->buffer_len;
|
|
if (!num_buf) {
|
|
pfirst_buffer = pasync_handle->pbuffer;
|
|
num_buf++;
|
|
}
|
|
memcpy(pfirst_buffer + offset,
|
|
pasync_handle->pbuffer, buf_len);
|
|
offset = buf_len;
|
|
}
|
|
index++;
|
|
}
|
|
|
|
status = beiscsi_process_async_pdu(beiscsi_conn, phba,
|
|
(beiscsi_conn->beiscsi_conn_cid -
|
|
phba->fw_config.iscsi_cid_start),
|
|
phdr, hdr_len, pfirst_buffer,
|
|
buf_len);
|
|
|
|
if (status == 0)
|
|
hwi_free_async_msg(phba, cri);
|
|
return 0;
|
|
}
|
|
|
|
static unsigned int
|
|
hwi_gather_async_pdu(struct beiscsi_conn *beiscsi_conn,
|
|
struct beiscsi_hba *phba,
|
|
struct async_pdu_handle *pasync_handle)
|
|
{
|
|
struct hwi_async_pdu_context *pasync_ctx;
|
|
struct hwi_controller *phwi_ctrlr;
|
|
unsigned int bytes_needed = 0, status = 0;
|
|
unsigned short cri = pasync_handle->cri;
|
|
struct pdu_base *ppdu;
|
|
|
|
phwi_ctrlr = phba->phwi_ctrlr;
|
|
pasync_ctx = HWI_GET_ASYNC_PDU_CTX(phwi_ctrlr);
|
|
|
|
list_del(&pasync_handle->link);
|
|
if (pasync_handle->is_header) {
|
|
pasync_ctx->async_header.busy_entries--;
|
|
if (pasync_ctx->async_entry[cri].wait_queue.hdr_received) {
|
|
hwi_free_async_msg(phba, cri);
|
|
BUG();
|
|
}
|
|
|
|
pasync_ctx->async_entry[cri].wait_queue.bytes_received = 0;
|
|
pasync_ctx->async_entry[cri].wait_queue.hdr_received = 1;
|
|
pasync_ctx->async_entry[cri].wait_queue.hdr_len =
|
|
(unsigned short)pasync_handle->buffer_len;
|
|
list_add_tail(&pasync_handle->link,
|
|
&pasync_ctx->async_entry[cri].wait_queue.list);
|
|
|
|
ppdu = pasync_handle->pbuffer;
|
|
bytes_needed = ((((ppdu->dw[offsetof(struct amap_pdu_base,
|
|
data_len_hi) / 32] & PDUBASE_DATALENHI_MASK) << 8) &
|
|
0xFFFF0000) | ((be16_to_cpu((ppdu->
|
|
dw[offsetof(struct amap_pdu_base, data_len_lo) / 32]
|
|
& PDUBASE_DATALENLO_MASK) >> 16)) & 0x0000FFFF));
|
|
|
|
if (status == 0) {
|
|
pasync_ctx->async_entry[cri].wait_queue.bytes_needed =
|
|
bytes_needed;
|
|
|
|
if (bytes_needed == 0)
|
|
status = hwi_fwd_async_msg(beiscsi_conn, phba,
|
|
pasync_ctx, cri);
|
|
}
|
|
} else {
|
|
pasync_ctx->async_data.busy_entries--;
|
|
if (pasync_ctx->async_entry[cri].wait_queue.hdr_received) {
|
|
list_add_tail(&pasync_handle->link,
|
|
&pasync_ctx->async_entry[cri].wait_queue.
|
|
list);
|
|
pasync_ctx->async_entry[cri].wait_queue.
|
|
bytes_received +=
|
|
(unsigned short)pasync_handle->buffer_len;
|
|
|
|
if (pasync_ctx->async_entry[cri].wait_queue.
|
|
bytes_received >=
|
|
pasync_ctx->async_entry[cri].wait_queue.
|
|
bytes_needed)
|
|
status = hwi_fwd_async_msg(beiscsi_conn, phba,
|
|
pasync_ctx, cri);
|
|
}
|
|
}
|
|
return status;
|
|
}
|
|
|
|
static void hwi_process_default_pdu_ring(struct beiscsi_conn *beiscsi_conn,
|
|
struct beiscsi_hba *phba,
|
|
struct i_t_dpdu_cqe *pdpdu_cqe)
|
|
{
|
|
struct hwi_controller *phwi_ctrlr;
|
|
struct hwi_async_pdu_context *pasync_ctx;
|
|
struct async_pdu_handle *pasync_handle = NULL;
|
|
unsigned int cq_index = -1;
|
|
|
|
phwi_ctrlr = phba->phwi_ctrlr;
|
|
pasync_ctx = HWI_GET_ASYNC_PDU_CTX(phwi_ctrlr);
|
|
pasync_handle = hwi_get_async_handle(phba, beiscsi_conn, pasync_ctx,
|
|
pdpdu_cqe, &cq_index);
|
|
|
|
if (pasync_handle->consumed == 0)
|
|
hwi_update_async_writables(pasync_ctx, pasync_handle->is_header,
|
|
cq_index);
|
|
hwi_gather_async_pdu(beiscsi_conn, phba, pasync_handle);
|
|
hwi_post_async_buffers(phba, pasync_handle->is_header);
|
|
}
|
|
|
|
static void beiscsi_process_mcc_isr(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 (num_processed >= 32) {
|
|
hwi_ring_cq_db(phba, mcc_cq->id,
|
|
num_processed, 0, 0);
|
|
num_processed = 0;
|
|
}
|
|
if (mcc_compl->flags & CQE_FLAGS_ASYNC_MASK) {
|
|
/* Interpret flags as an async trailer */
|
|
if (is_link_state_evt(mcc_compl->flags))
|
|
/* Interpret compl as a async link evt */
|
|
beiscsi_async_link_state_process(phba,
|
|
(struct be_async_event_link_state *) mcc_compl);
|
|
else
|
|
SE_DEBUG(DBG_LVL_1,
|
|
" Unsupported Async Event, flags"
|
|
" = 0x%08x\n", mcc_compl->flags);
|
|
} else if (mcc_compl->flags & CQE_FLAGS_COMPLETED_MASK) {
|
|
be_mcc_compl_process_isr(&phba->ctrl, mcc_compl);
|
|
atomic_dec(&phba->ctrl.mcc_obj.q.used);
|
|
}
|
|
|
|
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, 0);
|
|
|
|
}
|
|
|
|
static unsigned int beiscsi_process_cq(struct be_eq_obj *pbe_eq)
|
|
{
|
|
struct be_queue_info *cq;
|
|
struct sol_cqe *sol;
|
|
struct dmsg_cqe *dmsg;
|
|
unsigned int num_processed = 0;
|
|
unsigned int tot_nump = 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) {
|
|
be_dws_le_to_cpu(sol, sizeof(struct sol_cqe));
|
|
|
|
ep = phba->ep_array[(u32) ((sol->
|
|
dw[offsetof(struct amap_sol_cqe, cid) / 32] &
|
|
SOL_CID_MASK) >> 6) -
|
|
phba->fw_config.iscsi_cid_start];
|
|
|
|
beiscsi_ep = ep->dd_data;
|
|
beiscsi_conn = beiscsi_ep->conn;
|
|
|
|
if (num_processed >= 32) {
|
|
hwi_ring_cq_db(phba, cq->id,
|
|
num_processed, 0, 0);
|
|
tot_nump += num_processed;
|
|
num_processed = 0;
|
|
}
|
|
|
|
switch ((u32) sol->dw[offsetof(struct amap_sol_cqe, code) /
|
|
32] & CQE_CODE_MASK) {
|
|
case SOL_CMD_COMPLETE:
|
|
hwi_complete_cmd(beiscsi_conn, phba, sol);
|
|
break;
|
|
case DRIVERMSG_NOTIFY:
|
|
SE_DEBUG(DBG_LVL_8, "Received DRIVERMSG_NOTIFY\n");
|
|
dmsg = (struct dmsg_cqe *)sol;
|
|
hwi_complete_drvr_msgs(beiscsi_conn, phba, sol);
|
|
break;
|
|
case UNSOL_HDR_NOTIFY:
|
|
SE_DEBUG(DBG_LVL_8, "Received UNSOL_HDR_ NOTIFY\n");
|
|
hwi_process_default_pdu_ring(beiscsi_conn, phba,
|
|
(struct i_t_dpdu_cqe *)sol);
|
|
break;
|
|
case UNSOL_DATA_NOTIFY:
|
|
SE_DEBUG(DBG_LVL_8, "Received UNSOL_DATA_NOTIFY\n");
|
|
hwi_process_default_pdu_ring(beiscsi_conn, phba,
|
|
(struct i_t_dpdu_cqe *)sol);
|
|
break;
|
|
case CXN_INVALIDATE_INDEX_NOTIFY:
|
|
case CMD_INVALIDATED_NOTIFY:
|
|
case CXN_INVALIDATE_NOTIFY:
|
|
SE_DEBUG(DBG_LVL_1,
|
|
"Ignoring CQ Error notification for cmd/cxn"
|
|
"invalidate\n");
|
|
break;
|
|
case SOL_CMD_KILLED_DATA_DIGEST_ERR:
|
|
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:
|
|
SE_DEBUG(DBG_LVL_1,
|
|
"CQ Error notification for cmd.. "
|
|
"code %d cid 0x%x\n",
|
|
sol->dw[offsetof(struct amap_sol_cqe, code) /
|
|
32] & CQE_CODE_MASK,
|
|
(sol->dw[offsetof(struct amap_sol_cqe, cid) /
|
|
32] & SOL_CID_MASK));
|
|
break;
|
|
case UNSOL_DATA_DIGEST_ERROR_NOTIFY:
|
|
SE_DEBUG(DBG_LVL_1,
|
|
"Digest error on def pdu ring, dropping..\n");
|
|
hwi_flush_default_pdu_buffer(phba, beiscsi_conn,
|
|
(struct i_t_dpdu_cqe *) sol);
|
|
break;
|
|
case CXN_KILLED_PDU_SIZE_EXCEEDS_DSL:
|
|
case CXN_KILLED_BURST_LEN_MISMATCH:
|
|
case CXN_KILLED_AHS_RCVD:
|
|
case CXN_KILLED_HDR_DIGEST_ERR:
|
|
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_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:
|
|
SE_DEBUG(DBG_LVL_1, "CQ Error %d, reset CID "
|
|
"0x%x...\n",
|
|
sol->dw[offsetof(struct amap_sol_cqe, code) /
|
|
32] & CQE_CODE_MASK,
|
|
(sol->dw[offsetof(struct amap_sol_cqe, cid) /
|
|
32] & CQE_CID_MASK));
|
|
iscsi_conn_failure(beiscsi_conn->conn,
|
|
ISCSI_ERR_CONN_FAILED);
|
|
break;
|
|
case CXN_KILLED_RST_SENT:
|
|
case CXN_KILLED_RST_RCVD:
|
|
SE_DEBUG(DBG_LVL_1, "CQ Error %d, reset"
|
|
"received/sent on CID 0x%x...\n",
|
|
sol->dw[offsetof(struct amap_sol_cqe, code) /
|
|
32] & CQE_CODE_MASK,
|
|
(sol->dw[offsetof(struct amap_sol_cqe, cid) /
|
|
32] & CQE_CID_MASK));
|
|
iscsi_conn_failure(beiscsi_conn->conn,
|
|
ISCSI_ERR_CONN_FAILED);
|
|
break;
|
|
default:
|
|
SE_DEBUG(DBG_LVL_1, "CQ Error Invalid code= %d "
|
|
"received on CID 0x%x...\n",
|
|
sol->dw[offsetof(struct amap_sol_cqe, code) /
|
|
32] & CQE_CODE_MASK,
|
|
(sol->dw[offsetof(struct amap_sol_cqe, cid) /
|
|
32] & CQE_CID_MASK));
|
|
break;
|
|
}
|
|
|
|
AMAP_SET_BITS(struct amap_sol_cqe, valid, sol, 0);
|
|
queue_tail_inc(cq);
|
|
sol = queue_tail_node(cq);
|
|
num_processed++;
|
|
}
|
|
|
|
if (num_processed > 0) {
|
|
tot_nump += num_processed;
|
|
hwi_ring_cq_db(phba, cq->id, num_processed, 1, 0);
|
|
}
|
|
return tot_nump;
|
|
}
|
|
|
|
void beiscsi_process_all_cqs(struct work_struct *work)
|
|
{
|
|
unsigned long flags;
|
|
struct hwi_controller *phwi_ctrlr;
|
|
struct hwi_context_memory *phwi_context;
|
|
struct be_eq_obj *pbe_eq;
|
|
struct beiscsi_hba *phba =
|
|
container_of(work, struct beiscsi_hba, work_cqs);
|
|
|
|
phwi_ctrlr = phba->phwi_ctrlr;
|
|
phwi_context = phwi_ctrlr->phwi_ctxt;
|
|
if (phba->msix_enabled)
|
|
pbe_eq = &phwi_context->be_eq[phba->num_cpus];
|
|
else
|
|
pbe_eq = &phwi_context->be_eq[0];
|
|
|
|
if (phba->todo_mcc_cq) {
|
|
spin_lock_irqsave(&phba->isr_lock, flags);
|
|
phba->todo_mcc_cq = 0;
|
|
spin_unlock_irqrestore(&phba->isr_lock, flags);
|
|
beiscsi_process_mcc_isr(phba);
|
|
}
|
|
|
|
if (phba->todo_cq) {
|
|
spin_lock_irqsave(&phba->isr_lock, flags);
|
|
phba->todo_cq = 0;
|
|
spin_unlock_irqrestore(&phba->isr_lock, flags);
|
|
beiscsi_process_cq(pbe_eq);
|
|
}
|
|
}
|
|
|
|
static int be_iopoll(struct blk_iopoll *iop, int budget)
|
|
{
|
|
static unsigned int ret;
|
|
struct beiscsi_hba *phba;
|
|
struct be_eq_obj *pbe_eq;
|
|
|
|
pbe_eq = container_of(iop, struct be_eq_obj, iopoll);
|
|
ret = beiscsi_process_cq(pbe_eq);
|
|
if (ret < budget) {
|
|
phba = pbe_eq->phba;
|
|
blk_iopoll_complete(iop);
|
|
SE_DEBUG(DBG_LVL_8, "rearm pbe_eq->q.id =%d\n", pbe_eq->q.id);
|
|
hwi_ring_eq_db(phba, pbe_eq->q.id, 0, 0, 1, 1);
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
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,
|
|
0);
|
|
} else if (num_sg == 2) {
|
|
AMAP_SET_BITS(struct amap_iscsi_wrb, sge0_last, pwrb,
|
|
0);
|
|
AMAP_SET_BITS(struct amap_iscsi_wrb, sge1_last, pwrb,
|
|
1);
|
|
} else {
|
|
AMAP_SET_BITS(struct amap_iscsi_wrb, sge0_last, pwrb,
|
|
0);
|
|
AMAP_SET_BITS(struct amap_iscsi_wrb, 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,
|
|
(addr & 0xFFFFFFFF));
|
|
AMAP_SET_BITS(struct amap_iscsi_sge, addr_hi, psgl,
|
|
(addr >> 32));
|
|
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_buffer(struct iscsi_wrb *pwrb, struct iscsi_task *task)
|
|
{
|
|
struct iscsi_sge *psgl;
|
|
unsigned long long addr;
|
|
struct beiscsi_io_task *io_task = task->dd_data;
|
|
struct beiscsi_conn *beiscsi_conn = io_task->conn;
|
|
struct beiscsi_hba *phba = beiscsi_conn->phba;
|
|
|
|
io_task->bhs_len = sizeof(struct be_nonio_bhs) - 2;
|
|
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);
|
|
|
|
if (task->data) {
|
|
if (task->data_count) {
|
|
AMAP_SET_BITS(struct amap_iscsi_wrb, dsp, pwrb, 1);
|
|
addr = (u64) pci_map_single(phba->pcidev,
|
|
task->data,
|
|
task->data_count, 1);
|
|
} else {
|
|
AMAP_SET_BITS(struct amap_iscsi_wrb, dsp, pwrb, 0);
|
|
addr = 0;
|
|
}
|
|
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,
|
|
task->data_count);
|
|
|
|
AMAP_SET_BITS(struct amap_iscsi_wrb, sge0_last, pwrb, 1);
|
|
} else {
|
|
AMAP_SET_BITS(struct amap_iscsi_wrb, dsp, pwrb, 0);
|
|
addr = 0;
|
|
}
|
|
|
|
psgl = (struct iscsi_sge *)io_task->psgl_handle->pfrag;
|
|
|
|
AMAP_SET_BITS(struct amap_iscsi_sge, len, psgl, io_task->bhs_len);
|
|
|
|
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 (task->data) {
|
|
psgl++;
|
|
AMAP_SET_BITS(struct amap_iscsi_sge, addr_hi, psgl, 0);
|
|
AMAP_SET_BITS(struct amap_iscsi_sge, addr_lo, psgl, 0);
|
|
AMAP_SET_BITS(struct amap_iscsi_sge, len, psgl, 0);
|
|
AMAP_SET_BITS(struct amap_iscsi_sge, sge_offset, psgl, 0);
|
|
AMAP_SET_BITS(struct amap_iscsi_sge, rsvd0, psgl, 0);
|
|
AMAP_SET_BITS(struct amap_iscsi_sge, last_sge, psgl, 0);
|
|
|
|
psgl++;
|
|
if (task->data) {
|
|
AMAP_SET_BITS(struct amap_iscsi_sge, addr_lo, psgl,
|
|
((u32)(addr & 0xFFFFFFFF)));
|
|
AMAP_SET_BITS(struct amap_iscsi_sge, addr_hi, psgl,
|
|
((u32)(addr >> 32)));
|
|
}
|
|
AMAP_SET_BITS(struct amap_iscsi_sge, len, psgl, 0x106);
|
|
}
|
|
AMAP_SET_BITS(struct amap_iscsi_sge, last_sge, psgl, 1);
|
|
}
|
|
|
|
static void beiscsi_find_mem_req(struct beiscsi_hba *phba)
|
|
{
|
|
unsigned int num_cq_pages, num_async_pdu_buf_pages;
|
|
unsigned int num_async_pdu_data_pages, wrb_sz_per_cxn;
|
|
unsigned int num_async_pdu_buf_sgl_pages, num_async_pdu_data_sgl_pages;
|
|
|
|
num_cq_pages = PAGES_REQUIRED(phba->params.num_cq_entries * \
|
|
sizeof(struct sol_cqe));
|
|
num_async_pdu_buf_pages =
|
|
PAGES_REQUIRED(phba->params.asyncpdus_per_ctrl * \
|
|
phba->params.defpdu_hdr_sz);
|
|
num_async_pdu_buf_sgl_pages =
|
|
PAGES_REQUIRED(phba->params.asyncpdus_per_ctrl * \
|
|
sizeof(struct phys_addr));
|
|
num_async_pdu_data_pages =
|
|
PAGES_REQUIRED(phba->params.asyncpdus_per_ctrl * \
|
|
phba->params.defpdu_data_sz);
|
|
num_async_pdu_data_sgl_pages =
|
|
PAGES_REQUIRED(phba->params.asyncpdus_per_ctrl * \
|
|
sizeof(struct phys_addr));
|
|
|
|
phba->params.hwi_ws_sz = sizeof(struct hwi_controller);
|
|
|
|
phba->mem_req[ISCSI_MEM_GLOBAL_HEADER] = 2 *
|
|
BE_ISCSI_PDU_HEADER_SIZE;
|
|
phba->mem_req[HWI_MEM_ADDN_CONTEXT] =
|
|
sizeof(struct hwi_context_memory);
|
|
|
|
|
|
phba->mem_req[HWI_MEM_WRB] = sizeof(struct iscsi_wrb)
|
|
* (phba->params.wrbs_per_cxn)
|
|
* phba->params.cxns_per_ctrl;
|
|
wrb_sz_per_cxn = sizeof(struct wrb_handle) *
|
|
(phba->params.wrbs_per_cxn);
|
|
phba->mem_req[HWI_MEM_WRBH] = roundup_pow_of_two((wrb_sz_per_cxn) *
|
|
phba->params.cxns_per_ctrl);
|
|
|
|
phba->mem_req[HWI_MEM_SGLH] = sizeof(struct sgl_handle) *
|
|
phba->params.icds_per_ctrl;
|
|
phba->mem_req[HWI_MEM_SGE] = sizeof(struct iscsi_sge) *
|
|
phba->params.num_sge_per_io * phba->params.icds_per_ctrl;
|
|
|
|
phba->mem_req[HWI_MEM_ASYNC_HEADER_BUF] =
|
|
num_async_pdu_buf_pages * PAGE_SIZE;
|
|
phba->mem_req[HWI_MEM_ASYNC_DATA_BUF] =
|
|
num_async_pdu_data_pages * PAGE_SIZE;
|
|
phba->mem_req[HWI_MEM_ASYNC_HEADER_RING] =
|
|
num_async_pdu_buf_sgl_pages * PAGE_SIZE;
|
|
phba->mem_req[HWI_MEM_ASYNC_DATA_RING] =
|
|
num_async_pdu_data_sgl_pages * PAGE_SIZE;
|
|
phba->mem_req[HWI_MEM_ASYNC_HEADER_HANDLE] =
|
|
phba->params.asyncpdus_per_ctrl *
|
|
sizeof(struct async_pdu_handle);
|
|
phba->mem_req[HWI_MEM_ASYNC_DATA_HANDLE] =
|
|
phba->params.asyncpdus_per_ctrl *
|
|
sizeof(struct async_pdu_handle);
|
|
phba->mem_req[HWI_MEM_ASYNC_PDU_CONTEXT] =
|
|
sizeof(struct hwi_async_pdu_context) +
|
|
(phba->params.cxns_per_ctrl * sizeof(struct hwi_async_entry));
|
|
}
|
|
|
|
static int beiscsi_alloc_mem(struct beiscsi_hba *phba)
|
|
{
|
|
struct be_mem_descriptor *mem_descr;
|
|
dma_addr_t bus_add;
|
|
struct mem_array *mem_arr, *mem_arr_orig;
|
|
unsigned int i, j, alloc_size, curr_alloc_size;
|
|
|
|
phba->phwi_ctrlr = kmalloc(phba->params.hwi_ws_sz, GFP_KERNEL);
|
|
if (!phba->phwi_ctrlr)
|
|
return -ENOMEM;
|
|
|
|
phba->init_mem = kcalloc(SE_MEM_MAX, sizeof(*mem_descr),
|
|
GFP_KERNEL);
|
|
if (!phba->init_mem) {
|
|
kfree(phba->phwi_ctrlr);
|
|
return -ENOMEM;
|
|
}
|
|
|
|
mem_arr_orig = kmalloc(sizeof(*mem_arr_orig) * BEISCSI_MAX_FRAGS_INIT,
|
|
GFP_KERNEL);
|
|
if (!mem_arr_orig) {
|
|
kfree(phba->init_mem);
|
|
kfree(phba->phwi_ctrlr);
|
|
return -ENOMEM;
|
|
}
|
|
|
|
mem_descr = phba->init_mem;
|
|
for (i = 0; i < SE_MEM_MAX; i++) {
|
|
j = 0;
|
|
mem_arr = mem_arr_orig;
|
|
alloc_size = phba->mem_req[i];
|
|
memset(mem_arr, 0, sizeof(struct mem_array) *
|
|
BEISCSI_MAX_FRAGS_INIT);
|
|
curr_alloc_size = min(be_max_phys_size * 1024, alloc_size);
|
|
do {
|
|
mem_arr->virtual_address = pci_alloc_consistent(
|
|
phba->pcidev,
|
|
curr_alloc_size,
|
|
&bus_add);
|
|
if (!mem_arr->virtual_address) {
|
|
if (curr_alloc_size <= BE_MIN_MEM_SIZE)
|
|
goto free_mem;
|
|
if (curr_alloc_size -
|
|
rounddown_pow_of_two(curr_alloc_size))
|
|
curr_alloc_size = rounddown_pow_of_two
|
|
(curr_alloc_size);
|
|
else
|
|
curr_alloc_size = curr_alloc_size / 2;
|
|
} else {
|
|
mem_arr->bus_address.u.
|
|
a64.address = (__u64) bus_add;
|
|
mem_arr->size = curr_alloc_size;
|
|
alloc_size -= curr_alloc_size;
|
|
curr_alloc_size = min(be_max_phys_size *
|
|
1024, alloc_size);
|
|
j++;
|
|
mem_arr++;
|
|
}
|
|
} while (alloc_size);
|
|
mem_descr->num_elements = j;
|
|
mem_descr->size_in_bytes = phba->mem_req[i];
|
|
mem_descr->mem_array = kmalloc(sizeof(*mem_arr) * j,
|
|
GFP_KERNEL);
|
|
if (!mem_descr->mem_array)
|
|
goto free_mem;
|
|
|
|
memcpy(mem_descr->mem_array, mem_arr_orig,
|
|
sizeof(struct mem_array) * j);
|
|
mem_descr++;
|
|
}
|
|
kfree(mem_arr_orig);
|
|
return 0;
|
|
free_mem:
|
|
mem_descr->num_elements = j;
|
|
while ((i) || (j)) {
|
|
for (j = mem_descr->num_elements; j > 0; j--) {
|
|
pci_free_consistent(phba->pcidev,
|
|
mem_descr->mem_array[j - 1].size,
|
|
mem_descr->mem_array[j - 1].
|
|
virtual_address,
|
|
(unsigned long)mem_descr->
|
|
mem_array[j - 1].
|
|
bus_address.u.a64.address);
|
|
}
|
|
if (i) {
|
|
i--;
|
|
kfree(mem_descr->mem_array);
|
|
mem_descr--;
|
|
}
|
|
}
|
|
kfree(mem_arr_orig);
|
|
kfree(phba->init_mem);
|
|
kfree(phba->phwi_ctrlr);
|
|
return -ENOMEM;
|
|
}
|
|
|
|
static int beiscsi_get_memory(struct beiscsi_hba *phba)
|
|
{
|
|
beiscsi_find_mem_req(phba);
|
|
return beiscsi_alloc_mem(phba);
|
|
}
|
|
|
|
static void iscsi_init_global_templates(struct beiscsi_hba *phba)
|
|
{
|
|
struct pdu_data_out *pdata_out;
|
|
struct pdu_nop_out *pnop_out;
|
|
struct be_mem_descriptor *mem_descr;
|
|
|
|
mem_descr = phba->init_mem;
|
|
mem_descr += ISCSI_MEM_GLOBAL_HEADER;
|
|
pdata_out =
|
|
(struct pdu_data_out *)mem_descr->mem_array[0].virtual_address;
|
|
memset(pdata_out, 0, BE_ISCSI_PDU_HEADER_SIZE);
|
|
|
|
AMAP_SET_BITS(struct amap_pdu_data_out, opcode, pdata_out,
|
|
IIOC_SCSI_DATA);
|
|
|
|
pnop_out =
|
|
(struct pdu_nop_out *)((unsigned char *)mem_descr->mem_array[0].
|
|
virtual_address + BE_ISCSI_PDU_HEADER_SIZE);
|
|
|
|
memset(pnop_out, 0, BE_ISCSI_PDU_HEADER_SIZE);
|
|
AMAP_SET_BITS(struct amap_pdu_nop_out, ttt, pnop_out, 0xFFFFFFFF);
|
|
AMAP_SET_BITS(struct amap_pdu_nop_out, f_bit, pnop_out, 1);
|
|
AMAP_SET_BITS(struct amap_pdu_nop_out, i_bit, pnop_out, 0);
|
|
}
|
|
|
|
static void beiscsi_init_wrb_handle(struct beiscsi_hba *phba)
|
|
{
|
|
struct be_mem_descriptor *mem_descr_wrbh, *mem_descr_wrb;
|
|
struct wrb_handle *pwrb_handle;
|
|
struct hwi_controller *phwi_ctrlr;
|
|
struct hwi_wrb_context *pwrb_context;
|
|
struct iscsi_wrb *pwrb;
|
|
unsigned int num_cxn_wrbh;
|
|
unsigned int num_cxn_wrb, j, idx, index;
|
|
|
|
mem_descr_wrbh = phba->init_mem;
|
|
mem_descr_wrbh += HWI_MEM_WRBH;
|
|
|
|
mem_descr_wrb = phba->init_mem;
|
|
mem_descr_wrb += HWI_MEM_WRB;
|
|
|
|
idx = 0;
|
|
pwrb_handle = mem_descr_wrbh->mem_array[idx].virtual_address;
|
|
num_cxn_wrbh = ((mem_descr_wrbh->mem_array[idx].size) /
|
|
((sizeof(struct wrb_handle)) *
|
|
phba->params.wrbs_per_cxn));
|
|
phwi_ctrlr = phba->phwi_ctrlr;
|
|
|
|
for (index = 0; index < phba->params.cxns_per_ctrl * 2; index += 2) {
|
|
pwrb_context = &phwi_ctrlr->wrb_context[index];
|
|
pwrb_context->pwrb_handle_base =
|
|
kzalloc(sizeof(struct wrb_handle *) *
|
|
phba->params.wrbs_per_cxn, GFP_KERNEL);
|
|
pwrb_context->pwrb_handle_basestd =
|
|
kzalloc(sizeof(struct wrb_handle *) *
|
|
phba->params.wrbs_per_cxn, GFP_KERNEL);
|
|
if (num_cxn_wrbh) {
|
|
pwrb_context->alloc_index = 0;
|
|
pwrb_context->wrb_handles_available = 0;
|
|
for (j = 0; j < phba->params.wrbs_per_cxn; j++) {
|
|
pwrb_context->pwrb_handle_base[j] = pwrb_handle;
|
|
pwrb_context->pwrb_handle_basestd[j] =
|
|
pwrb_handle;
|
|
pwrb_context->wrb_handles_available++;
|
|
pwrb_handle->wrb_index = j;
|
|
pwrb_handle++;
|
|
}
|
|
pwrb_context->free_index = 0;
|
|
num_cxn_wrbh--;
|
|
} else {
|
|
idx++;
|
|
pwrb_handle =
|
|
mem_descr_wrbh->mem_array[idx].virtual_address;
|
|
num_cxn_wrbh =
|
|
((mem_descr_wrbh->mem_array[idx].size) /
|
|
((sizeof(struct wrb_handle)) *
|
|
phba->params.wrbs_per_cxn));
|
|
pwrb_context->alloc_index = 0;
|
|
for (j = 0; j < phba->params.wrbs_per_cxn; j++) {
|
|
pwrb_context->pwrb_handle_base[j] = pwrb_handle;
|
|
pwrb_context->pwrb_handle_basestd[j] =
|
|
pwrb_handle;
|
|
pwrb_context->wrb_handles_available++;
|
|
pwrb_handle->wrb_index = j;
|
|
pwrb_handle++;
|
|
}
|
|
pwrb_context->free_index = 0;
|
|
num_cxn_wrbh--;
|
|
}
|
|
}
|
|
idx = 0;
|
|
pwrb = mem_descr_wrb->mem_array[idx].virtual_address;
|
|
num_cxn_wrb = (mem_descr_wrb->mem_array[idx].size) /
|
|
((sizeof(struct iscsi_wrb) *
|
|
phba->params.wrbs_per_cxn));
|
|
for (index = 0; index < phba->params.cxns_per_ctrl * 2; index += 2) {
|
|
pwrb_context = &phwi_ctrlr->wrb_context[index];
|
|
if (num_cxn_wrb) {
|
|
for (j = 0; j < phba->params.wrbs_per_cxn; j++) {
|
|
pwrb_handle = pwrb_context->pwrb_handle_base[j];
|
|
pwrb_handle->pwrb = pwrb;
|
|
pwrb++;
|
|
}
|
|
num_cxn_wrb--;
|
|
} else {
|
|
idx++;
|
|
pwrb = mem_descr_wrb->mem_array[idx].virtual_address;
|
|
num_cxn_wrb = (mem_descr_wrb->mem_array[idx].size) /
|
|
((sizeof(struct iscsi_wrb) *
|
|
phba->params.wrbs_per_cxn));
|
|
for (j = 0; j < phba->params.wrbs_per_cxn; j++) {
|
|
pwrb_handle = pwrb_context->pwrb_handle_base[j];
|
|
pwrb_handle->pwrb = pwrb;
|
|
pwrb++;
|
|
}
|
|
num_cxn_wrb--;
|
|
}
|
|
}
|
|
}
|
|
|
|
static void hwi_init_async_pdu_ctx(struct beiscsi_hba *phba)
|
|
{
|
|
struct hwi_controller *phwi_ctrlr;
|
|
struct hba_parameters *p = &phba->params;
|
|
struct hwi_async_pdu_context *pasync_ctx;
|
|
struct async_pdu_handle *pasync_header_h, *pasync_data_h;
|
|
unsigned int index;
|
|
struct be_mem_descriptor *mem_descr;
|
|
|
|
mem_descr = (struct be_mem_descriptor *)phba->init_mem;
|
|
mem_descr += HWI_MEM_ASYNC_PDU_CONTEXT;
|
|
|
|
phwi_ctrlr = phba->phwi_ctrlr;
|
|
phwi_ctrlr->phwi_ctxt->pasync_ctx = (struct hwi_async_pdu_context *)
|
|
mem_descr->mem_array[0].virtual_address;
|
|
pasync_ctx = phwi_ctrlr->phwi_ctxt->pasync_ctx;
|
|
memset(pasync_ctx, 0, sizeof(*pasync_ctx));
|
|
|
|
pasync_ctx->async_header.num_entries = p->asyncpdus_per_ctrl;
|
|
pasync_ctx->async_header.buffer_size = p->defpdu_hdr_sz;
|
|
pasync_ctx->async_data.buffer_size = p->defpdu_data_sz;
|
|
pasync_ctx->async_data.num_entries = p->asyncpdus_per_ctrl;
|
|
|
|
mem_descr = (struct be_mem_descriptor *)phba->init_mem;
|
|
mem_descr += HWI_MEM_ASYNC_HEADER_BUF;
|
|
if (mem_descr->mem_array[0].virtual_address) {
|
|
SE_DEBUG(DBG_LVL_8,
|
|
"hwi_init_async_pdu_ctx HWI_MEM_ASYNC_HEADER_BUF"
|
|
"va=%p\n", mem_descr->mem_array[0].virtual_address);
|
|
} else
|
|
shost_printk(KERN_WARNING, phba->shost,
|
|
"No Virtual address\n");
|
|
|
|
pasync_ctx->async_header.va_base =
|
|
mem_descr->mem_array[0].virtual_address;
|
|
|
|
pasync_ctx->async_header.pa_base.u.a64.address =
|
|
mem_descr->mem_array[0].bus_address.u.a64.address;
|
|
|
|
mem_descr = (struct be_mem_descriptor *)phba->init_mem;
|
|
mem_descr += HWI_MEM_ASYNC_HEADER_RING;
|
|
if (mem_descr->mem_array[0].virtual_address) {
|
|
SE_DEBUG(DBG_LVL_8,
|
|
"hwi_init_async_pdu_ctx HWI_MEM_ASYNC_HEADER_RING"
|
|
"va=%p\n", mem_descr->mem_array[0].virtual_address);
|
|
} else
|
|
shost_printk(KERN_WARNING, phba->shost,
|
|
"No Virtual address\n");
|
|
pasync_ctx->async_header.ring_base =
|
|
mem_descr->mem_array[0].virtual_address;
|
|
|
|
mem_descr = (struct be_mem_descriptor *)phba->init_mem;
|
|
mem_descr += HWI_MEM_ASYNC_HEADER_HANDLE;
|
|
if (mem_descr->mem_array[0].virtual_address) {
|
|
SE_DEBUG(DBG_LVL_8,
|
|
"hwi_init_async_pdu_ctx HWI_MEM_ASYNC_HEADER_HANDLE"
|
|
"va=%p\n", mem_descr->mem_array[0].virtual_address);
|
|
} else
|
|
shost_printk(KERN_WARNING, phba->shost,
|
|
"No Virtual address\n");
|
|
|
|
pasync_ctx->async_header.handle_base =
|
|
mem_descr->mem_array[0].virtual_address;
|
|
pasync_ctx->async_header.writables = 0;
|
|
INIT_LIST_HEAD(&pasync_ctx->async_header.free_list);
|
|
|
|
mem_descr = (struct be_mem_descriptor *)phba->init_mem;
|
|
mem_descr += HWI_MEM_ASYNC_DATA_BUF;
|
|
if (mem_descr->mem_array[0].virtual_address) {
|
|
SE_DEBUG(DBG_LVL_8,
|
|
"hwi_init_async_pdu_ctx HWI_MEM_ASYNC_DATA_BUF"
|
|
"va=%p\n", mem_descr->mem_array[0].virtual_address);
|
|
} else
|
|
shost_printk(KERN_WARNING, phba->shost,
|
|
"No Virtual address\n");
|
|
pasync_ctx->async_data.va_base =
|
|
mem_descr->mem_array[0].virtual_address;
|
|
pasync_ctx->async_data.pa_base.u.a64.address =
|
|
mem_descr->mem_array[0].bus_address.u.a64.address;
|
|
|
|
mem_descr = (struct be_mem_descriptor *)phba->init_mem;
|
|
mem_descr += HWI_MEM_ASYNC_DATA_RING;
|
|
if (mem_descr->mem_array[0].virtual_address) {
|
|
SE_DEBUG(DBG_LVL_8,
|
|
"hwi_init_async_pdu_ctx HWI_MEM_ASYNC_DATA_RING"
|
|
"va=%p\n", mem_descr->mem_array[0].virtual_address);
|
|
} else
|
|
shost_printk(KERN_WARNING, phba->shost,
|
|
"No Virtual address\n");
|
|
|
|
pasync_ctx->async_data.ring_base =
|
|
mem_descr->mem_array[0].virtual_address;
|
|
|
|
mem_descr = (struct be_mem_descriptor *)phba->init_mem;
|
|
mem_descr += HWI_MEM_ASYNC_DATA_HANDLE;
|
|
if (!mem_descr->mem_array[0].virtual_address)
|
|
shost_printk(KERN_WARNING, phba->shost,
|
|
"No Virtual address\n");
|
|
|
|
pasync_ctx->async_data.handle_base =
|
|
mem_descr->mem_array[0].virtual_address;
|
|
pasync_ctx->async_data.writables = 0;
|
|
INIT_LIST_HEAD(&pasync_ctx->async_data.free_list);
|
|
|
|
pasync_header_h =
|
|
(struct async_pdu_handle *)pasync_ctx->async_header.handle_base;
|
|
pasync_data_h =
|
|
(struct async_pdu_handle *)pasync_ctx->async_data.handle_base;
|
|
|
|
for (index = 0; index < p->asyncpdus_per_ctrl; index++) {
|
|
pasync_header_h->cri = -1;
|
|
pasync_header_h->index = (char)index;
|
|
INIT_LIST_HEAD(&pasync_header_h->link);
|
|
pasync_header_h->pbuffer =
|
|
(void *)((unsigned long)
|
|
(pasync_ctx->async_header.va_base) +
|
|
(p->defpdu_hdr_sz * index));
|
|
|
|
pasync_header_h->pa.u.a64.address =
|
|
pasync_ctx->async_header.pa_base.u.a64.address +
|
|
(p->defpdu_hdr_sz * index);
|
|
|
|
list_add_tail(&pasync_header_h->link,
|
|
&pasync_ctx->async_header.free_list);
|
|
pasync_header_h++;
|
|
pasync_ctx->async_header.free_entries++;
|
|
pasync_ctx->async_header.writables++;
|
|
|
|
INIT_LIST_HEAD(&pasync_ctx->async_entry[index].wait_queue.list);
|
|
INIT_LIST_HEAD(&pasync_ctx->async_entry[index].
|
|
header_busy_list);
|
|
pasync_data_h->cri = -1;
|
|
pasync_data_h->index = (char)index;
|
|
INIT_LIST_HEAD(&pasync_data_h->link);
|
|
pasync_data_h->pbuffer =
|
|
(void *)((unsigned long)
|
|
(pasync_ctx->async_data.va_base) +
|
|
(p->defpdu_data_sz * index));
|
|
|
|
pasync_data_h->pa.u.a64.address =
|
|
pasync_ctx->async_data.pa_base.u.a64.address +
|
|
(p->defpdu_data_sz * index);
|
|
|
|
list_add_tail(&pasync_data_h->link,
|
|
&pasync_ctx->async_data.free_list);
|
|
pasync_data_h++;
|
|
pasync_ctx->async_data.free_entries++;
|
|
pasync_ctx->async_data.writables++;
|
|
|
|
INIT_LIST_HEAD(&pasync_ctx->async_entry[index].data_busy_list);
|
|
}
|
|
|
|
pasync_ctx->async_header.host_write_ptr = 0;
|
|
pasync_ctx->async_header.ep_read_ptr = -1;
|
|
pasync_ctx->async_data.host_write_ptr = 0;
|
|
pasync_ctx->async_data.ep_read_ptr = -1;
|
|
}
|
|
|
|
static int
|
|
be_sgl_create_contiguous(void *virtual_address,
|
|
u64 physical_address, u32 length,
|
|
struct be_dma_mem *sgl)
|
|
{
|
|
WARN_ON(!virtual_address);
|
|
WARN_ON(!physical_address);
|
|
WARN_ON(!length > 0);
|
|
WARN_ON(!sgl);
|
|
|
|
sgl->va = virtual_address;
|
|
sgl->dma = (unsigned long)physical_address;
|
|
sgl->size = length;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static void be_sgl_destroy_contiguous(struct be_dma_mem *sgl)
|
|
{
|
|
memset(sgl, 0, sizeof(*sgl));
|
|
}
|
|
|
|
static void
|
|
hwi_build_be_sgl_arr(struct beiscsi_hba *phba,
|
|
struct mem_array *pmem, struct be_dma_mem *sgl)
|
|
{
|
|
if (sgl->va)
|
|
be_sgl_destroy_contiguous(sgl);
|
|
|
|
be_sgl_create_contiguous(pmem->virtual_address,
|
|
pmem->bus_address.u.a64.address,
|
|
pmem->size, sgl);
|
|
}
|
|
|
|
static void
|
|
hwi_build_be_sgl_by_offset(struct beiscsi_hba *phba,
|
|
struct mem_array *pmem, struct be_dma_mem *sgl)
|
|
{
|
|
if (sgl->va)
|
|
be_sgl_destroy_contiguous(sgl);
|
|
|
|
be_sgl_create_contiguous((unsigned char *)pmem->virtual_address,
|
|
pmem->bus_address.u.a64.address,
|
|
pmem->size, sgl);
|
|
}
|
|
|
|
static int be_fill_queue(struct be_queue_info *q,
|
|
u16 len, u16 entry_size, void *vaddress)
|
|
{
|
|
struct be_dma_mem *mem = &q->dma_mem;
|
|
|
|
memset(q, 0, sizeof(*q));
|
|
q->len = len;
|
|
q->entry_size = entry_size;
|
|
mem->size = len * entry_size;
|
|
mem->va = vaddress;
|
|
if (!mem->va)
|
|
return -ENOMEM;
|
|
memset(mem->va, 0, mem->size);
|
|
return 0;
|
|
}
|
|
|
|
static int beiscsi_create_eqs(struct beiscsi_hba *phba,
|
|
struct hwi_context_memory *phwi_context)
|
|
{
|
|
unsigned int i, num_eq_pages;
|
|
int ret, eq_for_mcc;
|
|
struct be_queue_info *eq;
|
|
struct be_dma_mem *mem;
|
|
void *eq_vaddress;
|
|
dma_addr_t paddr;
|
|
|
|
num_eq_pages = PAGES_REQUIRED(phba->params.num_eq_entries * \
|
|
sizeof(struct be_eq_entry));
|
|
|
|
if (phba->msix_enabled)
|
|
eq_for_mcc = 1;
|
|
else
|
|
eq_for_mcc = 0;
|
|
for (i = 0; i < (phba->num_cpus + eq_for_mcc); i++) {
|
|
eq = &phwi_context->be_eq[i].q;
|
|
mem = &eq->dma_mem;
|
|
phwi_context->be_eq[i].phba = phba;
|
|
eq_vaddress = pci_alloc_consistent(phba->pcidev,
|
|
num_eq_pages * PAGE_SIZE,
|
|
&paddr);
|
|
if (!eq_vaddress)
|
|
goto create_eq_error;
|
|
|
|
mem->va = eq_vaddress;
|
|
ret = be_fill_queue(eq, phba->params.num_eq_entries,
|
|
sizeof(struct be_eq_entry), eq_vaddress);
|
|
if (ret) {
|
|
shost_printk(KERN_ERR, phba->shost,
|
|
"be_fill_queue Failed for EQ\n");
|
|
goto create_eq_error;
|
|
}
|
|
|
|
mem->dma = paddr;
|
|
ret = beiscsi_cmd_eq_create(&phba->ctrl, eq,
|
|
phwi_context->cur_eqd);
|
|
if (ret) {
|
|
shost_printk(KERN_ERR, phba->shost,
|
|
"beiscsi_cmd_eq_create"
|
|
"Failedfor EQ\n");
|
|
goto create_eq_error;
|
|
}
|
|
SE_DEBUG(DBG_LVL_8, "eqid = %d\n", phwi_context->be_eq[i].q.id);
|
|
}
|
|
return 0;
|
|
create_eq_error:
|
|
for (i = 0; i < (phba->num_cpus + 1); i++) {
|
|
eq = &phwi_context->be_eq[i].q;
|
|
mem = &eq->dma_mem;
|
|
if (mem->va)
|
|
pci_free_consistent(phba->pcidev, num_eq_pages
|
|
* PAGE_SIZE,
|
|
mem->va, mem->dma);
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
static int beiscsi_create_cqs(struct beiscsi_hba *phba,
|
|
struct hwi_context_memory *phwi_context)
|
|
{
|
|
unsigned int i, num_cq_pages;
|
|
int ret;
|
|
struct be_queue_info *cq, *eq;
|
|
struct be_dma_mem *mem;
|
|
struct be_eq_obj *pbe_eq;
|
|
void *cq_vaddress;
|
|
dma_addr_t paddr;
|
|
|
|
num_cq_pages = PAGES_REQUIRED(phba->params.num_cq_entries * \
|
|
sizeof(struct sol_cqe));
|
|
|
|
for (i = 0; i < phba->num_cpus; i++) {
|
|
cq = &phwi_context->be_cq[i];
|
|
eq = &phwi_context->be_eq[i].q;
|
|
pbe_eq = &phwi_context->be_eq[i];
|
|
pbe_eq->cq = cq;
|
|
pbe_eq->phba = phba;
|
|
mem = &cq->dma_mem;
|
|
cq_vaddress = pci_alloc_consistent(phba->pcidev,
|
|
num_cq_pages * PAGE_SIZE,
|
|
&paddr);
|
|
if (!cq_vaddress)
|
|
goto create_cq_error;
|
|
ret = be_fill_queue(cq, phba->params.num_cq_entries,
|
|
sizeof(struct sol_cqe), cq_vaddress);
|
|
if (ret) {
|
|
shost_printk(KERN_ERR, phba->shost,
|
|
"be_fill_queue Failed for ISCSI CQ\n");
|
|
goto create_cq_error;
|
|
}
|
|
|
|
mem->dma = paddr;
|
|
ret = beiscsi_cmd_cq_create(&phba->ctrl, cq, eq, false,
|
|
false, 0);
|
|
if (ret) {
|
|
shost_printk(KERN_ERR, phba->shost,
|
|
"beiscsi_cmd_eq_create"
|
|
"Failed for ISCSI CQ\n");
|
|
goto create_cq_error;
|
|
}
|
|
SE_DEBUG(DBG_LVL_8, "iscsi cq_id is %d for eq_id %d\n",
|
|
cq->id, eq->id);
|
|
SE_DEBUG(DBG_LVL_8, "ISCSI CQ CREATED\n");
|
|
}
|
|
return 0;
|
|
|
|
create_cq_error:
|
|
for (i = 0; i < phba->num_cpus; i++) {
|
|
cq = &phwi_context->be_cq[i];
|
|
mem = &cq->dma_mem;
|
|
if (mem->va)
|
|
pci_free_consistent(phba->pcidev, num_cq_pages
|
|
* PAGE_SIZE,
|
|
mem->va, mem->dma);
|
|
}
|
|
return ret;
|
|
|
|
}
|
|
|
|
static int
|
|
beiscsi_create_def_hdr(struct beiscsi_hba *phba,
|
|
struct hwi_context_memory *phwi_context,
|
|
struct hwi_controller *phwi_ctrlr,
|
|
unsigned int def_pdu_ring_sz)
|
|
{
|
|
unsigned int idx;
|
|
int ret;
|
|
struct be_queue_info *dq, *cq;
|
|
struct be_dma_mem *mem;
|
|
struct be_mem_descriptor *mem_descr;
|
|
void *dq_vaddress;
|
|
|
|
idx = 0;
|
|
dq = &phwi_context->be_def_hdrq;
|
|
cq = &phwi_context->be_cq[0];
|
|
mem = &dq->dma_mem;
|
|
mem_descr = phba->init_mem;
|
|
mem_descr += HWI_MEM_ASYNC_HEADER_RING;
|
|
dq_vaddress = mem_descr->mem_array[idx].virtual_address;
|
|
ret = be_fill_queue(dq, mem_descr->mem_array[0].size /
|
|
sizeof(struct phys_addr),
|
|
sizeof(struct phys_addr), dq_vaddress);
|
|
if (ret) {
|
|
shost_printk(KERN_ERR, phba->shost,
|
|
"be_fill_queue Failed for DEF PDU HDR\n");
|
|
return ret;
|
|
}
|
|
mem->dma = (unsigned long)mem_descr->mem_array[idx].
|
|
bus_address.u.a64.address;
|
|
ret = be_cmd_create_default_pdu_queue(&phba->ctrl, cq, dq,
|
|
def_pdu_ring_sz,
|
|
phba->params.defpdu_hdr_sz);
|
|
if (ret) {
|
|
shost_printk(KERN_ERR, phba->shost,
|
|
"be_cmd_create_default_pdu_queue Failed DEFHDR\n");
|
|
return ret;
|
|
}
|
|
phwi_ctrlr->default_pdu_hdr.id = phwi_context->be_def_hdrq.id;
|
|
SE_DEBUG(DBG_LVL_8, "iscsi def pdu id is %d\n",
|
|
phwi_context->be_def_hdrq.id);
|
|
hwi_post_async_buffers(phba, 1);
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
beiscsi_create_def_data(struct beiscsi_hba *phba,
|
|
struct hwi_context_memory *phwi_context,
|
|
struct hwi_controller *phwi_ctrlr,
|
|
unsigned int def_pdu_ring_sz)
|
|
{
|
|
unsigned int idx;
|
|
int ret;
|
|
struct be_queue_info *dataq, *cq;
|
|
struct be_dma_mem *mem;
|
|
struct be_mem_descriptor *mem_descr;
|
|
void *dq_vaddress;
|
|
|
|
idx = 0;
|
|
dataq = &phwi_context->be_def_dataq;
|
|
cq = &phwi_context->be_cq[0];
|
|
mem = &dataq->dma_mem;
|
|
mem_descr = phba->init_mem;
|
|
mem_descr += HWI_MEM_ASYNC_DATA_RING;
|
|
dq_vaddress = mem_descr->mem_array[idx].virtual_address;
|
|
ret = be_fill_queue(dataq, mem_descr->mem_array[0].size /
|
|
sizeof(struct phys_addr),
|
|
sizeof(struct phys_addr), dq_vaddress);
|
|
if (ret) {
|
|
shost_printk(KERN_ERR, phba->shost,
|
|
"be_fill_queue Failed for DEF PDU DATA\n");
|
|
return ret;
|
|
}
|
|
mem->dma = (unsigned long)mem_descr->mem_array[idx].
|
|
bus_address.u.a64.address;
|
|
ret = be_cmd_create_default_pdu_queue(&phba->ctrl, cq, dataq,
|
|
def_pdu_ring_sz,
|
|
phba->params.defpdu_data_sz);
|
|
if (ret) {
|
|
shost_printk(KERN_ERR, phba->shost,
|
|
"be_cmd_create_default_pdu_queue Failed"
|
|
" for DEF PDU DATA\n");
|
|
return ret;
|
|
}
|
|
phwi_ctrlr->default_pdu_data.id = phwi_context->be_def_dataq.id;
|
|
SE_DEBUG(DBG_LVL_8, "iscsi def data id is %d\n",
|
|
phwi_context->be_def_dataq.id);
|
|
hwi_post_async_buffers(phba, 0);
|
|
SE_DEBUG(DBG_LVL_8, "DEFAULT PDU DATA RING CREATED\n");
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
beiscsi_post_pages(struct beiscsi_hba *phba)
|
|
{
|
|
struct be_mem_descriptor *mem_descr;
|
|
struct mem_array *pm_arr;
|
|
unsigned int page_offset, i;
|
|
struct be_dma_mem sgl;
|
|
int status;
|
|
|
|
mem_descr = phba->init_mem;
|
|
mem_descr += HWI_MEM_SGE;
|
|
pm_arr = mem_descr->mem_array;
|
|
|
|
page_offset = (sizeof(struct iscsi_sge) * phba->params.num_sge_per_io *
|
|
phba->fw_config.iscsi_icd_start) / PAGE_SIZE;
|
|
for (i = 0; i < mem_descr->num_elements; i++) {
|
|
hwi_build_be_sgl_arr(phba, pm_arr, &sgl);
|
|
status = be_cmd_iscsi_post_sgl_pages(&phba->ctrl, &sgl,
|
|
page_offset,
|
|
(pm_arr->size / PAGE_SIZE));
|
|
page_offset += pm_arr->size / PAGE_SIZE;
|
|
if (status != 0) {
|
|
shost_printk(KERN_ERR, phba->shost,
|
|
"post sgl failed.\n");
|
|
return status;
|
|
}
|
|
pm_arr++;
|
|
}
|
|
SE_DEBUG(DBG_LVL_8, "POSTED PAGES\n");
|
|
return 0;
|
|
}
|
|
|
|
static void be_queue_free(struct beiscsi_hba *phba, struct be_queue_info *q)
|
|
{
|
|
struct be_dma_mem *mem = &q->dma_mem;
|
|
if (mem->va)
|
|
pci_free_consistent(phba->pcidev, mem->size,
|
|
mem->va, mem->dma);
|
|
}
|
|
|
|
static int be_queue_alloc(struct beiscsi_hba *phba, struct be_queue_info *q,
|
|
u16 len, u16 entry_size)
|
|
{
|
|
struct be_dma_mem *mem = &q->dma_mem;
|
|
|
|
memset(q, 0, sizeof(*q));
|
|
q->len = len;
|
|
q->entry_size = entry_size;
|
|
mem->size = len * entry_size;
|
|
mem->va = pci_alloc_consistent(phba->pcidev, mem->size, &mem->dma);
|
|
if (!mem->va)
|
|
return -ENOMEM;
|
|
memset(mem->va, 0, mem->size);
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
beiscsi_create_wrb_rings(struct beiscsi_hba *phba,
|
|
struct hwi_context_memory *phwi_context,
|
|
struct hwi_controller *phwi_ctrlr)
|
|
{
|
|
unsigned int wrb_mem_index, offset, size, num_wrb_rings;
|
|
u64 pa_addr_lo;
|
|
unsigned int idx, num, i;
|
|
struct mem_array *pwrb_arr;
|
|
void *wrb_vaddr;
|
|
struct be_dma_mem sgl;
|
|
struct be_mem_descriptor *mem_descr;
|
|
int status;
|
|
|
|
idx = 0;
|
|
mem_descr = phba->init_mem;
|
|
mem_descr += HWI_MEM_WRB;
|
|
pwrb_arr = kmalloc(sizeof(*pwrb_arr) * phba->params.cxns_per_ctrl,
|
|
GFP_KERNEL);
|
|
if (!pwrb_arr) {
|
|
shost_printk(KERN_ERR, phba->shost,
|
|
"Memory alloc failed in create wrb ring.\n");
|
|
return -ENOMEM;
|
|
}
|
|
wrb_vaddr = mem_descr->mem_array[idx].virtual_address;
|
|
pa_addr_lo = mem_descr->mem_array[idx].bus_address.u.a64.address;
|
|
num_wrb_rings = mem_descr->mem_array[idx].size /
|
|
(phba->params.wrbs_per_cxn * sizeof(struct iscsi_wrb));
|
|
|
|
for (num = 0; num < phba->params.cxns_per_ctrl; num++) {
|
|
if (num_wrb_rings) {
|
|
pwrb_arr[num].virtual_address = wrb_vaddr;
|
|
pwrb_arr[num].bus_address.u.a64.address = pa_addr_lo;
|
|
pwrb_arr[num].size = phba->params.wrbs_per_cxn *
|
|
sizeof(struct iscsi_wrb);
|
|
wrb_vaddr += pwrb_arr[num].size;
|
|
pa_addr_lo += pwrb_arr[num].size;
|
|
num_wrb_rings--;
|
|
} else {
|
|
idx++;
|
|
wrb_vaddr = mem_descr->mem_array[idx].virtual_address;
|
|
pa_addr_lo = mem_descr->mem_array[idx].\
|
|
bus_address.u.a64.address;
|
|
num_wrb_rings = mem_descr->mem_array[idx].size /
|
|
(phba->params.wrbs_per_cxn *
|
|
sizeof(struct iscsi_wrb));
|
|
pwrb_arr[num].virtual_address = wrb_vaddr;
|
|
pwrb_arr[num].bus_address.u.a64.address\
|
|
= pa_addr_lo;
|
|
pwrb_arr[num].size = phba->params.wrbs_per_cxn *
|
|
sizeof(struct iscsi_wrb);
|
|
wrb_vaddr += pwrb_arr[num].size;
|
|
pa_addr_lo += pwrb_arr[num].size;
|
|
num_wrb_rings--;
|
|
}
|
|
}
|
|
for (i = 0; i < phba->params.cxns_per_ctrl; i++) {
|
|
wrb_mem_index = 0;
|
|
offset = 0;
|
|
size = 0;
|
|
|
|
hwi_build_be_sgl_by_offset(phba, &pwrb_arr[i], &sgl);
|
|
status = be_cmd_wrbq_create(&phba->ctrl, &sgl,
|
|
&phwi_context->be_wrbq[i]);
|
|
if (status != 0) {
|
|
shost_printk(KERN_ERR, phba->shost,
|
|
"wrbq create failed.");
|
|
kfree(pwrb_arr);
|
|
return status;
|
|
}
|
|
phwi_ctrlr->wrb_context[i * 2].cid = phwi_context->be_wrbq[i].
|
|
id;
|
|
}
|
|
kfree(pwrb_arr);
|
|
return 0;
|
|
}
|
|
|
|
static void free_wrb_handles(struct beiscsi_hba *phba)
|
|
{
|
|
unsigned int index;
|
|
struct hwi_controller *phwi_ctrlr;
|
|
struct hwi_wrb_context *pwrb_context;
|
|
|
|
phwi_ctrlr = phba->phwi_ctrlr;
|
|
for (index = 0; index < phba->params.cxns_per_ctrl * 2; index += 2) {
|
|
pwrb_context = &phwi_ctrlr->wrb_context[index];
|
|
kfree(pwrb_context->pwrb_handle_base);
|
|
kfree(pwrb_context->pwrb_handle_basestd);
|
|
}
|
|
}
|
|
|
|
static void be_mcc_queues_destroy(struct beiscsi_hba *phba)
|
|
{
|
|
struct be_queue_info *q;
|
|
struct be_ctrl_info *ctrl = &phba->ctrl;
|
|
|
|
q = &phba->ctrl.mcc_obj.q;
|
|
if (q->created)
|
|
beiscsi_cmd_q_destroy(ctrl, q, QTYPE_MCCQ);
|
|
be_queue_free(phba, q);
|
|
|
|
q = &phba->ctrl.mcc_obj.cq;
|
|
if (q->created)
|
|
beiscsi_cmd_q_destroy(ctrl, q, QTYPE_CQ);
|
|
be_queue_free(phba, q);
|
|
}
|
|
|
|
static void hwi_cleanup(struct beiscsi_hba *phba)
|
|
{
|
|
struct be_queue_info *q;
|
|
struct be_ctrl_info *ctrl = &phba->ctrl;
|
|
struct hwi_controller *phwi_ctrlr;
|
|
struct hwi_context_memory *phwi_context;
|
|
int i, eq_num;
|
|
|
|
phwi_ctrlr = phba->phwi_ctrlr;
|
|
phwi_context = phwi_ctrlr->phwi_ctxt;
|
|
for (i = 0; i < phba->params.cxns_per_ctrl; i++) {
|
|
q = &phwi_context->be_wrbq[i];
|
|
if (q->created)
|
|
beiscsi_cmd_q_destroy(ctrl, q, QTYPE_WRBQ);
|
|
}
|
|
free_wrb_handles(phba);
|
|
|
|
q = &phwi_context->be_def_hdrq;
|
|
if (q->created)
|
|
beiscsi_cmd_q_destroy(ctrl, q, QTYPE_DPDUQ);
|
|
|
|
q = &phwi_context->be_def_dataq;
|
|
if (q->created)
|
|
beiscsi_cmd_q_destroy(ctrl, q, QTYPE_DPDUQ);
|
|
|
|
beiscsi_cmd_q_destroy(ctrl, NULL, QTYPE_SGL);
|
|
|
|
for (i = 0; i < (phba->num_cpus); i++) {
|
|
q = &phwi_context->be_cq[i];
|
|
if (q->created)
|
|
beiscsi_cmd_q_destroy(ctrl, q, QTYPE_CQ);
|
|
}
|
|
if (phba->msix_enabled)
|
|
eq_num = 1;
|
|
else
|
|
eq_num = 0;
|
|
for (i = 0; i < (phba->num_cpus + eq_num); i++) {
|
|
q = &phwi_context->be_eq[i].q;
|
|
if (q->created)
|
|
beiscsi_cmd_q_destroy(ctrl, q, QTYPE_EQ);
|
|
}
|
|
be_mcc_queues_destroy(phba);
|
|
}
|
|
|
|
static int be_mcc_queues_create(struct beiscsi_hba *phba,
|
|
struct hwi_context_memory *phwi_context)
|
|
{
|
|
struct be_queue_info *q, *cq;
|
|
struct be_ctrl_info *ctrl = &phba->ctrl;
|
|
|
|
/* Alloc MCC compl queue */
|
|
cq = &phba->ctrl.mcc_obj.cq;
|
|
if (be_queue_alloc(phba, cq, MCC_CQ_LEN,
|
|
sizeof(struct be_mcc_compl)))
|
|
goto err;
|
|
/* Ask BE to create MCC compl queue; */
|
|
if (phba->msix_enabled) {
|
|
if (beiscsi_cmd_cq_create(ctrl, cq, &phwi_context->be_eq
|
|
[phba->num_cpus].q, false, true, 0))
|
|
goto mcc_cq_free;
|
|
} else {
|
|
if (beiscsi_cmd_cq_create(ctrl, cq, &phwi_context->be_eq[0].q,
|
|
false, true, 0))
|
|
goto mcc_cq_free;
|
|
}
|
|
|
|
/* Alloc MCC queue */
|
|
q = &phba->ctrl.mcc_obj.q;
|
|
if (be_queue_alloc(phba, q, MCC_Q_LEN, sizeof(struct be_mcc_wrb)))
|
|
goto mcc_cq_destroy;
|
|
|
|
/* Ask BE to create MCC queue */
|
|
if (beiscsi_cmd_mccq_create(phba, q, cq))
|
|
goto mcc_q_free;
|
|
|
|
return 0;
|
|
|
|
mcc_q_free:
|
|
be_queue_free(phba, q);
|
|
mcc_cq_destroy:
|
|
beiscsi_cmd_q_destroy(ctrl, cq, QTYPE_CQ);
|
|
mcc_cq_free:
|
|
be_queue_free(phba, cq);
|
|
err:
|
|
return -ENOMEM;
|
|
}
|
|
|
|
static int find_num_cpus(void)
|
|
{
|
|
int num_cpus = 0;
|
|
|
|
num_cpus = num_online_cpus();
|
|
if (num_cpus >= MAX_CPUS)
|
|
num_cpus = MAX_CPUS - 1;
|
|
|
|
SE_DEBUG(DBG_LVL_8, "num_cpus = %d\n", num_cpus);
|
|
return num_cpus;
|
|
}
|
|
|
|
static int hwi_init_port(struct beiscsi_hba *phba)
|
|
{
|
|
struct hwi_controller *phwi_ctrlr;
|
|
struct hwi_context_memory *phwi_context;
|
|
unsigned int def_pdu_ring_sz;
|
|
struct be_ctrl_info *ctrl = &phba->ctrl;
|
|
int status;
|
|
|
|
def_pdu_ring_sz =
|
|
phba->params.asyncpdus_per_ctrl * sizeof(struct phys_addr);
|
|
phwi_ctrlr = phba->phwi_ctrlr;
|
|
phwi_context = phwi_ctrlr->phwi_ctxt;
|
|
phwi_context->max_eqd = 0;
|
|
phwi_context->min_eqd = 0;
|
|
phwi_context->cur_eqd = 64;
|
|
be_cmd_fw_initialize(&phba->ctrl);
|
|
|
|
status = beiscsi_create_eqs(phba, phwi_context);
|
|
if (status != 0) {
|
|
shost_printk(KERN_ERR, phba->shost, "EQ not created\n");
|
|
goto error;
|
|
}
|
|
|
|
status = be_mcc_queues_create(phba, phwi_context);
|
|
if (status != 0)
|
|
goto error;
|
|
|
|
status = mgmt_check_supported_fw(ctrl, phba);
|
|
if (status != 0) {
|
|
shost_printk(KERN_ERR, phba->shost,
|
|
"Unsupported fw version\n");
|
|
goto error;
|
|
}
|
|
|
|
status = beiscsi_create_cqs(phba, phwi_context);
|
|
if (status != 0) {
|
|
shost_printk(KERN_ERR, phba->shost, "CQ not created\n");
|
|
goto error;
|
|
}
|
|
|
|
status = beiscsi_create_def_hdr(phba, phwi_context, phwi_ctrlr,
|
|
def_pdu_ring_sz);
|
|
if (status != 0) {
|
|
shost_printk(KERN_ERR, phba->shost,
|
|
"Default Header not created\n");
|
|
goto error;
|
|
}
|
|
|
|
status = beiscsi_create_def_data(phba, phwi_context,
|
|
phwi_ctrlr, def_pdu_ring_sz);
|
|
if (status != 0) {
|
|
shost_printk(KERN_ERR, phba->shost,
|
|
"Default Data not created\n");
|
|
goto error;
|
|
}
|
|
|
|
status = beiscsi_post_pages(phba);
|
|
if (status != 0) {
|
|
shost_printk(KERN_ERR, phba->shost, "Post SGL Pages Failed\n");
|
|
goto error;
|
|
}
|
|
|
|
status = beiscsi_create_wrb_rings(phba, phwi_context, phwi_ctrlr);
|
|
if (status != 0) {
|
|
shost_printk(KERN_ERR, phba->shost,
|
|
"WRB Rings not created\n");
|
|
goto error;
|
|
}
|
|
|
|
SE_DEBUG(DBG_LVL_8, "hwi_init_port success\n");
|
|
return 0;
|
|
|
|
error:
|
|
shost_printk(KERN_ERR, phba->shost, "hwi_init_port failed");
|
|
hwi_cleanup(phba);
|
|
return -ENOMEM;
|
|
}
|
|
|
|
static int hwi_init_controller(struct beiscsi_hba *phba)
|
|
{
|
|
struct hwi_controller *phwi_ctrlr;
|
|
|
|
phwi_ctrlr = phba->phwi_ctrlr;
|
|
if (1 == phba->init_mem[HWI_MEM_ADDN_CONTEXT].num_elements) {
|
|
phwi_ctrlr->phwi_ctxt = (struct hwi_context_memory *)phba->
|
|
init_mem[HWI_MEM_ADDN_CONTEXT].mem_array[0].virtual_address;
|
|
SE_DEBUG(DBG_LVL_8, " phwi_ctrlr->phwi_ctxt=%p\n",
|
|
phwi_ctrlr->phwi_ctxt);
|
|
} else {
|
|
shost_printk(KERN_ERR, phba->shost,
|
|
"HWI_MEM_ADDN_CONTEXT is more than one element."
|
|
"Failing to load\n");
|
|
return -ENOMEM;
|
|
}
|
|
|
|
iscsi_init_global_templates(phba);
|
|
beiscsi_init_wrb_handle(phba);
|
|
hwi_init_async_pdu_ctx(phba);
|
|
if (hwi_init_port(phba) != 0) {
|
|
shost_printk(KERN_ERR, phba->shost,
|
|
"hwi_init_controller failed\n");
|
|
return -ENOMEM;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static void beiscsi_free_mem(struct beiscsi_hba *phba)
|
|
{
|
|
struct be_mem_descriptor *mem_descr;
|
|
int i, j;
|
|
|
|
mem_descr = phba->init_mem;
|
|
i = 0;
|
|
j = 0;
|
|
for (i = 0; i < SE_MEM_MAX; i++) {
|
|
for (j = mem_descr->num_elements; j > 0; j--) {
|
|
pci_free_consistent(phba->pcidev,
|
|
mem_descr->mem_array[j - 1].size,
|
|
mem_descr->mem_array[j - 1].virtual_address,
|
|
(unsigned long)mem_descr->mem_array[j - 1].
|
|
bus_address.u.a64.address);
|
|
}
|
|
kfree(mem_descr->mem_array);
|
|
mem_descr++;
|
|
}
|
|
kfree(phba->init_mem);
|
|
kfree(phba->phwi_ctrlr);
|
|
}
|
|
|
|
static int beiscsi_init_controller(struct beiscsi_hba *phba)
|
|
{
|
|
int ret = -ENOMEM;
|
|
|
|
ret = beiscsi_get_memory(phba);
|
|
if (ret < 0) {
|
|
shost_printk(KERN_ERR, phba->shost, "beiscsi_dev_probe -"
|
|
"Failed in beiscsi_alloc_memory\n");
|
|
return ret;
|
|
}
|
|
|
|
ret = hwi_init_controller(phba);
|
|
if (ret)
|
|
goto free_init;
|
|
SE_DEBUG(DBG_LVL_8, "Return success from beiscsi_init_controller");
|
|
return 0;
|
|
|
|
free_init:
|
|
beiscsi_free_mem(phba);
|
|
return -ENOMEM;
|
|
}
|
|
|
|
static int beiscsi_init_sgl_handle(struct beiscsi_hba *phba)
|
|
{
|
|
struct be_mem_descriptor *mem_descr_sglh, *mem_descr_sg;
|
|
struct sgl_handle *psgl_handle;
|
|
struct iscsi_sge *pfrag;
|
|
unsigned int arr_index, i, idx;
|
|
|
|
phba->io_sgl_hndl_avbl = 0;
|
|
phba->eh_sgl_hndl_avbl = 0;
|
|
|
|
mem_descr_sglh = phba->init_mem;
|
|
mem_descr_sglh += HWI_MEM_SGLH;
|
|
if (1 == mem_descr_sglh->num_elements) {
|
|
phba->io_sgl_hndl_base = kzalloc(sizeof(struct sgl_handle *) *
|
|
phba->params.ios_per_ctrl,
|
|
GFP_KERNEL);
|
|
if (!phba->io_sgl_hndl_base) {
|
|
shost_printk(KERN_ERR, phba->shost,
|
|
"Mem Alloc Failed. Failing to load\n");
|
|
return -ENOMEM;
|
|
}
|
|
phba->eh_sgl_hndl_base = kzalloc(sizeof(struct sgl_handle *) *
|
|
(phba->params.icds_per_ctrl -
|
|
phba->params.ios_per_ctrl),
|
|
GFP_KERNEL);
|
|
if (!phba->eh_sgl_hndl_base) {
|
|
kfree(phba->io_sgl_hndl_base);
|
|
shost_printk(KERN_ERR, phba->shost,
|
|
"Mem Alloc Failed. Failing to load\n");
|
|
return -ENOMEM;
|
|
}
|
|
} else {
|
|
shost_printk(KERN_ERR, phba->shost,
|
|
"HWI_MEM_SGLH is more than one element."
|
|
"Failing to load\n");
|
|
return -ENOMEM;
|
|
}
|
|
|
|
arr_index = 0;
|
|
idx = 0;
|
|
while (idx < mem_descr_sglh->num_elements) {
|
|
psgl_handle = mem_descr_sglh->mem_array[idx].virtual_address;
|
|
|
|
for (i = 0; i < (mem_descr_sglh->mem_array[idx].size /
|
|
sizeof(struct sgl_handle)); i++) {
|
|
if (arr_index < phba->params.ios_per_ctrl) {
|
|
phba->io_sgl_hndl_base[arr_index] = psgl_handle;
|
|
phba->io_sgl_hndl_avbl++;
|
|
arr_index++;
|
|
} else {
|
|
phba->eh_sgl_hndl_base[arr_index -
|
|
phba->params.ios_per_ctrl] =
|
|
psgl_handle;
|
|
arr_index++;
|
|
phba->eh_sgl_hndl_avbl++;
|
|
}
|
|
psgl_handle++;
|
|
}
|
|
idx++;
|
|
}
|
|
SE_DEBUG(DBG_LVL_8,
|
|
"phba->io_sgl_hndl_avbl=%d"
|
|
"phba->eh_sgl_hndl_avbl=%d\n",
|
|
phba->io_sgl_hndl_avbl,
|
|
phba->eh_sgl_hndl_avbl);
|
|
mem_descr_sg = phba->init_mem;
|
|
mem_descr_sg += HWI_MEM_SGE;
|
|
SE_DEBUG(DBG_LVL_8, "\n mem_descr_sg->num_elements=%d\n",
|
|
mem_descr_sg->num_elements);
|
|
arr_index = 0;
|
|
idx = 0;
|
|
while (idx < mem_descr_sg->num_elements) {
|
|
pfrag = mem_descr_sg->mem_array[idx].virtual_address;
|
|
|
|
for (i = 0;
|
|
i < (mem_descr_sg->mem_array[idx].size) /
|
|
(sizeof(struct iscsi_sge) * phba->params.num_sge_per_io);
|
|
i++) {
|
|
if (arr_index < phba->params.ios_per_ctrl)
|
|
psgl_handle = phba->io_sgl_hndl_base[arr_index];
|
|
else
|
|
psgl_handle = phba->eh_sgl_hndl_base[arr_index -
|
|
phba->params.ios_per_ctrl];
|
|
psgl_handle->pfrag = pfrag;
|
|
AMAP_SET_BITS(struct amap_iscsi_sge, addr_hi, pfrag, 0);
|
|
AMAP_SET_BITS(struct amap_iscsi_sge, addr_lo, pfrag, 0);
|
|
pfrag += phba->params.num_sge_per_io;
|
|
psgl_handle->sgl_index =
|
|
phba->fw_config.iscsi_icd_start + arr_index++;
|
|
}
|
|
idx++;
|
|
}
|
|
phba->io_sgl_free_index = 0;
|
|
phba->io_sgl_alloc_index = 0;
|
|
phba->eh_sgl_free_index = 0;
|
|
phba->eh_sgl_alloc_index = 0;
|
|
return 0;
|
|
}
|
|
|
|
static int hba_setup_cid_tbls(struct beiscsi_hba *phba)
|
|
{
|
|
int i, new_cid;
|
|
|
|
phba->cid_array = kzalloc(sizeof(void *) * phba->params.cxns_per_ctrl,
|
|
GFP_KERNEL);
|
|
if (!phba->cid_array) {
|
|
shost_printk(KERN_ERR, phba->shost,
|
|
"Failed to allocate memory in "
|
|
"hba_setup_cid_tbls\n");
|
|
return -ENOMEM;
|
|
}
|
|
phba->ep_array = kzalloc(sizeof(struct iscsi_endpoint *) *
|
|
phba->params.cxns_per_ctrl * 2, GFP_KERNEL);
|
|
if (!phba->ep_array) {
|
|
shost_printk(KERN_ERR, phba->shost,
|
|
"Failed to allocate memory in "
|
|
"hba_setup_cid_tbls\n");
|
|
kfree(phba->cid_array);
|
|
return -ENOMEM;
|
|
}
|
|
new_cid = phba->fw_config.iscsi_cid_start;
|
|
for (i = 0; i < phba->params.cxns_per_ctrl; i++) {
|
|
phba->cid_array[i] = new_cid;
|
|
new_cid += 2;
|
|
}
|
|
phba->avlbl_cids = phba->params.cxns_per_ctrl;
|
|
return 0;
|
|
}
|
|
|
|
static void hwi_enable_intr(struct beiscsi_hba *phba)
|
|
{
|
|
struct be_ctrl_info *ctrl = &phba->ctrl;
|
|
struct hwi_controller *phwi_ctrlr;
|
|
struct hwi_context_memory *phwi_context;
|
|
struct be_queue_info *eq;
|
|
u8 __iomem *addr;
|
|
u32 reg, i;
|
|
u32 enabled;
|
|
|
|
phwi_ctrlr = phba->phwi_ctrlr;
|
|
phwi_context = phwi_ctrlr->phwi_ctxt;
|
|
|
|
addr = (u8 __iomem *) ((u8 __iomem *) ctrl->pcicfg +
|
|
PCICFG_MEMBAR_CTRL_INT_CTRL_OFFSET);
|
|
reg = ioread32(addr);
|
|
|
|
enabled = reg & MEMBAR_CTRL_INT_CTRL_HOSTINTR_MASK;
|
|
if (!enabled) {
|
|
reg |= MEMBAR_CTRL_INT_CTRL_HOSTINTR_MASK;
|
|
SE_DEBUG(DBG_LVL_8, "reg =x%08x addr=%p\n", reg, addr);
|
|
iowrite32(reg, addr);
|
|
}
|
|
|
|
if (!phba->msix_enabled) {
|
|
eq = &phwi_context->be_eq[0].q;
|
|
SE_DEBUG(DBG_LVL_8, "eq->id=%d\n", eq->id);
|
|
hwi_ring_eq_db(phba, eq->id, 0, 0, 1, 1);
|
|
} else {
|
|
for (i = 0; i <= phba->num_cpus; i++) {
|
|
eq = &phwi_context->be_eq[i].q;
|
|
SE_DEBUG(DBG_LVL_8, "eq->id=%d\n", eq->id);
|
|
hwi_ring_eq_db(phba, eq->id, 0, 0, 1, 1);
|
|
}
|
|
}
|
|
}
|
|
|
|
static void hwi_disable_intr(struct beiscsi_hba *phba)
|
|
{
|
|
struct be_ctrl_info *ctrl = &phba->ctrl;
|
|
|
|
u8 __iomem *addr = ctrl->pcicfg + PCICFG_MEMBAR_CTRL_INT_CTRL_OFFSET;
|
|
u32 reg = ioread32(addr);
|
|
|
|
u32 enabled = reg & MEMBAR_CTRL_INT_CTRL_HOSTINTR_MASK;
|
|
if (enabled) {
|
|
reg &= ~MEMBAR_CTRL_INT_CTRL_HOSTINTR_MASK;
|
|
iowrite32(reg, addr);
|
|
} else
|
|
shost_printk(KERN_WARNING, phba->shost,
|
|
"In hwi_disable_intr, Already Disabled\n");
|
|
}
|
|
|
|
static int beiscsi_get_boot_info(struct beiscsi_hba *phba)
|
|
{
|
|
struct be_cmd_resp_get_boot_target *boot_resp;
|
|
struct be_cmd_resp_get_session *session_resp;
|
|
struct be_mcc_wrb *wrb;
|
|
struct be_dma_mem nonemb_cmd;
|
|
unsigned int tag, wrb_num;
|
|
unsigned short status, extd_status;
|
|
struct be_queue_info *mccq = &phba->ctrl.mcc_obj.q;
|
|
int ret = -ENOMEM;
|
|
|
|
tag = beiscsi_get_boot_target(phba);
|
|
if (!tag) {
|
|
SE_DEBUG(DBG_LVL_1, "be_cmd_get_mac_addr Failed\n");
|
|
return -EAGAIN;
|
|
} else
|
|
wait_event_interruptible(phba->ctrl.mcc_wait[tag],
|
|
phba->ctrl.mcc_numtag[tag]);
|
|
|
|
wrb_num = (phba->ctrl.mcc_numtag[tag] & 0x00FF0000) >> 16;
|
|
extd_status = (phba->ctrl.mcc_numtag[tag] & 0x0000FF00) >> 8;
|
|
status = phba->ctrl.mcc_numtag[tag] & 0x000000FF;
|
|
if (status || extd_status) {
|
|
SE_DEBUG(DBG_LVL_1, "be_cmd_get_mac_addr Failed"
|
|
" status = %d extd_status = %d\n",
|
|
status, extd_status);
|
|
free_mcc_tag(&phba->ctrl, tag);
|
|
return -EBUSY;
|
|
}
|
|
wrb = queue_get_wrb(mccq, wrb_num);
|
|
free_mcc_tag(&phba->ctrl, tag);
|
|
boot_resp = embedded_payload(wrb);
|
|
|
|
if (boot_resp->boot_session_handle < 0) {
|
|
shost_printk(KERN_INFO, phba->shost, "No Boot Session.\n");
|
|
return -ENXIO;
|
|
}
|
|
|
|
nonemb_cmd.va = pci_alloc_consistent(phba->ctrl.pdev,
|
|
sizeof(*session_resp),
|
|
&nonemb_cmd.dma);
|
|
if (nonemb_cmd.va == NULL) {
|
|
SE_DEBUG(DBG_LVL_1,
|
|
"Failed to allocate memory for"
|
|
"beiscsi_get_session_info\n");
|
|
return -ENOMEM;
|
|
}
|
|
|
|
memset(nonemb_cmd.va, 0, sizeof(*session_resp));
|
|
tag = beiscsi_get_session_info(phba,
|
|
boot_resp->boot_session_handle, &nonemb_cmd);
|
|
if (!tag) {
|
|
SE_DEBUG(DBG_LVL_1, "beiscsi_get_session_info"
|
|
" Failed\n");
|
|
goto boot_freemem;
|
|
} else
|
|
wait_event_interruptible(phba->ctrl.mcc_wait[tag],
|
|
phba->ctrl.mcc_numtag[tag]);
|
|
|
|
wrb_num = (phba->ctrl.mcc_numtag[tag] & 0x00FF0000) >> 16;
|
|
extd_status = (phba->ctrl.mcc_numtag[tag] & 0x0000FF00) >> 8;
|
|
status = phba->ctrl.mcc_numtag[tag] & 0x000000FF;
|
|
if (status || extd_status) {
|
|
SE_DEBUG(DBG_LVL_1, "beiscsi_get_session_info Failed"
|
|
" status = %d extd_status = %d\n",
|
|
status, extd_status);
|
|
free_mcc_tag(&phba->ctrl, tag);
|
|
goto boot_freemem;
|
|
}
|
|
wrb = queue_get_wrb(mccq, wrb_num);
|
|
free_mcc_tag(&phba->ctrl, tag);
|
|
session_resp = nonemb_cmd.va ;
|
|
|
|
memcpy(&phba->boot_sess, &session_resp->session_info,
|
|
sizeof(struct mgmt_session_info));
|
|
ret = 0;
|
|
|
|
boot_freemem:
|
|
pci_free_consistent(phba->ctrl.pdev, nonemb_cmd.size,
|
|
nonemb_cmd.va, nonemb_cmd.dma);
|
|
return ret;
|
|
}
|
|
|
|
static void beiscsi_boot_release(void *data)
|
|
{
|
|
struct beiscsi_hba *phba = data;
|
|
|
|
scsi_host_put(phba->shost);
|
|
}
|
|
|
|
static int beiscsi_setup_boot_info(struct beiscsi_hba *phba)
|
|
{
|
|
struct iscsi_boot_kobj *boot_kobj;
|
|
|
|
/* get boot info using mgmt cmd */
|
|
if (beiscsi_get_boot_info(phba))
|
|
/* Try to see if we can carry on without this */
|
|
return 0;
|
|
|
|
phba->boot_kset = iscsi_boot_create_host_kset(phba->shost->host_no);
|
|
if (!phba->boot_kset)
|
|
return -ENOMEM;
|
|
|
|
/* get a ref because the show function will ref the phba */
|
|
if (!scsi_host_get(phba->shost))
|
|
goto free_kset;
|
|
boot_kobj = iscsi_boot_create_target(phba->boot_kset, 0, phba,
|
|
beiscsi_show_boot_tgt_info,
|
|
beiscsi_tgt_get_attr_visibility,
|
|
beiscsi_boot_release);
|
|
if (!boot_kobj)
|
|
goto put_shost;
|
|
|
|
if (!scsi_host_get(phba->shost))
|
|
goto free_kset;
|
|
boot_kobj = iscsi_boot_create_initiator(phba->boot_kset, 0, phba,
|
|
beiscsi_show_boot_ini_info,
|
|
beiscsi_ini_get_attr_visibility,
|
|
beiscsi_boot_release);
|
|
if (!boot_kobj)
|
|
goto put_shost;
|
|
|
|
if (!scsi_host_get(phba->shost))
|
|
goto free_kset;
|
|
boot_kobj = iscsi_boot_create_ethernet(phba->boot_kset, 0, phba,
|
|
beiscsi_show_boot_eth_info,
|
|
beiscsi_eth_get_attr_visibility,
|
|
beiscsi_boot_release);
|
|
if (!boot_kobj)
|
|
goto put_shost;
|
|
return 0;
|
|
|
|
put_shost:
|
|
scsi_host_put(phba->shost);
|
|
free_kset:
|
|
iscsi_boot_destroy_kset(phba->boot_kset);
|
|
return -ENOMEM;
|
|
}
|
|
|
|
static int beiscsi_init_port(struct beiscsi_hba *phba)
|
|
{
|
|
int ret;
|
|
|
|
ret = beiscsi_init_controller(phba);
|
|
if (ret < 0) {
|
|
shost_printk(KERN_ERR, phba->shost,
|
|
"beiscsi_dev_probe - Failed in"
|
|
"beiscsi_init_controller\n");
|
|
return ret;
|
|
}
|
|
ret = beiscsi_init_sgl_handle(phba);
|
|
if (ret < 0) {
|
|
shost_printk(KERN_ERR, phba->shost,
|
|
"beiscsi_dev_probe - Failed in"
|
|
"beiscsi_init_sgl_handle\n");
|
|
goto do_cleanup_ctrlr;
|
|
}
|
|
|
|
if (hba_setup_cid_tbls(phba)) {
|
|
shost_printk(KERN_ERR, phba->shost,
|
|
"Failed in hba_setup_cid_tbls\n");
|
|
kfree(phba->io_sgl_hndl_base);
|
|
kfree(phba->eh_sgl_hndl_base);
|
|
goto do_cleanup_ctrlr;
|
|
}
|
|
|
|
return ret;
|
|
|
|
do_cleanup_ctrlr:
|
|
hwi_cleanup(phba);
|
|
return ret;
|
|
}
|
|
|
|
static void hwi_purge_eq(struct beiscsi_hba *phba)
|
|
{
|
|
struct hwi_controller *phwi_ctrlr;
|
|
struct hwi_context_memory *phwi_context;
|
|
struct be_queue_info *eq;
|
|
struct be_eq_entry *eqe = NULL;
|
|
int i, eq_msix;
|
|
unsigned int num_processed;
|
|
|
|
phwi_ctrlr = phba->phwi_ctrlr;
|
|
phwi_context = phwi_ctrlr->phwi_ctxt;
|
|
if (phba->msix_enabled)
|
|
eq_msix = 1;
|
|
else
|
|
eq_msix = 0;
|
|
|
|
for (i = 0; i < (phba->num_cpus + eq_msix); i++) {
|
|
eq = &phwi_context->be_eq[i].q;
|
|
eqe = queue_tail_node(eq);
|
|
num_processed = 0;
|
|
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);
|
|
num_processed++;
|
|
}
|
|
|
|
if (num_processed)
|
|
hwi_ring_eq_db(phba, eq->id, 1, num_processed, 1, 1);
|
|
}
|
|
}
|
|
|
|
static void beiscsi_clean_port(struct beiscsi_hba *phba)
|
|
{
|
|
int mgmt_status;
|
|
|
|
mgmt_status = mgmt_epfw_cleanup(phba, CMD_CONNECTION_CHUTE_0);
|
|
if (mgmt_status)
|
|
shost_printk(KERN_WARNING, phba->shost,
|
|
"mgmt_epfw_cleanup FAILED\n");
|
|
|
|
hwi_purge_eq(phba);
|
|
hwi_cleanup(phba);
|
|
kfree(phba->io_sgl_hndl_base);
|
|
kfree(phba->eh_sgl_hndl_base);
|
|
kfree(phba->cid_array);
|
|
kfree(phba->ep_array);
|
|
}
|
|
|
|
void
|
|
beiscsi_offload_connection(struct beiscsi_conn *beiscsi_conn,
|
|
struct beiscsi_offload_params *params)
|
|
{
|
|
struct wrb_handle *pwrb_handle;
|
|
struct iscsi_target_context_update_wrb *pwrb = NULL;
|
|
struct be_mem_descriptor *mem_descr;
|
|
struct beiscsi_hba *phba = beiscsi_conn->phba;
|
|
u32 doorbell = 0;
|
|
|
|
/*
|
|
* We can always use 0 here because it is reserved by libiscsi for
|
|
* login/startup related tasks.
|
|
*/
|
|
pwrb_handle = alloc_wrb_handle(phba, (beiscsi_conn->beiscsi_conn_cid -
|
|
phba->fw_config.iscsi_cid_start));
|
|
pwrb = (struct iscsi_target_context_update_wrb *)pwrb_handle->pwrb;
|
|
memset(pwrb, 0, sizeof(*pwrb));
|
|
AMAP_SET_BITS(struct amap_iscsi_target_context_update_wrb,
|
|
max_burst_length, pwrb, params->dw[offsetof
|
|
(struct amap_beiscsi_offload_params,
|
|
max_burst_length) / 32]);
|
|
AMAP_SET_BITS(struct amap_iscsi_target_context_update_wrb,
|
|
max_send_data_segment_length, pwrb,
|
|
params->dw[offsetof(struct amap_beiscsi_offload_params,
|
|
max_send_data_segment_length) / 32]);
|
|
AMAP_SET_BITS(struct amap_iscsi_target_context_update_wrb,
|
|
first_burst_length,
|
|
pwrb,
|
|
params->dw[offsetof(struct amap_beiscsi_offload_params,
|
|
first_burst_length) / 32]);
|
|
|
|
AMAP_SET_BITS(struct amap_iscsi_target_context_update_wrb, erl, pwrb,
|
|
(params->dw[offsetof(struct amap_beiscsi_offload_params,
|
|
erl) / 32] & OFFLD_PARAMS_ERL));
|
|
AMAP_SET_BITS(struct amap_iscsi_target_context_update_wrb, dde, pwrb,
|
|
(params->dw[offsetof(struct amap_beiscsi_offload_params,
|
|
dde) / 32] & OFFLD_PARAMS_DDE) >> 2);
|
|
AMAP_SET_BITS(struct amap_iscsi_target_context_update_wrb, hde, pwrb,
|
|
(params->dw[offsetof(struct amap_beiscsi_offload_params,
|
|
hde) / 32] & OFFLD_PARAMS_HDE) >> 3);
|
|
AMAP_SET_BITS(struct amap_iscsi_target_context_update_wrb, ir2t, pwrb,
|
|
(params->dw[offsetof(struct amap_beiscsi_offload_params,
|
|
ir2t) / 32] & OFFLD_PARAMS_IR2T) >> 4);
|
|
AMAP_SET_BITS(struct amap_iscsi_target_context_update_wrb, imd, pwrb,
|
|
(params->dw[offsetof(struct amap_beiscsi_offload_params,
|
|
imd) / 32] & OFFLD_PARAMS_IMD) >> 5);
|
|
AMAP_SET_BITS(struct amap_iscsi_target_context_update_wrb, stat_sn,
|
|
pwrb,
|
|
(params->dw[offsetof(struct amap_beiscsi_offload_params,
|
|
exp_statsn) / 32] + 1));
|
|
AMAP_SET_BITS(struct amap_iscsi_target_context_update_wrb, type, pwrb,
|
|
0x7);
|
|
AMAP_SET_BITS(struct amap_iscsi_target_context_update_wrb, wrb_idx,
|
|
pwrb, pwrb_handle->wrb_index);
|
|
AMAP_SET_BITS(struct amap_iscsi_target_context_update_wrb, ptr2nextwrb,
|
|
pwrb, pwrb_handle->nxt_wrb_index);
|
|
AMAP_SET_BITS(struct amap_iscsi_target_context_update_wrb,
|
|
session_state, pwrb, 0);
|
|
AMAP_SET_BITS(struct amap_iscsi_target_context_update_wrb, compltonack,
|
|
pwrb, 1);
|
|
AMAP_SET_BITS(struct amap_iscsi_target_context_update_wrb, notpredblq,
|
|
pwrb, 0);
|
|
AMAP_SET_BITS(struct amap_iscsi_target_context_update_wrb, mode, pwrb,
|
|
0);
|
|
|
|
mem_descr = phba->init_mem;
|
|
mem_descr += ISCSI_MEM_GLOBAL_HEADER;
|
|
|
|
AMAP_SET_BITS(struct amap_iscsi_target_context_update_wrb,
|
|
pad_buffer_addr_hi, pwrb,
|
|
mem_descr->mem_array[0].bus_address.u.a32.address_hi);
|
|
AMAP_SET_BITS(struct amap_iscsi_target_context_update_wrb,
|
|
pad_buffer_addr_lo, pwrb,
|
|
mem_descr->mem_array[0].bus_address.u.a32.address_lo);
|
|
|
|
be_dws_le_to_cpu(pwrb, sizeof(struct iscsi_target_context_update_wrb));
|
|
|
|
doorbell |= beiscsi_conn->beiscsi_conn_cid & DB_WRB_POST_CID_MASK;
|
|
doorbell |= (pwrb_handle->wrb_index & DB_DEF_PDU_WRB_INDEX_MASK)
|
|
<< DB_DEF_PDU_WRB_INDEX_SHIFT;
|
|
doorbell |= 1 << DB_DEF_PDU_NUM_POSTED_SHIFT;
|
|
|
|
iowrite32(doorbell, phba->db_va + DB_TXULP0_OFFSET);
|
|
}
|
|
|
|
static void beiscsi_parse_pdu(struct iscsi_conn *conn, itt_t itt,
|
|
int *index, int *age)
|
|
{
|
|
*index = (int)itt;
|
|
if (age)
|
|
*age = conn->session->age;
|
|
}
|
|
|
|
/**
|
|
* beiscsi_alloc_pdu - allocates pdu and related resources
|
|
* @task: libiscsi task
|
|
* @opcode: opcode of pdu for task
|
|
*
|
|
* This is called with the session lock held. It will allocate
|
|
* the wrb and sgl if needed for the command. And it will prep
|
|
* the pdu's itt. beiscsi_parse_pdu will later translate
|
|
* the pdu itt to the libiscsi task itt.
|
|
*/
|
|
static int beiscsi_alloc_pdu(struct iscsi_task *task, uint8_t opcode)
|
|
{
|
|
struct beiscsi_io_task *io_task = task->dd_data;
|
|
struct iscsi_conn *conn = task->conn;
|
|
struct beiscsi_conn *beiscsi_conn = conn->dd_data;
|
|
struct beiscsi_hba *phba = beiscsi_conn->phba;
|
|
struct hwi_wrb_context *pwrb_context;
|
|
struct hwi_controller *phwi_ctrlr;
|
|
itt_t itt;
|
|
struct beiscsi_session *beiscsi_sess = beiscsi_conn->beiscsi_sess;
|
|
dma_addr_t paddr;
|
|
|
|
io_task->cmd_bhs = pci_pool_alloc(beiscsi_sess->bhs_pool,
|
|
GFP_ATOMIC, &paddr);
|
|
if (!io_task->cmd_bhs)
|
|
return -ENOMEM;
|
|
io_task->bhs_pa.u.a64.address = paddr;
|
|
io_task->libiscsi_itt = (itt_t)task->itt;
|
|
io_task->conn = beiscsi_conn;
|
|
|
|
task->hdr = (struct iscsi_hdr *)&io_task->cmd_bhs->iscsi_hdr;
|
|
task->hdr_max = sizeof(struct be_cmd_bhs);
|
|
io_task->psgl_handle = NULL;
|
|
io_task->psgl_handle = NULL;
|
|
|
|
if (task->sc) {
|
|
spin_lock(&phba->io_sgl_lock);
|
|
io_task->psgl_handle = alloc_io_sgl_handle(phba);
|
|
spin_unlock(&phba->io_sgl_lock);
|
|
if (!io_task->psgl_handle)
|
|
goto free_hndls;
|
|
io_task->pwrb_handle = alloc_wrb_handle(phba,
|
|
beiscsi_conn->beiscsi_conn_cid -
|
|
phba->fw_config.iscsi_cid_start);
|
|
if (!io_task->pwrb_handle)
|
|
goto free_io_hndls;
|
|
} else {
|
|
io_task->scsi_cmnd = NULL;
|
|
if ((opcode & ISCSI_OPCODE_MASK) == ISCSI_OP_LOGIN) {
|
|
if (!beiscsi_conn->login_in_progress) {
|
|
spin_lock(&phba->mgmt_sgl_lock);
|
|
io_task->psgl_handle = (struct sgl_handle *)
|
|
alloc_mgmt_sgl_handle(phba);
|
|
spin_unlock(&phba->mgmt_sgl_lock);
|
|
if (!io_task->psgl_handle)
|
|
goto free_hndls;
|
|
|
|
beiscsi_conn->login_in_progress = 1;
|
|
beiscsi_conn->plogin_sgl_handle =
|
|
io_task->psgl_handle;
|
|
io_task->pwrb_handle =
|
|
alloc_wrb_handle(phba,
|
|
beiscsi_conn->beiscsi_conn_cid -
|
|
phba->fw_config.iscsi_cid_start);
|
|
if (!io_task->pwrb_handle)
|
|
goto free_io_hndls;
|
|
beiscsi_conn->plogin_wrb_handle =
|
|
io_task->pwrb_handle;
|
|
|
|
} else {
|
|
io_task->psgl_handle =
|
|
beiscsi_conn->plogin_sgl_handle;
|
|
io_task->pwrb_handle =
|
|
beiscsi_conn->plogin_wrb_handle;
|
|
}
|
|
} else {
|
|
spin_lock(&phba->mgmt_sgl_lock);
|
|
io_task->psgl_handle = alloc_mgmt_sgl_handle(phba);
|
|
spin_unlock(&phba->mgmt_sgl_lock);
|
|
if (!io_task->psgl_handle)
|
|
goto free_hndls;
|
|
io_task->pwrb_handle =
|
|
alloc_wrb_handle(phba,
|
|
beiscsi_conn->beiscsi_conn_cid -
|
|
phba->fw_config.iscsi_cid_start);
|
|
if (!io_task->pwrb_handle)
|
|
goto free_mgmt_hndls;
|
|
|
|
}
|
|
}
|
|
itt = (itt_t) cpu_to_be32(((unsigned int)io_task->pwrb_handle->
|
|
wrb_index << 16) | (unsigned int)
|
|
(io_task->psgl_handle->sgl_index));
|
|
io_task->pwrb_handle->pio_handle = task;
|
|
|
|
io_task->cmd_bhs->iscsi_hdr.itt = itt;
|
|
return 0;
|
|
|
|
free_io_hndls:
|
|
spin_lock(&phba->io_sgl_lock);
|
|
free_io_sgl_handle(phba, io_task->psgl_handle);
|
|
spin_unlock(&phba->io_sgl_lock);
|
|
goto free_hndls;
|
|
free_mgmt_hndls:
|
|
spin_lock(&phba->mgmt_sgl_lock);
|
|
free_mgmt_sgl_handle(phba, io_task->psgl_handle);
|
|
spin_unlock(&phba->mgmt_sgl_lock);
|
|
free_hndls:
|
|
phwi_ctrlr = phba->phwi_ctrlr;
|
|
pwrb_context = &phwi_ctrlr->wrb_context[
|
|
beiscsi_conn->beiscsi_conn_cid -
|
|
phba->fw_config.iscsi_cid_start];
|
|
if (io_task->pwrb_handle)
|
|
free_wrb_handle(phba, pwrb_context, io_task->pwrb_handle);
|
|
io_task->pwrb_handle = NULL;
|
|
pci_pool_free(beiscsi_sess->bhs_pool, io_task->cmd_bhs,
|
|
io_task->bhs_pa.u.a64.address);
|
|
SE_DEBUG(DBG_LVL_1, "Alloc of SGL_ICD Failed\n");
|
|
return -ENOMEM;
|
|
}
|
|
|
|
static void beiscsi_cleanup_task(struct iscsi_task *task)
|
|
{
|
|
struct beiscsi_io_task *io_task = task->dd_data;
|
|
struct iscsi_conn *conn = task->conn;
|
|
struct beiscsi_conn *beiscsi_conn = conn->dd_data;
|
|
struct beiscsi_hba *phba = beiscsi_conn->phba;
|
|
struct beiscsi_session *beiscsi_sess = beiscsi_conn->beiscsi_sess;
|
|
struct hwi_wrb_context *pwrb_context;
|
|
struct hwi_controller *phwi_ctrlr;
|
|
|
|
phwi_ctrlr = phba->phwi_ctrlr;
|
|
pwrb_context = &phwi_ctrlr->wrb_context[beiscsi_conn->beiscsi_conn_cid
|
|
- phba->fw_config.iscsi_cid_start];
|
|
if (io_task->pwrb_handle) {
|
|
free_wrb_handle(phba, pwrb_context, io_task->pwrb_handle);
|
|
io_task->pwrb_handle = NULL;
|
|
}
|
|
|
|
if (io_task->cmd_bhs) {
|
|
pci_pool_free(beiscsi_sess->bhs_pool, io_task->cmd_bhs,
|
|
io_task->bhs_pa.u.a64.address);
|
|
}
|
|
|
|
if (task->sc) {
|
|
if (io_task->psgl_handle) {
|
|
spin_lock(&phba->io_sgl_lock);
|
|
free_io_sgl_handle(phba, io_task->psgl_handle);
|
|
spin_unlock(&phba->io_sgl_lock);
|
|
io_task->psgl_handle = NULL;
|
|
}
|
|
} else {
|
|
if (task->hdr &&
|
|
((task->hdr->opcode & ISCSI_OPCODE_MASK) == ISCSI_OP_LOGIN))
|
|
return;
|
|
if (io_task->psgl_handle) {
|
|
spin_lock(&phba->mgmt_sgl_lock);
|
|
free_mgmt_sgl_handle(phba, io_task->psgl_handle);
|
|
spin_unlock(&phba->mgmt_sgl_lock);
|
|
io_task->psgl_handle = NULL;
|
|
}
|
|
}
|
|
}
|
|
|
|
static int beiscsi_iotask(struct iscsi_task *task, struct scatterlist *sg,
|
|
unsigned int num_sg, unsigned int xferlen,
|
|
unsigned int writedir)
|
|
{
|
|
|
|
struct beiscsi_io_task *io_task = task->dd_data;
|
|
struct iscsi_conn *conn = task->conn;
|
|
struct beiscsi_conn *beiscsi_conn = conn->dd_data;
|
|
struct beiscsi_hba *phba = beiscsi_conn->phba;
|
|
struct iscsi_wrb *pwrb = NULL;
|
|
unsigned int doorbell = 0;
|
|
|
|
pwrb = io_task->pwrb_handle->pwrb;
|
|
io_task->cmd_bhs->iscsi_hdr.exp_statsn = 0;
|
|
io_task->bhs_len = sizeof(struct be_cmd_bhs);
|
|
|
|
if (writedir) {
|
|
memset(&io_task->cmd_bhs->iscsi_data_pdu, 0, 48);
|
|
AMAP_SET_BITS(struct amap_pdu_data_out, itt,
|
|
&io_task->cmd_bhs->iscsi_data_pdu,
|
|
(unsigned int)io_task->cmd_bhs->iscsi_hdr.itt);
|
|
AMAP_SET_BITS(struct amap_pdu_data_out, opcode,
|
|
&io_task->cmd_bhs->iscsi_data_pdu,
|
|
ISCSI_OPCODE_SCSI_DATA_OUT);
|
|
AMAP_SET_BITS(struct amap_pdu_data_out, final_bit,
|
|
&io_task->cmd_bhs->iscsi_data_pdu, 1);
|
|
AMAP_SET_BITS(struct amap_iscsi_wrb, type, pwrb,
|
|
INI_WR_CMD);
|
|
AMAP_SET_BITS(struct amap_iscsi_wrb, dsp, pwrb, 1);
|
|
} else {
|
|
AMAP_SET_BITS(struct amap_iscsi_wrb, type, pwrb,
|
|
INI_RD_CMD);
|
|
AMAP_SET_BITS(struct amap_iscsi_wrb, dsp, pwrb, 0);
|
|
}
|
|
memcpy(&io_task->cmd_bhs->iscsi_data_pdu.
|
|
dw[offsetof(struct amap_pdu_data_out, lun) / 32],
|
|
&io_task->cmd_bhs->iscsi_hdr.lun, sizeof(struct scsi_lun));
|
|
|
|
AMAP_SET_BITS(struct amap_iscsi_wrb, lun, pwrb,
|
|
cpu_to_be16(*(unsigned short *)&io_task->cmd_bhs->iscsi_hdr.lun));
|
|
AMAP_SET_BITS(struct amap_iscsi_wrb, r2t_exp_dtl, pwrb, xferlen);
|
|
AMAP_SET_BITS(struct amap_iscsi_wrb, wrb_idx, pwrb,
|
|
io_task->pwrb_handle->wrb_index);
|
|
AMAP_SET_BITS(struct amap_iscsi_wrb, cmdsn_itt, pwrb,
|
|
be32_to_cpu(task->cmdsn));
|
|
AMAP_SET_BITS(struct amap_iscsi_wrb, sgl_icd_idx, pwrb,
|
|
io_task->psgl_handle->sgl_index);
|
|
|
|
hwi_write_sgl(pwrb, sg, num_sg, io_task);
|
|
|
|
AMAP_SET_BITS(struct amap_iscsi_wrb, ptr2nextwrb, pwrb,
|
|
io_task->pwrb_handle->nxt_wrb_index);
|
|
be_dws_le_to_cpu(pwrb, sizeof(struct iscsi_wrb));
|
|
|
|
doorbell |= beiscsi_conn->beiscsi_conn_cid & DB_WRB_POST_CID_MASK;
|
|
doorbell |= (io_task->pwrb_handle->wrb_index &
|
|
DB_DEF_PDU_WRB_INDEX_MASK) << DB_DEF_PDU_WRB_INDEX_SHIFT;
|
|
doorbell |= 1 << DB_DEF_PDU_NUM_POSTED_SHIFT;
|
|
|
|
iowrite32(doorbell, phba->db_va + DB_TXULP0_OFFSET);
|
|
return 0;
|
|
}
|
|
|
|
static int beiscsi_mtask(struct iscsi_task *task)
|
|
{
|
|
struct beiscsi_io_task *io_task = task->dd_data;
|
|
struct iscsi_conn *conn = task->conn;
|
|
struct beiscsi_conn *beiscsi_conn = conn->dd_data;
|
|
struct beiscsi_hba *phba = beiscsi_conn->phba;
|
|
struct iscsi_wrb *pwrb = NULL;
|
|
unsigned int doorbell = 0;
|
|
unsigned int cid;
|
|
|
|
cid = beiscsi_conn->beiscsi_conn_cid;
|
|
pwrb = io_task->pwrb_handle->pwrb;
|
|
memset(pwrb, 0, sizeof(*pwrb));
|
|
AMAP_SET_BITS(struct amap_iscsi_wrb, cmdsn_itt, pwrb,
|
|
be32_to_cpu(task->cmdsn));
|
|
AMAP_SET_BITS(struct amap_iscsi_wrb, wrb_idx, pwrb,
|
|
io_task->pwrb_handle->wrb_index);
|
|
AMAP_SET_BITS(struct amap_iscsi_wrb, sgl_icd_idx, pwrb,
|
|
io_task->psgl_handle->sgl_index);
|
|
|
|
switch (task->hdr->opcode & ISCSI_OPCODE_MASK) {
|
|
case ISCSI_OP_LOGIN:
|
|
AMAP_SET_BITS(struct amap_iscsi_wrb, type, pwrb,
|
|
TGT_DM_CMD);
|
|
AMAP_SET_BITS(struct amap_iscsi_wrb, dmsg, pwrb, 0);
|
|
AMAP_SET_BITS(struct amap_iscsi_wrb, cmdsn_itt, pwrb, 1);
|
|
hwi_write_buffer(pwrb, task);
|
|
break;
|
|
case ISCSI_OP_NOOP_OUT:
|
|
if (task->hdr->ttt != ISCSI_RESERVED_TAG) {
|
|
AMAP_SET_BITS(struct amap_iscsi_wrb, type, pwrb,
|
|
TGT_DM_CMD);
|
|
AMAP_SET_BITS(struct amap_iscsi_wrb, cmdsn_itt,
|
|
pwrb, 0);
|
|
AMAP_SET_BITS(struct amap_iscsi_wrb, dmsg, pwrb, 1);
|
|
} else {
|
|
AMAP_SET_BITS(struct amap_iscsi_wrb, type, pwrb,
|
|
INI_RD_CMD);
|
|
AMAP_SET_BITS(struct amap_iscsi_wrb, dmsg, pwrb, 0);
|
|
}
|
|
hwi_write_buffer(pwrb, task);
|
|
break;
|
|
case ISCSI_OP_TEXT:
|
|
AMAP_SET_BITS(struct amap_iscsi_wrb, type, pwrb,
|
|
TGT_DM_CMD);
|
|
AMAP_SET_BITS(struct amap_iscsi_wrb, dmsg, pwrb, 0);
|
|
hwi_write_buffer(pwrb, task);
|
|
break;
|
|
case ISCSI_OP_SCSI_TMFUNC:
|
|
AMAP_SET_BITS(struct amap_iscsi_wrb, type, pwrb,
|
|
INI_TMF_CMD);
|
|
AMAP_SET_BITS(struct amap_iscsi_wrb, dmsg, pwrb, 0);
|
|
hwi_write_buffer(pwrb, task);
|
|
break;
|
|
case ISCSI_OP_LOGOUT:
|
|
AMAP_SET_BITS(struct amap_iscsi_wrb, dmsg, pwrb, 0);
|
|
AMAP_SET_BITS(struct amap_iscsi_wrb, type, pwrb,
|
|
HWH_TYPE_LOGOUT);
|
|
hwi_write_buffer(pwrb, task);
|
|
break;
|
|
|
|
default:
|
|
SE_DEBUG(DBG_LVL_1, "opcode =%d Not supported\n",
|
|
task->hdr->opcode & ISCSI_OPCODE_MASK);
|
|
return -EINVAL;
|
|
}
|
|
|
|
AMAP_SET_BITS(struct amap_iscsi_wrb, r2t_exp_dtl, pwrb,
|
|
task->data_count);
|
|
AMAP_SET_BITS(struct amap_iscsi_wrb, ptr2nextwrb, pwrb,
|
|
io_task->pwrb_handle->nxt_wrb_index);
|
|
be_dws_le_to_cpu(pwrb, sizeof(struct iscsi_wrb));
|
|
|
|
doorbell |= cid & DB_WRB_POST_CID_MASK;
|
|
doorbell |= (io_task->pwrb_handle->wrb_index &
|
|
DB_DEF_PDU_WRB_INDEX_MASK) << DB_DEF_PDU_WRB_INDEX_SHIFT;
|
|
doorbell |= 1 << DB_DEF_PDU_NUM_POSTED_SHIFT;
|
|
iowrite32(doorbell, phba->db_va + DB_TXULP0_OFFSET);
|
|
return 0;
|
|
}
|
|
|
|
static int beiscsi_task_xmit(struct iscsi_task *task)
|
|
{
|
|
struct beiscsi_io_task *io_task = task->dd_data;
|
|
struct scsi_cmnd *sc = task->sc;
|
|
struct scatterlist *sg;
|
|
int num_sg;
|
|
unsigned int writedir = 0, xferlen = 0;
|
|
|
|
if (!sc)
|
|
return beiscsi_mtask(task);
|
|
|
|
io_task->scsi_cmnd = sc;
|
|
num_sg = scsi_dma_map(sc);
|
|
if (num_sg < 0) {
|
|
SE_DEBUG(DBG_LVL_1, " scsi_dma_map Failed\n")
|
|
return num_sg;
|
|
}
|
|
xferlen = scsi_bufflen(sc);
|
|
sg = scsi_sglist(sc);
|
|
if (sc->sc_data_direction == DMA_TO_DEVICE) {
|
|
writedir = 1;
|
|
SE_DEBUG(DBG_LVL_4, "task->imm_count=0x%08x\n",
|
|
task->imm_count);
|
|
} else
|
|
writedir = 0;
|
|
return beiscsi_iotask(task, sg, num_sg, xferlen, writedir);
|
|
}
|
|
|
|
static void beiscsi_quiesce(struct beiscsi_hba *phba)
|
|
{
|
|
struct hwi_controller *phwi_ctrlr;
|
|
struct hwi_context_memory *phwi_context;
|
|
struct be_eq_obj *pbe_eq;
|
|
unsigned int i, msix_vec;
|
|
u8 *real_offset = 0;
|
|
u32 value = 0;
|
|
|
|
phwi_ctrlr = phba->phwi_ctrlr;
|
|
phwi_context = phwi_ctrlr->phwi_ctxt;
|
|
hwi_disable_intr(phba);
|
|
if (phba->msix_enabled) {
|
|
for (i = 0; i <= phba->num_cpus; i++) {
|
|
msix_vec = phba->msix_entries[i].vector;
|
|
free_irq(msix_vec, &phwi_context->be_eq[i]);
|
|
kfree(phba->msi_name[i]);
|
|
}
|
|
} else
|
|
if (phba->pcidev->irq)
|
|
free_irq(phba->pcidev->irq, phba);
|
|
pci_disable_msix(phba->pcidev);
|
|
destroy_workqueue(phba->wq);
|
|
if (blk_iopoll_enabled)
|
|
for (i = 0; i < phba->num_cpus; i++) {
|
|
pbe_eq = &phwi_context->be_eq[i];
|
|
blk_iopoll_disable(&pbe_eq->iopoll);
|
|
}
|
|
|
|
beiscsi_clean_port(phba);
|
|
beiscsi_free_mem(phba);
|
|
real_offset = (u8 *)phba->csr_va + MPU_EP_SEMAPHORE;
|
|
|
|
value = readl((void *)real_offset);
|
|
|
|
if (value & 0x00010000) {
|
|
value &= 0xfffeffff;
|
|
writel(value, (void *)real_offset);
|
|
}
|
|
beiscsi_unmap_pci_function(phba);
|
|
pci_free_consistent(phba->pcidev,
|
|
phba->ctrl.mbox_mem_alloced.size,
|
|
phba->ctrl.mbox_mem_alloced.va,
|
|
phba->ctrl.mbox_mem_alloced.dma);
|
|
}
|
|
|
|
static void beiscsi_remove(struct pci_dev *pcidev)
|
|
{
|
|
|
|
struct beiscsi_hba *phba = NULL;
|
|
|
|
phba = pci_get_drvdata(pcidev);
|
|
if (!phba) {
|
|
dev_err(&pcidev->dev, "beiscsi_remove called with no phba\n");
|
|
return;
|
|
}
|
|
|
|
beiscsi_quiesce(phba);
|
|
iscsi_boot_destroy_kset(phba->boot_kset);
|
|
iscsi_host_remove(phba->shost);
|
|
pci_dev_put(phba->pcidev);
|
|
iscsi_host_free(phba->shost);
|
|
pci_disable_device(pcidev);
|
|
}
|
|
|
|
static void beiscsi_shutdown(struct pci_dev *pcidev)
|
|
{
|
|
|
|
struct beiscsi_hba *phba = NULL;
|
|
|
|
phba = (struct beiscsi_hba *)pci_get_drvdata(pcidev);
|
|
if (!phba) {
|
|
dev_err(&pcidev->dev, "beiscsi_shutdown called with no phba\n");
|
|
return;
|
|
}
|
|
|
|
beiscsi_quiesce(phba);
|
|
pci_disable_device(pcidev);
|
|
}
|
|
|
|
static void beiscsi_msix_enable(struct beiscsi_hba *phba)
|
|
{
|
|
int i, status;
|
|
|
|
for (i = 0; i <= phba->num_cpus; i++)
|
|
phba->msix_entries[i].entry = i;
|
|
|
|
status = pci_enable_msix(phba->pcidev, phba->msix_entries,
|
|
(phba->num_cpus + 1));
|
|
if (!status)
|
|
phba->msix_enabled = true;
|
|
|
|
return;
|
|
}
|
|
|
|
static int __devinit beiscsi_dev_probe(struct pci_dev *pcidev,
|
|
const struct pci_device_id *id)
|
|
{
|
|
struct beiscsi_hba *phba = NULL;
|
|
struct hwi_controller *phwi_ctrlr;
|
|
struct hwi_context_memory *phwi_context;
|
|
struct be_eq_obj *pbe_eq;
|
|
int ret, num_cpus, i;
|
|
u8 *real_offset = 0;
|
|
u32 value = 0;
|
|
|
|
ret = beiscsi_enable_pci(pcidev);
|
|
if (ret < 0) {
|
|
dev_err(&pcidev->dev, "beiscsi_dev_probe-"
|
|
" Failed to enable pci device\n");
|
|
return ret;
|
|
}
|
|
|
|
phba = beiscsi_hba_alloc(pcidev);
|
|
if (!phba) {
|
|
dev_err(&pcidev->dev, "beiscsi_dev_probe-"
|
|
" Failed in beiscsi_hba_alloc\n");
|
|
goto disable_pci;
|
|
}
|
|
|
|
switch (pcidev->device) {
|
|
case BE_DEVICE_ID1:
|
|
case OC_DEVICE_ID1:
|
|
case OC_DEVICE_ID2:
|
|
phba->generation = BE_GEN2;
|
|
break;
|
|
case BE_DEVICE_ID2:
|
|
case OC_DEVICE_ID3:
|
|
phba->generation = BE_GEN3;
|
|
break;
|
|
default:
|
|
phba->generation = 0;
|
|
}
|
|
|
|
if (enable_msix)
|
|
num_cpus = find_num_cpus();
|
|
else
|
|
num_cpus = 1;
|
|
phba->num_cpus = num_cpus;
|
|
SE_DEBUG(DBG_LVL_8, "num_cpus = %d\n", phba->num_cpus);
|
|
|
|
if (enable_msix)
|
|
beiscsi_msix_enable(phba);
|
|
ret = be_ctrl_init(phba, pcidev);
|
|
if (ret) {
|
|
shost_printk(KERN_ERR, phba->shost, "beiscsi_dev_probe-"
|
|
"Failed in be_ctrl_init\n");
|
|
goto hba_free;
|
|
}
|
|
|
|
if (!num_hba) {
|
|
real_offset = (u8 *)phba->csr_va + MPU_EP_SEMAPHORE;
|
|
value = readl((void *)real_offset);
|
|
if (value & 0x00010000) {
|
|
gcrashmode++;
|
|
shost_printk(KERN_ERR, phba->shost,
|
|
"Loading Driver in crashdump mode\n");
|
|
ret = beiscsi_cmd_reset_function(phba);
|
|
if (ret) {
|
|
shost_printk(KERN_ERR, phba->shost,
|
|
"Reset Failed. Aborting Crashdump\n");
|
|
goto hba_free;
|
|
}
|
|
ret = be_chk_reset_complete(phba);
|
|
if (ret) {
|
|
shost_printk(KERN_ERR, phba->shost,
|
|
"Failed to get out of reset."
|
|
"Aborting Crashdump\n");
|
|
goto hba_free;
|
|
}
|
|
} else {
|
|
value |= 0x00010000;
|
|
writel(value, (void *)real_offset);
|
|
num_hba++;
|
|
}
|
|
}
|
|
|
|
spin_lock_init(&phba->io_sgl_lock);
|
|
spin_lock_init(&phba->mgmt_sgl_lock);
|
|
spin_lock_init(&phba->isr_lock);
|
|
ret = mgmt_get_fw_config(&phba->ctrl, phba);
|
|
if (ret != 0) {
|
|
shost_printk(KERN_ERR, phba->shost,
|
|
"Error getting fw config\n");
|
|
goto free_port;
|
|
}
|
|
phba->shost->max_id = phba->fw_config.iscsi_cid_count;
|
|
beiscsi_get_params(phba);
|
|
phba->shost->can_queue = phba->params.ios_per_ctrl;
|
|
ret = beiscsi_init_port(phba);
|
|
if (ret < 0) {
|
|
shost_printk(KERN_ERR, phba->shost, "beiscsi_dev_probe-"
|
|
"Failed in beiscsi_init_port\n");
|
|
goto free_port;
|
|
}
|
|
|
|
for (i = 0; i < MAX_MCC_CMD ; i++) {
|
|
init_waitqueue_head(&phba->ctrl.mcc_wait[i + 1]);
|
|
phba->ctrl.mcc_tag[i] = i + 1;
|
|
phba->ctrl.mcc_numtag[i + 1] = 0;
|
|
phba->ctrl.mcc_tag_available++;
|
|
}
|
|
|
|
phba->ctrl.mcc_alloc_index = phba->ctrl.mcc_free_index = 0;
|
|
|
|
snprintf(phba->wq_name, sizeof(phba->wq_name), "beiscsi_q_irq%u",
|
|
phba->shost->host_no);
|
|
phba->wq = alloc_workqueue(phba->wq_name, WQ_MEM_RECLAIM, 1);
|
|
if (!phba->wq) {
|
|
shost_printk(KERN_ERR, phba->shost, "beiscsi_dev_probe-"
|
|
"Failed to allocate work queue\n");
|
|
goto free_twq;
|
|
}
|
|
|
|
INIT_WORK(&phba->work_cqs, beiscsi_process_all_cqs);
|
|
|
|
phwi_ctrlr = phba->phwi_ctrlr;
|
|
phwi_context = phwi_ctrlr->phwi_ctxt;
|
|
if (blk_iopoll_enabled) {
|
|
for (i = 0; i < phba->num_cpus; i++) {
|
|
pbe_eq = &phwi_context->be_eq[i];
|
|
blk_iopoll_init(&pbe_eq->iopoll, be_iopoll_budget,
|
|
be_iopoll);
|
|
blk_iopoll_enable(&pbe_eq->iopoll);
|
|
}
|
|
}
|
|
ret = beiscsi_init_irqs(phba);
|
|
if (ret < 0) {
|
|
shost_printk(KERN_ERR, phba->shost, "beiscsi_dev_probe-"
|
|
"Failed to beiscsi_init_irqs\n");
|
|
goto free_blkenbld;
|
|
}
|
|
hwi_enable_intr(phba);
|
|
|
|
if (beiscsi_setup_boot_info(phba))
|
|
/*
|
|
* log error but continue, because we may not be using
|
|
* iscsi boot.
|
|
*/
|
|
shost_printk(KERN_ERR, phba->shost, "Could not set up "
|
|
"iSCSI boot info.");
|
|
|
|
SE_DEBUG(DBG_LVL_8, "\n\n\n SUCCESS - DRIVER LOADED\n\n\n");
|
|
return 0;
|
|
|
|
free_blkenbld:
|
|
destroy_workqueue(phba->wq);
|
|
if (blk_iopoll_enabled)
|
|
for (i = 0; i < phba->num_cpus; i++) {
|
|
pbe_eq = &phwi_context->be_eq[i];
|
|
blk_iopoll_disable(&pbe_eq->iopoll);
|
|
}
|
|
free_twq:
|
|
beiscsi_clean_port(phba);
|
|
beiscsi_free_mem(phba);
|
|
free_port:
|
|
real_offset = (u8 *)phba->csr_va + MPU_EP_SEMAPHORE;
|
|
|
|
value = readl((void *)real_offset);
|
|
|
|
if (value & 0x00010000) {
|
|
value &= 0xfffeffff;
|
|
writel(value, (void *)real_offset);
|
|
}
|
|
|
|
pci_free_consistent(phba->pcidev,
|
|
phba->ctrl.mbox_mem_alloced.size,
|
|
phba->ctrl.mbox_mem_alloced.va,
|
|
phba->ctrl.mbox_mem_alloced.dma);
|
|
beiscsi_unmap_pci_function(phba);
|
|
hba_free:
|
|
if (phba->msix_enabled)
|
|
pci_disable_msix(phba->pcidev);
|
|
iscsi_host_remove(phba->shost);
|
|
pci_dev_put(phba->pcidev);
|
|
iscsi_host_free(phba->shost);
|
|
disable_pci:
|
|
pci_disable_device(pcidev);
|
|
return ret;
|
|
}
|
|
|
|
struct iscsi_transport beiscsi_iscsi_transport = {
|
|
.owner = THIS_MODULE,
|
|
.name = DRV_NAME,
|
|
.caps = CAP_RECOVERY_L0 | CAP_HDRDGST | CAP_TEXT_NEGO |
|
|
CAP_MULTI_R2T | CAP_DATADGST | CAP_DATA_PATH_OFFLOAD,
|
|
.create_session = beiscsi_session_create,
|
|
.destroy_session = beiscsi_session_destroy,
|
|
.create_conn = beiscsi_conn_create,
|
|
.bind_conn = beiscsi_conn_bind,
|
|
.destroy_conn = iscsi_conn_teardown,
|
|
.attr_is_visible = be2iscsi_attr_is_visible,
|
|
.set_param = beiscsi_set_param,
|
|
.get_conn_param = iscsi_conn_get_param,
|
|
.get_session_param = iscsi_session_get_param,
|
|
.get_host_param = beiscsi_get_host_param,
|
|
.start_conn = beiscsi_conn_start,
|
|
.stop_conn = iscsi_conn_stop,
|
|
.send_pdu = iscsi_conn_send_pdu,
|
|
.xmit_task = beiscsi_task_xmit,
|
|
.cleanup_task = beiscsi_cleanup_task,
|
|
.alloc_pdu = beiscsi_alloc_pdu,
|
|
.parse_pdu_itt = beiscsi_parse_pdu,
|
|
.get_stats = beiscsi_conn_get_stats,
|
|
.get_ep_param = beiscsi_ep_get_param,
|
|
.ep_connect = beiscsi_ep_connect,
|
|
.ep_poll = beiscsi_ep_poll,
|
|
.ep_disconnect = beiscsi_ep_disconnect,
|
|
.session_recovery_timedout = iscsi_session_recovery_timedout,
|
|
};
|
|
|
|
static struct pci_driver beiscsi_pci_driver = {
|
|
.name = DRV_NAME,
|
|
.probe = beiscsi_dev_probe,
|
|
.remove = beiscsi_remove,
|
|
.shutdown = beiscsi_shutdown,
|
|
.id_table = beiscsi_pci_id_table
|
|
};
|
|
|
|
|
|
static int __init beiscsi_module_init(void)
|
|
{
|
|
int ret;
|
|
|
|
beiscsi_scsi_transport =
|
|
iscsi_register_transport(&beiscsi_iscsi_transport);
|
|
if (!beiscsi_scsi_transport) {
|
|
SE_DEBUG(DBG_LVL_1,
|
|
"beiscsi_module_init - Unable to register beiscsi"
|
|
"transport.\n");
|
|
return -ENOMEM;
|
|
}
|
|
SE_DEBUG(DBG_LVL_8, "In beiscsi_module_init, tt=%p\n",
|
|
&beiscsi_iscsi_transport);
|
|
|
|
ret = pci_register_driver(&beiscsi_pci_driver);
|
|
if (ret) {
|
|
SE_DEBUG(DBG_LVL_1,
|
|
"beiscsi_module_init - Unable to register"
|
|
"beiscsi pci driver.\n");
|
|
goto unregister_iscsi_transport;
|
|
}
|
|
return 0;
|
|
|
|
unregister_iscsi_transport:
|
|
iscsi_unregister_transport(&beiscsi_iscsi_transport);
|
|
return ret;
|
|
}
|
|
|
|
static void __exit beiscsi_module_exit(void)
|
|
{
|
|
pci_unregister_driver(&beiscsi_pci_driver);
|
|
iscsi_unregister_transport(&beiscsi_iscsi_transport);
|
|
}
|
|
|
|
module_init(beiscsi_module_init);
|
|
module_exit(beiscsi_module_exit);
|